diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index ad244323f4..feb3b667d0 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -13,6 +13,7 @@ VisualAlert = car.CarControl.HUDControl.VisualAlert AudibleAlert = car.CarControl.HUDControl.AudibleAlert EventName = car.CarEvent.EventName + # Alert priorities class Priority(IntEnum): LOWEST = 0 @@ -22,6 +23,7 @@ class Priority(IntEnum): HIGH = 4 HIGHEST = 5 + # Event types class ET: ENABLE = 'enable' @@ -33,6 +35,7 @@ class ET: IMMEDIATE_DISABLE = 'immediateDisable' PERMANENT = 'permanent' + # get event name from enum EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()} @@ -56,7 +59,7 @@ class Events: self.events.append(event_name) def clear(self): - self.events_prev = {k: (v+1 if k in self.events else 0) for k, v in self.events_prev.items()} + self.events_prev = {k: (v + 1 if k in self.events else 0) for k, v in self.events_prev.items()} self.events = self.static_events.copy() def any(self, event_type): @@ -94,10 +97,11 @@ class Events: event = car.CarEvent.new_message() event.name = event_name for event_type in EVENTS.get(event_name, {}).keys(): - setattr(event, event_type , True) + setattr(event, event_type, True) ret.append(event) return ret + class Alert: def __init__(self, alert_text_1: str, @@ -138,6 +142,7 @@ class Alert: def __gt__(self, alert2) -> bool: return self.alert_priority > alert2.alert_priority + class NoEntryAlert(Alert): def __init__(self, alert_text_2, audible_alert=AudibleAlert.chimeError, visual_alert=VisualAlert.none, duration_hud_alert=2.): @@ -161,6 +166,7 @@ class ImmediateDisableAlert(Alert): Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.), + class EngagementAlert(Alert): def __init__(self, audible_alert=True): super().__init__("", "", @@ -168,14 +174,15 @@ class EngagementAlert(Alert): Priority.MID, VisualAlert.none, audible_alert, .2, 0., 0.), + class NormalPermanentAlert(Alert): def __init__(self, alert_text_1: str, alert_text_2: str, duration_text: float = 0.2): super().__init__(alert_text_1, alert_text_2, AlertStatus.normal, AlertSize.mid if len(alert_text_2) else AlertSize.small, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., duration_text), -# ********** alert callback functions ********** +# ********** alert callback functions ********** def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: speed = int(round(CP.minSteerSpeed * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))) unit = "km/h" if metric else "mph" @@ -185,6 +192,7 @@ def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.none, 0., 0.4, .3) + def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: speed = int(MIN_SPEED_FILTER * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)) unit = "km/h" if metric else "mph" @@ -194,6 +202,7 @@ def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, met AlertStatus.normal, AlertSize.mid, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2) + def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: gps_integrated = sm['pandaState'].pandaType in [log.PandaState.PandaType.uno, log.PandaState.PandaType.dos] return Alert( @@ -202,12 +211,14 @@ def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Al AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=300.) + def wrong_car_mode_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: text = "Cruise Mode Disabled" if CP.carName == "honda": text = "Main Switch Off" return NoEntryAlert(text, duration_hud_alert=0.) + def startup_fuzzy_fingerprint_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: return Alert( "WARNING: No Exact Match on Car Model", @@ -215,6 +226,7 @@ def startup_fuzzy_fingerprint_alert(CP: car.CarParams, sm: messaging.SubMaster, AlertStatus.userPrompt, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.) + EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, bool], Alert]]]] = { # ********** events with no alerts ********** @@ -248,6 +260,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, + # Car is recognized, but marked as dashcam only EventName.startupNoControl: { ET.PERMANENT: Alert( "Dashcam mode", @@ -256,6 +269,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, + # Car is not recognized EventName.startupNoCar: { ET.PERMANENT: Alert( "Dashcam mode for unsupported car", @@ -264,6 +278,14 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, + # openpilot uses the version strings from various ECUs to detect the correct car model. + # Usually all ECUs are recognized and an exact match to a car model can be made. Sometimes + # one or two ECUs have unrecognized versions, but the others are present in the database. + # If openpilot is confident about the match to a car model, it fingerprints anyway. + # In this case an alert is thrown since there is a small chance the wrong car was detected + # and the user should pay extra attention. + # This alert can be prevented by adding all ECU firmware version to openpilot: + # https://github.com/commaai/openpilot/wiki/Fingerprinting EventName.startupFuzzyFingerprint: { ET.PERMANENT: startup_fuzzy_fingerprint_alert, }, @@ -292,6 +314,9 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, + # Some features or cars are marked as community features. If openpilot + # detects the use of a community feature it switches to dashcam mode + # until these features are allowed using a toggle in settings. EventName.communityFeatureDisallowed: { # LOW priority to overcome Cruise Error ET.PERMANENT: Alert( @@ -301,6 +326,9 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2), }, + # openpilot doesn't recognize the car. This switches openpilot into a + # read-only mode. This can be solved by adding your fingerprint. + # See https://github.com/commaai/openpilot/wiki/Fingerprinting for more information EventName.carUnrecognized: { ET.PERMANENT: Alert( "Dashcam Mode", @@ -353,6 +381,14 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .0, .0, .1, creation_delay=1.), }, + # openpilot tries to learn certain parameters about your car by observing + # how the car behaves to steering inputs from both human and openpilot driving. + # This includes: + # - steer ratio: gear ratio of the steering rack. Steering angle divided by tire angle + # - tire stiffness: how much grip your tires have + # - angle offset: most steering angle sensors are offset and measure a non zero angle when driving straight + # This alert is thrown when any of these values exceed a sanity check. This can be caused by + # bad alignment or bad sensor data. If this happens consistently consider creating an issue on GitHub EventName.vehicleModelInvalid: { ET.NO_ENTRY: NoEntryAlert("Vehicle Parameter Identification Failed"), ET.SOFT_DISABLE: SoftDisableAlert("Vehicle Parameter Identification Failed"), @@ -487,18 +523,24 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 1., 1.), }, + # Thrown when the fan is driven at >50% but is not rotating EventName.fanMalfunction: { ET.PERMANENT: NormalPermanentAlert("Fan Malfunction", "Contact Support"), }, + # Camera is not outputting frames at a constant framerate EventName.cameraMalfunction: { ET.PERMANENT: NormalPermanentAlert("Camera Malfunction", "Contact Support"), }, + # Unused EventName.gpsMalfunction: { ET.PERMANENT: NormalPermanentAlert("GPS Malfunction", "Contact Support"), }, + # When the GPS position and localizer diverge the localizer is reset to the + # current GPS position. This alert is thrown when the localizer is reset + # more often than expected. EventName.localizerMalfunction: { ET.PERMANENT: NormalPermanentAlert("Localizer unstable", "Contact Support"), }, @@ -604,6 +646,11 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo ET.NO_ENTRY: NoEntryAlert("Gear not D"), }, + # This alert is thrown when the calibration angles are outside of the acceptable range. + # For example if the device is pointed too much to the left or the right. + # Usually this can only be solved by removing the mount from the windshield completely, + # and attaching while making sure the device is pointed straight forward and is level. + # See https://comma.ai/setup for more information EventName.calibrationInvalid: { ET.PERMANENT: NormalPermanentAlert("Calibration Invalid", "Remount Device and Recalibrate"), ET.SOFT_DISABLE: SoftDisableAlert("Calibration Invalid: Remount Device & Recalibrate"), @@ -636,12 +683,17 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo ET.NO_ENTRY: NoEntryAlert("Low Battery"), }, + # Different openpilot services communicate between each other at a certain + # interval. If communication does not follow the regular schedule this alert + # is thrown. This can mean a service crashed, did not broadcast a message for + # ten times the regular interval, or the average interval is more than 10% too high. EventName.commIssue: { ET.SOFT_DISABLE: SoftDisableAlert("Communication Issue between Processes"), ET.NO_ENTRY: NoEntryAlert("Communication Issue between Processes", audible_alert=AudibleAlert.chimeDisengage), }, + # Thrown when manager detects a service exited unexpectedly while driving EventName.processNotRunning: { ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device", audible_alert=AudibleAlert.chimeDisengage), @@ -649,19 +701,29 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo EventName.radarFault: { ET.SOFT_DISABLE: SoftDisableAlert("Radar Error: Restart the Car"), - ET.NO_ENTRY : NoEntryAlert("Radar Error: Restart the Car"), + ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"), }, + # Every frame from the camera should be processed by the model. If modeld + # is not processing frames fast enough they have to be dropped. This alert is + # thrown when over 20% of frames are dropped. EventName.modeldLagging: { ET.SOFT_DISABLE: SoftDisableAlert("Driving model lagging"), - ET.NO_ENTRY : NoEntryAlert("Driving model lagging"), + ET.NO_ENTRY: NoEntryAlert("Driving model lagging"), }, + # Besides predicting the path, lane lines and lead car data the model also + # predicts the current velocity and rotation speed of the car. If the model is + # very uncertain about the current velocity while the car is moving, this + # usually means the model has trouble understanding the scene. This is used + # as a heuristic to warn the driver. EventName.posenetInvalid: { ET.SOFT_DISABLE: SoftDisableAlert("Model Output Uncertain"), ET.NO_ENTRY: NoEntryAlert("Model Output Uncertain"), }, + # When the localizer detects an acceleration of more than 40 m/s^2 (~4G) we + # alert the driver the device might have fallen from the windshield. EventName.deviceFalling: { ET.SOFT_DISABLE: SoftDisableAlert("Device Fell Off Mount"), ET.NO_ENTRY: NoEntryAlert("Device Fell Off Mount"), @@ -670,7 +732,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo EventName.lowMemory: { ET.SOFT_DISABLE: SoftDisableAlert("Low Memory: Reboot Your Device"), ET.PERMANENT: NormalPermanentAlert("Low Memory", "Reboot your Device"), - ET.NO_ENTRY : NoEntryAlert("Low Memory: Reboot Your Device", + ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device", audible_alert=AudibleAlert.chimeDisengage), }, @@ -699,12 +761,18 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo duration_text=10.), }, + # Sometimes the USB stack on the device can get into a bad state + # causing the connection to the panda to be lost EventName.usbError: { ET.SOFT_DISABLE: SoftDisableAlert("USB Error: Reboot Your Device"), ET.PERMANENT: NormalPermanentAlert("USB Error: Reboot Your Device", ""), ET.NO_ENTRY: NoEntryAlert("USB Error: Reboot Your Device"), }, + # This alert can be thrown for the following reasons: + # - No CAN data received at all + # - CAN data is received, but some message are not received at the right frequency + # If you're not writing a new car port, this is usually cause by faulty wiring EventName.canError: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("CAN Error: Check Connections"), ET.PERMANENT: Alert( @@ -745,15 +813,24 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo ET.NO_ENTRY: NoEntryAlert("Reverse Gear"), }, + # On cars that use stock ACC the car can decide to cancel ACC for various reasons. + # When this happens we can no long control the car so the user needs to be warned immediately. EventName.cruiseDisabled: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Is Off"), }, + # For planning the trajectory Model Predictive Control (MPC) is used. This is + # an optimization algorithm that is not guaranteed to find a feasible solution. + # If no solution is found or the solution has a very high cost this alert is thrown. EventName.plannerError: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Planner Solution Error"), ET.NO_ENTRY: NoEntryAlert("Planner Solution Error"), }, + # When the relay in the harness box opens the CAN bus between the LKAS camera + # and the rest of the car is separated. When messages from the LKAS camera + # are received on the car side this usually means the relay hasn't opened correctly + # and this alert is thrown. EventName.relayMalfunction: { ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Harness Malfunction"), ET.PERMANENT: NormalPermanentAlert("Harness Malfunction", "Check Hardware"), @@ -766,7 +843,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo "No close lead car", AlertStatus.normal, AlertSize.mid, Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.), - ET.NO_ENTRY : NoEntryAlert("No Close Lead Car"), + ET.NO_ENTRY: NoEntryAlert("No Close Lead Car"), }, EventName.speedTooLow: { @@ -777,6 +854,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.), }, + # When the car is driving faster than most cars in the training data the model outputs can be unpredictable EventName.speedTooHigh: { ET.WARNING: Alert( "Speed Too High",