carla/PythonAPI/examples/manual_control.py

1100 lines
46 KiB
Python
Raw Normal View History

2018-07-30 05:52:13 +08:00
#!/usr/bin/env python
2019-03-29 17:58:05 +08:00
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de
2018-07-30 05:52:13 +08:00
# Barcelona (UAB).
#
# This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>.
# Allows controlling a vehicle with a keyboard. For a simpler and more
# documented example, please take a look at tutorial.py.
2018-07-30 05:52:13 +08:00
"""
Welcome to CARLA manual control.
Use ARROWS or WASD keys for control.
W : throttle
S : brake
A/D : steer left/right
2018-07-30 05:52:13 +08:00
Q : toggle reverse
Space : hand-brake
P : toggle autopilot
M : toggle manual transmission
,/. : gear up/down
2018-07-30 05:52:13 +08:00
L : toggle next light type
SHIFT + L : toggle high beam
Z/X : toggle right/left blinker
I : toggle interior light
2018-10-30 03:23:50 +08:00
TAB : change sensor position
` or N : next sensor
2018-10-30 03:23:50 +08:00
[1-9] : change to sensor [1-9]
G : toggle radar visualization
C : change weather (Shift+C reverse)
2018-10-22 06:30:35 +08:00
Backspace : change vehicle
2018-10-10 00:04:50 +08:00
R : toggle recording images to disk
2018-07-30 05:52:13 +08:00
2019-02-12 19:06:35 +08:00
CTRL + R : toggle recording of simulation (replacing any previous)
CTRL + P : start replaying last recorded simulation
CTRL + + : increments the start time of the replay by 1 second (+SHIFT = 10 seconds)
CTRL + - : decrements the start time of the replay by 1 second (+SHIFT = 10 seconds)
2018-11-09 00:12:55 +08:00
F1 : toggle HUD
2018-10-10 00:04:50 +08:00
H/? : toggle help
ESC : quit
"""
2018-10-02 22:11:21 +08:00
2018-07-30 05:52:13 +08:00
from __future__ import print_function
2018-10-10 00:04:50 +08:00
# ==============================================================================
# -- find carla module ---------------------------------------------------------
# ==============================================================================
import glob
2018-10-02 22:11:21 +08:00
import os
2018-10-10 00:04:50 +08:00
import sys
try:
2019-03-29 02:15:13 +08:00
sys.path.append(glob.glob('../carla/dist/carla-*%d.%d-%s.egg' % (
2018-10-10 00:04:50 +08:00
sys.version_info.major,
sys.version_info.minor,
'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0])
except IndexError:
pass
# ==============================================================================
# -- imports -------------------------------------------------------------------
# ==============================================================================
2018-10-02 22:11:21 +08:00
2018-07-30 05:52:13 +08:00
import carla
2018-10-10 00:04:50 +08:00
from carla import ColorConverter as cc
2018-07-30 05:52:13 +08:00
import argparse
2018-11-09 00:12:55 +08:00
import collections
import datetime
2018-07-30 05:52:13 +08:00
import logging
2018-11-09 00:12:55 +08:00
import math
2018-07-30 05:52:13 +08:00
import random
import re
2018-10-10 00:04:50 +08:00
import weakref
2018-10-02 22:11:21 +08:00
2018-07-30 05:52:13 +08:00
try:
import pygame
2018-10-10 00:04:50 +08:00
from pygame.locals import KMOD_CTRL
from pygame.locals import KMOD_SHIFT
from pygame.locals import K_0
from pygame.locals import K_9
from pygame.locals import K_BACKQUOTE
2018-10-22 06:30:35 +08:00
from pygame.locals import K_BACKSPACE
from pygame.locals import K_COMMA
2018-07-30 05:52:13 +08:00
from pygame.locals import K_DOWN
2018-10-10 00:04:50 +08:00
from pygame.locals import K_ESCAPE
2018-11-09 00:12:55 +08:00
from pygame.locals import K_F1
2018-07-30 05:52:13 +08:00
from pygame.locals import K_LEFT
from pygame.locals import K_PERIOD
2018-07-30 05:52:13 +08:00
from pygame.locals import K_RIGHT
2018-10-10 00:04:50 +08:00
from pygame.locals import K_SLASH
2018-07-30 05:52:13 +08:00
from pygame.locals import K_SPACE
2018-10-10 00:04:50 +08:00
from pygame.locals import K_TAB
2018-07-30 05:52:13 +08:00
from pygame.locals import K_UP
from pygame.locals import K_a
from pygame.locals import K_c
from pygame.locals import K_g
2018-07-30 05:52:13 +08:00
from pygame.locals import K_d
2018-10-10 00:04:50 +08:00
from pygame.locals import K_h
from pygame.locals import K_m
from pygame.locals import K_n
2018-07-30 05:52:13 +08:00
from pygame.locals import K_p
from pygame.locals import K_q
from pygame.locals import K_r
from pygame.locals import K_s
from pygame.locals import K_w
from pygame.locals import K_l
from pygame.locals import K_i
from pygame.locals import K_z
from pygame.locals import K_x
from pygame.locals import K_MINUS
from pygame.locals import K_EQUALS
2018-07-30 05:52:13 +08:00
except ImportError:
raise RuntimeError('cannot import pygame, make sure pygame package is installed')
try:
import numpy as np
except ImportError:
raise RuntimeError('cannot import numpy, make sure numpy package is installed')
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
# ==============================================================================
# -- Global functions ----------------------------------------------------------
2018-10-10 00:04:50 +08:00
# ==============================================================================
2018-07-30 05:52:13 +08:00
def find_weather_presets():
rgx = re.compile('.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)')
name = lambda x: ' '.join(m.group(0) for m in rgx.finditer(x))
presets = [x for x in dir(carla.WeatherParameters) if re.match('[A-Z].+', x)]
return [(getattr(carla.WeatherParameters, x), name(x)) for x in presets]
2018-11-09 00:12:55 +08:00
def get_actor_display_name(actor, truncate=250):
name = ' '.join(actor.type_id.replace('_', '.').title().split('.')[1:])
2019-03-19 19:20:59 +08:00
return (name[:truncate - 1] + u'\u2026') if len(name) > truncate else name
2018-11-09 00:12:55 +08:00
# ==============================================================================
# -- World ---------------------------------------------------------------------
# ==============================================================================
2018-10-10 00:04:50 +08:00
class World(object):
def __init__(self, carla_world, hud, args):
2018-10-22 06:30:35 +08:00
self.world = carla_world
self.actor_role_name = args.rolename
try:
self.map = self.world.get_map()
except RuntimeError as error:
print('RuntimeError: {}'.format(error))
print(' The server could not send the OpenDRIVE (.xodr) file:')
print(' Make sure it exists, has the same name of your town, and is correct.')
sys.exit(1)
2018-10-10 00:04:50 +08:00
self.hud = hud
self.player = None
self.collision_sensor = None
self.lane_invasion_sensor = None
self.gnss_sensor = None
self.imu_sensor = None
self.radar_sensor = None
self.camera_manager = None
self._weather_presets = find_weather_presets()
self._weather_index = 0
self._actor_filter = args.filter
self._gamma = args.gamma
self.restart()
self.world.on_tick(hud.on_world_tick)
self.recording_enabled = False
self.recording_start = 0
2018-10-22 06:30:35 +08:00
def restart(self):
self.player_max_speed = 1.589
self.player_max_speed_fast = 3.713
# Keep same camera config if the camera manager exists.
2019-02-04 19:05:58 +08:00
cam_index = self.camera_manager.index if self.camera_manager is not None else 0
cam_pos_index = self.camera_manager.transform_index if self.camera_manager is not None else 0
# Get a random blueprint.
blueprint = random.choice(self.world.get_blueprint_library().filter(self._actor_filter))
blueprint.set_attribute('role_name', self.actor_role_name)
if blueprint.has_attribute('color'):
color = random.choice(blueprint.get_attribute('color').recommended_values)
blueprint.set_attribute('color', color)
if blueprint.has_attribute('driver_id'):
driver_id = random.choice(blueprint.get_attribute('driver_id').recommended_values)
blueprint.set_attribute('driver_id', driver_id)
if blueprint.has_attribute('is_invincible'):
blueprint.set_attribute('is_invincible', 'true')
# set the max speed
if blueprint.has_attribute('speed'):
self.player_max_speed = float(blueprint.get_attribute('speed').recommended_values[1])
self.player_max_speed_fast = float(blueprint.get_attribute('speed').recommended_values[2])
else:
print("No recommended values for 'speed' attribute")
# Spawn the player.
if self.player is not None:
spawn_point = self.player.get_transform()
spawn_point.location.z += 2.0
spawn_point.rotation.roll = 0.0
spawn_point.rotation.pitch = 0.0
self.destroy()
self.player = self.world.try_spawn_actor(blueprint, spawn_point)
while self.player is None:
if not self.map.get_spawn_points():
print('There are no spawn points available in your map/town.')
print('Please add some Vehicle Spawn Point to your UE4 scene.')
sys.exit(1)
2019-02-19 02:48:50 +08:00
spawn_points = self.map.get_spawn_points()
spawn_point = random.choice(spawn_points) if spawn_points else carla.Transform()
self.player = self.world.try_spawn_actor(blueprint, spawn_point)
# Set up the sensors.
self.collision_sensor = CollisionSensor(self.player, self.hud)
self.lane_invasion_sensor = LaneInvasionSensor(self.player, self.hud)
self.gnss_sensor = GnssSensor(self.player)
self.imu_sensor = IMUSensor(self.player)
self.camera_manager = CameraManager(self.player, self.hud, self._gamma)
2019-02-04 19:05:58 +08:00
self.camera_manager.transform_index = cam_pos_index
2018-10-22 06:30:35 +08:00
self.camera_manager.set_sensor(cam_index, notify=False)
actor_type = get_actor_display_name(self.player)
2018-10-22 06:30:35 +08:00
self.hud.notification(actor_type)
def next_weather(self, reverse=False):
self._weather_index += -1 if reverse else 1
self._weather_index %= len(self._weather_presets)
preset = self._weather_presets[self._weather_index]
self.hud.notification('Weather: %s' % preset[1])
self.player.get_world().set_weather(preset[0])
2018-10-10 00:04:50 +08:00
def toggle_radar(self):
if self.radar_sensor is None:
self.radar_sensor = RadarSensor(self.player)
elif self.radar_sensor.sensor is not None:
self.radar_sensor.sensor.destroy()
self.radar_sensor = None
2018-10-10 00:04:50 +08:00
def tick(self, clock):
self.hud.tick(self, clock)
def render(self, display):
self.camera_manager.render(display)
self.hud.render(display)
2019-02-04 19:05:58 +08:00
def destroy_sensors(self):
self.camera_manager.sensor.destroy()
self.camera_manager.sensor = None
self.camera_manager.index = None
2018-10-10 00:04:50 +08:00
def destroy(self):
if self.radar_sensor is not None:
self.toggle_radar()
2018-11-15 03:29:38 +08:00
actors = [
self.camera_manager.sensor,
self.collision_sensor.sensor,
self.lane_invasion_sensor.sensor,
self.gnss_sensor.sensor,
self.imu_sensor.sensor,
self.player]
2018-11-15 03:29:38 +08:00
for actor in actors:
2018-10-10 00:04:50 +08:00
if actor is not None:
actor.destroy()
2018-10-10 00:04:50 +08:00
# ==============================================================================
# -- KeyboardControl -----------------------------------------------------------
# ==============================================================================
class KeyboardControl(object):
"""Class that handles keyboard input."""
2018-10-10 00:04:50 +08:00
def __init__(self, world, start_in_autopilot):
self._autopilot_enabled = start_in_autopilot
if isinstance(world.player, carla.Vehicle):
self._control = carla.VehicleControl()
self._lights = carla.VehicleLightState.NONE
world.player.set_autopilot(self._autopilot_enabled)
world.player.set_light_state(self._lights)
elif isinstance(world.player, carla.Walker):
self._control = carla.WalkerControl()
self._autopilot_enabled = False
self._rotation = world.player.get_transform().rotation
else:
raise NotImplementedError("Actor type not supported")
2018-10-10 00:04:50 +08:00
self._steer_cache = 0.0
world.hud.notification("Press 'H' or '?' for help.", seconds=4.0)
def parse_events(self, client, world, clock):
if isinstance(self._control, carla.VehicleControl):
current_lights = self._lights
2018-10-10 00:04:50 +08:00
for event in pygame.event.get():
if event.type == pygame.QUIT:
return True
elif event.type == pygame.KEYUP:
if self._is_quit_shortcut(event.key):
return True
2018-10-22 06:30:35 +08:00
elif event.key == K_BACKSPACE:
Doterop/traffic manager (#2468) * Initial implementation of t.m. performance script * Remove redundant getLocation() calls * Demo for inter-client communication via Carla server * WIP: To do: For client usage needed to be changed. * Instead or client instance episodeProxy is passed to TM * Instead or client instance episodeProxy is passed to TM * parmeter improvements (walkers,cars,signs,lights) * adding section id to map setup * fix manual_control to reload car with autopilot on * Instead of client instance episodeProxy is passed to TM. * WIP: Only added vehicle register via RPC server / client call in TM to local instance from remote instance. Similarly other APIs we need to added. * Added all TM APIs * carla client now can provide TM instance if required. * While getting TM instance if server given data is not valid new TM instace is created. Need to decide what to do with earlier registed vehicle with previous TM. * Updated with HealthCheckRemoteTM() function to check TM server status instead of ResetAllTrafficLights() for any new user client. * Dynamic port selection added to TM server instace (in case of multiple TM serve run, it required). * Fixed change map error * If registered TM at carla server is not present then remote TM should be freed and new local TM instance is created. * Created Process for Local TM. It cheaks with total registered vehicles in it, if no register vehicles for 5 sec, it closes it self. In python spawn_npc.py Client Timeout set to 10 sec. as creating Process is system dependent call and takes time. User has to unregister vehicle before destroying it (if activated with TM). Also neet to check TM unregister function to remove vehicles from register list. * Fix for collision ignore bug * Update TM exit function check to compare registered vehicles with world actors that if any valid vehicle present cointinue to run the TM else stop. * Print removal * Splited work of main for loop in ApplyBatchCommandsSync * WIP! Trying to get client directly * WIP! Trying to access episode properly without getting it from TM ctr * Changes to BatchControlStage for Sync Mode Changes to Parameters for Synch Mode * Added port support for TM. Multiclient MultiTM * Added additions for RPC Synchronous Tick to TM. Error handeling and code organization. * Exposed API for Sync Mode * TMServer notifies Server that it is gonna be destroyed * Exposed Python API for Sync Mode * Add TM as separate process and synchronous tick calls * SetSynchronousModeTimeOutInMiliSecond method added * TM shutsdown and informs to the connected clients * WIP! Map change issues again * Updated Traffic Manager for: 1) Multi-client, Multi-TM (as required) can be reistered to server. 2) TM is not a PROCESS separated from the user-client. 3) TM will exit if user-client exit. 4) If server closed, TM also get closed by catching runtime err. * Server side changes ... * Fixed possible stall on TM destruction * fixing collision stage + cybertruck not safe * Merge branch 'master' into 'soumyadeep/traffic_manager' * WIP! Disconnection of server has to be properly handled by clients * format update * Fix bug unsignalized junctions * # WARNING: head commit changed in the meantime Merge branch 'doterop/traffic_manager' into soumyadeep/traffic_manager Updates for Syncronized Tick. * Updated runtime exception in Episode for smooth exit if TN server closed for any other user client. * Better Exception handeling * Merged with jackbart94/tm_reduce_getloc_calls * Merged soumyadeep/traffic_manager. Removed World getter and recovered Episode as a parameter of TM ctr. Fixed syntax errors. * Initial implementation of t.m. performance script * Remove redundant getLocation() calls * Demo for inter-client communication via Carla server * WIP: To do: For client usage needed to be changed. * Instead or client instance episodeProxy is passed to TM * Instead or client instance episodeProxy is passed to TM * parmeter improvements (walkers,cars,signs,lights) * fix manual_control to reload car with autopilot on * Instead of client instance episodeProxy is passed to TM. * WIP: Only added vehicle register via RPC server / client call in TM to local instance from remote instance. Similarly other APIs we need to added. * Added all TM APIs * carla client now can provide TM instance if required. * While getting TM instance if server given data is not valid new TM instace is created. Need to decide what to do with earlier registed vehicle with previous TM. * Updated with HealthCheckRemoteTM() function to check TM server status instead of ResetAllTrafficLights() for any new user client. * Dynamic port selection added to TM server instace (in case of multiple TM serve run, it required). * Fixed change map error * If registered TM at carla server is not present then remote TM should be freed and new local TM instance is created. * Created Process for Local TM. It cheaks with total registered vehicles in it, if no register vehicles for 5 sec, it closes it self. In python spawn_npc.py Client Timeout set to 10 sec. as creating Process is system dependent call and takes time. User has to unregister vehicle before destroying it (if activated with TM). Also neet to check TM unregister function to remove vehicles from register list. * Fix for collision ignore bug * Update TM exit function check to compare registered vehicles with world actors that if any valid vehicle present cointinue to run the TM else stop. * Splited work of main for loop in ApplyBatchCommandsSync * WIP! Trying to get client directly * WIP! Trying to access episode properly without getting it from TM ctr * Changes to BatchControlStage for Sync Mode Changes to Parameters for Synch Mode * Added port support for TM. Multiclient MultiTM * Added additions for RPC Synchronous Tick to TM. Error handeling and code organization. * Exposed API for Sync Mode * TMServer notifies Server that it is gonna be destroyed * Exposed Python API for Sync Mode * Add TM as separate process and synchronous tick calls * SetSynchronousModeTimeOutInMiliSecond method added * TM shutsdown and informs to the connected clients * WIP! Map change issues again * Updated Traffic Manager for: 1) Multi-client, Multi-TM (as required) can be reistered to server. 2) TM is not a PROCESS separated from the user-client. 3) TM will exit if user-client exit. 4) If server closed, TM also get closed by catching runtime err. * Fixed possible stall on TM destruction * fixing collision stage + cybertruck not safe * Merge branch 'master' into 'soumyadeep/traffic_manager' * WIP! Disconnection of server has to be properly handled by clients * format update * Fix bug unsignalized junctions * # WARNING: head commit changed in the meantime Merge branch 'doterop/traffic_manager' into soumyadeep/traffic_manager Updates for Syncronized Tick. * Updated runtime exception in Episode for smooth exit if TN server closed for any other user client. * Better Exception handeling * Merged with jackbart94/tm_reduce_getloc_calls * Merged soumyadeep/traffic_manager. Removed World getter and recovered Episode as a parameter of TM ctr. Fixed syntax errors. * Rebased with master * Changes after rebase * Solving tab errors * Updated Changelog * Removing Destroyed vehicle's from TM Server * Fixed uint compatibility with Windows * Merged soumyadeep/traffic_manager. Discarded destroyed actors * -Removed unnecessary files * restoring docs from rebase * Fix windows compilation * refactoring stage-related code * more code refactoring * When map change the simulation doesn't throw exception anymore * Fixed incorrect episode * Fixed map change and TM remote detached thread destruction * Syntax and comments fixes * Missed change on previous commit * Fixed compile minor compile issue * Cleaned and fixed some issues after merge * fix to sync localization bugs modified PID parameters revamping spawn_npc * deleted tm_spawn_npc * fixes spawn error in sync mode * Redoing TM sync logic * finished performance benchmark for tm * deprecated wrapped methods: register_vehicle unregister_vehicle * New TM management * Fixed sync mode on TM * Cleaned TM of prints and unussed functions * collision stage checks for (0,0,0) to ignore. in memory map has an # between keys to avoid possible mixup. fixed spawn_npc with new sync mode * changelog * added more connection retries * fixed changelog + comments (see reviewable) * Moved socket include's to single header * Added missing line at the end of the file * Fixed syntax errors * final commit * Minor correction in comment * update copyright year to 2020 + removed break * restoring unwanted changes * patch for smoke test error * Removed "todo" for pylint Co-authored-by: Jacopo Bartiromo <32928804+jackbart94@users.noreply.github.com> Co-authored-by: Praveen Kumar <35625166+pravinblaze@users.noreply.github.com> Co-authored-by: Soumyadeep <soumyadeep.dhar@kpit.com> Co-authored-by: joel-mb <joel.moriana@gmail.com> Co-authored-by: Sekhar Barua <58979936+sekhar2912@users.noreply.github.com> Co-authored-by: bernat <bernatx@gmail.com> Co-authored-by: Marc Garcia Puig <marcgpuig@gmail.com>
2020-02-29 02:58:13 +08:00
if self._autopilot_enabled:
world.player.set_autopilot(False)
world.restart()
world.player.set_autopilot(True)
else:
world.restart()
2018-11-09 00:12:55 +08:00
elif event.key == K_F1:
world.hud.toggle_info()
2018-10-10 00:04:50 +08:00
elif event.key == K_h or (event.key == K_SLASH and pygame.key.get_mods() & KMOD_SHIFT):
world.hud.help.toggle()
elif event.key == K_TAB:
world.camera_manager.toggle_camera()
elif event.key == K_c and pygame.key.get_mods() & KMOD_SHIFT:
world.next_weather(reverse=True)
elif event.key == K_c:
world.next_weather()
elif event.key == K_g:
world.toggle_radar()
2018-10-10 00:04:50 +08:00
elif event.key == K_BACKQUOTE:
world.camera_manager.next_sensor()
elif event.key == K_n:
world.camera_manager.next_sensor()
2018-10-10 00:04:50 +08:00
elif event.key > K_0 and event.key <= K_9:
2018-10-30 03:23:50 +08:00
world.camera_manager.set_sensor(event.key - 1 - K_0)
elif event.key == K_r and not (pygame.key.get_mods() & KMOD_CTRL):
2018-10-10 00:04:50 +08:00
world.camera_manager.toggle_recording()
elif event.key == K_r and (pygame.key.get_mods() & KMOD_CTRL):
if (world.recording_enabled):
client.stop_recorder()
world.recording_enabled = False
world.hud.notification("Recorder is OFF")
else:
client.start_recorder("manual_recording.rec")
world.recording_enabled = True
world.hud.notification("Recorder is ON")
elif event.key == K_p and (pygame.key.get_mods() & KMOD_CTRL):
# stop recorder
client.stop_recorder()
world.recording_enabled = False
# work around to fix camera at start of replaying
current_index = world.camera_manager.index
2019-02-04 19:05:58 +08:00
world.destroy_sensors()
# disable autopilot
self._autopilot_enabled = False
world.player.set_autopilot(self._autopilot_enabled)
world.hud.notification("Replaying file 'manual_recording.rec'")
# replayer
client.replay_file("manual_recording.rec", world.recording_start, 0, 0)
world.camera_manager.set_sensor(current_index)
elif event.key == K_MINUS and (pygame.key.get_mods() & KMOD_CTRL):
if pygame.key.get_mods() & KMOD_SHIFT:
world.recording_start -= 10
else:
world.recording_start -= 1
world.hud.notification("Recording start time is %d" % (world.recording_start))
elif event.key == K_EQUALS and (pygame.key.get_mods() & KMOD_CTRL):
if pygame.key.get_mods() & KMOD_SHIFT:
world.recording_start += 10
else:
world.recording_start += 1
world.hud.notification("Recording start time is %d" % (world.recording_start))
if isinstance(self._control, carla.VehicleControl):
if event.key == K_q:
self._control.gear = 1 if self._control.reverse else -1
elif event.key == K_m:
self._control.manual_gear_shift = not self._control.manual_gear_shift
self._control.gear = world.player.get_control().gear
2019-03-19 19:20:59 +08:00
world.hud.notification('%s Transmission' %
('Manual' if self._control.manual_gear_shift else 'Automatic'))
elif self._control.manual_gear_shift and event.key == K_COMMA:
self._control.gear = max(-1, self._control.gear - 1)
elif self._control.manual_gear_shift and event.key == K_PERIOD:
self._control.gear = self._control.gear + 1
elif event.key == K_p and not pygame.key.get_mods() & KMOD_CTRL:
self._autopilot_enabled = not self._autopilot_enabled
world.player.set_autopilot(self._autopilot_enabled)
world.hud.notification(
'Autopilot %s' % ('On' if self._autopilot_enabled else 'Off'))
elif event.key == K_l and pygame.key.get_mods() & KMOD_CTRL:
current_lights ^= carla.VehicleLightState.Special1
elif event.key == K_l and pygame.key.get_mods() & KMOD_SHIFT:
current_lights ^= carla.VehicleLightState.HighBeam
elif event.key == K_l:
# Use 'L' key to switch between lights:
# closed -> position -> low beam -> fog
if not self._lights & carla.VehicleLightState.Position:
world.hud.notification("Position lights")
current_lights |= carla.VehicleLightState.Position
else:
world.hud.notification("Low beam lights")
current_lights |= carla.VehicleLightState.LowBeam
if self._lights & carla.VehicleLightState.LowBeam:
world.hud.notification("Fog lights")
current_lights |= carla.VehicleLightState.Fog
if self._lights & carla.VehicleLightState.Fog:
world.hud.notification("Lights off")
current_lights ^= carla.VehicleLightState.Position
current_lights ^= carla.VehicleLightState.LowBeam
current_lights ^= carla.VehicleLightState.Fog
elif event.key == K_i:
current_lights ^= carla.VehicleLightState.Interior
elif event.key == K_z:
current_lights ^= carla.VehicleLightState.LeftBlinker
elif event.key == K_x:
current_lights ^= carla.VehicleLightState.RightBlinker
2018-10-10 00:04:50 +08:00
if not self._autopilot_enabled:
if isinstance(self._control, carla.VehicleControl):
self._parse_vehicle_keys(pygame.key.get_pressed(), clock.get_time())
self._control.reverse = self._control.gear < 0
# Set automatic control-related vehicle lights
if self._control.brake:
current_lights |= carla.VehicleLightState.Brake
else: # Remove the Brake flag
current_lights &= carla.VehicleLightState.All ^ carla.VehicleLightState.Brake
if self._control.reverse:
current_lights |= carla.VehicleLightState.Reverse
else: # Remove the Reverse flag
current_lights &= carla.VehicleLightState.All ^ carla.VehicleLightState.Reverse
if current_lights != self._lights: # Change the light state only if necessary
self._lights = current_lights
world.player.set_light_state(carla.VehicleLightState(self._lights))
elif isinstance(self._control, carla.WalkerControl):
self._parse_walker_keys(pygame.key.get_pressed(), clock.get_time(), world)
world.player.apply_control(self._control)
def _parse_vehicle_keys(self, keys, milliseconds):
2018-10-10 00:04:50 +08:00
self._control.throttle = 1.0 if keys[K_UP] or keys[K_w] else 0.0
steer_increment = 5e-4 * milliseconds
if keys[K_LEFT] or keys[K_a]:
if self._steer_cache > 0:
self._steer_cache = 0
else:
self._steer_cache -= steer_increment
2018-10-10 00:04:50 +08:00
elif keys[K_RIGHT] or keys[K_d]:
if self._steer_cache < 0:
self._steer_cache = 0
else:
self._steer_cache += steer_increment
2018-10-10 00:04:50 +08:00
else:
self._steer_cache = 0.0
self._steer_cache = min(0.7, max(-0.7, self._steer_cache))
self._control.steer = round(self._steer_cache, 1)
self._control.brake = 1.0 if keys[K_DOWN] or keys[K_s] else 0.0
self._control.hand_brake = keys[K_SPACE]
def _parse_walker_keys(self, keys, milliseconds, world):
self._control.speed = 0.0
if keys[K_DOWN] or keys[K_s]:
self._control.speed = 0.0
if keys[K_LEFT] or keys[K_a]:
self._control.speed = .01
self._rotation.yaw -= 0.08 * milliseconds
if keys[K_RIGHT] or keys[K_d]:
self._control.speed = .01
self._rotation.yaw += 0.08 * milliseconds
if keys[K_UP] or keys[K_w]:
self._control.speed = world.player_max_speed_fast if pygame.key.get_mods() & KMOD_SHIFT else world.player_max_speed
self._control.jump = keys[K_SPACE]
self._rotation.yaw = round(self._rotation.yaw, 1)
self._control.direction = self._rotation.get_forward_vector()
2018-10-10 00:04:50 +08:00
@staticmethod
def _is_quit_shortcut(key):
return (key == K_ESCAPE) or (key == K_q and pygame.key.get_mods() & KMOD_CTRL)
# ==============================================================================
# -- HUD -----------------------------------------------------------------------
# ==============================================================================
class HUD(object):
def __init__(self, width, height):
self.dim = (width, height)
font = pygame.font.Font(pygame.font.get_default_font(), 20)
2019-10-28 19:02:00 +08:00
font_name = 'courier' if os.name == 'nt' else 'mono'
fonts = [x for x in pygame.font.get_fonts() if font_name in x]
2018-11-16 01:05:44 +08:00
default_font = 'ubuntumono'
mono = default_font if default_font in fonts else fonts[0]
mono = pygame.font.match_font(mono)
2019-10-28 19:02:00 +08:00
self._font_mono = pygame.font.Font(mono, 12 if os.name == 'nt' else 14)
2018-10-10 00:04:50 +08:00
self._notifications = FadingText(font, (width, 40), (0, height - 40))
self.help = HelpText(pygame.font.Font(mono, 16), width, height)
2018-10-10 00:04:50 +08:00
self.server_fps = 0
self.frame = 0
2018-11-09 00:12:55 +08:00
self.simulation_time = 0
self._show_info = True
self._info_text = []
self._server_clock = pygame.time.Clock()
def on_world_tick(self, timestamp):
self._server_clock.tick()
2019-02-01 00:23:45 +08:00
self.server_fps = self._server_clock.get_fps()
self.frame = timestamp.frame
2018-11-09 00:12:55 +08:00
self.simulation_time = timestamp.elapsed_seconds
2018-10-10 00:04:50 +08:00
def tick(self, world, clock):
self._notifications.tick(world, clock)
2018-11-09 00:12:55 +08:00
if not self._show_info:
return
t = world.player.get_transform()
v = world.player.get_velocity()
c = world.player.get_control()
compass = world.imu_sensor.compass
heading = 'N' if compass > 270.5 or compass < 89.5 else ''
heading += 'S' if 90.5 < compass < 269.5 else ''
heading += 'E' if 0.5 < compass < 179.5 else ''
heading += 'W' if 180.5 < compass < 359.5 else ''
2018-11-09 00:12:55 +08:00
colhist = world.collision_sensor.get_collision_history()
collision = [colhist[x + self.frame - 200] for x in range(0, 200)]
2018-11-09 00:12:55 +08:00
max_col = max(1.0, max(collision))
collision = [x / max_col for x in collision]
2018-11-16 01:05:44 +08:00
vehicles = world.world.get_actors().filter('vehicle.*')
2018-11-09 00:12:55 +08:00
self._info_text = [
'Server: % 16.0f FPS' % self.server_fps,
'Client: % 16.0f FPS' % clock.get_fps(),
2018-11-16 01:05:44 +08:00
'',
'Vehicle: % 20s' % get_actor_display_name(world.player, truncate=20),
2019-02-19 02:48:50 +08:00
'Map: % 20s' % world.map.name,
2018-11-16 01:05:44 +08:00
'Simulation time: % 12s' % datetime.timedelta(seconds=int(self.simulation_time)),
2018-11-09 00:12:55 +08:00
'',
2018-11-16 01:05:44 +08:00
'Speed: % 15.0f km/h' % (3.6 * math.sqrt(v.x**2 + v.y**2 + v.z**2)),
u'Compass:% 17.0f\N{DEGREE SIGN} % 2s' % (compass, heading),
2019-12-11 18:27:17 +08:00
'Accelero: (%5.1f,%5.1f,%5.1f)' % (world.imu_sensor.accelerometer),
'Gyroscop: (%5.1f,%5.1f,%5.1f)' % (world.imu_sensor.gyroscope),
2018-11-16 01:05:44 +08:00
'Location:% 20s' % ('(% 5.1f, % 5.1f)' % (t.location.x, t.location.y)),
'GNSS:% 24s' % ('(% 2.6f, % 3.6f)' % (world.gnss_sensor.lat, world.gnss_sensor.lon)),
2018-11-16 01:05:44 +08:00
'Height: % 18.0f m' % t.location.z,
'']
if isinstance(c, carla.VehicleControl):
self._info_text += [
('Throttle:', c.throttle, 0.0, 1.0),
('Steer:', c.steer, -1.0, 1.0),
('Brake:', c.brake, 0.0, 1.0),
('Reverse:', c.reverse),
('Hand brake:', c.hand_brake),
('Manual:', c.manual_gear_shift),
'Gear: %s' % {-1: 'R', 0: 'N'}.get(c.gear, c.gear)]
elif isinstance(c, carla.WalkerControl):
self._info_text += [
('Speed:', c.speed, 0.0, 5.556),
('Jump:', c.jump)]
self._info_text += [
2018-11-09 00:12:55 +08:00
'',
2018-11-16 01:05:44 +08:00
'Collision:',
collision,
2018-11-09 00:12:55 +08:00
'',
'Number of vehicles: % 8d' % len(vehicles)]
2018-11-09 00:12:55 +08:00
if len(vehicles) > 1:
2018-11-16 01:05:44 +08:00
self._info_text += ['Nearby vehicles:']
2018-11-09 00:12:55 +08:00
distance = lambda l: math.sqrt((l.x - t.location.x)**2 + (l.y - t.location.y)**2 + (l.z - t.location.z)**2)
vehicles = [(distance(x.get_location()), x) for x in vehicles if x.id != world.player.id]
2018-11-09 00:12:55 +08:00
for d, vehicle in sorted(vehicles):
if d > 200.0:
break
vehicle_type = get_actor_display_name(vehicle, truncate=22)
self._info_text.append('% 4dm %s' % (d, vehicle_type))
2018-10-10 00:04:50 +08:00
2018-11-09 00:12:55 +08:00
def toggle_info(self):
self._show_info = not self._show_info
2018-10-10 00:04:50 +08:00
def notification(self, text, seconds=2.0):
self._notifications.set_text(text, seconds=seconds)
def error(self, text):
self._notifications.set_text('Error: %s' % text, (255, 0, 0))
def render(self, display):
2018-11-09 00:12:55 +08:00
if self._show_info:
info_surface = pygame.Surface((220, self.dim[1]))
info_surface.set_alpha(100)
display.blit(info_surface, (0, 0))
v_offset = 4
bar_h_offset = 100
bar_width = 106
for item in self._info_text:
if v_offset + 18 > self.dim[1]:
break
if isinstance(item, list):
if len(item) > 1:
points = [(x + 8, v_offset + 8 + (1.0 - y) * 30) for x, y in enumerate(item)]
pygame.draw.lines(display, (255, 136, 0), False, points, 2)
item = None
v_offset += 18
elif isinstance(item, tuple):
if isinstance(item[1], bool):
rect = pygame.Rect((bar_h_offset, v_offset + 8), (6, 6))
pygame.draw.rect(display, (255, 255, 255), rect, 0 if item[1] else 1)
else:
rect_border = pygame.Rect((bar_h_offset, v_offset + 8), (bar_width, 6))
pygame.draw.rect(display, (255, 255, 255), rect_border, 1)
f = (item[1] - item[2]) / (item[3] - item[2])
if item[2] < 0.0:
rect = pygame.Rect((bar_h_offset + f * (bar_width - 6), v_offset + 8), (6, 6))
else:
rect = pygame.Rect((bar_h_offset, v_offset + 8), (f * bar_width, 6))
pygame.draw.rect(display, (255, 255, 255), rect)
item = item[0]
2019-03-19 19:20:59 +08:00
if item: # At this point has to be a str.
2018-11-09 00:12:55 +08:00
surface = self._font_mono.render(item, True, (255, 255, 255))
display.blit(surface, (8, v_offset))
v_offset += 18
2018-10-10 00:04:50 +08:00
self._notifications.render(display)
self.help.render(display)
# ==============================================================================
# -- FadingText ----------------------------------------------------------------
# ==============================================================================
class FadingText(object):
def __init__(self, font, dim, pos):
self.font = font
self.dim = dim
self.pos = pos
self.seconds_left = 0
self.surface = pygame.Surface(self.dim)
def set_text(self, text, color=(255, 255, 255), seconds=2.0):
text_texture = self.font.render(text, True, color)
self.surface = pygame.Surface(self.dim)
self.seconds_left = seconds
self.surface.fill((0, 0, 0, 0))
self.surface.blit(text_texture, (10, 11))
def tick(self, _, clock):
delta_seconds = 1e-3 * clock.get_time()
self.seconds_left = max(0.0, self.seconds_left - delta_seconds)
self.surface.set_alpha(500.0 * self.seconds_left)
def render(self, display):
display.blit(self.surface, self.pos)
# ==============================================================================
# -- HelpText ------------------------------------------------------------------
# ==============================================================================
class HelpText(object):
"""Helper class to handle text output using pygame"""
2018-10-10 00:04:50 +08:00
def __init__(self, font, width, height):
2018-10-22 06:30:35 +08:00
lines = __doc__.split('\n')
2018-10-10 00:04:50 +08:00
self.font = font
self.line_space = 18
self.dim = (780, len(lines) * self.line_space + 12)
2018-10-10 00:04:50 +08:00
self.pos = (0.5 * width - 0.5 * self.dim[0], 0.5 * height - 0.5 * self.dim[1])
self.seconds_left = 0
self.surface = pygame.Surface(self.dim)
self.surface.fill((0, 0, 0, 0))
2018-10-22 06:30:35 +08:00
for n, line in enumerate(lines):
2018-10-10 00:04:50 +08:00
text_texture = self.font.render(line, True, (255, 255, 255))
self.surface.blit(text_texture, (22, n * self.line_space))
2018-10-10 00:04:50 +08:00
self._render = False
self.surface.set_alpha(220)
def toggle(self):
self._render = not self._render
def render(self, display):
if self._render:
display.blit(self.surface, self.pos)
2018-10-21 02:33:37 +08:00
# ==============================================================================
# -- CollisionSensor -----------------------------------------------------------
# ==============================================================================
class CollisionSensor(object):
def __init__(self, parent_actor, hud):
self.sensor = None
2019-02-04 19:05:58 +08:00
self.history = []
2018-10-21 02:33:37 +08:00
self._parent = parent_actor
2019-02-04 19:05:58 +08:00
self.hud = hud
2018-10-21 02:33:37 +08:00
world = self._parent.get_world()
bp = world.get_blueprint_library().find('sensor.other.collision')
self.sensor = world.spawn_actor(bp, carla.Transform(), attach_to=self._parent)
# We need to pass the lambda a weak reference to self to avoid circular
# reference.
weak_self = weakref.ref(self)
self.sensor.listen(lambda event: CollisionSensor._on_collision(weak_self, event))
2018-11-09 00:12:55 +08:00
def get_collision_history(self):
history = collections.defaultdict(int)
2019-02-04 19:05:58 +08:00
for frame, intensity in self.history:
2018-11-09 00:12:55 +08:00
history[frame] += intensity
return history
2018-10-21 02:33:37 +08:00
@staticmethod
def _on_collision(weak_self, event):
self = weak_self()
if not self:
return
actor_type = get_actor_display_name(event.other_actor)
2019-02-04 19:05:58 +08:00
self.hud.notification('Collision with %r' % actor_type)
2018-11-09 00:12:55 +08:00
impulse = event.normal_impulse
intensity = math.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2)
self.history.append((event.frame, intensity))
2019-02-04 19:05:58 +08:00
if len(self.history) > 4000:
self.history.pop(0)
2018-10-21 02:33:37 +08:00
# ==============================================================================
# -- LaneInvasionSensor --------------------------------------------------------
# ==============================================================================
class LaneInvasionSensor(object):
def __init__(self, parent_actor, hud):
self.sensor = None
self._parent = parent_actor
2019-02-04 19:05:58 +08:00
self.hud = hud
world = self._parent.get_world()
bp = world.get_blueprint_library().find('sensor.other.lane_invasion')
self.sensor = world.spawn_actor(bp, carla.Transform(), attach_to=self._parent)
# We need to pass the lambda a weak reference to self to avoid circular
# reference.
weak_self = weakref.ref(self)
self.sensor.listen(lambda event: LaneInvasionSensor._on_invasion(weak_self, event))
@staticmethod
def _on_invasion(weak_self, event):
self = weak_self()
if not self:
return
2019-03-30 19:27:47 +08:00
lane_types = set(x.type for x in event.crossed_lane_markings)
text = ['%r' % str(x).split()[-1] for x in lane_types]
2019-02-04 19:05:58 +08:00
self.hud.notification('Crossed line %s' % ' and '.join(text))
2019-11-16 01:37:12 +08:00
# ==============================================================================
# -- GnssSensor ----------------------------------------------------------------
# ==============================================================================
2019-01-31 19:58:39 +08:00
class GnssSensor(object):
def __init__(self, parent_actor):
self.sensor = None
self._parent = parent_actor
self.lat = 0.0
self.lon = 0.0
world = self._parent.get_world()
bp = world.get_blueprint_library().find('sensor.other.gnss')
self.sensor = world.spawn_actor(bp, carla.Transform(carla.Location(x=1.0, z=2.8)), attach_to=self._parent)
# We need to pass the lambda a weak reference to self to avoid circular
# reference.
weak_self = weakref.ref(self)
self.sensor.listen(lambda event: GnssSensor._on_gnss_event(weak_self, event))
2019-01-31 19:58:39 +08:00
@staticmethod
def _on_gnss_event(weak_self, event):
self = weak_self()
if not self:
return
self.lat = event.latitude
self.lon = event.longitude
# ==============================================================================
# -- IMUSensor -----------------------------------------------------------------
# ==============================================================================
class IMUSensor(object):
def __init__(self, parent_actor):
self.sensor = None
self._parent = parent_actor
self.accelerometer = (0.0, 0.0, 0.0)
self.gyroscope = (0.0, 0.0, 0.0)
self.compass = 0.0
world = self._parent.get_world()
bp = world.get_blueprint_library().find('sensor.other.imu')
self.sensor = world.spawn_actor(
bp, carla.Transform(), attach_to=self._parent)
# We need to pass the lambda a weak reference to self to avoid circular
# reference.
weak_self = weakref.ref(self)
self.sensor.listen(
lambda sensor_data: IMUSensor._IMU_callback(weak_self, sensor_data))
@staticmethod
def _IMU_callback(weak_self, sensor_data):
self = weak_self()
if not self:
return
2019-12-11 18:27:17 +08:00
limits = (-99.9, 99.9)
self.accelerometer = (
max(limits[0], min(limits[1], sensor_data.accelerometer.x)),
max(limits[0], min(limits[1], sensor_data.accelerometer.y)),
max(limits[0], min(limits[1], sensor_data.accelerometer.z)))
self.gyroscope = (
max(limits[0], min(limits[1], math.degrees(sensor_data.gyroscope.x))),
max(limits[0], min(limits[1], math.degrees(sensor_data.gyroscope.y))),
max(limits[0], min(limits[1], math.degrees(sensor_data.gyroscope.z))))
self.compass = math.degrees(sensor_data.compass)
# ==============================================================================
# -- RadarSensor ---------------------------------------------------------------
# ==============================================================================
class RadarSensor(object):
def __init__(self, parent_actor):
self.sensor = None
self._parent = parent_actor
self.velocity_range = 7.5 # m/s
world = self._parent.get_world()
self.debug = world.debug
bp = world.get_blueprint_library().find('sensor.other.radar')
bp.set_attribute('horizontal_fov', str(35))
bp.set_attribute('vertical_fov', str(20))
self.sensor = world.spawn_actor(
bp,
carla.Transform(
carla.Location(x=2.8, z=1.0),
carla.Rotation(pitch=5)),
attach_to=self._parent)
# We need a weak reference to self to avoid circular reference.
weak_self = weakref.ref(self)
self.sensor.listen(
lambda radar_data: RadarSensor._Radar_callback(weak_self, radar_data))
@staticmethod
def _Radar_callback(weak_self, radar_data):
self = weak_self()
if not self:
return
# To get a numpy [[vel, altitude, azimuth, depth],...[,,,]]:
# points = np.frombuffer(radar_data.raw_data, dtype=np.dtype('f4'))
# points = np.reshape(points, (len(radar_data), 4))
current_rot = radar_data.transform.rotation
for detect in radar_data:
azi = math.degrees(detect.azimuth)
alt = math.degrees(detect.altitude)
# The 0.25 adjusts a bit the distance so the dots can
# be properly seen
fw_vec = carla.Vector3D(x=detect.depth - 0.25)
carla.Transform(
carla.Location(),
carla.Rotation(
pitch=current_rot.pitch + alt,
yaw=current_rot.yaw + azi,
roll=current_rot.roll)).transform(fw_vec)
def clamp(min_v, max_v, value):
return max(min_v, min(value, max_v))
norm_velocity = detect.velocity / self.velocity_range # range [-1, 1]
r = int(clamp(0.0, 1.0, 1.0 - norm_velocity) * 255.0)
g = int(clamp(0.0, 1.0, 1.0 - abs(norm_velocity)) * 255.0)
b = int(abs(clamp(- 1.0, 0.0, - 1.0 - norm_velocity)) * 255.0)
self.debug.draw_point(
radar_data.transform.location + fw_vec,
size=0.075,
life_time=0.06,
persistent_lines=False,
color=carla.Color(r, g, b))
2018-10-10 00:04:50 +08:00
# ==============================================================================
# -- CameraManager -------------------------------------------------------------
# ==============================================================================
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
class CameraManager(object):
def __init__(self, parent_actor, hud, gamma_correction):
2018-10-10 00:04:50 +08:00
self.sensor = None
2019-02-04 19:05:58 +08:00
self.surface = None
2018-10-10 00:04:50 +08:00
self._parent = parent_actor
2019-02-04 19:05:58 +08:00
self.hud = hud
self.recording = False
bound_y = 0.5 + self._parent.bounding_box.extent.y
Attachment = carla.AttachmentType
2018-10-10 00:04:50 +08:00
self._camera_transforms = [
(carla.Transform(carla.Location(x=-5.5, z=2.5), carla.Rotation(pitch=8.0)), Attachment.SpringArm),
(carla.Transform(carla.Location(x=1.6, z=1.7)), Attachment.Rigid),
(carla.Transform(carla.Location(x=5.5, y=1.5, z=1.5)), Attachment.SpringArm),
(carla.Transform(carla.Location(x=-8.0, z=6.0), carla.Rotation(pitch=6.0)), Attachment.SpringArm),
(carla.Transform(carla.Location(x=-1, y=-bound_y, z=0.5)), Attachment.Rigid)]
2019-02-04 19:05:58 +08:00
self.transform_index = 1
self.sensors = [
['sensor.camera.rgb', cc.Raw, 'Camera RGB', {}],
['sensor.camera.depth', cc.Raw, 'Camera Depth (Raw)', {}],
['sensor.camera.depth', cc.Depth, 'Camera Depth (Gray Scale)', {}],
['sensor.camera.depth', cc.LogarithmicDepth, 'Camera Depth (Logarithmic Gray Scale)', {}],
['sensor.camera.semantic_segmentation', cc.Raw, 'Camera Semantic Segmentation (Raw)', {}],
2019-03-19 19:20:59 +08:00
['sensor.camera.semantic_segmentation', cc.CityScapesPalette,
'Camera Semantic Segmentation (CityScapes Palette)', {}],
['sensor.lidar.ray_cast', None, 'Lidar (Ray-Cast)', {}],
['sensor.camera.rgb', cc.Raw, 'Camera RGB Distorted',
{'lens_circle_multiplier': '3.0',
'lens_circle_falloff': '3.0',
'chromatic_aberration_intensity': '0.5',
'chromatic_aberration_offset': '0'}]]
2018-10-10 00:04:50 +08:00
world = self._parent.get_world()
bp_library = world.get_blueprint_library()
2019-02-04 19:05:58 +08:00
for item in self.sensors:
2018-10-10 00:04:50 +08:00
bp = bp_library.find(item[0])
2018-10-30 03:23:50 +08:00
if item[0].startswith('sensor.camera'):
bp.set_attribute('image_size_x', str(hud.dim[0]))
bp.set_attribute('image_size_y', str(hud.dim[1]))
if bp.has_attribute('gamma'):
bp.set_attribute('gamma', str(gamma_correction))
for attr_name, attr_value in item[3].items():
bp.set_attribute(attr_name, attr_value)
2018-12-22 04:21:43 +08:00
elif item[0].startswith('sensor.lidar'):
bp.set_attribute('range', '50')
2018-10-10 00:04:50 +08:00
item.append(bp)
2019-02-04 19:05:58 +08:00
self.index = None
2018-10-10 00:04:50 +08:00
def toggle_camera(self):
2019-02-04 19:05:58 +08:00
self.transform_index = (self.transform_index + 1) % len(self._camera_transforms)
self.set_sensor(self.index, notify=False, force_respawn=True)
2018-10-10 00:04:50 +08:00
def set_sensor(self, index, notify=True, force_respawn=False):
2019-02-04 19:05:58 +08:00
index = index % len(self.sensors)
needs_respawn = True if self.index is None else \
(force_respawn or (self.sensors[index][2] != self.sensors[self.index][2]))
2018-10-10 00:04:50 +08:00
if needs_respawn:
if self.sensor is not None:
self.sensor.destroy()
2019-02-04 19:05:58 +08:00
self.surface = None
2018-10-10 00:04:50 +08:00
self.sensor = self._parent.get_world().spawn_actor(
2019-02-04 19:05:58 +08:00
self.sensors[index][-1],
self._camera_transforms[self.transform_index][0],
attach_to=self._parent,
attachment_type=self._camera_transforms[self.transform_index][1])
2018-10-10 00:04:50 +08:00
# We need to pass the lambda a weak reference to self to avoid
# circular reference.
weak_self = weakref.ref(self)
self.sensor.listen(lambda image: CameraManager._parse_image(weak_self, image))
2018-10-22 06:30:35 +08:00
if notify:
2019-02-04 19:05:58 +08:00
self.hud.notification(self.sensors[index][2])
self.index = index
2018-10-10 00:04:50 +08:00
def next_sensor(self):
2019-02-04 19:05:58 +08:00
self.set_sensor(self.index + 1)
2018-10-10 00:04:50 +08:00
def toggle_recording(self):
2019-02-04 19:05:58 +08:00
self.recording = not self.recording
self.hud.notification('Recording %s' % ('On' if self.recording else 'Off'))
2018-10-10 00:04:50 +08:00
def render(self, display):
2019-02-04 19:05:58 +08:00
if self.surface is not None:
display.blit(self.surface, (0, 0))
2018-10-10 00:04:50 +08:00
@staticmethod
def _parse_image(weak_self, image):
self = weak_self()
if not self:
return
2019-02-04 19:05:58 +08:00
if self.sensors[self.index][0].startswith('sensor.lidar'):
2018-10-30 03:23:50 +08:00
points = np.frombuffer(image.raw_data, dtype=np.dtype('f4'))
2019-03-19 19:20:59 +08:00
points = np.reshape(points, (int(points.shape[0] / 3), 3))
2018-10-30 03:23:50 +08:00
lidar_data = np.array(points[:, :2])
2019-02-04 19:05:58 +08:00
lidar_data *= min(self.hud.dim) / 100.0
lidar_data += (0.5 * self.hud.dim[0], 0.5 * self.hud.dim[1])
2019-03-19 19:20:59 +08:00
lidar_data = np.fabs(lidar_data) # pylint: disable=E1111
2018-10-30 03:23:50 +08:00
lidar_data = lidar_data.astype(np.int32)
lidar_data = np.reshape(lidar_data, (-1, 2))
2019-02-04 19:05:58 +08:00
lidar_img_size = (self.hud.dim[0], self.hud.dim[1], 3)
lidar_img = np.zeros((lidar_img_size), dtype = int)
2018-10-30 03:23:50 +08:00
lidar_img[tuple(lidar_data.T)] = (255, 255, 255)
2019-02-04 19:05:58 +08:00
self.surface = pygame.surfarray.make_surface(lidar_img)
2018-10-30 03:23:50 +08:00
else:
2019-02-04 19:05:58 +08:00
image.convert(self.sensors[self.index][1])
2018-10-30 03:23:50 +08:00
array = np.frombuffer(image.raw_data, dtype=np.dtype("uint8"))
array = np.reshape(array, (image.height, image.width, 4))
array = array[:, :, :3]
array = array[:, :, ::-1]
2019-02-04 19:05:58 +08:00
self.surface = pygame.surfarray.make_surface(array.swapaxes(0, 1))
if self.recording:
image.save_to_disk('_out/%08d' % image.frame)
2018-07-30 05:52:13 +08:00
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
# ==============================================================================
# -- game_loop() ---------------------------------------------------------------
# ==============================================================================
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
def game_loop(args):
pygame.init()
pygame.font.init()
world = None
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
try:
client = carla.Client(args.host, args.port)
client.set_timeout(2.0)
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
display = pygame.display.set_mode(
(args.width, args.height),
pygame.HWSURFACE | pygame.DOUBLEBUF)
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
hud = HUD(args.width, args.height)
world = World(client.get_world(), hud, args)
2018-10-10 00:04:50 +08:00
controller = KeyboardControl(world, args.autopilot)
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
clock = pygame.time.Clock()
while True:
clock.tick_busy_loop(60)
if controller.parse_events(client, world, clock):
2018-10-10 00:04:50 +08:00
return
world.tick(clock)
world.render(display)
pygame.display.flip()
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
finally:
2018-10-02 22:11:21 +08:00
if (world and world.recording_enabled):
client.stop_recorder()
2018-10-10 00:04:50 +08:00
if world is not None:
world.destroy()
2018-10-02 22:11:21 +08:00
2018-10-10 00:04:50 +08:00
pygame.quit()
2018-07-30 05:52:13 +08:00
2018-10-10 00:04:50 +08:00
# ==============================================================================
# -- main() --------------------------------------------------------------------
# ==============================================================================
2018-07-30 05:52:13 +08:00
2018-10-02 22:11:21 +08:00
2018-07-30 05:52:13 +08:00
def main():
argparser = argparse.ArgumentParser(
description='CARLA Manual Control Client')
argparser.add_argument(
'-v', '--verbose',
action='store_true',
dest='debug',
help='print debug information')
argparser.add_argument(
'--host',
metavar='H',
2018-10-02 22:11:21 +08:00
default='127.0.0.1',
2018-10-10 00:04:50 +08:00
help='IP of the host server (default: 127.0.0.1)')
2018-07-30 05:52:13 +08:00
argparser.add_argument(
'-p', '--port',
metavar='P',
default=2000,
type=int,
help='TCP port to listen to (default: 2000)')
argparser.add_argument(
'-a', '--autopilot',
action='store_true',
help='enable autopilot')
2018-10-10 00:04:50 +08:00
argparser.add_argument(
'--res',
metavar='WIDTHxHEIGHT',
default='1280x720',
help='window resolution (default: 1280x720)')
argparser.add_argument(
'--filter',
metavar='PATTERN',
default='vehicle.*',
help='actor filter (default: "vehicle.*")')
argparser.add_argument(
'--rolename',
metavar='NAME',
default='hero',
help='actor role name (default: "hero")')
argparser.add_argument(
'--gamma',
default=2.2,
type=float,
help='Gamma correction of the camera (default: 2.2)')
2018-07-30 05:52:13 +08:00
args = argparser.parse_args()
2018-10-10 00:04:50 +08:00
args.width, args.height = [int(x) for x in args.res.split('x')]
2018-07-30 05:52:13 +08:00
log_level = logging.DEBUG if args.debug else logging.INFO
logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
logging.info('listening to server %s:%s', args.host, args.port)
2018-10-10 00:04:50 +08:00
2018-07-30 05:52:13 +08:00
print(__doc__)
2018-10-10 00:04:50 +08:00
try:
2018-07-30 05:52:13 +08:00
2018-10-10 00:04:50 +08:00
game_loop(args)
2018-07-30 05:52:13 +08:00
except KeyboardInterrupt:
print('\nCancelled by user. Bye!')
2018-10-10 00:04:50 +08:00
if __name__ == '__main__':
main()