37 changed files with 14478 additions and 1 deletions
-
3.gitignore
-
1402carla_examples/V2XDemo.py
-
872carla_examples/automatic_control.py
-
484carla_examples/bounding_boxes.py
-
376carla_examples/carla_cosmos_gen.py
-
381carla_examples/client_bounding_boxes.py
-
84carla_examples/cosmos_aov.yaml
-
418carla_examples/draw_skeleton.py
-
142carla_examples/dynamic_weather.py
-
35carla_examples/follow_vehicle.py
-
366carla_examples/generate_traffic.py
-
24carla_examples/get_component_test.py
-
695carla_examples/invertedai_traffic.py
-
345carla_examples/lidar_to_camera.py
-
1367carla_examples/manual_control.py
-
1175carla_examples/manual_control_carsim.py
-
1196carla_examples/manual_control_chrono.py
-
848carla_examples/manual_control_steeringwheel.py
-
1627carla_examples/no_rendering_mode.py
-
323carla_examples/open3d_lidar.py
-
9carla_examples/requirements.txt
-
131carla_examples/sensor_synchronization.py
-
66carla_examples/show_recorder_actors_blocked.py
-
64carla_examples/show_recorder_collisions.py
-
68carla_examples/show_recorder_file_info.py
-
141carla_examples/start_recording.py
-
109carla_examples/start_replaying.py
-
195carla_examples/synchronous_mode.py
-
83carla_examples/test_addsecondvx.py
-
116carla_examples/tutorial.py
-
116carla_examples/tutorial_gbuffer.py
-
51carla_examples/vehicle_gallery.py
-
133carla_examples/vehicle_physics.py
-
375carla_examples/visualize_multiple_sensors.py
-
472intel/old_implement.md
-
123intel/shenron_implementation_guide.md
-
64intel/shenron_integration.md
1402
carla_examples/V2XDemo.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,872 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2018 Intel Labs. |
|||
# authors: German Ros (german.ros@intel.com) |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
"""Example of automatic vehicle control from client side.""" |
|||
|
|||
from __future__ import print_function |
|||
|
|||
import argparse |
|||
import collections |
|||
import datetime |
|||
import logging |
|||
import math |
|||
import os |
|||
import numpy.random as random |
|||
import re |
|||
import sys |
|||
import weakref |
|||
|
|||
try: |
|||
import pygame |
|||
from pygame.locals import KMOD_CTRL |
|||
from pygame.locals import K_ESCAPE |
|||
from pygame.locals import K_q |
|||
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') |
|||
|
|||
# ============================================================================== |
|||
# -- Add PythonAPI for release mode -------------------------------------------- |
|||
# ============================================================================== |
|||
try: |
|||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/carla') |
|||
except IndexError: |
|||
pass |
|||
|
|||
import carla |
|||
from carla import ColorConverter as cc |
|||
|
|||
from agents.navigation.behavior_agent import BehaviorAgent # pylint: disable=import-error |
|||
from agents.navigation.basic_agent import BasicAgent # pylint: disable=import-error |
|||
from agents.navigation.constant_velocity_agent import ConstantVelocityAgent # pylint: disable=import-error |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- Global functions ---------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
def find_weather_presets(): |
|||
"""Method to find weather presets""" |
|||
rgx = re.compile('.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)') |
|||
def name(x): return ' '.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] |
|||
|
|||
|
|||
def get_actor_display_name(actor, truncate=250): |
|||
"""Method to get actor display name""" |
|||
name = ' '.join(actor.type_id.replace('_', '.').title().split('.')[1:]) |
|||
return (name[:truncate - 1] + u'\u2026') if len(name) > truncate else name |
|||
|
|||
def get_actor_blueprints(world, filter, generation): |
|||
bps = world.get_blueprint_library().filter(filter) |
|||
|
|||
if generation.lower() == "all": |
|||
return bps |
|||
|
|||
# If the filter returns only one bp, we assume that this one needed |
|||
# and therefore, we ignore the generation |
|||
if len(bps) == 1: |
|||
return bps |
|||
|
|||
try: |
|||
int_generation = int(generation) |
|||
# Check if generation is in available generations |
|||
if int_generation in [1, 2, 3]: |
|||
bps = [x for x in bps if int(x.get_attribute('generation')) == int_generation] |
|||
return bps |
|||
else: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
except: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
|
|||
# ============================================================================== |
|||
# -- World --------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
class World(object): |
|||
""" Class representing the surrounding environment """ |
|||
|
|||
def __init__(self, carla_world, hud, args): |
|||
"""Constructor method""" |
|||
self._args = args |
|||
self.world = carla_world |
|||
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) |
|||
self.hud = hud |
|||
self.player = None |
|||
self.collision_sensor = None |
|||
self.lane_invasion_sensor = None |
|||
self.gnss_sensor = None |
|||
self.camera_manager = None |
|||
self._weather_presets = find_weather_presets() |
|||
self._weather_index = 0 |
|||
self._actor_filter = args.filter |
|||
self._actor_generation = args.generation |
|||
self.restart(args) |
|||
self.world.on_tick(hud.on_world_tick) |
|||
self.recording_enabled = False |
|||
self.recording_start = 0 |
|||
|
|||
def restart(self, args): |
|||
"""Restart the world""" |
|||
# Keep same camera config if the camera manager exists. |
|||
cam_index = self.camera_manager.index if self.camera_manager is not None else 0 |
|||
cam_pos_id = self.camera_manager.transform_index if self.camera_manager is not None else 0 |
|||
|
|||
# Get a random blueprint. |
|||
blueprint_list = get_actor_blueprints(self.world, self._actor_filter, self._actor_generation) |
|||
if not blueprint_list: |
|||
raise ValueError("Couldn't find any blueprints with the specified filters") |
|||
blueprint = random.choice(blueprint_list) |
|||
blueprint.set_attribute('role_name', 'hero') |
|||
if blueprint.has_attribute('color'): |
|||
color = random.choice(blueprint.get_attribute('color').recommended_values) |
|||
blueprint.set_attribute('color', color) |
|||
|
|||
# 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) |
|||
self.modify_vehicle_physics(self.player) |
|||
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) |
|||
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) |
|||
self.modify_vehicle_physics(self.player) |
|||
|
|||
if self._args.sync: |
|||
self.world.tick() |
|||
else: |
|||
self.world.wait_for_tick() |
|||
|
|||
# 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.camera_manager = CameraManager(self.player, self.hud) |
|||
self.camera_manager.transform_index = cam_pos_id |
|||
self.camera_manager.set_sensor(cam_index, notify=False) |
|||
actor_type = get_actor_display_name(self.player) |
|||
self.hud.notification(actor_type) |
|||
|
|||
def next_weather(self, reverse=False): |
|||
"""Get next weather setting""" |
|||
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]) |
|||
|
|||
def modify_vehicle_physics(self, actor): |
|||
#If actor is not a vehicle, we cannot use the physics control |
|||
try: |
|||
physics_control = actor.get_physics_control() |
|||
physics_control.use_sweep_wheel_collision = True |
|||
actor.apply_physics_control(physics_control) |
|||
except Exception: |
|||
pass |
|||
|
|||
def tick(self, clock): |
|||
"""Method for every tick""" |
|||
self.hud.tick(self, clock) |
|||
|
|||
def render(self, display): |
|||
"""Render world""" |
|||
self.camera_manager.render(display) |
|||
self.hud.render(display) |
|||
|
|||
def destroy_sensors(self): |
|||
"""Destroy sensors""" |
|||
self.camera_manager.sensor.destroy() |
|||
self.camera_manager.sensor = None |
|||
self.camera_manager.index = None |
|||
|
|||
def destroy(self): |
|||
"""Destroys all actors""" |
|||
actors = [ |
|||
self.camera_manager.sensor, |
|||
self.collision_sensor.sensor, |
|||
self.lane_invasion_sensor.sensor, |
|||
self.gnss_sensor.sensor, |
|||
self.player] |
|||
for actor in actors: |
|||
if actor is not None: |
|||
actor.destroy() |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- KeyboardControl ----------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class KeyboardControl(object): |
|||
def __init__(self, world): |
|||
world.hud.notification("Press 'H' or '?' for help.", seconds=4.0) |
|||
|
|||
def parse_events(self): |
|||
for event in pygame.event.get(): |
|||
if event.type == pygame.QUIT: |
|||
return True |
|||
if event.type == pygame.KEYUP: |
|||
if self._is_quit_shortcut(event.key): |
|||
return True |
|||
|
|||
@staticmethod |
|||
def _is_quit_shortcut(key): |
|||
"""Shortcut for quitting""" |
|||
return (key == K_ESCAPE) or (key == K_q and pygame.key.get_mods() & KMOD_CTRL) |
|||
|
|||
# ============================================================================== |
|||
# -- HUD ----------------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class HUD(object): |
|||
"""Class for HUD text""" |
|||
|
|||
def __init__(self, width, height): |
|||
"""Constructor method""" |
|||
self.dim = (width, height) |
|||
font = pygame.font.Font(pygame.font.get_default_font(), 20) |
|||
font_name = 'courier' if os.name == 'nt' else 'mono' |
|||
fonts = [x for x in pygame.font.get_fonts() if font_name in x] |
|||
default_font = 'ubuntumono' |
|||
mono = default_font if default_font in fonts else fonts[0] |
|||
mono = pygame.font.match_font(mono) |
|||
self._font_mono = pygame.font.Font(mono, 12 if os.name == 'nt' else 14) |
|||
self._notifications = FadingText(font, (width, 40), (0, height - 40)) |
|||
self.help = HelpText(pygame.font.Font(mono, 24), width, height) |
|||
self.server_fps = 0 |
|||
self.frame = 0 |
|||
self.simulation_time = 0 |
|||
self._show_info = True |
|||
self._info_text = [] |
|||
self._server_clock = pygame.time.Clock() |
|||
|
|||
def on_world_tick(self, timestamp): |
|||
"""Gets informations from the world at every tick""" |
|||
self._server_clock.tick() |
|||
self.server_fps = self._server_clock.get_fps() |
|||
self.frame = timestamp.frame_count |
|||
self.simulation_time = timestamp.elapsed_seconds |
|||
|
|||
def tick(self, world, clock): |
|||
"""HUD method for every tick""" |
|||
self._notifications.tick(world, clock) |
|||
if not self._show_info: |
|||
return |
|||
transform = world.player.get_transform() |
|||
vel = world.player.get_velocity() |
|||
control = world.player.get_control() |
|||
heading = 'N' if abs(transform.rotation.yaw) < 89.5 else '' |
|||
heading += 'S' if abs(transform.rotation.yaw) > 90.5 else '' |
|||
heading += 'E' if 179.5 > transform.rotation.yaw > 0.5 else '' |
|||
heading += 'W' if -0.5 > transform.rotation.yaw > -179.5 else '' |
|||
colhist = world.collision_sensor.get_collision_history() |
|||
collision = [colhist[x + self.frame - 200] for x in range(0, 200)] |
|||
max_col = max(1.0, max(collision)) |
|||
collision = [x / max_col for x in collision] |
|||
vehicles = world.world.get_actors().filter('vehicle.*') |
|||
|
|||
self._info_text = [ |
|||
'Server: % 16.0f FPS' % self.server_fps, |
|||
'Client: % 16.0f FPS' % clock.get_fps(), |
|||
'', |
|||
'Vehicle: % 20s' % get_actor_display_name(world.player, truncate=20), |
|||
'Map: % 20s' % world.map.name.split('/')[-1], |
|||
'Simulation time: % 12s' % datetime.timedelta(seconds=int(self.simulation_time)), |
|||
'', |
|||
'Speed: % 15.0f km/h' % (3.6 * math.sqrt(vel.x**2 + vel.y**2 + vel.z**2)), |
|||
u'Heading:% 16.0f\N{DEGREE SIGN} % 2s' % (transform.rotation.yaw, heading), |
|||
'Location:% 20s' % ('(% 5.1f, % 5.1f)' % (transform.location.x, transform.location.y)), |
|||
'GNSS:% 24s' % ('(% 2.6f, % 3.6f)' % (world.gnss_sensor.lat, world.gnss_sensor.lon)), |
|||
'Height: % 18.0f m' % transform.location.z, |
|||
''] |
|||
if isinstance(control, carla.VehicleControl): |
|||
self._info_text += [ |
|||
('Throttle:', control.throttle, 0.0, 1.0), |
|||
('Steer:', control.steer, -1.0, 1.0), |
|||
('Brake:', control.brake, 0.0, 1.0), |
|||
('Reverse:', control.reverse), |
|||
('Hand brake:', control.hand_brake), |
|||
('Manual:', control.manual_gear_shift), |
|||
'Gear: %s' % {-1: 'R', 0: 'N'}.get(control.gear, control.gear)] |
|||
elif isinstance(control, carla.WalkerControl): |
|||
self._info_text += [ |
|||
('Speed:', control.speed, 0.0, 5.556), |
|||
('Jump:', control.jump)] |
|||
self._info_text += [ |
|||
'', |
|||
'Collision:', |
|||
collision, |
|||
'', |
|||
'Number of vehicles: % 8d' % len(vehicles)] |
|||
|
|||
if len(vehicles) > 1: |
|||
self._info_text += ['Nearby vehicles:'] |
|||
|
|||
def dist(l): |
|||
return math.sqrt((l.x - transform.location.x)**2 + (l.y - transform.location.y) |
|||
** 2 + (l.z - transform.location.z)**2) |
|||
vehicles = [(dist(x.get_location()), x) for x in vehicles if x.id != world.player.id] |
|||
|
|||
for dist, vehicle in sorted(vehicles): |
|||
if dist > 200.0: |
|||
break |
|||
vehicle_type = get_actor_display_name(vehicle, truncate=22) |
|||
self._info_text.append('% 4dm %s' % (dist, vehicle_type)) |
|||
|
|||
def toggle_info(self): |
|||
"""Toggle info on or off""" |
|||
self._show_info = not self._show_info |
|||
|
|||
def notification(self, text, seconds=2.0): |
|||
"""Notification text""" |
|||
self._notifications.set_text(text, seconds=seconds) |
|||
|
|||
def error(self, text): |
|||
"""Error text""" |
|||
self._notifications.set_text('Error: %s' % text, (255, 0, 0)) |
|||
|
|||
def render(self, display): |
|||
"""Render for HUD class""" |
|||
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 - 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) |
|||
fig = (item[1] - item[2]) / (item[3] - item[2]) |
|||
if item[2] < 0.0: |
|||
rect = pygame.Rect( |
|||
(bar_h_offset + fig * (bar_width - 6), v_offset + 8), (6, 6)) |
|||
else: |
|||
rect = pygame.Rect((bar_h_offset, v_offset + 8), (fig * bar_width, 6)) |
|||
pygame.draw.rect(display, (255, 255, 255), rect) |
|||
item = item[0] |
|||
if item: # At this point has to be a str. |
|||
surface = self._font_mono.render(item, True, (255, 255, 255)) |
|||
display.blit(surface, (8, v_offset)) |
|||
v_offset += 18 |
|||
self._notifications.render(display) |
|||
self.help.render(display) |
|||
|
|||
# ============================================================================== |
|||
# -- FadingText ---------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class FadingText(object): |
|||
""" Class for fading text """ |
|||
|
|||
def __init__(self, font, dim, pos): |
|||
"""Constructor method""" |
|||
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): |
|||
"""Set fading text""" |
|||
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): |
|||
"""Fading text method for every tick""" |
|||
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): |
|||
"""Render fading text method""" |
|||
display.blit(self.surface, self.pos) |
|||
|
|||
# ============================================================================== |
|||
# -- HelpText ------------------------------------------------------------------ |
|||
# ============================================================================== |
|||
|
|||
|
|||
class HelpText(object): |
|||
""" Helper class for text render""" |
|||
|
|||
def __init__(self, font, width, height): |
|||
"""Constructor method""" |
|||
lines = __doc__.split('\n') |
|||
self.font = font |
|||
self.dim = (680, len(lines) * 22 + 12) |
|||
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)) |
|||
for i, line in enumerate(lines): |
|||
text_texture = self.font.render(line, True, (255, 255, 255)) |
|||
self.surface.blit(text_texture, (22, i * 22)) |
|||
self._render = False |
|||
self.surface.set_alpha(220) |
|||
|
|||
def toggle(self): |
|||
"""Toggle on or off the render help""" |
|||
self._render = not self._render |
|||
|
|||
def render(self, display): |
|||
"""Render help text method""" |
|||
if self._render: |
|||
display.blit(self.surface, self.pos) |
|||
|
|||
# ============================================================================== |
|||
# -- CollisionSensor ----------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class CollisionSensor(object): |
|||
""" Class for collision sensors""" |
|||
|
|||
def __init__(self, parent_actor, hud): |
|||
"""Constructor method""" |
|||
self.sensor = None |
|||
self.history = [] |
|||
self._parent = parent_actor |
|||
self.hud = hud |
|||
world = self._parent.get_world() |
|||
blueprint = world.get_blueprint_library().find('sensor.other.collision') |
|||
self.sensor = world.spawn_actor(blueprint, 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)) |
|||
|
|||
def get_collision_history(self): |
|||
"""Gets the history of collisions""" |
|||
history = collections.defaultdict(int) |
|||
for frame, intensity in self.history: |
|||
history[frame] += intensity |
|||
return history |
|||
|
|||
@staticmethod |
|||
def _on_collision(weak_self, event): |
|||
"""On collision method""" |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
actor_type = get_actor_display_name(event.other_actor) |
|||
self.hud.notification('Collision with %r' % actor_type) |
|||
impulse = event.normal_impulse |
|||
intensity = math.sqrt(impulse.x ** 2 + impulse.y ** 2 + impulse.z ** 2) |
|||
self.history.append((event.frame, intensity)) |
|||
if len(self.history) > 4000: |
|||
self.history.pop(0) |
|||
|
|||
# ============================================================================== |
|||
# -- LaneInvasionSensor -------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class LaneInvasionSensor(object): |
|||
"""Class for lane invasion sensors""" |
|||
|
|||
def __init__(self, parent_actor, hud): |
|||
"""Constructor method""" |
|||
self.sensor = None |
|||
self._parent = parent_actor |
|||
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): |
|||
"""On invasion method""" |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
lane_types = set(x.type for x in event.crossed_lane_markings) |
|||
text = ['%r' % str(x).split()[-1] for x in lane_types] |
|||
self.hud.notification('Crossed line %s' % ' and '.join(text)) |
|||
|
|||
# ============================================================================== |
|||
# -- GnssSensor -------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class GnssSensor(object): |
|||
""" Class for GNSS sensors""" |
|||
|
|||
def __init__(self, parent_actor): |
|||
"""Constructor method""" |
|||
self.sensor = None |
|||
self._parent = parent_actor |
|||
self.lat = 0.0 |
|||
self.lon = 0.0 |
|||
world = self._parent.get_world() |
|||
blueprint = world.get_blueprint_library().find('sensor.other.gnss') |
|||
self.sensor = world.spawn_actor(blueprint, 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)) |
|||
|
|||
@staticmethod |
|||
def _on_gnss_event(weak_self, event): |
|||
"""GNSS method""" |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
self.lat = event.latitude |
|||
self.lon = event.longitude |
|||
|
|||
# ============================================================================== |
|||
# -- CameraManager ------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class CameraManager(object): |
|||
""" Class for camera management""" |
|||
|
|||
def __init__(self, parent_actor, hud): |
|||
"""Constructor method""" |
|||
self.sensor = None |
|||
self.surface = None |
|||
self._parent = parent_actor |
|||
self.hud = hud |
|||
self.recording = False |
|||
bound_x = 0.5 + self._parent.bounding_box.extent.x |
|||
bound_y = 0.5 + self._parent.bounding_box.extent.y |
|||
bound_z = 0.5 + self._parent.bounding_box.extent.z |
|||
attachment = carla.AttachmentType |
|||
self._camera_transforms = [ |
|||
(carla.Transform(carla.Location(x=-2.0*bound_x, y=+0.0*bound_y, z=2.0*bound_z), carla.Rotation(pitch=8.0)), attachment.SpringArmGhost), |
|||
(carla.Transform(carla.Location(x=+0.8*bound_x, y=+0.0*bound_y, z=1.3*bound_z)), attachment.Rigid), |
|||
(carla.Transform(carla.Location(x=+1.9*bound_x, y=+1.0*bound_y, z=1.2*bound_z)), attachment.SpringArmGhost), |
|||
(carla.Transform(carla.Location(x=-2.8*bound_x, y=+0.0*bound_y, z=4.6*bound_z), carla.Rotation(pitch=6.0)), attachment.SpringArmGhost), |
|||
(carla.Transform(carla.Location(x=-1.0, y=-1.0*bound_y, z=0.4*bound_z)), attachment.Rigid)] |
|||
|
|||
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)'], |
|||
['sensor.camera.semantic_segmentation', cc.CityScapesPalette, |
|||
'Camera Semantic Segmentation (CityScapes Palette)'], |
|||
['sensor.lidar.ray_cast', None, 'Lidar (Ray-Cast)']] |
|||
world = self._parent.get_world() |
|||
bp_library = world.get_blueprint_library() |
|||
for item in self.sensors: |
|||
blp = bp_library.find(item[0]) |
|||
if item[0].startswith('sensor.camera'): |
|||
blp.set_attribute('image_size_x', str(hud.dim[0])) |
|||
blp.set_attribute('image_size_y', str(hud.dim[1])) |
|||
elif item[0].startswith('sensor.lidar'): |
|||
blp.set_attribute('range', '50') |
|||
item.append(blp) |
|||
self.index = None |
|||
|
|||
def toggle_camera(self): |
|||
"""Activate a camera""" |
|||
self.transform_index = (self.transform_index + 1) % len(self._camera_transforms) |
|||
self.set_sensor(self.index, notify=False, force_respawn=True) |
|||
|
|||
def set_sensor(self, index, notify=True, force_respawn=False): |
|||
"""Set a sensor""" |
|||
index = index % len(self.sensors) |
|||
needs_respawn = True if self.index is None else ( |
|||
force_respawn or (self.sensors[index][0] != self.sensors[self.index][0])) |
|||
if needs_respawn: |
|||
if self.sensor is not None: |
|||
self.sensor.destroy() |
|||
self.surface = None |
|||
self.sensor = self._parent.get_world().spawn_actor( |
|||
self.sensors[index][-1], |
|||
self._camera_transforms[self.transform_index][0], |
|||
attach_to=self._parent, |
|||
attachment_type=self._camera_transforms[self.transform_index][1]) |
|||
|
|||
# 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)) |
|||
if notify: |
|||
self.hud.notification(self.sensors[index][2]) |
|||
self.index = index |
|||
|
|||
def next_sensor(self): |
|||
"""Get the next sensor""" |
|||
self.set_sensor(self.index + 1) |
|||
|
|||
def toggle_recording(self): |
|||
"""Toggle recording on or off""" |
|||
self.recording = not self.recording |
|||
self.hud.notification('Recording %s' % ('On' if self.recording else 'Off')) |
|||
|
|||
def render(self, display): |
|||
"""Render method""" |
|||
if self.surface is not None: |
|||
display.blit(self.surface, (0, 0)) |
|||
|
|||
@staticmethod |
|||
def _parse_image(weak_self, image): |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
if self.sensors[self.index][0].startswith('sensor.lidar'): |
|||
points = np.frombuffer(image.raw_data, dtype=np.dtype('f4')) |
|||
points = np.reshape(points, (int(points.shape[0] / 4), 4)) |
|||
lidar_data = np.array(points[:, :2]) |
|||
lidar_data *= min(self.hud.dim) / 100.0 |
|||
lidar_data += (0.5 * self.hud.dim[0], 0.5 * self.hud.dim[1]) |
|||
lidar_data = np.fabs(lidar_data) # pylint: disable=assignment-from-no-return |
|||
lidar_data = lidar_data.astype(np.int32) |
|||
lidar_data = np.reshape(lidar_data, (-1, 2)) |
|||
lidar_img_size = (self.hud.dim[0], self.hud.dim[1], 3) |
|||
lidar_img = np.zeros(lidar_img_size) |
|||
lidar_img[tuple(lidar_data.T)] = (255, 255, 255) |
|||
self.surface = pygame.surfarray.make_surface(lidar_img) |
|||
else: |
|||
image.convert(self.sensors[self.index][1]) |
|||
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] |
|||
self.surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) |
|||
if self.recording: |
|||
image.save_to_disk('_out/%08d' % image.frame) |
|||
|
|||
# ============================================================================== |
|||
# -- Game Loop --------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
def game_loop(args): |
|||
""" |
|||
Main loop of the simulation. It handles updating all the HUD information, |
|||
ticking the agent and, if needed, the world. |
|||
""" |
|||
|
|||
pygame.init() |
|||
pygame.font.init() |
|||
world = None |
|||
|
|||
try: |
|||
if args.seed: |
|||
random.seed(args.seed) |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(60.0) |
|||
|
|||
traffic_manager = client.get_trafficmanager() |
|||
sim_world = client.get_world() |
|||
|
|||
if args.sync: |
|||
settings = sim_world.get_settings() |
|||
settings.synchronous_mode = True |
|||
settings.fixed_delta_seconds = 0.05 |
|||
sim_world.apply_settings(settings) |
|||
|
|||
traffic_manager.set_synchronous_mode(True) |
|||
|
|||
display = pygame.display.set_mode( |
|||
(args.width, args.height), |
|||
pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
|
|||
hud = HUD(args.width, args.height) |
|||
world = World(client.get_world(), hud, args) |
|||
controller = KeyboardControl(world) |
|||
if args.agent == "Basic": |
|||
agent = BasicAgent(world.player, 30) |
|||
agent.follow_speed_limits(True) |
|||
elif args.agent == "Constant": |
|||
agent = ConstantVelocityAgent(world.player, 30) |
|||
ground_loc = world.world.ground_projection(world.player.get_location(), 5) |
|||
if ground_loc: |
|||
world.player.set_location(ground_loc.location + carla.Location(z=0.01)) |
|||
agent.follow_speed_limits(True) |
|||
elif args.agent == "Behavior": |
|||
agent = BehaviorAgent(world.player, behavior=args.behavior) |
|||
|
|||
# Set the agent destination |
|||
spawn_points = world.map.get_spawn_points() |
|||
destination = random.choice(spawn_points).location |
|||
agent.set_destination(destination) |
|||
|
|||
clock = pygame.time.Clock() |
|||
|
|||
while True: |
|||
clock.tick() |
|||
if args.sync: |
|||
world.world.tick() |
|||
else: |
|||
world.world.wait_for_tick() |
|||
if controller.parse_events(): |
|||
return |
|||
|
|||
world.tick(clock) |
|||
world.render(display) |
|||
pygame.display.flip() |
|||
|
|||
if agent.done(): |
|||
if args.loop: |
|||
agent.set_destination(random.choice(spawn_points).location) |
|||
world.hud.notification("Target reached", seconds=4.0) |
|||
print("The target has been reached, searching for another target") |
|||
else: |
|||
print("The target has been reached, stopping the simulation") |
|||
break |
|||
|
|||
control = agent.run_step() |
|||
control.manual_gear_shift = False |
|||
world.player.apply_control(control) |
|||
|
|||
finally: |
|||
|
|||
if world is not None: |
|||
settings = world.world.get_settings() |
|||
settings.synchronous_mode = False |
|||
settings.fixed_delta_seconds = None |
|||
world.world.apply_settings(settings) |
|||
traffic_manager.set_synchronous_mode(True) |
|||
|
|||
world.destroy() |
|||
|
|||
pygame.quit() |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- main() -------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
def main(): |
|||
"""Main method""" |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description='CARLA Automatic Control Client') |
|||
argparser.add_argument( |
|||
'-v', '--verbose', |
|||
action='store_true', |
|||
dest='debug', |
|||
help='Print debug information') |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'--res', |
|||
metavar='WIDTHxHEIGHT', |
|||
default='1280x720', |
|||
help='Window resolution (default: 1280x720)') |
|||
argparser.add_argument( |
|||
'--sync', |
|||
action='store_true', |
|||
help='Synchronous mode execution') |
|||
argparser.add_argument( |
|||
'--filter', |
|||
metavar='PATTERN', |
|||
default='vehicle.*', |
|||
help='Actor filter (default: "vehicle.*")') |
|||
argparser.add_argument( |
|||
'--generation', |
|||
metavar='G', |
|||
default='2', |
|||
help='restrict to certain actor generation (values: "1","2","All" - default: "2")') |
|||
argparser.add_argument( |
|||
'-l', '--loop', |
|||
action='store_true', |
|||
dest='loop', |
|||
help='Sets a new random destination upon reaching the previous one (default: False)') |
|||
argparser.add_argument( |
|||
"-a", "--agent", type=str, |
|||
choices=["Behavior", "Basic", "Constant"], |
|||
help="select which agent to run", |
|||
default="Behavior") |
|||
argparser.add_argument( |
|||
'-b', '--behavior', type=str, |
|||
choices=["cautious", "normal", "aggressive"], |
|||
help='Choose one of the possible agent behaviors (default: normal) ', |
|||
default='normal') |
|||
argparser.add_argument( |
|||
'-s', '--seed', |
|||
help='Set seed for repeating executions (default: None)', |
|||
default=None, |
|||
type=int) |
|||
|
|||
args = argparser.parse_args() |
|||
|
|||
args.width, args.height = [int(x) for x in args.res.split('x')] |
|||
|
|||
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) |
|||
|
|||
print(__doc__) |
|||
|
|||
try: |
|||
game_loop(args) |
|||
|
|||
except KeyboardInterrupt: |
|||
print('\nCancelled by user. Bye!') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
main() |
|||
@ -0,0 +1,484 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
# Generates 2D and 3D bounding boxes for a simulation and can save them as JSON |
|||
# Instructions: |
|||
|
|||
""" |
|||
Welcome to CARLA bounding boxes. |
|||
R : toggle recording images and bounding boxes |
|||
3 : visualize bounding boxes in 3D |
|||
2 : vizualize bounding boxes in 2D |
|||
ESC : quit |
|||
""" |
|||
|
|||
import carla |
|||
import json |
|||
import random |
|||
import queue |
|||
import pygame |
|||
import argparse |
|||
import numpy as np |
|||
from math import radians |
|||
|
|||
from pygame.locals import K_ESCAPE |
|||
from pygame.locals import K_2 |
|||
from pygame.locals import K_3 |
|||
from pygame.locals import K_r |
|||
|
|||
# Bounding box edge topology order |
|||
EDGES = [[0,1], [1,3], [3,2], [2,0], [0,4], [4,5], [5,1], [5,7], [7,6], [6,4], [6,2], [7,3]] |
|||
|
|||
# Map for CARLA semantic labels to class names and colors |
|||
SEMANTIC_MAP = {0: ('unlabelled', (0,0,0)), 1: ('road', (128,64,0)),2: ('sidewalk', (244,35,232)), |
|||
3: ('building', (70,70,70)), 4: ('wall', (102,102,156)), 5: ('fence', (190,153,153)), |
|||
6: ('pole', (153,153,153)), 7: ('traffic light', (250,170,30)), |
|||
8: ('traffic sign', (220,220,0)), 9: ('vegetation', (107,142,35)), |
|||
10: ('terrain', (152,251,152)), 11: ('sky', (70,130,180)), |
|||
12: ('pedestrian', (220,20,60)), 13: ('rider', (255,0,0)), |
|||
14: ('car', (0,0,142)), 15: ('truck', (0,0,70)), 16: ('bus', (0,60,100)), |
|||
17: ('train', (0,80,100)), 18: ('motorcycle', (0,0,230)), |
|||
19: ('bicycle', (119,11,32)), 20: ('static', (110,190,160)), |
|||
21: ('dynamic', (170,120,50)), 22: ('other', (55,90,80)), |
|||
23: ('water', (45,60,150)), 24: ('road line', (157,234,50)), |
|||
25: ('ground', (81,0,81)), 26: ('bridge', (150,100,100)), |
|||
27: ('rail track', (230,150,140)), 28: ('guard rail', (180,165,180))} |
|||
|
|||
# Calculate the camera projection matrix |
|||
def build_projection_matrix(w, h, fov, is_behind_camera=False): |
|||
focal = w / (2.0 * np.tan(fov * np.pi / 360.0)) |
|||
K = np.identity(3) |
|||
|
|||
if is_behind_camera: |
|||
K[0, 0] = K[1, 1] = -focal |
|||
else: |
|||
K[0, 0] = K[1, 1] = focal |
|||
|
|||
K[0, 2] = w / 2.0 |
|||
K[1, 2] = h / 2.0 |
|||
return K |
|||
|
|||
# Calculate 2D projection of 3D coordinate |
|||
def get_image_point(loc, K, w2c): |
|||
|
|||
# Format the input coordinate (loc is a carla.Position object) |
|||
point = np.array([loc.x, loc.y, loc.z, 1]) |
|||
# transform to camera coordinates |
|||
point_camera = np.dot(w2c, point) |
|||
|
|||
# New we must change from UE4's coordinate system to an "standard" |
|||
# (x, y ,z) -> (y, -z, x) |
|||
# and we remove the fourth componebonent also |
|||
point_camera = [point_camera[1], -point_camera[2], point_camera[0]] |
|||
|
|||
# now project 3D->2D using the camera matrix |
|||
point_img = np.dot(K, point_camera) |
|||
# normalize |
|||
point_img[0] /= point_img[2] |
|||
point_img[1] /= point_img[2] |
|||
|
|||
return point_img[0:2] |
|||
|
|||
# Verify that the point is within the image plane |
|||
def point_in_canvas(pos, img_h, img_w): |
|||
"""Return true if point is in canvas""" |
|||
if (pos[0] >= 0) and (pos[0] < img_w) and (pos[1] >= 0) and (pos[1] < img_h): |
|||
return True |
|||
return False |
|||
|
|||
# Decode the instance segmentation map into semantic labels and actor IDs |
|||
def decode_instance_segmentation(img_rgba: np.ndarray): |
|||
semantic_labels = img_rgba[..., 2] # R channel |
|||
actor_ids = img_rgba[..., 1].astype(np.uint16) + (img_rgba[..., 0].astype(np.uint16) << 8) |
|||
return semantic_labels, actor_ids |
|||
|
|||
# Generate a 2D bounding box for an actor from the actor ID image |
|||
def bbox_2d_for_actor(actor, actor_ids: np.ndarray, semantic_labels: np.ndarray): |
|||
mask = (actor_ids == actor.id) |
|||
if not np.any(mask): |
|||
return None # actor not present |
|||
ys, xs = np.where(mask) |
|||
xmin, xmax = xs.min(), xs.max() |
|||
ymin, ymax = ys.min(), ys.max() |
|||
return {'actor_id': actor.id, |
|||
'semantic_label': actor.semantic_tags[0], |
|||
'bbox_2d': (xmin, ymin, xmax, ymax)} |
|||
|
|||
# Generate a 3D bounding box for an actor from the simulation |
|||
def bbox_3d_for_actor(actor, ego, camera_bp, camera): |
|||
|
|||
# Get the world to camera matrix |
|||
world_2_camera = np.array(camera.get_transform().get_inverse_matrix()) |
|||
|
|||
# Get the attributes from the camera |
|||
image_w = camera_bp.get_attribute("image_size_x").as_int() |
|||
image_h = camera_bp.get_attribute("image_size_y").as_int() |
|||
fov = camera_bp.get_attribute("fov").as_float() |
|||
|
|||
# Calculate the camera projection matrix to project from 3D -> 2D |
|||
K = build_projection_matrix(image_w, image_h, fov) |
|||
K_b = build_projection_matrix(image_w, image_h, fov, is_behind_camera=True) |
|||
|
|||
ego_bbox_loc = ego.get_transform().location + ego.bounding_box.location |
|||
ego_bbox_transform = carla.Transform(ego_bbox_loc, ego.get_transform().rotation) |
|||
|
|||
npc_bbox_loc = actor.get_transform().location + actor.bounding_box.location |
|||
#npc_bbox_transform = carla.Transform(npc_bbox_loc, actor.get_transform().rotation) |
|||
|
|||
npc_loc_ego_space = ego_bbox_transform.inverse_transform(npc_bbox_loc) |
|||
|
|||
verts = [v for v in actor.bounding_box.get_world_vertices(actor.get_transform())] |
|||
|
|||
projection = [] |
|||
for edge in EDGES: |
|||
p1 = get_image_point(verts[edge[0]], K, world_2_camera) |
|||
p2 = get_image_point(verts[edge[1]], K, world_2_camera) |
|||
|
|||
p1_in_canvas = point_in_canvas(p1, image_h, image_w) |
|||
p2_in_canvas = point_in_canvas(p2, image_h, image_w) |
|||
|
|||
if not p1_in_canvas and not p2_in_canvas: |
|||
continue |
|||
|
|||
ray0 = verts[edge[0]] - camera.get_transform().location |
|||
ray1 = verts[edge[1]] - camera.get_transform().location |
|||
cam_forward_vec = camera.get_transform().get_forward_vector() |
|||
|
|||
# One of the vertexes is behind the camera |
|||
if not (cam_forward_vec.dot(ray0) > 0): |
|||
p1 = get_image_point(verts[edge[0]], K_b, world_2_camera) |
|||
if not (cam_forward_vec.dot(ray1) > 0): |
|||
p2 = get_image_point(verts[edge[1]], K_b, world_2_camera) |
|||
|
|||
projection.append((int(p1[0]), int(p1[1]), int(p2[0]), int(p2[1]))) |
|||
|
|||
return {'actor_id': actor.id, |
|||
'semantic_label': actor.semantic_tags[0], |
|||
'bbox_3d': { |
|||
'center': { |
|||
'x': npc_loc_ego_space.x, |
|||
'y': npc_loc_ego_space.y, |
|||
'z': npc_loc_ego_space.z |
|||
}, |
|||
'dimensions': { |
|||
'length': actor.bounding_box.extent.x*2, |
|||
'width': actor.bounding_box.extent.y*2, |
|||
'height': actor.bounding_box.extent.z*2, |
|||
}, |
|||
'rotation_yaw': radians(actor.get_transform().rotation.yaw - ego.get_transform().rotation.yaw) |
|||
}, |
|||
'projection': projection |
|||
} |
|||
|
|||
# Visualize 2D bounding boxes in Pygame |
|||
def visualize_2d_bboxes(surface, img, bboxes): |
|||
|
|||
rgb_img = img[:, :, :3][:, :, ::-1] |
|||
frame_surface = pygame.surfarray.make_surface(np.transpose(rgb_img[..., 0:3], (1,0,2))) |
|||
surface.blit(frame_surface, (0, 0)) |
|||
|
|||
font = pygame.font.SysFont("Arial", 18) |
|||
|
|||
for item in bboxes: |
|||
bbox = item['2d'] |
|||
if bbox is not None: |
|||
xmin, ymin, xmax, ymax = [int(v) for v in bbox['bbox_2d']] |
|||
label = SEMANTIC_MAP[bbox['semantic_label']][0] |
|||
color = SEMANTIC_MAP[bbox['semantic_label']][1] |
|||
pygame.draw.rect(surface, color, pygame.Rect(xmin, ymin, xmax-xmin, ymax-ymin), 2) |
|||
text_surface = font.render(label, True, (255,255,255), color) |
|||
text_rect = text_surface.get_rect(topleft=(xmin, ymin-20)) |
|||
surface.blit(text_surface, text_rect) |
|||
|
|||
return surface |
|||
|
|||
# Visualize 3D bounding boxes in Pygame |
|||
def visualize_3d_bboxes(surface, img, bboxes): |
|||
|
|||
rgb_img = img[:, :, :3][:, :, ::-1] |
|||
frame_surface = pygame.surfarray.make_surface(np.transpose(rgb_img[..., 0:3], (1,0,2))) |
|||
surface.blit(frame_surface, (0, 0)) |
|||
|
|||
for item in bboxes: |
|||
bbox = item['3d'] |
|||
color = SEMANTIC_MAP[bbox['semantic_label']][1] |
|||
|
|||
n = 0 |
|||
mean_x = 0 |
|||
mean_y = 0 |
|||
for line in bbox['projection']: |
|||
mean_x += line[0] |
|||
mean_y += line[1] |
|||
n += 1 |
|||
pygame.draw.line(surface, color, (line[0], line[1]), (line[2],line[3]), 2) |
|||
|
|||
if n > 0: |
|||
mean_x /= n |
|||
mean_y /= n |
|||
|
|||
# --- Render label --- |
|||
font = pygame.font.SysFont("Arial", 18) |
|||
text_surface = font.render(SEMANTIC_MAP[bbox['semantic_label']][0], True, (255,255,255), color) # black text, filled bg |
|||
text_rect = text_surface.get_rect(topleft=(mean_x, mean_y)) |
|||
surface.blit(text_surface, text_rect) |
|||
|
|||
def calculate_relative_velocity(actor, ego): |
|||
# Calculate the relative velocity in world frame |
|||
rel_vel = actor.get_velocity() - ego.get_velocity() |
|||
# Now convert to local frame of ego |
|||
vel_ego_frame = ego.get_transform().inverse_transform(rel_vel) |
|||
|
|||
return { |
|||
'x': vel_ego_frame.x, |
|||
'y': vel_ego_frame.y, |
|||
'z': vel_ego_frame.z |
|||
} |
|||
|
|||
def vehicle_light_state_to_dict(vehicle: carla.Vehicle): |
|||
state = vehicle.get_light_state() |
|||
return { |
|||
"position": bool(state & carla.VehicleLightState.Position), |
|||
"low_beam": bool(state & carla.VehicleLightState.LowBeam), |
|||
"high_beam": bool(state & carla.VehicleLightState.HighBeam), |
|||
"brake": bool(state & carla.VehicleLightState.Brake), |
|||
"reverse": bool(state & carla.VehicleLightState.Reverse), |
|||
"left_blinker": bool(state & carla.VehicleLightState.LeftBlinker), |
|||
"right_blinker":bool(state & carla.VehicleLightState.RightBlinker), |
|||
"fog": bool(state & carla.VehicleLightState.Fog), |
|||
"interior": bool(state & carla.VehicleLightState.Interior), |
|||
"special1": bool(state & carla.VehicleLightState.Special1), |
|||
"special2": bool(state & carla.VehicleLightState.Special2), |
|||
} |
|||
|
|||
def main(): |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description='CARLA bounding boxes') |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-d', '--distance', |
|||
metavar='D', |
|||
default=50, |
|||
type=int, |
|||
help='Actor distance threshold') |
|||
argparser.add_argument( |
|||
'--res', |
|||
metavar='WIDTHxHEIGHT', |
|||
default='1280x720', |
|||
help='window resolution (default: 1280x720)') |
|||
args = argparser.parse_args() |
|||
|
|||
args.width, args.height = [int(x) for x in args.res.split('x')] |
|||
|
|||
pygame.init() |
|||
|
|||
# State variables |
|||
record = False |
|||
display_3d = False |
|||
run_simulation = True |
|||
|
|||
clock = pygame.time.Clock() |
|||
pygame.display.set_caption("Bounding Box Visualization") |
|||
display = pygame.display.set_mode( |
|||
(args.width, args.height), |
|||
pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
display.fill((0,0,0)) |
|||
pygame.display.flip() |
|||
|
|||
# Connect to the CARLA server and get the world object |
|||
client = carla.Client(args.host, args.port) |
|||
world = client.get_world() |
|||
|
|||
# Set up the simulator in synchronous mode |
|||
settings = world.get_settings() |
|||
settings.synchronous_mode = True # Enables synchronous mode |
|||
settings.fixed_delta_seconds = 0.05 |
|||
world.apply_settings(settings) |
|||
|
|||
# Set the traffic manager to Synchronous mode |
|||
traffic_manager = client.get_trafficmanager() |
|||
traffic_manager.set_synchronous_mode(True) |
|||
|
|||
bp_lib = world.get_blueprint_library() |
|||
|
|||
# Get the map spawn points |
|||
spawn_points = world.get_map().get_spawn_points() |
|||
|
|||
# spawn vehicle |
|||
vehicle_bp =bp_lib.find('vehicle.lincoln.mkz_2020') |
|||
ego_vehicle = world.try_spawn_actor(vehicle_bp, random.choice(spawn_points)) |
|||
|
|||
# spawn RGB camera |
|||
camera_bp = bp_lib.find('sensor.camera.rgb') |
|||
camera_bp.set_attribute('image_size_x', str(args.width)) |
|||
camera_bp.set_attribute('image_size_y', str(args.height)) |
|||
camera_init_trans = carla.Transform(carla.Location(z=2)) |
|||
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=ego_vehicle) |
|||
|
|||
# spawn instance segmentation camera |
|||
inst_camera_bp = bp_lib.find('sensor.camera.instance_segmentation') |
|||
inst_camera_bp.set_attribute('image_size_x', str(args.width)) |
|||
inst_camera_bp.set_attribute('image_size_y', str(args.height)) |
|||
camera_init_trans = carla.Transform(carla.Location(z=2)) |
|||
inst_camera = world.spawn_actor(inst_camera_bp, camera_init_trans, attach_to=ego_vehicle) |
|||
|
|||
ego_vehicle.set_autopilot(True) |
|||
|
|||
# Add some traffic |
|||
npcs = [] |
|||
for i in range(100): |
|||
vehicle_bp = random.choice(bp_lib.filter('vehicle')) |
|||
npc = world.try_spawn_actor(vehicle_bp, random.choice(spawn_points)) |
|||
if npc: |
|||
npc.set_autopilot(True) |
|||
npcs.append(npc) |
|||
|
|||
# Create queues to store and retrieve the sensor data |
|||
image_queue = queue.Queue() |
|||
camera.listen(image_queue.put) |
|||
|
|||
inst_queue = queue.Queue() |
|||
inst_camera.listen(inst_queue.put) |
|||
|
|||
try: |
|||
while run_simulation: |
|||
for event in pygame.event.get(): |
|||
if event.type == pygame.KEYUP: |
|||
if event.key == K_r: |
|||
record = True |
|||
if event.key == K_2: |
|||
display_3d = False |
|||
if event.key == K_3: |
|||
display_3d = True |
|||
if event.key == K_ESCAPE: |
|||
run_simulation = False |
|||
if event.type == pygame.QUIT: |
|||
run_simulation = False |
|||
|
|||
world.tick() |
|||
snapshot = world.get_snapshot() |
|||
|
|||
json_frame_data = { |
|||
'frame_id': snapshot.frame, |
|||
'timestamp': snapshot.timestamp.elapsed_seconds, |
|||
'objects': [] |
|||
} |
|||
|
|||
image = image_queue.get() |
|||
img = np.reshape(np.copy(image.raw_data), (image.height, image.width, 4)) |
|||
|
|||
if record: |
|||
image.save_to_disk('_out/%08d' % image.frame) |
|||
|
|||
inst_seg_image = inst_queue.get() |
|||
inst_seg = np.reshape(np.copy(inst_seg_image.raw_data), (inst_seg_image.height, inst_seg_image.width, 4)) |
|||
|
|||
# Decode instance segmentation image |
|||
semantic_labels, actor_ids = decode_instance_segmentation(inst_seg) |
|||
|
|||
# Empty list to collect bounding boxes for this frame |
|||
frame_bboxes = [] |
|||
|
|||
# Loop through the NPCs in the simulation |
|||
for npc in world.get_actors().filter('*vehicle*'): |
|||
|
|||
# Filter out the ego vehicle |
|||
if npc.id !=ego_vehicle.id: |
|||
|
|||
npc_bbox = npc.bounding_box |
|||
dist = npc.get_transform().location.distance(ego_vehicle.get_transform().location) |
|||
|
|||
# Filter for the vehicles within 50m |
|||
if dist < args.distance: |
|||
|
|||
# Limit to vehicles in front of the camera |
|||
forward_vec = camera.get_transform().get_forward_vector() |
|||
inter_vehicle_vec = npc.get_transform().location - camera.get_transform().location |
|||
|
|||
if forward_vec.dot(inter_vehicle_vec) > 0: |
|||
|
|||
# Generate 2D and 2D bounding boxes for each actor |
|||
npc_bbox_2d = bbox_2d_for_actor(npc, actor_ids, semantic_labels) |
|||
npc_bbox_3d = bbox_3d_for_actor(npc, ego_vehicle, camera_bp, camera) |
|||
|
|||
frame_bboxes.append({'3d': npc_bbox_3d, '2d': npc_bbox_2d}) |
|||
|
|||
json_frame_data['objects'].append({ |
|||
'id': npc.id, |
|||
'class': SEMANTIC_MAP[npc.semantic_tags[0]][0], |
|||
'blueprint_id': npc.type_id, |
|||
'velocity': calculate_relative_velocity(npc, ego_vehicle), |
|||
'bbox_3d': npc_bbox_3d['bbox_3d'], |
|||
'bbox_2d': { |
|||
'xmin': int(npc_bbox_2d['bbox_2d'][0]), |
|||
'ymin': int(npc_bbox_2d['bbox_2d'][1]), |
|||
'xmax': int(npc_bbox_2d['bbox_2d'][2]), |
|||
'ymax': int(npc_bbox_2d['bbox_2d'][3]), |
|||
} if npc_bbox_2d else None, |
|||
'light_state': vehicle_light_state_to_dict(npc) |
|||
|
|||
}) |
|||
|
|||
# Draw the scene in Pygame |
|||
display.fill((0,0,0)) |
|||
if display_3d: |
|||
visualize_3d_bboxes(display, img, frame_bboxes) |
|||
else: |
|||
visualize_2d_bboxes(display, img, frame_bboxes) |
|||
pygame.display.flip() |
|||
clock.tick(30) # 30 FPS |
|||
if record: |
|||
with open(f"_out/{snapshot.frame}.json", 'w') as f: |
|||
json.dump(json_frame_data, f) |
|||
|
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
|
|||
ego_vehicle.destroy() |
|||
camera.stop() |
|||
camera.destroy() |
|||
inst_camera.stop() |
|||
inst_camera.destroy() |
|||
for npc in npcs: |
|||
npc.set_autopilot(False) |
|||
npc.destroy() |
|||
|
|||
world.tick() |
|||
|
|||
# Set up the simulator in synchronous mode |
|||
settings = world.get_settings() |
|||
settings.synchronous_mode = False # Disables synchronous mode |
|||
settings.fixed_delta_seconds = None |
|||
world.apply_settings(settings) |
|||
|
|||
# Set the traffic manager to Synchronous mode |
|||
traffic_manager.set_synchronous_mode(False) |
|||
|
|||
pygame.quit() |
|||
|
|||
print('\ndone.') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
print('Bounding boxes script instructions:') |
|||
print('R : toggle recording images as PNG and bounding boxes as JSON') |
|||
print('3 : view the bounding boxes in 3D') |
|||
print('2 : view the bounding boxes in 2D') |
|||
print('ESC : quit') |
|||
main() |
|||
@ -0,0 +1,376 @@ |
|||
#!/usr/bin/env python3 |
|||
|
|||
import argparse |
|||
import multiprocessing as mp |
|||
import logging |
|||
from enum import Enum |
|||
from dataclasses import dataclass |
|||
from typing import Dict, List, Sequence, Tuple |
|||
from pathlib import Path |
|||
import yaml |
|||
import subprocess |
|||
import numpy as np |
|||
import cv2 |
|||
import carla |
|||
from PIL import Image |
|||
|
|||
# === ENUMS AND DATA STRUCTURES === |
|||
class AOV(Enum): |
|||
RGB = 0 |
|||
DEPTH = 1 |
|||
SEMANTIC_SEGMENTATION = 2 |
|||
INSTANCE_SEGMENTATION = 3 |
|||
NORMALS = 4 |
|||
COSMOS_VISUALIZATION = 5 |
|||
|
|||
@dataclass |
|||
class FrameBundle: |
|||
index: int |
|||
frames: Dict[AOV, np.ndarray] |
|||
timestamp: float |
|||
|
|||
def extract_between(input_string, left_delim, right_delim): |
|||
try: |
|||
start = input_string.index(left_delim) + len(left_delim) |
|||
end = input_string.index(right_delim, start) |
|||
return input_string[start:end] |
|||
except ValueError: |
|||
return None |
|||
|
|||
|
|||
def parse_frames_duration(info): |
|||
frames = extract_between(info, "Frames: ", "\n") |
|||
duration = extract_between(info, "Duration: ", " seconds") |
|||
|
|||
if frames and duration: |
|||
return int(frames), float(duration) |
|||
else: |
|||
return -1, -1.0 |
|||
|
|||
# === CONFIGURATION LOADERS === |
|||
CLASSES_TO_KEEP_SHADED_SEG: List[Sequence[int]] = [] |
|||
CLASSES_TO_KEEP_CANNY: List[Sequence[int]] = [] |
|||
|
|||
def load_class_filter_config(path: str): |
|||
with open(path, 'r') as f: |
|||
config = yaml.safe_load(f) |
|||
global CLASSES_TO_KEEP_SHADED_SEG, CLASSES_TO_KEEP_CANNY |
|||
CLASSES_TO_KEEP_SHADED_SEG = config.get('shaded_segmentation_classes', []) |
|||
CLASSES_TO_KEEP_CANNY = config.get('canny_classes', []) |
|||
|
|||
# === ORIGINAL POST-PROCESSING FUNCTIONS === |
|||
def masked_edges_from_semseg( |
|||
rgb_img: np.ndarray, |
|||
semseg_img: np.ndarray, |
|||
classes: List[Sequence[int]], |
|||
*, |
|||
gaussian_kernel: Tuple[int, int] = (5, 5), |
|||
gaussian_sigma: float = 1.0, |
|||
canny_thresh1: int = 100, |
|||
canny_thresh2: int = 200, |
|||
) -> Tuple[np.ndarray, np.ndarray]: |
|||
blurred_rgb = cv2.GaussianBlur(rgb_img, gaussian_kernel, gaussian_sigma) |
|||
mask = np.zeros(semseg_img.shape[:2], dtype=np.uint8) |
|||
for color in classes: |
|||
lower = np.array(color, dtype=np.uint8) |
|||
upper = np.array(color, dtype=np.uint8) |
|||
mask |= cv2.inRange(semseg_img, lower, upper) |
|||
mask_bool = mask.astype(bool) |
|||
masked_rgb = np.zeros_like(rgb_img) |
|||
masked_rgb[mask_bool] = blurred_rgb[mask_bool] |
|||
gray = cv2.cvtColor(masked_rgb, cv2.COLOR_RGB2GRAY) |
|||
edges = cv2.Canny(gray, canny_thresh1, canny_thresh2) |
|||
return masked_rgb, edges |
|||
|
|||
|
|||
def created_shaded_composition( |
|||
sem: np.ndarray, inst: np.ndarray, nor: np.ndarray, classes_to_keep: List[Sequence[int]] |
|||
) -> np.ndarray: |
|||
semantics = sem[..., ::-1] |
|||
instances = inst[..., ::-1] |
|||
normals = nor[..., ::-1] |
|||
light_source = np.array([1.0, 0.0, 0.0]) |
|||
mask = np.zeros(semantics.shape[:2], dtype=bool) |
|||
for color in classes_to_keep: |
|||
mask |= (semantics == np.array(color)).all(-1) |
|||
mask_exp = mask[..., None] |
|||
composed = np.where(mask_exp, semantics, instances) |
|||
normals_f = normals.astype(np.float32) / 255.0 |
|||
shading = np.dot(normals_f, light_source) |
|||
shaded_seg = (composed.astype(np.float32) * shading[..., None]).astype(np.uint8) |
|||
return shaded_seg |
|||
|
|||
|
|||
def create_shuffled_colormap( |
|||
size=65536, base_cmap_name='prism', seed=None, fix_zero=True |
|||
) -> np.ndarray: |
|||
import matplotlib.pyplot as plt |
|||
if seed is not None: |
|||
np.random.seed(seed) |
|||
try: |
|||
cmap_func = plt.get_cmap(base_cmap_name) |
|||
except ValueError: |
|||
cmap_func = plt.get_cmap('turbo') |
|||
base_colors = cmap_func(np.linspace(0, 1, size))[:, :3] |
|||
indices = np.arange(size) |
|||
if fix_zero: |
|||
shuffled = np.concatenate(([0], np.random.permutation(indices[1:]))) |
|||
else: |
|||
shuffled = np.random.permutation(indices) |
|||
shuffled_colors = base_colors[shuffled] |
|||
colormap_uint8 = (shuffled_colors * 255).astype(np.uint8) |
|||
if fix_zero: |
|||
colormap_uint8[0] = [0, 0, 0] |
|||
return colormap_uint8 |
|||
|
|||
|
|||
def reconstruct_ids_vectorized(image_data_uint8: np.ndarray) -> np.ndarray: |
|||
low = image_data_uint8[:, :, 1].astype(np.uint16) |
|||
high = image_data_uint8[:, :, 2].astype(np.uint16) |
|||
return (high << 8) | low |
|||
|
|||
|
|||
def apply_colormap_vectorized(ids_uint16: np.ndarray, colormap: np.ndarray) -> np.ndarray: |
|||
return colormap[ids_uint16] |
|||
|
|||
|
|||
def depth_to_log_grayscale( |
|||
depth_map: np.ndarray, |
|||
near_clip=0.01, |
|||
far_clip=1000.0, |
|||
inverted_depth=True |
|||
) -> Image.Image: |
|||
clipped = np.clip(depth_map, near_clip, far_clip) |
|||
log_depth = np.log(clipped) |
|||
norm_log = (log_depth - np.log(near_clip)) / (np.log(far_clip) - np.log(near_clip)) |
|||
if inverted_depth: |
|||
norm_log = 1.0 - norm_log |
|||
gray_img = (norm_log * 255).astype(np.uint8) |
|||
return Image.fromarray(gray_img) |
|||
|
|||
# Pre-generate colormap for instance segmentation |
|||
colormap_uint8 = create_shuffled_colormap(seed=140) |
|||
|
|||
# === SENSOR INFO WRAPPER === |
|||
class SensorInfo: |
|||
def __init__(self, sensor, stype: AOV): |
|||
self.sensor = sensor |
|||
self.sensor_type = stype |
|||
self.queue = mp.Queue() |
|||
sensor.listen(self._callback) |
|||
|
|||
def _callback(self, data): |
|||
conv_map = { |
|||
AOV.RGB: carla.ColorConverter.Raw, |
|||
AOV.SEMANTIC_SEGMENTATION: carla.ColorConverter.CityScapesPalette, |
|||
AOV.COSMOS_VISUALIZATION: carla.ColorConverter.Raw |
|||
} |
|||
conv = conv_map.get(self.sensor_type, carla.ColorConverter.Raw) |
|||
data.convert(conv) |
|||
arr = np.frombuffer(data.raw_data, dtype=np.uint8) |
|||
h, w = data.height, data.width |
|||
raw = arr.reshape((h, w, 4)) |
|||
img = raw if self.sensor_type == AOV.DEPTH else raw[:, :, :3] |
|||
self.queue.put((img.copy(), data.frame, data.timestamp)) |
|||
|
|||
def capture_current_frame(self): |
|||
try: |
|||
return self.queue.get(timeout=1.0) |
|||
except Exception: |
|||
return None |
|||
|
|||
# === WORKERS === |
|||
|
|||
def post_processing_worker(raw_q: mp.Queue, proc_q: mp.Queue): |
|||
logging.info(f"[{mp.current_process().name}] starting") |
|||
while True: |
|||
bundle = raw_q.get() |
|||
if bundle is None: |
|||
break |
|||
processed = {} |
|||
frames = bundle.frames |
|||
if AOV.RGB in frames: |
|||
processed['RGB'] = frames[AOV.RGB] |
|||
if AOV.RGB in frames and AOV.SEMANTIC_SEGMENTATION in frames: |
|||
masked, edges = masked_edges_from_semseg( |
|||
frames[AOV.RGB], frames[AOV.SEMANTIC_SEGMENTATION], CLASSES_TO_KEEP_CANNY |
|||
) |
|||
processed['RGB_MASKED'] = masked |
|||
processed['RGB_EDGES'] = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB) |
|||
if AOV.DEPTH in frames: |
|||
depth_bgra = frames[AOV.DEPTH] |
|||
scales = np.array([65536.0, 256.0, 1.0, 0.0]) / (256**3 - 1) * 1000 |
|||
depth_map = np.dot(depth_bgra, scales).astype(np.float32) |
|||
gray_img = depth_to_log_grayscale(depth_map) |
|||
processed['DEPTH'] = np.array(gray_img.convert('RGB')) |
|||
if AOV.SEMANTIC_SEGMENTATION in frames: |
|||
processed['SEMANTIC_SEGMENTATION'] = frames[AOV.SEMANTIC_SEGMENTATION] |
|||
if AOV.INSTANCE_SEGMENTATION in frames: |
|||
ids = reconstruct_ids_vectorized(frames[AOV.INSTANCE_SEGMENTATION]) |
|||
colored = apply_colormap_vectorized(ids, colormap_uint8) |
|||
processed['INSTANCE_SEGMENTATION'] = colored |
|||
if AOV.COSMOS_VISUALIZATION in frames: |
|||
processed['COSMOS_VISUALIZATION'] = frames[AOV.COSMOS_VISUALIZATION] |
|||
proc_q.put((bundle.index, processed)) |
|||
logging.info(f"[{mp.current_process().name}] exiting") |
|||
|
|||
|
|||
def video_writer_worker(proc_q: mp.Queue, out_dir: Path, fps: float): |
|||
logging.info("[Writer] starting") |
|||
writers = {} |
|||
paths = {} |
|||
write_count = 0 |
|||
|
|||
def get_writer(key: str, shape: Tuple[int, int]): |
|||
if key not in writers: |
|||
tmp = out_dir / f"{key.lower()}_tmp.mp4" |
|||
final = out_dir / f"{key.lower()}.mp4" |
|||
fourcc = cv2.VideoWriter_fourcc(*'mp4v') |
|||
w = cv2.VideoWriter(str(tmp), fourcc, fps, (shape[1], shape[0])) |
|||
writers[key] = w |
|||
paths[key] = (tmp, final) |
|||
return writers[key] |
|||
|
|||
while True: |
|||
item = proc_q.get() |
|||
if item is None: |
|||
break |
|||
idx, frames = item |
|||
for key, img in frames.items(): |
|||
get_writer(key, img.shape[:2]).write(img) |
|||
write_count += 1 |
|||
if write_count % 100 == 0: |
|||
logging.info(f"[Writer] wrote {write_count} frames total") |
|||
|
|||
for key, w in writers.items(): |
|||
w.release() |
|||
tmp, final = paths[key] |
|||
try: |
|||
subprocess.run(['ffmpeg', '-i', str(tmp), '-r', '24', '-c:v', 'libx264', |
|||
'-y', '-loglevel', 'error', str(final)], check=True, |
|||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
|||
except subprocess.CalledProcessError as e: |
|||
logging.error(f"FFmpeg failed for {key}: {e}") |
|||
tmp.unlink(missing_ok=True) |
|||
logging.info("[Writer] exiting") |
|||
|
|||
# === MAIN === |
|||
def main(): |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument('--sensors', type=str, required=True) |
|||
parser.add_argument('--class-filter-config', type=str) |
|||
parser.add_argument('-f','--recorder-filename', type=str, required=True) |
|||
parser.add_argument('-o','--output-dir', type=str, required=True) |
|||
parser.add_argument('-s','--start', type=float, default=0.0) |
|||
parser.add_argument('-d','--duration', type=float, default=0.0) |
|||
parser.add_argument('--host', type=str, default='127.0.0.1') |
|||
parser.add_argument('--port', type=int, default=2000) |
|||
parser.add_argument('-c','--camera', type=int, default=0) |
|||
parser.add_argument('--time-factor', type=float, default=1.0) |
|||
parser.add_argument('--ignore-hero', action='store_true') |
|||
parser.add_argument('--move-spectator', action='store_true') |
|||
parser.add_argument('--spawn-sensors', action='store_true') |
|||
parser.add_argument('--num-post-workers', type=int, default=max(1, mp.cpu_count()-1)) |
|||
args = parser.parse_args() |
|||
|
|||
logging.basicConfig( |
|||
level=logging.INFO, |
|||
format='%(asctime)s %(levelname)s %(processName)s: %(message)s' |
|||
) |
|||
logging.info("Starting CarlaCosmos-DataAcquisition parallel pipeline") |
|||
|
|||
if args.class_filter_config: |
|||
load_class_filter_config(args.class_filter_config) |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(60.0) |
|||
client.reload_world() |
|||
|
|||
info = client.show_recorder_file_info(args.recorder_filename, False) |
|||
log_frames, log_duration = parse_frames_duration(info) |
|||
|
|||
log_delta = log_duration / log_frames |
|||
fps = round(1.0 / log_delta) |
|||
logging.info(f"Recorder: {log_frames} frames, {log_duration:.2f}s, fps={fps}") |
|||
|
|||
client.set_replayer_time_factor(args.time_factor) |
|||
client.set_replayer_ignore_hero(args.ignore_hero) |
|||
client.set_replayer_ignore_spectator(not args.move_spectator) |
|||
client.replay_file( |
|||
args.recorder_filename, args.start, args.duration, args.camera, args.spawn_sensors |
|||
) |
|||
|
|||
world = client.get_world() |
|||
settings = world.get_settings() |
|||
settings.synchronous_mode = True |
|||
settings.fixed_delta_seconds = log_delta |
|||
world.apply_settings(settings) |
|||
|
|||
with open(args.sensors.replace('file:',''), 'r') as f: |
|||
sensor_cfg = yaml.safe_load(f) |
|||
vehicle = world.get_actor(args.camera) |
|||
sensor_infos = [] |
|||
for entry in sensor_cfg: |
|||
bp = world.get_blueprint_library().find(f"sensor.camera.{entry['sensor']}") |
|||
for k,v in entry.get('attributes',{}).items(): bp.set_attribute(k,str(v)) |
|||
tf = entry.get('transform',{}) |
|||
transform = carla.Transform( |
|||
carla.Location(**tf.get('location',{})), |
|||
carla.Rotation(**tf.get('rotation',{})) |
|||
) |
|||
sensor = world.spawn_actor(bp, transform, attach_to=vehicle) |
|||
|
|||
# If it's the cosmos visualization sensor, set it to ignore the ego vehicle |
|||
if entry['sensor'].upper() == 'COSMOS_VISUALIZATION': |
|||
sensor.set_ignored_vehicles([args.camera]) # Only this sensor ignores ego |
|||
|
|||
sensor_infos.append(SensorInfo(sensor, AOV[entry['sensor'].upper()])) |
|||
|
|||
raw_q = mp.Queue() |
|||
proc_q = mp.Queue() |
|||
workers = [] |
|||
for i in range(args.num_post_workers): |
|||
p = mp.Process( |
|||
target=post_processing_worker, |
|||
args=(raw_q, proc_q), |
|||
name=f"PostProc-{i}" |
|||
) |
|||
p.start(); workers.append(p) |
|||
|
|||
out_dir = Path(args.output_dir) |
|||
out_dir.mkdir(parents=True, exist_ok=True) |
|||
writer = mp.Process( |
|||
target=video_writer_worker, |
|||
args=(proc_q, out_dir, fps), |
|||
name="Writer" |
|||
) |
|||
writer.start() |
|||
|
|||
timestamp = args.start |
|||
total = log_duration if args.duration == 0.0 else args.duration |
|||
frame_count = 0 |
|||
try: |
|||
while timestamp < args.start + total: |
|||
idx = world.tick() |
|||
frame_dict = {} |
|||
for si in sensor_infos: |
|||
res = si.capture_current_frame() |
|||
if res: |
|||
img,_,_ = res |
|||
frame_dict[si.sensor_type] = img |
|||
raw_q.put(FrameBundle(idx, frame_dict, timestamp)) |
|||
frame_count += 1 |
|||
if frame_count % 100 == 0: |
|||
logging.info(f"Queued frame {frame_count}, timestamp={timestamp:.3f}, idx={idx}") |
|||
timestamp += log_delta |
|||
finally: |
|||
for _ in workers: raw_q.put(None) |
|||
for p in workers: p.join() |
|||
proc_q.put(None); writer.join() |
|||
client.stop_replayer(keep_actors=False) |
|||
for si in sensor_infos: si.sensor.stop(); si.sensor.destroy() |
|||
settings.synchronous_mode = False; settings.fixed_delta_seconds = None; world.apply_settings(settings) |
|||
logging.info("Finished CarlaCosmos-DataAcquisition parallel pipeline") |
|||
|
|||
if __name__ == '__main__': |
|||
main() |
|||
@ -0,0 +1,381 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Aptiv |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
An example of client-side bounding boxes with basic car controls. |
|||
|
|||
Controls: |
|||
|
|||
W : throttle |
|||
S : brake |
|||
AD : steer |
|||
Space : hand-brake |
|||
|
|||
ESC : quit |
|||
""" |
|||
|
|||
# ============================================================================== |
|||
# -- imports ------------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
import carla |
|||
|
|||
import weakref |
|||
import random |
|||
|
|||
try: |
|||
import pygame |
|||
from pygame.locals import K_ESCAPE |
|||
from pygame.locals import K_SPACE |
|||
from pygame.locals import K_a |
|||
from pygame.locals import K_d |
|||
from pygame.locals import K_s |
|||
from pygame.locals import K_w |
|||
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') |
|||
|
|||
VIEW_WIDTH = 1920//2 |
|||
VIEW_HEIGHT = 1080//2 |
|||
VIEW_FOV = 90 |
|||
|
|||
BB_COLOR = (248, 64, 24) |
|||
|
|||
# ============================================================================== |
|||
# -- ClientSideBoundingBoxes --------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class ClientSideBoundingBoxes(object): |
|||
""" |
|||
This is a module responsible for creating 3D bounding boxes and drawing them |
|||
client-side on pygame surface. |
|||
""" |
|||
|
|||
@staticmethod |
|||
def get_bounding_boxes(vehicles, camera): |
|||
""" |
|||
Creates 3D bounding boxes based on carla vehicle list and camera. |
|||
""" |
|||
|
|||
bounding_boxes = [ClientSideBoundingBoxes.get_bounding_box(vehicle, camera) for vehicle in vehicles] |
|||
# filter objects behind camera |
|||
bounding_boxes = [bb for bb in bounding_boxes if all(bb[:, 2] > 0)] |
|||
return bounding_boxes |
|||
|
|||
@staticmethod |
|||
def draw_bounding_boxes(display, bounding_boxes): |
|||
""" |
|||
Draws bounding boxes on pygame display. |
|||
""" |
|||
|
|||
bb_surface = pygame.Surface((VIEW_WIDTH, VIEW_HEIGHT)) |
|||
bb_surface.set_colorkey((0, 0, 0)) |
|||
for bbox in bounding_boxes: |
|||
points = [(int(bbox[i, 0]), int(bbox[i, 1])) for i in range(8)] |
|||
# draw lines |
|||
# base |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[0], points[1]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[0], points[1]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[1], points[2]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[2], points[3]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[3], points[0]) |
|||
# top |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[4], points[5]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[5], points[6]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[6], points[7]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[7], points[4]) |
|||
# base-top |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[0], points[4]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[1], points[5]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[2], points[6]) |
|||
pygame.draw.line(bb_surface, BB_COLOR, points[3], points[7]) |
|||
display.blit(bb_surface, (0, 0)) |
|||
|
|||
@staticmethod |
|||
def get_bounding_box(vehicle, camera): |
|||
""" |
|||
Returns 3D bounding box for a vehicle based on camera view. |
|||
""" |
|||
|
|||
bb_cords = ClientSideBoundingBoxes._create_bb_points(vehicle) |
|||
cords_x_y_z = ClientSideBoundingBoxes._vehicle_to_sensor(bb_cords, vehicle, camera)[:3, :] |
|||
cords_y_minus_z_x = np.concatenate([cords_x_y_z[1, :], -cords_x_y_z[2, :], cords_x_y_z[0, :]]) |
|||
bbox = np.transpose(np.dot(camera.calibration, cords_y_minus_z_x)) |
|||
camera_bbox = np.concatenate([bbox[:, 0] / bbox[:, 2], bbox[:, 1] / bbox[:, 2], bbox[:, 2]], axis=1) |
|||
return camera_bbox |
|||
|
|||
@staticmethod |
|||
def _create_bb_points(vehicle): |
|||
""" |
|||
Returns 3D bounding box for a vehicle. |
|||
""" |
|||
|
|||
cords = np.zeros((8, 4)) |
|||
extent = vehicle.bounding_box.extent |
|||
cords[0, :] = np.array([extent.x, extent.y, -extent.z, 1]) |
|||
cords[1, :] = np.array([-extent.x, extent.y, -extent.z, 1]) |
|||
cords[2, :] = np.array([-extent.x, -extent.y, -extent.z, 1]) |
|||
cords[3, :] = np.array([extent.x, -extent.y, -extent.z, 1]) |
|||
cords[4, :] = np.array([extent.x, extent.y, extent.z, 1]) |
|||
cords[5, :] = np.array([-extent.x, extent.y, extent.z, 1]) |
|||
cords[6, :] = np.array([-extent.x, -extent.y, extent.z, 1]) |
|||
cords[7, :] = np.array([extent.x, -extent.y, extent.z, 1]) |
|||
return cords |
|||
|
|||
@staticmethod |
|||
def _vehicle_to_sensor(cords, vehicle, sensor): |
|||
""" |
|||
Transforms coordinates of a vehicle bounding box to sensor. |
|||
""" |
|||
|
|||
world_cord = ClientSideBoundingBoxes._vehicle_to_world(cords, vehicle) |
|||
sensor_cord = ClientSideBoundingBoxes._world_to_sensor(world_cord, sensor) |
|||
return sensor_cord |
|||
|
|||
@staticmethod |
|||
def _vehicle_to_world(cords, vehicle): |
|||
""" |
|||
Transforms coordinates of a vehicle bounding box to world. |
|||
""" |
|||
|
|||
bb_transform = carla.Transform(vehicle.bounding_box.location) |
|||
bb_vehicle_matrix = ClientSideBoundingBoxes.get_matrix(bb_transform) |
|||
vehicle_world_matrix = ClientSideBoundingBoxes.get_matrix(vehicle.get_transform()) |
|||
bb_world_matrix = np.dot(vehicle_world_matrix, bb_vehicle_matrix) |
|||
world_cords = np.dot(bb_world_matrix, np.transpose(cords)) |
|||
return world_cords |
|||
|
|||
@staticmethod |
|||
def _world_to_sensor(cords, sensor): |
|||
""" |
|||
Transforms world coordinates to sensor. |
|||
""" |
|||
|
|||
sensor_world_matrix = ClientSideBoundingBoxes.get_matrix(sensor.get_transform()) |
|||
world_sensor_matrix = np.linalg.inv(sensor_world_matrix) |
|||
sensor_cords = np.dot(world_sensor_matrix, cords) |
|||
return sensor_cords |
|||
|
|||
@staticmethod |
|||
def get_matrix(transform): |
|||
""" |
|||
Creates matrix from carla transform. |
|||
""" |
|||
|
|||
rotation = transform.rotation |
|||
location = transform.location |
|||
c_y = np.cos(np.radians(rotation.yaw)) |
|||
s_y = np.sin(np.radians(rotation.yaw)) |
|||
c_r = np.cos(np.radians(rotation.roll)) |
|||
s_r = np.sin(np.radians(rotation.roll)) |
|||
c_p = np.cos(np.radians(rotation.pitch)) |
|||
s_p = np.sin(np.radians(rotation.pitch)) |
|||
matrix = np.matrix(np.identity(4)) |
|||
matrix[0, 3] = location.x |
|||
matrix[1, 3] = location.y |
|||
matrix[2, 3] = location.z |
|||
matrix[0, 0] = c_p * c_y |
|||
matrix[0, 1] = c_y * s_p * s_r - s_y * c_r |
|||
matrix[0, 2] = -c_y * s_p * c_r - s_y * s_r |
|||
matrix[1, 0] = s_y * c_p |
|||
matrix[1, 1] = s_y * s_p * s_r + c_y * c_r |
|||
matrix[1, 2] = -s_y * s_p * c_r + c_y * s_r |
|||
matrix[2, 0] = s_p |
|||
matrix[2, 1] = -c_p * s_r |
|||
matrix[2, 2] = c_p * c_r |
|||
return matrix |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- BasicSynchronousClient ---------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class BasicSynchronousClient(object): |
|||
""" |
|||
Basic implementation of a synchronous client. |
|||
""" |
|||
|
|||
def __init__(self): |
|||
self.client = None |
|||
self.world = None |
|||
self.camera = None |
|||
self.car = None |
|||
|
|||
self.display = None |
|||
self.image = None |
|||
self.capture = True |
|||
|
|||
def camera_blueprint(self): |
|||
""" |
|||
Returns camera blueprint. |
|||
""" |
|||
|
|||
camera_bp = self.world.get_blueprint_library().find('sensor.camera.rgb') |
|||
camera_bp.set_attribute('image_size_x', str(VIEW_WIDTH)) |
|||
camera_bp.set_attribute('image_size_y', str(VIEW_HEIGHT)) |
|||
camera_bp.set_attribute('fov', str(VIEW_FOV)) |
|||
return camera_bp |
|||
|
|||
def set_synchronous_mode(self, synchronous_mode): |
|||
""" |
|||
Sets synchronous mode. |
|||
""" |
|||
|
|||
settings = self.world.get_settings() |
|||
settings.synchronous_mode = synchronous_mode |
|||
self.world.apply_settings(settings) |
|||
|
|||
def setup_car(self): |
|||
""" |
|||
Spawns actor-vehicle to be controled. |
|||
""" |
|||
|
|||
car_bp = self.world.get_blueprint_library().filter('vehicle.*')[0] |
|||
location = random.choice(self.world.get_map().get_spawn_points()) |
|||
self.car = self.world.spawn_actor(car_bp, location) |
|||
|
|||
def setup_camera(self): |
|||
""" |
|||
Spawns actor-camera to be used to render view. |
|||
Sets calibration for client-side boxes rendering. |
|||
""" |
|||
|
|||
camera_transform = carla.Transform(carla.Location(x=-5.5, z=2.8), carla.Rotation(pitch=-15)) |
|||
self.camera = self.world.spawn_actor(self.camera_blueprint(), camera_transform, attach_to=self.car) |
|||
weak_self = weakref.ref(self) |
|||
self.camera.listen(lambda image: weak_self().set_image(weak_self, image)) |
|||
|
|||
calibration = np.identity(3) |
|||
calibration[0, 2] = VIEW_WIDTH / 2.0 |
|||
calibration[1, 2] = VIEW_HEIGHT / 2.0 |
|||
calibration[0, 0] = calibration[1, 1] = VIEW_WIDTH / (2.0 * np.tan(VIEW_FOV * np.pi / 360.0)) |
|||
self.camera.calibration = calibration |
|||
|
|||
def control(self, car): |
|||
""" |
|||
Applies control to main car based on pygame pressed keys. |
|||
Will return True If ESCAPE is hit, otherwise False to end main loop. |
|||
""" |
|||
|
|||
keys = pygame.key.get_pressed() |
|||
if keys[K_ESCAPE]: |
|||
return True |
|||
|
|||
control = car.get_control() |
|||
control.throttle = 0 |
|||
if keys[K_w]: |
|||
control.throttle = 1 |
|||
control.reverse = False |
|||
elif keys[K_s]: |
|||
control.throttle = 1 |
|||
control.reverse = True |
|||
if keys[K_a]: |
|||
control.steer = max(-1., min(control.steer - 0.05, 0)) |
|||
elif keys[K_d]: |
|||
control.steer = min(1., max(control.steer + 0.05, 0)) |
|||
else: |
|||
control.steer = 0 |
|||
control.hand_brake = keys[K_SPACE] |
|||
|
|||
car.apply_control(control) |
|||
return False |
|||
|
|||
@staticmethod |
|||
def set_image(weak_self, img): |
|||
""" |
|||
Sets image coming from camera sensor. |
|||
The self.capture flag is a mean of synchronization - once the flag is |
|||
set, next coming image will be stored. |
|||
""" |
|||
|
|||
self = weak_self() |
|||
if self.capture: |
|||
self.image = img |
|||
self.capture = False |
|||
|
|||
def render(self, display): |
|||
""" |
|||
Transforms image from camera sensor and blits it to main pygame display. |
|||
""" |
|||
|
|||
if self.image is not None: |
|||
array = np.frombuffer(self.image.raw_data, dtype=np.dtype("uint8")) |
|||
array = np.reshape(array, (self.image.height, self.image.width, 4)) |
|||
array = array[:, :, :3] |
|||
array = array[:, :, ::-1] |
|||
surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) |
|||
display.blit(surface, (0, 0)) |
|||
|
|||
def game_loop(self): |
|||
""" |
|||
Main program loop. |
|||
""" |
|||
|
|||
try: |
|||
pygame.init() |
|||
|
|||
self.client = carla.Client('127.0.0.1', 2000) |
|||
self.client.set_timeout(2.0) |
|||
self.world = self.client.get_world() |
|||
|
|||
self.setup_car() |
|||
self.setup_camera() |
|||
|
|||
self.display = pygame.display.set_mode((VIEW_WIDTH, VIEW_HEIGHT), pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
pygame_clock = pygame.time.Clock() |
|||
|
|||
self.set_synchronous_mode(True) |
|||
vehicles = self.world.get_actors().filter('vehicle.*') |
|||
|
|||
while True: |
|||
self.world.tick() |
|||
|
|||
self.capture = True |
|||
pygame_clock.tick_busy_loop(20) |
|||
|
|||
self.render(self.display) |
|||
bounding_boxes = ClientSideBoundingBoxes.get_bounding_boxes(vehicles, self.camera) |
|||
ClientSideBoundingBoxes.draw_bounding_boxes(self.display, bounding_boxes) |
|||
|
|||
pygame.display.flip() |
|||
|
|||
pygame.event.pump() |
|||
if self.control(self.car): |
|||
return |
|||
|
|||
finally: |
|||
self.set_synchronous_mode(False) |
|||
self.camera.destroy() |
|||
self.car.destroy() |
|||
pygame.quit() |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- main() -------------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
def main(): |
|||
""" |
|||
Initializes the client-side bounding box demo. |
|||
""" |
|||
|
|||
try: |
|||
client = BasicSynchronousClient() |
|||
client.game_loop() |
|||
finally: |
|||
print('EXIT') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
main() |
|||
@ -0,0 +1,84 @@ |
|||
- sensor: rgb |
|||
attributes: |
|||
image_size_x: 1920 |
|||
image_size_y: 1080 |
|||
fov: 110 |
|||
transform: |
|||
location: |
|||
x: 0 |
|||
y: 0 |
|||
z: 1.8 |
|||
rotation: |
|||
pitch: 8 |
|||
yaw: 0 |
|||
roll: 0 |
|||
- sensor: normals |
|||
attributes: |
|||
image_size_x: 1920 |
|||
image_size_y: 1080 |
|||
fov: 110 |
|||
transform: |
|||
location: |
|||
x: 0 |
|||
y: 0 |
|||
z: 1.8 |
|||
rotation: |
|||
pitch: 8 |
|||
yaw: 0 |
|||
roll: 0 |
|||
- sensor: depth |
|||
attributes: |
|||
image_size_x: 1920 |
|||
image_size_y: 1080 |
|||
fov: 110 |
|||
transform: |
|||
location: |
|||
x: 0 |
|||
y: 0 |
|||
z: 1.8 |
|||
rotation: |
|||
pitch: 8 |
|||
yaw: 0 |
|||
roll: 0 |
|||
- sensor: semantic_segmentation |
|||
attributes: |
|||
image_size_x: 1920 |
|||
image_size_y: 1080 |
|||
fov: 110 |
|||
transform: |
|||
location: |
|||
x: 0 |
|||
y: 0 |
|||
z: 1.8 |
|||
rotation: |
|||
pitch: 8 |
|||
yaw: 0 |
|||
roll: 0 |
|||
- sensor: instance_segmentation |
|||
attributes: |
|||
image_size_x: 1920 |
|||
image_size_y: 1080 |
|||
fov: 110 |
|||
transform: |
|||
location: |
|||
x: 0 |
|||
y: 0 |
|||
z: 1.8 |
|||
rotation: |
|||
pitch: 8 |
|||
yaw: 0 |
|||
roll: 0 |
|||
- sensor: cosmos_visualization |
|||
attributes: |
|||
image_size_x: 1920 |
|||
image_size_y: 1080 |
|||
fov: 110 |
|||
transform: |
|||
location: |
|||
x: 0 |
|||
y: 0 |
|||
z: 1.8 |
|||
rotation: |
|||
pitch: 8 |
|||
yaw: 0 |
|||
roll: 0 |
|||
@ -0,0 +1,418 @@ |
|||
#!/usr/bin/env python |
|||
# Copyright (c) 2021 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import math |
|||
import argparse |
|||
import copy |
|||
from multiprocessing import Pool |
|||
from PIL import Image |
|||
|
|||
import carla |
|||
import random |
|||
|
|||
try: |
|||
import pygame |
|||
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') |
|||
|
|||
try: |
|||
import queue |
|||
except ImportError: |
|||
import Queue as queue |
|||
|
|||
|
|||
class CarlaSyncMode(object): |
|||
""" |
|||
Context manager to synchronize output from different sensors. Synchronous |
|||
mode is enabled as long as we are inside this context |
|||
|
|||
with CarlaSyncMode(world, sensors) as sync_mode: |
|||
while True: |
|||
data = sync_mode.tick(timeout=1.0) |
|||
|
|||
""" |
|||
|
|||
def __init__(self, world, *sensors, **kwargs): |
|||
self.world = world |
|||
self.sensors = sensors |
|||
self.frame = None |
|||
self.delta_seconds = 1.0 / kwargs.get('fps', 20) |
|||
self._queues = [] |
|||
self._settings = None |
|||
|
|||
def __enter__(self): |
|||
self._settings = self.world.get_settings() |
|||
self.frame = self.world.apply_settings(carla.WorldSettings( |
|||
no_rendering_mode=False, |
|||
synchronous_mode=True, |
|||
fixed_delta_seconds=self.delta_seconds)) |
|||
|
|||
def make_queue(register_event): |
|||
q = queue.Queue() |
|||
register_event(q.put) |
|||
self._queues.append(q) |
|||
|
|||
make_queue(self.world.on_tick) |
|||
for sensor in self.sensors: |
|||
make_queue(sensor.listen) |
|||
return self |
|||
def tick(self, timeout): |
|||
self.frame = self.world.tick() |
|||
data = [self._retrieve_data(q, timeout) for q in self._queues] |
|||
assert all(x.frame == self.frame for x in data) |
|||
return data |
|||
|
|||
def __exit__(self, *args, **kwargs): |
|||
self.world.apply_settings(self._settings) |
|||
|
|||
def _retrieve_data(self, sensor_queue, timeout): |
|||
while True: |
|||
data = sensor_queue.get(timeout=timeout) |
|||
if data.frame == self.frame: |
|||
return data |
|||
# --------------- |
|||
|
|||
def build_projection_matrix(w, h, fov): |
|||
focal = w / (2.0 * np.tan(fov * np.pi / 360.0)) |
|||
K = np.identity(3) |
|||
K[0, 0] = K[1, 1] = focal |
|||
K[0, 2] = w / 2.0 |
|||
K[1, 2] = h / 2.0 |
|||
return K |
|||
|
|||
def get_image_as_array(image): |
|||
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] |
|||
# make the array writeable doing a deep copy |
|||
array2 = copy.deepcopy(array) |
|||
return array2 |
|||
|
|||
def draw_image(surface, array, blend=False): |
|||
image_surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) |
|||
if blend: |
|||
image_surface.set_alpha(100) |
|||
surface.blit(image_surface, (0, 0)) |
|||
|
|||
|
|||
def get_font(): |
|||
fonts = [x for x in pygame.font.get_fonts()] |
|||
default_font = 'ubuntumono' |
|||
font = default_font if default_font in fonts else fonts[0] |
|||
font = pygame.font.match_font(font) |
|||
return pygame.font.Font(font, 14) |
|||
|
|||
def get_screen_points(camera, K, image_w, image_h, points3d): |
|||
|
|||
# get 4x4 matrix to transform points from world to camera coordinates |
|||
world_2_camera = np.array(camera.get_transform().get_inverse_matrix()) |
|||
|
|||
# build the points array in numpy format as (x, y, z, 1) to be operable with a 4x4 matrix |
|||
points_temp = [] |
|||
for p in points3d: |
|||
points_temp += [p.x, p.y, p.z, 1] |
|||
points = np.array(points_temp).reshape(-1, 4).T |
|||
|
|||
# convert world points to camera space |
|||
points_camera = np.dot(world_2_camera, points) |
|||
|
|||
# New we must change from UE4's coordinate system to an "standard" |
|||
# (x, y ,z) -> (y, -z, x) |
|||
# and we remove the fourth component also |
|||
points = np.array([ |
|||
points_camera[1], |
|||
points_camera[2] * -1, |
|||
points_camera[0]]) |
|||
|
|||
# Finally we can use our K matrix to do the actual 3D -> 2D. |
|||
points_2d = np.dot(K, points) |
|||
|
|||
# normalize the values and transpose |
|||
points_2d = np.array([ |
|||
points_2d[0, :] / points_2d[2, :], |
|||
points_2d[1, :] / points_2d[2, :], |
|||
points_2d[2, :]]).T |
|||
|
|||
return points_2d |
|||
|
|||
def draw_points_on_buffer(buffer, image_w, image_h, points_2d, color, size=4): |
|||
half = int(size / 2) |
|||
# draw each point |
|||
for p in points_2d: |
|||
x = int(p[0]) |
|||
y = int(p[1]) |
|||
for j in range(y - half, y + half): |
|||
if (j >=0 and j <image_h): |
|||
for i in range(x - half, x + half): |
|||
if (i >=0 and i <image_w): |
|||
buffer[j][i][0] = color[0] |
|||
buffer[j][i][1] = color[1] |
|||
buffer[j][i][2] = color[2] |
|||
|
|||
def draw_line_on_buffer(buffer, image_w, image_h, points_2d, color, size=4): |
|||
x0 = int(points_2d[0][0]) |
|||
y0 = int(points_2d[0][1]) |
|||
x1 = int(points_2d[1][0]) |
|||
y1 = int(points_2d[1][1]) |
|||
dx = abs(x1 - x0) |
|||
if x0 < x1: |
|||
sx = 1 |
|||
else: |
|||
sx = -1 |
|||
dy = -abs(y1 - y0) |
|||
if y0 < y1: |
|||
sy = 1 |
|||
else: |
|||
sy = -1 |
|||
err = dx + dy |
|||
while True: |
|||
draw_points_on_buffer(buffer, image_w, image_h, ((x0,y0),), color, size) |
|||
if (x0 == x1 and y0 == y1): |
|||
break |
|||
e2 = 2 * err |
|||
if (e2 >= dy): |
|||
err += dy |
|||
x0 += sx |
|||
if (e2 <= dx): |
|||
err += dx |
|||
y0 += sy |
|||
|
|||
def draw_skeleton(buffer, image_w, image_h, boneIndex, points2d, color, size=4): |
|||
try: |
|||
# draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_root"]], points2d[boneIndex["crl_hips__C"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hips__C"]], points2d[boneIndex["crl_spine__C"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hips__C"]], points2d[boneIndex["crl_thigh__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hips__C"]], points2d[boneIndex["crl_thigh__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_spine__C"]], points2d[boneIndex["crl_spine01__C"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_spine01__C"]], points2d[boneIndex["crl_shoulder__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_spine01__C"]], points2d[boneIndex["crl_neck__C"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_spine01__C"]], points2d[boneIndex["crl_shoulder__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_shoulder__L"]], points2d[boneIndex["crl_arm__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_arm__L"]], points2d[boneIndex["crl_foreArm__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_foreArm__L"]], points2d[boneIndex["crl_hand__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__L"]], points2d[boneIndex["crl_handThumb__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__L"]], points2d[boneIndex["crl_handIndex__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__L"]], points2d[boneIndex["crl_handMiddle__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__L"]], points2d[boneIndex["crl_handRing__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__L"]], points2d[boneIndex["crl_handPinky__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handThumb__L"]], points2d[boneIndex["crl_handThumb01__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handThumb01__L"]], points2d[boneIndex["crl_handThumb02__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handThumb02__L"]], points2d[boneIndex["crl_handThumbEnd__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handIndex__L"]], points2d[boneIndex["crl_handIndex01__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handIndex01__L"]], points2d[boneIndex["crl_handIndex02__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handIndex02__L"]], points2d[boneIndex["crl_handIndexEnd__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handMiddle__L"]], points2d[boneIndex["crl_handMiddle01__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handMiddle01__L"]], points2d[boneIndex["crl_handMiddle02__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handMiddle02__L"]], points2d[boneIndex["crl_handMiddleEnd__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handRing__L"]], points2d[boneIndex["crl_handRing01__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handRing01__L"]], points2d[boneIndex["crl_handRing02__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handRing02__L"]], points2d[boneIndex["crl_handRingEnd__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handPinky__L"]], points2d[boneIndex["crl_handPinky01__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handPinky01__L"]], points2d[boneIndex["crl_handPinky02__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handPinky02__L"]], points2d[boneIndex["crl_handPinkyEnd__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_neck__C"]], points2d[boneIndex["crl_Head__C"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_Head__C"]], points2d[boneIndex["crl_eye__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_Head__C"]], points2d[boneIndex["crl_eye__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_shoulder__R"]], points2d[boneIndex["crl_arm__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_arm__R"]], points2d[boneIndex["crl_foreArm__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_foreArm__R"]], points2d[boneIndex["crl_hand__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__R"]], points2d[boneIndex["crl_handThumb__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__R"]], points2d[boneIndex["crl_handIndex__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__R"]], points2d[boneIndex["crl_handMiddle__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__R"]], points2d[boneIndex["crl_handRing__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_hand__R"]], points2d[boneIndex["crl_handPinky__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handThumb__R"]], points2d[boneIndex["crl_handThumb01__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handThumb01__R"]], points2d[boneIndex["crl_handThumb02__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handThumb02__R"]], points2d[boneIndex["crl_handThumbEnd__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handIndex__R"]], points2d[boneIndex["crl_handIndex01__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handIndex01__R"]], points2d[boneIndex["crl_handIndex02__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handIndex02__R"]], points2d[boneIndex["crl_handIndexEnd__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handMiddle__R"]], points2d[boneIndex["crl_handMiddle01__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handMiddle01__R"]], points2d[boneIndex["crl_handMiddle02__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handMiddle02__R"]], points2d[boneIndex["crl_handMiddleEnd__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handRing__R"]], points2d[boneIndex["crl_handRing01__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handRing01__R"]], points2d[boneIndex["crl_handRing02__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handRing02__R"]], points2d[boneIndex["crl_handRingEnd__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handPinky__R"]], points2d[boneIndex["crl_handPinky01__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handPinky01__R"]], points2d[boneIndex["crl_handPinky02__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_handPinky02__R"]], points2d[boneIndex["crl_handPinkyEnd__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_thigh__R"]], points2d[boneIndex["crl_leg__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_leg__R"]], points2d[boneIndex["crl_foot__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_foot__R"]], points2d[boneIndex["crl_toe__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_toe__R"]], points2d[boneIndex["crl_toeEnd__R"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_thigh__L"]], points2d[boneIndex["crl_leg__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_leg__L"]], points2d[boneIndex["crl_foot__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_foot__L"]], points2d[boneIndex["crl_toe__L"]]), color, size) |
|||
draw_line_on_buffer(buffer, image_w, image_h, (points2d[boneIndex["crl_toe__L"]], points2d[boneIndex["crl_toeEnd__L"]]), color, size) |
|||
except: |
|||
pass |
|||
|
|||
def should_quit(): |
|||
for event in pygame.event.get(): |
|||
if event.type == pygame.QUIT: |
|||
return True |
|||
elif event.type == pygame.KEYUP: |
|||
if event.key == pygame.K_ESCAPE: |
|||
return True |
|||
return False |
|||
|
|||
def write_image(frame, id, buffer): |
|||
# Save the image using Pillow module. |
|||
img = Image.fromarray(buffer) |
|||
img.save('_out/%s_%06d.png' % (id, frame)) |
|||
|
|||
def main(): |
|||
argparser = argparse.ArgumentParser( |
|||
description='CARLA Manual Control Client') |
|||
argparser.add_argument( |
|||
'--fov', |
|||
default=60, |
|||
type=int, |
|||
help='FOV for camera') |
|||
argparser.add_argument( |
|||
'--res', |
|||
metavar='WIDTHxHEIGHT', |
|||
# default='1920x1080', |
|||
default='800x600', |
|||
help='window resolution (default: 800x600)') |
|||
args = argparser.parse_args() |
|||
|
|||
args.width, args.height = [int(x) for x in args.res.split('x')] |
|||
|
|||
actor_list = [] |
|||
pygame.init() |
|||
|
|||
display = pygame.display.set_mode( |
|||
(args.width, args.height), |
|||
pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
font = get_font() |
|||
clock = pygame.time.Clock() |
|||
|
|||
client = carla.Client('localhost', 2000) |
|||
client.set_timeout(5.0) |
|||
|
|||
world = client.get_world() |
|||
|
|||
# spawn a camera |
|||
camera_bp = world.get_blueprint_library().find('sensor.camera.rgb') |
|||
camera_bp.set_attribute("image_size_x", str(args.width)) |
|||
camera_bp.set_attribute("image_size_y", str(args.height)) |
|||
camera_bp.set_attribute("fov", str(args.fov)) |
|||
camera = world.spawn_actor(camera_bp, carla.Transform()) |
|||
|
|||
# spawn a pedestrian |
|||
world.set_pedestrians_seed(1235) |
|||
ped_bp = random.choice(world.get_blueprint_library().filter("walker.pedestrian.*")) |
|||
trans = carla.Transform() |
|||
trans.location = world.get_random_location_from_navigation() |
|||
ped = world.spawn_actor(ped_bp, trans) |
|||
walker_controller_bp = world.get_blueprint_library().find('controller.ai.walker') |
|||
controller = world.spawn_actor(walker_controller_bp, carla.Transform(), ped) |
|||
controller.start() |
|||
controller.go_to_location(world.get_random_location_from_navigation()) |
|||
controller.set_max_speed(1.7) |
|||
|
|||
# keep tracking of actors to remove |
|||
actor_list.append(camera) |
|||
actor_list.append(ped) |
|||
actor_list.append(controller) |
|||
|
|||
# get some attributes from the camera |
|||
image_w = camera_bp.get_attribute("image_size_x").as_int() |
|||
image_h = camera_bp.get_attribute("image_size_y").as_int() |
|||
fov = camera_bp.get_attribute("fov").as_float() |
|||
|
|||
try: |
|||
pool = Pool(processes=5) |
|||
# Create a synchronous mode context. |
|||
with CarlaSyncMode(world, camera, fps=30) as sync_mode: |
|||
|
|||
# set the projection matrix |
|||
K = build_projection_matrix(image_w, image_h, fov) |
|||
|
|||
blending = 0 |
|||
turning = 0 |
|||
while True: |
|||
if should_quit(): |
|||
return |
|||
clock.tick() |
|||
|
|||
# make some transition from custom pose to animation |
|||
ped.blend_pose(math.sin(blending)) |
|||
|
|||
# move the pedestrian |
|||
blending += 0.015 |
|||
turning += 0.009 |
|||
|
|||
# move camera around |
|||
trans = ped.get_transform() |
|||
x = math.cos(turning) * -3 |
|||
y = math.sin(turning) * 3 |
|||
trans.location.x += x |
|||
trans.location.y += y |
|||
trans.location.z = 2 |
|||
trans.rotation.pitch = -16 |
|||
trans.rotation.roll = 0 |
|||
trans.rotation.yaw = -360 * (turning/(math.pi*2)) |
|||
camera.set_transform(trans) |
|||
|
|||
# Advance the simulation and wait for the data. |
|||
snapshot, image_rgb = sync_mode.tick(timeout=5.0) |
|||
|
|||
# Draw the display. |
|||
buffer = get_image_as_array(image_rgb) |
|||
|
|||
# get the pedestrian bones |
|||
bones = ped.get_bones() |
|||
|
|||
# prepare the bones (get name and world position) |
|||
boneIndex = {} |
|||
points = [] |
|||
for i, bone in enumerate(bones.bone_transforms): |
|||
boneIndex[bone.name] = i |
|||
points.append(bone.world.location) |
|||
|
|||
# project the 3d points to 2d screen |
|||
points2d = get_screen_points(camera, K, image_w, image_h, points) |
|||
|
|||
# draw the skeleton lines |
|||
draw_skeleton(buffer, image_w, image_h, boneIndex, points2d, (0, 255, 0), 2) |
|||
|
|||
# draw the bone points |
|||
draw_points_on_buffer(buffer, image_w, image_h, points2d[1:], (255, 0, 0), 4) |
|||
|
|||
draw_image(display, buffer) |
|||
# pool.apply_async(write_image, (snapshot.frame, "ped", buffer)) |
|||
|
|||
# display.blit(font.render('%d bones' % len(points), True, (255, 255, 255)), (8, 10)) |
|||
|
|||
pygame.display.flip() |
|||
|
|||
finally: |
|||
# time.sleep(5) |
|||
print('destroying actors.') |
|||
for actor in actor_list: |
|||
actor.destroy() |
|||
pygame.quit() |
|||
pool.close() |
|||
print('done.') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
|
|||
main() |
|||
|
|||
except KeyboardInterrupt: |
|||
print('\nCancelled by user. Bye!') |
|||
@ -0,0 +1,142 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
CARLA Dynamic Weather: |
|||
|
|||
Connect to a CARLA Simulator instance and control the weather. Change Sun |
|||
position smoothly with time and generate storms occasionally. |
|||
""" |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
import math |
|||
import sys |
|||
|
|||
|
|||
def clamp(value, minimum=0.0, maximum=100.0): |
|||
return max(minimum, min(value, maximum)) |
|||
|
|||
|
|||
class Sun(object): |
|||
def __init__(self, azimuth, altitude): |
|||
self.azimuth = azimuth |
|||
self.altitude = altitude |
|||
self._t = 0.0 |
|||
|
|||
def tick(self, delta_seconds): |
|||
self._t += 0.008 * delta_seconds |
|||
self._t %= 2.0 * math.pi |
|||
self.azimuth += 0.25 * delta_seconds |
|||
self.azimuth %= 360.0 |
|||
self.altitude = (70 * math.sin(self._t)) - 20 |
|||
|
|||
def __str__(self): |
|||
return 'Sun(alt: %.2f, azm: %.2f)' % (self.altitude, self.azimuth) |
|||
|
|||
|
|||
class Storm(object): |
|||
def __init__(self, precipitation): |
|||
self._t = precipitation if precipitation > 0.0 else -50.0 |
|||
self._increasing = True |
|||
self.clouds = 0.0 |
|||
self.rain = 0.0 |
|||
self.wetness = 0.0 |
|||
self.puddles = 0.0 |
|||
self.wind = 0.0 |
|||
self.fog = 0.0 |
|||
|
|||
def tick(self, delta_seconds): |
|||
delta = (1.3 if self._increasing else -1.3) * delta_seconds |
|||
self._t = clamp(delta + self._t, -250.0, 100.0) |
|||
self.clouds = clamp(self._t + 40.0, 0.0, 90.0) |
|||
self.rain = clamp(self._t, 0.0, 80.0) |
|||
delay = -10.0 if self._increasing else 90.0 |
|||
self.puddles = clamp(self._t + delay, 0.0, 85.0) |
|||
self.wetness = clamp(self._t * 5, 0.0, 100.0) |
|||
self.wind = 5.0 if self.clouds <= 20 else 90 if self.clouds >= 70 else 40 |
|||
self.fog = clamp(self._t - 10, 0.0, 30.0) |
|||
if self._t == -250.0: |
|||
self._increasing = True |
|||
if self._t == 100.0: |
|||
self._increasing = False |
|||
|
|||
def __str__(self): |
|||
return 'Storm(clouds=%d%%, rain=%d%%, wind=%d%%)' % (self.clouds, self.rain, self.wind) |
|||
|
|||
|
|||
class Weather(object): |
|||
def __init__(self, weather): |
|||
self.weather = weather |
|||
self._sun = Sun(weather.sun_azimuth_angle, weather.sun_altitude_angle) |
|||
self._storm = Storm(weather.precipitation) |
|||
|
|||
def tick(self, delta_seconds): |
|||
self._sun.tick(delta_seconds) |
|||
self._storm.tick(delta_seconds) |
|||
self.weather.cloudiness = self._storm.clouds |
|||
self.weather.precipitation = self._storm.rain |
|||
self.weather.precipitation_deposits = self._storm.puddles |
|||
self.weather.wind_intensity = self._storm.wind |
|||
self.weather.fog_density = self._storm.fog |
|||
self.weather.wetness = self._storm.wetness |
|||
self.weather.sun_azimuth_angle = self._sun.azimuth |
|||
self.weather.sun_altitude_angle = self._sun.altitude |
|||
|
|||
def __str__(self): |
|||
return '%s %s' % (self._sun, self._storm) |
|||
|
|||
|
|||
def main(): |
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-s', '--speed', |
|||
metavar='FACTOR', |
|||
default=1.0, |
|||
type=float, |
|||
help='rate at which the weather changes (default: 1.0)') |
|||
args = argparser.parse_args() |
|||
|
|||
speed_factor = args.speed |
|||
update_freq = 0.1 / speed_factor |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
|
|||
weather = Weather(world.get_weather()) |
|||
|
|||
elapsed_time = 0.0 |
|||
|
|||
while True: |
|||
timestamp = world.wait_for_tick(seconds=30.0).timestamp |
|||
elapsed_time += timestamp.delta_seconds |
|||
if elapsed_time > update_freq: |
|||
weather.tick(speed_factor * elapsed_time) |
|||
world.set_weather(weather.weather) |
|||
sys.stdout.write('\r' + str(weather) + 12 * ' ') |
|||
sys.stdout.flush() |
|||
elapsed_time = 0.0 |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
@ -0,0 +1,35 @@ |
|||
import carla |
|||
import time |
|||
|
|||
client = carla.Client("localhost", 2000) |
|||
client.set_timeout(5.0) |
|||
|
|||
world = client.get_world() |
|||
blueprint_library = world.get_blueprint_library() |
|||
|
|||
# Spawn vehicle |
|||
vehicle_bp = blueprint_library.filter("model3")[0] |
|||
spawn_point = world.get_map().get_spawn_points()[0] |
|||
|
|||
vehicle = world.spawn_actor(vehicle_bp, spawn_point) |
|||
vehicle.set_autopilot(True) |
|||
|
|||
# Get spectator (this is the simulator camera) |
|||
spectator = world.get_spectator() |
|||
|
|||
try: |
|||
while True: |
|||
transform = vehicle.get_transform() |
|||
|
|||
# Position camera behind and above vehicle |
|||
spectator_transform = carla.Transform( |
|||
transform.location + carla.Location(x=-6, z=3), |
|||
transform.rotation |
|||
) |
|||
|
|||
spectator.set_transform(spectator_transform) |
|||
|
|||
time.sleep(0.05) |
|||
|
|||
finally: |
|||
vehicle.destroy() |
|||
@ -0,0 +1,366 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2021 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
"""Example script to generate traffic in the simulation""" |
|||
|
|||
import carla |
|||
|
|||
from carla import VehicleLightState as vls |
|||
from carla.command import SpawnActor, SetAutopilot, FutureActor, DestroyActor |
|||
|
|||
import argparse |
|||
import logging |
|||
from numpy import random |
|||
import time |
|||
|
|||
|
|||
def get_actor_blueprints(world, filter, generation): |
|||
bps = world.get_blueprint_library().filter(filter) |
|||
|
|||
if generation.lower() == "all": |
|||
return bps |
|||
|
|||
# If the filter returns only one bp, we assume that this one needed |
|||
# and therefore, we ignore the generation |
|||
if len(bps) == 1: |
|||
return bps |
|||
|
|||
try: |
|||
int_generation = int(generation) |
|||
# Check if generation is in available generations |
|||
if int_generation in [1, 2, 3]: |
|||
bps = [x for x in bps if int(x.get_attribute('generation')) == int_generation] |
|||
return bps |
|||
else: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
except: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
|
|||
def main(): |
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-n', '--number-of-vehicles', |
|||
metavar='N', |
|||
default=30, |
|||
type=int, |
|||
help='Number of vehicles (default: 30)') |
|||
argparser.add_argument( |
|||
'-w', '--number-of-walkers', |
|||
metavar='W', |
|||
default=10, |
|||
type=int, |
|||
help='Number of walkers (default: 10)') |
|||
argparser.add_argument( |
|||
'--safe', |
|||
action='store_true', |
|||
help='Avoid spawning vehicles prone to accidents') |
|||
argparser.add_argument( |
|||
'--filterv', |
|||
metavar='PATTERN', |
|||
default='vehicle.*', |
|||
help='Filter vehicle model (default: "vehicle.*")') |
|||
argparser.add_argument( |
|||
'--generationv', |
|||
metavar='G', |
|||
default='All', |
|||
help='restrict to certain vehicle generation (values: "1","2","All" - default: "All")') |
|||
argparser.add_argument( |
|||
'--filterw', |
|||
metavar='PATTERN', |
|||
default='walker.pedestrian.*', |
|||
help='Filter pedestrian type (default: "walker.pedestrian.*")') |
|||
argparser.add_argument( |
|||
'--generationw', |
|||
metavar='G', |
|||
default='2', |
|||
help='restrict to certain pedestrian generation (values: "1","2","All" - default: "2")') |
|||
argparser.add_argument( |
|||
'--tm-port', |
|||
metavar='P', |
|||
default=8000, |
|||
type=int, |
|||
help='Port to communicate with TM (default: 8000)') |
|||
argparser.add_argument( |
|||
'--asynch', |
|||
action='store_true', |
|||
help='Activate asynchronous mode execution') |
|||
argparser.add_argument( |
|||
'--hybrid', |
|||
action='store_true', |
|||
help='Activate hybrid mode for Traffic Manager') |
|||
argparser.add_argument( |
|||
'-s', '--seed', |
|||
metavar='S', |
|||
type=int, |
|||
help='Set random device seed and deterministic mode for Traffic Manager') |
|||
argparser.add_argument( |
|||
'--seedw', |
|||
metavar='S', |
|||
default=0, |
|||
type=int, |
|||
help='Set the seed for pedestrians module') |
|||
argparser.add_argument( |
|||
'--car-lights-on', |
|||
action='store_true', |
|||
default=False, |
|||
help='Enable automatic car light management') |
|||
argparser.add_argument( |
|||
'--hero', |
|||
action='store_true', |
|||
default=False, |
|||
help='Set one of the vehicles as hero') |
|||
argparser.add_argument( |
|||
'--respawn', |
|||
action='store_true', |
|||
default=False, |
|||
help='Automatically respawn dormant vehicles (only in large maps)') |
|||
argparser.add_argument( |
|||
'--no-rendering', |
|||
action='store_true', |
|||
default=False, |
|||
help='Activate no rendering mode') |
|||
|
|||
args = argparser.parse_args() |
|||
|
|||
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) |
|||
|
|||
vehicles_list = [] |
|||
walkers_list = [] |
|||
all_id = [] |
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(10.0) |
|||
synchronous_master = False |
|||
random.seed(args.seed if args.seed is not None else int(time.time())) |
|||
|
|||
try: |
|||
world = client.get_world() |
|||
|
|||
traffic_manager = client.get_trafficmanager(args.tm_port) |
|||
traffic_manager.set_global_distance_to_leading_vehicle(2.5) |
|||
if args.respawn: |
|||
traffic_manager.set_respawn_dormant_vehicles(True) |
|||
if args.hybrid: |
|||
traffic_manager.set_hybrid_physics_mode(True) |
|||
traffic_manager.set_hybrid_physics_radius(70.0) |
|||
if args.seed is not None: |
|||
traffic_manager.set_random_device_seed(args.seed) |
|||
|
|||
settings = world.get_settings() |
|||
if not args.asynch: |
|||
traffic_manager.set_synchronous_mode(True) |
|||
if not settings.synchronous_mode: |
|||
synchronous_master = True |
|||
settings.synchronous_mode = True |
|||
settings.fixed_delta_seconds = 0.05 |
|||
else: |
|||
synchronous_master = False |
|||
else: |
|||
print("You are currently in asynchronous mode. If this is a traffic simulation, \ |
|||
you could experience some issues. If it's not working correctly, switch to synchronous \ |
|||
mode by using traffic_manager.set_synchronous_mode(True)") |
|||
|
|||
if args.no_rendering: |
|||
settings.no_rendering_mode = True |
|||
world.apply_settings(settings) |
|||
|
|||
blueprints = get_actor_blueprints(world, args.filterv, args.generationv) |
|||
if not blueprints: |
|||
raise ValueError("Couldn't find any vehicles with the specified filters") |
|||
blueprintsWalkers = get_actor_blueprints(world, args.filterw, args.generationw) |
|||
if not blueprintsWalkers: |
|||
raise ValueError("Couldn't find any walkers with the specified filters") |
|||
|
|||
if args.safe: |
|||
blueprints = [x for x in blueprints if x.get_attribute('base_type') == 'car'] |
|||
|
|||
blueprints = sorted(blueprints, key=lambda bp: bp.id) |
|||
|
|||
spawn_points = world.get_map().get_spawn_points() |
|||
number_of_spawn_points = len(spawn_points) |
|||
|
|||
if args.number_of_vehicles < number_of_spawn_points: |
|||
random.shuffle(spawn_points) |
|||
elif args.number_of_vehicles > number_of_spawn_points: |
|||
msg = 'requested %d vehicles, but could only find %d spawn points' |
|||
logging.warning(msg, args.number_of_vehicles, number_of_spawn_points) |
|||
args.number_of_vehicles = number_of_spawn_points |
|||
|
|||
# -------------- |
|||
# Spawn vehicles |
|||
# -------------- |
|||
batch = [] |
|||
hero = args.hero |
|||
for n, transform in enumerate(spawn_points): |
|||
if n >= args.number_of_vehicles: |
|||
break |
|||
blueprint = random.choice(blueprints) |
|||
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 hero: |
|||
blueprint.set_attribute('role_name', 'hero') |
|||
hero = False |
|||
else: |
|||
blueprint.set_attribute('role_name', 'autopilot') |
|||
|
|||
# spawn the cars and set their autopilot and light state all together |
|||
batch.append(SpawnActor(blueprint, transform) |
|||
.then(SetAutopilot(FutureActor, True, traffic_manager.get_port()))) |
|||
|
|||
for response in client.apply_batch_sync(batch, synchronous_master): |
|||
if response.error: |
|||
logging.error(response.error) |
|||
else: |
|||
vehicles_list.append(response.actor_id) |
|||
|
|||
# Set automatic vehicle lights update if specified |
|||
if args.car_lights_on: |
|||
all_vehicle_actors = world.get_actors(vehicles_list) |
|||
for actor in all_vehicle_actors: |
|||
traffic_manager.update_vehicle_lights(actor, True) |
|||
|
|||
# ------------- |
|||
# Spawn Walkers |
|||
# ------------- |
|||
# some settings |
|||
percentagePedestriansRunning = 0.0 # how many pedestrians will run |
|||
percentagePedestriansCrossing = 0.0 # how many pedestrians will walk through the road |
|||
if args.seedw: |
|||
world.set_pedestrians_seed(args.seedw) |
|||
random.seed(args.seedw) |
|||
# 1. take all the random locations to spawn |
|||
spawn_points = [] |
|||
for i in range(args.number_of_walkers): |
|||
spawn_point = carla.Transform() |
|||
loc = world.get_random_location_from_navigation() |
|||
if (loc != None): |
|||
spawn_point.location = loc |
|||
spawn_points.append(spawn_point) |
|||
# 2. we spawn the walker object |
|||
batch = [] |
|||
walker_speed = [] |
|||
for spawn_point in spawn_points: |
|||
walker_bp = random.choice(blueprintsWalkers) |
|||
# set as not invincible |
|||
probability = random.randint(0,100 + 1); |
|||
if walker_bp.has_attribute('is_invincible'): |
|||
walker_bp.set_attribute('is_invincible', 'false') |
|||
if walker_bp.has_attribute('can_use_wheelchair') and probability < 11: |
|||
walker_bp.set_attribute('use_wheelchair', 'true') |
|||
# set the max speed |
|||
if walker_bp.has_attribute('speed'): |
|||
if (random.random() > percentagePedestriansRunning): |
|||
# walking |
|||
walker_speed.append(walker_bp.get_attribute('speed').recommended_values[1]) |
|||
else: |
|||
# running |
|||
walker_speed.append(walker_bp.get_attribute('speed').recommended_values[2]) |
|||
else: |
|||
print("Walker has no speed") |
|||
walker_speed.append(0.0) |
|||
batch.append(SpawnActor(walker_bp, spawn_point)) |
|||
results = client.apply_batch_sync(batch, True) |
|||
walker_speed2 = [] |
|||
for i in range(len(results)): |
|||
if results[i].error: |
|||
logging.error(results[i].error) |
|||
else: |
|||
walkers_list.append({"id": results[i].actor_id}) |
|||
walker_speed2.append(walker_speed[i]) |
|||
walker_speed = walker_speed2 |
|||
# 3. we spawn the walker controller |
|||
batch = [] |
|||
walker_controller_bp = world.get_blueprint_library().find('controller.ai.walker') |
|||
for i in range(len(walkers_list)): |
|||
batch.append(SpawnActor(walker_controller_bp, carla.Transform(), walkers_list[i]["id"])) |
|||
results = client.apply_batch_sync(batch, True) |
|||
for i in range(len(results)): |
|||
if results[i].error: |
|||
logging.error(results[i].error) |
|||
else: |
|||
walkers_list[i]["con"] = results[i].actor_id |
|||
# 4. we put together the walkers and controllers id to get the objects from their id |
|||
for i in range(len(walkers_list)): |
|||
all_id.append(walkers_list[i]["con"]) |
|||
all_id.append(walkers_list[i]["id"]) |
|||
all_actors = world.get_actors(all_id) |
|||
|
|||
# wait for a tick to ensure client receives the last transform of the walkers we have just created |
|||
if args.asynch or not synchronous_master: |
|||
world.wait_for_tick() |
|||
else: |
|||
world.tick() |
|||
|
|||
# 5. initialize each controller and set target to walk to (list is [controler, actor, controller, actor ...]) |
|||
# set how many pedestrians can cross the road |
|||
world.set_pedestrians_cross_factor(percentagePedestriansCrossing) |
|||
for i in range(0, len(all_id), 2): |
|||
# start walker |
|||
all_actors[i].start() |
|||
# set walk to random point |
|||
all_actors[i].go_to_location(world.get_random_location_from_navigation()) |
|||
# max speed |
|||
all_actors[i].set_max_speed(float(walker_speed[int(i/2)])) |
|||
|
|||
print('spawned %d vehicles and %d walkers, press Ctrl+C to exit.' % (len(vehicles_list), len(walkers_list))) |
|||
|
|||
# Example of how to use Traffic Manager parameters |
|||
traffic_manager.global_percentage_speed_difference(30.0) |
|||
|
|||
while True: |
|||
if not args.asynch and synchronous_master: |
|||
world.tick() |
|||
else: |
|||
world.wait_for_tick() |
|||
|
|||
finally: |
|||
|
|||
if not args.asynch and synchronous_master: |
|||
settings = world.get_settings() |
|||
settings.synchronous_mode = False |
|||
settings.no_rendering_mode = False |
|||
settings.fixed_delta_seconds = None |
|||
world.apply_settings(settings) |
|||
|
|||
print('\ndestroying %d vehicles' % len(vehicles_list)) |
|||
client.apply_batch([DestroyActor(x) for x in vehicles_list]) |
|||
|
|||
# stop walker controllers (list is [controller, actor, controller, actor ...]) |
|||
for i in range(0, len(all_id), 2): |
|||
all_actors[i].stop() |
|||
|
|||
print('\ndestroying %d walkers' % len(walkers_list)) |
|||
client.apply_batch([DestroyActor(x) for x in all_id]) |
|||
|
|||
time.sleep(0.5) |
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,24 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2025 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
client = carla.Client('localhost', 2000) |
|||
world = client.get_world() |
|||
|
|||
location = carla.Location(200.0, 200.0, 200.0) |
|||
rotation = carla.Rotation(0.0, 0.0, 0.0) |
|||
transform = carla.Transform(location, rotation) |
|||
|
|||
bp_library = world.get_blueprint_library() |
|||
bp_audi = bp_library.find('vehicle.audi.tt') |
|||
audi = world.spawn_actor(bp_audi, transform) |
|||
|
|||
component_transform = audi.get_component_world_transform('front-blinker-r-1') |
|||
print(component_transform) |
|||
|
|||
@ -0,0 +1,695 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2024 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
Example script to generate realistic traffic with the InvertedAI API |
|||
""" |
|||
|
|||
import os |
|||
import time |
|||
import carla |
|||
import argparse |
|||
import logging |
|||
import math |
|||
import random |
|||
import invertedai as iai |
|||
import numpy as np |
|||
from tqdm import tqdm |
|||
from invertedai.common import AgentProperties, AgentState, TrafficLightState, Point |
|||
from carla import command, Location |
|||
|
|||
#--------- |
|||
# CARLA Utils |
|||
#--------- |
|||
|
|||
# Argument parser |
|||
def argument_parser(): |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__, |
|||
formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
|||
|
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to') |
|||
argparser.add_argument( |
|||
'-n', '--number-of-vehicles', |
|||
metavar='N', |
|||
default=50, |
|||
type=int, |
|||
help='Number of vehicles spawned by InvertedAI') |
|||
argparser.add_argument( |
|||
'-w', '--number-of-walkers', |
|||
metavar='W', |
|||
default=0, |
|||
type=int, |
|||
help='Number of walkers') |
|||
argparser.add_argument( |
|||
'--safe', |
|||
type=bool, |
|||
default=True, |
|||
help='Avoid spawning vehicles prone to accidents') |
|||
argparser.add_argument( |
|||
'--filterv', |
|||
metavar='PATTERN', |
|||
default='vehicle.*', |
|||
help='Filter vehicle model') |
|||
argparser.add_argument( |
|||
'--generationv', |
|||
metavar='G', |
|||
default='All', |
|||
help='restrict to certain vehicle generation') |
|||
argparser.add_argument( |
|||
'--filterw', |
|||
metavar='PATTERN', |
|||
default='walker.pedestrian.*', |
|||
help='Filter pedestrian type') |
|||
argparser.add_argument( |
|||
'--generationw', |
|||
metavar='G', |
|||
default='All', |
|||
help='restrict to certain pedestrian generation') |
|||
argparser.add_argument( |
|||
'-s', '--seed', |
|||
metavar='S', |
|||
type=int, |
|||
help='Set random seed') |
|||
argparser.add_argument( |
|||
'--hero', |
|||
action='store_true', |
|||
default=False, |
|||
help='Set one of the vehicles as hero') |
|||
argparser.add_argument( |
|||
'--iai-key', |
|||
type=str, |
|||
help="InvertedAI API key.") |
|||
argparser.add_argument( |
|||
'--record', |
|||
action='store_true', |
|||
help="Record the simulation using the CARLA recorder", |
|||
default=False) |
|||
argparser.add_argument( |
|||
'--sim-length', |
|||
type=int, |
|||
default=60, |
|||
help="Length of the simulation in seconds") |
|||
argparser.add_argument( |
|||
'--location', |
|||
type=str, |
|||
help=f"IAI formatted map on which to create simulate (default: carla:Town10HD, only tested there)", |
|||
default='carla:Town10HD') |
|||
argparser.add_argument( |
|||
'--capacity', |
|||
type=int, |
|||
help=f"The capacity parameter of a quadtree leaf before splitting", |
|||
default=100) |
|||
argparser.add_argument( |
|||
'--width', |
|||
type=int, |
|||
help=f"Full width of the area to initialize", |
|||
default=250) |
|||
argparser.add_argument( |
|||
'--height', |
|||
type=int, |
|||
help=f"Full height of the area to initialize", |
|||
default=250) |
|||
argparser.add_argument( |
|||
'--map-center', |
|||
type=int, |
|||
nargs='+', |
|||
help=f"Center of the area to initialize", |
|||
default=tuple([-50,20])) |
|||
argparser.add_argument( |
|||
'--iai-async', |
|||
type=bool, |
|||
help=f"Whether to call drive asynchronously", |
|||
default=True) |
|||
argparser.add_argument( |
|||
'--api-model', |
|||
type=str, |
|||
help=f"IAI API model version", |
|||
default="bI5p") |
|||
argparser.add_argument( |
|||
'--iai-log', |
|||
action="store_true", |
|||
help=f"Export a log file for the InvertedAI cosimulation, which can be replayed afterwards") |
|||
argparser.add_argument( |
|||
'--iai-waypoint-distance', |
|||
type=int, |
|||
default=15, |
|||
help=f"Distance to the next waypoint for IAI agents" |
|||
) |
|||
argparser.add_argument( |
|||
'--iai-waypoint-detection-threshold', |
|||
type=int, |
|||
default=2, |
|||
help=f"Distance to which an agent is deemed as having reached its waypoint" |
|||
) |
|||
argparser.add_argument( |
|||
'--iai-max-distance-away', |
|||
type=int, |
|||
default=20, |
|||
help=f"Maximum distance away before a new waypoint is set for an agent" |
|||
) |
|||
|
|||
args = argparser.parse_args() |
|||
|
|||
return args |
|||
|
|||
# Setup CARLA client and world |
|||
def setup_carla_environment(host, port, location): |
|||
map_name = location.split(":")[-1] |
|||
|
|||
step_length = 0.1 # 0.1 is the only step length that is supported by invertedai so far |
|||
|
|||
client = carla.Client(host, port) |
|||
client.set_timeout(200.0) |
|||
|
|||
# Configure the simulation environment |
|||
world = client.load_world(map_name) |
|||
world_settings = carla.WorldSettings( |
|||
synchronous_mode=True, |
|||
fixed_delta_seconds=step_length, |
|||
) |
|||
world.apply_settings(world_settings) |
|||
|
|||
return client, world |
|||
|
|||
# Set spectator view on a hero vehicle |
|||
def set_spectator(world, hero_v): |
|||
|
|||
spectator_offset_x = -6. |
|||
spectator_offset_z = 6. |
|||
spectator_offset_pitch = 20 |
|||
|
|||
hero_t = hero_v.get_transform() |
|||
|
|||
yaw = hero_t.rotation.yaw |
|||
spectator_l = hero_t.location + carla.Location( |
|||
spectator_offset_x * math.cos(math.radians(yaw)), |
|||
spectator_offset_x * math.sin(math.radians(yaw)), |
|||
spectator_offset_z, |
|||
) |
|||
spectator_t = carla.Transform(spectator_l, hero_t.rotation) |
|||
spectator_t.rotation.pitch -= spectator_offset_pitch |
|||
world.get_spectator().set_transform(spectator_t) |
|||
|
|||
#--------- |
|||
# Initialize actors |
|||
#--------- |
|||
|
|||
# Initialize IAI agents from CARLA actors |
|||
def initialize_iai_agent(actor, agent_type): |
|||
|
|||
transf = actor.get_transform() |
|||
vel = actor.get_velocity() |
|||
speed = math.sqrt(vel.x**2. + vel.y**2. +vel.z**2.) |
|||
|
|||
agent_state = AgentState.fromlist([ |
|||
transf.location.x, |
|||
transf.location.y, |
|||
math.radians(transf.rotation.yaw), |
|||
speed |
|||
]) |
|||
|
|||
bb = actor.bounding_box |
|||
length, width = bb.extent.x*2, bb.extent.y*2 |
|||
|
|||
agent_properties = AgentProperties(length=length, width=width, agent_type=agent_type) |
|||
if agent_type=="car": |
|||
agent_properties.rear_axis_offset = length*0.38 # Empirical value fitted from InvertedAI initialization |
|||
|
|||
return agent_state, agent_properties |
|||
|
|||
# Initialize IAI pedestrians from CARLA actors |
|||
def initialize_pedestrians(pedestrians): |
|||
|
|||
iai_pedestrians_states, iai_pedestrians_properties = [], [] |
|||
for actor in pedestrians: |
|||
iai_ped_state, iai_ped_properties = initialize_iai_agent(actor,agent_type="pedestrian") |
|||
iai_pedestrians_states.append(iai_ped_state) |
|||
iai_pedestrians_properties.append(iai_ped_properties) |
|||
|
|||
return iai_pedestrians_states, iai_pedestrians_properties |
|||
|
|||
# Spawn pedestrians in the simulation, which are driven by CARLA controllers (not by invertedai) |
|||
def spawn_pedestrians(client, world, num_pedestrians, bps): |
|||
|
|||
batch = [] |
|||
|
|||
# Get spawn points for pedestrians |
|||
spawn_points = [] |
|||
for i in range(num_pedestrians): |
|||
|
|||
loc = world.get_random_location_from_navigation() |
|||
if (loc is not None): |
|||
spawn_point = carla.Transform(location=loc) |
|||
#Apply Offset in vertical to avoid collision spawning |
|||
spawn_point.location.z += 1 |
|||
spawn_points.append(spawn_point) |
|||
|
|||
pedestrians = [] |
|||
walkers_list = [] |
|||
|
|||
# Spawn pedestrians |
|||
for i in range(len(spawn_points)): |
|||
walker_bp = random.choice(bps) |
|||
if walker_bp.has_attribute('is_invincible'): |
|||
walker_bp.set_attribute('is_invincible', 'false') |
|||
spawn_point = spawn_points[i] |
|||
batch.append(command.SpawnActor(walker_bp, spawn_point)) |
|||
|
|||
results = client.apply_batch_sync(batch, True) |
|||
pedestrians = world.get_actors().filter('walker.*') |
|||
for i in range(len(results)): |
|||
if results[i].error: |
|||
logging.error(results[i].error) |
|||
else: |
|||
walkers_list.append({"id": results[i].actor_id}) |
|||
|
|||
# Spawn CARLA IA controllers for pedestrians |
|||
batch = [] |
|||
walker_controller_bp = world.get_blueprint_library().find('controller.ai.walker') |
|||
for i in range(len(walkers_list)): |
|||
batch.append(command.SpawnActor(walker_controller_bp, carla.Transform(), walkers_list[i]["id"])) |
|||
results = client.apply_batch_sync(batch, True) |
|||
|
|||
world.tick() |
|||
|
|||
for controller in world.get_actors().filter('controller.ai.walker'): |
|||
controller.start() |
|||
dest = world.get_random_location_from_navigation() |
|||
controller.go_to_location(dest) |
|||
controller.set_max_speed(0.5 + random.random()) |
|||
|
|||
return pedestrians |
|||
|
|||
# Get blueprints according to the given filters |
|||
def get_actor_blueprints(world, filter, generation): |
|||
bps = world.get_blueprint_library().filter(filter) |
|||
|
|||
if generation.lower() == "all": |
|||
return bps |
|||
|
|||
# If the filter returns only one bp, we assume that this one needed |
|||
# and therefore, we ignore the generation |
|||
if len(bps) == 1: |
|||
return bps |
|||
|
|||
try: |
|||
int_generation = int(generation) |
|||
# Check if generation is in available generations |
|||
if int_generation in [1, 2, 3, 4]: |
|||
bps = [x for x in bps if int(x.get_attribute('generation')) == int_generation] |
|||
return bps |
|||
else: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
except: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
|
|||
#--------- |
|||
# InvertedAI - CARLA synchronization routines |
|||
#--------- |
|||
|
|||
# Get CARLA transform from IAI transform |
|||
def transform_iai_to_carla(agent_state): |
|||
agent_transform = carla.Transform( |
|||
carla.Location( |
|||
agent_state.center.x, |
|||
agent_state.center.y, |
|||
0.1 |
|||
), |
|||
carla.Rotation( |
|||
yaw=math.degrees(agent_state.orientation) |
|||
) |
|||
) |
|||
|
|||
return agent_transform |
|||
|
|||
# Update transforms of CARLA agents driven by IAI and tick the world |
|||
def update_transforms(iai2carla,response): |
|||
""" |
|||
Tick the carla simulation forward one time step |
|||
Assume carla_actors is a list of carla actors controlled by IAI |
|||
""" |
|||
for agent_id in iai2carla.keys(): |
|||
agentdict = iai2carla[agent_id] |
|||
if agentdict["is_iai"]: |
|||
agent = response.agent_states[agent_id] |
|||
agent_transform = transform_iai_to_carla(agent) |
|||
try: |
|||
actor = agentdict["actor"] |
|||
actor.set_transform(agent_transform) |
|||
except: |
|||
pass |
|||
|
|||
# Assign existing IAI agents to CARLA vehicle blueprints and add these agents to the CARLA simulation |
|||
def assign_carla_blueprints_to_iai_agents(world,vehicle_blueprints,agent_properties,agent_states,recurrent_states,is_iai,noniai_actors): |
|||
|
|||
agent_properties_new = [] |
|||
agent_states_new = [] |
|||
recurrent_states_new = [] |
|||
iai2carla = {} |
|||
|
|||
for agent_id, state in enumerate(agent_states): |
|||
|
|||
if not is_iai[agent_id]: |
|||
agent_properties_new.append(agent_properties[agent_id]) |
|||
agent_states_new.append(agent_states[agent_id]) |
|||
recurrent_states_new.append(recurrent_states[agent_id]) |
|||
actor = noniai_actors[agent_id] |
|||
iai2carla[len(iai2carla)] = {"actor":actor, "is_iai":False, "type":agent_properties[agent_id].agent_type} |
|||
|
|||
else: |
|||
|
|||
blueprint = random.choice(vehicle_blueprints) |
|||
if blueprint.has_attribute('color'): |
|||
color = random.choice(blueprint.get_attribute('color').recommended_values) |
|||
blueprint.set_attribute('color', color) |
|||
agent_transform = transform_iai_to_carla(state) |
|||
|
|||
actor = world.try_spawn_actor(blueprint,agent_transform) |
|||
|
|||
if actor is not None: |
|||
bb = actor.bounding_box.extent |
|||
|
|||
agent_attr = agent_properties[agent_id] |
|||
|
|||
agent_attr.length = 2*bb.x |
|||
agent_attr.width = 2*bb.y |
|||
agent_attr.rear_axis_offset = 2*bb.x/3 |
|||
|
|||
agent_properties_new.append(agent_attr) |
|||
agent_states_new.append(agent_states[agent_id]) |
|||
recurrent_states_new.append(recurrent_states[agent_id]) |
|||
|
|||
actor.set_simulate_physics(False) |
|||
|
|||
iai2carla[len(iai2carla)] = {"actor":actor, "is_iai":True, "type":agent_properties[agent_id].agent_type} |
|||
|
|||
if len(agent_properties_new) == 0: |
|||
raise Exception("No vehicles could be placed in Carla environment.") |
|||
|
|||
return agent_properties_new, agent_states_new, recurrent_states_new, iai2carla |
|||
|
|||
# Initialize InvertedAI co-simulation |
|||
def initialize_simulation(args, world, agent_states=None, agent_properties=None): |
|||
|
|||
iai_seed = args.seed if args.seed is not None else random.randint(1,10000) |
|||
traffic_lights_states, carla2iai_tl = initialize_tl_states(world) |
|||
|
|||
################################################################################################# |
|||
# Initialize IAI Agents |
|||
map_center = args.map_center |
|||
print(f"Call location info.") |
|||
location_info_response = iai.location_info( |
|||
location = args.location, |
|||
rendering_center = map_center |
|||
) |
|||
print(f"Begin initialization.") |
|||
# Acquire a grid of 100x100m regions in which to initialize vehicles to be controlled by IAI. |
|||
regions = iai.get_regions_default( |
|||
location = args.location, |
|||
total_num_agents = args.number_of_vehicles, |
|||
area_shape = (int(args.width/2),int(args.height/2)), |
|||
map_center = map_center, |
|||
) |
|||
# Place vehicles within the specified regions which will consider the relative states of nearby vehicles in neighbouring regions. |
|||
response = iai.large_initialize( |
|||
location = args.location, |
|||
regions = regions, |
|||
traffic_light_state_history = [traffic_lights_states], |
|||
agent_states = agent_states, |
|||
agent_properties = agent_properties, |
|||
random_seed = iai_seed |
|||
) |
|||
|
|||
return response, carla2iai_tl, location_info_response |
|||
|
|||
#--------- |
|||
# Synchronize InvertedAI and CARLA traffic lights |
|||
#--------- |
|||
|
|||
# Mapping between CARLA and IAI traffic lights IDs |
|||
def get_traffic_lights_mapping(world): |
|||
tls = world.get_actors().filter('traffic.traffic_light*') |
|||
tl_ids = sorted([tl.id for tl in list(tls)]) |
|||
carla2iai_tl = {} |
|||
# ID for IAI traffic lights, only valid for Town10 for now (in both UE4 and UE5 versions of the map) |
|||
iai_tl_id = 4364 |
|||
for carla_tl_id in tl_ids: |
|||
carla2iai_tl[str(carla_tl_id)] = [str(iai_tl_id), str(iai_tl_id+1000)] |
|||
iai_tl_id+=1 |
|||
|
|||
return carla2iai_tl |
|||
|
|||
# Returns IAI traffic light state based on CARLA traffic light state |
|||
def get_traffic_light_state_from_carla(carla_tl_state): |
|||
|
|||
if carla_tl_state == carla.TrafficLightState.Red: |
|||
return TrafficLightState.red |
|||
|
|||
elif carla_tl_state == carla.TrafficLightState.Yellow: |
|||
return TrafficLightState.yellow |
|||
|
|||
elif carla_tl_state == carla.TrafficLightState.Green: |
|||
return TrafficLightState.green |
|||
|
|||
else: # Unknown state, turn off traffic light |
|||
return TrafficLightState.Off |
|||
|
|||
# Assign IAI traffic lights based on the CARLA ones |
|||
def assign_iai_traffic_lights_from_carla(world, iai_tl, carla2iai_tl): |
|||
|
|||
traffic_lights = world.get_actors().filter('traffic.traffic_light*') |
|||
|
|||
carla_tl_dict = {} |
|||
for tl in traffic_lights: |
|||
carla_tl_dict[str(tl.id)]=tl.state |
|||
|
|||
for carla_tl_id, carla_state in carla_tl_dict.items(): |
|||
iai_tl_id_pair = carla2iai_tl[carla_tl_id] |
|||
for iai_tl_id in iai_tl_id_pair: |
|||
iai_tl[iai_tl_id] = get_traffic_light_state_from_carla(carla_state) |
|||
|
|||
return iai_tl |
|||
|
|||
# Initialize traffic lights states |
|||
def initialize_tl_states(world): |
|||
carla2iai_tl = get_traffic_lights_mapping(world) |
|||
iai_tl_states = {} |
|||
for tlpair in carla2iai_tl.values(): |
|||
for tl in tlpair: |
|||
iai_tl_states[tl] = TrafficLightState.red # Initialize to given value |
|||
|
|||
iai_tl_states = assign_iai_traffic_lights_from_carla(world, iai_tl_states, carla2iai_tl) |
|||
return iai_tl_states, carla2iai_tl |
|||
|
|||
|
|||
def get_distance(point1, point2): |
|||
return np.sqrt((point2[0] - point1[0])**2 + (point2[1] - point1[1])**2) |
|||
|
|||
|
|||
def new_waypoint_needed(waypoint, agent_state, detection_threshold, max_distance_away): |
|||
distance_from_waypoint = get_distance(waypoint, [agent_state.center.x, agent_state.center.y]) |
|||
return distance_from_waypoint <= detection_threshold or distance_from_waypoint >= max_distance_away |
|||
|
|||
|
|||
def get_next_waypoints(carla_map, agent_states, waypoints, args): |
|||
indices_of_interest = [] |
|||
interested_agent_states = [] |
|||
if len(waypoints) == 0: |
|||
indices_of_interest = [i for i in range(len(agent_states))] |
|||
else: |
|||
indices_of_interest = [i for i in range(len(agent_states)) if new_waypoint_needed(waypoints[i], agent_states[i], args.iai_waypoint_detection_threshold, args.iai_max_distance_away)] |
|||
vehicle_locations = [Location(x=agent_states[i].center.x, y=agent_states[i].center.y, z=0.1) for i in indices_of_interest] |
|||
closest_waypoints = [carla_map.get_waypoint(vehicle_location, project_to_road=True, lane_type=carla.LaneType.Driving) for vehicle_location in vehicle_locations] |
|||
next_waypoints = [np.random.choice(wp.next(args.iai_waypoint_distance)) for wp in closest_waypoints] |
|||
results = [] |
|||
if len(waypoints) == 0: |
|||
results = [[waypoint.transform.location.x, waypoint.transform.location.y] for waypoint in next_waypoints] |
|||
else: |
|||
results = waypoints |
|||
for ind in range(len(next_waypoints)): |
|||
results[indices_of_interest[ind]] = [next_waypoints[ind].transform.location.x, next_waypoints[ind].transform.location.y] |
|||
return results |
|||
|
|||
#--------- |
|||
# Main |
|||
#--------- |
|||
def main(): |
|||
|
|||
args = argument_parser() |
|||
|
|||
# Setup CARLA client and world |
|||
client, world = setup_carla_environment(args.host, args.port, args.location) |
|||
|
|||
# Specify the IAI API key |
|||
try: |
|||
iai.add_apikey(args.iai_key) |
|||
except: |
|||
print("\n\tYou need to indicate the InvertedAI API key with the argument --iai-key. To obtain one, please go to https://www.inverted.ai \n") |
|||
|
|||
num_pedestrians = args.number_of_walkers |
|||
|
|||
FPS = int(1./world.get_settings().fixed_delta_seconds) |
|||
|
|||
if args.record: |
|||
logfolder = os.getcwd()+"/logs/" |
|||
if not os.path.exists(logfolder): |
|||
os.system("mkdir "+logfolder) |
|||
logfile = logfolder+"carla_record.log" |
|||
client.start_recorder(logfile) |
|||
print("Recording on file: %s" % logfile) |
|||
|
|||
seed = args.seed |
|||
|
|||
if seed: |
|||
random.seed(seed) |
|||
|
|||
vehicle_blueprints = get_actor_blueprints(world, args.filterv, args.generationv) |
|||
if args.safe: |
|||
vehicle_blueprints = [x for x in vehicle_blueprints if x.get_attribute('base_type') == 'car'] |
|||
|
|||
agent_states, agent_properties = [], [] |
|||
is_iai = [] |
|||
noniai_actors = [] |
|||
|
|||
# Add pedestrians (not driven by IAI) |
|||
if num_pedestrians>0: |
|||
if seed: |
|||
world.set_pedestrians_seed(seed) |
|||
blueprintsWalkers = get_actor_blueprints(world, args.filterw, args.generationw) |
|||
if not blueprintsWalkers: |
|||
raise ValueError("Couldn't find any walkers with the specified filters") |
|||
pedestrians = spawn_pedestrians(client, world, num_pedestrians, blueprintsWalkers) |
|||
iai_pedestrians_states, iai_pedestrians_properties = initialize_pedestrians(pedestrians) |
|||
agent_states.extend(iai_pedestrians_states) |
|||
agent_properties.extend(iai_pedestrians_properties) |
|||
is_iai.extend( [False]*len(iai_pedestrians_states) ) |
|||
noniai_actors.extend(pedestrians) |
|||
|
|||
else: |
|||
pedestrians = [] |
|||
|
|||
num_noniai = len(agent_properties) |
|||
# Initialize InvertedAI co-simulation |
|||
response, carla2iai_tl, location_info_response = initialize_simulation(args, world, agent_states=agent_states, agent_properties=agent_properties) |
|||
agent_properties = response.agent_properties |
|||
is_iai.extend( [True]*(len(agent_properties)-num_noniai) ) |
|||
|
|||
# Map IAI agents to CARLA actors and update response properties and states |
|||
print(f"Number of agents initialized: {len(response.agent_states)}") |
|||
agent_properties, agent_states_new, recurrent_states_new, iai2carla = assign_carla_blueprints_to_iai_agents(world,vehicle_blueprints,agent_properties,response.agent_states,response.recurrent_states,is_iai,noniai_actors) |
|||
traffic_lights_states = assign_iai_traffic_lights_from_carla(world,response.traffic_lights_states, carla2iai_tl) |
|||
response.agent_states = agent_states_new |
|||
response.agent_properties = agent_properties |
|||
response.recurrent_states = recurrent_states_new |
|||
response.traffic_lights_states = traffic_lights_states |
|||
|
|||
# Write InvertedAI log file, which can be opened afterwards to visualize a gif and further analysis |
|||
# See an example of usage here: https://github.com/inverted-ai/invertedai/blob/master/examples/scenario_log_example.py |
|||
|
|||
if args.iai_log: |
|||
log_writer = iai.LogWriter() |
|||
log_writer.initialize( |
|||
location=args.location, |
|||
location_info_response=location_info_response, |
|||
init_response=response |
|||
) |
|||
iailog_path = os.path.join(os.getcwd(),f"iai_log.json") |
|||
|
|||
# Perform first CARLA simulation tick |
|||
world.tick() |
|||
|
|||
iai_agent_indices = [i for i in range(len(agent_properties)) if iai2carla[i]["is_iai"]] |
|||
try: |
|||
|
|||
vehicles = world.get_actors().filter('vehicle.*') |
|||
print("Total number of agents:",len(agent_properties),"Vehicles",len(vehicles), "Pedestrians:",len(pedestrians)) |
|||
for index in iai_agent_indices: |
|||
agent_properties[index].max_speed = 10.0 |
|||
# Get hero vehicle |
|||
hero_v = None |
|||
if args.hero: |
|||
hero_v = vehicles[0] |
|||
|
|||
carla_map = world.get_map() |
|||
waypoints = [] |
|||
for frame in tqdm(range(args.sim_length * FPS)): |
|||
response.traffic_lights_states = assign_iai_traffic_lights_from_carla(world, response.traffic_lights_states, carla2iai_tl) |
|||
iai_agent_states = [response.agent_states[i] for i in iai_agent_indices] |
|||
waypoints = get_next_waypoints(carla_map, iai_agent_states, waypoints, args) |
|||
for i in range(len(waypoints)): |
|||
agent_properties[iai_agent_indices[i]].waypoint = Point(x=waypoints[i][0], y=waypoints[i][1]) |
|||
|
|||
# IAI update step |
|||
response = iai.large_drive( |
|||
location = args.location, |
|||
agent_states = response.agent_states, |
|||
agent_properties = agent_properties, |
|||
recurrent_states = response.recurrent_states, |
|||
traffic_lights_states = response.traffic_lights_states, |
|||
single_call_agent_limit = args.capacity, |
|||
async_api_calls = args.iai_async, |
|||
api_model_version = args.api_model, |
|||
random_seed = seed |
|||
) |
|||
|
|||
if args.iai_log: |
|||
log_writer.drive(drive_response=response) |
|||
|
|||
# Update CARLA actors with new transforms from IAI agents |
|||
update_transforms(iai2carla,response) |
|||
|
|||
# Tick CARLA simulation |
|||
world.tick() |
|||
|
|||
# Update agents not driven by IAI in IAI cosimulation, like pedestrians |
|||
for agent_id in iai2carla.keys(): |
|||
agentdict = iai2carla[agent_id] |
|||
|
|||
if not agentdict["is_iai"] and agentdict["type"] == "pedestrian": |
|||
actor = agentdict["actor"] |
|||
state, properties = initialize_iai_agent(actor, agentdict["type"]) |
|||
response.agent_states[agent_id] = state |
|||
agent_properties[agent_id] = properties |
|||
|
|||
# Update spectator view if there is hero vehicle |
|||
if hero_v is not None: |
|||
set_spectator(world, hero_v) |
|||
|
|||
|
|||
|
|||
finally: |
|||
time.sleep(0.5) |
|||
|
|||
if args.record: |
|||
client.stop_recorder() |
|||
|
|||
if args.iai_log: |
|||
log_writer.export_to_file(log_path=iailog_path) |
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,345 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
Lidar projection on RGB camera example |
|||
""" |
|||
|
|||
import os |
|||
import sys |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
from queue import Queue |
|||
from queue import Empty |
|||
from matplotlib import cm |
|||
|
|||
try: |
|||
import numpy as np |
|||
except ImportError: |
|||
raise RuntimeError('cannot import numpy, make sure numpy package is installed') |
|||
|
|||
try: |
|||
from PIL import Image |
|||
except ImportError: |
|||
raise RuntimeError('cannot import PIL, make sure "Pillow" package is installed') |
|||
|
|||
VIRIDIS = np.array(cm._colormaps.get_cmap('viridis').colors) |
|||
VID_RANGE = np.linspace(0.0, 1.0, VIRIDIS.shape[0]) |
|||
|
|||
def sensor_callback(data, queue): |
|||
""" |
|||
This simple callback just stores the data on a thread safe Python Queue |
|||
to be retrieved from the "main thread". |
|||
""" |
|||
queue.put(data) |
|||
|
|||
|
|||
def tutorial(args): |
|||
""" |
|||
This function is intended to be a tutorial on how to retrieve data in a |
|||
synchronous way, and project 3D points from a lidar to a 2D camera. |
|||
""" |
|||
# Connect to the server |
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
bp_lib = world.get_blueprint_library() |
|||
|
|||
traffic_manager = client.get_trafficmanager(8000) |
|||
traffic_manager.set_synchronous_mode(True) |
|||
|
|||
original_settings = world.get_settings() |
|||
settings = world.get_settings() |
|||
settings.synchronous_mode = True |
|||
settings.fixed_delta_seconds = 3.0 |
|||
world.apply_settings(settings) |
|||
|
|||
vehicle = None |
|||
camera = None |
|||
lidar = None |
|||
|
|||
try: |
|||
if not os.path.isdir('_out'): |
|||
os.mkdir('_out') |
|||
# Search the desired blueprints |
|||
vehicle_bp = bp_lib.filter("vehicle.lincoln.mkz_2017")[0] |
|||
camera_bp = bp_lib.filter("sensor.camera.rgb")[0] |
|||
lidar_bp = bp_lib.filter("sensor.lidar.ray_cast")[0] |
|||
|
|||
# Configure the blueprints |
|||
camera_bp.set_attribute("image_size_x", str(args.width)) |
|||
camera_bp.set_attribute("image_size_y", str(args.height)) |
|||
|
|||
if args.no_noise: |
|||
lidar_bp.set_attribute('dropoff_general_rate', '0.0') |
|||
lidar_bp.set_attribute('dropoff_intensity_limit', '1.0') |
|||
lidar_bp.set_attribute('dropoff_zero_intensity', '0.0') |
|||
lidar_bp.set_attribute('upper_fov', str(args.upper_fov)) |
|||
lidar_bp.set_attribute('lower_fov', str(args.lower_fov)) |
|||
lidar_bp.set_attribute('channels', str(args.channels)) |
|||
lidar_bp.set_attribute('range', str(args.range)) |
|||
lidar_bp.set_attribute('points_per_second', str(args.points_per_second)) |
|||
|
|||
# Spawn the blueprints |
|||
vehicle = world.spawn_actor( |
|||
blueprint=vehicle_bp, |
|||
transform=world.get_map().get_spawn_points()[0]) |
|||
vehicle.set_autopilot(True) |
|||
camera = world.spawn_actor( |
|||
blueprint=camera_bp, |
|||
transform=carla.Transform(carla.Location(x=1.6, z=1.6)), |
|||
attach_to=vehicle) |
|||
lidar = world.spawn_actor( |
|||
blueprint=lidar_bp, |
|||
transform=carla.Transform(carla.Location(x=1.0, z=1.8)), |
|||
attach_to=vehicle) |
|||
|
|||
# Build the K projection matrix: |
|||
# K = [[Fx, 0, image_w/2], |
|||
# [ 0, Fy, image_h/2], |
|||
# [ 0, 0, 1]] |
|||
image_w = camera_bp.get_attribute("image_size_x").as_int() |
|||
image_h = camera_bp.get_attribute("image_size_y").as_int() |
|||
fov = camera_bp.get_attribute("fov").as_float() |
|||
focal = image_w / (2.0 * np.tan(fov * np.pi / 360.0)) |
|||
|
|||
# In this case Fx and Fy are the same since the pixel aspect |
|||
# ratio is 1 |
|||
K = np.identity(3) |
|||
K[0, 0] = K[1, 1] = focal |
|||
K[0, 2] = image_w / 2.0 |
|||
K[1, 2] = image_h / 2.0 |
|||
|
|||
# The sensor data will be saved in thread-safe Queues |
|||
image_queue = Queue() |
|||
lidar_queue = Queue() |
|||
|
|||
camera.listen(lambda data: sensor_callback(data, image_queue)) |
|||
lidar.listen(lambda data: sensor_callback(data, lidar_queue)) |
|||
|
|||
for frame in range(args.frames): |
|||
world.tick() |
|||
world_frame = world.get_snapshot().frame |
|||
|
|||
try: |
|||
# Get the data once it's received. |
|||
image_data = image_queue.get(True, 1.0) |
|||
lidar_data = lidar_queue.get(True, 1.0) |
|||
except Empty: |
|||
print("[Warning] Some sensor data has been missed") |
|||
continue |
|||
|
|||
assert image_data.frame == lidar_data.frame == world_frame |
|||
# At this point, we have the synchronized information from the 2 sensors. |
|||
sys.stdout.write("\r(%d/%d) Simulation: %d Camera: %d Lidar: %d" % |
|||
(frame, args.frames, world_frame, image_data.frame, lidar_data.frame) + ' ') |
|||
sys.stdout.flush() |
|||
|
|||
# Get the raw BGRA buffer and convert it to an array of RGB of |
|||
# shape (image_data.height, image_data.width, 3). |
|||
im_array = np.copy(np.frombuffer(image_data.raw_data, dtype=np.dtype("uint8"))) |
|||
im_array = np.reshape(im_array, (image_data.height, image_data.width, 4)) |
|||
im_array = im_array[:, :, :3][:, :, ::-1] |
|||
|
|||
# Get the lidar data and convert it to a numpy array. |
|||
p_cloud_size = len(lidar_data) |
|||
p_cloud = np.copy(np.frombuffer(lidar_data.raw_data, dtype=np.dtype('f4'))) |
|||
p_cloud = np.reshape(p_cloud, (p_cloud_size, 4)) |
|||
|
|||
# Lidar intensity array of shape (p_cloud_size,) but, for now, let's |
|||
# focus on the 3D points. |
|||
intensity = np.array(p_cloud[:, 3]) |
|||
|
|||
# Point cloud in lidar sensor space array of shape (3, p_cloud_size). |
|||
local_lidar_points = np.array(p_cloud[:, :3]).T |
|||
|
|||
# Add an extra 1.0 at the end of each 3d point so it becomes of |
|||
# shape (4, p_cloud_size) and it can be multiplied by a (4, 4) matrix. |
|||
local_lidar_points = np.r_[ |
|||
local_lidar_points, [np.ones(local_lidar_points.shape[1])]] |
|||
|
|||
# This (4, 4) matrix transforms the points from lidar space to world space. |
|||
lidar_2_world = lidar.get_transform().get_matrix() |
|||
|
|||
# Transform the points from lidar space to world space. |
|||
world_points = np.dot(lidar_2_world, local_lidar_points) |
|||
|
|||
# This (4, 4) matrix transforms the points from world to sensor coordinates. |
|||
world_2_camera = np.array(camera.get_transform().get_inverse_matrix()) |
|||
|
|||
# Transform the points from world space to camera space. |
|||
sensor_points = np.dot(world_2_camera, world_points) |
|||
|
|||
# New we must change from UE4's coordinate system to an "standard" |
|||
# camera coordinate system (the same used by OpenCV): |
|||
|
|||
# ^ z . z |
|||
# | / |
|||
# | to: +-------> x |
|||
# | . x | |
|||
# |/ | |
|||
# +-------> y v y |
|||
|
|||
# This can be achieved by multiplying by the following matrix: |
|||
# [[ 0, 1, 0 ], |
|||
# [ 0, 0, -1 ], |
|||
# [ 1, 0, 0 ]] |
|||
|
|||
# Or, in this case, is the same as swapping: |
|||
# (x, y ,z) -> (y, -z, x) |
|||
point_in_camera_coords = np.array([ |
|||
sensor_points[1], |
|||
sensor_points[2] * -1, |
|||
sensor_points[0]]) |
|||
|
|||
# Finally we can use our K matrix to do the actual 3D -> 2D. |
|||
points_2d = np.dot(K, point_in_camera_coords) |
|||
|
|||
# Remember to normalize the x, y values by the 3rd value. |
|||
points_2d = np.array([ |
|||
points_2d[0, :] / points_2d[2, :], |
|||
points_2d[1, :] / points_2d[2, :], |
|||
points_2d[2, :]]) |
|||
|
|||
# At this point, points_2d[0, :] contains all the x and points_2d[1, :] |
|||
# contains all the y values of our points. In order to properly |
|||
# visualize everything on a screen, the points that are out of the screen |
|||
# must be discarted, the same with points behind the camera projection plane. |
|||
points_2d = points_2d.T |
|||
intensity = intensity.T |
|||
points_in_canvas_mask = \ |
|||
(points_2d[:, 0] > 0.0) & (points_2d[:, 0] < image_w) & \ |
|||
(points_2d[:, 1] > 0.0) & (points_2d[:, 1] < image_h) & \ |
|||
(points_2d[:, 2] > 0.0) |
|||
points_2d = points_2d[points_in_canvas_mask] |
|||
intensity = intensity[points_in_canvas_mask] |
|||
|
|||
# Extract the screen coords (uv) as integers. |
|||
u_coord = points_2d[:, 0].astype(int) |
|||
v_coord = points_2d[:, 1].astype(int) |
|||
|
|||
# Since at the time of the creation of this script, the intensity function |
|||
# is returning high values, these are adjusted to be nicely visualized. |
|||
intensity = 4 * intensity - 3 |
|||
color_map = np.array([ |
|||
np.interp(intensity, VID_RANGE, VIRIDIS[:, 0]) * 255.0, |
|||
np.interp(intensity, VID_RANGE, VIRIDIS[:, 1]) * 255.0, |
|||
np.interp(intensity, VID_RANGE, VIRIDIS[:, 2]) * 255.0]).astype(int).T |
|||
|
|||
if args.dot_extent <= 0: |
|||
# Draw the 2d points on the image as a single pixel using numpy. |
|||
im_array[v_coord, u_coord] = color_map |
|||
else: |
|||
# Draw the 2d points on the image as squares of extent args.dot_extent. |
|||
for i in range(len(points_2d)): |
|||
# I'm not a NumPy expert and I don't know how to set bigger dots |
|||
# without using this loop, so if anyone has a better solution, |
|||
# make sure to update this script. Meanwhile, it's fast enough :) |
|||
im_array[ |
|||
v_coord[i]-args.dot_extent : v_coord[i]+args.dot_extent, |
|||
u_coord[i]-args.dot_extent : u_coord[i]+args.dot_extent] = color_map[i] |
|||
|
|||
# Save the image using Pillow module. |
|||
image = Image.fromarray(im_array) |
|||
image.save("_out/%08d.png" % image_data.frame) |
|||
|
|||
finally: |
|||
# Apply the original settings when exiting. |
|||
world.apply_settings(original_settings) |
|||
|
|||
# Destroy the actors in the scene. |
|||
if camera: |
|||
camera.destroy() |
|||
if lidar: |
|||
lidar.destroy() |
|||
if vehicle: |
|||
vehicle.destroy() |
|||
|
|||
|
|||
def main(): |
|||
"""Start function""" |
|||
argparser = argparse.ArgumentParser( |
|||
description='CARLA Sensor sync and projection tutorial') |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'--res', |
|||
metavar='WIDTHxHEIGHT', |
|||
default='680x420', |
|||
help='window resolution (default: 1280x720)') |
|||
argparser.add_argument( |
|||
'-f', '--frames', |
|||
metavar='N', |
|||
default=500, |
|||
type=int, |
|||
help='number of frames to record (default: 500)') |
|||
argparser.add_argument( |
|||
'-d', '--dot-extent', |
|||
metavar='SIZE', |
|||
default=2, |
|||
type=int, |
|||
help='visualization dot extent in pixels (Recomended [1-4]) (default: 2)') |
|||
argparser.add_argument( |
|||
'--no-noise', |
|||
action='store_true', |
|||
help='remove the drop off and noise from the normal (non-semantic) lidar') |
|||
argparser.add_argument( |
|||
'--upper-fov', |
|||
metavar='F', |
|||
default=30.0, |
|||
type=float, |
|||
help='lidar\'s upper field of view in degrees (default: 15.0)') |
|||
argparser.add_argument( |
|||
'--lower-fov', |
|||
metavar='F', |
|||
default=-25.0, |
|||
type=float, |
|||
help='lidar\'s lower field of view in degrees (default: -25.0)') |
|||
argparser.add_argument( |
|||
'-c', '--channels', |
|||
metavar='C', |
|||
default=64.0, |
|||
type=float, |
|||
help='lidar\'s channel count (default: 64)') |
|||
argparser.add_argument( |
|||
'-r', '--range', |
|||
metavar='R', |
|||
default=100.0, |
|||
type=float, |
|||
help='lidar\'s maximum range in meters (default: 100.0)') |
|||
argparser.add_argument( |
|||
'--points-per-second', |
|||
metavar='N', |
|||
default='100000', |
|||
type=int, |
|||
help='lidar points per second (default: 100000)') |
|||
args = argparser.parse_args() |
|||
args.width, args.height = [int(x) for x in args.res.split('x')] |
|||
args.dot_extent -= 1 |
|||
|
|||
try: |
|||
tutorial(args) |
|||
|
|||
except KeyboardInterrupt: |
|||
print('\nCancelled by user. Bye!') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
1367
carla_examples/manual_control.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1175
carla_examples/manual_control_carsim.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1196
carla_examples/manual_control_chrono.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,848 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Intel Labs |
|||
# |
|||
# 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. |
|||
|
|||
""" |
|||
Welcome to CARLA manual control with steering wheel Logitech G29. |
|||
|
|||
To drive start by preshing the brake pedal. |
|||
Change your wheel_config.ini according to your steering wheel. |
|||
|
|||
To find out the values of your steering wheel use jstest-gtk in Ubuntu. |
|||
|
|||
""" |
|||
|
|||
from __future__ import print_function |
|||
|
|||
# ============================================================================== |
|||
# -- imports ------------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
import carla |
|||
|
|||
from carla import ColorConverter as cc |
|||
|
|||
import argparse |
|||
import collections |
|||
import datetime |
|||
import logging |
|||
import math |
|||
import os |
|||
import random |
|||
import re |
|||
import sys |
|||
import weakref |
|||
|
|||
if sys.version_info >= (3, 0): |
|||
|
|||
from configparser import ConfigParser |
|||
|
|||
else: |
|||
|
|||
from ConfigParser import RawConfigParser as ConfigParser |
|||
|
|||
try: |
|||
import pygame |
|||
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 |
|||
from pygame.locals import K_BACKSPACE |
|||
from pygame.locals import K_COMMA |
|||
from pygame.locals import K_DOWN |
|||
from pygame.locals import K_ESCAPE |
|||
from pygame.locals import K_F1 |
|||
from pygame.locals import K_LEFT |
|||
from pygame.locals import K_PERIOD |
|||
from pygame.locals import K_RIGHT |
|||
from pygame.locals import K_SLASH |
|||
from pygame.locals import K_SPACE |
|||
from pygame.locals import K_TAB |
|||
from pygame.locals import K_UP |
|||
from pygame.locals import K_a |
|||
from pygame.locals import K_c |
|||
from pygame.locals import K_d |
|||
from pygame.locals import K_h |
|||
from pygame.locals import K_m |
|||
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 |
|||
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') |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- Global functions ---------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
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] |
|||
|
|||
|
|||
def get_actor_display_name(actor, truncate=250): |
|||
name = ' '.join(actor.type_id.replace('_', '.').title().split('.')[1:]) |
|||
return (name[:truncate - 1] + u'\u2026') if len(name) > truncate else name |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- World --------------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class World(object): |
|||
def __init__(self, carla_world, hud, actor_filter): |
|||
self.world = carla_world |
|||
self.hud = hud |
|||
self.player = None |
|||
self.collision_sensor = None |
|||
self.lane_invasion_sensor = None |
|||
self.gnss_sensor = None |
|||
self.camera_manager = None |
|||
self._weather_presets = find_weather_presets() |
|||
self._weather_index = 0 |
|||
self._actor_filter = actor_filter |
|||
self.restart() |
|||
self.world.on_tick(hud.on_world_tick) |
|||
|
|||
def restart(self): |
|||
# Keep same camera config if the camera manager exists. |
|||
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', 'hero') |
|||
if blueprint.has_attribute('color'): |
|||
color = random.choice(blueprint.get_attribute('color').recommended_values) |
|||
blueprint.set_attribute('color', color) |
|||
# 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: |
|||
spawn_points = self.world.get_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.camera_manager = CameraManager(self.player, self.hud) |
|||
self.camera_manager.transform_index = cam_pos_index |
|||
self.camera_manager.set_sensor(cam_index, notify=False) |
|||
actor_type = get_actor_display_name(self.player) |
|||
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]) |
|||
|
|||
def tick(self, clock): |
|||
self.hud.tick(self, clock) |
|||
|
|||
def render(self, display): |
|||
self.camera_manager.render(display) |
|||
self.hud.render(display) |
|||
|
|||
def destroy(self): |
|||
sensors = [ |
|||
self.camera_manager.sensor, |
|||
self.collision_sensor.sensor, |
|||
self.lane_invasion_sensor.sensor, |
|||
self.gnss_sensor.sensor] |
|||
for sensor in sensors: |
|||
if sensor is not None: |
|||
sensor.stop() |
|||
sensor.destroy() |
|||
if self.player is not None: |
|||
self.player.destroy() |
|||
|
|||
# ============================================================================== |
|||
# -- DualControl ----------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class DualControl(object): |
|||
def __init__(self, world, start_in_autopilot): |
|||
self._autopilot_enabled = start_in_autopilot |
|||
if isinstance(world.player, carla.Vehicle): |
|||
self._control = carla.VehicleControl() |
|||
world.player.set_autopilot(self._autopilot_enabled) |
|||
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") |
|||
self._steer_cache = 0.0 |
|||
world.hud.notification("Press 'H' or '?' for help.", seconds=4.0) |
|||
|
|||
# initialize steering wheel |
|||
pygame.joystick.init() |
|||
|
|||
joystick_count = pygame.joystick.get_count() |
|||
if joystick_count > 1: |
|||
raise ValueError("Please Connect Just One Joystick") |
|||
|
|||
self._joystick = pygame.joystick.Joystick(0) |
|||
self._joystick.init() |
|||
|
|||
self._parser = ConfigParser() |
|||
self._parser.read('wheel_config.ini') |
|||
self._steer_idx = int( |
|||
self._parser.get('G29 Racing Wheel', 'steering_wheel')) |
|||
self._throttle_idx = int( |
|||
self._parser.get('G29 Racing Wheel', 'throttle')) |
|||
self._brake_idx = int(self._parser.get('G29 Racing Wheel', 'brake')) |
|||
self._reverse_idx = int(self._parser.get('G29 Racing Wheel', 'reverse')) |
|||
self._handbrake_idx = int( |
|||
self._parser.get('G29 Racing Wheel', 'handbrake')) |
|||
|
|||
def parse_events(self, world, clock): |
|||
for event in pygame.event.get(): |
|||
if event.type == pygame.QUIT: |
|||
return True |
|||
elif event.type == pygame.JOYBUTTONDOWN: |
|||
if event.button == 0: |
|||
world.restart() |
|||
elif event.button == 1: |
|||
world.hud.toggle_info() |
|||
elif event.button == 2: |
|||
world.camera_manager.toggle_camera() |
|||
elif event.button == 3: |
|||
world.next_weather() |
|||
elif event.button == self._reverse_idx: |
|||
self._control.gear = 1 if self._control.reverse else -1 |
|||
elif event.button == 23: |
|||
world.camera_manager.next_sensor() |
|||
|
|||
elif event.type == pygame.KEYUP: |
|||
if self._is_quit_shortcut(event.key): |
|||
return True |
|||
elif event.key == K_BACKSPACE: |
|||
world.restart() |
|||
elif event.key == K_F1: |
|||
world.hud.toggle_info() |
|||
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_BACKQUOTE: |
|||
world.camera_manager.next_sensor() |
|||
elif event.key > K_0 and event.key <= K_9: |
|||
world.camera_manager.set_sensor(event.key - 1 - K_0) |
|||
elif event.key == K_r: |
|||
world.camera_manager.toggle_recording() |
|||
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 |
|||
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: |
|||
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')) |
|||
|
|||
if not self._autopilot_enabled: |
|||
if isinstance(self._control, carla.VehicleControl): |
|||
self._parse_vehicle_keys(pygame.key.get_pressed(), clock.get_time()) |
|||
self._parse_vehicle_wheel() |
|||
self._control.reverse = self._control.gear < 0 |
|||
elif isinstance(self._control, carla.WalkerControl): |
|||
self._parse_walker_keys(pygame.key.get_pressed(), clock.get_time()) |
|||
world.player.apply_control(self._control) |
|||
|
|||
def _parse_vehicle_keys(self, keys, milliseconds): |
|||
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]: |
|||
self._steer_cache -= steer_increment |
|||
elif keys[K_RIGHT] or keys[K_d]: |
|||
self._steer_cache += steer_increment |
|||
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_vehicle_wheel(self): |
|||
numAxes = self._joystick.get_numaxes() |
|||
jsInputs = [float(self._joystick.get_axis(i)) for i in range(numAxes)] |
|||
# print (jsInputs) |
|||
jsButtons = [float(self._joystick.get_button(i)) for i in |
|||
range(self._joystick.get_numbuttons())] |
|||
|
|||
# Custom function to map range of inputs [1, -1] to outputs [0, 1] i.e 1 from inputs means nothing is pressed |
|||
# For the steering, it seems fine as it is |
|||
K1 = 1.0 # 0.55 |
|||
steerCmd = K1 * math.tan(1.1 * jsInputs[self._steer_idx]) |
|||
|
|||
K2 = 1.6 # 1.6 |
|||
throttleCmd = K2 + (2.05 * math.log10( |
|||
-0.7 * jsInputs[self._throttle_idx] + 1.4) - 1.2) / 0.92 |
|||
if throttleCmd <= 0: |
|||
throttleCmd = 0 |
|||
elif throttleCmd > 1: |
|||
throttleCmd = 1 |
|||
|
|||
brakeCmd = 1.6 + (2.05 * math.log10( |
|||
-0.7 * jsInputs[self._brake_idx] + 1.4) - 1.2) / 0.92 |
|||
if brakeCmd <= 0: |
|||
brakeCmd = 0 |
|||
elif brakeCmd > 1: |
|||
brakeCmd = 1 |
|||
|
|||
self._control.steer = steerCmd |
|||
self._control.brake = brakeCmd |
|||
self._control.throttle = throttleCmd |
|||
|
|||
#toggle = jsButtons[self._reverse_idx] |
|||
|
|||
self._control.hand_brake = bool(jsButtons[self._handbrake_idx]) |
|||
|
|||
def _parse_walker_keys(self, keys, milliseconds): |
|||
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 = 5.556 if pygame.key.get_mods() & KMOD_SHIFT else 2.778 |
|||
self._control.jump = keys[K_SPACE] |
|||
self._rotation.yaw = round(self._rotation.yaw, 1) |
|||
self._control.direction = self._rotation.get_forward_vector() |
|||
|
|||
@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) |
|||
font_name = 'courier' if os.name == 'nt' else 'mono' |
|||
fonts = [x for x in pygame.font.get_fonts() if font_name in x] |
|||
default_font = 'ubuntumono' |
|||
mono = default_font if default_font in fonts else fonts[0] |
|||
mono = pygame.font.match_font(mono) |
|||
self._font_mono = pygame.font.Font(mono, 12 if os.name == 'nt' else 14) |
|||
self._notifications = FadingText(font, (width, 40), (0, height - 40)) |
|||
self.help = HelpText(pygame.font.Font(mono, 24), width, height) |
|||
self.server_fps = 0 |
|||
self.frame = 0 |
|||
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() |
|||
self.server_fps = self._server_clock.get_fps() |
|||
self.frame = timestamp.frame |
|||
self.simulation_time = timestamp.elapsed_seconds |
|||
|
|||
def tick(self, world, clock): |
|||
self._notifications.tick(world, clock) |
|||
if not self._show_info: |
|||
return |
|||
t = world.player.get_transform() |
|||
v = world.player.get_velocity() |
|||
c = world.player.get_control() |
|||
heading = 'N' if abs(t.rotation.yaw) < 89.5 else '' |
|||
heading += 'S' if abs(t.rotation.yaw) > 90.5 else '' |
|||
heading += 'E' if 179.5 > t.rotation.yaw > 0.5 else '' |
|||
heading += 'W' if -0.5 > t.rotation.yaw > -179.5 else '' |
|||
colhist = world.collision_sensor.get_collision_history() |
|||
collision = [colhist[x + self.frame - 200] for x in range(0, 200)] |
|||
max_col = max(1.0, max(collision)) |
|||
collision = [x / max_col for x in collision] |
|||
vehicles = world.world.get_actors().filter('vehicle.*') |
|||
self._info_text = [ |
|||
'Server: % 16.0f FPS' % self.server_fps, |
|||
'Client: % 16.0f FPS' % clock.get_fps(), |
|||
'', |
|||
'Vehicle: % 20s' % get_actor_display_name(world.player, truncate=20), |
|||
'Map: % 20s' % world.world.get_map().name.split('/')[-1], |
|||
'Simulation time: % 12s' % datetime.timedelta(seconds=int(self.simulation_time)), |
|||
'', |
|||
'Speed: % 15.0f km/h' % (3.6 * math.sqrt(v.x**2 + v.y**2 + v.z**2)), |
|||
u'Heading:% 16.0f\N{DEGREE SIGN} % 2s' % (t.rotation.yaw, heading), |
|||
'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)), |
|||
'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 += [ |
|||
'', |
|||
'Collision:', |
|||
collision, |
|||
'', |
|||
'Number of vehicles: % 8d' % len(vehicles)] |
|||
if len(vehicles) > 1: |
|||
self._info_text += ['Nearby vehicles:'] |
|||
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] |
|||
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)) |
|||
|
|||
def toggle_info(self): |
|||
self._show_info = not self._show_info |
|||
|
|||
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): |
|||
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] |
|||
if item: # At this point has to be a str. |
|||
surface = self._font_mono.render(item, True, (255, 255, 255)) |
|||
display.blit(surface, (8, v_offset)) |
|||
v_offset += 18 |
|||
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): |
|||
def __init__(self, font, width, height): |
|||
lines = __doc__.split('\n') |
|||
self.font = font |
|||
self.dim = (680, len(lines) * 22 + 12) |
|||
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)) |
|||
for n, line in enumerate(lines): |
|||
text_texture = self.font.render(line, True, (255, 255, 255)) |
|||
self.surface.blit(text_texture, (22, n * 22)) |
|||
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) |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- CollisionSensor ----------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class CollisionSensor(object): |
|||
def __init__(self, parent_actor, hud): |
|||
self.sensor = None |
|||
self.history = [] |
|||
self._parent = parent_actor |
|||
self.hud = hud |
|||
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)) |
|||
|
|||
def get_collision_history(self): |
|||
history = collections.defaultdict(int) |
|||
for frame, intensity in self.history: |
|||
history[frame] += intensity |
|||
return history |
|||
|
|||
@staticmethod |
|||
def _on_collision(weak_self, event): |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
actor_type = get_actor_display_name(event.other_actor) |
|||
self.hud.notification('Collision with %r' % actor_type) |
|||
impulse = event.normal_impulse |
|||
intensity = math.sqrt(impulse.x**2 + impulse.y**2 + impulse.z**2) |
|||
self.history.append((event.frame, intensity)) |
|||
if len(self.history) > 4000: |
|||
self.history.pop(0) |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- LaneInvasionSensor -------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class LaneInvasionSensor(object): |
|||
def __init__(self, parent_actor, hud): |
|||
self.sensor = None |
|||
self._parent = parent_actor |
|||
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 |
|||
lane_types = set(x.type for x in event.crossed_lane_markings) |
|||
text = ['%r' % str(x).split()[-1] for x in lane_types] |
|||
self.hud.notification('Crossed line %s' % ' and '.join(text)) |
|||
|
|||
# ============================================================================== |
|||
# -- GnssSensor -------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
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)) |
|||
|
|||
@staticmethod |
|||
def _on_gnss_event(weak_self, event): |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
self.lat = event.latitude |
|||
self.lon = event.longitude |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- CameraManager ------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
class CameraManager(object): |
|||
def __init__(self, parent_actor, hud): |
|||
self.sensor = None |
|||
self.surface = None |
|||
self._parent = parent_actor |
|||
self.hud = hud |
|||
self.recording = False |
|||
self._camera_transforms = [ |
|||
carla.Transform(carla.Location(x=-5.5, z=2.8), carla.Rotation(pitch=-15)), |
|||
carla.Transform(carla.Location(x=1.6, z=1.7))] |
|||
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)'], |
|||
['sensor.camera.semantic_segmentation', cc.CityScapesPalette, |
|||
'Camera Semantic Segmentation (CityScapes Palette)'], |
|||
['sensor.lidar.ray_cast', None, 'Lidar (Ray-Cast)']] |
|||
world = self._parent.get_world() |
|||
bp_library = world.get_blueprint_library() |
|||
for item in self.sensors: |
|||
bp = bp_library.find(item[0]) |
|||
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])) |
|||
elif item[0].startswith('sensor.lidar'): |
|||
bp.set_attribute('range', '50') |
|||
item.append(bp) |
|||
self.index = None |
|||
|
|||
def toggle_camera(self): |
|||
self.transform_index = (self.transform_index + 1) % len(self._camera_transforms) |
|||
self.sensor.set_transform(self._camera_transforms[self.transform_index]) |
|||
|
|||
def set_sensor(self, index, notify=True): |
|||
index = index % len(self.sensors) |
|||
needs_respawn = True if self.index is None \ |
|||
else self.sensors[index][0] != self.sensors[self.index][0] |
|||
if needs_respawn: |
|||
if self.sensor is not None: |
|||
self.sensor.destroy() |
|||
self.surface = None |
|||
self.sensor = self._parent.get_world().spawn_actor( |
|||
self.sensors[index][-1], |
|||
self._camera_transforms[self.transform_index], |
|||
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 image: CameraManager._parse_image(weak_self, image)) |
|||
if notify: |
|||
self.hud.notification(self.sensors[index][2]) |
|||
self.index = index |
|||
|
|||
def next_sensor(self): |
|||
self.set_sensor(self.index + 1) |
|||
|
|||
def toggle_recording(self): |
|||
self.recording = not self.recording |
|||
self.hud.notification('Recording %s' % ('On' if self.recording else 'Off')) |
|||
|
|||
def render(self, display): |
|||
if self.surface is not None: |
|||
display.blit(self.surface, (0, 0)) |
|||
|
|||
@staticmethod |
|||
def _parse_image(weak_self, image): |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
if self.sensors[self.index][0].startswith('sensor.lidar'): |
|||
points = np.frombuffer(image.raw_data, dtype=np.dtype('f4')) |
|||
points = np.reshape(points, (int(points.shape[0] / 4), 4)) |
|||
lidar_data = np.array(points[:, :2]) |
|||
lidar_data *= min(self.hud.dim) / 100.0 |
|||
lidar_data += (0.5 * self.hud.dim[0], 0.5 * self.hud.dim[1]) |
|||
lidar_data = np.fabs(lidar_data) # pylint: disable=E1111 |
|||
lidar_data = lidar_data.astype(np.int32) |
|||
lidar_data = np.reshape(lidar_data, (-1, 2)) |
|||
lidar_img_size = (self.hud.dim[0], self.hud.dim[1], 3) |
|||
lidar_img = np.zeros(lidar_img_size) |
|||
lidar_img[tuple(lidar_data.T)] = (255, 255, 255) |
|||
self.surface = pygame.surfarray.make_surface(lidar_img) |
|||
else: |
|||
image.convert(self.sensors[self.index][1]) |
|||
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] |
|||
self.surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) |
|||
if self.recording: |
|||
image.save_to_disk('_out/%08d' % image.frame) |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- game_loop() --------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
def game_loop(args): |
|||
pygame.init() |
|||
pygame.font.init() |
|||
world = None |
|||
|
|||
try: |
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(2.0) |
|||
|
|||
display = pygame.display.set_mode( |
|||
(args.width, args.height), |
|||
pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
|
|||
hud = HUD(args.width, args.height) |
|||
world = World(client.get_world(), hud, args.filter) |
|||
controller = DualControl(world, args.autopilot) |
|||
|
|||
clock = pygame.time.Clock() |
|||
while True: |
|||
clock.tick_busy_loop(60) |
|||
if controller.parse_events(world, clock): |
|||
return |
|||
world.tick(clock) |
|||
world.render(display) |
|||
pygame.display.flip() |
|||
|
|||
finally: |
|||
|
|||
if world is not None: |
|||
world.destroy() |
|||
|
|||
pygame.quit() |
|||
|
|||
|
|||
# ============================================================================== |
|||
# -- main() -------------------------------------------------------------------- |
|||
# ============================================================================== |
|||
|
|||
|
|||
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', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
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') |
|||
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.*")') |
|||
args = argparser.parse_args() |
|||
|
|||
args.width, args.height = [int(x) for x in args.res.split('x')] |
|||
|
|||
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) |
|||
|
|||
print(__doc__) |
|||
|
|||
try: |
|||
|
|||
game_loop(args) |
|||
|
|||
except KeyboardInterrupt: |
|||
print('\nCancelled by user. Bye!') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
1627
carla_examples/no_rendering_mode.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,323 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
"""Open3D Lidar visualization example for CARLA""" |
|||
|
|||
import sys |
|||
import argparse |
|||
import time |
|||
from datetime import datetime |
|||
import random |
|||
import numpy as np |
|||
from matplotlib import cm |
|||
import open3d as o3d |
|||
|
|||
import carla |
|||
|
|||
VIRIDIS = np.array(cm._colormaps.get_cmap('plasma').colors) |
|||
VID_RANGE = np.linspace(0.0, 1.0, VIRIDIS.shape[0]) |
|||
LABEL_COLORS = np.array([ |
|||
(0, 0, 0), # 0: None |
|||
# cityscape labels |
|||
(128, 64, 128), # 1: Roads |
|||
(244, 35, 232), # 2: Sidewalks |
|||
(70, 70, 70), # 3: Buildings |
|||
(102, 102, 156), # 4: Walls |
|||
(190, 153, 153), # 5: Fences |
|||
(153, 153, 153), # 6: Poles |
|||
(250, 170, 30), # 7: TrafficLight |
|||
(220, 220, 0), # 8: TrafficSigns |
|||
(107, 142, 35), # 9: Vegetation |
|||
(145, 170, 100), # 10: Terrain |
|||
(70, 130, 180), # 11: Sky |
|||
(220, 20, 60), # 12: Pedestrians |
|||
(255, 0, 0), # 13: Rider |
|||
(0, 0, 142), # 14: Car |
|||
(0, 0, 70), # 15: Truck |
|||
(0, 60, 100), # 16: Bus |
|||
(0, 80, 100), # 17: Train |
|||
(0, 0, 230), # 18: Motorcycle |
|||
(119, 11, 32), # 19: Bicycle |
|||
# custom labels |
|||
(110, 190, 160), # 20: Static (custom, teal) |
|||
(170, 120, 50), # 21: Dynamic (custom, brown) |
|||
(55, 90, 80), # 22: Other (custom, dark teal) |
|||
(45, 60, 150), # 23: Water (custom, blue) |
|||
(157, 234, 50), # 24: RoadLines (custom, bright green) |
|||
(81, 0, 81), # 25: Ground (custom, purple) |
|||
(150, 100, 100), # 26: Bridge (custom, muted red) |
|||
(230, 150, 140), # 27: RailTrack (custom, pink) |
|||
(180, 165, 180), # 28: GuardRail (custom, light purple) |
|||
]) / 255.0 # normalize to [0, 1] for Open3D |
|||
|
|||
def lidar_callback(point_cloud, point_list): |
|||
"""Prepares a point cloud with intensity |
|||
colors ready to be consumed by Open3D""" |
|||
data = np.copy(np.frombuffer(point_cloud.raw_data, dtype=np.dtype('f4'))) |
|||
data = np.reshape(data, (int(data.shape[0] / 4), 4)) |
|||
|
|||
# Isolate the intensity and compute a color for it |
|||
intensity = data[:, -1] |
|||
intensity_col = 1.0 - np.log(intensity) / np.log(np.exp(-0.004 * 100)) |
|||
int_color = np.c_[ |
|||
np.interp(intensity_col, VID_RANGE, VIRIDIS[:, 0]), |
|||
np.interp(intensity_col, VID_RANGE, VIRIDIS[:, 1]), |
|||
np.interp(intensity_col, VID_RANGE, VIRIDIS[:, 2])] |
|||
|
|||
# Isolate the 3D data |
|||
points = data[:, :-1] |
|||
|
|||
# We're negating the y to correclty visualize a world that matches |
|||
# what we see in Unreal since Open3D uses a right-handed coordinate system |
|||
points[:, :1] = -points[:, :1] |
|||
|
|||
# # An example of converting points from sensor to vehicle space if we had |
|||
# # a carla.Transform variable named "tran": |
|||
# points = np.append(points, np.ones((points.shape[0], 1)), axis=1) |
|||
# points = np.dot(tran.get_matrix(), points.T).T |
|||
# points = points[:, :-1] |
|||
|
|||
point_list.points = o3d.utility.Vector3dVector(points) |
|||
point_list.colors = o3d.utility.Vector3dVector(int_color) |
|||
|
|||
|
|||
def semantic_lidar_callback(point_cloud, point_list): |
|||
"""Prepares a point cloud with semantic segmentation |
|||
colors ready to be consumed by Open3D""" |
|||
data = np.frombuffer(point_cloud.raw_data, dtype=np.dtype([ |
|||
('x', np.float32), ('y', np.float32), ('z', np.float32), |
|||
('CosAngle', np.float32), ('ObjIdx', np.uint32), ('ObjTag', np.uint32)])) |
|||
|
|||
# We're negating the y to correclty visualize a world that matches |
|||
# what we see in Unreal since Open3D uses a right-handed coordinate system |
|||
points = np.array([data['x'], -data['y'], data['z']]).T |
|||
|
|||
# # An example of adding some noise to our data if needed: |
|||
# points += np.random.uniform(-0.05, 0.05, size=points.shape) |
|||
|
|||
# Colorize the pointcloud based on the CityScapes color palette |
|||
labels = np.array(data['ObjTag']) |
|||
int_color = LABEL_COLORS[labels] |
|||
|
|||
# # In case you want to make the color intensity depending |
|||
# # of the incident ray angle, you can use: |
|||
# int_color *= np.array(data['CosAngle'])[:, None] |
|||
|
|||
point_list.points = o3d.utility.Vector3dVector(points) |
|||
point_list.colors = o3d.utility.Vector3dVector(int_color) |
|||
|
|||
|
|||
def generate_lidar_bp(arg, world, blueprint_library, delta): |
|||
"""Generates a CARLA blueprint based on the script parameters""" |
|||
if arg.semantic: |
|||
lidar_bp = world.get_blueprint_library().find('sensor.lidar.ray_cast_semantic') |
|||
else: |
|||
lidar_bp = blueprint_library.find('sensor.lidar.ray_cast') |
|||
if arg.no_noise: |
|||
lidar_bp.set_attribute('dropoff_general_rate', '0.0') |
|||
lidar_bp.set_attribute('dropoff_intensity_limit', '1.0') |
|||
lidar_bp.set_attribute('dropoff_zero_intensity', '0.0') |
|||
else: |
|||
lidar_bp.set_attribute('noise_stddev', '0.2') |
|||
|
|||
lidar_bp.set_attribute('upper_fov', str(arg.upper_fov)) |
|||
lidar_bp.set_attribute('lower_fov', str(arg.lower_fov)) |
|||
lidar_bp.set_attribute('channels', str(arg.channels)) |
|||
lidar_bp.set_attribute('range', str(arg.range)) |
|||
lidar_bp.set_attribute('rotation_frequency', str(1.0 / delta)) |
|||
lidar_bp.set_attribute('points_per_second', str(arg.points_per_second)) |
|||
return lidar_bp |
|||
|
|||
|
|||
def add_open3d_axis(vis): |
|||
"""Add a small 3D axis on Open3D Visualizer""" |
|||
axis = o3d.geometry.LineSet() |
|||
axis.points = o3d.utility.Vector3dVector(np.array([ |
|||
[0.0, 0.0, 0.0], |
|||
[1.0, 0.0, 0.0], |
|||
[0.0, 1.0, 0.0], |
|||
[0.0, 0.0, 1.0]])) |
|||
axis.lines = o3d.utility.Vector2iVector(np.array([ |
|||
[0, 1], |
|||
[0, 2], |
|||
[0, 3]])) |
|||
axis.colors = o3d.utility.Vector3dVector(np.array([ |
|||
[1.0, 0.0, 0.0], |
|||
[0.0, 1.0, 0.0], |
|||
[0.0, 0.0, 1.0]])) |
|||
vis.add_geometry(axis) |
|||
|
|||
|
|||
def main(arg): |
|||
"""Main function of the script""" |
|||
client = carla.Client(arg.host, arg.port) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
|
|||
try: |
|||
original_settings = world.get_settings() |
|||
settings = world.get_settings() |
|||
traffic_manager = client.get_trafficmanager(8000) |
|||
traffic_manager.set_synchronous_mode(True) |
|||
|
|||
delta = 0.05 |
|||
|
|||
settings.fixed_delta_seconds = delta |
|||
settings.synchronous_mode = True |
|||
settings.no_rendering_mode = arg.no_rendering |
|||
world.apply_settings(settings) |
|||
|
|||
blueprint_library = world.get_blueprint_library() |
|||
vehicle_bp = blueprint_library.filter(arg.filter)[0] |
|||
vehicle_transform = random.choice(world.get_map().get_spawn_points()) |
|||
vehicle = world.spawn_actor(vehicle_bp, vehicle_transform) |
|||
vehicle.set_autopilot(arg.no_autopilot) |
|||
|
|||
lidar_bp = generate_lidar_bp(arg, world, blueprint_library, delta) |
|||
|
|||
user_offset = carla.Location(arg.x, arg.y, arg.z) |
|||
lidar_transform = carla.Transform(carla.Location(x=-0.5, z=1.8) + user_offset) |
|||
|
|||
lidar = world.spawn_actor(lidar_bp, lidar_transform, attach_to=vehicle) |
|||
|
|||
point_list = o3d.geometry.PointCloud() |
|||
if arg.semantic: |
|||
lidar.listen(lambda data: semantic_lidar_callback(data, point_list)) |
|||
else: |
|||
lidar.listen(lambda data: lidar_callback(data, point_list)) |
|||
|
|||
vis = o3d.visualization.Visualizer() |
|||
vis.create_window( |
|||
window_name='Carla Lidar', |
|||
width=960, |
|||
height=540, |
|||
left=480, |
|||
top=270) |
|||
vis.get_render_option().background_color = [0.05, 0.05, 0.05] |
|||
vis.get_render_option().point_size = 1 |
|||
vis.get_render_option().show_coordinate_frame = True |
|||
|
|||
if arg.show_axis: |
|||
add_open3d_axis(vis) |
|||
|
|||
frame = 0 |
|||
dt0 = datetime.now() |
|||
while True: |
|||
if frame == 2: |
|||
vis.add_geometry(point_list) |
|||
vis.update_geometry(point_list) |
|||
|
|||
vis.poll_events() |
|||
vis.update_renderer() |
|||
# # This can fix Open3D jittering issues: |
|||
time.sleep(0.005) |
|||
world.tick() |
|||
|
|||
process_time = datetime.now() - dt0 |
|||
sys.stdout.write('\r' + 'FPS: ' + str(1.0 / process_time.total_seconds())) |
|||
sys.stdout.flush() |
|||
dt0 = datetime.now() |
|||
frame += 1 |
|||
|
|||
finally: |
|||
world.apply_settings(original_settings) |
|||
traffic_manager.set_synchronous_mode(False) |
|||
|
|||
vehicle.destroy() |
|||
lidar.destroy() |
|||
vis.destroy_window() |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='localhost', |
|||
help='IP of the host CARLA Simulator (default: localhost)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port of CARLA Simulator (default: 2000)') |
|||
argparser.add_argument( |
|||
'--no-rendering', |
|||
action='store_true', |
|||
help='use the no-rendering mode which will provide some extra' |
|||
' performance but you will lose the articulated objects in the' |
|||
' lidar, such as pedestrians') |
|||
argparser.add_argument( |
|||
'--semantic', |
|||
action='store_true', |
|||
help='use the semantic lidar instead, which provides ground truth' |
|||
' information') |
|||
argparser.add_argument( |
|||
'--no-noise', |
|||
action='store_true', |
|||
help='remove the drop off and noise from the normal (non-semantic) lidar') |
|||
argparser.add_argument( |
|||
'--no-autopilot', |
|||
action='store_false', |
|||
help='disables the autopilot so the vehicle will remain stopped') |
|||
argparser.add_argument( |
|||
'--show-axis', |
|||
action='store_true', |
|||
help='show the cartesian coordinates axis') |
|||
argparser.add_argument( |
|||
'--filter', |
|||
metavar='PATTERN', |
|||
default='model3', |
|||
help='actor filter (default: "vehicle.*")') |
|||
argparser.add_argument( |
|||
'--upper-fov', |
|||
default=15.0, |
|||
type=float, |
|||
help='lidar\'s upper field of view in degrees (default: 15.0)') |
|||
argparser.add_argument( |
|||
'--lower-fov', |
|||
default=-25.0, |
|||
type=float, |
|||
help='lidar\'s lower field of view in degrees (default: -25.0)') |
|||
argparser.add_argument( |
|||
'--channels', |
|||
default=64.0, |
|||
type=float, |
|||
help='lidar\'s channel count (default: 64)') |
|||
argparser.add_argument( |
|||
'--range', |
|||
default=100.0, |
|||
type=float, |
|||
help='lidar\'s maximum range in meters (default: 100.0)') |
|||
argparser.add_argument( |
|||
'--points-per-second', |
|||
default=500000, |
|||
type=int, |
|||
help='lidar\'s points per second (default: 500000)') |
|||
argparser.add_argument( |
|||
'-x', |
|||
default=0.0, |
|||
type=float, |
|||
help='offset in the sensor position in the X-axis in meters (default: 0.0)') |
|||
argparser.add_argument( |
|||
'-y', |
|||
default=0.0, |
|||
type=float, |
|||
help='offset in the sensor position in the Y-axis in meters (default: 0.0)') |
|||
argparser.add_argument( |
|||
'-z', |
|||
default=0.0, |
|||
type=float, |
|||
help='offset in the sensor position in the Z-axis in meters (default: 0.0)') |
|||
args = argparser.parse_args() |
|||
|
|||
try: |
|||
main(args) |
|||
except KeyboardInterrupt: |
|||
print(' - Exited by user.') |
|||
@ -0,0 +1,9 @@ |
|||
invertedai@git+https://github.com/inverted-ai/invertedai.git@develop ; python_version >= "3.10" |
|||
future |
|||
numpy<2.0.0 |
|||
networkx |
|||
Shapely |
|||
pygame |
|||
matplotlib |
|||
open3d |
|||
Pillow |
|||
@ -0,0 +1,131 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
Sensor synchronization example for CARLA |
|||
|
|||
The communication model for the syncronous mode in CARLA sends the snapshot |
|||
of the world and the sensors streams in parallel. |
|||
We provide this script as an example of how to syncrononize the sensor |
|||
data gathering in the client. |
|||
To to this, we create a queue that is being filled by every sensor when the |
|||
client receives its data and the main loop is blocked until all the sensors |
|||
have received its data. |
|||
This suppose that all the sensors gather information at every tick. It this is |
|||
not the case, the clients needs to take in account at each frame how many |
|||
sensors are going to tick at each frame. |
|||
""" |
|||
|
|||
from queue import Queue |
|||
from queue import Empty |
|||
|
|||
import carla |
|||
|
|||
|
|||
# Sensor callback. |
|||
# This is where you receive the sensor data and |
|||
# process it as you liked and the important part is that, |
|||
# at the end, it should include an element into the sensor queue. |
|||
def sensor_callback(sensor_data, sensor_queue, sensor_name): |
|||
# Do stuff with the sensor_data data like save it to disk |
|||
# Then you just need to add to the queue |
|||
sensor_queue.put((sensor_data.frame, sensor_name)) |
|||
|
|||
|
|||
def main(): |
|||
# We start creating the client |
|||
client = carla.Client('localhost', 2000) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
|
|||
try: |
|||
# We need to save the settings to be able to recover them at the end |
|||
# of the script to leave the server in the same state that we found it. |
|||
original_settings = world.get_settings() |
|||
settings = world.get_settings() |
|||
|
|||
# We set CARLA syncronous mode |
|||
settings.fixed_delta_seconds = 0.2 |
|||
settings.synchronous_mode = True |
|||
world.apply_settings(settings) |
|||
|
|||
# We create the sensor queue in which we keep track of the information |
|||
# already received. This structure is thread safe and can be |
|||
# accessed by all the sensors callback concurrently without problem. |
|||
sensor_queue = Queue() |
|||
|
|||
# Bluepints for the sensors |
|||
blueprint_library = world.get_blueprint_library() |
|||
cam_bp = blueprint_library.find('sensor.camera.rgb') |
|||
lidar_bp = blueprint_library.find('sensor.lidar.ray_cast') |
|||
radar_bp = blueprint_library.find('sensor.other.radar') |
|||
|
|||
# We create all the sensors and keep them in a list for convenience. |
|||
sensor_list = [] |
|||
|
|||
cam01 = world.spawn_actor(cam_bp, carla.Transform()) |
|||
cam01.listen(lambda data: sensor_callback(data, sensor_queue, "camera01")) |
|||
sensor_list.append(cam01) |
|||
|
|||
cam02 = world.spawn_actor(cam_bp, carla.Transform()) |
|||
cam02.listen(lambda data: sensor_callback(data, sensor_queue, "camera02")) |
|||
sensor_list.append(cam02) |
|||
|
|||
cam03 = world.spawn_actor(cam_bp, carla.Transform()) |
|||
cam03.listen(lambda data: sensor_callback(data, sensor_queue, "camera03")) |
|||
sensor_list.append(cam03) |
|||
|
|||
lidar_bp.set_attribute('points_per_second', '100000') |
|||
lidar01 = world.spawn_actor(lidar_bp, carla.Transform()) |
|||
lidar01.listen(lambda data: sensor_callback(data, sensor_queue, "lidar01")) |
|||
sensor_list.append(lidar01) |
|||
|
|||
lidar_bp.set_attribute('points_per_second', '1000000') |
|||
lidar02 = world.spawn_actor(lidar_bp, carla.Transform()) |
|||
lidar02.listen(lambda data: sensor_callback(data, sensor_queue, "lidar02")) |
|||
sensor_list.append(lidar02) |
|||
|
|||
radar01 = world.spawn_actor(radar_bp, carla.Transform()) |
|||
radar01.listen(lambda data: sensor_callback(data, sensor_queue, "radar01")) |
|||
sensor_list.append(radar01) |
|||
|
|||
radar02 = world.spawn_actor(radar_bp, carla.Transform()) |
|||
radar02.listen(lambda data: sensor_callback(data, sensor_queue, "radar02")) |
|||
sensor_list.append(radar02) |
|||
|
|||
# Main loop |
|||
while True: |
|||
# Tick the server |
|||
world.tick() |
|||
w_frame = world.get_snapshot().frame |
|||
print("\nWorld's frame: %d" % w_frame) |
|||
|
|||
# Now, we wait to the sensors data to be received. |
|||
# As the queue is blocking, we will wait in the queue.get() methods |
|||
# until all the information is processed and we continue with the next frame. |
|||
# We include a timeout of 1.0 s (in the get method) and if some information is |
|||
# not received in this time we continue. |
|||
try: |
|||
for _ in range(len(sensor_list)): |
|||
s_frame = sensor_queue.get(True, 1.0) |
|||
print(" Frame: %d Sensor: %s" % (s_frame[0], s_frame[1])) |
|||
|
|||
except Empty: |
|||
print(" Some of the sensor information is missed") |
|||
|
|||
finally: |
|||
world.apply_settings(original_settings) |
|||
for sensor in sensor_list: |
|||
sensor.destroy() |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
print(' - Exited by user.') |
|||
@ -0,0 +1,66 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
|
|||
|
|||
def main(): |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-f', '--recorder_filename', |
|||
metavar='F', |
|||
default="test1.rec", |
|||
help='recorder filename (test1.rec)') |
|||
argparser.add_argument( |
|||
'-t', '--time', |
|||
metavar='T', |
|||
default="30", |
|||
type=float, |
|||
help='minimum time to consider it is blocked') |
|||
argparser.add_argument( |
|||
'-d', '--distance', |
|||
metavar='D', |
|||
default="100", |
|||
type=float, |
|||
help='minimum distance to consider it is not moving (in cm)') |
|||
args = argparser.parse_args() |
|||
|
|||
try: |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(60.0) |
|||
|
|||
print(client.show_recorder_actors_blocked(args.recorder_filename, args.time, args.distance)) |
|||
|
|||
finally: |
|||
pass |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,64 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
|
|||
|
|||
def main(): |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-f', '--recorder_filename', |
|||
metavar='F', |
|||
default="test1.rec", |
|||
help='recorder filename (test1.rec)') |
|||
argparser.add_argument( |
|||
'-t', '--types', |
|||
metavar='T', |
|||
default="aa", |
|||
help='pair of types (a=any, h=hero, v=vehicle, w=walkers, t=trafficLight, o=others') |
|||
args = argparser.parse_args() |
|||
|
|||
try: |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(60.0) |
|||
|
|||
# types pattern samples: |
|||
# -t aa == any to any == show every collision (the default) |
|||
# -t vv == vehicle to vehicle == show every collision between vehicles only |
|||
# -t vt == vehicle to traffic light == show every collision between a vehicle and a traffic light |
|||
# -t hh == hero to hero == show collision between a hero and another hero |
|||
print(client.show_recorder_collisions(args.recorder_filename, args.types[0], args.types[1])) |
|||
|
|||
finally: |
|||
pass |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,68 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
|
|||
|
|||
def main(): |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-f', '--recorder_filename', |
|||
metavar='F', |
|||
default="test1.rec", |
|||
help='recorder filename (test1.rec)') |
|||
argparser.add_argument( |
|||
'-a', '--show_all', |
|||
action='store_true', |
|||
help='show detailed info about all frames content') |
|||
argparser.add_argument( |
|||
'-s', '--save_to_file', |
|||
metavar='S', |
|||
help='save result to file (specify name and extension)') |
|||
|
|||
args = argparser.parse_args() |
|||
|
|||
try: |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(60.0) |
|||
if args.save_to_file: |
|||
doc = open(args.save_to_file, "w+") |
|||
doc.write(client.show_recorder_file_info(args.recorder_filename, args.show_all)) |
|||
doc.close() |
|||
else: |
|||
print(client.show_recorder_file_info(args.recorder_filename, args.show_all)) |
|||
|
|||
|
|||
finally: |
|||
pass |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,141 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
import random |
|||
import time |
|||
import logging |
|||
from carla.command import SpawnActor, SetAutopilot, FutureActor, DestroyActor |
|||
|
|||
def main(): |
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-n', '--number-of-vehicles', |
|||
metavar='N', |
|||
default=10, |
|||
type=int, |
|||
help='number of vehicles (default: 10)') |
|||
argparser.add_argument( |
|||
'-d', '--delay', |
|||
metavar='D', |
|||
default=2.0, |
|||
type=float, |
|||
help='delay in seconds between spawns (default: 2.0)') |
|||
argparser.add_argument( |
|||
'--safe', |
|||
action='store_true', |
|||
help='avoid spawning vehicles prone to accidents') |
|||
argparser.add_argument( |
|||
'-f', '--recorder_filename', |
|||
metavar='F', |
|||
default="test1.log", |
|||
help='recorder filename (test1.log)') |
|||
argparser.add_argument( |
|||
'-t', '--recorder_time', |
|||
metavar='T', |
|||
default=0, |
|||
type=int, |
|||
help='recorder duration (auto-stop)') |
|||
args = argparser.parse_args() |
|||
|
|||
actor_list = [] |
|||
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) |
|||
|
|||
try: |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
blueprints = world.get_blueprint_library().filter('vehicle.*') |
|||
|
|||
spawn_points = world.get_map().get_spawn_points() |
|||
random.shuffle(spawn_points) |
|||
|
|||
print('found %d spawn points.' % len(spawn_points)) |
|||
|
|||
count = args.number_of_vehicles |
|||
|
|||
print("Recording on file: %s" % client.start_recorder(args.recorder_filename)) |
|||
|
|||
if args.safe: |
|||
blueprints = [x for x in blueprints if int(x.get_attribute('number_of_wheels')) == 4] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('microlino')] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('carlacola')] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('cybertruck')] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('t2')] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('sprinter')] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('firetruck')] |
|||
blueprints = [x for x in blueprints if not x.id.endswith('ambulance')] |
|||
|
|||
spawn_points = world.get_map().get_spawn_points() |
|||
number_of_spawn_points = len(spawn_points) |
|||
|
|||
if count < number_of_spawn_points: |
|||
random.shuffle(spawn_points) |
|||
elif count > number_of_spawn_points: |
|||
msg = 'requested %d vehicles, but could only find %d spawn points' |
|||
logging.warning(msg, count, number_of_spawn_points) |
|||
count = number_of_spawn_points |
|||
|
|||
batch = [] |
|||
for n, transform in enumerate(spawn_points): |
|||
if n >= count: |
|||
break |
|||
blueprint = random.choice(blueprints) |
|||
if blueprint.has_attribute('color'): |
|||
color = random.choice(blueprint.get_attribute('color').recommended_values) |
|||
blueprint.set_attribute('color', color) |
|||
blueprint.set_attribute('role_name', 'autopilot') |
|||
batch.append(SpawnActor(blueprint, transform).then(SetAutopilot(FutureActor, True))) |
|||
|
|||
for response in client.apply_batch_sync(batch): |
|||
if response.error: |
|||
logging.error(response.error) |
|||
else: |
|||
actor_list.append(response.actor_id) |
|||
|
|||
print('spawned %d vehicles, press Ctrl+C to exit.' % len(actor_list)) |
|||
|
|||
if (args.recorder_time > 0): |
|||
time.sleep(args.recorder_time) |
|||
else: |
|||
while True: |
|||
world.wait_for_tick() |
|||
# time.sleep(0.1) |
|||
|
|||
finally: |
|||
|
|||
print('\ndestroying %d actors' % len(actor_list)) |
|||
client.apply_batch_sync([DestroyActor(x) for x in actor_list]) |
|||
|
|||
print("Stop recording") |
|||
client.stop_recorder() |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,109 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import argparse |
|||
|
|||
|
|||
def main(): |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'-s', '--start', |
|||
metavar='S', |
|||
default=0.0, |
|||
type=float, |
|||
help='starting time (default: 0.0)') |
|||
argparser.add_argument( |
|||
'-d', '--duration', |
|||
metavar='D', |
|||
default=0.0, |
|||
type=float, |
|||
help='duration (default: 0.0)') |
|||
argparser.add_argument( |
|||
'-f', '--recorder-filename', |
|||
metavar='F', |
|||
default="test1.log", |
|||
help='recorder filename (test1.log)') |
|||
argparser.add_argument( |
|||
'-c', '--camera', |
|||
metavar='C', |
|||
default=0, |
|||
type=int, |
|||
help='camera follows an actor (ex: 82)') |
|||
argparser.add_argument( |
|||
'-x', '--time-factor', |
|||
metavar='X', |
|||
default=1.0, |
|||
type=float, |
|||
help='time factor (default 1.0)') |
|||
argparser.add_argument( |
|||
'-i', '--ignore-hero', |
|||
action='store_true', |
|||
help='ignore hero vehicles') |
|||
argparser.add_argument( |
|||
'--move-spectator', |
|||
action='store_true', |
|||
help='move spectator camera') |
|||
argparser.add_argument( |
|||
'--top-view', |
|||
action='store_true', |
|||
help='enable top-down camera view when following an actor') |
|||
argparser.add_argument( |
|||
'--spawn-sensors', |
|||
action='store_true', |
|||
help='spawn sensors in the replayed world') |
|||
args = argparser.parse_args() |
|||
|
|||
try: |
|||
|
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(60.0) |
|||
|
|||
# set the time factor for the replayer |
|||
client.set_replayer_time_factor(args.time_factor) |
|||
|
|||
# set to ignore the hero vehicles or not |
|||
client.set_replayer_ignore_hero(args.ignore_hero) |
|||
|
|||
# set to ignore the spectator camera or not |
|||
client.set_replayer_ignore_spectator(not args.move_spectator) |
|||
|
|||
# set desired offset |
|||
offset = carla.Transform(carla.Location(-10, 0, 5), carla.Rotation(-25, 0, 0)) |
|||
if args.top_view: |
|||
offset = carla.Transform(carla.Location(0, 0, 40), carla.Rotation(-90, 0, 0)) |
|||
|
|||
# replay the session |
|||
print(client.replay_file(args.recorder_filename, args.start, args.duration, args.camera, args.spawn_sensors, offset)) |
|||
|
|||
finally: |
|||
pass |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
main() |
|||
except KeyboardInterrupt: |
|||
pass |
|||
finally: |
|||
print('\ndone.') |
|||
@ -0,0 +1,195 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import random |
|||
|
|||
try: |
|||
import pygame |
|||
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') |
|||
|
|||
try: |
|||
import queue |
|||
except ImportError: |
|||
import Queue as queue |
|||
|
|||
|
|||
class CarlaSyncMode(object): |
|||
""" |
|||
Context manager to synchronize output from different sensors. Synchronous |
|||
mode is enabled as long as we are inside this context |
|||
|
|||
with CarlaSyncMode(world, sensors) as sync_mode: |
|||
while True: |
|||
data = sync_mode.tick(timeout=1.0) |
|||
|
|||
""" |
|||
|
|||
def __init__(self, world, *sensors, **kwargs): |
|||
self.world = world |
|||
self.sensors = sensors |
|||
self.frame = None |
|||
self.delta_seconds = 1.0 / kwargs.get('fps', 20) |
|||
self._queues = [] |
|||
self._settings = None |
|||
|
|||
def __enter__(self): |
|||
self._settings = self.world.get_settings() |
|||
self.frame = self.world.apply_settings(carla.WorldSettings( |
|||
no_rendering_mode=False, |
|||
synchronous_mode=True, |
|||
fixed_delta_seconds=self.delta_seconds)) |
|||
|
|||
def make_queue(register_event): |
|||
q = queue.Queue() |
|||
register_event(q.put) |
|||
self._queues.append(q) |
|||
|
|||
make_queue(self.world.on_tick) |
|||
for sensor in self.sensors: |
|||
make_queue(sensor.listen) |
|||
return self |
|||
|
|||
def tick(self, timeout): |
|||
self.frame = self.world.tick() |
|||
data = [self._retrieve_data(q, timeout) for q in self._queues] |
|||
assert all(x.frame == self.frame for x in data) |
|||
return data |
|||
|
|||
def __exit__(self, *args, **kwargs): |
|||
self.world.apply_settings(self._settings) |
|||
|
|||
def _retrieve_data(self, sensor_queue, timeout): |
|||
while True: |
|||
data = sensor_queue.get(timeout=timeout) |
|||
if data.frame == self.frame: |
|||
return data |
|||
|
|||
|
|||
def draw_image(surface, image, blend=False): |
|||
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] |
|||
image_surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) |
|||
if blend: |
|||
image_surface.set_alpha(100) |
|||
surface.blit(image_surface, (0, 0)) |
|||
|
|||
|
|||
def get_font(): |
|||
fonts = [x for x in pygame.font.get_fonts()] |
|||
default_font = 'ubuntumono' |
|||
font = default_font if default_font in fonts else fonts[0] |
|||
font = pygame.font.match_font(font) |
|||
return pygame.font.Font(font, 14) |
|||
|
|||
|
|||
def should_quit(): |
|||
for event in pygame.event.get(): |
|||
if event.type == pygame.QUIT: |
|||
return True |
|||
elif event.type == pygame.KEYUP: |
|||
if event.key == pygame.K_ESCAPE: |
|||
return True |
|||
return False |
|||
|
|||
|
|||
def main(): |
|||
actor_list = [] |
|||
pygame.init() |
|||
|
|||
display = pygame.display.set_mode( |
|||
(800, 600), |
|||
pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
font = get_font() |
|||
clock = pygame.time.Clock() |
|||
|
|||
client = carla.Client('localhost', 2000) |
|||
client.set_timeout(2.0) |
|||
|
|||
world = client.get_world() |
|||
|
|||
try: |
|||
m = world.get_map() |
|||
start_pose = random.choice(m.get_spawn_points()) |
|||
waypoint = m.get_waypoint(start_pose.location) |
|||
|
|||
blueprint_library = world.get_blueprint_library() |
|||
|
|||
vehicle = world.spawn_actor( |
|||
random.choice(blueprint_library.filter('vehicle.*')), |
|||
start_pose) |
|||
actor_list.append(vehicle) |
|||
vehicle.set_simulate_physics(False) |
|||
|
|||
camera_rgb = world.spawn_actor( |
|||
blueprint_library.find('sensor.camera.rgb'), |
|||
carla.Transform(carla.Location(x=-5.5, z=2.8), carla.Rotation(pitch=-15)), |
|||
attach_to=vehicle) |
|||
actor_list.append(camera_rgb) |
|||
|
|||
camera_semseg = world.spawn_actor( |
|||
blueprint_library.find('sensor.camera.semantic_segmentation'), |
|||
carla.Transform(carla.Location(x=-5.5, z=2.8), carla.Rotation(pitch=-15)), |
|||
attach_to=vehicle) |
|||
actor_list.append(camera_semseg) |
|||
|
|||
# Create a synchronous mode context. |
|||
with CarlaSyncMode(world, camera_rgb, camera_semseg, fps=30) as sync_mode: |
|||
while True: |
|||
if should_quit(): |
|||
return |
|||
clock.tick() |
|||
|
|||
# Advance the simulation and wait for the data. |
|||
snapshot, image_rgb, image_semseg = sync_mode.tick(timeout=2.0) |
|||
|
|||
# Choose the next waypoint and update the car location. |
|||
waypoint = random.choice(waypoint.next(1.5)) |
|||
vehicle.set_transform(waypoint.transform) |
|||
|
|||
image_semseg.convert(carla.ColorConverter.CityScapesPalette) |
|||
fps = round(1.0 / snapshot.timestamp.delta_seconds) |
|||
|
|||
# Draw the display. |
|||
draw_image(display, image_rgb) |
|||
draw_image(display, image_semseg, blend=True) |
|||
display.blit( |
|||
font.render('% 5d FPS (real)' % clock.get_fps(), True, (255, 255, 255)), |
|||
(8, 10)) |
|||
display.blit( |
|||
font.render('% 5d FPS (simulated)' % fps, True, (255, 255, 255)), |
|||
(8, 28)) |
|||
pygame.display.flip() |
|||
|
|||
finally: |
|||
|
|||
print('destroying actors.') |
|||
for actor in actor_list: |
|||
actor.destroy() |
|||
|
|||
pygame.quit() |
|||
print('done.') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
try: |
|||
|
|||
main() |
|||
|
|||
except KeyboardInterrupt: |
|||
print('\nCancelled by user. Bye!') |
|||
@ -0,0 +1,83 @@ |
|||
import carla |
|||
import random |
|||
import weakref |
|||
|
|||
def get_actor_blueprints(world, filter, generation): |
|||
bps = world.get_blueprint_library().filter(filter) |
|||
|
|||
if generation.lower() == "all": |
|||
return bps |
|||
|
|||
# If the filter returns only one bp, we assume that this one needed |
|||
# and therefore, we ignore the generation |
|||
if len(bps) == 1: |
|||
return bps |
|||
|
|||
try: |
|||
int_generation = int(generation) |
|||
# Check if generation is in available generations |
|||
if int_generation in [1, 2]: |
|||
bps = [x for x in bps if int(x.get_attribute('generation')) == int_generation] |
|||
return bps |
|||
else: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
except: |
|||
print(" Warning! Actor Generation is not valid. No actor will be spawned.") |
|||
return [] |
|||
|
|||
class V2XSensor(object): |
|||
def __init__(self, parent_actor): |
|||
self.sensor = None |
|||
self._parent = parent_actor |
|||
world = self._parent.get_world() |
|||
#bp = world.get_blueprint_library().find('sensor.other.v2x_custom') |
|||
bp = world.get_blueprint_library().find('sensor.other.v2x') |
|||
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: V2XSensor._V2X_callback(weak_self, sensor_data)) |
|||
|
|||
def destroy(self): |
|||
self.sensor.stop() |
|||
self.sensor.destroy() |
|||
|
|||
@staticmethod |
|||
def _V2X_callback(weak_self, sensor_data): |
|||
self = weak_self() |
|||
if not self: |
|||
return |
|||
for data in sensor_data: |
|||
msg = data.get() |
|||
# stationId = msg["Header"]["Station ID"] |
|||
power = data.power |
|||
print(msg) |
|||
# print('Cam message received from %s ' % stationId) |
|||
print('Cam message received with power %f ' % power) |
|||
|
|||
client = carla.Client("localhost",2000) |
|||
client.set_timeout(2000.0) |
|||
|
|||
world = client.get_world() |
|||
smap = world.get_map() |
|||
# acl = world.get_actor(28) |
|||
# acl.send("test") |
|||
|
|||
|
|||
spawn_points = smap.get_spawn_points() |
|||
spawn_point = random.choice(spawn_points) if spawn_points else carla.Transform() |
|||
blueprint = random.choice(get_actor_blueprints(world, "vehicle.*", "2")) |
|||
blueprint.set_attribute('role_name', "test") |
|||
player = world.try_spawn_actor(blueprint, spawn_point) |
|||
v2x_sensor = V2XSensor(player) |
|||
|
|||
world.wait_for_tick() |
|||
try: |
|||
while True: |
|||
world.wait_for_tick() |
|||
finally: |
|||
v2x_sensor.destroy() |
|||
player.destroy() |
|||
@ -0,0 +1,116 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import random |
|||
import time |
|||
|
|||
|
|||
def main(): |
|||
actor_list = [] |
|||
|
|||
# In this tutorial script, we are going to add a vehicle to the simulation |
|||
# and let it drive in autopilot. We will also create a camera attached to |
|||
# that vehicle, and save all the images generated by the camera to disk. |
|||
|
|||
try: |
|||
# First of all, we need to create the client that will send the requests |
|||
# to the simulator. Here we'll assume the simulator is accepting |
|||
# requests in the localhost at port 2000. |
|||
client = carla.Client('localhost', 2000) |
|||
client.set_timeout(10.0) |
|||
|
|||
# Once we have a client we can retrieve the world that is currently |
|||
# running. |
|||
world = client.get_world() |
|||
|
|||
# The world contains the list blueprints that we can use for adding new |
|||
# actors into the simulation. |
|||
blueprint_library = world.get_blueprint_library() |
|||
|
|||
# Now let's filter all the blueprints of type 'vehicle' and choose one |
|||
# at random. |
|||
bp = random.choice(blueprint_library.filter('vehicle')) |
|||
|
|||
# A blueprint contains the list of attributes that define a vehicle's |
|||
# instance, we can read them and modify some of them. For instance, |
|||
# let's randomize its color. |
|||
if bp.has_attribute('color'): |
|||
color = random.choice(bp.get_attribute('color').recommended_values) |
|||
bp.set_attribute('color', color) |
|||
|
|||
# Now we need to give an initial transform to the vehicle. We choose a |
|||
# random transform from the list of recommended spawn points of the map. |
|||
transform = random.choice(world.get_map().get_spawn_points()) |
|||
|
|||
# So let's tell the world to spawn the vehicle. |
|||
vehicle = world.spawn_actor(bp, transform) |
|||
|
|||
# It is important to note that the actors we create won't be destroyed |
|||
# unless we call their "destroy" function. If we fail to call "destroy" |
|||
# they will stay in the simulation even after we quit the Python script. |
|||
# For that reason, we are storing all the actors we create so we can |
|||
# destroy them afterwards. |
|||
actor_list.append(vehicle) |
|||
print('created %s' % vehicle.type_id) |
|||
|
|||
# Let's put the vehicle to drive around. |
|||
vehicle.set_autopilot(True) |
|||
|
|||
# Let's add now a "depth" camera attached to the vehicle. Note that the |
|||
# transform we give here is now relative to the vehicle. |
|||
camera_bp = blueprint_library.find('sensor.camera.depth') |
|||
camera_transform = carla.Transform(carla.Location(x=1.5, z=2.4)) |
|||
camera = world.spawn_actor(camera_bp, camera_transform, attach_to=vehicle) |
|||
actor_list.append(camera) |
|||
print('created %s' % camera.type_id) |
|||
|
|||
# Now we register the function that will be called each time the sensor |
|||
# receives an image. In this example we are saving the image to disk |
|||
# converting the pixels to gray-scale. |
|||
cc = carla.ColorConverter.LogarithmicDepth |
|||
camera.listen(lambda image: image.save_to_disk('_out/%06d.png' % image.frame, cc)) |
|||
|
|||
# Oh wait, I don't like the location we gave to the vehicle, I'm going |
|||
# to move it a bit forward. |
|||
location = vehicle.get_location() |
|||
location.x += 40 |
|||
vehicle.set_location(location) |
|||
print('moved vehicle to %s' % location) |
|||
|
|||
# But the city now is probably quite empty, let's add a few more |
|||
# vehicles. |
|||
transform.location += carla.Location(x=40, y=-3.2) |
|||
transform.rotation.yaw = -180.0 |
|||
for _ in range(0, 10): |
|||
transform.location.x += 8.0 |
|||
|
|||
bp = random.choice(blueprint_library.filter('vehicle')) |
|||
|
|||
# This time we are using try_spawn_actor. If the spot is already |
|||
# occupied by another object, the function will return None. |
|||
npc = world.try_spawn_actor(bp, transform) |
|||
if npc is not None: |
|||
actor_list.append(npc) |
|||
npc.set_autopilot(True) |
|||
print('created %s' % npc.type_id) |
|||
|
|||
time.sleep(5) |
|||
|
|||
finally: |
|||
|
|||
print('destroying actors') |
|||
camera.destroy() |
|||
client.apply_batch([carla.command.DestroyActor(x) for x in actor_list]) |
|||
print('done.') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
@ -0,0 +1,116 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import random |
|||
import time |
|||
|
|||
|
|||
|
|||
def main(): |
|||
actor_list = [] |
|||
|
|||
# In this tutorial script, we are going to add a vehicle to the simulation |
|||
# and let it drive in autopilot. We will also create a camera attached to |
|||
# that vehicle, and save all the images generated by the camera to disk. |
|||
# Additionally, we will save all of the gbuffer textures for each frame. |
|||
|
|||
try: |
|||
# First of all, we need to create the client that will send the requests |
|||
# to the simulator. Here we'll assume the simulator is accepting |
|||
# requests in the localhost at port 2000. |
|||
client = carla.Client('127.0.0.1', 2000) |
|||
client.set_timeout(2.0) |
|||
|
|||
# Once we have a client we can retrieve the world that is currently |
|||
# running. |
|||
world = client.get_world() |
|||
|
|||
# The world contains the list blueprints that we can use for adding new |
|||
# actors into the simulation. |
|||
blueprint_library = world.get_blueprint_library() |
|||
|
|||
# Now let's filter all the blueprints of type 'vehicle' and choose one |
|||
# at random. |
|||
bp = random.choice(blueprint_library.filter('vehicle')) |
|||
|
|||
# A blueprint contains the list of attributes that define a vehicle's |
|||
# instance, we can read them and modify some of them. For instance, |
|||
# let's randomize its color. |
|||
if bp.has_attribute('color'): |
|||
color = random.choice(bp.get_attribute('color').recommended_values) |
|||
bp.set_attribute('color', color) |
|||
|
|||
# Now we need to give an initial transform to the vehicle. We choose a |
|||
# random transform from the list of recommended spawn points of the map. |
|||
transform = world.get_map().get_spawn_points()[0] |
|||
|
|||
# So let's tell the world to spawn the vehicle. |
|||
vehicle = world.spawn_actor(bp, transform) |
|||
|
|||
# It is important to note that the actors we create won't be destroyed |
|||
# unless we call their "destroy" function. If we fail to call "destroy" |
|||
# they will stay in the simulation even after we quit the Python script. |
|||
# For that reason, we are storing all the actors we create so we can |
|||
# destroy them afterwards. |
|||
actor_list.append(vehicle) |
|||
print('created %s' % vehicle.type_id) |
|||
|
|||
# Let's put the vehicle to drive around. |
|||
vehicle.set_autopilot(True) |
|||
|
|||
# Let's add now a "rgb" camera attached to the vehicle. Note that the |
|||
# transform we give here is now relative to the vehicle. |
|||
camera_bp = blueprint_library.find('sensor.camera.rgb') |
|||
camera_bp.set_attribute('image_size_x', '1920') |
|||
camera_bp.set_attribute('image_size_y', '1080') |
|||
camera_transform = carla.Transform(carla.Location(x=1.5, z=2.4)) |
|||
camera = world.spawn_actor(camera_bp, camera_transform, attach_to=vehicle) |
|||
actor_list.append(camera) |
|||
print('created %s' % camera.type_id) |
|||
|
|||
# Register a callback for whenever a new frame is available. This step is |
|||
# currently required to correctly receive the gbuffer textures, as it is |
|||
# used to determine whether the sensor is active. |
|||
camera.listen(lambda image: image.save_to_disk('_out/FinalColor-%06d.png' % image.frame)) |
|||
|
|||
# Here we will register the callbacks for each gbuffer texture. |
|||
# The function "listen_to_gbuffer" behaves like the regular listen function, |
|||
# but you must first pass it the ID of the desired gbuffer texture. |
|||
camera.enable_gbuffers(True) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.SceneColor, lambda image: image.save_to_disk('_out/GBuffer-SceneColor-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.SceneDepth, lambda image: image.save_to_disk('_out/GBuffer-SceneDepth-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.SceneStencil, lambda image: image.save_to_disk('_out/GBuffer-SceneStencil-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.GBufferA, lambda image: image.save_to_disk('_out/GBuffer-A-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.GBufferB, lambda image: image.save_to_disk('_out/GBuffer-B-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.GBufferC, lambda image: image.save_to_disk('_out/GBuffer-C-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.GBufferD, lambda image: image.save_to_disk('_out/GBuffer-D-%06d.png' % image.frame)) |
|||
# Note that some gbuffer textures may not be available for a particular scene. |
|||
# For example, the textures E and F are likely unavailable in this example, |
|||
# which will result in them being sent as black images. |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.GBufferE, lambda image: image.save_to_disk('_out/GBuffer-E-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.GBufferF, lambda image: image.save_to_disk('_out/GBuffer-F-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.Velocity, lambda image: image.save_to_disk('_out/GBuffer-Velocity-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.SSAO, lambda image: image.save_to_disk('_out/GBuffer-SSAO-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.CustomDepth, lambda image: image.save_to_disk('_out/GBuffer-CustomDepth-%06d.png' % image.frame)) |
|||
camera.listen_to_gbuffer(carla.GBufferTextureID.CustomStencil, lambda image: image.save_to_disk('_out/GBuffer-CustomStencil-%06d.png' % image.frame)) |
|||
|
|||
time.sleep(10) |
|||
|
|||
finally: |
|||
|
|||
print('destroying actors') |
|||
camera.destroy() |
|||
client.apply_batch([carla.command.DestroyActor(x) for x in actor_list]) |
|||
print('done.') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
@ -0,0 +1,51 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
import carla |
|||
|
|||
import math |
|||
import random |
|||
|
|||
|
|||
def get_transform(vehicle_location, angle, d=6.4): |
|||
a = math.radians(angle) |
|||
location = carla.Location(d * math.cos(a), d * math.sin(a), 2.0) + vehicle_location |
|||
return carla.Transform(location, carla.Rotation(yaw=180 + angle, pitch=-15)) |
|||
|
|||
|
|||
def main(): |
|||
client = carla.Client('localhost', 2000) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
spectator = world.get_spectator() |
|||
vehicle_blueprints = world.get_blueprint_library().filter('vehicle') |
|||
|
|||
location = random.choice(world.get_map().get_spawn_points()).location |
|||
|
|||
for blueprint in vehicle_blueprints: |
|||
transform = carla.Transform(location, carla.Rotation(yaw=-45.0)) |
|||
vehicle = world.spawn_actor(blueprint, transform) |
|||
|
|||
try: |
|||
|
|||
print(vehicle.type_id) |
|||
|
|||
angle = 0 |
|||
while angle < 356: |
|||
timestamp = world.wait_for_tick().timestamp |
|||
angle += timestamp.delta_seconds * 60.0 |
|||
spectator.set_transform(get_transform(vehicle.get_location(), angle - 90)) |
|||
|
|||
finally: |
|||
|
|||
vehicle.destroy() |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
@ -0,0 +1,133 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
Vehicle physics example for CARLA |
|||
Small example that shows the effect of different impulse and force application |
|||
methods to a vehicle. |
|||
""" |
|||
|
|||
import argparse |
|||
|
|||
import carla |
|||
|
|||
|
|||
def print_step_info(world, vehicle): |
|||
snapshot = world.get_snapshot() |
|||
print("%d %06.03f %+8.03f %+8.03f %+8.03f %+8.03f %+8.03f %+8.03f %+8.03f %+8.03f %+8.03f" % |
|||
(snapshot.frame, snapshot.timestamp.elapsed_seconds, \ |
|||
vehicle.get_acceleration().x, vehicle.get_acceleration().y, vehicle.get_acceleration().z, \ |
|||
vehicle.get_velocity().x, vehicle.get_velocity().y, vehicle.get_velocity().z, \ |
|||
vehicle.get_location().x, vehicle.get_location().y, vehicle.get_location().z)) |
|||
|
|||
|
|||
def wait(world, frames=100): |
|||
for i in range(0, frames): |
|||
world.tick() |
|||
|
|||
|
|||
def main(arg): |
|||
"""Main function of the script""" |
|||
client = carla.Client(arg.host, arg.port) |
|||
client.set_timeout(2.0) |
|||
world = client.get_world() |
|||
|
|||
try: |
|||
# Setting the world and the spawn properties |
|||
original_settings = world.get_settings() |
|||
settings = world.get_settings() |
|||
|
|||
delta = 0.1 |
|||
settings.fixed_delta_seconds = delta |
|||
settings.synchronous_mode = True |
|||
world.apply_settings(settings) |
|||
|
|||
blueprint_library = world.get_blueprint_library() |
|||
vehicle_bp = blueprint_library.filter(arg.filter)[0] |
|||
|
|||
vehicle_transform = world.get_map().get_spawn_points()[0] |
|||
vehicle_transform.location.z += 3 |
|||
vehicle = world.spawn_actor(vehicle_bp, vehicle_transform) |
|||
|
|||
physics_vehicle = vehicle.get_physics_control() |
|||
car_mass = physics_vehicle.mass |
|||
|
|||
spectator_transform = carla.Transform(vehicle_transform.location, vehicle_transform.rotation) |
|||
spectator_transform.location += vehicle_transform.get_forward_vector() * 20 |
|||
spectator_transform.rotation.yaw += 180 |
|||
spectator = world.get_spectator() |
|||
spectator.set_transform(spectator_transform) |
|||
|
|||
|
|||
# We let the vehicle stabilize and save the transform to reset it after each test. |
|||
wait(world) |
|||
vehicle.set_target_velocity(carla.Vector3D(0, 0, 0)) |
|||
vehicle_transform = vehicle.get_transform() |
|||
wait(world) |
|||
|
|||
|
|||
# Impulse/Force at the center of mass of the object |
|||
impulse = 10 * car_mass |
|||
|
|||
print("# Adding an Impulse of %f N s" % impulse) |
|||
vehicle.add_impulse(carla.Vector3D(0, 0, impulse)) |
|||
|
|||
wait(world) |
|||
vehicle.set_transform(vehicle_transform) |
|||
vehicle.set_target_velocity(carla.Vector3D(0, 0, 0)) |
|||
wait(world) |
|||
|
|||
print("# Adding a Force of %f N" % (impulse / delta)) |
|||
# The add_force method should not be use for instantaneous forces like this one, |
|||
# it is more useful for constant or variable forces acting in a finite amount of time. |
|||
# In this script it is done with the proper scaling to show the equivalence |
|||
# between the add_impulse and add_force methods. |
|||
# As in this case the force is going to be applied during the whole step dt=delta |
|||
# a force more or less equivalent is impulse / delta. |
|||
vehicle.add_force(carla.Vector3D(0, 0, impulse / delta)) |
|||
|
|||
wait(world) |
|||
vehicle.set_transform(vehicle_transform) |
|||
vehicle.set_target_velocity(carla.Vector3D(0, 0, 0)) |
|||
wait(world) |
|||
|
|||
wait(world, 500) |
|||
|
|||
|
|||
finally: |
|||
world.apply_settings(original_settings) |
|||
|
|||
vehicle.destroy() |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
|
|||
argparser = argparse.ArgumentParser( |
|||
description=__doc__) |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='localhost', |
|||
help='IP of the host CARLA Simulator (default: localhost)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port of CARLA Simulator (default: 2000)') |
|||
argparser.add_argument( |
|||
'--filter', |
|||
metavar='PATTERN', |
|||
default='model3', |
|||
help='actor filter (default: "vehicle.*")') |
|||
args = argparser.parse_args() |
|||
|
|||
try: |
|||
main(args) |
|||
except KeyboardInterrupt: |
|||
print(' - Exited by user.') |
|||
@ -0,0 +1,375 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de |
|||
# Barcelona (UAB). |
|||
# |
|||
# This work is licensed under the terms of the MIT license. |
|||
# For a copy, see <https://opensource.org/licenses/MIT>. |
|||
|
|||
""" |
|||
Script that render multiple sensors in the same pygame window |
|||
|
|||
By default, it renders four cameras, one LiDAR and one Semantic LiDAR. |
|||
It can easily be configure for any different number of sensors. |
|||
To do that, check lines 290-308. |
|||
""" |
|||
|
|||
import carla |
|||
import argparse |
|||
import random |
|||
import time |
|||
import numpy as np |
|||
|
|||
|
|||
try: |
|||
import pygame |
|||
from pygame.locals import K_ESCAPE |
|||
from pygame.locals import K_q |
|||
except ImportError: |
|||
raise RuntimeError('cannot import pygame, make sure pygame package is installed') |
|||
|
|||
class CustomTimer: |
|||
def __init__(self): |
|||
try: |
|||
self.timer = time.perf_counter |
|||
except AttributeError: |
|||
self.timer = time.time |
|||
|
|||
def time(self): |
|||
return self.timer() |
|||
|
|||
class DisplayManager: |
|||
def __init__(self, grid_size, window_size): |
|||
pygame.init() |
|||
pygame.font.init() |
|||
self.display = pygame.display.set_mode(window_size, pygame.HWSURFACE | pygame.DOUBLEBUF) |
|||
|
|||
self.grid_size = grid_size |
|||
self.window_size = window_size |
|||
self.sensor_list = [] |
|||
|
|||
def get_window_size(self): |
|||
return [int(self.window_size[0]), int(self.window_size[1])] |
|||
|
|||
def get_display_size(self): |
|||
return [int(self.window_size[0]/self.grid_size[1]), int(self.window_size[1]/self.grid_size[0])] |
|||
|
|||
def get_display_offset(self, gridPos): |
|||
dis_size = self.get_display_size() |
|||
return [int(gridPos[1] * dis_size[0]), int(gridPos[0] * dis_size[1])] |
|||
|
|||
def add_sensor(self, sensor): |
|||
self.sensor_list.append(sensor) |
|||
|
|||
def get_sensor_list(self): |
|||
return self.sensor_list |
|||
|
|||
def render(self): |
|||
if not self.render_enabled(): |
|||
return |
|||
|
|||
for s in self.sensor_list: |
|||
s.render() |
|||
|
|||
pygame.display.flip() |
|||
|
|||
def destroy(self): |
|||
for s in self.sensor_list: |
|||
s.destroy() |
|||
|
|||
def render_enabled(self): |
|||
return self.display != None |
|||
|
|||
class SensorManager: |
|||
def __init__(self, world, display_man, sensor_type, transform, attached, sensor_options, display_pos): |
|||
self.surface = None |
|||
self.world = world |
|||
self.display_man = display_man |
|||
self.display_pos = display_pos |
|||
self.sensor = self.init_sensor(sensor_type, transform, attached, sensor_options) |
|||
self.sensor_options = sensor_options |
|||
self.timer = CustomTimer() |
|||
|
|||
self.time_processing = 0.0 |
|||
self.tics_processing = 0 |
|||
|
|||
self.display_man.add_sensor(self) |
|||
|
|||
def init_sensor(self, sensor_type, transform, attached, sensor_options): |
|||
if sensor_type == 'RGBCamera': |
|||
camera_bp = self.world.get_blueprint_library().find('sensor.camera.rgb') |
|||
disp_size = self.display_man.get_display_size() |
|||
camera_bp.set_attribute('image_size_x', str(disp_size[0])) |
|||
camera_bp.set_attribute('image_size_y', str(disp_size[1])) |
|||
|
|||
for key in sensor_options: |
|||
camera_bp.set_attribute(key, sensor_options[key]) |
|||
|
|||
camera = self.world.spawn_actor(camera_bp, transform, attach_to=attached) |
|||
camera.listen(self.save_rgb_image) |
|||
|
|||
return camera |
|||
|
|||
elif sensor_type == 'LiDAR': |
|||
lidar_bp = self.world.get_blueprint_library().find('sensor.lidar.ray_cast') |
|||
lidar_bp.set_attribute('range', '100') |
|||
lidar_bp.set_attribute('dropoff_general_rate', lidar_bp.get_attribute('dropoff_general_rate').recommended_values[0]) |
|||
lidar_bp.set_attribute('dropoff_intensity_limit', lidar_bp.get_attribute('dropoff_intensity_limit').recommended_values[0]) |
|||
lidar_bp.set_attribute('dropoff_zero_intensity', lidar_bp.get_attribute('dropoff_zero_intensity').recommended_values[0]) |
|||
|
|||
for key in sensor_options: |
|||
lidar_bp.set_attribute(key, sensor_options[key]) |
|||
|
|||
lidar = self.world.spawn_actor(lidar_bp, transform, attach_to=attached) |
|||
|
|||
lidar.listen(self.save_lidar_image) |
|||
|
|||
return lidar |
|||
|
|||
elif sensor_type == 'SemanticLiDAR': |
|||
lidar_bp = self.world.get_blueprint_library().find('sensor.lidar.ray_cast_semantic') |
|||
lidar_bp.set_attribute('range', '100') |
|||
|
|||
for key in sensor_options: |
|||
lidar_bp.set_attribute(key, sensor_options[key]) |
|||
|
|||
lidar = self.world.spawn_actor(lidar_bp, transform, attach_to=attached) |
|||
|
|||
lidar.listen(self.save_semanticlidar_image) |
|||
|
|||
return lidar |
|||
|
|||
elif sensor_type == "Radar": |
|||
radar_bp = self.world.get_blueprint_library().find('sensor.other.radar') |
|||
for key in sensor_options: |
|||
radar_bp.set_attribute(key, sensor_options[key]) |
|||
|
|||
radar = self.world.spawn_actor(radar_bp, transform, attach_to=attached) |
|||
radar.listen(self.save_radar_image) |
|||
|
|||
return radar |
|||
|
|||
else: |
|||
return None |
|||
|
|||
def get_sensor(self): |
|||
return self.sensor |
|||
|
|||
def save_rgb_image(self, image): |
|||
t_start = self.timer.time() |
|||
|
|||
image.convert(carla.ColorConverter.Raw) |
|||
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] |
|||
|
|||
if self.display_man.render_enabled(): |
|||
self.surface = pygame.surfarray.make_surface(array.swapaxes(0, 1)) |
|||
|
|||
t_end = self.timer.time() |
|||
self.time_processing += (t_end-t_start) |
|||
self.tics_processing += 1 |
|||
|
|||
def save_lidar_image(self, image): |
|||
t_start = self.timer.time() |
|||
|
|||
disp_size = self.display_man.get_display_size() |
|||
lidar_range = 2.0*float(self.sensor_options['range']) |
|||
|
|||
points = np.frombuffer(image.raw_data, dtype=np.dtype('f4')) |
|||
points = np.reshape(points, (int(points.shape[0] / 4), 4)) |
|||
lidar_data = np.array(points[:, :2]) |
|||
lidar_data *= min(disp_size) / lidar_range |
|||
lidar_data += (0.5 * disp_size[0], 0.5 * disp_size[1]) |
|||
lidar_data = np.fabs(lidar_data) # pylint: disable=E1111 |
|||
lidar_data = lidar_data.astype(np.int32) |
|||
lidar_data = np.reshape(lidar_data, (-1, 2)) |
|||
lidar_img_size = (disp_size[0], disp_size[1], 3) |
|||
lidar_img = np.zeros((lidar_img_size), dtype=np.uint8) |
|||
|
|||
lidar_img[tuple(lidar_data.T)] = (255, 255, 255) |
|||
|
|||
if self.display_man.render_enabled(): |
|||
self.surface = pygame.surfarray.make_surface(lidar_img) |
|||
|
|||
t_end = self.timer.time() |
|||
self.time_processing += (t_end-t_start) |
|||
self.tics_processing += 1 |
|||
|
|||
def save_semanticlidar_image(self, image): |
|||
t_start = self.timer.time() |
|||
|
|||
disp_size = self.display_man.get_display_size() |
|||
lidar_range = 2.0*float(self.sensor_options['range']) |
|||
|
|||
points = np.frombuffer(image.raw_data, dtype=np.dtype('f4')) |
|||
points = np.reshape(points, (int(points.shape[0] / 6), 6)) |
|||
lidar_data = np.array(points[:, :2]) |
|||
lidar_data *= min(disp_size) / lidar_range |
|||
lidar_data += (0.5 * disp_size[0], 0.5 * disp_size[1]) |
|||
lidar_data = np.fabs(lidar_data) # pylint: disable=E1111 |
|||
lidar_data = lidar_data.astype(np.int32) |
|||
lidar_data = np.reshape(lidar_data, (-1, 2)) |
|||
lidar_img_size = (disp_size[0], disp_size[1], 3) |
|||
lidar_img = np.zeros((lidar_img_size), dtype=np.uint8) |
|||
|
|||
lidar_img[tuple(lidar_data.T)] = (255, 255, 255) |
|||
|
|||
if self.display_man.render_enabled(): |
|||
self.surface = pygame.surfarray.make_surface(lidar_img) |
|||
|
|||
t_end = self.timer.time() |
|||
self.time_processing += (t_end-t_start) |
|||
self.tics_processing += 1 |
|||
|
|||
def save_radar_image(self, radar_data): |
|||
t_start = self.timer.time() |
|||
points = np.frombuffer(radar_data.raw_data, dtype=np.dtype('f4')) |
|||
points = np.reshape(points, (len(radar_data), 4)) |
|||
|
|||
t_end = self.timer.time() |
|||
self.time_processing += (t_end-t_start) |
|||
self.tics_processing += 1 |
|||
|
|||
def render(self): |
|||
if self.surface is not None: |
|||
offset = self.display_man.get_display_offset(self.display_pos) |
|||
self.display_man.display.blit(self.surface, offset) |
|||
|
|||
def destroy(self): |
|||
self.sensor.destroy() |
|||
|
|||
def run_simulation(args, client): |
|||
"""This function performed one test run using the args parameters |
|||
and connecting to the carla client passed. |
|||
""" |
|||
|
|||
display_manager = None |
|||
vehicle = None |
|||
vehicle_list = [] |
|||
timer = CustomTimer() |
|||
|
|||
try: |
|||
|
|||
# Getting the world and |
|||
world = client.get_world() |
|||
original_settings = world.get_settings() |
|||
|
|||
if args.sync: |
|||
traffic_manager = client.get_trafficmanager(8000) |
|||
settings = world.get_settings() |
|||
traffic_manager.set_synchronous_mode(True) |
|||
settings.synchronous_mode = True |
|||
settings.fixed_delta_seconds = 0.05 |
|||
world.apply_settings(settings) |
|||
|
|||
|
|||
# Instanciating the vehicle to which we attached the sensors |
|||
bp = world.get_blueprint_library().filter('charger_2020')[0] |
|||
vehicle = world.spawn_actor(bp, random.choice(world.get_map().get_spawn_points())) |
|||
vehicle_list.append(vehicle) |
|||
vehicle.set_autopilot(True) |
|||
|
|||
# Display Manager organize all the sensors an its display in a window |
|||
# If can easily configure the grid and the total window size |
|||
display_manager = DisplayManager(grid_size=[2, 3], window_size=[args.width, args.height]) |
|||
|
|||
# Then, SensorManager can be used to spawn RGBCamera, LiDARs and SemanticLiDARs as needed |
|||
# and assign each of them to a grid position, |
|||
SensorManager(world, display_manager, 'RGBCamera', carla.Transform(carla.Location(x=0, z=2.4), carla.Rotation(yaw=-90)), |
|||
vehicle, {}, display_pos=[0, 0]) |
|||
SensorManager(world, display_manager, 'RGBCamera', carla.Transform(carla.Location(x=0, z=2.4), carla.Rotation(yaw=+00)), |
|||
vehicle, {}, display_pos=[0, 1]) |
|||
SensorManager(world, display_manager, 'RGBCamera', carla.Transform(carla.Location(x=0, z=2.4), carla.Rotation(yaw=+90)), |
|||
vehicle, {}, display_pos=[0, 2]) |
|||
SensorManager(world, display_manager, 'RGBCamera', carla.Transform(carla.Location(x=0, z=2.4), carla.Rotation(yaw=180)), |
|||
vehicle, {}, display_pos=[1, 1]) |
|||
|
|||
SensorManager(world, display_manager, 'LiDAR', carla.Transform(carla.Location(x=0, z=2.4)), |
|||
vehicle, {'channels' : '64', 'range' : '100', 'points_per_second': '250000', 'rotation_frequency': '20'}, display_pos=[1, 0]) |
|||
SensorManager(world, display_manager, 'SemanticLiDAR', carla.Transform(carla.Location(x=0, z=2.4)), |
|||
vehicle, {'channels' : '64', 'range' : '100', 'points_per_second': '100000', 'rotation_frequency': '20'}, display_pos=[1, 2]) |
|||
|
|||
|
|||
#Simulation loop |
|||
call_exit = False |
|||
time_init_sim = timer.time() |
|||
while True: |
|||
# Carla Tick |
|||
if args.sync: |
|||
world.tick() |
|||
else: |
|||
world.wait_for_tick() |
|||
|
|||
# Render received data |
|||
display_manager.render() |
|||
|
|||
for event in pygame.event.get(): |
|||
if event.type == pygame.QUIT: |
|||
call_exit = True |
|||
elif event.type == pygame.KEYDOWN: |
|||
if event.key == K_ESCAPE or event.key == K_q: |
|||
call_exit = True |
|||
break |
|||
|
|||
if call_exit: |
|||
break |
|||
|
|||
finally: |
|||
if display_manager: |
|||
display_manager.destroy() |
|||
|
|||
client.apply_batch([carla.command.DestroyActor(x) for x in vehicle_list]) |
|||
|
|||
world.apply_settings(original_settings) |
|||
|
|||
|
|||
|
|||
def main(): |
|||
argparser = argparse.ArgumentParser( |
|||
description='CARLA Sensor tutorial') |
|||
argparser.add_argument( |
|||
'--host', |
|||
metavar='H', |
|||
default='127.0.0.1', |
|||
help='IP of the host server (default: 127.0.0.1)') |
|||
argparser.add_argument( |
|||
'-p', '--port', |
|||
metavar='P', |
|||
default=2000, |
|||
type=int, |
|||
help='TCP port to listen to (default: 2000)') |
|||
argparser.add_argument( |
|||
'--sync', |
|||
action='store_true', |
|||
help='Synchronous mode execution') |
|||
argparser.add_argument( |
|||
'--async', |
|||
dest='sync', |
|||
action='store_false', |
|||
help='Asynchronous mode execution') |
|||
argparser.set_defaults(sync=True) |
|||
argparser.add_argument( |
|||
'--res', |
|||
metavar='WIDTHxHEIGHT', |
|||
default='1280x720', |
|||
help='window resolution (default: 1280x720)') |
|||
|
|||
args = argparser.parse_args() |
|||
|
|||
args.width, args.height = [int(x) for x in args.res.split('x')] |
|||
|
|||
try: |
|||
client = carla.Client(args.host, args.port) |
|||
client.set_timeout(5.0) |
|||
|
|||
run_simulation(args, client) |
|||
|
|||
except KeyboardInterrupt: |
|||
print('\nCancelled by user. Bye!') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
|
|||
main() |
|||
@ -0,0 +1,472 @@ |
|||
# Shenron-Style Radar Integration Plan (CARLA → Foxglove Pipeline) |
|||
|
|||
## Objective |
|||
|
|||
Implement a **C-Shenron-inspired radar synthesis module** within the existing CARLA ADAS pipeline to generate a **high-fidelity radar point cloud and heatmap**, while **retaining CARLA’s native radar** for benchmarking and calibration. |
|||
|
|||
--- |
|||
|
|||
# 1. Why Shenron Radar (Core Improvements Over CARLA Radar) |
|||
|
|||
## Limitations of CARLA Radar |
|||
|
|||
* Sparse ray-cast sampling (low spatial density) |
|||
* No Radar Cross Section (RCS) modeling |
|||
* No intensity / energy modeling |
|||
* No angular resolution control |
|||
* No beam pattern or antenna simulation |
|||
* No realistic clustering or spread |
|||
* Outputs discrete points → not field-based representation |
|||
|
|||
--- |
|||
|
|||
## Shenron Key Ideas (What Makes It Better) |
|||
|
|||
These are the **core principles you must implement**: |
|||
|
|||
### 1. Dense Geometry-Based Sampling |
|||
|
|||
* Uses **LiDAR / depth data** instead of sparse rays |
|||
* Captures full object surfaces → better shape fidelity |
|||
|
|||
--- |
|||
|
|||
### 2. Radar Cross Section (RCS) Approximation |
|||
|
|||
* Assign reflectivity based on object type |
|||
* Vehicles ≫ pedestrians ≫ static clutter |
|||
|
|||
--- |
|||
|
|||
### 3. Energy-Based Modeling (Not Binary Hits) |
|||
|
|||
* Each point contributes **intensity** |
|||
* Follows simplified radar equation: |
|||
|
|||
``` |
|||
I ∝ σ / R⁴ |
|||
``` |
|||
|
|||
--- |
|||
|
|||
### 4. Range-Azimuth Grid Representation |
|||
|
|||
* Converts scene into **radar image space** |
|||
* Aggregates energy into bins instead of discrete hits |
|||
|
|||
--- |
|||
|
|||
### 5. Angular Resolution Simulation |
|||
|
|||
* Models antenna array limitations: |
|||
|
|||
``` |
|||
Δθ ≈ 2 / N_antennas |
|||
``` |
|||
|
|||
* Applies angular blur → realistic radar spread |
|||
|
|||
--- |
|||
|
|||
### 6. Doppler Field (Not Per-Ray Velocity) |
|||
|
|||
* Computes **radial velocity per cluster/bin** |
|||
* Produces realistic motion patterns |
|||
|
|||
--- |
|||
|
|||
### 7. Multi-Sensor Fusion Capability |
|||
|
|||
* Supports multiple radar views (F, B, L, R) |
|||
* Improves situational awareness |
|||
|
|||
--- |
|||
|
|||
### 8. Field Representation Instead of Hit Detection |
|||
|
|||
* CARLA: “Ray hit object” |
|||
* Shenron: “Energy returned from direction” |
|||
|
|||
--- |
|||
|
|||
# 2. Integration Strategy (Your Pipeline) |
|||
|
|||
## Current Pipeline |
|||
|
|||
``` |
|||
CARLA → SensorManager → Recorder → Dataset → MCAP → Foxglove |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## Updated Pipeline |
|||
|
|||
``` |
|||
CARLA |
|||
↓ |
|||
SensorManager (Camera + LiDAR + Native Radar) |
|||
↓ |
|||
RadarSynthesizer (NEW) |
|||
↓ |
|||
Recorder |
|||
├── /radar_carla (existing) |
|||
├── /radar_shenron (NEW) |
|||
↓ |
|||
MCAP → Foxglove |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## Key Design Decision |
|||
|
|||
* DO NOT remove CARLA radar |
|||
* Add **parallel synthetic radar** |
|||
* Enables: |
|||
|
|||
* A/B comparison |
|||
* Parameter tuning |
|||
* Real-world calibration |
|||
|
|||
--- |
|||
|
|||
# 3. Implementation Plan (Step-by-Step) |
|||
|
|||
--- |
|||
|
|||
## STEP 1 — Create RadarSynthesizer Module |
|||
|
|||
**File:** |
|||
|
|||
``` |
|||
src/radar_synthesizer.py |
|||
``` |
|||
|
|||
**Interface:** |
|||
|
|||
```python |
|||
class RadarSynthesizer: |
|||
def __init__(self, config): |
|||
pass |
|||
|
|||
def generate(self, lidar_points, ego_state, gt_objects): |
|||
return radar_points |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## STEP 2 — Input Data Sources |
|||
|
|||
From your pipeline: |
|||
|
|||
* LiDAR → `[N, 4] (x, y, z, intensity)` |
|||
* Ground Truth → object labels, velocity, bounding boxes |
|||
* Ego state → pose + velocity |
|||
|
|||
--- |
|||
|
|||
## STEP 3 — Coordinate Transformation |
|||
|
|||
Convert LiDAR points to radar space: |
|||
|
|||
``` |
|||
R = sqrt(x² + y² + z²) |
|||
θ = atan2(y, x) |
|||
φ = asin(z / R) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## STEP 4 — Object Association |
|||
|
|||
For each LiDAR point: |
|||
|
|||
* Assign it to nearest bounding box |
|||
* Extract: |
|||
|
|||
* object class |
|||
* velocity |
|||
|
|||
Output: |
|||
|
|||
``` |
|||
point → {x, y, z, object_type, velocity} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## STEP 5 — RCS Modeling |
|||
|
|||
Define: |
|||
|
|||
```python |
|||
RCS_MAP = { |
|||
"vehicle": 10.0, |
|||
"walker": 1.0, |
|||
"static": 0.3 |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## STEP 6 — Intensity Calculation |
|||
|
|||
Apply simplified radar equation: |
|||
|
|||
``` |
|||
I = σ / R⁴ |
|||
``` |
|||
|
|||
Optional extensions: |
|||
|
|||
* Angle attenuation |
|||
* Surface normal weighting |
|||
|
|||
--- |
|||
|
|||
## STEP 7 — Range-Azimuth Grid Formation |
|||
|
|||
Discretize: |
|||
|
|||
```python |
|||
range_bins = 256 |
|||
angle_bins = 128 |
|||
``` |
|||
|
|||
Accumulate: |
|||
|
|||
```python |
|||
grid[r_bin][theta_bin] += intensity |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## STEP 8 — Angular Resolution Simulation |
|||
|
|||
Define antenna count: |
|||
|
|||
```python |
|||
N_antennas = 64 |
|||
``` |
|||
|
|||
Apply blur: |
|||
|
|||
```python |
|||
sigma_theta = k / N_antennas |
|||
``` |
|||
|
|||
Use Gaussian smoothing across angle axis. |
|||
|
|||
--- |
|||
|
|||
## STEP 9 — Doppler Modeling |
|||
|
|||
Compute radial velocity: |
|||
|
|||
``` |
|||
v_r = v_rel · r_hat |
|||
``` |
|||
|
|||
Assign: |
|||
|
|||
* Per grid cell |
|||
* Or per cluster |
|||
|
|||
--- |
|||
|
|||
## STEP 10 — Thresholding & Point Cloud Generation |
|||
|
|||
Convert grid back: |
|||
|
|||
```python |
|||
if grid[r][θ] > threshold: |
|||
x = r * cos(θ) |
|||
y = r * sin(θ) |
|||
z = 0 |
|||
``` |
|||
|
|||
Attach velocity. |
|||
|
|||
--- |
|||
|
|||
## STEP 11 — Recorder Integration |
|||
|
|||
Modify `recorder.py`: |
|||
|
|||
Add new topic: |
|||
|
|||
``` |
|||
/radar_shenron |
|||
``` |
|||
|
|||
Format: |
|||
|
|||
``` |
|||
[N, 4] → x, y, z, velocity |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## STEP 12 — MCAP Integration |
|||
|
|||
Update `data_to_mcap.py`: |
|||
|
|||
Add: |
|||
|
|||
| Topic | Schema | |
|||
| ---------------- | ---------- | |
|||
| `/radar_shenron` | PointCloud | |
|||
|
|||
--- |
|||
|
|||
## STEP 13 — Foxglove Visualization |
|||
|
|||
Display: |
|||
|
|||
* `/radar_carla` |
|||
* `/radar_shenron` |
|||
|
|||
Side-by-side comparison: |
|||
|
|||
* Density |
|||
* Spread |
|||
* Stability |
|||
|
|||
--- |
|||
|
|||
# 4. Development Phases |
|||
|
|||
--- |
|||
|
|||
## Phase 1 — Offline Prototype |
|||
|
|||
* Use saved LiDAR logs |
|||
* Generate radar output |
|||
* Validate visually |
|||
|
|||
--- |
|||
|
|||
## Phase 2 — Online Integration |
|||
|
|||
* Plug into SensorManager |
|||
* Generate per frame |
|||
|
|||
--- |
|||
|
|||
## Phase 3 — Calibration Mode |
|||
|
|||
* Compare: |
|||
|
|||
* CARLA radar |
|||
* Shenron radar |
|||
* Tune: |
|||
|
|||
* RCS values |
|||
* noise |
|||
* thresholds |
|||
|
|||
--- |
|||
|
|||
## Phase 4 — Real-World Matching (Future) |
|||
|
|||
* Match with real radar logs |
|||
* Fit: |
|||
|
|||
* noise distribution |
|||
* angular spread |
|||
* detection density |
|||
|
|||
--- |
|||
|
|||
# 5. Performance Considerations |
|||
|
|||
* Use NumPy vectorization |
|||
* Avoid Python loops |
|||
* Keep grid size moderate |
|||
* Consider async processing if needed |
|||
|
|||
--- |
|||
|
|||
# 6. Optional Enhancements |
|||
|
|||
--- |
|||
|
|||
## 6.1 Noise Model |
|||
|
|||
``` |
|||
range_noise ~ N(0, σ_r) |
|||
velocity_noise ~ N(0, σ_v) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 6.2 Clutter Simulation |
|||
|
|||
* Ground reflections |
|||
* Random scatter |
|||
|
|||
--- |
|||
|
|||
## 6.3 Occlusion Modeling |
|||
|
|||
* Use LiDAR density |
|||
* Drop points behind objects |
|||
|
|||
--- |
|||
|
|||
## 6.4 Temporal Persistence |
|||
|
|||
* Retain previous detections |
|||
* Apply decay |
|||
|
|||
--- |
|||
|
|||
# 7. Key Engineering Insight |
|||
|
|||
> The biggest shift is: |
|||
|
|||
**From:** |
|||
|
|||
* Discrete ray-hit detection |
|||
|
|||
**To:** |
|||
|
|||
* Continuous energy field representation |
|||
|
|||
--- |
|||
|
|||
# 8. Expected Outcomes |
|||
|
|||
Compared to CARLA radar: |
|||
|
|||
| Feature | CARLA | Shenron-style | |
|||
| --------------- | ------- | ------------- | |
|||
| Density | Low | High | |
|||
| Clustering | Poor | Realistic | |
|||
| Angular spread | None | Modeled | |
|||
| Physics realism | Low | Medium | |
|||
| ML usefulness | Limited | High | |
|||
|
|||
--- |
|||
|
|||
# 9. Final Recommendation |
|||
|
|||
Start with a **minimal MVP**: |
|||
|
|||
1. LiDAR → spherical |
|||
2. Add RCS scaling |
|||
3. Bin into grid |
|||
4. Convert to point cloud |
|||
|
|||
Then iterate. |
|||
|
|||
--- |
|||
|
|||
# 10. Next Step |
|||
|
|||
* Implement `radar_synthesizer.py` |
|||
* Validate offline |
|||
* Integrate into pipeline |
|||
|
|||
--- |
|||
|
|||
**End of Document** |
|||
@ -0,0 +1,123 @@ |
|||
# Shenron Radar Integration Guide (Fox Pipeline) |
|||
|
|||
This guide details the "Model-Based Development" (MBD) integration of the physics-based Shenron radar simulation into the Fox CARLA ADAS pipeline. |
|||
|
|||
--- |
|||
|
|||
## 1. Architecture Overview (MBD Standard) |
|||
|
|||
In this approach, the Shenron simulator is treated as a **strictly isolated model block** (black box). Your main pipeline (recording/visualization) never touches the internal math or physics files. Instead, it interacts with a single **Shenron Model API**. |
|||
|
|||
**The MBD Interface:** |
|||
- **Model Location**: `ISOLATE/` |
|||
- **Public API**: `ShenronRadarModel` (class) |
|||
- **Inputs**: Canonical Semantic LiDAR array `[N, 7]`. |
|||
- **Outputs**: Radar Point Cloud `[N, 4]`. |
|||
|
|||
--- |
|||
|
|||
## 2. File System Setup (MBD Style) |
|||
|
|||
## 2. File System Setup |
|||
|
|||
To isolate the logic, we use the `ISOLATE/` directory. Copy this entire folder into the root of your `Fox/` repository. |
|||
|
|||
**Required Directory Structure:** |
|||
```text |
|||
Fox/ |
|||
├── ISOLATE/ <-- The Shenron Core |
|||
│ ├── e2e_agent_sem_lidar2shenron_package/ |
|||
│ │ ├── shenron/ (Sceneset.py, heatmap_gen_fast.py) |
|||
│ │ ├── ConfigureRadar.py (Hardware settings) |
|||
│ │ └── lidar.py (Main conversion logic) |
|||
│ └── sim_radar_utils/ (FFT and BEV Processing) |
|||
├── src/ |
|||
│ └── sensors.py (Update to Semantic LiDAR) |
|||
├── scripts/ |
|||
│ ├── generate_shenron.py <-- NEW: Batch processor |
|||
│ └── data_to_mcap.py (Update to include Shenron topic) |
|||
└── data/ (Target dataset) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 3. Step-by-Step Implementation |
|||
|
|||
### Step 1: Update LiDAR Sensor |
|||
In `src/sensors.py`, you must change the LiDAR blueprint to the **semantic** version. Shenron requires semantic tags (Road, Vehicle, Building) to apply radar reflection coefficients via Fresnel equations. |
|||
|
|||
**File:** [`src/sensors.py`](file:///d:/Work/Repo/C-Shenron/src/sensors.py) |
|||
```python |
|||
# Change this: |
|||
# 'type': 'sensor.lidar.ray_cast' |
|||
# To this: |
|||
'type': 'sensor.lidar.ray_cast_semantic' |
|||
``` |
|||
|
|||
### Step 2: Implement the Batch Processor |
|||
Create `scripts/generate_shenron.py`. This script acts as a wrapper for the `ISOLATE` package. It iterates through your `data/` directory, looks for `lidar/` folders, and generates a corresponding `shenron_radar/` folder. |
|||
|
|||
> [!TIP] |
|||
> Use the **`heatmap_gen_fast.py`** logic within the processor to leverage CUDA/GPU acceleration for the FMCW signal generation. |
|||
|
|||
### Step 3: Update the MCAP Writer |
|||
Modify `scripts/data_to_mcap.py` to recognize the new `shenron_radar/` directory. |
|||
|
|||
**File:** [`scripts/data_to_mcap.py`](file:///d:/Work/Repo/C-Shenron/scripts/data_to_mcap.py) |
|||
```python |
|||
# Add a check for the shenron folder |
|||
shenron_path = session_dir / "shenron_radar" |
|||
if shenron_path.exists(): |
|||
# Write to Foxglove topic: "/radar/shenron" |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 4. Operational Workflow |
|||
|
|||
To generate a dataset with high-accuracy radar, follow this sequence: |
|||
|
|||
1. **Run Simulation**: |
|||
```powershell |
|||
run.bat braking --frames 200 |
|||
``` |
|||
2. **Generate Shenron Radar** (Post-Simulation): |
|||
```powershell |
|||
python scripts/generate_shenron.py |
|||
``` |
|||
3. **Convert to MCAP**: |
|||
```powershell |
|||
python scripts/data_to_mcap.py |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 5. Troubleshooting & Dependencies |
|||
|
|||
### Hardware Requirements |
|||
- **GPU**: NVIDIA T1000 with 8GB VRAM is sufficient. The model uses PyTorch tensors to parallelize the FMCW chirp calculations across the GPU cores. |
|||
- **CPU/RAM**: Standard multi-core CPU; 16GB+ RAM recommended for handling large point cloud buffers. |
|||
|
|||
### Installation |
|||
Execute these commands in your `carla312` environment: |
|||
```powershell |
|||
pip install torch |
|||
pip install open3d |
|||
``` |
|||
|
|||
### Data Interface Adaptation |
|||
The most critical "surgical" modification occurs in **`ISOLATE/e2e_agent_sem_lidar2shenron_package/lidar.py`**. |
|||
|
|||
Your CARLA 0.9.16 Semantic LiDAR data typically contains 7 columns: |
|||
`[x, y, z, intensity, cos_inc_angle, object_idx, semantic_tag]` |
|||
|
|||
The `map_carla_semantic_lidar_latest` function in `lidar.py` must be updated to index your specific columns: |
|||
```python |
|||
def map_carla_semantic_lidar_latest(carla_sem_lidar_data): |
|||
# Ensure indices match your [N, 7] array |
|||
# e.g., if semantic_tag is index 6: |
|||
carla_sem_lidar_data_crop = carla_sem_lidar_data[:, (0, 1, 2, 6)] |
|||
... |
|||
``` |
|||
|
|||
The script `scripts/generate_shenron.py` will handle loading your `.npy` files and feeding them into this function. |
|||
@ -0,0 +1,64 @@ |
|||
# [Plan] Shenron Model-Based Development (MBD) Integration |
|||
|
|||
This plan shifts the integration to a **Model-Based Development** architecture. We will treat the isolated Shenron logic as a "strictly defined black box" (similar to a Simulink model) and enhance it to output "Rich" radar data. |
|||
|
|||
## User Review Required |
|||
|
|||
> [!IMPORTANT] |
|||
> **Rich Point Cloud**: We will modify the extraction logic to output more than just geometry. The new format will be: `[x, y, z, velocity, SNR, RCS_estimate]`. |
|||
> |
|||
> **Source of Data**: |
|||
> - **SNR**: Extracted from the peak magnitude in the Range-Doppler heatmaps. |
|||
> - **RCS**: Back-calculated from the return power logic in `Sceneset`. |
|||
> - **Noise**: Sampled from the simulated noise floor in `ConfigureRadar`. |
|||
|
|||
--- |
|||
|
|||
## Phase 1: Environment & Dependency Setup |
|||
... (Previously defined) |
|||
|
|||
--- |
|||
|
|||
## Phase 2: The "Black Box" Wrapper & Rich Extraction |
|||
|
|||
### [MODIFY] `ISOLATE/sim_radar_utils/radar_processor.py` |
|||
Modify `convert_to_pcd()` to preserve the magnitude of the detected peaks. |
|||
- **Current**: Returns `[x, y]`. |
|||
- **New**: Returns `[x, y, z, velocity, magnitude]`. |
|||
|
|||
### [NEW] `ISOLATE/model_wrapper.py` |
|||
The wrapper will now handle the "Rich" data synthesis: |
|||
1. Call `Sceneset` to get the ground-truth physical power (RCS). |
|||
2. Call `HeatmapGen` to generate the noisy ADC stream. |
|||
3. Call `RadarProcessor` to extract detected peaks. |
|||
4. Merge the detected peaks with the underlying RCS data to provide a complete "Semantic Radar" point cloud. |
|||
|
|||
--- |
|||
|
|||
## Phase 3: Modular Pipeline Integration |
|||
... (Previously defined) |
|||
|
|||
--- |
|||
|
|||
## Verification Plan |
|||
|
|||
### Automated Verification |
|||
1. **API Integrity**: Run a script that initializes the `ShenronRadarModel` and passes a dummy [N, 7] array to ensure it executes and returns the correct [N, 4] shape. |
|||
2. **Batch Run**: Execute `generate_shenron.py` on an existing dataset. |
|||
|
|||
### Manual Verification |
|||
1. Compare the MCAP output with vs. without the Shenron layer to ensure topic alignment. |
|||
|
|||
--- |
|||
|
|||
## Verification Plan |
|||
|
|||
### Automated Verification |
|||
1. **Pilot Run**: Record a 100-frame scenario with semantic LiDAR. |
|||
2. **Post-Process**: Run `python scripts/generate_shenron.py`. |
|||
3. **Check Output**: Verify `data/<session>/shenron_radar/` contains 100 `.npy` files. |
|||
|
|||
### Manual Verification |
|||
1. Run `python scripts/data_to_mcap.py`. |
|||
2. Open the MCAP in Foxglove and verify that the `/radar/shenron` topic perfectly overlays with the `/lidar` data but shows radar-typical noise and sparsity. |
|||
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue