standalone process

pull/32380/head
Shane Smiskol 1 year ago
parent 3f9f33d25f
commit e2d62fa7b5
  1. 2
      cereal
  2. 47
      selfdrive/car/card.py
  3. 36
      selfdrive/controls/controlsd.py
  4. 1
      selfdrive/manager/process_config.py

@ -1 +1 @@
Subproject commit 84af7ef6654b2e810a2871b8ddd27ad170696599 Subproject commit c2adf2fbc460b3854334a9430b918565547b3757

@ -9,7 +9,7 @@ from cereal import car
from panda import ALTERNATIVE_EXPERIENCE from panda import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.selfdrive.car.car_helpers import get_car, get_one_can from openpilot.selfdrive.car.car_helpers import get_car, get_one_can
@ -18,19 +18,20 @@ from openpilot.selfdrive.car.interfaces import CarInterfaceBase
REPLAY = "REPLAY" in os.environ REPLAY = "REPLAY" in os.environ
class CarD: class Car:
CI: CarInterfaceBase CI: CarInterfaceBase
CS: car.CarState CS: car.CarState
def __init__(self, CI=None): def __init__(self, CI=None):
self.can_sock = messaging.sub_sock('can', timeout=20) self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates']) self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'controlsState'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput']) self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput'])
self.can_rcv_timeout_counter = 0 # consecutive timeout count self.can_rcv_timeout_counter = 0 # consecutive timeout count
self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count
self.CC_prev = car.CarControl.new_message() self.CC_prev = car.CarControl.new_message()
self.CoS_prev = car.CarState.new_message()
self.last_actuators = None self.last_actuators = None
@ -75,9 +76,8 @@ class CarD:
self.params.put_nonblocking("CarParamsCache", cp_bytes) self.params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes) self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
def initialize(self): # card is driven by can recv, expected at 100Hz
"""Initialize CarInterface, once controls are ready""" self.rk = Ratekeeper(100, print_delay_threshold=None)
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
def state_update(self): def state_update(self):
"""carState update loop, driven by can""" """carState update loop, driven by can"""
@ -102,10 +102,6 @@ class CarD:
if can_rcv_valid and REPLAY: if can_rcv_valid and REPLAY:
self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime
self.state_publish()
return self.CS
def state_publish(self): def state_publish(self):
"""carState and carParams publish loop""" """carState and carParams publish loop"""
@ -113,6 +109,9 @@ class CarD:
cs_send = messaging.new_message('carState') cs_send = messaging.new_message('carState')
cs_send.valid = self.CS.canValid cs_send.valid = self.CS.canValid
cs_send.carState = self.CS cs_send.carState = self.CS
cs_send.canRcvTimeout = self.can_rcv_timeout
cs_send.canErrorCounter = self.can_rcv_cum_timeout_counter
cs_send.cumLagMs = -self.rk.remaining * 1000.
self.pm.send('carState', cs_send) self.pm.send('carState', cs_send)
# carParams - logged every 50 seconds (> 1 per segment) # carParams - logged every 50 seconds (> 1 per segment)
@ -138,3 +137,31 @@ class CarD:
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid)) self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid))
self.CC_prev = CC self.CC_prev = CC
def step(self):
if self.sm['controlsState'].initialized and not self.CoS_prev.initialized:
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
self.state_update()
self.state_publish()
if not self.CP.passive and self.sm['controlsState'].initialized:
self.controls_update(self.sm['carControl'])
self.CoS_prev = self.sm['controlsState']
def card_thread(self):
while True:
self.step()
self.rk.monitor_time()
def main():
config_realtime_process(4, Priority.CTRL_HIGH)
car = Car()
car.card_thread()
if __name__ == "__main__":
main()

@ -18,8 +18,7 @@ from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.car.car_helpers import get_startup_event from openpilot.selfdrive.car.car_helpers import get_car_interface, get_startup_event
from openpilot.selfdrive.car.card import CarD
from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert
from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature
from openpilot.selfdrive.controls.lib.events import Events, ET from openpilot.selfdrive.controls.lib.events import Events, ET
@ -61,16 +60,16 @@ ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES)
class Controls: class Controls:
def __init__(self, CI=None): def __init__(self, CI=None):
self.card = CarD(CI)
self.params = Params() self.params = Params()
with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg: with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg:
# TODO: this shouldn't need to be a builder # TODO: this shouldn't need to be a builder
self.CP = msg.as_builder() self.CP = msg.as_builder()
self.CI = self.card.CI # TODO: if we want to remove usage of CI, we need to move controllers and other fields to card (like curvature & desiredCurvature)
# TODO: we need to ensure no CI calls here updates any state that we expect to be used to change sendcan over in card, because it won't
# TODO: perhaps there's a different attribute or object that just provides car-specific functions for long and lat
self.CI = get_car_interface(self.CP)
# Ensure the current branch is cached, otherwise the first iteration of controlsd lags # Ensure the current branch is cached, otherwise the first iteration of controlsd lags
self.branch = get_short_branch() self.branch = get_short_branch()
@ -89,7 +88,8 @@ class Controls:
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman', 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'testJoystick'] + self.camera_packets + self.sensor_packets, 'carState', 'testJoystick'] + self.camera_packets + self.sensor_packets,
poll='carState',
ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ], ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ],
frequency=int(1/DT_CTRL)) frequency=int(1/DT_CTRL))
@ -165,7 +165,7 @@ class Controls:
elif self.CP.passive: elif self.CP.passive:
self.events.add(EventName.dashcamMode, static=True) self.events.add(EventName.dashcamMode, static=True)
# controlsd is driven by can recv, expected at 100Hz # controlsd is driven by carState, expected at 100Hz
self.rk = Ratekeeper(100, print_delay_threshold=None) self.rk = Ratekeeper(100, print_delay_threshold=None)
def set_initial_state(self): def set_initial_state(self):
@ -324,7 +324,7 @@ class Controls:
# generic catch-all. ideally, a more specific event should be added above instead # generic catch-all. ideally, a more specific event should be added above instead
has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE)) has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE))
no_system_errors = (not has_disable_events) or (len(self.events) == num_events) no_system_errors = (not has_disable_events) or (len(self.events) == num_events)
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors: if (not self.sm.all_checks() or CS.canRcvTimeout) and no_system_errors:
if not self.sm.all_alive(): if not self.sm.all_alive():
self.events.add(EventName.commIssue) self.events.add(EventName.commIssue)
elif not self.sm.all_freq_ok(): elif not self.sm.all_freq_ok():
@ -336,7 +336,7 @@ class Controls:
'invalid': [s for s, valid in self.sm.valid.items() if not valid], 'invalid': [s for s, valid in self.sm.valid.items() if not valid],
'not_alive': [s for s, alive in self.sm.alive.items() if not alive], 'not_alive': [s for s, alive in self.sm.alive.items() if not alive],
'not_freq_ok': [s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok], 'not_freq_ok': [s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok],
'can_rcv_timeout': self.card.can_rcv_timeout, 'can_rcv_timeout': CS.canRcvTimeout,
} }
if logs != self.logged_comm_issue: if logs != self.logged_comm_issue:
cloudlog.event("commIssue", error=True, **logs) cloudlog.event("commIssue", error=True, **logs)
@ -398,9 +398,10 @@ class Controls:
def data_sample(self): def data_sample(self):
"""Receive data from sockets and update carState""" """Receive data from sockets and update carState"""
CS = self.card.state_update() # TODO: is this right?
self.sm.update(20)
self.sm.update(0) CS = self.sm['carState']
if not self.initialized: if not self.initialized:
all_valid = CS.canValid and self.sm.all_checks() all_valid = CS.canValid and self.sm.all_checks()
@ -412,8 +413,8 @@ class Controls:
if VisionStreamType.VISION_STREAM_WIDE_ROAD not in available_streams: if VisionStreamType.VISION_STREAM_WIDE_ROAD not in available_streams:
self.sm.ignore_alive.append('wideRoadCameraState') self.sm.ignore_alive.append('wideRoadCameraState')
if not self.CP.passive: # if not self.CP.passive:
self.card.initialize() # self.card.initialize()
self.initialized = True self.initialized = True
self.set_initial_state() self.set_initial_state()
@ -439,7 +440,7 @@ class Controls:
# All pandas not in silent mode must have controlsAllowed when openpilot is enabled # All pandas not in silent mode must have controlsAllowed when openpilot is enabled
if self.enabled and any(not ps.controlsAllowed for ps in self.sm['pandaStates'] if self.enabled and any(not ps.controlsAllowed for ps in self.sm['pandaStates']
if ps.safetyModel not in IGNORED_SAFETY_MODES): if ps.safetyModel not in IGNORED_SAFETY_MODES):
self.mismatch_counter += 1 self.mismatch_counter += 1
return CS return CS
@ -727,7 +728,7 @@ class Controls:
hudControl.visualAlert = current_alert.visual_alert hudControl.visualAlert = current_alert.visual_alert
if not self.CP.passive and self.initialized: if not self.CP.passive and self.initialized:
self.card.controls_update(CC) # self.card.controls_update(CC)
self.last_actuators = CO.actuatorsOutput self.last_actuators = CO.actuatorsOutput
if self.CP.steerControlType == car.CarParams.SteerControlType.angle: if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \ self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
@ -775,9 +776,10 @@ class Controls:
controlsState.cumLagMs = -self.rk.remaining * 1000. controlsState.cumLagMs = -self.rk.remaining * 1000.
controlsState.startMonoTime = int(start_time * 1e9) controlsState.startMonoTime = int(start_time * 1e9)
controlsState.forceDecel = bool(force_decel) controlsState.forceDecel = bool(force_decel)
controlsState.canErrorCounter = self.card.can_rcv_cum_timeout_counter # controlsState.canErrorCounter = self.card.can_rcv_cum_timeout_counter
controlsState.experimentalMode = self.experimental_mode controlsState.experimentalMode = self.experimental_mode
controlsState.personality = self.personality controlsState.personality = self.personality
controlsState.initialized = self.initialized
lat_tuning = self.CP.lateralTuning.which() lat_tuning = self.CP.lateralTuning.which()
if self.joystick_mode: if self.joystick_mode:

@ -64,6 +64,7 @@ procs = [
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad), PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad),
PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad), PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad),
PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad), PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad),
PythonProcess("card", "selfdrive.car.card", only_onroad),
PythonProcess("deleter", "system.loggerd.deleter", always_run), PythonProcess("deleter", "system.loggerd.deleter", always_run),
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)), PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),

Loading…
Cancel
Save