carla/Docs/tuto_G_pygame.md

278 lines
11 KiB
Markdown
Raw Permalink Normal View History

Docs preview (#4983) * add UE4 warning * fixed UE4 build warning * removed file * created inst seg tutorial * added instance segmentation image * added to index * fix * added menu entries * texture streaming tutorial * reorganised instance segmentation tutorial * texture tutorial revision * typos * typos * added PIL link * added tooltip * remove tooltip image * replace tooltip image * removed tooltip image * added tooltip image * replaced image * texture streaming tutorial update * started pedestrian bones tutorial * added image * updates * updates * added download link for skeleton.txt * fixed link * fixed link * completed pedestrian tutorial * small change * small fix * TM tutorial * added new gif * typo * finished TM tutorial * small edit * small edit * typo * pygame tutorial * added gif to pygame tutorial * corrected gif location * replaced manual_control.gif * typos * fixed pygame tutorial * import numpy * pedestrian tutorial corrections * corrected pedestrian bones tutorial * added actors and blueprints * indexed getting started tutorial * fixed links * index.md refactor * mkdocs.yml nav refactor * mkdocs.yml syntax error * main docs refactor * new documentation structure * content authoring tutorials * content authoring tutorial * content authoring * latest changes * vehicle authoring tutorial * finished vehicle content tutorial * finished vehicles tutorial * adjusted outline * finalise authoring tutorials * rearrange index.md * extended index.md * change mkdocs format * update jinja version * bounding box tutorial * fix stray files * remove changes in build.sh * proof read * guillermo's pr edits * bbox tutorial changes * guillermo's pr edits 1 * added modeling guidelines and blender add on * added COCO export format * added bounding boxes to tutorials * merged bounding box tutorial Co-authored-by: germanros1987 <38517452+germanros1987@users.noreply.github.com>
2022-04-14 18:38:49 +08:00
# Pygame for vehicle control
[__PyGame__](https://www.pygame.org/news) is a cross-platform set of Python modules useful for writing video games. It provides a useful way of rendering real-time visual output from CARLA in order to monitor sensor output, such as cameras. PyGame can also capture keyboard events, so it is a good way to control actors such as vehicles.
In this tutorial, we will learn to set up a simple PyGame interface that allows us to monitor autonomous traffic driving around a map controlled by the Traffic Manager (TM) and then take manual control over any vehicle using the keyboard.
## 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
import pygame
import numpy as np
# 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
We want to create a collection of vehicles spawned throughout the city and give the TM control over them.
```py
# Retrieve the map's spawn points
spawn_points = world.get_map().get_spawn_points()
# 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)
# 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))
```
## Rendering camera output and controlling vehicles with PyGame
Now we have a city populated with traffic, we can set up a camera to follow one of the cars and set up a control interface to take over it with keyboard input.
Firstly, we need to define a callback function for `camera.listen(...)` to render the pixel data to the PyGame interface. PyGame renders data to surfaces, then the screen, so in the callback, we populate a surface stored in an object passed to the callback function.
```py
# Render object to keep and pass the PyGame surface
class RenderObject(object):
def __init__(self, width, height):
init_image = np.random.randint(0,255,(height,width,3),dtype='uint8')
self.surface = pygame.surfarray.make_surface(init_image.swapaxes(0,1))
# Camera sensor callback, reshapes raw data from camera into 2D RGB and applies to PyGame surface
def pygame_callback(data, obj):
img = np.reshape(np.copy(data.raw_data), (data.height, data.width, 4))
img = img[:,:,:3]
img = img[:, :, ::-1]
obj.surface = pygame.surfarray.make_surface(img.swapaxes(0,1))
```
Now we will create an object to handle the control logic. This can often need some tuning to meet specific needs, but this outlines a basic interface. When in control of a vehicle, this control interface allows control through the arrow keys on any standard keyboard. Forward arrow accelerates, backward arrow brakes and the left and right arrows turn the vehicle. If the down arrow is held when the vehicle is stationary or comes to a stop, it will engage reverse and start to move backwards.
```py
# Control object to manage vehicle controls
class ControlObject(object):
def __init__(self, veh):
# Conrol parameters to store the control state
self._vehicle = veh
self._steer = 0
self._throttle = False
self._brake = False
self._steer = None
self._steer_cache = 0
# A carla.VehicleControl object is needed to alter the
# vehicle's control state
self._control = carla.VehicleControl()
# Check for key press events in the PyGame window
# and define the control state
def parse_control(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
self._vehicle.set_autopilot(False)
if event.key == pygame.K_UP:
self._throttle = True
if event.key == pygame.K_DOWN:
self._brake = True
if event.key == pygame.K_RIGHT:
self._steer = 1
if event.key == pygame.K_LEFT:
self._steer = -1
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
self._throttle = False
if event.key == pygame.K_DOWN:
self._brake = False
self._control.reverse = False
if event.key == pygame.K_RIGHT:
self._steer = None
if event.key == pygame.K_LEFT:
self._steer = None
# Process the current control state, change the control parameter
# if the key remains pressed
def process_control(self):
if self._throttle:
self._control.throttle = min(self._control.throttle + 0.01, 1)
self._control.gear = 1
self._control.brake = False
elif not self._brake:
self._control.throttle = 0.0
if self._brake:
# If the down arrow is held down when the car is stationary, switch to reverse
if self._vehicle.get_velocity().length() < 0.01 and not self._control.reverse:
self._control.brake = 0.0
self._control.gear = 1
self._control.reverse = True
self._control.throttle = min(self._control.throttle + 0.1, 1)
elif self._control.reverse:
self._control.throttle = min(self._control.throttle + 0.1, 1)
else:
self._control.throttle = 0.0
self._control.brake = min(self._control.brake + 0.3, 1)
else:
self._control.brake = 0.0
if self._steer is not None:
if self._steer == 1:
self._steer_cache += 0.03
if self._steer == -1:
self._steer_cache -= 0.03
min(0.7, max(-0.7, self._steer_cache))
self._control.steer = round(self._steer_cache,1)
else:
if self._steer_cache > 0.0:
self._steer_cache *= 0.2
if self._steer_cache < 0.0:
self._steer_cache *= 0.2
if 0.01 > self._steer_cache > -0.01:
self._steer_cache = 0.0
self._control.steer = round(self._steer_cache,1)
# Ápply the control parameters to the ego vehicle
self._vehicle.apply_control(self._control)
```
Now we will initialise the vehicle and the camera.
```py
# Randomly select a vehicle to follow with the camera
ego_vehicle = random.choice(vehicles)
# Initialise the camera floating behind the vehicle
camera_init_trans = carla.Transform(carla.Location(x=-5, z=3), carla.Rotation(pitch=-20))
camera_bp = world.get_blueprint_library().find('sensor.camera.rgb')
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=ego_vehicle)
# Start camera with PyGame callback
camera.listen(lambda image: pygame_callback(image, renderObject))
# Get camera dimensions
image_w = camera_bp.get_attribute("image_size_x").as_int()
image_h = camera_bp.get_attribute("image_size_y").as_int()
# Instantiate objects for rendering and vehicle control
renderObject = RenderObject(image_w, image_h)
controlObject = ControlObject(ego_vehicle)
```
Initialise the PyGame interface. This will call up a new window for PyGame.
```py
# Initialise the display
pygame.init()
gameDisplay = pygame.display.set_mode((image_w,image_h), pygame.HWSURFACE | pygame.DOUBLEBUF)
# Draw black to the display
gameDisplay.fill((0,0,0))
gameDisplay.blit(renderObject.surface, (0,0))
pygame.display.flip()
```
Now we can start the game loop. The view can cycle randomly through different vehicles in the map and visualize their journey through the traffic while controlled by the TM. Pressing the TAB key switches to a randomly chosen new vehicle and pressing the RETURN key enables manual control of the vehicle through the arrow keys on the keyboard. This kind of setup could be useful for example if needing to challenge an agent with erratic driving behaviour. The selection logic could be tweaked to select a vehicle close to the one driven by the agent.
```py
# Game loop
crashed = False
while not crashed:
# Advance the simulation time
world.tick()
# Update the display
gameDisplay.blit(renderObject.surface, (0,0))
pygame.display.flip()
# Process the current control state
controlObject.process_control()
# Collect key press events
for event in pygame.event.get():
# If the window is closed, break the while loop
if event.type == pygame.QUIT:
crashed = True
# Parse effect of key press event on control state
controlObject.parse_control(event)
if event.type == pygame.KEYUP:
# TAB key switches vehicle
if event.key == pygame.K_TAB:
ego_vehicle.set_autopilot(True)
ego_vehicle = random.choice(vehicles)
# Ensure vehicle is still alive (might have been destroyed)
if ego_vehicle.is_alive:
# Stop and remove the camera
camera.stop()
camera.destroy()
# Spawn new camera and attach to new vehicle
controlObject = ControlObject(ego_vehicle)
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=ego_vehicle)
camera.listen(lambda image: pygame_callback(image, renderObject))
# Update PyGame window
gameDisplay.fill((0,0,0))
gameDisplay.blit(renderObject.surface, (0,0))
pygame.display.flip()
# Stop camera and quit PyGame after exiting game loop
camera.stop()
pygame.quit()
```
![manual_control](../img/tuto_G_pygame/manual_control.gif)