227 lines
9.8 KiB
Markdown
227 lines
9.8 KiB
Markdown
|
# Traffic manager
|
||
|
|
||
|
When we train neural networks to control autonomous vehicles, one of the key challenges the autonomous driving agent has to contend with is other road users. On top of the task of recognising and navigating the topology of the road network and maintaining lane discipline, the autonomous driving agent must also recognise other vehicles and anticipate the impact on its planned course of action. CARLA's Traffic Manager (TM) enables the management of an ensemble of vehicles navigating through the simulation and creating obstacles and challenges for the vehicle of interest, i.e. the vehicle we are training or controlling. In the CARLA literature, we refer to this vehicle as the "Ego vehicle" to distinguish it.
|
||
|
|
||
|
The TM manages the behavior and lifecycles of Non Player Character (NPC) vehicles within the map, populating the simulation with vehicles that act as other road users do on the real road network. In this tutorial, we will cover some of the functionality of the TM and how to use it in your simulations to create and control NPCs.
|
||
|
|
||
|
## Setting up the simulator and initialising traffic manager
|
||
|
|
||
|
First, we will initialise the TM and create some traffic randomly distributed around the city.
|
||
|
|
||
|
```py
|
||
|
import carla
|
||
|
import random
|
||
|
|
||
|
# Connect to the client and retrieve the world object
|
||
|
client = carla.Client('localhost', 2000)
|
||
|
world = client.get_world()
|
||
|
|
||
|
# Set up the simulator in synchronous mode
|
||
|
settings = world.get_settings()
|
||
|
settings.synchronous_mode = True # Enables synchronous mode
|
||
|
settings.fixed_delta_seconds = 0.05
|
||
|
world.apply_settings(settings)
|
||
|
|
||
|
# Set up the TM in synchronous mode
|
||
|
traffic_manager = client.get_trafficmanager()
|
||
|
traffic_manager.set_synchronous_mode(True)
|
||
|
|
||
|
# Set a seed so behaviour can be repeated if necessary
|
||
|
traffic_manager.set_random_device_seed(0)
|
||
|
random.seed(0)
|
||
|
|
||
|
# We will aslo set up the spectator so we can see what we do
|
||
|
spectator = world.get_spectator()
|
||
|
|
||
|
```
|
||
|
|
||
|
## Spawning vehicles
|
||
|
|
||
|
When we create TM vehicles, they need a map location at which to spawn. We can define these ourselves using our own chosen map coordinates. However, to help with this, each CARLA map has a set of pre-defined spawn points spread evenly throughout the road network. We can use these spawn points to spawn our vehicles.
|
||
|
|
||
|
```py
|
||
|
spawn_points = world.get_map().get_spawn_points()
|
||
|
```
|
||
|
|
||
|
We can use CARLA's debug functions to see where the spawn points are. Run the following code then fly through the map and inspect where the spawn points are. This will come in handy when we want to choose more specific points to use for spawning or guiding vehicles.
|
||
|
|
||
|
```py
|
||
|
# Draw the spawn point locations as numbers in the map
|
||
|
for i, spawn_point in enumerate(spawn_points):
|
||
|
world.debug.draw_string(spawn_point.location, str(i), life_time=10)
|
||
|
|
||
|
# In synchronous mode, we need to run the simulation to fly the spectator
|
||
|
while True:
|
||
|
world.tick()
|
||
|
```
|
||
|
|
||
|
Now let's spawn some vehicles.
|
||
|
|
||
|
```py
|
||
|
# Select some models from the blueprint library
|
||
|
models = ['dodge', 'audi', 'model3', 'mini', 'mustang', 'lincoln', 'prius', 'nissan', 'crown', 'impala']
|
||
|
blueprints = []
|
||
|
for vehicle in world.get_blueprint_library().filter('*vehicle*'):
|
||
|
if any(model in vehicle.id for model in models):
|
||
|
blueprints.append(vehicle)
|
||
|
|
||
|
# Set a max number of vehicles and prepare a list for those we spawn
|
||
|
max_vehicles = 50
|
||
|
max_vehicles = min([max_vehicles, len(spawn_points)])
|
||
|
vehicles = []
|
||
|
|
||
|
# Take a random sample of the spawn points and spawn some vehicles
|
||
|
for i, spawn_point in enumerate(random.sample(spawn_points, max_vehicles)):
|
||
|
temp = world.try_spawn_actor(random.choice(blueprints), spawn_point)
|
||
|
if temp is not None:
|
||
|
vehicles.append(temp)
|
||
|
|
||
|
# Run the simulation so we can inspect the results with the spectator
|
||
|
while True:
|
||
|
world.tick()
|
||
|
|
||
|
|
||
|
```
|
||
|
|
||
|
If you fly through the map with the spectator now, you should see stationary vehicles occupying the roads in the map.
|
||
|
|
||
|
## Controlling vehicles with Traffic Manager
|
||
|
|
||
|
We can now give the TM control over our vehicles and let the simulation run. Once the TM takes control of the vehicles, they will move around the roads autonomously, following features of the road network like lanes and traffic lights and avoiding collisions with other vehicles.
|
||
|
|
||
|
The TM has a number of functions that allow specific behaviors to be modified for each vehicle. In the following example, we set each vehicle with a random probability of ignoring traffic lights, so some vehicles will tend to ignore traffic lights, while others will obey them. There are a number of different behaviours that can be set, refer to the Python API reference for details.
|
||
|
|
||
|
```py
|
||
|
# Parse the list of spawned vehicles and give control to the TM through set_autopilot()
|
||
|
for vehicle in vehicles:
|
||
|
vehicle.set_autopilot(True)
|
||
|
# Randomly set the probability that a vehicle will ignore traffic lights
|
||
|
traffic_manager.ignore_lights_percentage(vehicle, random.randint(0,50))
|
||
|
|
||
|
while True:
|
||
|
world.tick()
|
||
|
|
||
|
```
|
||
|
|
||
|
If you now fly through the map with the spectator, you will see vehicles driving autonomously around the map.
|
||
|
|
||
|
![intersection_traffic](../img/tuto_G_traffic_manager/traffic.gif)
|
||
|
|
||
|
## Specify routes for vehicles
|
||
|
|
||
|
In the previous steps, we saw how to spawn a collection of vehicles into a map, then hand control of them over to the TM to create a busy town full of moving traffic. The TM has deeper functionality to control the behavior of vehicles more closely.
|
||
|
|
||
|
We will now use the `traffic_manager.set_path()` function to guide TM vehicles along specific paths. In this case, we will create two converging streams of traffic that will converge in the center of town and create congestion.
|
||
|
|
||
|
Firstly, we'll choose some waypoints to construct our path. Spawn points are convenient waypoints and in the same way as earlier we can use CARLA's debug tools to draw the locations of the spawn points on the map. By flying through the map with the spectator, we can choose the indices of the spawn points we want to use for our path. The `set_path()` function uses a list of coordinates specified as [carla.Locations](python_api.md#carla.Location).
|
||
|
|
||
|
```py
|
||
|
# Draw the spawn point locations as numbers in the map
|
||
|
for i, spawn_point in enumerate(spawn_points):
|
||
|
world.debug.draw_string(spawn_point.location, str(i), life_time=10)
|
||
|
|
||
|
# In synchronous mode, we need to run the simulation to fly the spectator
|
||
|
while True:
|
||
|
world.tick()
|
||
|
```
|
||
|
|
||
|
We choose our spawn points and waypoints to create two converging streams of traffic within the town, creating congestion, which might be an interesting scenario to present to an autonomous driving agent.
|
||
|
|
||
|
```py
|
||
|
spawn_points = world.get_map().get_spawn_points()
|
||
|
|
||
|
# Route 1
|
||
|
spawn_point_1 = spawn_points[32]
|
||
|
# Create route 1 from the chosen spawn points
|
||
|
route_1_indices = [129, 28, 124, 33, 97, 119, 58, 154, 147]
|
||
|
route_1 = []
|
||
|
for ind in route_1_indices:
|
||
|
route_1.append(spawn_points[ind].location)
|
||
|
|
||
|
# Route 2
|
||
|
spawn_point_2 = spawn_points[149]
|
||
|
# Create route 2 from the chosen spawn points
|
||
|
route_2_indices = [21, 76, 38, 34, 90, 3]
|
||
|
route_2 = []
|
||
|
for ind in route_2_indices:
|
||
|
route_2.append(spawn_points[ind].location)
|
||
|
|
||
|
# Now let's print them in the map so we can see our routes
|
||
|
world.debug.draw_string(spawn_point_1.location, 'Spawn point 1', life_time=30, color=carla.Color(255,0,0))
|
||
|
world.debug.draw_string(spawn_point_2.location, 'Spawn point 2', life_time=30, color=carla.Color(0,0,255))
|
||
|
|
||
|
for ind in route_1_indices:
|
||
|
spawn_points[ind].location
|
||
|
world.debug.draw_string(spawn_points[ind].location, str(ind), life_time=60, color=carla.Color(255,0,0))
|
||
|
|
||
|
for ind in route_2_indices:
|
||
|
spawn_points[ind].location
|
||
|
world.debug.draw_string(spawn_points[ind].location, str(ind), life_time=60, color=carla.Color(0,0,255))
|
||
|
|
||
|
while True:
|
||
|
world.tick()
|
||
|
|
||
|
```
|
||
|
|
||
|
|
||
|
![routes](../img/tuto_G_traffic_manager/set_paths.png)
|
||
|
|
||
|
Now that we have chosen our spawn points and way points, we can now start spawning traffic and setting the spawned vehicles to follow our waypoint lists.
|
||
|
|
||
|
```py
|
||
|
|
||
|
# Set delay to create gap between spawn times
|
||
|
spawn_delay = 20
|
||
|
counter = spawn_delay
|
||
|
|
||
|
# Set max vehicles (set smaller for low hardward spec)
|
||
|
max_vehicles = 200
|
||
|
# Alternate between spawn points
|
||
|
alt = False
|
||
|
|
||
|
spawn_points = world.get_map().get_spawn_points()
|
||
|
while True:
|
||
|
world.tick()
|
||
|
|
||
|
n_vehicles = len(world.get_actors().filter('*vehicle*'))
|
||
|
vehicle_bp = random.choice(blueprints)
|
||
|
|
||
|
# Spawn vehicle only after delay
|
||
|
if counter == spawn_delay and n_vehicles < max_vehicles:
|
||
|
# Alternate spawn points
|
||
|
if alt:
|
||
|
vehicle = world.try_spawn_actor(vehicle_bp, spawn_point_1)
|
||
|
else:
|
||
|
vehicle = world.try_spawn_actor(vehicle_bp, spawn_point_2)
|
||
|
|
||
|
if vehicle: # IF vehicle is succesfully spawned
|
||
|
vehicle.set_autopilot(True) # Give TM control over vehicle
|
||
|
|
||
|
# Set parameters of TM vehicle control, we don't want lane changes
|
||
|
traffic_manager.update_vehicle_lights(vehicle, True)
|
||
|
traffic_manager.random_left_lanechange_percentage(vehicle, 0)
|
||
|
traffic_manager.random_right_lanechange_percentage(vehicle, 0)
|
||
|
traffic_manager.auto_lane_change(vehicle, False)
|
||
|
|
||
|
# Alternate between routes
|
||
|
if alt:
|
||
|
traffic_manager.set_path(vehicle, route_1)
|
||
|
alt = False
|
||
|
else:
|
||
|
traffic_manager.set_path(vehicle, route_2)
|
||
|
alt = True
|
||
|
|
||
|
vehicle = None
|
||
|
|
||
|
counter -= 1
|
||
|
elif counter > 0:
|
||
|
counter -= 1
|
||
|
elif counter == 0:
|
||
|
counter = spawn_delay
|
||
|
|
||
|
```
|
||
|
|
||
|
With the above code, we have created two converging streams of traffic originating from opposite sides of the map, guided by the `set_path()` function of the TM. This results in congestion on a road in the center of town. This kind of technique could be used on a larger scale to simulate multiple tricky cases for autonomous vehicles, such as a busy roundabout or highway intersection.
|
||
|
|
||
|
![converging_paths](../img/tuto_G_traffic_manager/converging_paths.gif)
|