@ -1,9 +1,10 @@
#!/usr/bin/env python3
#!/usr/bin/env python3
import argparse
import argparse
import math
import math
import os
import signal
import threading
import threading
import time
import time
import os
from multiprocessing import Process , Queue
from multiprocessing import Process , Queue
from typing import Any
from typing import Any
@ -12,8 +13,6 @@ import numpy as np
import pyopencl as cl
import pyopencl as cl
import pyopencl . array as cl_array
import pyopencl . array as cl_array
from lib . can import can_function
import cereal . messaging as messaging
import cereal . messaging as messaging
from cereal import log
from cereal import log
from cereal . visionipc . visionipc_pyx import VisionIpcServer , VisionStreamType # pylint: disable=no-name-in-module, import-error
from cereal . visionipc . visionipc_pyx import VisionIpcServer , VisionStreamType # pylint: disable=no-name-in-module, import-error
@ -22,15 +21,9 @@ from common.numpy_fast import clip
from common . params import Params
from common . params import Params
from common . realtime import DT_DMON , Ratekeeper
from common . realtime import DT_DMON , Ratekeeper
from selfdrive . car . honda . values import CruiseButtons
from selfdrive . car . honda . values import CruiseButtons
from selfdrive . manager . helpers import unblock_stdout
from selfdrive . test . helpers import set_params_enabled
from selfdrive . test . helpers import set_params_enabled
from tools . sim . lib . can import can_function
parser = argparse . ArgumentParser ( description = ' Bridge between CARLA and openpilot. ' )
parser . add_argument ( ' --joystick ' , action = ' store_true ' )
parser . add_argument ( ' --low_quality ' , action = ' store_true ' )
parser . add_argument ( ' --town ' , type = str , default = ' Town04_Opt ' )
parser . add_argument ( ' --spawn_point ' , dest = ' num_selected_spawn_point ' , type = int , default = 16 )
args = parser . parse_args ( )
W , H = 1928 , 1208
W , H = 1928 , 1208
REPEAT_COUNTER = 5
REPEAT_COUNTER = 5
@ -41,6 +34,16 @@ pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'sensorEvent
sm = messaging . SubMaster ( [ ' carControl ' , ' controlsState ' ] )
sm = messaging . SubMaster ( [ ' carControl ' , ' controlsState ' ] )
def parse_args ( add_args = None ) :
parser = argparse . ArgumentParser ( description = ' Bridge between CARLA and openpilot. ' )
parser . add_argument ( ' --joystick ' , action = ' store_true ' )
parser . add_argument ( ' --low_quality ' , action = ' store_true ' )
parser . add_argument ( ' --town ' , type = str , default = ' Town04_Opt ' )
parser . add_argument ( ' --spawn_point ' , dest = ' num_selected_spawn_point ' , type = int , default = 16 )
return parser . parse_args ( add_args )
class VehicleState :
class VehicleState :
def __init__ ( self ) :
def __init__ ( self ) :
self . speed = 0
self . speed = 0
@ -84,7 +87,9 @@ class Camerad:
# TODO: move rgb_to_yuv.cl to local dir once the frame stream camera is removed
# TODO: move rgb_to_yuv.cl to local dir once the frame stream camera is removed
kernel_fn = os . path . join ( BASEDIR , " selfdrive " , " camerad " , " transforms " , " rgb_to_yuv.cl " )
kernel_fn = os . path . join ( BASEDIR , " selfdrive " , " camerad " , " transforms " , " rgb_to_yuv.cl " )
prg = cl . Program ( self . ctx , open ( kernel_fn ) . read ( ) ) . build ( cl_arg )
self . _kernel_file = open ( kernel_fn )
prg = cl . Program ( self . ctx , self . _kernel_file . read ( ) ) . build ( cl_arg )
self . krnl = prg . rgb_to_yuv
self . krnl = prg . rgb_to_yuv
self . Wdiv4 = W / / 4 if ( W % 4 == 0 ) else ( W + ( 4 - W % 4 ) ) / / 4
self . Wdiv4 = W / / 4 if ( W % 4 == 0 ) else ( W + ( 4 - W % 4 ) ) / / 4
self . Hdiv4 = H / / 4 if ( H % 4 == 0 ) else ( H + ( 4 - H % 4 ) ) / / 4
self . Hdiv4 = H / / 4 if ( H % 4 == 0 ) else ( H + ( 4 - H % 4 ) ) / / 4
@ -126,6 +131,9 @@ class Camerad:
setattr ( dat , pub_type , msg )
setattr ( dat , pub_type , msg )
pm . send ( pub_type , dat )
pm . send ( pub_type , dat )
def close ( self ) :
self . _kernel_file . close ( )
def imu_callback ( imu , vehicle_state ) :
def imu_callback ( imu , vehicle_state ) :
vehicle_state . bearing_deg = math . degrees ( imu . compass )
vehicle_state . bearing_deg = math . degrees ( imu . compass )
@ -231,12 +239,48 @@ def can_function_runner(vs: VehicleState, exit_event: threading.Event):
i + = 1
i + = 1
def bridge ( q ) :
def connect_carla_client ( ) :
# setup CARLA
client = carla . Client ( " 127.0.0.1 " , 2000 )
client = carla . Client ( " 127.0.0.1 " , 2000 )
client . set_timeout ( 10.0 )
client . set_timeout ( 10 )
world = client . load_world ( args . town )
return client
class CarlaBridge :
def __init__ ( self , args ) :
set_params_enabled ( )
msg = messaging . new_message ( ' liveCalibration ' )
msg . liveCalibration . validBlocks = 20
msg . liveCalibration . rpyCalib = [ 0.0 , 0.0 , 0.0 ]
Params ( ) . put ( " CalibrationParams " , msg . to_bytes ( ) )
self . _args = args
self . _carla_objects = [ ]
self . _camerad = None
self . _threads_exit_event = threading . Event ( )
self . _threads = [ ]
self . _shutdown = False
self . started = False
signal . signal ( signal . SIGTERM , self . _on_shutdown )
def _on_shutdown ( self , signal , frame ) :
self . _shutdown = True
def bridge_keep_alive ( self , q : Queue , retries : int ) :
while not self . _shutdown :
try :
self . _run ( q )
break
except RuntimeError as e :
if retries == 0 :
raise
retries - = 1
print ( f " Restarting bridge. Retries left { retries } . Error: { e } " )
def _run ( self , q : Queue ) :
client = connect_carla_client ( )
world = client . load_world ( self . _args . town )
settings = world . get_settings ( )
settings = world . get_settings ( )
settings . synchronous_mode = True # Enables synchronous mode
settings . synchronous_mode = True # Enables synchronous mode
settings . fixed_delta_seconds = 0.05
settings . fixed_delta_seconds = 0.05
@ -244,7 +288,7 @@ def bridge(q):
world . set_weather ( carla . WeatherParameters . ClearSunset )
world . set_weather ( carla . WeatherParameters . ClearSunset )
if args . low_quality :
if self . _ args. low_quality :
world . unload_map_layer ( carla . MapLayer . Foliage )
world . unload_map_layer ( carla . MapLayer . Foliage )
world . unload_map_layer ( carla . MapLayer . Buildings )
world . unload_map_layer ( carla . MapLayer . Buildings )
world . unload_map_layer ( carla . MapLayer . ParkedVehicles )
world . unload_map_layer ( carla . MapLayer . ParkedVehicles )
@ -258,12 +302,11 @@ def bridge(q):
vehicle_bp = blueprint_library . filter ( ' vehicle.tesla.* ' ) [ 1 ]
vehicle_bp = blueprint_library . filter ( ' vehicle.tesla.* ' ) [ 1 ]
spawn_points = world_map . get_spawn_points ( )
spawn_points = world_map . get_spawn_points ( )
assert len ( spawn_points ) > args . num_selected_spawn_point , \
assert len ( spawn_points ) > self . _args . num_selected_spawn_point , f ''' No spawn point { self . _args . num_selected_spawn_point } , try a value between 0 and
f ''' No spawn point { args . num_selected_spawn_point } , try a value between 0 and
{ len ( spawn_points ) } for this town . '''
{ len ( spawn_points ) } for this town . '''
spawn_point = spawn_points [ args . num_selected_spawn_point ]
spawn_point = spawn_points [ self . _ args. num_selected_spawn_point ]
vehicle = world . spawn_actor ( vehicle_bp , spawn_point )
vehicle = world . spawn_actor ( vehicle_bp , spawn_point )
self . _carla_objects . append ( vehicle )
max_steer_angle = vehicle . get_physics_control ( ) . wheels [ 0 ] . max_steer_angle
max_steer_angle = vehicle . get_physics_control ( ) . wheels [ 0 ] . max_steer_angle
# make tires less slippery
# make tires less slippery
@ -282,16 +325,17 @@ def bridge(q):
blueprint . set_attribute ( ' image_size_x ' , str ( W ) )
blueprint . set_attribute ( ' image_size_x ' , str ( W ) )
blueprint . set_attribute ( ' image_size_y ' , str ( H ) )
blueprint . set_attribute ( ' image_size_y ' , str ( H ) )
blueprint . set_attribute ( ' fov ' , str ( fov ) )
blueprint . set_attribute ( ' fov ' , str ( fov ) )
blueprint . set_attribute ( ' sensor_tick ' , ' 0.05 ' )
if self . _args . low_quality :
blueprint . set_attribute ( ' enable_postprocess_effects ' , ' False ' )
camera = world . spawn_actor ( blueprint , transform , attach_to = vehicle )
camera = world . spawn_actor ( blueprint , transform , attach_to = vehicle )
camera . listen ( callback )
camera . listen ( callback )
return camera
return camera
camerad = Camerad ( )
self . _ camerad = Camerad ( )
road_camera = create_camera ( fov = 40 , callback = camerad . cam_callback_road )
road_camera = create_camera ( fov = 40 , callback = self . _ camerad. cam_callback_road )
road_wide_camera = create_camera ( fov = 163 , callback = camerad . cam_callback_wide_road ) # fov bigger than 163 shows unwanted artifacts
road_wide_camera = create_camera ( fov = 163 , callback = self . _ camerad. cam_callback_wide_road ) # fov bigger than 163 shows unwanted artifacts
cameras = [ road_camera , road_wide_camera ]
self . _carla_objects . extend ( [ road_camera , road_wide_camera ] )
vehicle_state = VehicleState ( )
vehicle_state = VehicleState ( )
@ -304,14 +348,13 @@ def bridge(q):
gps = world . spawn_actor ( gps_bp , transform , attach_to = vehicle )
gps = world . spawn_actor ( gps_bp , transform , attach_to = vehicle )
gps . listen ( lambda gps : gps_callback ( gps , vehicle_state ) )
gps . listen ( lambda gps : gps_callback ( gps , vehicle_state ) )
self . _carla_objects . extend ( [ imu , gps ] )
# launch fake car threads
# launch fake car threads
threads = [ ]
self . _threads . append ( threading . Thread ( target = panda_state_function , args = ( vehicle_state , self . _threads_exit_event , ) ) )
exit_event = threading . Event ( )
self . _threads . append ( threading . Thread ( target = peripheral_state_function , args = ( self . _threads_exit_event , ) ) )
threads . append ( threading . Thread ( target = panda_state_function , args = ( vehicle_state , exit_event , ) ) )
self . _threads . append ( threading . Thread ( target = fake_driver_monitoring , args = ( self . _threads_exit_event , ) ) )
threads . append ( threading . Thread ( target = peripheral_state_function , args = ( exit_event , ) ) )
self . _threads . append ( threading . Thread ( target = can_function_runner , args = ( vehicle_state , self . _threads_exit_event , ) ) )
threads . append ( threading . Thread ( target = fake_driver_monitoring , args = ( exit_event , ) ) )
for t in self . _threads :
threads . append ( threading . Thread ( target = can_function_runner , args = ( vehicle_state , exit_event , ) ) )
for t in threads :
t . start ( )
t . start ( )
# can loop
# can loop
@ -325,16 +368,16 @@ def bridge(q):
vc = carla . VehicleControl ( throttle = 0 , steer = 0 , brake = 0 , reverse = False )
vc = carla . VehicleControl ( throttle = 0 , steer = 0 , brake = 0 , reverse = False )
is_openpilot_engaged = False
is_openpilot_engaged = False
throttle_out = steer_out = brake_out = 0
throttle_out = steer_out = brake_out = 0.
throttle_op = steer_op = brake_op = 0
throttle_op = steer_op = brake_op = 0.
throttle_manual = steer_manual = brake_manual = 0
throttle_manual = steer_manual = brake_manual = 0.
old_steer = old_brake = old_throttle = 0
old_steer = old_brake = old_throttle = 0.
throttle_manual_multiplier = 0.7 # keyboard signal is always 1
throttle_manual_multiplier = 0.7 # keyboard signal is always 1
brake_manual_multiplier = 0.7 # keyboard signal is always 1
brake_manual_multiplier = 0.7 # keyboard signal is always 1
steer_manual_multiplier = 45 * STEER_RATIO # keyboard signal is always 1
steer_manual_multiplier = 45 * STEER_RATIO # keyboard signal is always 1
while True :
while not self . _shutdown :
# 1. Read the throttle, steer and brake from op or manual controls
# 1. Read the throttle, steer and brake from op or manual controls
# 2. Set instructions in Carla
# 2. Set instructions in Carla
# 3. Send current carstate to op via can
# 3. Send current carstate to op via can
@ -445,51 +488,51 @@ def bridge(q):
vehicle_state . is_engaged = is_openpilot_engaged
vehicle_state . is_engaged = is_openpilot_engaged
if rk . frame % PRINT_DECIMATION == 0 :
if rk . frame % PRINT_DECIMATION == 0 :
print ( " frame: " , " engaged: " , is_openpilot_engaged , " ; throttle: " , round ( vc . throttle , 3 ) , " ; steer(c/deg): " , round ( vc . steer , 3 ) , round ( steer_out , 3 ) , " ; brake: " , round ( vc . brake , 3 ) )
print ( " frame: " , " engaged: " , is_openpilot_engaged , " ; throttle: " , round ( vc . throttle , 3 ) , " ; steer(c/deg): " ,
round ( vc . steer , 3 ) , round ( steer_out , 3 ) , " ; brake: " , round ( vc . brake , 3 ) )
if rk . frame % 5 == 0 :
if rk . frame % 5 == 0 :
world . tick ( )
world . tick ( )
rk . keep_time ( )
rk . keep_time ( )
self . started = True
# Clean up resources in the opposite order they were created.
# Clean up resources in the opposite order they were created.
exit_event . set ( )
self . close ( )
for t in reversed ( threads ) :
t . join ( )
gps . destroy ( )
imu . destroy ( )
for c in cameras :
c . destroy ( )
vehicle . destroy ( )
def bridge_keep_alive ( q : Any ) :
def close ( self ) :
while 1 :
self . _threads_exit_event . set ( )
if self . _camerad is not None :
self . _camerad . close ( )
for s in self . _carla_objects :
try :
try :
bridge ( q )
s . destroy ( )
break
except Exception as e :
except RuntimeError as e :
print ( " Failed to destroy carla object " , e )
print ( " Restarting bridge. Error: " , e )
for t in reversed ( self . _threads ) :
t . join ( )
def run ( self , queue , retries = - 1 ) :
unblock_stdout ( ) # Fix error when publishing too many lag message
bridge_p = Process ( target = self . bridge_keep_alive , args = ( queue , retries ) , daemon = True )
bridge_p . start ( )
return bridge_p
if __name__ == " __main__ " :
# make sure params are in a good state
set_params_enabled ( )
msg = messaging . new_message ( ' liveCalibration ' )
msg . liveCalibration . validBlocks = 20
msg . liveCalibration . rpyCalib = [ 0.0 , 0.0 , 0.0 ]
Params ( ) . put ( " CalibrationParams " , msg . to_bytes ( ) )
if __name__ == " __main__ " :
q : Any = Queue ( )
q : Any = Queue ( )
p = Process ( target = bridge_keep_alive , args = ( q , ) , daemon = True )
args = parse_args ( )
p . start ( )
carla_bridge = CarlaBridge ( args )
p = carla_bridge . run ( q )
if args . joystick :
if args . joystick :
# start input poll for joystick
# start input poll for joystick
from lib . manual_ctrl import wheel_poll_thread
from tools . sim . lib . manual_ctrl import wheel_poll_thread
wheel_poll_thread ( q )
wheel_poll_thread ( q )
else :
else :
# start input poll for keyboard
# start input poll for keyboard
from lib . keyboard_ctrl import keyboard_poll_thread
from tools . sim . lib . keyboard_ctrl import keyboard_poll_thread
keyboard_poll_thread ( q )
keyboard_poll_thread ( q )
p . join ( )