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>.
|
|
|
|
|
2018-10-28 18:23:59 +08:00
|
|
|
# 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
|
2020-01-29 21:43:35 +08:00
|
|
|
A/D : steer left/right
|
2018-07-30 05:52:13 +08:00
|
|
|
Q : toggle reverse
|
|
|
|
Space : hand-brake
|
|
|
|
P : toggle autopilot
|
2018-12-11 23:31:31 +08:00
|
|
|
M : toggle manual transmission
|
|
|
|
,/. : gear up/down
|
2018-07-30 05:52:13 +08:00
|
|
|
|
2020-01-29 21:43:35 +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
|
2020-02-08 00:17:18 +08:00
|
|
|
` or N : next sensor
|
2018-10-30 03:23:50 +08:00
|
|
|
[1-9] : change to sensor [1-9]
|
2019-12-11 16:24:49 +08:00
|
|
|
G : toggle radar visualization
|
2018-10-18 06:16:59 +08:00
|
|
|
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
|
2018-10-18 06:16:59 +08:00
|
|
|
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
|
2018-12-11 23:31:31 +08:00
|
|
|
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
|
2018-12-11 23:31:31 +08:00
|
|
|
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
|
2018-10-15 18:01:31 +08:00
|
|
|
from pygame.locals import K_c
|
2019-12-11 16:24:49 +08:00
|
|
|
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
|
2018-12-11 23:31:31 +08:00
|
|
|
from pygame.locals import K_m
|
2020-02-08 00:17:18 +08:00
|
|
|
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
|
2020-01-29 21:43:35 +08:00
|
|
|
from pygame.locals import K_l
|
|
|
|
from pygame.locals import K_i
|
|
|
|
from pygame.locals import K_z
|
|
|
|
from pygame.locals import K_x
|
2019-02-06 17:18:20 +08:00
|
|
|
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
|
|
|
# ==============================================================================
|
2018-12-13 20:58:20 +08:00
|
|
|
# -- Global functions ----------------------------------------------------------
|
2018-10-10 00:04:50 +08:00
|
|
|
# ==============================================================================
|
2018-07-30 05:52:13 +08:00
|
|
|
|
|
|
|
|
2018-10-18 06:16:59 +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
|
|
|
|
|
|
|
|
2018-12-13 20:58:20 +08:00
|
|
|
# ==============================================================================
|
|
|
|
# -- World ---------------------------------------------------------------------
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
|
|
|
2018-10-10 00:04:50 +08:00
|
|
|
class World(object):
|
2019-07-03 21:42:05 +08:00
|
|
|
def __init__(self, carla_world, hud, args):
|
2018-10-22 06:30:35 +08:00
|
|
|
self.world = carla_world
|
2019-07-03 21:42:05 +08:00
|
|
|
self.actor_role_name = args.rolename
|
2019-10-04 01:08:53 +08:00
|
|
|
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
|
2019-01-23 02:49:41 +08:00
|
|
|
self.player = None
|
2018-12-13 20:58:20 +08:00
|
|
|
self.collision_sensor = None
|
|
|
|
self.lane_invasion_sensor = None
|
2019-01-23 17:51:15 +08:00
|
|
|
self.gnss_sensor = None
|
2019-12-11 16:24:49 +08:00
|
|
|
self.imu_sensor = None
|
|
|
|
self.radar_sensor = None
|
2018-12-13 20:58:20 +08:00
|
|
|
self.camera_manager = None
|
2018-10-18 06:16:59 +08:00
|
|
|
self._weather_presets = find_weather_presets()
|
|
|
|
self._weather_index = 0
|
2019-07-03 21:42:05 +08:00
|
|
|
self._actor_filter = args.filter
|
|
|
|
self._gamma = args.gamma
|
2018-12-13 20:58:20 +08:00
|
|
|
self.restart()
|
|
|
|
self.world.on_tick(hud.on_world_tick)
|
2019-02-06 17:18:20 +08:00
|
|
|
self.recording_enabled = False
|
|
|
|
self.recording_start = 0
|
2018-10-15 18:01:31 +08:00
|
|
|
|
2018-10-22 06:30:35 +08:00
|
|
|
def restart(self):
|
2019-10-04 21:00:00 +08:00
|
|
|
self.player_max_speed = 1.589
|
|
|
|
self.player_max_speed_fast = 3.713
|
2018-12-13 20:58:20 +08:00
|
|
|
# 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
|
2019-01-23 02:49:41 +08:00
|
|
|
# Get a random blueprint.
|
|
|
|
blueprint = random.choice(self.world.get_blueprint_library().filter(self._actor_filter))
|
2019-03-22 16:59:01 +08:00
|
|
|
blueprint.set_attribute('role_name', self.actor_role_name)
|
2018-12-13 20:58:20 +08:00
|
|
|
if blueprint.has_attribute('color'):
|
|
|
|
color = random.choice(blueprint.get_attribute('color').recommended_values)
|
|
|
|
blueprint.set_attribute('color', color)
|
2019-06-11 23:27:02 +08:00
|
|
|
if blueprint.has_attribute('driver_id'):
|
|
|
|
driver_id = random.choice(blueprint.get_attribute('driver_id').recommended_values)
|
|
|
|
blueprint.set_attribute('driver_id', driver_id)
|
2019-05-08 19:45:02 +08:00
|
|
|
if blueprint.has_attribute('is_invincible'):
|
|
|
|
blueprint.set_attribute('is_invincible', 'true')
|
2019-10-04 21:00:00 +08:00
|
|
|
# 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")
|
2019-01-23 02:49:41 +08:00
|
|
|
# Spawn the player.
|
|
|
|
if self.player is not None:
|
|
|
|
spawn_point = self.player.get_transform()
|
2018-12-13 20:58:20 +08:00
|
|
|
spawn_point.location.z += 2.0
|
|
|
|
spawn_point.rotation.roll = 0.0
|
|
|
|
spawn_point.rotation.pitch = 0.0
|
|
|
|
self.destroy()
|
2019-01-23 02:49:41 +08:00
|
|
|
self.player = self.world.try_spawn_actor(blueprint, spawn_point)
|
|
|
|
while self.player is None:
|
2019-10-04 01:08:53 +08:00
|
|
|
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()
|
2018-12-13 20:58:20 +08:00
|
|
|
spawn_point = random.choice(spawn_points) if spawn_points else carla.Transform()
|
2019-01-23 02:49:41 +08:00
|
|
|
self.player = self.world.try_spawn_actor(blueprint, spawn_point)
|
2018-12-13 20:58:20 +08:00
|
|
|
# Set up the sensors.
|
2019-01-23 02:49:41 +08:00
|
|
|
self.collision_sensor = CollisionSensor(self.player, self.hud)
|
|
|
|
self.lane_invasion_sensor = LaneInvasionSensor(self.player, self.hud)
|
2019-01-23 17:51:15 +08:00
|
|
|
self.gnss_sensor = GnssSensor(self.player)
|
2019-12-11 16:24:49 +08:00
|
|
|
self.imu_sensor = IMUSensor(self.player)
|
2019-07-03 21:42:05 +08:00
|
|
|
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)
|
2019-01-23 02:49:41 +08:00
|
|
|
actor_type = get_actor_display_name(self.player)
|
2018-10-22 06:30:35 +08:00
|
|
|
self.hud.notification(actor_type)
|
|
|
|
|
2018-10-18 06:16:59 +08:00
|
|
|
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])
|
2019-01-23 02:49:41 +08:00
|
|
|
self.player.get_world().set_weather(preset[0])
|
2018-10-10 00:04:50 +08:00
|
|
|
|
2019-12-11 16:24:49 +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
|
2019-02-06 17:18:20 +08:00
|
|
|
|
2018-10-10 00:04:50 +08:00
|
|
|
def destroy(self):
|
2019-12-11 16:24:49 +08:00
|
|
|
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,
|
2019-01-23 17:51:15 +08:00
|
|
|
self.gnss_sensor.sensor,
|
2019-12-11 16:24:49 +08:00
|
|
|
self.imu_sensor.sensor,
|
2019-01-23 02:49:41 +08:00
|
|
|
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()
|
|
|
|
|
2019-12-11 16:24:49 +08:00
|
|
|
|
2018-10-10 00:04:50 +08:00
|
|
|
# ==============================================================================
|
|
|
|
# -- KeyboardControl -----------------------------------------------------------
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
class KeyboardControl(object):
|
2020-01-29 21:43:35 +08:00
|
|
|
"""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
|
2019-01-23 02:49:41 +08:00
|
|
|
if isinstance(world.player, carla.Vehicle):
|
|
|
|
self._control = carla.VehicleControl()
|
2020-01-29 21:43:35 +08:00
|
|
|
self._lights = carla.VehicleLightState.NONE
|
2019-01-23 02:49:41 +08:00
|
|
|
world.player.set_autopilot(self._autopilot_enabled)
|
2020-01-29 21:43:35 +08:00
|
|
|
world.player.set_light_state(self._lights)
|
2019-01-23 02:49:41 +08:00
|
|
|
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)
|
|
|
|
|
2019-02-06 17:18:20 +08:00
|
|
|
def parse_events(self, client, world, clock):
|
2020-02-25 19:18:37 +08:00
|
|
|
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()
|
2018-10-18 06:16:59 +08:00
|
|
|
elif event.key == K_c and pygame.key.get_mods() & KMOD_SHIFT:
|
|
|
|
world.next_weather(reverse=True)
|
2018-10-15 18:01:31 +08:00
|
|
|
elif event.key == K_c:
|
|
|
|
world.next_weather()
|
2019-12-11 16:24:49 +08:00
|
|
|
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()
|
2020-02-08 00:17:18 +08:00
|
|
|
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)
|
2019-02-06 17:18:20 +08:00
|
|
|
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()
|
2019-02-06 17:18:20 +08:00
|
|
|
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
|
2020-01-29 21:43:35 +08:00
|
|
|
current_index = world.camera_manager.index
|
2019-02-04 19:05:58 +08:00
|
|
|
world.destroy_sensors()
|
2019-02-06 17:18:20 +08:00
|
|
|
# disable autopilot
|
|
|
|
self._autopilot_enabled = False
|
|
|
|
world.player.set_autopilot(self._autopilot_enabled)
|
|
|
|
world.hud.notification("Replaying file 'manual_recording.rec'")
|
|
|
|
# replayer
|
2019-02-21 19:13:48 +08:00
|
|
|
client.replay_file("manual_recording.rec", world.recording_start, 0, 0)
|
2020-01-29 21:43:35 +08:00
|
|
|
world.camera_manager.set_sensor(current_index)
|
2019-02-06 17:18:20 +08:00
|
|
|
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))
|
2019-01-23 02:49:41 +08:00
|
|
|
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'))
|
2019-01-23 02:49:41 +08:00
|
|
|
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
|
2020-01-29 21:43:35 +08:00
|
|
|
elif event.key == K_p and not pygame.key.get_mods() & KMOD_CTRL:
|
2019-01-23 02:49:41 +08:00
|
|
|
self._autopilot_enabled = not self._autopilot_enabled
|
|
|
|
world.player.set_autopilot(self._autopilot_enabled)
|
2020-01-29 21:43:35 +08:00
|
|
|
world.hud.notification(
|
|
|
|
'Autopilot %s' % ('On' if self._autopilot_enabled else 'Off'))
|
2020-02-25 19:18:37 +08:00
|
|
|
elif event.key == K_l and pygame.key.get_mods() & KMOD_CTRL:
|
|
|
|
current_lights ^= carla.VehicleLightState.Special1
|
2020-01-29 21:43:35 +08:00
|
|
|
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:
|
2019-01-23 02:49:41 +08:00
|
|
|
if isinstance(self._control, carla.VehicleControl):
|
|
|
|
self._parse_vehicle_keys(pygame.key.get_pressed(), clock.get_time())
|
|
|
|
self._control.reverse = self._control.gear < 0
|
2020-01-29 21:43:35 +08:00
|
|
|
# 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))
|
2019-01-23 02:49:41 +08:00
|
|
|
elif isinstance(self._control, carla.WalkerControl):
|
2019-10-04 21:00:00 +08:00
|
|
|
self._parse_walker_keys(pygame.key.get_pressed(), clock.get_time(), world)
|
2019-01-23 02:49:41 +08:00
|
|
|
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]:
|
2019-08-01 17:36:34 +08:00
|
|
|
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]:
|
2019-08-01 17:36:34 +08:00
|
|
|
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]
|
|
|
|
|
2019-10-04 21:00:00 +08:00
|
|
|
def _parse_walker_keys(self, keys, milliseconds, world):
|
2019-01-23 02:49:41 +08:00
|
|
|
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]:
|
2019-10-04 21:00:00 +08:00
|
|
|
self._control.speed = world.player_max_speed_fast if pygame.key.get_mods() & KMOD_SHIFT else world.player_max_speed
|
2019-01-23 02:49:41 +08:00
|
|
|
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))
|
2020-01-29 21:43:35 +08:00
|
|
|
self.help = HelpText(pygame.font.Font(mono, 16), width, height)
|
2018-10-10 00:04:50 +08:00
|
|
|
self.server_fps = 0
|
2019-06-25 21:50:47 +08:00
|
|
|
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()
|
2019-06-25 21:50:47 +08:00
|
|
|
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):
|
2019-02-07 02:23:24 +08:00
|
|
|
self._notifications.tick(world, clock)
|
2018-11-09 00:12:55 +08:00
|
|
|
if not self._show_info:
|
|
|
|
return
|
2019-01-23 02:49:41 +08:00
|
|
|
t = world.player.get_transform()
|
|
|
|
v = world.player.get_velocity()
|
|
|
|
c = world.player.get_control()
|
2019-12-11 16:24:49 +08:00
|
|
|
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()
|
2019-06-25 21:50:47 +08:00
|
|
|
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 = [
|
2019-02-05 02:29:03 +08:00
|
|
|
'Server: % 16.0f FPS' % self.server_fps,
|
|
|
|
'Client: % 16.0f FPS' % clock.get_fps(),
|
2018-11-16 01:05:44 +08:00
|
|
|
'',
|
2019-01-23 02:49:41 +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)),
|
2019-12-11 16:24:49 +08:00
|
|
|
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)),
|
2019-01-23 17:51:15 +08:00
|
|
|
'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,
|
2019-01-23 02:49:41 +08:00
|
|
|
'']
|
|
|
|
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
|
|
|
'',
|
2019-01-23 02:49:41 +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)
|
2019-01-23 02:49:41 +08:00
|
|
|
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):
|
2020-01-29 21:43:35 +08:00
|
|
|
"""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
|
2020-01-29 21:43:35 +08:00
|
|
|
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))
|
2020-01-29 21:43:35 +08:00
|
|
|
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
|
2018-11-13 22:54:43 +08:00
|
|
|
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)
|
2019-06-25 21:50:47 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
2018-11-13 22:54:11 +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
|
2018-11-13 22:54:11 +08:00
|
|
|
world = self._parent.get_world()
|
2019-03-27 16:36:45 +08:00
|
|
|
bp = world.get_blueprint_library().find('sensor.other.lane_invasion')
|
2018-11-13 22:54:11 +08:00
|
|
|
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))
|
2018-11-13 22:54:11 +08:00
|
|
|
|
2019-11-16 01:37:12 +08:00
|
|
|
|
2019-01-23 17:51:15 +08:00
|
|
|
# ==============================================================================
|
2019-12-11 16:24:49 +08:00
|
|
|
# -- GnssSensor ----------------------------------------------------------------
|
2019-01-23 17:51:15 +08:00
|
|
|
# ==============================================================================
|
|
|
|
|
2019-01-31 19:58:39 +08:00
|
|
|
|
2019-01-23 17:51:15 +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
|
|
|
|
2019-01-23 17:51:15 +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
|
|
|
|
|
2018-11-13 22:54:11 +08:00
|
|
|
|
2019-12-11 16:24:49 +08:00
|
|
|
# ==============================================================================
|
|
|
|
# -- IMUSensor -----------------------------------------------------------------
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
class IMUSensor(object):
|
|
|
|
def __init__(self, parent_actor):
|
|
|
|
self.sensor = None
|
|
|
|
self._parent = parent_actor
|
2020-03-04 19:16:08 +08:00
|
|
|
self.accelerometer = (0.0, 0.0, 0.0)
|
|
|
|
self.gyroscope = (0.0, 0.0, 0.0)
|
2019-12-11 16:24:49 +08:00
|
|
|
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)
|
2019-12-11 16:24:49 +08:00
|
|
|
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):
|
2019-07-03 21:42:05 +08:00
|
|
|
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
|
2019-05-28 00:07:03 +08:00
|
|
|
bound_y = 0.5 + self._parent.bounding_box.extent.y
|
|
|
|
Attachment = carla.AttachmentType
|
2018-10-10 00:04:50 +08:00
|
|
|
self._camera_transforms = [
|
2019-05-28 00:07:03 +08:00
|
|
|
(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 = [
|
2019-10-04 01:08:53 +08:00
|
|
|
['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,
|
2019-10-04 01:08:53 +08:00
|
|
|
'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]))
|
2019-07-03 21:42:05 +08:00
|
|
|
if bp.has_attribute('gamma'):
|
|
|
|
bp.set_attribute('gamma', str(gamma_correction))
|
2019-10-04 01:08:53 +08:00
|
|
|
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'):
|
2019-11-14 01:50:21 +08:00
|
|
|
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)
|
2019-05-28 00:07:03 +08:00
|
|
|
self.set_sensor(self.index, notify=False, force_respawn=True)
|
2018-10-10 00:04:50 +08:00
|
|
|
|
2019-05-28 00:07:03 +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)
|
2019-05-28 00:07:03 +08:00
|
|
|
needs_respawn = True if self.index is None else \
|
2019-10-04 01:08:53 +08:00
|
|
|
(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],
|
2019-05-28 00:07:03 +08:00
|
|
|
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)
|
2019-04-16 16:01:50 +08:00
|
|
|
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:
|
2019-06-25 21:50:47 +08:00
|
|
|
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)
|
2019-07-03 21:42:05 +08:00
|
|
|
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)
|
2019-02-06 17:18:20 +08:00
|
|
|
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
|
|
|
|
2019-02-25 21:10:57 +08:00
|
|
|
if (world and world.recording_enabled):
|
2019-02-21 21:02:23 +08:00
|
|
|
client.stop_recorder()
|
2019-02-06 17:18:20 +08:00
|
|
|
|
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)')
|
2019-01-23 02:49:41 +08:00
|
|
|
argparser.add_argument(
|
|
|
|
'--filter',
|
|
|
|
metavar='PATTERN',
|
|
|
|
default='vehicle.*',
|
|
|
|
help='actor filter (default: "vehicle.*")')
|
2019-03-22 16:59:01 +08:00
|
|
|
argparser.add_argument(
|
|
|
|
'--rolename',
|
|
|
|
metavar='NAME',
|
|
|
|
default='hero',
|
|
|
|
help='actor role name (default: "hero")')
|
2019-07-03 21:42:05 +08:00
|
|
|
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()
|