fix: sim: gracefully shutdown the bridge (#21413)

pull/21440/head
orphen 4 years ago committed by GitHub
parent 6395fc3abd
commit 325cabd4da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .pylintrc
  2. 52
      tools/sim/bridge.py
  3. 5
      tools/sim/lib/keyboard_ctrl.py

@ -161,7 +161,7 @@ contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference # List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular # system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted. # expressions are accepted.
generated-members=capnp.* cereal.* pygame.* zmq.* setproctitle.* smbus2.* usb1.* serial.* cv2.* ft4222.* generated-members=capnp.* cereal.* pygame.* zmq.* setproctitle.* smbus2.* usb1.* serial.* cv2.* ft4222.* carla.*
# Tells whether missing members accessed in mixin class should be ignored. A # Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive). # mixin class is detected if its name ends with "mixin" (case insensitive).

@ -1,12 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import atexit
import carla # pylint: disable=import-error import carla # pylint: disable=import-error
import math import math
import numpy as np import numpy as np
import time import time
import threading import threading
from cereal import log from cereal import log
from multiprocessing import Process, Queue
from typing import Any from typing import Any
import cereal.messaging as messaging import cereal.messaging as messaging
@ -82,9 +82,9 @@ def imu_callback(imu, vehicle_state):
dat.sensorEvents[1].gyroUncalibrated.v = [imu.gyroscope.x, imu.gyroscope.y, imu.gyroscope.z] dat.sensorEvents[1].gyroUncalibrated.v = [imu.gyroscope.x, imu.gyroscope.y, imu.gyroscope.z]
pm.send('sensorEvents', dat) pm.send('sensorEvents', dat)
def panda_state_function(): def panda_state_function(exit_event: threading.Event):
pm = messaging.PubMaster(['pandaState']) pm = messaging.PubMaster(['pandaState'])
while 1: while not exit_event.is_set():
dat = messaging.new_message('pandaState') dat = messaging.new_message('pandaState')
dat.valid = True dat.valid = True
dat.pandaState = { dat.pandaState = {
@ -125,10 +125,9 @@ def gps_callback(gps, vehicle_state):
pm.send('gpsLocationExternal', dat) pm.send('gpsLocationExternal', dat)
def fake_driver_monitoring(): def fake_driver_monitoring(exit_event: threading.Event):
pm = messaging.PubMaster(['driverState','driverMonitoringState']) pm = messaging.PubMaster(['driverState','driverMonitoringState'])
while 1: while not exit_event.is_set():
# dmonitoringmodeld output # dmonitoringmodeld output
dat = messaging.new_message('driverState') dat = messaging.new_message('driverState')
dat.driverState.faceProb = 1.0 dat.driverState.faceProb = 1.0
@ -145,9 +144,9 @@ def fake_driver_monitoring():
time.sleep(DT_DMON) time.sleep(DT_DMON)
def can_function_runner(vs): def can_function_runner(vs: VehicleState, exit_event: threading.Event):
i = 0 i = 0
while 1: while not exit_event.is_set():
can_function(pm, vs.speed, vs.angle, i, vs.cruise_button, vs.is_engaged) can_function(pm, vs.speed, vs.angle, i, vs.cruise_button, vs.is_engaged)
time.sleep(0.01) time.sleep(0.01)
i+=1 i+=1
@ -211,18 +210,14 @@ 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))
def destroy():
print("clean exit")
imu.destroy()
camera.destroy()
vehicle.destroy()
print("done")
atexit.register(destroy)
# launch fake car threads # launch fake car threads
threading.Thread(target=panda_state_function).start() threads = []
threading.Thread(target=fake_driver_monitoring).start() exit_event = threading.Event()
threading.Thread(target=can_function_runner, args=(vehicle_state,)).start() threads.append(threading.Thread(target=panda_state_function, args=(exit_event,)))
threads.append(threading.Thread(target=fake_driver_monitoring, args=(exit_event,)))
threads.append(threading.Thread(target=can_function_runner, args=(vehicle_state, exit_event,)))
for t in threads:
t.start()
# can loop # can loop
rk = Ratekeeper(100, print_delay_threshold=0.05) rk = Ratekeeper(100, print_delay_threshold=0.05)
@ -283,6 +278,8 @@ def bridge(q):
elif m[1] == "cancel": elif m[1] == "cancel":
cruise_button = CruiseButtons.CANCEL cruise_button = CruiseButtons.CANCEL
is_openpilot_engaged = False is_openpilot_engaged = False
elif m[0] == "quit":
break
throttle_out = throttle_manual * throttle_manual_multiplier throttle_out = throttle_manual * throttle_manual_multiplier
steer_out = steer_manual * steer_manual_multiplier steer_out = steer_manual * steer_manual_multiplier
@ -361,10 +358,20 @@ def bridge(q):
rk.keep_time() rk.keep_time()
def go(q: Any): # Clean up resources in the opposite order they were created.
exit_event.set()
for t in reversed(threads):
t.join()
gps.destroy()
imu.destroy()
camera.destroy()
vehicle.destroy()
def bridge_keep_alive(q: Any):
while 1: while 1:
try: try:
bridge(q) bridge(q)
break
except RuntimeError: except RuntimeError:
print("Restarting bridge...") print("Restarting bridge...")
@ -377,16 +384,15 @@ if __name__ == "__main__":
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())
from multiprocessing import Process, Queue
q: Any = Queue() q: Any = Queue()
p = Process(target=go, args=(q,)) p = Process(target=bridge_keep_alive, args=(q,), daemon=True)
p.daemon = True
p.start() p.start()
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 lib.manual_ctrl import wheel_poll_thread
wheel_poll_thread(q) wheel_poll_thread(q)
p.join()
else: else:
# start input poll for keyboard # start input poll for keyboard
from lib.keyboard_ctrl import keyboard_poll_thread from lib.keyboard_ctrl import keyboard_poll_thread

@ -35,7 +35,7 @@ def getch() -> str:
termios.tcsetattr(STDIN_FD, termios.TCSADRAIN, old_settings) termios.tcsetattr(STDIN_FD, termios.TCSADRAIN, old_settings)
return ch return ch
def keyboard_poll_thread(q: 'Queue[str]') -> NoReturn: def keyboard_poll_thread(q: 'Queue[str]'):
while True: while True:
c = getch() c = getch()
# print("got %s" % c) # print("got %s" % c)
@ -54,7 +54,8 @@ def keyboard_poll_thread(q: 'Queue[str]') -> NoReturn:
elif c == 'd': elif c == 'd':
q.put("steer_%f" % -0.15) q.put("steer_%f" % -0.15)
elif c == 'q': elif c == 'q':
exit(0) q.put("quit")
break
def test(q: 'Queue[str]') -> NoReturn: def test(q: 'Queue[str]') -> NoReturn:
while True: while True:

Loading…
Cancel
Save