Simulator: cleanup in preparation for metadrive (#29903)
* sim bridge cleanup
* fix carla
* remove that exception
* pr cleanup
* update car in a thread
* more cleanup
* import sorting
* handle exits better
old-commit-hash: 7f6718a7cb
test-msgs
parent
109515a208
commit
27cacb51d4
12 changed files with 773 additions and 659 deletions
@ -1,561 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import argparse |
|
||||||
import math |
|
||||||
import os |
|
||||||
import signal |
|
||||||
import threading |
|
||||||
import time |
|
||||||
from multiprocessing import Process, Queue |
|
||||||
from typing import Any |
|
||||||
|
|
||||||
import carla |
|
||||||
import numpy as np |
|
||||||
import pyopencl as cl |
|
||||||
import pyopencl.array as cl_array |
|
||||||
|
|
||||||
import cereal.messaging as messaging |
|
||||||
from cereal import log |
|
||||||
from cereal.visionipc import VisionIpcServer, VisionStreamType |
|
||||||
from openpilot.common.basedir import BASEDIR |
|
||||||
from openpilot.common.numpy_fast import clip |
|
||||||
from openpilot.common.params import Params |
|
||||||
from openpilot.common.realtime import DT_DMON, Ratekeeper |
|
||||||
from openpilot.selfdrive.car.honda.values import CruiseButtons |
|
||||||
from openpilot.selfdrive.test.helpers import set_params_enabled |
|
||||||
from openpilot.tools.sim.lib.can import can_function |
|
||||||
|
|
||||||
W, H = 1928, 1208 |
|
||||||
REPEAT_COUNTER = 5 |
|
||||||
PRINT_DECIMATION = 100 |
|
||||||
STEER_RATIO = 15. |
|
||||||
|
|
||||||
pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'accelerometer', 'gyroscope', 'can', "gpsLocationExternal"]) |
|
||||||
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('--high_quality', action='store_true') |
|
||||||
parser.add_argument('--dual_camera', 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) |
|
||||||
parser.add_argument('--host', dest='host', type=str, default='127.0.0.1') |
|
||||||
parser.add_argument('--port', dest='port', type=int, default=2000) |
|
||||||
|
|
||||||
return parser.parse_args(add_args) |
|
||||||
|
|
||||||
|
|
||||||
class VehicleState: |
|
||||||
def __init__(self): |
|
||||||
self.speed = 0.0 |
|
||||||
self.angle = 0.0 |
|
||||||
self.bearing_deg = 0.0 |
|
||||||
self.vel = carla.Vector3D() |
|
||||||
self.cruise_button = 0 |
|
||||||
self.is_engaged = False |
|
||||||
self.ignition = True |
|
||||||
|
|
||||||
|
|
||||||
def steer_rate_limit(old, new): |
|
||||||
# Rate limiting to 0.5 degrees per step |
|
||||||
limit = 0.5 |
|
||||||
if new > old + limit: |
|
||||||
return old + limit |
|
||||||
elif new < old - limit: |
|
||||||
return old - limit |
|
||||||
else: |
|
||||||
return new |
|
||||||
|
|
||||||
|
|
||||||
class Camerad: |
|
||||||
def __init__(self, dual_camera): |
|
||||||
self.frame_road_id = 0 |
|
||||||
self.frame_wide_id = 0 |
|
||||||
self.vipc_server = VisionIpcServer("camerad") |
|
||||||
|
|
||||||
self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_ROAD, 5, False, W, H) |
|
||||||
if dual_camera: |
|
||||||
self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_WIDE_ROAD, 5, False, W, H) |
|
||||||
self.vipc_server.start_listener() |
|
||||||
|
|
||||||
# set up for pyopencl rgb to yuv conversion |
|
||||||
self.ctx = cl.create_some_context() |
|
||||||
self.queue = cl.CommandQueue(self.ctx) |
|
||||||
cl_arg = f" -DHEIGHT={H} -DWIDTH={W} -DRGB_STRIDE={W * 3} -DUV_WIDTH={W // 2} -DUV_HEIGHT={H // 2} -DRGB_SIZE={W * H} -DCL_DEBUG " |
|
||||||
|
|
||||||
kernel_fn = os.path.join(BASEDIR, "tools/sim/rgb_to_nv12.cl") |
|
||||||
with open(kernel_fn) as f: |
|
||||||
prg = cl.Program(self.ctx, f.read()).build(cl_arg) |
|
||||||
self.krnl = prg.rgb_to_nv12 |
|
||||||
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 |
|
||||||
|
|
||||||
def cam_callback_road(self, image): |
|
||||||
self._cam_callback(image, self.frame_road_id, 'roadCameraState', VisionStreamType.VISION_STREAM_ROAD) |
|
||||||
self.frame_road_id += 1 |
|
||||||
|
|
||||||
def cam_callback_wide_road(self, image): |
|
||||||
self._cam_callback(image, self.frame_wide_id, 'wideRoadCameraState', VisionStreamType.VISION_STREAM_WIDE_ROAD) |
|
||||||
self.frame_wide_id += 1 |
|
||||||
|
|
||||||
def _cam_callback(self, image, frame_id, pub_type, yuv_type): |
|
||||||
img = np.frombuffer(image.raw_data, dtype=np.dtype("uint8")) |
|
||||||
img = np.reshape(img, (H, W, 4)) |
|
||||||
img = img[:, :, [0, 1, 2]].copy() |
|
||||||
|
|
||||||
# convert RGB frame to YUV |
|
||||||
rgb = np.reshape(img, (H, W * 3)) |
|
||||||
rgb_cl = cl_array.to_device(self.queue, rgb) |
|
||||||
yuv_cl = cl_array.empty_like(rgb_cl) |
|
||||||
self.krnl(self.queue, (np.int32(self.Wdiv4), np.int32(self.Hdiv4)), None, rgb_cl.data, yuv_cl.data).wait() |
|
||||||
yuv = np.resize(yuv_cl.get(), rgb.size // 2) |
|
||||||
eof = int(frame_id * 0.05 * 1e9) |
|
||||||
|
|
||||||
self.vipc_server.send(yuv_type, yuv.data.tobytes(), frame_id, eof, eof) |
|
||||||
|
|
||||||
dat = messaging.new_message(pub_type) |
|
||||||
msg = { |
|
||||||
"frameId": frame_id, |
|
||||||
"transform": [1.0, 0.0, 0.0, |
|
||||||
0.0, 1.0, 0.0, |
|
||||||
0.0, 0.0, 1.0] |
|
||||||
} |
|
||||||
setattr(dat, pub_type, msg) |
|
||||||
pm.send(pub_type, dat) |
|
||||||
|
|
||||||
def imu_callback(imu, vehicle_state): |
|
||||||
# send 5x since 'sensor_tick' doesn't seem to work. limited by the world tick? |
|
||||||
for _ in range(5): |
|
||||||
vehicle_state.bearing_deg = math.degrees(imu.compass) |
|
||||||
dat = messaging.new_message('accelerometer') |
|
||||||
dat.accelerometer.sensor = 4 |
|
||||||
dat.accelerometer.type = 0x10 |
|
||||||
dat.accelerometer.timestamp = dat.logMonoTime # TODO: use the IMU timestamp |
|
||||||
dat.accelerometer.init('acceleration') |
|
||||||
dat.accelerometer.acceleration.v = [imu.accelerometer.x, imu.accelerometer.y, imu.accelerometer.z] |
|
||||||
pm.send('accelerometer', dat) |
|
||||||
|
|
||||||
# copied these numbers from locationd |
|
||||||
dat = messaging.new_message('gyroscope') |
|
||||||
dat.gyroscope.sensor = 5 |
|
||||||
dat.gyroscope.type = 0x10 |
|
||||||
dat.gyroscope.timestamp = dat.logMonoTime # TODO: use the IMU timestamp |
|
||||||
dat.gyroscope.init('gyroUncalibrated') |
|
||||||
dat.gyroscope.gyroUncalibrated.v = [imu.gyroscope.x, imu.gyroscope.y, imu.gyroscope.z] |
|
||||||
pm.send('gyroscope', dat) |
|
||||||
time.sleep(0.01) |
|
||||||
|
|
||||||
|
|
||||||
def panda_state_function(vs: VehicleState, exit_event: threading.Event): |
|
||||||
pm = messaging.PubMaster(['pandaStates']) |
|
||||||
while not exit_event.is_set(): |
|
||||||
dat = messaging.new_message('pandaStates', 1) |
|
||||||
dat.valid = True |
|
||||||
dat.pandaStates[0] = { |
|
||||||
'ignitionLine': vs.ignition, |
|
||||||
'pandaType': "blackPanda", |
|
||||||
'controlsAllowed': True, |
|
||||||
'safetyModel': 'hondaNidec' |
|
||||||
} |
|
||||||
pm.send('pandaStates', dat) |
|
||||||
time.sleep(0.5) |
|
||||||
|
|
||||||
|
|
||||||
def peripheral_state_function(exit_event: threading.Event): |
|
||||||
pm = messaging.PubMaster(['peripheralState']) |
|
||||||
while not exit_event.is_set(): |
|
||||||
dat = messaging.new_message('peripheralState') |
|
||||||
dat.valid = True |
|
||||||
# fake peripheral state data |
|
||||||
dat.peripheralState = { |
|
||||||
'pandaType': log.PandaState.PandaType.blackPanda, |
|
||||||
'voltage': 12000, |
|
||||||
'current': 5678, |
|
||||||
'fanSpeedRpm': 1000 |
|
||||||
} |
|
||||||
pm.send('peripheralState', dat) |
|
||||||
time.sleep(0.5) |
|
||||||
|
|
||||||
|
|
||||||
def gps_callback(gps, vehicle_state): |
|
||||||
dat = messaging.new_message('gpsLocationExternal') |
|
||||||
|
|
||||||
# transform vel from carla to NED |
|
||||||
# north is -Y in CARLA |
|
||||||
velNED = [ |
|
||||||
-vehicle_state.vel.y, # north/south component of NED is negative when moving south |
|
||||||
vehicle_state.vel.x, # positive when moving east, which is x in carla |
|
||||||
vehicle_state.vel.z, |
|
||||||
] |
|
||||||
|
|
||||||
dat.gpsLocationExternal = { |
|
||||||
"unixTimestampMillis": int(time.time() * 1000), |
|
||||||
"flags": 1, # valid fix |
|
||||||
"accuracy": 1.0, |
|
||||||
"verticalAccuracy": 1.0, |
|
||||||
"speedAccuracy": 0.1, |
|
||||||
"bearingAccuracyDeg": 0.1, |
|
||||||
"vNED": velNED, |
|
||||||
"bearingDeg": vehicle_state.bearing_deg, |
|
||||||
"latitude": gps.latitude, |
|
||||||
"longitude": gps.longitude, |
|
||||||
"altitude": gps.altitude, |
|
||||||
"speed": vehicle_state.speed, |
|
||||||
"source": log.GpsLocationData.SensorSource.ublox, |
|
||||||
} |
|
||||||
|
|
||||||
pm.send('gpsLocationExternal', dat) |
|
||||||
|
|
||||||
|
|
||||||
def fake_driver_monitoring(exit_event: threading.Event): |
|
||||||
pm = messaging.PubMaster(['driverStateV2', 'driverMonitoringState']) |
|
||||||
while not exit_event.is_set(): |
|
||||||
# dmonitoringmodeld output |
|
||||||
dat = messaging.new_message('driverStateV2') |
|
||||||
dat.driverStateV2.leftDriverData.faceOrientation = [0., 0., 0.] |
|
||||||
dat.driverStateV2.leftDriverData.faceProb = 1.0 |
|
||||||
dat.driverStateV2.rightDriverData.faceOrientation = [0., 0., 0.] |
|
||||||
dat.driverStateV2.rightDriverData.faceProb = 1.0 |
|
||||||
pm.send('driverStateV2', dat) |
|
||||||
|
|
||||||
# dmonitoringd output |
|
||||||
dat = messaging.new_message('driverMonitoringState') |
|
||||||
dat.driverMonitoringState = { |
|
||||||
"faceDetected": True, |
|
||||||
"isDistracted": False, |
|
||||||
"awarenessStatus": 1., |
|
||||||
} |
|
||||||
pm.send('driverMonitoringState', dat) |
|
||||||
|
|
||||||
time.sleep(DT_DMON) |
|
||||||
|
|
||||||
|
|
||||||
def can_function_runner(vs: VehicleState, exit_event: threading.Event): |
|
||||||
i = 0 |
|
||||||
while not exit_event.is_set(): |
|
||||||
can_function(pm, vs.speed, vs.angle, i, vs.cruise_button, vs.is_engaged) |
|
||||||
time.sleep(0.01) |
|
||||||
i += 1 |
|
||||||
|
|
||||||
|
|
||||||
def connect_carla_client(host: str, port: int): |
|
||||||
client = carla.Client(host, port) |
|
||||||
client.set_timeout(5) |
|
||||||
return client |
|
||||||
|
|
||||||
|
|
||||||
class CarlaBridge: |
|
||||||
|
|
||||||
def __init__(self, arguments): |
|
||||||
set_params_enabled() |
|
||||||
|
|
||||||
self.params = Params() |
|
||||||
|
|
||||||
msg = messaging.new_message('liveCalibration') |
|
||||||
msg.liveCalibration.validBlocks = 20 |
|
||||||
msg.liveCalibration.rpyCalib = [0.0, 0.0, 0.0] |
|
||||||
self.params.put("CalibrationParams", msg.to_bytes()) |
|
||||||
self.params.put_bool("DisengageOnAccelerator", True) |
|
||||||
|
|
||||||
self._args = arguments |
|
||||||
self._carla_objects = [] |
|
||||||
self._camerad = None |
|
||||||
self._exit_event = threading.Event() |
|
||||||
self._threads = [] |
|
||||||
self._keep_alive = True |
|
||||||
self.started = False |
|
||||||
signal.signal(signal.SIGTERM, self._on_shutdown) |
|
||||||
self._exit = threading.Event() |
|
||||||
|
|
||||||
def _on_shutdown(self, signal, frame): |
|
||||||
self._keep_alive = False |
|
||||||
|
|
||||||
def bridge_keep_alive(self, q: Queue, retries: int): |
|
||||||
try: |
|
||||||
while self._keep_alive: |
|
||||||
try: |
|
||||||
self._run(q) |
|
||||||
break |
|
||||||
except RuntimeError as e: |
|
||||||
self.close() |
|
||||||
if retries == 0: |
|
||||||
raise |
|
||||||
|
|
||||||
# Reset for another try |
|
||||||
self._carla_objects = [] |
|
||||||
self._threads = [] |
|
||||||
self._exit_event = threading.Event() |
|
||||||
|
|
||||||
retries -= 1 |
|
||||||
if retries <= -1: |
|
||||||
print(f"Restarting bridge. Error: {e} ") |
|
||||||
else: |
|
||||||
print(f"Restarting bridge. Retries left {retries}. Error: {e} ") |
|
||||||
finally: |
|
||||||
# Clean up resources in the opposite order they were created. |
|
||||||
self.close() |
|
||||||
|
|
||||||
def _run(self, q: Queue): |
|
||||||
client = connect_carla_client(self._args.host, self._args.port) |
|
||||||
world = client.load_world(self._args.town) |
|
||||||
|
|
||||||
settings = world.get_settings() |
|
||||||
settings.synchronous_mode = True # Enables synchronous mode |
|
||||||
settings.fixed_delta_seconds = 0.05 |
|
||||||
world.apply_settings(settings) |
|
||||||
|
|
||||||
world.set_weather(carla.WeatherParameters.ClearSunset) |
|
||||||
|
|
||||||
if not self._args.high_quality: |
|
||||||
world.unload_map_layer(carla.MapLayer.Foliage) |
|
||||||
world.unload_map_layer(carla.MapLayer.Buildings) |
|
||||||
world.unload_map_layer(carla.MapLayer.ParkedVehicles) |
|
||||||
world.unload_map_layer(carla.MapLayer.Props) |
|
||||||
world.unload_map_layer(carla.MapLayer.StreetLights) |
|
||||||
world.unload_map_layer(carla.MapLayer.Particles) |
|
||||||
|
|
||||||
blueprint_library = world.get_blueprint_library() |
|
||||||
|
|
||||||
world_map = world.get_map() |
|
||||||
|
|
||||||
vehicle_bp = blueprint_library.filter('vehicle.tesla.*')[1] |
|
||||||
vehicle_bp.set_attribute('role_name', 'hero') |
|
||||||
spawn_points = world_map.get_spawn_points() |
|
||||||
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 |
|
||||||
{len(spawn_points)} for this town.''' |
|
||||||
spawn_point = spawn_points[self._args.num_selected_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 |
|
||||||
|
|
||||||
# make tires less slippery |
|
||||||
# wheel_control = carla.WheelPhysicsControl(tire_friction=5) |
|
||||||
physics_control = vehicle.get_physics_control() |
|
||||||
physics_control.mass = 2326 |
|
||||||
# physics_control.wheels = [wheel_control]*4 |
|
||||||
physics_control.torque_curve = [[20.0, 500.0], [5000.0, 500.0]] |
|
||||||
physics_control.gear_switch_time = 0.0 |
|
||||||
vehicle.apply_physics_control(physics_control) |
|
||||||
|
|
||||||
transform = carla.Transform(carla.Location(x=0.8, z=1.13)) |
|
||||||
|
|
||||||
def create_camera(fov, callback): |
|
||||||
blueprint = blueprint_library.find('sensor.camera.rgb') |
|
||||||
blueprint.set_attribute('image_size_x', str(W)) |
|
||||||
blueprint.set_attribute('image_size_y', str(H)) |
|
||||||
blueprint.set_attribute('fov', str(fov)) |
|
||||||
if not self._args.high_quality: |
|
||||||
blueprint.set_attribute('enable_postprocess_effects', 'False') |
|
||||||
camera = world.spawn_actor(blueprint, transform, attach_to=vehicle) |
|
||||||
camera.listen(callback) |
|
||||||
return camera |
|
||||||
|
|
||||||
self._camerad = Camerad(self._args.dual_camera) |
|
||||||
|
|
||||||
if self._args.dual_camera: |
|
||||||
road_wide_camera = create_camera(fov=120, callback=self._camerad.cam_callback_wide_road) # fov bigger than 120 shows unwanted artifacts |
|
||||||
self._carla_objects.append(road_wide_camera) |
|
||||||
road_camera = create_camera(fov=40, callback=self._camerad.cam_callback_road) |
|
||||||
self._carla_objects.append(road_camera) |
|
||||||
|
|
||||||
vehicle_state = VehicleState() |
|
||||||
|
|
||||||
# re-enable IMU |
|
||||||
imu_bp = blueprint_library.find('sensor.other.imu') |
|
||||||
imu_bp.set_attribute('sensor_tick', '0.01') |
|
||||||
imu = world.spawn_actor(imu_bp, transform, attach_to=vehicle) |
|
||||||
imu.listen(lambda imu: imu_callback(imu, vehicle_state)) |
|
||||||
|
|
||||||
gps_bp = blueprint_library.find('sensor.other.gnss') |
|
||||||
gps = world.spawn_actor(gps_bp, transform, attach_to=vehicle) |
|
||||||
gps.listen(lambda gps: gps_callback(gps, vehicle_state)) |
|
||||||
self.params.put_bool("UbloxAvailable", True) |
|
||||||
|
|
||||||
self._carla_objects.extend([imu, gps]) |
|
||||||
# launch fake car threads |
|
||||||
self._threads.append(threading.Thread(target=panda_state_function, args=(vehicle_state, self._exit_event,))) |
|
||||||
self._threads.append(threading.Thread(target=peripheral_state_function, args=(self._exit_event,))) |
|
||||||
self._threads.append(threading.Thread(target=fake_driver_monitoring, args=(self._exit_event,))) |
|
||||||
self._threads.append(threading.Thread(target=can_function_runner, args=(vehicle_state, self._exit_event,))) |
|
||||||
for t in self._threads: |
|
||||||
t.start() |
|
||||||
|
|
||||||
# init |
|
||||||
throttle_ease_out_counter = REPEAT_COUNTER |
|
||||||
brake_ease_out_counter = REPEAT_COUNTER |
|
||||||
steer_ease_out_counter = REPEAT_COUNTER |
|
||||||
|
|
||||||
vc = carla.VehicleControl(throttle=0, steer=0, brake=0, reverse=False) |
|
||||||
|
|
||||||
is_openpilot_engaged = False |
|
||||||
throttle_out = steer_out = brake_out = 0. |
|
||||||
throttle_op = steer_op = brake_op = 0. |
|
||||||
throttle_manual = steer_manual = brake_manual = 0. |
|
||||||
|
|
||||||
old_steer = old_brake = old_throttle = 0. |
|
||||||
throttle_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 |
|
||||||
|
|
||||||
# Simulation tends to be slow in the initial steps. This prevents lagging later |
|
||||||
for _ in range(20): |
|
||||||
world.tick() |
|
||||||
|
|
||||||
# loop |
|
||||||
rk = Ratekeeper(100, print_delay_threshold=0.05) |
|
||||||
|
|
||||||
while self._keep_alive: |
|
||||||
# 1. Read the throttle, steer and brake from op or manual controls |
|
||||||
# 2. Set instructions in Carla |
|
||||||
# 3. Send current carstate to op via can |
|
||||||
|
|
||||||
cruise_button = 0 |
|
||||||
throttle_out = steer_out = brake_out = 0.0 |
|
||||||
throttle_op = steer_op = brake_op = 0.0 |
|
||||||
throttle_manual = steer_manual = brake_manual = 0.0 |
|
||||||
|
|
||||||
# --------------Step 1------------------------------- |
|
||||||
if not q.empty(): |
|
||||||
message = q.get() |
|
||||||
m = message.split('_') |
|
||||||
if m[0] == "steer": |
|
||||||
steer_manual = float(m[1]) |
|
||||||
is_openpilot_engaged = False |
|
||||||
elif m[0] == "throttle": |
|
||||||
throttle_manual = float(m[1]) |
|
||||||
is_openpilot_engaged = False |
|
||||||
elif m[0] == "brake": |
|
||||||
brake_manual = float(m[1]) |
|
||||||
is_openpilot_engaged = False |
|
||||||
elif m[0] == "reverse": |
|
||||||
cruise_button = CruiseButtons.CANCEL |
|
||||||
is_openpilot_engaged = False |
|
||||||
elif m[0] == "cruise": |
|
||||||
if m[1] == "down": |
|
||||||
cruise_button = CruiseButtons.DECEL_SET |
|
||||||
is_openpilot_engaged = True |
|
||||||
elif m[1] == "up": |
|
||||||
cruise_button = CruiseButtons.RES_ACCEL |
|
||||||
is_openpilot_engaged = True |
|
||||||
elif m[1] == "cancel": |
|
||||||
cruise_button = CruiseButtons.CANCEL |
|
||||||
is_openpilot_engaged = False |
|
||||||
elif m[0] == "ignition": |
|
||||||
vehicle_state.ignition = not vehicle_state.ignition |
|
||||||
elif m[0] == "quit": |
|
||||||
break |
|
||||||
|
|
||||||
throttle_out = throttle_manual * throttle_manual_multiplier |
|
||||||
steer_out = steer_manual * steer_manual_multiplier |
|
||||||
brake_out = brake_manual * brake_manual_multiplier |
|
||||||
|
|
||||||
old_steer = steer_out |
|
||||||
old_throttle = throttle_out |
|
||||||
old_brake = brake_out |
|
||||||
|
|
||||||
if is_openpilot_engaged: |
|
||||||
sm.update(0) |
|
||||||
|
|
||||||
# TODO gas and brake is deprecated |
|
||||||
throttle_op = clip(sm['carControl'].actuators.accel / 1.6, 0.0, 1.0) |
|
||||||
brake_op = clip(-sm['carControl'].actuators.accel / 4.0, 0.0, 1.0) |
|
||||||
steer_op = sm['carControl'].actuators.steeringAngleDeg |
|
||||||
|
|
||||||
throttle_out = throttle_op |
|
||||||
steer_out = steer_op |
|
||||||
brake_out = brake_op |
|
||||||
|
|
||||||
steer_out = steer_rate_limit(old_steer, steer_out) |
|
||||||
old_steer = steer_out |
|
||||||
|
|
||||||
else: |
|
||||||
if throttle_out == 0 and old_throttle > 0: |
|
||||||
if throttle_ease_out_counter > 0: |
|
||||||
throttle_out = old_throttle |
|
||||||
throttle_ease_out_counter += -1 |
|
||||||
else: |
|
||||||
throttle_ease_out_counter = REPEAT_COUNTER |
|
||||||
old_throttle = 0 |
|
||||||
|
|
||||||
if brake_out == 0 and old_brake > 0: |
|
||||||
if brake_ease_out_counter > 0: |
|
||||||
brake_out = old_brake |
|
||||||
brake_ease_out_counter += -1 |
|
||||||
else: |
|
||||||
brake_ease_out_counter = REPEAT_COUNTER |
|
||||||
old_brake = 0 |
|
||||||
|
|
||||||
if steer_out == 0 and old_steer != 0: |
|
||||||
if steer_ease_out_counter > 0: |
|
||||||
steer_out = old_steer |
|
||||||
steer_ease_out_counter += -1 |
|
||||||
else: |
|
||||||
steer_ease_out_counter = REPEAT_COUNTER |
|
||||||
old_steer = 0 |
|
||||||
|
|
||||||
# --------------Step 2------------------------------- |
|
||||||
steer_carla = steer_out / (max_steer_angle * STEER_RATIO * -1) |
|
||||||
|
|
||||||
steer_carla = np.clip(steer_carla, -1, 1) |
|
||||||
steer_out = steer_carla * (max_steer_angle * STEER_RATIO * -1) |
|
||||||
old_steer = steer_carla * (max_steer_angle * STEER_RATIO * -1) |
|
||||||
|
|
||||||
vc.throttle = throttle_out / 0.6 |
|
||||||
vc.steer = steer_carla |
|
||||||
vc.brake = brake_out |
|
||||||
vehicle.apply_control(vc) |
|
||||||
|
|
||||||
# --------------Step 3------------------------------- |
|
||||||
vel = vehicle.get_velocity() |
|
||||||
speed = math.sqrt(vel.x ** 2 + vel.y ** 2 + vel.z ** 2) # in m/s |
|
||||||
vehicle_state.speed = speed |
|
||||||
vehicle_state.vel = vel |
|
||||||
vehicle_state.angle = steer_out |
|
||||||
vehicle_state.cruise_button = cruise_button |
|
||||||
vehicle_state.is_engaged = is_openpilot_engaged |
|
||||||
|
|
||||||
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)) |
|
||||||
|
|
||||||
if rk.frame % 5 == 0: |
|
||||||
world.tick() |
|
||||||
rk.keep_time() |
|
||||||
self.started = True |
|
||||||
|
|
||||||
def close(self): |
|
||||||
self.started = False |
|
||||||
self._exit_event.set() |
|
||||||
|
|
||||||
for s in self._carla_objects: |
|
||||||
try: |
|
||||||
s.destroy() |
|
||||||
except Exception as e: |
|
||||||
print("Failed to destroy carla object", e) |
|
||||||
for t in reversed(self._threads): |
|
||||||
t.join() |
|
||||||
|
|
||||||
def run(self, queue, retries=-1): |
|
||||||
bridge_p = Process(target=self.bridge_keep_alive, args=(queue, retries), daemon=True) |
|
||||||
bridge_p.start() |
|
||||||
return bridge_p |
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
q: Any = Queue() |
|
||||||
args = parse_args() |
|
||||||
|
|
||||||
carla_bridge = CarlaBridge(args) |
|
||||||
p = carla_bridge.run(q) |
|
||||||
|
|
||||||
if args.joystick: |
|
||||||
# start input poll for joystick |
|
||||||
from openpilot.tools.sim.lib.manual_ctrl import wheel_poll_thread |
|
||||||
|
|
||||||
wheel_poll_thread(q) |
|
||||||
else: |
|
||||||
# start input poll for keyboard |
|
||||||
from openpilot.tools.sim.lib.keyboard_ctrl import keyboard_poll_thread |
|
||||||
|
|
||||||
keyboard_poll_thread(q) |
|
||||||
p.join() |
|
@ -0,0 +1,163 @@ |
|||||||
|
import numpy as np |
||||||
|
|
||||||
|
from openpilot.common.params import Params |
||||||
|
from openpilot.tools.sim.lib.common import SimulatorState, vec3 |
||||||
|
from openpilot.tools.sim.bridge.common import World, SimulatorBridge |
||||||
|
from openpilot.tools.sim.lib.camerad import W, H |
||||||
|
|
||||||
|
|
||||||
|
class CarlaWorld(World): |
||||||
|
def __init__(self, world, vehicle, high_quality=False, dual_camera=False): |
||||||
|
super().__init__(dual_camera) |
||||||
|
import carla |
||||||
|
self.world = world |
||||||
|
self.vc: carla.VehicleControl = carla.VehicleControl(throttle=0, steer=0, brake=0, reverse=False) |
||||||
|
self.vehicle = vehicle |
||||||
|
self.max_steer_angle: float = vehicle.get_physics_control().wheels[0].max_steer_angle |
||||||
|
self.params = Params() |
||||||
|
|
||||||
|
self.steer_ratio = 15 |
||||||
|
|
||||||
|
self.carla_objects = [] |
||||||
|
|
||||||
|
blueprint_library = self.world.get_blueprint_library() |
||||||
|
transform = carla.Transform(carla.Location(x=0.8, z=1.13)) |
||||||
|
|
||||||
|
def create_camera(fov, callback): |
||||||
|
blueprint = blueprint_library.find('sensor.camera.rgb') |
||||||
|
blueprint.set_attribute('image_size_x', str(W)) |
||||||
|
blueprint.set_attribute('image_size_y', str(H)) |
||||||
|
blueprint.set_attribute('fov', str(fov)) |
||||||
|
blueprint.set_attribute('sensor_tick', str(1/20)) |
||||||
|
if not high_quality: |
||||||
|
blueprint.set_attribute('enable_postprocess_effects', 'False') |
||||||
|
camera = world.spawn_actor(blueprint, transform, attach_to=vehicle) |
||||||
|
camera.listen(callback) |
||||||
|
return camera |
||||||
|
|
||||||
|
self.road_camera = create_camera(fov=40, callback=self.cam_callback_road) |
||||||
|
if dual_camera: |
||||||
|
self.road_wide_camera = create_camera(fov=120, callback=self.cam_callback_wide_road) # fov bigger than 120 shows unwanted artifacts |
||||||
|
else: |
||||||
|
self.road_wide_camera = None |
||||||
|
|
||||||
|
# re-enable IMU |
||||||
|
imu_bp = blueprint_library.find('sensor.other.imu') |
||||||
|
imu_bp.set_attribute('sensor_tick', '0.01') |
||||||
|
self.imu = world.spawn_actor(imu_bp, transform, attach_to=vehicle) |
||||||
|
|
||||||
|
gps_bp = blueprint_library.find('sensor.other.gnss') |
||||||
|
self.gps = world.spawn_actor(gps_bp, transform, attach_to=vehicle) |
||||||
|
self.params.put_bool("UbloxAvailable", True) |
||||||
|
|
||||||
|
self.carla_objects = [self.imu, self.gps, self.road_camera, self.road_wide_camera] |
||||||
|
|
||||||
|
def close(self): |
||||||
|
for s in self.carla_objects: |
||||||
|
if s is not None: |
||||||
|
try: |
||||||
|
s.destroy() |
||||||
|
except Exception as e: |
||||||
|
print("Failed to destroy carla object", e) |
||||||
|
|
||||||
|
def carla_image_to_rgb(self, image): |
||||||
|
rgb = np.frombuffer(image.raw_data, dtype=np.dtype("uint8")) |
||||||
|
rgb = np.reshape(rgb, (H, W, 4)) |
||||||
|
return np.ascontiguousarray(rgb[:, :, [0, 1, 2]]) |
||||||
|
|
||||||
|
def cam_callback_road(self, image): |
||||||
|
with self.image_lock: |
||||||
|
self.road_image = self.carla_image_to_rgb(image) |
||||||
|
|
||||||
|
def cam_callback_wide_road(self, image): |
||||||
|
with self.image_lock: |
||||||
|
self.wide_road_image = self.carla_image_to_rgb(image) |
||||||
|
|
||||||
|
def apply_controls(self, steer_angle, throttle_out, brake_out): |
||||||
|
self.vc.throttle = throttle_out |
||||||
|
|
||||||
|
steer_carla = steer_angle * -1 / (self.max_steer_angle * self.steer_ratio) |
||||||
|
steer_carla = np.clip(steer_carla, -1, 1) |
||||||
|
|
||||||
|
self.vc.steer = steer_carla |
||||||
|
self.vc.brake = brake_out |
||||||
|
self.vehicle.apply_control(self.vc) |
||||||
|
|
||||||
|
def read_sensors(self, simulator_state: SimulatorState): |
||||||
|
simulator_state.imu.bearing = self.imu.get_transform().rotation.yaw |
||||||
|
|
||||||
|
simulator_state.imu.accelerometer = vec3( |
||||||
|
self.imu.get_acceleration().x, |
||||||
|
self.imu.get_acceleration().y, |
||||||
|
self.imu.get_acceleration().z |
||||||
|
) |
||||||
|
|
||||||
|
simulator_state.imu.gyroscope = vec3( |
||||||
|
self.imu.get_angular_velocity().x, |
||||||
|
self.imu.get_angular_velocity().y, |
||||||
|
self.imu.get_angular_velocity().z |
||||||
|
) |
||||||
|
|
||||||
|
simulator_state.gps.from_xy([self.vehicle.get_location().x, self.vehicle.get_location().y]) |
||||||
|
|
||||||
|
simulator_state.velocity = self.vehicle.get_velocity() |
||||||
|
simulator_state.valid = True |
||||||
|
simulator_state.steering_angle = self.vc.steer * self.max_steer_angle |
||||||
|
|
||||||
|
def read_cameras(self): |
||||||
|
pass # cameras are read within a callback for carla |
||||||
|
|
||||||
|
def tick(self): |
||||||
|
self.world.tick() |
||||||
|
|
||||||
|
|
||||||
|
class CarlaBridge(SimulatorBridge): |
||||||
|
TICKS_PER_FRAME = 5 |
||||||
|
|
||||||
|
def __init__(self, arguments): |
||||||
|
super().__init__(arguments) |
||||||
|
self.host = arguments.host |
||||||
|
self.port = arguments.port |
||||||
|
self.town = arguments.town |
||||||
|
self.num_selected_spawn_point = arguments.num_selected_spawn_point |
||||||
|
|
||||||
|
def spawn_world(self): |
||||||
|
import carla |
||||||
|
client = carla.Client(self.host, self.port) |
||||||
|
client.set_timeout(5) |
||||||
|
|
||||||
|
world = client.load_world(self.town) |
||||||
|
|
||||||
|
settings = world.get_settings() |
||||||
|
settings.fixed_delta_seconds = 0.01 |
||||||
|
world.apply_settings(settings) |
||||||
|
|
||||||
|
world.set_weather(carla.WeatherParameters.ClearSunset) |
||||||
|
|
||||||
|
if not self.high_quality: |
||||||
|
world.unload_map_layer(carla.MapLayer.Foliage) |
||||||
|
world.unload_map_layer(carla.MapLayer.Buildings) |
||||||
|
world.unload_map_layer(carla.MapLayer.ParkedVehicles) |
||||||
|
world.unload_map_layer(carla.MapLayer.Props) |
||||||
|
world.unload_map_layer(carla.MapLayer.StreetLights) |
||||||
|
world.unload_map_layer(carla.MapLayer.Particles) |
||||||
|
|
||||||
|
blueprint_library = world.get_blueprint_library() |
||||||
|
|
||||||
|
world_map = world.get_map() |
||||||
|
|
||||||
|
vehicle_bp = blueprint_library.filter('vehicle.tesla.*')[1] |
||||||
|
vehicle_bp.set_attribute('role_name', 'hero') |
||||||
|
spawn_points = world_map.get_spawn_points() |
||||||
|
assert len(spawn_points) > self.num_selected_spawn_point, \ |
||||||
|
f'''No spawn point {self.num_selected_spawn_point}, try a value between 0 and {len(spawn_points)} for this town.''' |
||||||
|
spawn_point = spawn_points[self.num_selected_spawn_point] |
||||||
|
vehicle = world.spawn_actor(vehicle_bp, spawn_point) |
||||||
|
|
||||||
|
physics_control = vehicle.get_physics_control() |
||||||
|
physics_control.mass = 2326 |
||||||
|
physics_control.torque_curve = [[20.0, 500.0], [5000.0, 500.0]] |
||||||
|
physics_control.gear_switch_time = 0.0 |
||||||
|
vehicle.apply_physics_control(physics_control) |
||||||
|
|
||||||
|
return CarlaWorld(world, vehicle, dual_camera=self.dual_camera) |
@ -0,0 +1,163 @@ |
|||||||
|
import signal |
||||||
|
import threading |
||||||
|
import functools |
||||||
|
|
||||||
|
from multiprocessing import Process, Queue |
||||||
|
from abc import ABC, abstractmethod |
||||||
|
from typing import Optional |
||||||
|
|
||||||
|
import cereal.messaging as messaging |
||||||
|
|
||||||
|
from openpilot.common.params import Params |
||||||
|
from openpilot.common.numpy_fast import clip |
||||||
|
from openpilot.common.realtime import Ratekeeper |
||||||
|
from openpilot.selfdrive.test.helpers import set_params_enabled |
||||||
|
from openpilot.selfdrive.car.honda.values import CruiseButtons |
||||||
|
from openpilot.tools.sim.lib.common import SimulatorState, World |
||||||
|
from openpilot.tools.sim.lib.simulated_car import SimulatedCar |
||||||
|
from openpilot.tools.sim.lib.simulated_sensors import SimulatedSensors |
||||||
|
|
||||||
|
|
||||||
|
def rk_loop(function, hz, exit_event: threading.Event): |
||||||
|
rk = Ratekeeper(hz) |
||||||
|
while not exit_event.is_set(): |
||||||
|
function() |
||||||
|
rk.keep_time() |
||||||
|
|
||||||
|
|
||||||
|
class SimulatorBridge(ABC): |
||||||
|
TICKS_PER_FRAME = 5 |
||||||
|
|
||||||
|
def __init__(self, arguments): |
||||||
|
set_params_enabled() |
||||||
|
self.params = Params() |
||||||
|
|
||||||
|
self.rk = Ratekeeper(100) |
||||||
|
|
||||||
|
msg = messaging.new_message('liveCalibration') |
||||||
|
msg.liveCalibration.validBlocks = 20 |
||||||
|
msg.liveCalibration.rpyCalib = [0.0, 0.0, 0.0] |
||||||
|
self.params.put("CalibrationParams", msg.to_bytes()) |
||||||
|
|
||||||
|
self.dual_camera = arguments.dual_camera |
||||||
|
self.high_quality = arguments.high_quality |
||||||
|
|
||||||
|
self._exit_event = threading.Event() |
||||||
|
self._threads = [] |
||||||
|
self._keep_alive = True |
||||||
|
self.started = False |
||||||
|
signal.signal(signal.SIGTERM, self._on_shutdown) |
||||||
|
self._exit = threading.Event() |
||||||
|
self.simulator_state = SimulatorState() |
||||||
|
|
||||||
|
self.world: Optional[World] = None |
||||||
|
|
||||||
|
def _on_shutdown(self, signal, frame): |
||||||
|
self.shutdown() |
||||||
|
|
||||||
|
def shutdown(self): |
||||||
|
self._keep_alive = False |
||||||
|
|
||||||
|
def bridge_keep_alive(self, q: Queue, retries: int): |
||||||
|
try: |
||||||
|
self._run(q) |
||||||
|
finally: |
||||||
|
self.close() |
||||||
|
|
||||||
|
def close(self): |
||||||
|
self.started = False |
||||||
|
self._exit_event.set() |
||||||
|
|
||||||
|
if self.world is not None: |
||||||
|
self.world.close() |
||||||
|
|
||||||
|
def run(self, queue, retries=-1): |
||||||
|
bridge_p = Process(target=self.bridge_keep_alive, args=(queue, retries), daemon=True) |
||||||
|
bridge_p.start() |
||||||
|
return bridge_p |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def spawn_world(self) -> World: |
||||||
|
pass |
||||||
|
|
||||||
|
def _run(self, q: Queue): |
||||||
|
self.world = self.spawn_world() |
||||||
|
|
||||||
|
self.simulated_car = SimulatedCar() |
||||||
|
self.simulated_sensors = SimulatedSensors(self.dual_camera) |
||||||
|
|
||||||
|
self.simulated_car_thread = threading.Thread(target=rk_loop, args=(functools.partial(self.simulated_car.update, self.simulator_state), |
||||||
|
100, self._exit_event)) |
||||||
|
self.simulated_car_thread.start() |
||||||
|
|
||||||
|
rk = Ratekeeper(100, print_delay_threshold=None) |
||||||
|
|
||||||
|
# Simulation tends to be slow in the initial steps. This prevents lagging later |
||||||
|
for _ in range(20): |
||||||
|
self.world.tick() |
||||||
|
|
||||||
|
throttle_manual = steer_manual = brake_manual = 0. |
||||||
|
|
||||||
|
while self._keep_alive: |
||||||
|
throttle_out = steer_out = brake_out = 0.0 |
||||||
|
throttle_op = steer_op = brake_op = 0.0 |
||||||
|
|
||||||
|
self.simulator_state.cruise_button = 0 |
||||||
|
|
||||||
|
throttle_manual = steer_manual = brake_manual = 0. |
||||||
|
|
||||||
|
# Read manual controls |
||||||
|
if not q.empty(): |
||||||
|
message = q.get() |
||||||
|
m = message.split('_') |
||||||
|
if m[0] == "steer": |
||||||
|
steer_manual = float(m[1]) |
||||||
|
elif m[0] == "throttle": |
||||||
|
throttle_manual = float(m[1]) |
||||||
|
elif m[0] == "brake": |
||||||
|
brake_manual = float(m[1]) |
||||||
|
elif m[0] == "cruise": |
||||||
|
if m[1] == "down": |
||||||
|
self.simulator_state.cruise_button = CruiseButtons.DECEL_SET |
||||||
|
elif m[1] == "up": |
||||||
|
self.simulator_state.cruise_button = CruiseButtons.RES_ACCEL |
||||||
|
elif m[1] == "cancel": |
||||||
|
self.simulator_state.cruise_button = CruiseButtons.CANCEL |
||||||
|
elif m[1] == "main": |
||||||
|
self.simulator_state.cruise_button = CruiseButtons.MAIN |
||||||
|
elif m[0] == "ignition": |
||||||
|
self.simulator_state.ignition = not self.simulator_state.ignition |
||||||
|
elif m[0] == "quit": |
||||||
|
break |
||||||
|
|
||||||
|
self.simulator_state.user_brake = brake_manual |
||||||
|
self.simulator_state.user_gas = throttle_manual |
||||||
|
|
||||||
|
steer_manual = steer_manual * -40 |
||||||
|
|
||||||
|
# Update openpilot on current sensor state |
||||||
|
self.simulated_sensors.update(self.simulator_state, self.world) |
||||||
|
|
||||||
|
is_openpilot_engaged = self.simulated_car.sm['controlsState'].active |
||||||
|
|
||||||
|
self.simulated_car.sm.update(0) |
||||||
|
|
||||||
|
if is_openpilot_engaged: |
||||||
|
throttle_op = clip(self.simulated_car.sm['carControl'].actuators.accel / 1.6, 0.0, 1.0) |
||||||
|
brake_op = clip(-self.simulated_car.sm['carControl'].actuators.accel / 4.0, 0.0, 1.0) |
||||||
|
steer_op = self.simulated_car.sm['carControl'].actuators.steeringAngleDeg |
||||||
|
|
||||||
|
throttle_out = throttle_op if is_openpilot_engaged else throttle_manual |
||||||
|
brake_out = brake_op if is_openpilot_engaged else brake_manual |
||||||
|
steer_out = steer_op if is_openpilot_engaged else steer_manual |
||||||
|
|
||||||
|
self.world.apply_controls(steer_out, throttle_out, brake_out) |
||||||
|
self.world.read_sensors(self.simulator_state) |
||||||
|
|
||||||
|
if self.rk.frame % self.TICKS_PER_FRAME == 0: |
||||||
|
self.world.tick() |
||||||
|
self.world.read_cameras() |
||||||
|
|
||||||
|
self.started = True |
||||||
|
|
||||||
|
rk.keep_time() |
@ -0,0 +1,70 @@ |
|||||||
|
import numpy as np |
||||||
|
import os |
||||||
|
import pyopencl as cl |
||||||
|
import pyopencl.array as cl_array |
||||||
|
|
||||||
|
from cereal.visionipc import VisionIpcServer, VisionStreamType |
||||||
|
from cereal import messaging |
||||||
|
|
||||||
|
from openpilot.common.basedir import BASEDIR |
||||||
|
from openpilot.tools.sim.lib.common import W, H |
||||||
|
|
||||||
|
class Camerad: |
||||||
|
"""Simulates the camerad daemon""" |
||||||
|
def __init__(self, dual_camera): |
||||||
|
self.pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState']) |
||||||
|
|
||||||
|
self.frame_road_id = 0 |
||||||
|
self.frame_wide_id = 0 |
||||||
|
self.vipc_server = VisionIpcServer("camerad") |
||||||
|
|
||||||
|
self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_ROAD, 5, False, W, H) |
||||||
|
if dual_camera: |
||||||
|
self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_WIDE_ROAD, 5, False, W, H) |
||||||
|
|
||||||
|
self.vipc_server.start_listener() |
||||||
|
|
||||||
|
# set up for pyopencl rgb to yuv conversion |
||||||
|
self.ctx = cl.create_some_context() |
||||||
|
self.queue = cl.CommandQueue(self.ctx) |
||||||
|
cl_arg = f" -DHEIGHT={H} -DWIDTH={W} -DRGB_STRIDE={W * 3} -DUV_WIDTH={W // 2} -DUV_HEIGHT={H // 2} -DRGB_SIZE={W * H} -DCL_DEBUG " |
||||||
|
|
||||||
|
kernel_fn = os.path.join(BASEDIR, "tools/sim/rgb_to_nv12.cl") |
||||||
|
with open(kernel_fn) as f: |
||||||
|
prg = cl.Program(self.ctx, f.read()).build(cl_arg) |
||||||
|
self.krnl = prg.rgb_to_nv12 |
||||||
|
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 |
||||||
|
|
||||||
|
def cam_send_yuv_road(self, yuv): |
||||||
|
self._send_yuv(yuv, self.frame_road_id, 'roadCameraState', VisionStreamType.VISION_STREAM_ROAD) |
||||||
|
self.frame_road_id += 1 |
||||||
|
|
||||||
|
def cam_send_yuv_wide_road(self, yuv): |
||||||
|
self._send_yuv(yuv, self.frame_wide_id, 'wideRoadCameraState', VisionStreamType.VISION_STREAM_WIDE_ROAD) |
||||||
|
self.frame_wide_id += 1 |
||||||
|
|
||||||
|
# Returns: yuv bytes |
||||||
|
def rgb_to_yuv(self, rgb): |
||||||
|
assert rgb.shape == (H, W, 3), f"{rgb.shape}" |
||||||
|
assert rgb.dtype == np.uint8 |
||||||
|
|
||||||
|
rgb_cl = cl_array.to_device(self.queue, rgb) |
||||||
|
yuv_cl = cl_array.empty_like(rgb_cl) |
||||||
|
self.krnl(self.queue, (self.Wdiv4, self.Hdiv4), None, rgb_cl.data, yuv_cl.data).wait() |
||||||
|
yuv = np.resize(yuv_cl.get(), rgb.size // 2) |
||||||
|
return yuv.data.tobytes() |
||||||
|
|
||||||
|
def _send_yuv(self, yuv, frame_id, pub_type, yuv_type): |
||||||
|
eof = int(frame_id * 0.05 * 1e9) |
||||||
|
self.vipc_server.send(yuv_type, yuv, frame_id, eof, eof) |
||||||
|
|
||||||
|
dat = messaging.new_message(pub_type) |
||||||
|
msg = { |
||||||
|
"frameId": frame_id, |
||||||
|
"transform": [1.0, 0.0, 0.0, |
||||||
|
0.0, 1.0, 0.0, |
||||||
|
0.0, 0.0, 1.0] |
||||||
|
} |
||||||
|
setattr(dat, pub_type, msg) |
||||||
|
self.pm.send(pub_type, dat) |
@ -1,94 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import cereal.messaging as messaging |
|
||||||
from opendbc.can.packer import CANPacker |
|
||||||
from opendbc.can.parser import CANParser |
|
||||||
from openpilot.selfdrive.boardd.boardd_api_impl import can_list_to_can_capnp |
|
||||||
from openpilot.selfdrive.car import crc8_pedal |
|
||||||
|
|
||||||
packer = CANPacker("honda_civic_touring_2016_can_generated") |
|
||||||
rpacker = CANPacker("acura_ilx_2016_nidec") |
|
||||||
|
|
||||||
|
|
||||||
def get_car_can_parser(): |
|
||||||
dbc_f = 'honda_civic_touring_2016_can_generated' |
|
||||||
checks = [ |
|
||||||
(0xe4, 100), |
|
||||||
(0x1fa, 50), |
|
||||||
(0x200, 50), |
|
||||||
] |
|
||||||
return CANParser(dbc_f, checks, 0) |
|
||||||
cp = get_car_can_parser() |
|
||||||
|
|
||||||
def can_function(pm, speed, angle, idx, cruise_button, is_engaged): |
|
||||||
|
|
||||||
msg = [] |
|
||||||
|
|
||||||
# *** powertrain bus *** |
|
||||||
|
|
||||||
speed = speed * 3.6 # convert m/s to kph |
|
||||||
msg.append(packer.make_can_msg("ENGINE_DATA", 0, {"XMISSION_SPEED": speed})) |
|
||||||
msg.append(packer.make_can_msg("WHEEL_SPEEDS", 0, { |
|
||||||
"WHEEL_SPEED_FL": speed, |
|
||||||
"WHEEL_SPEED_FR": speed, |
|
||||||
"WHEEL_SPEED_RL": speed, |
|
||||||
"WHEEL_SPEED_RR": speed |
|
||||||
})) |
|
||||||
|
|
||||||
msg.append(packer.make_can_msg("SCM_BUTTONS", 0, {"CRUISE_BUTTONS": cruise_button})) |
|
||||||
|
|
||||||
values = {"COUNTER_PEDAL": idx & 0xF} |
|
||||||
checksum = crc8_pedal(packer.make_can_msg("GAS_SENSOR", 0, {"COUNTER_PEDAL": idx & 0xF})[2][:-1]) |
|
||||||
values["CHECKSUM_PEDAL"] = checksum |
|
||||||
msg.append(packer.make_can_msg("GAS_SENSOR", 0, values)) |
|
||||||
|
|
||||||
msg.append(packer.make_can_msg("GEARBOX", 0, {"GEAR": 4, "GEAR_SHIFTER": 8})) |
|
||||||
msg.append(packer.make_can_msg("GAS_PEDAL_2", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("SEATBELT_STATUS", 0, {"SEATBELT_DRIVER_LATCHED": 1})) |
|
||||||
msg.append(packer.make_can_msg("STEER_STATUS", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("STEERING_SENSORS", 0, {"STEER_ANGLE": angle})) |
|
||||||
msg.append(packer.make_can_msg("VSA_STATUS", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("STANDSTILL", 0, {"WHEELS_MOVING": 1 if speed >= 1.0 else 0})) |
|
||||||
msg.append(packer.make_can_msg("STEER_MOTOR_TORQUE", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("EPB_STATUS", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("DOORS_STATUS", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("CRUISE_PARAMS", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("CRUISE", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("SCM_FEEDBACK", 0, {"MAIN_ON": 1})) |
|
||||||
msg.append(packer.make_can_msg("POWERTRAIN_DATA", 0, {"ACC_STATUS": int(is_engaged)})) |
|
||||||
msg.append(packer.make_can_msg("HUD_SETTING", 0, {})) |
|
||||||
msg.append(packer.make_can_msg("CAR_SPEED", 0, {})) |
|
||||||
|
|
||||||
# *** cam bus *** |
|
||||||
msg.append(packer.make_can_msg("STEERING_CONTROL", 2, {})) |
|
||||||
msg.append(packer.make_can_msg("ACC_HUD", 2, {})) |
|
||||||
msg.append(packer.make_can_msg("LKAS_HUD", 2, {})) |
|
||||||
msg.append(packer.make_can_msg("BRAKE_COMMAND", 2, {})) |
|
||||||
|
|
||||||
# *** radar bus *** |
|
||||||
if idx % 5 == 0: |
|
||||||
msg.append(rpacker.make_can_msg("RADAR_DIAGNOSTIC", 1, {"RADAR_STATE": 0x79})) |
|
||||||
for i in range(16): |
|
||||||
msg.append(rpacker.make_can_msg("TRACK_%d" % i, 1, {"LONG_DIST": 255.5})) |
|
||||||
|
|
||||||
pm.send('can', can_list_to_can_capnp(msg)) |
|
||||||
|
|
||||||
def sendcan_function(sendcan): |
|
||||||
sc = messaging.drain_sock_raw(sendcan) |
|
||||||
cp.update_strings(sc, sendcan=True) |
|
||||||
|
|
||||||
if cp.vl[0x1fa]['COMPUTER_BRAKE_REQUEST']: |
|
||||||
brake = cp.vl[0x1fa]['COMPUTER_BRAKE'] / 1024. |
|
||||||
else: |
|
||||||
brake = 0.0 |
|
||||||
|
|
||||||
if cp.vl[0x200]['GAS_COMMAND'] > 0: |
|
||||||
gas = ( cp.vl[0x200]['GAS_COMMAND'] + 83.3 ) / (0.253984064 * 2**16) |
|
||||||
else: |
|
||||||
gas = 0.0 |
|
||||||
|
|
||||||
if cp.vl[0xe4]['STEER_TORQUE_REQUEST']: |
|
||||||
steer_torque = cp.vl[0xe4]['STEER_TORQUE']/3840 |
|
||||||
else: |
|
||||||
steer_torque = 0.0 |
|
||||||
|
|
||||||
return gas, brake, steer_torque |
|
@ -0,0 +1,86 @@ |
|||||||
|
import math |
||||||
|
import threading |
||||||
|
import numpy as np |
||||||
|
|
||||||
|
from abc import ABC, abstractmethod |
||||||
|
from collections import namedtuple |
||||||
|
|
||||||
|
W, H = 1928, 1208 |
||||||
|
|
||||||
|
|
||||||
|
vec3 = namedtuple("vec3", ["x", "y", "z"]) |
||||||
|
|
||||||
|
class GPSState: |
||||||
|
def __init__(self): |
||||||
|
self.latitude = 0 |
||||||
|
self.longitude = 0 |
||||||
|
self.altitude = 0 |
||||||
|
|
||||||
|
def from_xy(self, xy): |
||||||
|
"""Simulates a lat/lon from an xy coordinate on a plane, for simple simlation. TODO: proper global projection?""" |
||||||
|
BASE_LAT = 32.75308505188913 |
||||||
|
BASE_LON = -117.2095393365393 |
||||||
|
DEG_TO_METERS = 100000 |
||||||
|
|
||||||
|
self.latitude = float(BASE_LAT + xy[0] / DEG_TO_METERS) |
||||||
|
self.longitude = float(BASE_LON + xy[1] / DEG_TO_METERS) |
||||||
|
self.altitude = 0 |
||||||
|
|
||||||
|
|
||||||
|
class IMUState: |
||||||
|
def __init__(self): |
||||||
|
self.accelerometer: vec3 = vec3(0,0,0) |
||||||
|
self.gyroscope: vec3 = vec3(0,0,0) |
||||||
|
self.bearing: float = 0 |
||||||
|
|
||||||
|
|
||||||
|
class SimulatorState: |
||||||
|
def __init__(self): |
||||||
|
self.valid = False |
||||||
|
self.is_engaged = False |
||||||
|
self.ignition = True |
||||||
|
|
||||||
|
self.velocity: vec3 = None |
||||||
|
self.bearing: float = 0 |
||||||
|
self.gps = GPSState() |
||||||
|
self.imu = IMUState() |
||||||
|
|
||||||
|
self.steering_angle: float = 0 |
||||||
|
|
||||||
|
self.user_gas: float = 0 |
||||||
|
self.user_brake: float = 0 |
||||||
|
|
||||||
|
self.cruise_button = 0 |
||||||
|
|
||||||
|
@property |
||||||
|
def speed(self): |
||||||
|
return math.sqrt(self.velocity.x ** 2 + self.velocity.y ** 2 + self.velocity.z ** 2) |
||||||
|
|
||||||
|
|
||||||
|
class World(ABC): |
||||||
|
def __init__(self, dual_camera): |
||||||
|
self.dual_camera = dual_camera |
||||||
|
|
||||||
|
self.image_lock = threading.Lock() |
||||||
|
self.road_image = np.zeros((H, W, 3), dtype=np.uint8) |
||||||
|
self.wide_road_image = np.zeros((H, W, 3), dtype=np.uint8) |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def apply_controls(self, steer_sim, throttle_out, brake_out): |
||||||
|
pass |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def tick(self): |
||||||
|
pass |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def read_sensors(self, simulator_state: SimulatorState): |
||||||
|
pass |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def read_cameras(self): |
||||||
|
pass |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def close(self): |
||||||
|
pass |
@ -0,0 +1,110 @@ |
|||||||
|
import cereal.messaging as messaging |
||||||
|
|
||||||
|
from opendbc.can.packer import CANPacker |
||||||
|
from opendbc.can.parser import CANParser |
||||||
|
from openpilot.selfdrive.boardd.boardd_api_impl import can_list_to_can_capnp |
||||||
|
from openpilot.selfdrive.car import crc8_pedal |
||||||
|
from openpilot.tools.sim.lib.common import SimulatorState |
||||||
|
|
||||||
|
|
||||||
|
class SimulatedCar: |
||||||
|
"""Simulates a honda civic 2016 (panda state + can messages) to OpenPilot""" |
||||||
|
packer = CANPacker("honda_civic_touring_2016_can_generated") |
||||||
|
rpacker = CANPacker("acura_ilx_2016_nidec") |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
self.pm = messaging.PubMaster(['can', 'pandaStates']) |
||||||
|
self.sm = messaging.SubMaster(['carControl', 'controlsState']) |
||||||
|
self.cp = self.get_car_can_parser() |
||||||
|
self.idx = 0 |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def get_car_can_parser(): |
||||||
|
dbc_f = 'honda_civic_touring_2016_can_generated' |
||||||
|
checks = [ |
||||||
|
(0xe4, 100), |
||||||
|
(0x1fa, 50), |
||||||
|
(0x200, 50), |
||||||
|
] |
||||||
|
return CANParser(dbc_f, checks, 0) |
||||||
|
|
||||||
|
def send_can_messages(self, simulator_state: SimulatorState): |
||||||
|
if not simulator_state.valid: |
||||||
|
return |
||||||
|
|
||||||
|
msg = [] |
||||||
|
|
||||||
|
# *** powertrain bus *** |
||||||
|
|
||||||
|
speed = simulator_state.speed * 3.6 # convert m/s to kph |
||||||
|
msg.append(self.packer.make_can_msg("ENGINE_DATA", 0, {"XMISSION_SPEED": speed})) |
||||||
|
msg.append(self.packer.make_can_msg("WHEEL_SPEEDS", 0, { |
||||||
|
"WHEEL_SPEED_FL": speed, |
||||||
|
"WHEEL_SPEED_FR": speed, |
||||||
|
"WHEEL_SPEED_RL": speed, |
||||||
|
"WHEEL_SPEED_RR": speed |
||||||
|
})) |
||||||
|
|
||||||
|
msg.append(self.packer.make_can_msg("SCM_BUTTONS", 0, {"CRUISE_BUTTONS": simulator_state.cruise_button})) |
||||||
|
|
||||||
|
values = { |
||||||
|
"COUNTER_PEDAL": self.idx & 0xF, |
||||||
|
"INTERCEPTOR_GAS": simulator_state.user_gas * 2**12, |
||||||
|
"INTERCEPTOR_GAS2": simulator_state.user_gas * 2**12, |
||||||
|
} |
||||||
|
checksum = crc8_pedal(self.packer.make_can_msg("GAS_SENSOR", 0, values)[2][:-1]) |
||||||
|
values["CHECKSUM_PEDAL"] = checksum |
||||||
|
msg.append(self.packer.make_can_msg("GAS_SENSOR", 0, values)) |
||||||
|
|
||||||
|
msg.append(self.packer.make_can_msg("GEARBOX", 0, {"GEAR": 4, "GEAR_SHIFTER": 8})) |
||||||
|
msg.append(self.packer.make_can_msg("GAS_PEDAL_2", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("SEATBELT_STATUS", 0, {"SEATBELT_DRIVER_LATCHED": 1})) |
||||||
|
msg.append(self.packer.make_can_msg("STEER_STATUS", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("STEERING_SENSORS", 0, {"STEER_ANGLE": simulator_state.steering_angle})) |
||||||
|
msg.append(self.packer.make_can_msg("VSA_STATUS", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("STANDSTILL", 0, {"WHEELS_MOVING": 1 if simulator_state.speed >= 1.0 else 0})) |
||||||
|
msg.append(self.packer.make_can_msg("STEER_MOTOR_TORQUE", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("EPB_STATUS", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("DOORS_STATUS", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("CRUISE_PARAMS", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("CRUISE", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("SCM_FEEDBACK", 0, {"MAIN_ON": 1})) |
||||||
|
msg.append(self.packer.make_can_msg("POWERTRAIN_DATA", 0, |
||||||
|
{ |
||||||
|
"ACC_STATUS": int(simulator_state.is_engaged), |
||||||
|
"PEDAL_GAS": simulator_state.user_gas, |
||||||
|
"BRAKE_PRESSED": simulator_state.user_brake > 0 |
||||||
|
})) |
||||||
|
msg.append(self.packer.make_can_msg("HUD_SETTING", 0, {})) |
||||||
|
msg.append(self.packer.make_can_msg("CAR_SPEED", 0, {})) |
||||||
|
|
||||||
|
# *** cam bus *** |
||||||
|
msg.append(self.packer.make_can_msg("STEERING_CONTROL", 2, {})) |
||||||
|
msg.append(self.packer.make_can_msg("ACC_HUD", 2, {})) |
||||||
|
msg.append(self.packer.make_can_msg("LKAS_HUD", 2, {})) |
||||||
|
msg.append(self.packer.make_can_msg("BRAKE_COMMAND", 2, {})) |
||||||
|
|
||||||
|
# *** radar bus *** |
||||||
|
if self.idx % 5 == 0: |
||||||
|
msg.append(self.rpacker.make_can_msg("RADAR_DIAGNOSTIC", 1, {"RADAR_STATE": 0x79})) |
||||||
|
for i in range(16): |
||||||
|
msg.append(self.rpacker.make_can_msg("TRACK_%d" % i, 1, {"LONG_DIST": 255.5})) |
||||||
|
|
||||||
|
self.pm.send('can', can_list_to_can_capnp(msg)) |
||||||
|
|
||||||
|
def send_panda_state(self, simulator_state): |
||||||
|
dat = messaging.new_message('pandaStates', 1) |
||||||
|
dat.valid = True |
||||||
|
dat.pandaStates[0] = { |
||||||
|
'ignitionLine': simulator_state.ignition, |
||||||
|
'pandaType': "blackPanda", |
||||||
|
'controlsAllowed': True, |
||||||
|
'safetyModel': 'hondaNidec', |
||||||
|
} |
||||||
|
self.pm.send('pandaStates', dat) |
||||||
|
|
||||||
|
def update(self, simulator_state: SimulatorState): |
||||||
|
self.send_can_messages(simulator_state) |
||||||
|
self.send_panda_state(simulator_state) |
||||||
|
|
||||||
|
self.idx += 1 |
@ -0,0 +1,127 @@ |
|||||||
|
import time |
||||||
|
|
||||||
|
from cereal import log |
||||||
|
import cereal.messaging as messaging |
||||||
|
|
||||||
|
from openpilot.common.params import Params |
||||||
|
from openpilot.common.realtime import DT_DMON |
||||||
|
from openpilot.tools.sim.lib.camerad import Camerad |
||||||
|
|
||||||
|
from typing import TYPE_CHECKING |
||||||
|
if TYPE_CHECKING: |
||||||
|
from openpilot.tools.sim.lib.common import World, SimulatorState |
||||||
|
|
||||||
|
|
||||||
|
class SimulatedSensors: |
||||||
|
"""Simulates the C3 sensors (acc, gyro, gps, peripherals, dm state, cameras) to OpenPilot""" |
||||||
|
|
||||||
|
def __init__(self, dual_camera=False): |
||||||
|
self.pm = messaging.PubMaster(['accelerometer', 'gyroscope', 'gpsLocationExternal', 'driverStateV2', 'driverMonitoringState', 'peripheralState']) |
||||||
|
self.camerad = Camerad(dual_camera=dual_camera) |
||||||
|
self.last_perp_update = 0 |
||||||
|
self.last_dmon_update = 0 |
||||||
|
|
||||||
|
def send_imu_message(self, simulator_state: 'SimulatorState'): |
||||||
|
for _ in range(5): |
||||||
|
dat = messaging.new_message('accelerometer') |
||||||
|
dat.accelerometer.sensor = 4 |
||||||
|
dat.accelerometer.type = 0x10 |
||||||
|
dat.accelerometer.timestamp = dat.logMonoTime # TODO: use the IMU timestamp |
||||||
|
dat.accelerometer.init('acceleration') |
||||||
|
dat.accelerometer.acceleration.v = [simulator_state.imu.accelerometer.x, simulator_state.imu.accelerometer.y, simulator_state.imu.accelerometer.z] |
||||||
|
self.pm.send('accelerometer', dat) |
||||||
|
|
||||||
|
# copied these numbers from locationd |
||||||
|
dat = messaging.new_message('gyroscope') |
||||||
|
dat.gyroscope.sensor = 5 |
||||||
|
dat.gyroscope.type = 0x10 |
||||||
|
dat.gyroscope.timestamp = dat.logMonoTime # TODO: use the IMU timestamp |
||||||
|
dat.gyroscope.init('gyroUncalibrated') |
||||||
|
dat.gyroscope.gyroUncalibrated.v = [simulator_state.imu.gyroscope.x, simulator_state.imu.gyroscope.y, simulator_state.imu.gyroscope.z] |
||||||
|
self.pm.send('gyroscope', dat) |
||||||
|
|
||||||
|
def send_gps_message(self, simulator_state: 'SimulatorState'): |
||||||
|
if not simulator_state.valid: |
||||||
|
return |
||||||
|
|
||||||
|
# transform vel from carla to NED |
||||||
|
# north is -Y in CARLA |
||||||
|
velNED = [ |
||||||
|
-simulator_state.velocity.y, # north/south component of NED is negative when moving south |
||||||
|
simulator_state.velocity.x, # positive when moving east, which is x in carla |
||||||
|
simulator_state.velocity.z, |
||||||
|
] |
||||||
|
|
||||||
|
for _ in range(10): |
||||||
|
dat = messaging.new_message('gpsLocationExternal') |
||||||
|
dat.gpsLocationExternal = { |
||||||
|
"unixTimestampMillis": int(time.time() * 1000), |
||||||
|
"flags": 1, # valid fix |
||||||
|
"accuracy": 1.0, |
||||||
|
"verticalAccuracy": 1.0, |
||||||
|
"speedAccuracy": 0.1, |
||||||
|
"bearingAccuracyDeg": 0.1, |
||||||
|
"vNED": velNED, |
||||||
|
"bearingDeg": simulator_state.imu.bearing, |
||||||
|
"latitude": simulator_state.gps.latitude, |
||||||
|
"longitude": simulator_state.gps.longitude, |
||||||
|
"altitude": simulator_state.gps.altitude, |
||||||
|
"speed": simulator_state.speed, |
||||||
|
"source": log.GpsLocationData.SensorSource.ublox, |
||||||
|
} |
||||||
|
|
||||||
|
self.pm.send('gpsLocationExternal', dat) |
||||||
|
|
||||||
|
def send_peripheral_state(self): |
||||||
|
dat = messaging.new_message('peripheralState') |
||||||
|
dat.valid = True |
||||||
|
dat.peripheralState = { |
||||||
|
'pandaType': log.PandaState.PandaType.blackPanda, |
||||||
|
'voltage': 12000, |
||||||
|
'current': 5678, |
||||||
|
'fanSpeedRpm': 1000 |
||||||
|
} |
||||||
|
Params().put_bool("ObdMultiplexingEnabled", False) |
||||||
|
self.pm.send('peripheralState', dat) |
||||||
|
|
||||||
|
def send_fake_driver_monitoring(self): |
||||||
|
# dmonitoringmodeld output |
||||||
|
dat = messaging.new_message('driverStateV2') |
||||||
|
dat.driverStateV2.leftDriverData.faceOrientation = [0., 0., 0.] |
||||||
|
dat.driverStateV2.leftDriverData.faceProb = 1.0 |
||||||
|
dat.driverStateV2.rightDriverData.faceOrientation = [0., 0., 0.] |
||||||
|
dat.driverStateV2.rightDriverData.faceProb = 1.0 |
||||||
|
self.pm.send('driverStateV2', dat) |
||||||
|
|
||||||
|
# dmonitoringd output |
||||||
|
dat = messaging.new_message('driverMonitoringState') |
||||||
|
dat.driverMonitoringState = { |
||||||
|
"faceDetected": True, |
||||||
|
"isDistracted": False, |
||||||
|
"awarenessStatus": 1., |
||||||
|
} |
||||||
|
self.pm.send('driverMonitoringState', dat) |
||||||
|
|
||||||
|
def send_camera_images(self, world: 'World'): |
||||||
|
with world.image_lock: |
||||||
|
yuv = self.camerad.rgb_to_yuv(world.road_image) |
||||||
|
self.camerad.cam_send_yuv_road(yuv) |
||||||
|
|
||||||
|
if world.dual_camera: |
||||||
|
yuv = self.camerad.rgb_to_yuv(world.wide_road_image) |
||||||
|
self.camerad.cam_send_yuv_wide_road(yuv) |
||||||
|
|
||||||
|
def update(self, simulator_state: 'SimulatorState', world: 'World'): |
||||||
|
now = time.time() |
||||||
|
self.send_imu_message(simulator_state) |
||||||
|
self.send_gps_message(simulator_state) |
||||||
|
|
||||||
|
if (now - self.last_dmon_update) > DT_DMON/2: |
||||||
|
self.send_fake_driver_monitoring() |
||||||
|
self.last_dmon_update = now |
||||||
|
|
||||||
|
if (now - self.last_perp_update) > 0.25: |
||||||
|
self.send_peripheral_state() |
||||||
|
self.last_perp_update = now |
||||||
|
|
||||||
|
self.send_camera_images(world) |
@ -0,0 +1,50 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
import argparse |
||||||
|
|
||||||
|
from typing import Any |
||||||
|
from multiprocessing import Queue |
||||||
|
|
||||||
|
from openpilot.tools.sim.bridge.common import SimulatorBridge |
||||||
|
from openpilot.tools.sim.bridge.carla import CarlaBridge |
||||||
|
|
||||||
|
|
||||||
|
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('--high_quality', action='store_true') |
||||||
|
parser.add_argument('--dual_camera', action='store_true') |
||||||
|
parser.add_argument('--simulator', dest='simulator', type=str, default='carla') |
||||||
|
|
||||||
|
# Carla specific |
||||||
|
parser.add_argument('--town', type=str, default='Town04_Opt') |
||||||
|
parser.add_argument('--spawn_point', dest='num_selected_spawn_point', type=int, default=16) |
||||||
|
parser.add_argument('--host', dest='host', type=str, default='127.0.0.1') |
||||||
|
parser.add_argument('--port', dest='port', type=int, default=2000) |
||||||
|
|
||||||
|
return parser.parse_args(add_args) |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
q: Any = Queue() |
||||||
|
args = parse_args() |
||||||
|
|
||||||
|
simulator_bridge: SimulatorBridge |
||||||
|
if args.simulator == "carla": |
||||||
|
simulator_bridge = CarlaBridge(args) |
||||||
|
else: |
||||||
|
raise AssertionError("simulator type not supported") |
||||||
|
p = simulator_bridge.run(q) |
||||||
|
|
||||||
|
if args.joystick: |
||||||
|
# start input poll for joystick |
||||||
|
from openpilot.tools.sim.lib.manual_ctrl import wheel_poll_thread |
||||||
|
|
||||||
|
wheel_poll_thread(q) |
||||||
|
else: |
||||||
|
# start input poll for keyboard |
||||||
|
from openpilot.tools.sim.lib.keyboard_ctrl import keyboard_poll_thread |
||||||
|
|
||||||
|
keyboard_poll_thread(q) |
||||||
|
|
||||||
|
simulator_bridge.shutdown() |
||||||
|
|
||||||
|
p.join() |
Loading…
Reference in new issue