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: ee433dfa57a60364423a222a7f18dc137b77f532
This commit is contained in:
@@ -1,44 +1,56 @@
|
||||
openpilot in simulator
|
||||
=====================
|
||||
|
||||
openpilot implements a [bridge](bridge.py) that allows it to run in the [CARLA simulator](https://carla.org/).
|
||||
openpilot implements a [bridge](bridge.py) that allows it to run in the [CARLA simulator](https://carla.org/).
|
||||
|
||||
## 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:
|
||||
```
|
||||
./start_openpilot_docker.sh --low_quality
|
||||
```
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
First, start the CARLA server in one terminal.
|
||||
```
|
||||
Start Carla simulator, openpilot and bridge processes located in tools/sim:
|
||||
``` bash
|
||||
# Terminal 1
|
||||
./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.
|
||||
All inputs:
|
||||
|
||||
## Controls
|
||||
|
||||
You can control openpilot driving in the simulation with the following keys
|
||||
|
||||
| key | functionality |
|
||||
| :---: | :---------------: |
|
||||
| 1 | Cruise up 5 mph |
|
||||
| 2 | Cruise down 5 mph |
|
||||
| 3 | Cruise cancel |
|
||||
| q | Exit all |
|
||||
|
||||
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.
|
||||
| key | functionality |
|
||||
|:----:|:-----------------:|
|
||||
| 1 | Cruise up 5 mph |
|
||||
| 2 | Cruise down 5 mph |
|
||||
| 3 | Cruise cancel |
|
||||
| q | Exit all |
|
||||
| wasd | Control manually |
|
||||
|
||||
## Further Reading
|
||||
|
||||
|
||||
@@ -32,11 +32,10 @@ STEER_RATIO = 15.
|
||||
pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'sensorEvents', '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('--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('--spawn_point', dest='num_selected_spawn_point', type=int, default=16)
|
||||
|
||||
@@ -86,10 +85,9 @@ class Camerad:
|
||||
|
||||
# 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")
|
||||
self._kernel_file = open(kernel_fn)
|
||||
|
||||
prg = cl.Program(self.ctx, self._kernel_file.read()).build(cl_arg)
|
||||
self.krnl = prg.rgb_to_yuv
|
||||
with open(kernel_fn) as f:
|
||||
prg = cl.Program(self.ctx, f.read()).build(cl_arg)
|
||||
self.krnl = prg.rgb_to_yuv
|
||||
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
|
||||
|
||||
@@ -130,10 +128,6 @@ class Camerad:
|
||||
setattr(dat, pub_type, msg)
|
||||
pm.send(pub_type, dat)
|
||||
|
||||
def close(self):
|
||||
self._kernel_file.close()
|
||||
|
||||
|
||||
def imu_callback(imu, vehicle_state):
|
||||
vehicle_state.bearing_deg = math.degrees(imu.compass)
|
||||
dat = messaging.new_message('sensorEvents', 2)
|
||||
@@ -240,13 +234,13 @@ def can_function_runner(vs: VehicleState, exit_event: threading.Event):
|
||||
|
||||
def connect_carla_client():
|
||||
client = carla.Client("127.0.0.1", 2000)
|
||||
client.set_timeout(10)
|
||||
client.set_timeout(5)
|
||||
return client
|
||||
|
||||
|
||||
class CarlaBridge:
|
||||
|
||||
def __init__(self, args):
|
||||
def __init__(self, arguments):
|
||||
set_params_enabled()
|
||||
|
||||
msg = messaging.new_message('liveCalibration')
|
||||
@@ -254,32 +248,48 @@ class CarlaBridge:
|
||||
msg.liveCalibration.rpyCalib = [0.0, 0.0, 0.0]
|
||||
Params().put("CalibrationParams", msg.to_bytes())
|
||||
|
||||
self._args = args
|
||||
self._args = arguments
|
||||
self._carla_objects = []
|
||||
self._camerad = None
|
||||
self._threads_exit_event = threading.Event()
|
||||
self._exit_event = threading.Event()
|
||||
self._threads = []
|
||||
self._shutdown = False
|
||||
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._shutdown = True
|
||||
self._keep_alive = False
|
||||
|
||||
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} ")
|
||||
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()
|
||||
world = client.load_world(self._args.town)
|
||||
|
||||
settings = world.get_settings()
|
||||
settings.synchronous_mode = True # Enables synchronous mode
|
||||
settings.fixed_delta_seconds = 0.05
|
||||
@@ -287,7 +297,7 @@ class CarlaBridge:
|
||||
|
||||
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.Buildings)
|
||||
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_y', str(H))
|
||||
blueprint.set_attribute('fov', str(fov))
|
||||
if self._args.low_quality:
|
||||
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)
|
||||
@@ -332,7 +342,7 @@ class CarlaBridge:
|
||||
|
||||
self._camerad = Camerad()
|
||||
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])
|
||||
|
||||
@@ -349,16 +359,13 @@ class CarlaBridge:
|
||||
|
||||
self._carla_objects.extend([imu, gps])
|
||||
# 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=peripheral_state_function, args=(self._threads_exit_event,)))
|
||||
self._threads.append(threading.Thread(target=fake_driver_monitoring, args=(self._threads_exit_event,)))
|
||||
self._threads.append(threading.Thread(target=can_function_runner, 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._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()
|
||||
|
||||
# can loop
|
||||
rk = Ratekeeper(100, print_delay_threshold=0.05)
|
||||
|
||||
# init
|
||||
throttle_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
|
||||
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
|
||||
# 2. Set instructions in Carla
|
||||
# 3. Send current carstate to op via can
|
||||
@@ -495,13 +509,9 @@ class CarlaBridge:
|
||||
rk.keep_time()
|
||||
self.started = True
|
||||
|
||||
# Clean up resources in the opposite order they were created.
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
self._threads_exit_event.set()
|
||||
if self._camerad is not None:
|
||||
self._camerad.close()
|
||||
self.started = False
|
||||
self._exit_event.set()
|
||||
for s in self._carla_objects:
|
||||
try:
|
||||
s.destroy()
|
||||
|
||||
@@ -38,7 +38,7 @@ def getch() -> str:
|
||||
def keyboard_poll_thread(q: 'Queue[str]'):
|
||||
while True:
|
||||
c = getch()
|
||||
# print("got %s" % c)
|
||||
print("got %s" % c)
|
||||
if c == '1':
|
||||
q.put("cruise_up")
|
||||
elif c == '2':
|
||||
|
||||
@@ -25,4 +25,4 @@ docker run \
|
||||
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
|
||||
-it \
|
||||
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
|
||||
"""
|
||||
processes = None
|
||||
carla_process = None
|
||||
|
||||
def setUp(self):
|
||||
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)
|
||||
|
||||
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.
|
||||
unblock_stdout()
|
||||
|
||||
def test_run_bridge(self):
|
||||
# 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}")
|
||||
# Wait 10 seconds to startup carla
|
||||
time.sleep(10)
|
||||
|
||||
def test_engage(self):
|
||||
# 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'])
|
||||
q = Queue()
|
||||
carla_bridge = CarlaBridge(bridge.parse_args(['--low_quality']))
|
||||
carla_bridge = CarlaBridge(bridge.parse_args([]))
|
||||
p_bridge = carla_bridge.run(q, retries=3)
|
||||
self.processes.append(p_bridge)
|
||||
|
||||
@@ -70,7 +61,7 @@ class TestCarlaIntegration(unittest.TestCase):
|
||||
no_car_events_issues_once = True
|
||||
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()
|
||||
min_counts_control_active = 100
|
||||
@@ -99,8 +90,11 @@ class TestCarlaIntegration(unittest.TestCase):
|
||||
p.wait(15)
|
||||
else:
|
||||
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__":
|
||||
|
||||
Reference in New Issue
Block a user