carla/PythonAPI/no_rendering_mode.py

1151 lines
46 KiB
Python
Executable File

#!/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>.
# Allows visualising a 2D map generated by vehicles.
"""
Welcome to CARLA No-Rendering Mode Visualizer
TAB : toggle hero mode
Mouse Wheel : zoom in / zoom out
Mouse Drag : move map (map mode only)
W : throttle
S : brake
AD : steer
Q : toggle reverse
Space : hand-brake
P : toggle autopilot
M : toggle manual transmission
,/. : gear up/down
F1 : toggle HUD
I : toggle actor ids
H/? : toggle help
ESC : quit
"""
# ==============================================================================
# -- find carla module ---------------------------------------------------------
# ==============================================================================
import glob
import os
import sys
try:
sys.path.append(glob.glob('**/carla-*%d.%d-%s.egg' % (
sys.version_info.major,
sys.version_info.minor,
'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0])
except IndexError:
pass
# ==============================================================================
# -- imports -------------------------------------------------------------------
# ==============================================================================
import carla
from carla import TrafficLightState as tls
import argparse
import logging
import datetime
import weakref
import math
import random
try:
import pygame
from pygame.locals import KMOD_CTRL
from pygame.locals import KMOD_SHIFT
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_d
from pygame.locals import K_h
from pygame.locals import K_i
from pygame.locals import K_m
from pygame.locals import K_p
from pygame.locals import K_q
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')
# ==============================================================================
# -- Constants -----------------------------------------------------------------
# ==============================================================================
# Colors
# We will use the color palette used in Tango Desktop Project (Each color is indexed depending on brightness level)
# See: https://en.wikipedia.org/wiki/Tango_Desktop_Project
COLOR_BUTTER_0 = pygame.Color(252, 233, 79)
COLOR_BUTTER_1 = pygame.Color(237, 212, 0)
COLOR_BUTTER_2 = pygame.Color(196, 160, 0)
COLOR_ORANGE_0 = pygame.Color(252, 175, 62)
COLOR_ORANGE_1 = pygame.Color(245, 121, 0)
COLOR_ORANGE_2 = pygame.Color(209, 92, 0)
COLOR_CHOCOLATE_0 = pygame.Color(233, 185, 110)
COLOR_CHOCOLATE_1 = pygame.Color(193, 125, 17)
COLOR_CHOCOLATE_2 = pygame.Color(143, 89, 2)
COLOR_CHAMELEON_0 = pygame.Color(138, 226, 52)
COLOR_CHAMELEON_1 = pygame.Color(115, 210, 22)
COLOR_CHAMELEON_2 = pygame.Color(78, 154, 6)
COLOR_SKY_BLUE_0 = pygame.Color(114, 159, 207)
COLOR_SKY_BLUE_1 = pygame.Color(52, 101, 164)
COLOR_SKY_BLUE_2 = pygame.Color(32, 74, 135)
COLOR_PLUM_0 = pygame.Color(173, 127, 168)
COLOR_PLUM_1 = pygame.Color(117, 80, 123)
COLOR_PLUM_2 = pygame.Color(92, 53, 102)
COLOR_SCARLET_RED_0 = pygame.Color(239, 41, 41)
COLOR_SCARLET_RED_1 = pygame.Color(204, 0, 0)
COLOR_SCARLET_RED_2 = pygame.Color(164, 0, 0)
COLOR_ALUMINIUM_0 = pygame.Color(238, 238, 236)
COLOR_ALUMINIUM_1 = pygame.Color(211, 215, 207)
COLOR_ALUMINIUM_2 = pygame.Color(186, 189, 182)
COLOR_ALUMINIUM_3 = pygame.Color(136, 138, 133)
COLOR_ALUMINIUM_4 = pygame.Color(85, 87, 83)
COLOR_ALUMINIUM_5 = pygame.Color(46, 52, 54)
COLOR_WHITE = pygame.Color(255, 255, 255)
COLOR_BLACK = pygame.Color(0, 0, 0)
# Module Defines
MODULE_WORLD = 'WORLD'
MODULE_HUD = 'HUD'
MODULE_INPUT = 'INPUT'
PIXELS_PER_METER = 15
MAP_DEFAULT_SCALE = 0.1
HERO_DEFAULT_SCALE = 1.0
PIXELS_AHEAD_VEHICLE = 150
# ==============================================================================
# -- Util -----------------------------------------------------------
# ==============================================================================
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
class Util(object):
@staticmethod
def blits(destination_surface, source_surfaces, rect=None, blend_mode=0):
for surface in source_surfaces:
destination_surface.blit(surface[0], surface[1], rect, blend_mode)
# ==============================================================================
# -- ModuleManager -------------------------------------------------------------
# ==============================================================================
class ModuleManager(object):
def __init__(self):
self.modules = []
def register_module(self, module):
self.modules.append(module)
def clear_modules(self):
del self.modules[:]
def tick(self, clock):
# Update all the modules
for module in self.modules:
module.tick(clock)
def render(self, display):
display.fill(COLOR_ALUMINIUM_5)
for module in self.modules:
module.render(display)
def get_module(self, name):
for module in self.modules:
if module.name == name:
return module
def start_modules(self):
for module in self.modules:
module.start()
# ==============================================================================
# -- 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=COLOR_WHITE, seconds=2.0):
text_texture = self.font.render(text, True, color)
self.surface = pygame.Surface(self.dim)
self.seconds_left = seconds
self.surface.fill(COLOR_BLACK)
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)
# ==============================================================================
# -- ModuleHUD -----------------------------------------------------------------
# ==============================================================================
class ModuleHUD (object):
def __init__(self, name, width, height):
self.name = name
self.dim = (width, height)
self._init_hud_params()
self._init_data_params()
def start(self):
pass
def _init_hud_params(self):
fonts = [x for x in pygame.font.get_fonts() if 'mono' 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, 14)
self._header_font = pygame.font.SysFont('Arial', 14)
self.help = HelpText(pygame.font.Font(mono, 24), *self.dim)
self._notifications = FadingText(
pygame.font.Font(pygame.font.get_default_font(), 20),
(self.dim[0], 40), (0, self.dim[1] - 40))
def _init_data_params(self):
self.show_info = True
self.show_actor_ids = False
self._info_text = {}
def notification(self, text, seconds=2.0):
self._notifications.set_text(text, seconds=seconds)
def tick(self, clock):
self._notifications.tick(clock)
def add_info(self, module_name, info):
self._info_text[module_name] = info
def render_actors_ids(self, vehicle_id_surface, list_actors, world_to_pixel, hero_actor):
vehicle_id_surface.fill(COLOR_BLACK)
if self.show_actor_ids:
vehicle_id_surface.set_alpha(150)
for actor in list_actors:
location = actor.get_location()
x, y = world_to_pixel(location)
angle = 0
if hero_actor is not None:
angle = -hero_actor.get_transform().rotation.yaw - 90
color = COLOR_ORANGE_0
if actor.attributes['role_name'] == 'hero':
color = COLOR_SCARLET_RED_0
font_surface = self._header_font.render(str(actor.id), True, color)
rotated_font_surface = pygame.transform.rotate(font_surface, angle).convert_alpha()
rect = rotated_font_surface.get_rect(center=(x,y))
vehicle_id_surface.blit(rotated_font_surface, rect)
return vehicle_id_surface
def render(self, display):
if self.show_info:
info_surface = pygame.Surface((240, self.dim[1]))
info_surface.set_alpha(100)
display.blit(info_surface, (0, 0))
v_offset = 4
bar_h_offset = 100
bar_width = 106
i = 0
for module_name, module_info in self._info_text.items():
if not module_info:
continue
surface = self._header_font.render(module_name, True, COLOR_ALUMINIUM_0).convert_alpha()
display.blit(surface, (8 + bar_width / 2, 18 * i + v_offset))
v_offset += 12
i += 1
for item in module_info:
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
elif isinstance(item, tuple):
if isinstance(item[1], bool):
rect = pygame.Rect((bar_h_offset, v_offset + 8), (6, 6))
pygame.draw.rect(display, COLOR_ALUMINIUM_0, 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, COLOR_ALUMINIUM_0, 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, COLOR_ALUMINIUM_0, rect)
item = item[0]
if item: # At this point has to be a str.
surface = self._font_mono.render(item, True, COLOR_ALUMINIUM_0).convert_alpha()
display.blit(surface, (8, 18 * i + v_offset))
v_offset += 18
v_offset += 24
self._notifications.render(display)
self.help.render(display)
# ==============================================================================
# -- TrafficLightSurfaces ------------------------------------------------------
# ==============================================================================
class TrafficLightSurfaces(object):
"""Holds the surfaces (scaled and rotated) for painting traffic lights"""
def __init__(self):
def make_surface(tl):
w = 40
surface = pygame.Surface((w, 3 * w), pygame.SRCALPHA)
surface.fill((31, 31, 31) if tl != 'h' else (245, 121, 0))
if tl != 'h':
hw = int(w / 2)
off = (48, 48, 48)
red = (239, 41, 41)
yellow = (252, 175, 62)
green = (138, 226, 52)
pygame.draw.circle(surface, red if tl == tls.Red else off, (hw, hw), int(0.4 * w))
pygame.draw.circle(surface, yellow if tl == tls.Yellow else off, (hw, w + hw), int(0.4 * w))
pygame.draw.circle(surface, green if tl == tls.Green else off, (hw, 2 * w + hw), int(0.4 * w))
return pygame.transform.smoothscale(surface, (15, 45) if tl != 'h' else (19, 49))
self._original_surfaces = {
'h': make_surface('h'),
tls.Red: make_surface(tls.Red),
tls.Yellow: make_surface(tls.Yellow),
tls.Green: make_surface(tls.Green),
tls.Off: make_surface(tls.Off),
tls.Unknown: make_surface(tls.Unknown)
}
self.surfaces = dict(self._original_surfaces)
def rotozoom(self, angle, scale):
for key, surface in self._original_surfaces.items():
self.surfaces[key] = pygame.transform.rotozoom(surface, angle, scale)
# ==============================================================================
# -- World ---------------------------------------------------------------------
# ==============================================================================
class MapImage(object):
def __init__(self, carla_map, pixels_per_meter=10):
self._pixels_per_meter = pixels_per_meter
self.scale = 1.0
waypoints = carla_map.generate_waypoints(2)
margin = 50
max_x = max(waypoints, key=lambda x: x.transform.location.x).transform.location.x + margin
max_y = max(waypoints, key=lambda x: x.transform.location.y).transform.location.y + margin
min_x = min(waypoints, key=lambda x: x.transform.location.x).transform.location.x - margin
min_y = min(waypoints, key=lambda x: x.transform.location.y).transform.location.y - margin
self._width = max(max_x - min_x, max_y - min_y)
self._world_offset = (min_x, min_y)
width_in_pixels = int(self._pixels_per_meter * self._width)
self._big_map_surface = pygame.Surface((width_in_pixels, width_in_pixels)).convert()
self.draw_road_map(self._big_map_surface, carla_map, self.world_to_pixel)
self.surface = self._big_map_surface
def draw_road_map(self, map_surface, carla_map, world_to_pixel):
map_surface.fill(COLOR_ALUMINIUM_5)
precision = 0.05
def draw_lane_marking(surface, points, solid=True):
if solid:
pygame.draw.lines(surface, (252, 175, 62), False, points, 2)
else:
broken_lines = [x for n, x in enumerate(zip(*(iter(points),) * 20)) if n % 3 == 0]
for line in broken_lines:
pygame.draw.lines(surface, (251, 241, 199), False, line, 2)
def draw_arrow(surface, transform, color=(31, 31, 31)):
transform.rotation.yaw += 180
forward = transform.get_forward_vector()
transform.rotation.yaw += 90
right_dir = transform.get_forward_vector()
start = transform.location
end = start + 2.0 * forward
right = start + 0.8 * forward + 0.4 * right_dir
left = start + 0.8 * forward - 0.4 * right_dir
pygame.draw.lines(
surface, color, False, [
world_to_pixel(x) for x in [
start, end]], 4)
pygame.draw.lines(
surface, color, False, [
world_to_pixel(x) for x in [
left, start, right]], 4)
def lateral_shift(transform, shift):
transform.rotation.yaw += 90
return transform.location + shift * transform.get_forward_vector()
def does_cross_solid_line(waypoint, shift):
w = carla_map.get_waypoint(lateral_shift(waypoint.transform, shift), project_to_road=False)
if w is None or w.road_id != waypoint.road_id:
return True
else:
return (w.lane_id * waypoint.lane_id < 0) or w.lane_id == waypoint.lane_id
topology = [x[0] for x in carla_map.get_topology()]
topology = sorted(topology, key=lambda w: w.transform.location.z)
for waypoint in topology:
waypoints = [waypoint]
nxt = waypoint.next(precision)[0]
while nxt.road_id == waypoint.road_id:
waypoints.append(nxt)
nxt = nxt.next(precision)[0]
left_marking = [lateral_shift(w.transform, -w.lane_width * 0.5) for w in waypoints]
right_marking = [lateral_shift(w.transform, w.lane_width * 0.5) for w in waypoints]
polygon = left_marking + [x for x in reversed(right_marking)]
polygon = [world_to_pixel(x) for x in polygon]
pygame.draw.polygon(map_surface, (38, 38, 38), polygon, 10)
pygame.draw.polygon(map_surface, (38, 38, 38), polygon)
if not waypoint.is_intersection:
sample = waypoints[int(len(waypoints) / 2)]
draw_lane_marking(
map_surface,
[world_to_pixel(x) for x in left_marking],
does_cross_solid_line(sample, -sample.lane_width * 1.1))
draw_lane_marking(
map_surface,
[world_to_pixel(x) for x in right_marking],
does_cross_solid_line(sample, sample.lane_width * 1.1))
for n, wp in enumerate(waypoints):
if (n % 400) == 0:
draw_arrow(map_surface, wp.transform)
def world_to_pixel(self, location, offset=(0, 0)):
x = self.scale * self._pixels_per_meter * (location.x - self._world_offset[0])
y = self.scale * self._pixels_per_meter * (location.y - self._world_offset[1])
return [int(x - offset[0]), int(y - offset[1])]
def world_to_pixel_width(self, width):
return int(self.scale * self._pixels_per_meter * width)
def scale_map(self, scale):
if scale != self.scale:
self.scale = scale
width = int(self._big_map_surface.get_width() * self.scale)
self.surface = pygame.transform.smoothscale(self._big_map_surface, (width, width))
class ModuleWorld(object):
def __init__(self, name, host, port, timeout, actor_filter):
self.client = None
self.name = name
self.host = host
self.port = port
self.timeout = timeout
self.actor_filter = actor_filter
self.server_fps = 0.0
self.simulation_time = 0
self.server_clock = pygame.time.Clock()
# World data
self.world = None
self.town_map = None
self.actors = None
# Store necessary modules
self.module_hud = None
self.module_input = None
self.surface_size = [0, 0]
self.prev_scaled_size = 0
self.scaled_size = 0
# Hero actor
self.hero_actor = None
self.scale_offset = [0, 0]
self.vehicle_id_surface = None
self.result_surface = None
self.traffic_light_surfaces = TrafficLightSurfaces()
def _get_data_from_carla(self, host, port, timeout):
try:
self.client = carla.Client(host, port)
self.client.set_timeout(timeout)
world = self.client.get_world()
town_map = world.get_map()
actors = world.get_actors()
return (world, town_map, actors)
except Exception as ex:
logging.error(ex)
exit_game()
def start(self):
self.world, self.town_map, self.actors = self._get_data_from_carla(self.host, self.port, self.timeout)
# Create Surfaces
self.map_image = MapImage(self.town_map, PIXELS_PER_METER)
# Store necessary modules
self.module_hud = module_manager.get_module(MODULE_HUD)
self.module_input = module_manager.get_module(MODULE_INPUT)
self.original_surface_size = min(self.module_hud.dim[0], self.module_hud.dim[1])
self.surface_size = self.map_image._big_map_surface.get_width()
self.scaled_size = int(self.surface_size)
self.prev_scaled_size = int(self.surface_size)
# Render Actors
self.actors_surface = pygame.Surface((self.map_image.surface.get_width(), self.map_image.surface.get_height()))
self.actors_surface.set_colorkey(COLOR_BLACK)
self.vehicle_id_surface = pygame.Surface((self.surface_size, self.surface_size)).convert()
self.vehicle_id_surface.set_colorkey(COLOR_BLACK)
self.border_round_surface = pygame.Surface(self.module_hud.dim, pygame.SRCALPHA).convert()
self.border_round_surface.set_colorkey(COLOR_WHITE)
self.border_round_surface.fill(COLOR_BLACK)
center_offset = (int(self.module_hud.dim[0] / 2), int(self.module_hud.dim[1] / 2))
pygame.draw.circle(self.border_round_surface, COLOR_ALUMINIUM_2, center_offset, int(self.module_hud.dim[1]/ 2))
pygame.draw.circle(self.border_round_surface, COLOR_WHITE, center_offset, int((self.module_hud.dim[1] - 8)/ 2))
scaled_original_size = self.original_surface_size * (1.0 / 0.9)
self.hero_surface = pygame.Surface((scaled_original_size, scaled_original_size)).convert()
self.result_surface = pygame.Surface((self.surface_size, self.surface_size)).convert()
self.result_surface.set_colorkey(COLOR_BLACK)
weak_self = weakref.ref(self)
self.world.on_tick(lambda timestamp: ModuleWorld.on_world_tick(weak_self, timestamp))
def select_hero_actor(self):
hero_vehicles = [
actor for actor in self.actors if 'vehicle' in actor.type_id and actor.attributes['role_name'] == 'hero']
if len(hero_vehicles) > 0:
self.hero_actor = random.choice(hero_vehicles)
else:
self._spawn_hero()
def _spawn_hero(self):
# 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.
while self.hero_actor is None:
spawn_points = self.world.get_map().get_spawn_points()
spawn_point = random.choice(spawn_points) if spawn_points else carla.Transform()
self.hero_actor = self.world.try_spawn_actor(blueprint, spawn_point)
if self.hero_actor is not None:
self.hero_actor.set_autopilot()
def tick(self, clock):
self.update_hud_info(clock)
def update_hud_info(self, clock):
hero_mode_text = []
if self.hero_actor is not None:
hero_speed = self.hero_actor.get_velocity()
hero_speed_text = 3.6 * math.sqrt(hero_speed.x ** 2 + hero_speed.y ** 2 + hero_speed.z ** 2)
state = self.hero_actor.get_traffic_light_state()
affected_traffic_light = 'None'
if state == carla.libcarla.TrafficLightState.Green:
affected_traffic_light = 'GREEN'
elif state == carla.libcarla.TrafficLightState.Yellow:
affected_traffic_light = 'YELLOW'
else:
affected_traffic_light = 'RED'
affected_speed_limit = self.hero_actor.get_speed_limit()
hero_mode_text = [
'Hero Mode: ON',
'Hero ID: %7d' % self.hero_actor.id,
'Hero Vehicle: %14s' % get_actor_display_name(self.hero_actor, truncate=14),
'Hero Speed: %3d km/h' % hero_speed_text,
'Hero Affected by:',
' Traffic Light: %12s' % affected_traffic_light,
' Speed Limit: %3d km/h' % affected_speed_limit
]
else:
hero_mode_text = ['Hero Mode: OFF']
self.server_fps = self.server_clock.get_fps()
self.server_fps = 'inf' if self.server_fps == float('inf') else round(self.server_fps)
module_info_text = [
'Server: % 16s FPS' % self.server_fps,
'Client: % 16s FPS' % round(clock.get_fps()),
'Simulation Time: % 12s' % datetime.timedelta(seconds=int(self.simulation_time)),
'Map Name: %10s' % self.world.map_name,
]
module_info_text = module_info_text
module_hud = module_manager.get_module(MODULE_HUD)
module_hud.add_info(self.name, module_info_text)
module_hud.add_info('HERO', hero_mode_text)
@staticmethod
def on_world_tick(weak_self, timestamp):
self = weak_self()
if not self:
return
self.server_clock.tick()
self.server_fps = self.server_clock.get_fps()
self.simulation_time = timestamp.elapsed_seconds
self.world = self.client.get_world()
self.actors = self.world.get_actors()
def _split_actors(self, actors):
vehicles = []
traffic_lights = []
speed_limits = []
walkers = []
for actor in actors:
if 'vehicle' in actor.type_id:
vehicles.append(actor)
elif 'traffic_light' in actor.type_id:
traffic_lights.append(actor)
elif 'speed_limit' in actor.type_id:
speed_limits.append(actor)
elif 'walker' in actor.type_id:
walkers.append(actor)
info_text = []
if self.hero_actor is not None and len(vehicles) > 1:
location = self.hero_actor.get_location()
vehicle_list = [x for x in vehicles if x.id != self.hero_actor.id]
distance = lambda v: location.distance(v.get_location())
for n, vehicle in enumerate(sorted(vehicle_list, key=distance)):
if n > 15:
break
vehicle_type = get_actor_display_name(vehicle, truncate=22)
info_text.append('% 5d %s' % (vehicle.id, vehicle_type))
module_manager.get_module(MODULE_HUD).add_info(
'NEARBY VEHICLES',
info_text)
return (vehicles, traffic_lights, speed_limits, walkers)
def _render_traffic_lights(self, surface, list_tl, world_to_pixel):
for tl in list_tl:
pos = world_to_pixel(tl.get_location())
hero = self.hero_actor
if hero is not None and hero.is_at_traffic_light() and tl.id == hero.get_traffic_light().id:
srf = self.traffic_light_surfaces.surfaces['h']
surface.blit(srf, srf.get_rect(center=pos))
srf = self.traffic_light_surfaces.surfaces[tl.state]
surface.blit(srf, srf.get_rect(center=pos))
def _render_speed_limits(self, surface, list_sl, world_to_pixel, world_to_pixel_width):
font_size = world_to_pixel_width(2)
radius = world_to_pixel_width(2)
font = pygame.font.SysFont('Arial', font_size)
for sl in list_sl:
x, y = world_to_pixel(sl.get_location())
# Render speed limit
white_circle_radius = int(radius * 0.75)
pygame.draw.circle(surface, (239, 41, 41), (x, y), radius)
pygame.draw.circle(surface, (251, 241, 199), (x, y), white_circle_radius)
limit = sl.type_id.split('.')[2]
font_surface = font.render(limit, False, (31, 31, 31))
# Blit
if self.hero_actor is not None:
# Rotate font surface with respect to hero vehicle front
angle = -self.hero_actor.get_transform().rotation.yaw - 90.0
font_surface = pygame.transform.rotate(font_surface, angle)
offset = font_surface.get_rect(center=(x, y))
surface.blit(font_surface, offset)
else:
surface.blit(font_surface, (x - radius / 2, y - radius / 2))
def _render_walkers(self, surface, list_w, world_to_pixel):
for w in list_w:
color = COLOR_ALUMINIUM_0
# Compute bounding box points
bb = w.bounding_box.extent
corners = [
carla.Location(x=-bb.x, y=-bb.y),
carla.Location(x=bb.x, y=-bb.y),
carla.Location(x=bb.x, y=bb.y),
carla.Location(x=-bb.x, y=bb.y)]
t = w.get_transform()
t.transform(corners)
corners = [world_to_pixel(p) for p in corners]
pygame.draw.polygon(surface, color, corners)
def _render_vehicles(self, surface, list_v, world_to_pixel, world_to_pixel_width, scale_factor):
for v in list_v:
color = COLOR_ORANGE_1
if v.attributes['role_name'] == 'hero':
color = COLOR_SCARLET_RED_2
# Compute bounding box points
bb = v.bounding_box.extent
corners = [carla.Location(x=-bb.x, y=-bb.y),
carla.Location(x=bb.x - 0.8, y=-bb.y),
carla.Location(x=bb.x, y=0),
carla.Location(x=bb.x - 0.8, y=bb.y),
carla.Location(x=-bb.x, y=bb.y),
carla.Location(x=-bb.x, y=-bb.y)
]
t = v.get_transform()
t.transform(corners)
corners = [world_to_pixel(p) for p in corners]
pygame.draw.lines(surface, color, False, corners, 2)
def render_actors(self, surface, vehicles, traffic_lights, speed_limits, walkers, scale_factor):
self._render_vehicles(surface, vehicles, self.map_image.world_to_pixel,
self.map_image.world_to_pixel_width, scale_factor)
self._render_traffic_lights(surface, traffic_lights, self.map_image.world_to_pixel)
self._render_speed_limits(surface, speed_limits, self.map_image.world_to_pixel,
self.map_image.world_to_pixel_width)
self._render_walkers(surface, walkers, self.map_image.world_to_pixel)
def clip_surfaces(self, clipping_rect):
self.actors_surface.set_clip(clipping_rect)
self.vehicle_id_surface.set_clip(clipping_rect)
self.result_surface.set_clip(clipping_rect)
def _compute_scale(self, scale_factor):
m = self.module_input.mouse_pos
# Percentage of surface where mouse position is actually
px = (m[0] - self.scale_offset[0]) / float(self.prev_scaled_size)
py = (m[1] - self.scale_offset[1]) / float(self.prev_scaled_size)
# Offset will be the previously accumulated offset added with the
# difference of mouse positions in the old and new scales
diff_between_scales = ((float(self.prev_scaled_size) * px) - (float(self.scaled_size) * px),
(float(self.prev_scaled_size) * py) - (float(self.scaled_size) * py))
self.scale_offset = (self.scale_offset[0] + diff_between_scales[0],
self.scale_offset[1] + diff_between_scales[1])
# Update previous scale
self.prev_scaled_size = self.scaled_size
# Scale performed
self.map_image.scale_map(scale_factor)
def render(self, display):
self.result_surface.fill(COLOR_BLACK)
vehicles, traffic_lights, speed_limits, walkers = self._split_actors(self.actors)
scale_factor = self.module_input.wheel_offset
self.scaled_size = int(self.map_image._width * scale_factor)
if self.scaled_size != self.prev_scaled_size:
self._compute_scale(scale_factor)
# Render Actors
hero_vehicles = [v for v in vehicles if v.attributes['role_name'] == 'hero']
if len(hero_vehicles) == 0:
self.hero_actor = None
self.actors_surface.fill(COLOR_BLACK)
self.render_actors(self.actors_surface, vehicles, traffic_lights, speed_limits, walkers, scale_factor)
# Render Ids
self.module_hud.render_actors_ids(self.vehicle_id_surface, vehicles,
self.map_image.world_to_pixel, self.hero_actor)
# Blit surfaces
surfaces = ((self.map_image.surface, (0, 0)),
(self.actors_surface, (0, 0)),
(self.vehicle_id_surface, (0, 0)),
)
angle = 0.0 if self.hero_actor is None else self.hero_actor.get_transform().rotation.yaw + 90.0
self.traffic_light_surfaces.rotozoom(-angle, self.map_image.scale)
center_offset = (0, 0)
if self.hero_actor is not None:
hero_location_screen = self.map_image.world_to_pixel(self.hero_actor.get_location())
hero_front = self.hero_actor.get_transform().get_forward_vector()
translation_offset = (hero_location_screen[0] - self.hero_surface.get_width() / 2 + hero_front.x * PIXELS_AHEAD_VEHICLE,
(hero_location_screen[1] - self.hero_surface.get_height() / 2 + hero_front.y * PIXELS_AHEAD_VEHICLE))
# Apply clipping rect
clipping_rect = pygame.Rect(translation_offset[0],
translation_offset[1],
self.hero_surface.get_width(),
self.hero_surface.get_height())
self.clip_surfaces(clipping_rect)
Util.blits(self.result_surface, surfaces)
self.border_round_surface.set_clip(clipping_rect)
self.hero_surface.fill(COLOR_ALUMINIUM_5)
self.hero_surface.blit(self.result_surface, (-translation_offset[0],
-translation_offset[1]))
rotated_result_surface = pygame.transform.rotozoom(self.hero_surface, angle, 0.9).convert()
center = (display.get_width() / 2, display.get_height() / 2)
rotation_pivot = rotated_result_surface.get_rect(center=center)
display.blit(rotated_result_surface, rotation_pivot)
display.blit(self.border_round_surface, (0,0))
else:
# Translation offset
translation_offset = (self.module_input.mouse_offset[0] * scale_factor + self.scale_offset[0],
self.module_input.mouse_offset[1] * scale_factor + self.scale_offset[1])
center_offset = ((display.get_width() - self.surface_size) / 2 * scale_factor, 0)
# Apply clipping rect
clipping_rect = pygame.Rect(-translation_offset[0] - center_offset[0], -translation_offset[1],
self.module_hud.dim[0], self.module_hud.dim[1])
self.clip_surfaces(clipping_rect)
Util.blits(self.result_surface, surfaces)
display.blit(self.result_surface, (translation_offset[0] + center_offset[0],
translation_offset[1]))
# ==============================================================================
# -- Input -----------------------------------------------------------
# ==============================================================================
class ModuleInput(object):
def __init__(self, name):
self.name = name
self.mouse_pos = (0, 0)
self.mouse_offset = [0.0, 0.0]
self.wheel_offset = 0.1
self.wheel_amount = 0.025
self._steer_cache = 0.0
self._control = None
self._autopilot_enabled = True
def start(self):
hud = module_manager.get_module(MODULE_HUD)
hud.notification("Press 'H' or '?' for help.", seconds=4.0)
def render(self, display):
pass
def tick(self, clock):
self.parse_input(clock)
def _parse_events(self):
self.mouse_pos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_game()
elif event.type == pygame.KEYUP:
if self._is_quit_shortcut(event.key):
exit_game()
elif event.key == K_h or (event.key == K_SLASH and pygame.key.get_mods() & KMOD_SHIFT):
module_hud = module_manager.get_module(MODULE_HUD)
module_hud.help.toggle()
elif event.key == K_TAB:
module_world = module_manager.get_module(MODULE_WORLD)
module_hud = module_manager.get_module(MODULE_HUD)
if module_world.hero_actor is None:
module_world.select_hero_actor()
self.wheel_offset = HERO_DEFAULT_SCALE
self._control = carla.VehicleControl()
self._autopilot_enabled = False
module_hud.notification('Hero Mode')
else:
self.wheel_offset = MAP_DEFAULT_SCALE
self.mouse_offset = [0, 0]
self.mouse_pos = [0,0]
module_world.scale_offset = [0,0]
module_world.hero_actor = None
module_hud.notification('Map Mode')
elif event.key == K_F1:
module_hud = module_manager.get_module(MODULE_HUD)
module_hud.show_info = not module_hud.show_info
elif event.key == K_i:
module_hud = module_manager.get_module(MODULE_HUD)
module_hud.show_actor_ids = not module_hud.show_actor_ids
elif 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
world = module_manager.get_module(MODULE_WORLD)
self._control.gear = world.hero_actor.get_control().gear
module_hud = module_manager.get_module(MODULE_HUD)
module_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 = module_manager.get_module(MODULE_WORLD)
world.hero_actor.set_autopilot(self._autopilot_enabled)
module_hud = module_manager.get_module(MODULE_HUD)
module_hud.notification('Autopilot %s' % ('On' if self._autopilot_enabled else 'Off'))
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
self.wheel_offset += self.wheel_amount
if self.wheel_offset >= 1.0:
self.wheel_offset = 1.0
elif event.button == 5:
self.wheel_offset -= self.wheel_amount
if self.wheel_offset <= 0.1:
self.wheel_offset = 0.1
def _parse_keys(self, milliseconds):
keys = pygame.key.get_pressed()
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_mouse(self):
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
self.mouse_offset[0] += (1.0 / self.wheel_offset) * (x - self.mouse_pos[0])
self.mouse_offset[1] += (1.0 / self.wheel_offset) * (y - self.mouse_pos[1])
self.mouse_pos = (x, y)
def parse_input(self, clock):
self._parse_events()
self._parse_mouse()
if not self._autopilot_enabled:
if isinstance(self._control, carla.VehicleControl):
self._parse_keys(clock.get_time())
self._control.reverse = self._control.gear < 0
world = module_manager.get_module(MODULE_WORLD)
if (world.hero_actor is not None):
world.hero_actor.apply_control(self._control)
@staticmethod
def _is_quit_shortcut(key):
return (key == K_ESCAPE) or (key == K_q and pygame.key.get_mods() & KMOD_CTRL)
# ==============================================================================
# -- Global Objects ------------------------------------------------------------
# ==============================================================================
module_manager = ModuleManager()
# ==============================================================================
# -- Game Loop ---------------------------------------------------------------
# ==============================================================================
def game_loop(args):
# Init Pygame
pygame.init()
display = pygame.display.set_mode(
(args.width, args.height),
pygame.HWSURFACE | pygame.DOUBLEBUF)
pygame.display.set_caption(args.description)
font = pygame.font.Font(pygame.font.get_default_font(), 20)
text_surface = font.render('Rendering map...', True, COLOR_WHITE)
display.blit(text_surface, text_surface.get_rect(center=(args.width/2, args.height/2)))
pygame.display.flip()
# Init modules
input_module = ModuleInput(MODULE_INPUT)
hud_module = ModuleHUD(MODULE_HUD, args.width, args.height)
world_module = ModuleWorld(MODULE_WORLD, args.host, args.port, 2.0, args.filter)
# Register Modules
module_manager.register_module(world_module)
module_manager.register_module(hud_module)
module_manager.register_module(input_module)
module_manager.start_modules()
clock = pygame.time.Clock()
while True:
clock.tick_busy_loop(60)
module_manager.tick(clock)
module_manager.render(display)
pygame.display.flip()
def exit_game():
module_manager.clear_modules()
pygame.quit()
sys.exit()
# ==============================================================================
# -- Main --------------------------------------------------------------------
# ==============================================================================
def main():
# Parse arguments
argparser = argparse.ArgumentParser(
description='CARLA No Rendering Mode Visualizer')
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(
'--filter',
metavar='PATTERN',
default='vehicle.*',
help='actor filter (default: "vehicle.*")')
args = argparser.parse_args()
args.description = argparser.description
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()