Simulator: Fix CI and set low_quality default (#24354)

* Change low_quality argument and fix closing carla bridge

* Some fixes

* Change carla process in test

* Change fov to 120, higher doesn't look good

* Update readme and remove redundant test

* update

* Add folder description
old-commit-hash: ee433dfa57
taco
Gijs Koning 3 years ago committed by GitHub
parent effe065577
commit d0fc9afa48
  1. 46
      tools/sim/README.md
  2. 76
      tools/sim/bridge.py
  3. 2
      tools/sim/lib/keyboard_ctrl.py
  4. 2
      tools/sim/start_carla.sh
  5. 28
      tools/sim/test/test_carla_integration.py

@ -5,40 +5,52 @@ openpilot implements a [bridge](bridge.py) that allows it to run in the [CARLA s
## System Requirements ## System Requirements
openpilot doesn't have any extreme hardware requirements, however CARLA requires an NVIDIA graphics card and is very resource-intensive and may not run smoothly on your system. For this case, we have a low quality mode you can activate by running: openpilot doesn't have any extreme hardware requirements, however CARLA requires an NVIDIA graphics card and is very resource-intensive and may not run smoothly on your system.
``` For this case, we have a the simulator in low quality by default.
./start_openpilot_docker.sh --low_quality
```
You can also check out the [CARLA python documentation](https://carla.readthedocs.io/en/latest/python_api/) to find more parameters to tune that might increase performance on your system. You can also check out the [CARLA python documentation](https://carla.readthedocs.io/en/latest/python_api/) to find more parameters to tune that might increase performance on your system.
## Running the simulator ## Running the simulator
Start Carla simulator, openpilot and bridge processes located in tools/sim:
First, start the CARLA server in one terminal. ``` bash
``` # Terminal 1
./start_carla.sh ./start_carla.sh
# Terminal 2 - Run openpilot and bridge in one Docker:
./start_openpilot_docker.sh
# Running the latest local code execute
# Terminal 2:
./launch_openpilot.sh
# Terminal 3
./bridge.py
``` ```
Then, start the bridge and openpilot in another terminal. ### Bridge usage
_Same commands hold for start_openpilot_docker_
``` ```
./start_openpilot_docker.sh $ ./bridge.py -h
Usage: bridge.py [options]
Bridge between CARLA and openpilot.
Options:
-h, --help show this help message and exit
--joystick Use joystick input to control the car
--high_quality Set simulator to higher quality (requires good GPU)
--town TOWN Select map to drive in
--spawn_point NUM Number of the spawn point to start in
``` ```
To engage openpilot press 1 a few times while focused on bridge.py to increase the cruise speed. To engage openpilot press 1 a few times while focused on bridge.py to increase the cruise speed.
All inputs:
## Controls
You can control openpilot driving in the simulation with the following keys
| key | functionality | | key | functionality |
| :---: | :---------------: | |:----:|:-----------------:|
| 1 | Cruise up 5 mph | | 1 | Cruise up 5 mph |
| 2 | Cruise down 5 mph | | 2 | Cruise down 5 mph |
| 3 | Cruise cancel | | 3 | Cruise cancel |
| q | Exit all | | q | Exit all |
| wasd | Control manually |
To see the options for changing the environment, such as the town, spawn point or precipitation, you can run `./start_openpilot_docker.sh --help`.
This will print the help output inside the docker container. You need to exit the docker container before running `./start_openpilot_docker.sh` again.
## Further Reading ## Further Reading

@ -32,11 +32,10 @@ STEER_RATIO = 15.
pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'sensorEvents', 'can', "gpsLocationExternal"]) pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'sensorEvents', 'can', "gpsLocationExternal"])
sm = messaging.SubMaster(['carControl', 'controlsState']) sm = messaging.SubMaster(['carControl', 'controlsState'])
def parse_args(add_args=None): def parse_args(add_args=None):
parser = argparse.ArgumentParser(description='Bridge between CARLA and openpilot.') parser = argparse.ArgumentParser(description='Bridge between CARLA and openpilot.')
parser.add_argument('--joystick', action='store_true') parser.add_argument('--joystick', action='store_true')
parser.add_argument('--low_quality', action='store_true') parser.add_argument('--high_quality', action='store_true')
parser.add_argument('--town', type=str, default='Town04_Opt') 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('--spawn_point', dest='num_selected_spawn_point', type=int, default=16)
@ -86,9 +85,8 @@ 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")
self._kernel_file = open(kernel_fn) with open(kernel_fn) as f:
prg = cl.Program(self.ctx, f.read()).build(cl_arg)
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
@ -130,10 +128,6 @@ 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)
dat = messaging.new_message('sensorEvents', 2) dat = messaging.new_message('sensorEvents', 2)
@ -240,13 +234,13 @@ def can_function_runner(vs: VehicleState, exit_event: threading.Event):
def connect_carla_client(): def connect_carla_client():
client = carla.Client("127.0.0.1", 2000) client = carla.Client("127.0.0.1", 2000)
client.set_timeout(10) client.set_timeout(5)
return client return client
class CarlaBridge: class CarlaBridge:
def __init__(self, args): def __init__(self, arguments):
set_params_enabled() set_params_enabled()
msg = messaging.new_message('liveCalibration') msg = messaging.new_message('liveCalibration')
@ -254,32 +248,48 @@ class CarlaBridge:
msg.liveCalibration.rpyCalib = [0.0, 0.0, 0.0] msg.liveCalibration.rpyCalib = [0.0, 0.0, 0.0]
Params().put("CalibrationParams", msg.to_bytes()) Params().put("CalibrationParams", msg.to_bytes())
self._args = args self._args = arguments
self._carla_objects = [] self._carla_objects = []
self._camerad = None self._camerad = None
self._threads_exit_event = threading.Event() self._exit_event = threading.Event()
self._threads = [] self._threads = []
self._shutdown = False self._keep_alive = True
self.started = False self.started = False
signal.signal(signal.SIGTERM, self._on_shutdown) signal.signal(signal.SIGTERM, self._on_shutdown)
self._exit = threading.Event()
def _on_shutdown(self, signal, frame): def _on_shutdown(self, signal, frame):
self._shutdown = True self._keep_alive = False
def bridge_keep_alive(self, q: Queue, retries: int): def bridge_keep_alive(self, q: Queue, retries: int):
while not self._shutdown: try:
while self._keep_alive:
try: try:
self._run(q) self._run(q)
break break
except RuntimeError as e: except RuntimeError as e:
self.close()
if retries == 0: if retries == 0:
raise raise
# Reset for another try
self._carla_objects = []
self._threads = []
self._exit_event = threading.Event()
retries -= 1 retries -= 1
if retries <= -1:
print(f"Restarting bridge. Error: {e} ")
else:
print(f"Restarting bridge. Retries left {retries}. Error: {e} ") 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): def _run(self, q: Queue):
client = connect_carla_client() client = connect_carla_client()
world = client.load_world(self._args.town) 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
@ -287,7 +297,7 @@ class CarlaBridge:
world.set_weather(carla.WeatherParameters.ClearSunset) world.set_weather(carla.WeatherParameters.ClearSunset)
if self._args.low_quality: if not self._args.high_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)
@ -324,7 +334,7 @@ class CarlaBridge:
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))
if self._args.low_quality: if not self._args.high_quality:
blueprint.set_attribute('enable_postprocess_effects', 'False') 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)
@ -332,7 +342,7 @@ class CarlaBridge:
self._camerad = Camerad() self._camerad = Camerad()
road_camera = create_camera(fov=40, callback=self._camerad.cam_callback_road) road_camera = create_camera(fov=40, callback=self._camerad.cam_callback_road)
road_wide_camera = create_camera(fov=163, callback=self._camerad.cam_callback_wide_road) # fov bigger than 163 shows unwanted artifacts road_wide_camera = create_camera(fov=120, callback=self._camerad.cam_callback_wide_road) # fov bigger than 120 shows unwanted artifacts
self._carla_objects.extend([road_camera, road_wide_camera]) self._carla_objects.extend([road_camera, road_wide_camera])
@ -349,16 +359,13 @@ class CarlaBridge:
self._carla_objects.extend([imu, gps]) self._carla_objects.extend([imu, gps])
# launch fake car threads # launch fake car threads
self._threads.append(threading.Thread(target=panda_state_function, args=(vehicle_state, self._threads_exit_event,))) 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._threads_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._threads_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._threads_exit_event,))) self._threads.append(threading.Thread(target=can_function_runner, args=(vehicle_state, self._exit_event,)))
for t in self._threads: for t in self._threads:
t.start() t.start()
# can loop
rk = Ratekeeper(100, print_delay_threshold=0.05)
# init # init
throttle_ease_out_counter = REPEAT_COUNTER throttle_ease_out_counter = REPEAT_COUNTER
brake_ease_out_counter = REPEAT_COUNTER brake_ease_out_counter = REPEAT_COUNTER
@ -376,7 +383,14 @@ class CarlaBridge:
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 not self._shutdown: # 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 # 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
@ -495,13 +509,9 @@ class CarlaBridge:
rk.keep_time() rk.keep_time()
self.started = True self.started = True
# Clean up resources in the opposite order they were created.
self.close()
def close(self): def close(self):
self._threads_exit_event.set() self.started = False
if self._camerad is not None: self._exit_event.set()
self._camerad.close()
for s in self._carla_objects: for s in self._carla_objects:
try: try:
s.destroy() s.destroy()

@ -38,7 +38,7 @@ def getch() -> str:
def keyboard_poll_thread(q: 'Queue[str]'): def keyboard_poll_thread(q: 'Queue[str]'):
while True: while True:
c = getch() c = getch()
# print("got %s" % c) print("got %s" % c)
if c == '1': if c == '1':
q.put("cruise_up") q.put("cruise_up")
elif c == '2': elif c == '2':

@ -25,4 +25,4 @@ docker run \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \ -v /tmp/.X11-unix:/tmp/.X11-unix:rw \
-it \ -it \
carlasim/carla:0.9.12 \ carlasim/carla:0.9.12 \
/bin/bash ./CarlaUE4.sh -opengl -nosound -RenderOffScreen -benchmark -fps=20 -quality-level=High /bin/bash ./CarlaUE4.sh -opengl -nosound -RenderOffScreen -benchmark -fps=20 -quality-level=Low

@ -15,27 +15,18 @@ class TestCarlaIntegration(unittest.TestCase):
Tests need Carla simulator to run Tests need Carla simulator to run
""" """
processes = None processes = None
carla_process = None
def setUp(self): def setUp(self):
self.processes = [] self.processes = []
# We want to make sure that carla_sim docker is still running. Skip output shell # We want to make sure that carla_sim docker isn't still running.
subprocess.run("docker rm -f carla_sim", shell=True, stderr=subprocess.PIPE, check=False) subprocess.run("docker rm -f carla_sim", shell=True, stderr=subprocess.PIPE, check=False)
self.processes.append(subprocess.Popen(".././start_carla.sh")) self.carla_process = subprocess.Popen(".././start_carla.sh")
# Too many lagging messages in bridge.py can cause a crash. This prevents it. # Too many lagging messages in bridge.py can cause a crash. This prevents it.
unblock_stdout() unblock_stdout()
# Wait 10 seconds to startup carla
def test_run_bridge(self): time.sleep(10)
# Test bridge connect with carla and runs without any errors for 60 seconds
test_duration = 60
carla_bridge = CarlaBridge(bridge.parse_args(['--low_quality']))
p = carla_bridge.run(Queue(), retries=3)
self.processes = [p]
time.sleep(test_duration)
self.assertEqual(p.exitcode, None, f"Bridge process should be running, but exited with code {p.exitcode}")
def test_engage(self): def test_engage(self):
# Startup manager and bridge.py. Check processes are running, then engage and verify. # Startup manager and bridge.py. Check processes are running, then engage and verify.
@ -44,7 +35,7 @@ class TestCarlaIntegration(unittest.TestCase):
sm = messaging.SubMaster(['controlsState', 'carEvents', 'managerState']) sm = messaging.SubMaster(['controlsState', 'carEvents', 'managerState'])
q = Queue() q = Queue()
carla_bridge = CarlaBridge(bridge.parse_args(['--low_quality'])) carla_bridge = CarlaBridge(bridge.parse_args([]))
p_bridge = carla_bridge.run(q, retries=3) p_bridge = carla_bridge.run(q, retries=3)
self.processes.append(p_bridge) self.processes.append(p_bridge)
@ -70,7 +61,7 @@ class TestCarlaIntegration(unittest.TestCase):
no_car_events_issues_once = True no_car_events_issues_once = True
break break
self.assertTrue(no_car_events_issues_once, f"Failed because sm offline, or CarEvents '{car_event_issues}' or processes not running '{not_running}'") self.assertTrue(no_car_events_issues_once, f"Failed because no messages received, or CarEvents '{car_event_issues}' or processes not running '{not_running}'")
start_time = time.monotonic() start_time = time.monotonic()
min_counts_control_active = 100 min_counts_control_active = 100
@ -99,8 +90,11 @@ class TestCarlaIntegration(unittest.TestCase):
p.wait(15) p.wait(15)
else: else:
p.join(15) p.join(15)
subprocess.run("docker rm -f carla_sim", shell=True, stderr=subprocess.PIPE, check=False)
# Stop carla simulator by removing docker container
subprocess.run("docker rm -f carla_sim", shell=True, stderr=subprocess.PIPE, check=False)
if self.carla_process is not None:
self.carla_process.wait()
if __name__ == "__main__": if __name__ == "__main__":

Loading…
Cancel
Save