mirror of https://github.com/commaai/openpilot.git
parent
05a09435b0
commit
48340cc8cb
2
cereal
2
cereal
|
@ -1 +1 @@
|
|||
Subproject commit 4f68db8f6aa31e87d968da882460e196c6b101a3
|
||||
Subproject commit 856c9812d552fe0ac640b75074b080f76c9a3cba
|
|
@ -202,8 +202,8 @@ selfdrive/controls/radard.py
|
|||
selfdrive/controls/dmonitoringd.py
|
||||
selfdrive/controls/lib/__init__.py
|
||||
selfdrive/controls/lib/alertmanager.py
|
||||
selfdrive/controls/lib/alerts.py
|
||||
selfdrive/controls/lib/alerts_offroad.json
|
||||
selfdrive/controls/lib/events.py
|
||||
selfdrive/controls/lib/drive_helpers.py
|
||||
selfdrive/controls/lib/driver_monitor.py
|
||||
selfdrive/controls/lib/latcontrol_pid.py
|
||||
|
|
|
@ -9,17 +9,18 @@ import cereal.messaging as messaging
|
|||
from selfdrive.car import gen_empty_fingerprint
|
||||
|
||||
from cereal import car
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
def get_startup_alert(car_recognized, controller_available):
|
||||
alert = 'startup'
|
||||
def get_startup_event(car_recognized, controller_available):
|
||||
event = EventName.startup
|
||||
if Params().get("GitRemote", encoding="utf8") in ['git@github.com:commaai/openpilot.git', 'https://github.com/commaai/openpilot.git']:
|
||||
if Params().get("GitBranch", encoding="utf8") not in ['devel', 'release2-staging', 'dashcam-staging', 'release2', 'dashcam']:
|
||||
alert = 'startupMaster'
|
||||
event = EventName.startupMaster
|
||||
if not car_recognized:
|
||||
alert = 'startupNoCar'
|
||||
event = EventName.startupNoCar
|
||||
elif car_recognized and not controller_available:
|
||||
alert = 'startupNoControl'
|
||||
return alert
|
||||
event = EventName.startupNoControl
|
||||
return event
|
||||
|
||||
|
||||
def load_interfaces(brand_names):
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
|
||||
from selfdrive.car.chrysler.values import Ecu, ECU_FINGERPRINT, CAR, FINGERPRINTS
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
@ -71,12 +70,13 @@ class CarInterface(CarInterfaceBase):
|
|||
ret.buttonEvents = []
|
||||
|
||||
# events
|
||||
events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.low], gas_resume_speed=2.)
|
||||
events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.low], \
|
||||
gas_resume_speed=2.)
|
||||
|
||||
if ret.vEgo < self.CP.minSteerSpeed:
|
||||
events.append(create_event('belowSteerSpeed', [ET.WARNING]))
|
||||
events.add(car.CarEvent.EventName.belowSteerSpeed)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
# copy back carState packet to CS
|
||||
self.CS.out = ret.as_reader()
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
from cereal import car
|
||||
from selfdrive.swaglog import cloudlog
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
|
||||
from selfdrive.car.ford.values import MAX_ANGLE, Ecu, ECU_FINGERPRINT, FINGERPRINTS
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
@ -62,9 +61,9 @@ class CarInterface(CarInterfaceBase):
|
|||
events = self.create_common_events(ret)
|
||||
|
||||
if self.CS.lkas_state not in [2, 3] and ret.vEgo > 13.* CV.MPH_TO_MS and ret.cruiseState.enabled:
|
||||
events.append(create_event('steerTempUnavailableMute', [ET.WARNING]))
|
||||
events.add(car.CarEvent.EventName.steerTempUnavailableMute)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
self.CS.out = ret.as_reader()
|
||||
return self.CS.out
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET
|
||||
from selfdrive.car.gm.values import CAR, Ecu, ECU_FINGERPRINT, CruiseButtons, \
|
||||
AccState, FINGERPRINTS
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
|
||||
|
@ -149,24 +149,24 @@ class CarInterface(CarInterfaceBase):
|
|||
events = self.create_common_events(ret, pcm_enable=False)
|
||||
|
||||
if ret.vEgo < self.CP.minEnableSpeed:
|
||||
events.append(create_event('speedTooLow', [ET.NO_ENTRY]))
|
||||
events.add(EventName.speedTooLow)
|
||||
if self.CS.park_brake:
|
||||
events.append(create_event('parkBrake', [ET.NO_ENTRY, ET.USER_DISABLE]))
|
||||
events.add(EventName.parkBrake)
|
||||
if ret.cruiseState.standstill:
|
||||
events.append(create_event('resumeRequired', [ET.WARNING]))
|
||||
events.add(EventName.resumeRequired)
|
||||
if self.CS.pcm_acc_status == AccState.FAULTED:
|
||||
events.append(create_event('controlsFailed', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
|
||||
events.add(EventName.controlsFailed)
|
||||
|
||||
# handle button presses
|
||||
for b in ret.buttonEvents:
|
||||
# do enable on both accel and decel buttons
|
||||
if b.type in [ButtonType.accelCruise, ButtonType.decelCruise] and not b.pressed:
|
||||
events.append(create_event('buttonEnable', [ET.ENABLE]))
|
||||
events.add(EventName.buttonEnable)
|
||||
# do disable on button down
|
||||
if b.type == ButtonType.cancel and b.pressed:
|
||||
events.append(create_event('buttonCancel', [ET.USER_DISABLE]))
|
||||
events.add(EventName.buttonCancel)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
# copy back carState packet to CS
|
||||
self.CS.out = ret.as_reader()
|
||||
|
|
|
@ -5,7 +5,7 @@ from common.numpy_fast import clip, interp
|
|||
from common.realtime import DT_CTRL
|
||||
from selfdrive.swaglog import cloudlog
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET, get_events
|
||||
from selfdrive.controls.lib.events import ET
|
||||
from selfdrive.car.honda.values import CruiseButtons, CAR, HONDA_BOSCH, Ecu, ECU_FINGERPRINT, FINGERPRINTS
|
||||
from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
|
||||
from selfdrive.controls.lib.planner import _A_CRUISE_MAX_V_FOLLOWING
|
||||
|
@ -14,6 +14,7 @@ from selfdrive.car.interfaces import CarInterfaceBase
|
|||
A_ACC_MAX = max(_A_CRUISE_MAX_V_FOLLOWING)
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
def compute_gb_honda(accel, speed):
|
||||
creep_brake = 0.0
|
||||
|
@ -468,25 +469,25 @@ class CarInterface(CarInterfaceBase):
|
|||
# events
|
||||
events = self.create_common_events(ret, pcm_enable=False)
|
||||
if self.CS.brake_error:
|
||||
events.append(create_event('brakeUnavailable', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE, ET.PERMANENT]))
|
||||
events.add(EventName.brakeUnavailable)
|
||||
if self.CS.brake_hold and self.CS.CP.carFingerprint not in HONDA_BOSCH:
|
||||
events.append(create_event('brakeHold', [ET.NO_ENTRY, ET.USER_DISABLE]))
|
||||
events.add(EventName.brakeHold)
|
||||
if self.CS.park_brake:
|
||||
events.append(create_event('parkBrake', [ET.NO_ENTRY, ET.USER_DISABLE]))
|
||||
events.add(EventName.parkBrake)
|
||||
|
||||
if self.CP.enableCruise and ret.vEgo < self.CP.minEnableSpeed:
|
||||
events.append(create_event('speedTooLow', [ET.NO_ENTRY]))
|
||||
events.add(EventName.speedTooLow)
|
||||
|
||||
# it can happen that car cruise disables while comma system is enabled: need to
|
||||
# keep braking if needed or if the speed is very low
|
||||
if self.CP.enableCruise and not ret.cruiseState.enabled and (c.actuators.brake <= 0. or not self.CP.openpilotLongitudinalControl):
|
||||
# non loud alert if cruise disbales below 25mph as expected (+ a little margin)
|
||||
if ret.vEgo < self.CP.minEnableSpeed + 2.:
|
||||
events.append(create_event('speedTooLow', [ET.IMMEDIATE_DISABLE]))
|
||||
events.add(EventName.speedTooLow)
|
||||
else:
|
||||
events.append(create_event("cruiseDisabled", [ET.IMMEDIATE_DISABLE]))
|
||||
events.add(EventName.cruiseDisabled)
|
||||
if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001:
|
||||
events.append(create_event('manualRestart', [ET.WARNING]))
|
||||
events.add(EventName.manualRestart)
|
||||
|
||||
cur_time = self.frame * DT_CTRL
|
||||
enable_pressed = False
|
||||
|
@ -500,7 +501,7 @@ class CarInterface(CarInterfaceBase):
|
|||
|
||||
# do disable on button down
|
||||
if b.type == "cancel" and b.pressed:
|
||||
events.append(create_event('buttonCancel', [ET.USER_DISABLE]))
|
||||
events.add(EventName.buttonCancel)
|
||||
|
||||
if self.CP.enableCruise:
|
||||
# KEEP THIS EVENT LAST! send enable event if button is pressed and there are
|
||||
|
@ -510,13 +511,13 @@ class CarInterface(CarInterfaceBase):
|
|||
if ((cur_time - self.last_enable_pressed) < 0.2 and
|
||||
(cur_time - self.last_enable_sent) > 0.2 and
|
||||
ret.cruiseState.enabled) or \
|
||||
(enable_pressed and get_events(events, [ET.NO_ENTRY])):
|
||||
events.append(create_event('buttonEnable', [ET.ENABLE]))
|
||||
(enable_pressed and events.any(ET.NO_ENTRY)):
|
||||
events.add(EventName.buttonEnable)
|
||||
self.last_enable_sent = cur_time
|
||||
elif enable_pressed:
|
||||
events.append(create_event('buttonEnable', [ET.ENABLE]))
|
||||
events.add(EventName.buttonEnable)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
self.CS.out = ret.as_reader()
|
||||
return self.CS.out
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
|
||||
from selfdrive.car.hyundai.values import Ecu, ECU_FINGERPRINT, CAR, FINGERPRINTS
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
@ -180,9 +179,9 @@ class CarInterface(CarInterfaceBase):
|
|||
if ret.vEgo > (self.CP.minSteerSpeed + 4.):
|
||||
self.low_speed_alert = False
|
||||
if self.low_speed_alert:
|
||||
events.append(create_event('belowSteerSpeed', [ET.WARNING]))
|
||||
events.add(car.CarEvent.EventName.belowSteerSpeed)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
self.CS.out = ret.as_reader()
|
||||
return self.CS.out
|
||||
|
|
|
@ -5,10 +5,11 @@ from common.kalman.simple_kalman import KF1D
|
|||
from common.realtime import DT_CTRL
|
||||
from selfdrive.car import gen_empty_fingerprint
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
|
||||
from selfdrive.controls.lib.events import Events
|
||||
from selfdrive.controls.lib.vehicle_model import VehicleModel
|
||||
|
||||
GearShifter = car.CarState.GearShifter
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
# generic car and radar interfaces
|
||||
|
||||
|
@ -81,45 +82,47 @@ class CarInterfaceBase():
|
|||
raise NotImplementedError
|
||||
|
||||
def create_common_events(self, cs_out, extra_gears=[], gas_resume_speed=-1, pcm_enable=True):
|
||||
events = []
|
||||
events = Events()
|
||||
|
||||
if cs_out.doorOpen:
|
||||
events.append(create_event('doorOpen', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
events.add(EventName.doorOpen)
|
||||
if cs_out.seatbeltUnlatched:
|
||||
events.append(create_event('seatbeltNotLatched', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
events.add(EventName.seatbeltNotLatched)
|
||||
if cs_out.gearShifter != GearShifter.drive and cs_out.gearShifter not in extra_gears:
|
||||
events.append(create_event('wrongGear', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
events.add(EventName.wrongGear)
|
||||
if cs_out.gearShifter == GearShifter.reverse:
|
||||
events.append(create_event('reverseGear', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
|
||||
events.add(EventName.reverseGear)
|
||||
if not cs_out.cruiseState.available:
|
||||
events.append(create_event('wrongCarMode', [ET.NO_ENTRY, ET.USER_DISABLE]))
|
||||
events.add(EventName.wrongCarMode)
|
||||
if cs_out.espDisabled:
|
||||
events.append(create_event('espDisabled', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
events.add(EventName.espDisabled)
|
||||
if cs_out.gasPressed:
|
||||
events.append(create_event('pedalPressed', [ET.PRE_ENABLE]))
|
||||
events.add(EventName.gasPressed)
|
||||
if cs_out.stockFcw:
|
||||
events.add(EventName.stockFcw)
|
||||
if cs_out.stockAeb:
|
||||
events.append(create_event('stockAeb', []))
|
||||
events.add(EventName.stockAeb)
|
||||
if cs_out.vEgo > 92 * CV.MPH_TO_MS:
|
||||
events.append(create_event('speedTooHigh', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
events.add(EventName.speedTooHigh)
|
||||
|
||||
if cs_out.steerError:
|
||||
events.append(create_event('steerUnavailable', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE, ET.PERMANENT]))
|
||||
events.add(EventName.steerUnavailable)
|
||||
elif cs_out.steerWarning:
|
||||
events.append(create_event('steerTempUnavailable', [ET.NO_ENTRY, ET.WARNING]))
|
||||
events.add(EventName.steerTempUnavailable)
|
||||
|
||||
# Disable on rising edge of gas or brake. Also disable on brake when speed > 0.
|
||||
# Optionally allow to press gas at zero speed to resume.
|
||||
# e.g. Chrysler does not spam the resume button yet, so resuming with gas is handy. FIXME!
|
||||
if (cs_out.gasPressed and (not self.CS.out.gasPressed) and cs_out.vEgo > gas_resume_speed) or \
|
||||
(cs_out.brakePressed and (not self.CS.out.brakePressed or not cs_out.standstill)):
|
||||
events.append(create_event('pedalPressed', [ET.NO_ENTRY, ET.USER_DISABLE]))
|
||||
events.add(EventName.pedalPressed)
|
||||
|
||||
# we engage when pcm is active (rising edge)
|
||||
if pcm_enable:
|
||||
if cs_out.cruiseState.enabled and not self.CS.out.cruiseState.enabled:
|
||||
events.append(create_event('pcmEnable', [ET.ENABLE]))
|
||||
events.add(EventName.pcmEnable)
|
||||
elif not cs_out.cruiseState.enabled:
|
||||
events.append(create_event('pcmDisable', [ET.USER_DISABLE]))
|
||||
events.add(EventName.pcmDisable)
|
||||
|
||||
return events
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET
|
||||
from selfdrive.car.nissan.values import CAR
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
@ -77,9 +76,9 @@ class CarInterface(CarInterfaceBase):
|
|||
events = self.create_common_events(ret)
|
||||
|
||||
if self.CS.lkas_enabled:
|
||||
events.append(create_event('invalidLkasSetting', [ET.PERMANENT]))
|
||||
events.add(car.CarEvent.EventName.invalidLkasSetting)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
self.CS.out = ret.as_reader()
|
||||
return self.CS.out
|
||||
|
|
|
@ -63,7 +63,7 @@ class CarInterface(CarInterfaceBase):
|
|||
be.type = car.CarState.ButtonEvent.Type.accelCruise
|
||||
buttonEvents.append(be)
|
||||
|
||||
ret.events = self.create_common_events(ret)
|
||||
ret.events = self.create_common_events(ret).to_msg()
|
||||
|
||||
self.CS.out = ret.as_reader()
|
||||
return self.CS.out
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
|
||||
from selfdrive.car.toyota.values import Ecu, ECU_FINGERPRINT, CAR, TSS2_CAR, FINGERPRINTS
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
|
||||
from selfdrive.swaglog import cloudlog
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
|
@ -297,19 +297,19 @@ class CarInterface(CarInterfaceBase):
|
|||
events = self.create_common_events(ret)
|
||||
|
||||
if self.cp_cam.can_invalid_cnt >= 200 and self.CP.enableCamera:
|
||||
events.append(create_event('invalidGiraffeToyota', [ET.PERMANENT]))
|
||||
events.add(EventName.invalidGiraffeToyota)
|
||||
if self.CS.low_speed_lockout and self.CP.openpilotLongitudinalControl:
|
||||
events.append(create_event('lowSpeedLockout', [ET.NO_ENTRY, ET.PERMANENT]))
|
||||
events.add(EventName.lowSpeedLockout)
|
||||
if ret.vEgo < self.CP.minEnableSpeed and self.CP.openpilotLongitudinalControl:
|
||||
events.append(create_event('speedTooLow', [ET.NO_ENTRY]))
|
||||
events.add(EventName.speedTooLow)
|
||||
if c.actuators.gas > 0.1:
|
||||
# some margin on the actuator to not false trigger cancellation while stopping
|
||||
events.append(create_event('speedTooLow', [ET.IMMEDIATE_DISABLE]))
|
||||
events.add(EventName.speedTooLow)
|
||||
if ret.vEgo < 0.001:
|
||||
# while in standstill, send a user alert
|
||||
events.append(create_event('manualRestart', [ET.WARNING]))
|
||||
events.add(EventName.manualRestart)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
|
||||
self.CS.out = ret.as_reader()
|
||||
return self.CS.out
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from cereal import car
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET
|
||||
from selfdrive.car.volkswagen.values import CAR, BUTTON_STATES
|
||||
from common.params import put_nonblocking
|
||||
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
GEAR = car.CarState.GearShifter
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
def __init__(self, CP, CarController, CarState):
|
||||
|
@ -108,11 +108,11 @@ class CarInterface(CarInterfaceBase):
|
|||
|
||||
# Vehicle health and operation safety checks
|
||||
if self.CS.parkingBrakeSet:
|
||||
events.append(create_event('parkBrake', [ET.NO_ENTRY, ET.USER_DISABLE]))
|
||||
events.add(EventName.parkBrake)
|
||||
if self.CS.steeringFault:
|
||||
events.append(create_event('steerTempUnavailable', [ET.NO_ENTRY, ET.WARNING]))
|
||||
events.add(EventName.steerTempUnavailable)
|
||||
|
||||
ret.events = events
|
||||
ret.events = events.to_msg()
|
||||
ret.buttonEvents = buttonEvents
|
||||
ret.canMonoTimes = canMonoTimes
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import gc
|
||||
import capnp
|
||||
from cereal import car, log
|
||||
from common.numpy_fast import clip
|
||||
from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper, DT_CTRL
|
||||
|
@ -10,21 +9,18 @@ from common.params import Params, put_nonblocking
|
|||
import cereal.messaging as messaging
|
||||
from selfdrive.config import Conversions as CV
|
||||
from selfdrive.boardd.boardd import can_list_to_can_capnp
|
||||
from selfdrive.car.car_helpers import get_car, get_startup_alert
|
||||
from selfdrive.car.car_helpers import get_car, get_startup_event
|
||||
from selfdrive.controls.lib.lane_planner import CAMERA_OFFSET
|
||||
from selfdrive.controls.lib.drive_helpers import get_events, \
|
||||
create_event, \
|
||||
EventTypes as ET, \
|
||||
update_v_cruise, \
|
||||
initialize_v_cruise
|
||||
from selfdrive.controls.lib.drive_helpers import update_v_cruise, initialize_v_cruise
|
||||
from selfdrive.controls.lib.longcontrol import LongControl, STARTING_TARGET_SPEED
|
||||
from selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
||||
from selfdrive.controls.lib.latcontrol_indi import LatControlINDI
|
||||
from selfdrive.controls.lib.latcontrol_lqr import LatControlLQR
|
||||
from selfdrive.controls.lib.events import Events, ET
|
||||
from selfdrive.controls.lib.alertmanager import AlertManager
|
||||
from selfdrive.controls.lib.vehicle_model import VehicleModel
|
||||
from selfdrive.controls.lib.planner import LON_MPC_STEP
|
||||
from selfdrive.locationd.calibration_helpers import Calibration, Filter
|
||||
from selfdrive.locationd.calibration_helpers import Calibration
|
||||
|
||||
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
|
||||
LANE_DEPARTURE_THRESHOLD = 0.1
|
||||
|
@ -38,19 +34,7 @@ LongitudinalPlanSource = log.Plan.LongitudinalPlanSource
|
|||
Desire = log.PathPlan.Desire
|
||||
LaneChangeState = log.PathPlan.LaneChangeState
|
||||
LaneChangeDirection = log.PathPlan.LaneChangeDirection
|
||||
|
||||
|
||||
def events_to_bytes(events):
|
||||
# optimization when comparing capnp structs: str() or tree traverse are much slower
|
||||
ret = []
|
||||
for e in events:
|
||||
if isinstance(e, capnp.lib.capnp._DynamicStructReader):
|
||||
e = e.as_builder()
|
||||
if not e.is_root:
|
||||
e = e.copy()
|
||||
ret.append(e.to_bytes())
|
||||
return ret
|
||||
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
class Controls:
|
||||
def __init__(self, sm=None, pm=None, can_sock=None):
|
||||
|
@ -91,6 +75,10 @@ class Controls:
|
|||
passive = params.get("Passive", encoding='utf8') == "1" or \
|
||||
internet_needed or not openpilot_enabled_toggle
|
||||
|
||||
# detect sound card presence and ensure successful init
|
||||
sounds_available = not os.path.isfile('/EON') or (os.path.isdir('/proc/asound/card0') \
|
||||
and open('/proc/asound/card0/state').read().strip() == 'ONLINE')
|
||||
|
||||
car_recognized = self.CP.carName != 'mock'
|
||||
# If stock camera is disconnected, we loaded car controls and it's not dashcam mode
|
||||
controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive
|
||||
|
@ -108,6 +96,7 @@ class Controls:
|
|||
|
||||
self.CC = car.CarControl.new_message()
|
||||
self.AM = AlertManager()
|
||||
self.events = Events()
|
||||
|
||||
self.LoC = LongControl(self.CP, self.CI.compute_gb)
|
||||
self.VM = VehicleModel(self.CP)
|
||||
|
@ -130,7 +119,8 @@ class Controls:
|
|||
self.can_error_counter = 0
|
||||
self.last_blinker_frame = 0
|
||||
self.saturated_count = 0
|
||||
self.events_prev = ""
|
||||
self.events_prev = []
|
||||
self.current_alert_types = []
|
||||
|
||||
self.sm['liveCalibration'].calStatus = Calibration.INVALID
|
||||
self.sm['pathPlan'].sensorValid = True
|
||||
|
@ -140,124 +130,96 @@ class Controls:
|
|||
self.sm['dMonitoringState'].awarenessStatus = 1.
|
||||
self.sm['dMonitoringState'].faceDetected = False
|
||||
|
||||
startup_alert = get_startup_alert(car_recognized, controller_available)
|
||||
self.AM.add(self.sm.frame, startup_alert, False)
|
||||
self.startup_event = get_startup_event(car_recognized, controller_available)
|
||||
|
||||
if not sounds_available:
|
||||
self.events.add(EventName.soundsUnavailable, static=True)
|
||||
if internet_needed:
|
||||
self.events.add(EventName.internetConnectivityNeeded, static=True)
|
||||
if community_feature_disallowed:
|
||||
self.events.add(EventName.communityFeatureDisallowed, static=True)
|
||||
if self.read_only and not passive:
|
||||
self.events.add(EventName.carUnrecognized, static=True)
|
||||
|
||||
# controlsd is driven by can recv, expected at 100Hz
|
||||
self.rk = Ratekeeper(100, print_delay_threshold=None)
|
||||
|
||||
self.prof = Profiler(False) # off by default
|
||||
|
||||
# detect sound card presence and ensure successful init
|
||||
sounds_available = not os.path.isfile('/EON') or (os.path.isdir('/proc/asound/card0') \
|
||||
and open('/proc/asound/card0/state').read().strip() == 'ONLINE')
|
||||
|
||||
self.static_events = []
|
||||
if not sounds_available:
|
||||
self.static_events.append(create_event('soundsUnavailable', [ET.NO_ENTRY, ET.PERMANENT]))
|
||||
if internet_needed:
|
||||
self.static_events.append(create_event('internetConnectivityNeeded', [ET.NO_ENTRY, ET.PERMANENT]))
|
||||
if community_feature_disallowed:
|
||||
self.static_events.append(create_event('communityFeatureDisallowed', [ET.PERMANENT]))
|
||||
if self.read_only and not passive:
|
||||
self.static_events.append(create_event('carUnrecognized', [ET.PERMANENT]))
|
||||
|
||||
|
||||
def create_events(self, CS):
|
||||
def update_events(self, CS):
|
||||
"""Compute carEvents from carState"""
|
||||
|
||||
events = self.static_events.copy()
|
||||
events.extend(CS.events)
|
||||
events.extend(self.sm['dMonitoringState'].events)
|
||||
self.events.clear()
|
||||
self.events.add_from_msg(CS.events)
|
||||
self.events.add_from_msg(self.sm['dMonitoringState'].events)
|
||||
|
||||
# Handle startup event
|
||||
if self.startup_event is not None:
|
||||
self.events.add(self.startup_event)
|
||||
self.startup_event = None
|
||||
|
||||
# Create events for battery, temperature, disk space, and memory
|
||||
if self.sm['thermal'].batteryPercent < 1 and self.sm['thermal'].chargingError:
|
||||
# at zero percent battery, while discharging, OP should not allowed
|
||||
events.append(create_event('lowBattery', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.lowBattery)
|
||||
if self.sm['thermal'].thermalStatus >= ThermalStatus.red:
|
||||
events.append(create_event('overheat', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.overheat)
|
||||
if self.sm['thermal'].freeSpace < 0.07:
|
||||
# under 7% of space free no enable allowed
|
||||
events.append(create_event('outOfSpace', [ET.NO_ENTRY]))
|
||||
self.events.add(EventName.outOfSpace)
|
||||
if self.sm['thermal'].memUsedPercent > 90:
|
||||
events.append(create_event('lowMemory', [ET.NO_ENTRY, ET.SOFT_DISABLE, ET.PERMANENT]))
|
||||
self.events.add(EventName.lowMemory)
|
||||
|
||||
# Handle calibration status
|
||||
cal_status = self.sm['liveCalibration'].calStatus
|
||||
if cal_status != Calibration.CALIBRATED:
|
||||
if cal_status == Calibration.UNCALIBRATED:
|
||||
events.append(create_event('calibrationIncomplete', [ET.NO_ENTRY, ET.SOFT_DISABLE, ET.PERMANENT]))
|
||||
self.events.add(EventName.calibrationIncomplete)
|
||||
else:
|
||||
events.append(create_event('calibrationInvalid', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.calibrationInvalid)
|
||||
|
||||
# Handle lane change
|
||||
if self.sm['pathPlan'].laneChangeState == LaneChangeState.preLaneChange:
|
||||
if self.sm['pathPlan'].laneChangeDirection == LaneChangeDirection.left:
|
||||
events.append(create_event('preLaneChangeLeft', [ET.WARNING]))
|
||||
self.events.add(EventName.preLaneChangeLeft)
|
||||
else:
|
||||
events.append(create_event('preLaneChangeRight', [ET.WARNING]))
|
||||
self.events.add(EventName.preLaneChangeRight)
|
||||
elif self.sm['pathPlan'].laneChangeState in [LaneChangeState.laneChangeStarting, \
|
||||
LaneChangeState.laneChangeFinishing]:
|
||||
events.append(create_event('laneChange', [ET.WARNING]))
|
||||
self.events.add(EventName.laneChange)
|
||||
|
||||
if self.can_rcv_error:
|
||||
events.append(create_event('canError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
|
||||
self.events.add(EventName.canError)
|
||||
if self.mismatch_counter >= 200:
|
||||
events.append(create_event('controlsMismatch', [ET.IMMEDIATE_DISABLE]))
|
||||
self.events.add(EventName.controlsMismatch)
|
||||
if not self.sm.alive['plan'] and self.sm.alive['pathPlan']:
|
||||
# only plan not being received: radar not communicating
|
||||
events.append(create_event('radarCommIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.radarCommIssue)
|
||||
elif not self.sm.all_alive_and_valid():
|
||||
events.append(create_event('commIssue', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.commIssue)
|
||||
if not self.sm['pathPlan'].mpcSolutionValid:
|
||||
events.append(create_event('plannerError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
|
||||
self.events.add(EventName.plannerError)
|
||||
if not self.sm['pathPlan'].sensorValid and os.getenv("NOSENSOR") is None:
|
||||
events.append(create_event('sensorDataInvalid', [ET.NO_ENTRY, ET.PERMANENT]))
|
||||
self.events.add(EventName.sensorDataInvalid)
|
||||
if not self.sm['pathPlan'].paramsValid:
|
||||
events.append(create_event('vehicleModelInvalid', [ET.WARNING]))
|
||||
self.events.add(EventName.vehicleModelInvalid)
|
||||
if not self.sm['pathPlan'].posenetValid:
|
||||
events.append(create_event('posenetInvalid', [ET.NO_ENTRY, ET.WARNING]))
|
||||
self.events.add(EventName.posenetInvalid)
|
||||
if not self.sm['plan'].radarValid:
|
||||
events.append(create_event('radarFault', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.radarFault)
|
||||
if self.sm['plan'].radarCanError:
|
||||
events.append(create_event('radarCanError', [ET.NO_ENTRY, ET.SOFT_DISABLE]))
|
||||
self.events.add(EventName.radarCanError)
|
||||
if not CS.canValid:
|
||||
events.append(create_event('canError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
|
||||
self.events.add(EventName.canError)
|
||||
if log.HealthData.FaultType.relayMalfunction in self.sm['health'].faults:
|
||||
events.append(create_event('relayMalfunction', [ET.NO_ENTRY, ET.PERMANENT, ET.IMMEDIATE_DISABLE]))
|
||||
self.events.add(EventName.relayMalfunction)
|
||||
if self.sm['plan'].fcw:
|
||||
self.events.add(EventName.fcw)
|
||||
|
||||
# Only allow engagement with brake pressed when stopped behind another stopped car
|
||||
if CS.brakePressed and self.sm['plan'].vTargetFuture >= STARTING_TARGET_SPEED \
|
||||
and not self.CP.radarOffCan and CS.vEgo < 0.3:
|
||||
events.append(create_event('noTarget', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE]))
|
||||
|
||||
|
||||
# TODO: clean up this alert creation in alerts refactor
|
||||
|
||||
if self.active:
|
||||
for e in get_events(events, [ET.WARNING]):
|
||||
# TODO: handle non static text in a cleaner way, like a callback
|
||||
extra_text = ""
|
||||
if e == "belowSteerSpeed":
|
||||
if self.is_metric:
|
||||
extra_text = str(int(round(self.CP.minSteerSpeed * CV.MS_TO_KPH))) + " kph"
|
||||
else:
|
||||
extra_text = str(int(round(self.CP.minSteerSpeed * CV.MS_TO_MPH))) + " mph"
|
||||
self.AM.add(self.sm.frame, e, self.enabled, extra_text_2=extra_text)
|
||||
|
||||
for e in get_events(events, [ET.PERMANENT]):
|
||||
# TODO: handle non static text in a cleaner way, like a callback
|
||||
extra_text_1, extra_text_2 = "", ""
|
||||
if e == "calibrationIncomplete":
|
||||
extra_text_1 = str(self.sm['liveCalibration'].calPerc) + "%"
|
||||
if self.is_metric:
|
||||
extra_text_2 = str(int(round(Filter.MIN_SPEED * CV.MS_TO_KPH))) + " kph"
|
||||
else:
|
||||
extra_text_2 = str(int(round(Filter.MIN_SPEED * CV.MS_TO_MPH))) + " mph"
|
||||
self.AM.add(self.sm.frame, str(e) + "Permanent", self.enabled, \
|
||||
extra_text_1=extra_text_1, extra_text_2=extra_text_2)
|
||||
|
||||
return events
|
||||
self.events.add(EventName.noTarget)
|
||||
|
||||
|
||||
def data_sample(self):
|
||||
|
@ -289,7 +251,7 @@ class Controls:
|
|||
return CS
|
||||
|
||||
|
||||
def state_transition(self, CS, events):
|
||||
def state_transition(self, CS):
|
||||
"""Compute conditional state transitions and execute actions on state transitions"""
|
||||
|
||||
self.v_cruise_kph_last = self.v_cruise_kph
|
||||
|
@ -304,70 +266,68 @@ class Controls:
|
|||
# entrance in SOFT_DISABLING state
|
||||
self.soft_disable_timer = max(0, self.soft_disable_timer - 1)
|
||||
|
||||
alert_types = []
|
||||
self.current_alert_types = [ET.PERMANENT]
|
||||
|
||||
# ENABLED, PRE ENABLING, SOFT DISABLING
|
||||
if self.state != State.disabled:
|
||||
# user and immediate disable always have priority in a non-disabled state
|
||||
if get_events(events, [ET.USER_DISABLE]):
|
||||
if self.events.any(ET.USER_DISABLE):
|
||||
self.state = State.disabled
|
||||
self.AM.add(self.sm.frame, "disable", self.enabled)
|
||||
self.current_alert_types.append(ET.USER_DISABLE)
|
||||
|
||||
elif get_events(events, [ET.IMMEDIATE_DISABLE]):
|
||||
elif self.events.any(ET.IMMEDIATE_DISABLE):
|
||||
self.state = State.disabled
|
||||
alert_types = [ET.IMMEDIATE_DISABLE]
|
||||
self.current_alert_types.append(ET.IMMEDIATE_DISABLE)
|
||||
|
||||
else:
|
||||
# ENABLED
|
||||
if self.state == State.enabled:
|
||||
if get_events(events, [ET.SOFT_DISABLE]):
|
||||
if self.events.any(ET.SOFT_DISABLE):
|
||||
self.state = State.softDisabling
|
||||
self.soft_disable_timer = 300 # 3s
|
||||
alert_types = [ET.SOFT_DISABLE]
|
||||
self.current_alert_types.append(ET.SOFT_DISABLE)
|
||||
|
||||
# SOFT DISABLING
|
||||
elif self.state == State.softDisabling:
|
||||
if not get_events(events, [ET.SOFT_DISABLE]):
|
||||
if not self.events.any(ET.SOFT_DISABLE):
|
||||
# no more soft disabling condition, so go back to ENABLED
|
||||
self.state = State.enabled
|
||||
|
||||
elif get_events(events, [ET.SOFT_DISABLE]) and self.soft_disable_timer > 0:
|
||||
alert_types = [ET.SOFT_DISABLE]
|
||||
elif self.events.any(ET.SOFT_DISABLE) and self.soft_disable_timer > 0:
|
||||
self.current_alert_types.append(ET.SOFT_DISABLE)
|
||||
|
||||
elif self.soft_disable_timer <= 0:
|
||||
self.state = State.disabled
|
||||
|
||||
# PRE ENABLING
|
||||
elif self.state == State.preEnabled:
|
||||
if not get_events(events, [ET.PRE_ENABLE]):
|
||||
if not self.events.any(ET.PRE_ENABLE):
|
||||
self.state = State.enabled
|
||||
|
||||
# DISABLED
|
||||
elif self.state == State.disabled:
|
||||
if get_events(events, [ET.ENABLE]):
|
||||
if get_events(events, [ET.NO_ENTRY]):
|
||||
for e in get_events(events, [ET.NO_ENTRY]):
|
||||
self.AM.add(self.sm.frame, str(e) + "NoEntry", self.enabled)
|
||||
if self.events.any(ET.ENABLE):
|
||||
if self.events.any(ET.NO_ENTRY):
|
||||
self.current_alert_types.append(ET.NO_ENTRY)
|
||||
|
||||
else:
|
||||
if get_events(events, [ET.PRE_ENABLE]):
|
||||
if self.events.any(ET.PRE_ENABLE):
|
||||
self.state = State.preEnabled
|
||||
else:
|
||||
self.state = State.enabled
|
||||
self.AM.add(self.sm.frame, "enable", self.enabled)
|
||||
self.current_alert_types.append(ET.ENABLE)
|
||||
self.v_cruise_kph = initialize_v_cruise(CS.vEgo, CS.buttonEvents, self.v_cruise_kph_last)
|
||||
|
||||
for e in get_events(events, alert_types):
|
||||
self.AM.add(self.sm.frame, e, self.enabled)
|
||||
|
||||
# Check if actuators are enabled
|
||||
self.active = self.state == State.enabled or self.state == State.softDisabling
|
||||
if self.active:
|
||||
self.current_alert_types.append(ET.WARNING)
|
||||
|
||||
# Check if openpilot is engaged
|
||||
self.enabled = self.active or self.state == State.preEnabled
|
||||
|
||||
|
||||
def state_control(self, CS, events):
|
||||
def state_control(self, CS):
|
||||
"""Given the state, this function returns an actuators packet"""
|
||||
|
||||
plan = self.sm['plan']
|
||||
|
@ -378,14 +338,6 @@ class Controls:
|
|||
if CS.leftBlinker or CS.rightBlinker:
|
||||
self.last_blinker_frame = self.sm.frame
|
||||
|
||||
if plan.fcw:
|
||||
# send FCW alert if triggered by planner
|
||||
self.AM.add(self.sm.frame, "fcw", self.enabled)
|
||||
|
||||
elif CS.stockFcw:
|
||||
# send a silent alert when stock fcw triggers, since the car is already beeping
|
||||
self.AM.add(self.sm.frame, "fcwStock", self.enabled)
|
||||
|
||||
# State specific actions
|
||||
|
||||
if not self.active:
|
||||
|
@ -419,12 +371,12 @@ class Controls:
|
|||
right_deviation = actuators.steer < 0 and path_plan.dPoly[3] < -0.1
|
||||
|
||||
if left_deviation or right_deviation:
|
||||
self.AM.add(self.sm.frame, "steerSaturated", self.enabled)
|
||||
self.events.add(EventName.steerSaturated)
|
||||
|
||||
return actuators, v_acc_sol, a_acc_sol, lac_log
|
||||
|
||||
|
||||
def publish_logs(self, CS, events, start_time, actuators, v_acc, a_acc, lac_log):
|
||||
def publish_logs(self, CS, start_time, actuators, v_acc, a_acc, lac_log):
|
||||
"""Send actuators and hud commands to the car, send controlsstate and MPC logging"""
|
||||
|
||||
CC = car.CarControl.new_message()
|
||||
|
@ -466,9 +418,10 @@ class Controls:
|
|||
CC.hudControl.rightLaneDepart = bool(r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)
|
||||
|
||||
if CC.hudControl.rightLaneDepart or CC.hudControl.leftLaneDepart:
|
||||
self.AM.add(self.sm.frame, 'ldwPermanent', False)
|
||||
events.append(create_event('ldw', [ET.PERMANENT]))
|
||||
self.events.add(EventName.ldw)
|
||||
|
||||
alerts = self.events.create_alerts(self.current_alert_types, [self.CP, self.sm, self.is_metric])
|
||||
self.AM.add_many(self.sm.frame, alerts, self.enabled)
|
||||
self.AM.process_alerts(self.sm.frame)
|
||||
CC.hudControl.visualAlert = self.AM.visual_alert
|
||||
|
||||
|
@ -505,7 +458,7 @@ class Controls:
|
|||
controlsState.curvature = self.VM.calc_curvature(steer_angle_rad, CS.vEgo)
|
||||
controlsState.steerOverride = CS.steeringPressed
|
||||
controlsState.state = self.state
|
||||
controlsState.engageable = not bool(get_events(events, [ET.NO_ENTRY]))
|
||||
controlsState.engageable = not self.events.any(ET.NO_ENTRY)
|
||||
controlsState.longControlState = self.LoC.long_control_state
|
||||
controlsState.vPid = float(self.LoC.v_pid)
|
||||
controlsState.vCruise = float(self.v_cruise_kph)
|
||||
|
@ -534,19 +487,19 @@ class Controls:
|
|||
self.pm.send('controlsState', dat)
|
||||
|
||||
# carState
|
||||
car_events = self.events.to_msg()
|
||||
cs_send = messaging.new_message('carState')
|
||||
cs_send.valid = CS.canValid
|
||||
cs_send.carState = CS
|
||||
cs_send.carState.events = events
|
||||
cs_send.carState.events = car_events
|
||||
self.pm.send('carState', cs_send)
|
||||
|
||||
# carEvents - logged every second or on change
|
||||
events_bytes = events_to_bytes(events)
|
||||
if (self.sm.frame % int(1. / DT_CTRL) == 0) or (events_bytes != self.events_prev):
|
||||
ce_send = messaging.new_message('carEvents', len(events))
|
||||
ce_send.carEvents = events
|
||||
if (self.sm.frame % int(1. / DT_CTRL) == 0) or (self.events.names != self.events_prev):
|
||||
ce_send = messaging.new_message('carEvents', len(self.events))
|
||||
ce_send.carEvents = car_events
|
||||
self.pm.send('carEvents', ce_send)
|
||||
self.events_prev = events_bytes
|
||||
self.events_prev = self.events.names.copy()
|
||||
|
||||
# carParams - logged every 50 seconds (> 1 per segment)
|
||||
if (self.sm.frame % int(50. / DT_CTRL) == 0):
|
||||
|
@ -571,20 +524,20 @@ class Controls:
|
|||
CS = self.data_sample()
|
||||
self.prof.checkpoint("Sample")
|
||||
|
||||
events = self.create_events(CS)
|
||||
self.update_events(CS)
|
||||
|
||||
if not self.read_only:
|
||||
# Update control state
|
||||
self.state_transition(CS, events)
|
||||
self.state_transition(CS)
|
||||
self.prof.checkpoint("State transition")
|
||||
|
||||
# Compute actuators (runs PID loops and lateral MPC)
|
||||
actuators, v_acc, a_acc, lac_log = self.state_control(CS, events)
|
||||
actuators, v_acc, a_acc, lac_log = self.state_control(CS)
|
||||
|
||||
self.prof.checkpoint("State Control")
|
||||
|
||||
# Publish data
|
||||
self.publish_logs(CS, events, start_time, actuators, v_acc, a_acc, lac_log)
|
||||
self.publish_logs(CS, start_time, actuators, v_acc, a_acc, lac_log)
|
||||
self.prof.checkpoint("Sent")
|
||||
|
||||
def controlsd_thread(self):
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
import gc
|
||||
from cereal import car
|
||||
from common.realtime import set_realtime_priority
|
||||
from common.params import Params
|
||||
import cereal.messaging as messaging
|
||||
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET
|
||||
from selfdrive.controls.lib.events import Events
|
||||
from selfdrive.controls.lib.driver_monitor import DriverStatus, MAX_TERMINAL_ALERTS, MAX_TERMINAL_DURATION
|
||||
from selfdrive.locationd.calibration_helpers import Calibration
|
||||
|
||||
|
@ -57,7 +58,7 @@ def dmonitoringd_thread(sm=None, pm=None):
|
|||
v_cruise != v_cruise_last or \
|
||||
sm['carState'].steeringPressed
|
||||
if driver_engaged:
|
||||
_ = driver_status.update([], True, sm['carState'].cruiseState.enabled, sm['carState'].standstill)
|
||||
driver_status.update(Events(), True, sm['carState'].cruiseState.enabled, sm['carState'].standstill)
|
||||
v_cruise_last = v_cruise
|
||||
|
||||
# Get model meta
|
||||
|
@ -66,18 +67,18 @@ def dmonitoringd_thread(sm=None, pm=None):
|
|||
|
||||
# Get data from dmonitoringmodeld
|
||||
if sm.updated['driverState']:
|
||||
events = []
|
||||
events = Events()
|
||||
driver_status.get_pose(sm['driverState'], cal_rpy, sm['carState'].vEgo, sm['carState'].cruiseState.enabled)
|
||||
# Block any engage after certain distrations
|
||||
if driver_status.terminal_alert_cnt >= MAX_TERMINAL_ALERTS or driver_status.terminal_time >= MAX_TERMINAL_DURATION:
|
||||
events.append(create_event("tooDistracted", [ET.NO_ENTRY]))
|
||||
events.add(car.CarEvent.EventName.tooDistracted)
|
||||
# Update events from driver state
|
||||
events = driver_status.update(events, driver_engaged, sm['carState'].cruiseState.enabled, sm['carState'].standstill)
|
||||
driver_status.update(events, driver_engaged, sm['carState'].cruiseState.enabled, sm['carState'].standstill)
|
||||
|
||||
# dMonitoringState packet
|
||||
dat = messaging.new_message('dMonitoringState')
|
||||
dat.dMonitoringState = {
|
||||
"events": events,
|
||||
"events": events.to_msg(),
|
||||
"faceDetected": driver_status.face_detected,
|
||||
"isDistracted": driver_status.driver_distracted,
|
||||
"awarenessStatus": driver_status.awareness,
|
||||
|
@ -100,4 +101,4 @@ def main(sm=None, pm=None):
|
|||
dmonitoringd_thread(sm, pm)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from cereal import car, log
|
||||
from common.realtime import DT_CTRL
|
||||
from selfdrive.swaglog import cloudlog
|
||||
from selfdrive.controls.lib.alerts import ALERTS
|
||||
import copy
|
||||
|
||||
|
||||
|
@ -14,21 +13,21 @@ class AlertManager():
|
|||
|
||||
def __init__(self):
|
||||
self.activealerts = []
|
||||
self.alerts = {alert.alert_type: alert for alert in ALERTS}
|
||||
|
||||
def alertPresent(self):
|
||||
def alert_present(self):
|
||||
return len(self.activealerts) > 0
|
||||
|
||||
def add(self, frame, alert_type, enabled=True, extra_text_1='', extra_text_2=''):
|
||||
alert_type = str(alert_type)
|
||||
added_alert = copy.copy(self.alerts[alert_type])
|
||||
added_alert.alert_text_1 += extra_text_1
|
||||
added_alert.alert_text_2 += extra_text_2
|
||||
def add_many(self, frame, alerts, enabled=True):
|
||||
for a in alerts:
|
||||
self.add(frame, a, enabled=enabled)
|
||||
|
||||
def add(self, frame, alert, enabled=True):
|
||||
added_alert = copy.copy(alert)
|
||||
added_alert.start_time = frame * DT_CTRL
|
||||
|
||||
# if new alert is higher priority, log it
|
||||
if not self.alertPresent() or added_alert.alert_priority > self.activealerts[0].alert_priority:
|
||||
cloudlog.event('alert_add', alert_type=alert_type, enabled=enabled)
|
||||
if not self.alert_present() or added_alert.alert_priority > self.activealerts[0].alert_priority:
|
||||
cloudlog.event('alert_add', alert_type=added_alert.alert_type, enabled=enabled)
|
||||
|
||||
self.activealerts.append(added_alert)
|
||||
|
||||
|
@ -42,7 +41,7 @@ class AlertManager():
|
|||
self.activealerts = [a for a in self.activealerts if a.start_time +
|
||||
max(a.duration_sound, a.duration_hud_alert, a.duration_text) > cur_time]
|
||||
|
||||
current_alert = self.activealerts[0] if self.alertPresent() else None
|
||||
current_alert = self.activealerts[0] if self.alert_present() else None
|
||||
|
||||
# start with assuming no alerts
|
||||
self.alert_type = ""
|
||||
|
|
|
@ -1,825 +0,0 @@
|
|||
from cereal import car, log
|
||||
|
||||
# Priority
|
||||
class Priority:
|
||||
LOWEST = 0
|
||||
LOWER = 1
|
||||
LOW = 2
|
||||
MID = 3
|
||||
HIGH = 4
|
||||
HIGHEST = 5
|
||||
|
||||
AlertSize = log.ControlsState.AlertSize
|
||||
AlertStatus = log.ControlsState.AlertStatus
|
||||
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
class Alert():
|
||||
def __init__(self,
|
||||
alert_type,
|
||||
alert_text_1,
|
||||
alert_text_2,
|
||||
alert_status,
|
||||
alert_size,
|
||||
alert_priority,
|
||||
visual_alert,
|
||||
audible_alert,
|
||||
duration_sound,
|
||||
duration_hud_alert,
|
||||
duration_text,
|
||||
alert_rate=0.):
|
||||
|
||||
self.alert_type = alert_type
|
||||
self.alert_text_1 = alert_text_1
|
||||
self.alert_text_2 = alert_text_2
|
||||
self.alert_status = alert_status
|
||||
self.alert_size = alert_size
|
||||
self.alert_priority = alert_priority
|
||||
self.visual_alert = visual_alert
|
||||
self.audible_alert = audible_alert
|
||||
|
||||
self.duration_sound = duration_sound
|
||||
self.duration_hud_alert = duration_hud_alert
|
||||
self.duration_text = duration_text
|
||||
|
||||
self.start_time = 0.
|
||||
self.alert_rate = alert_rate
|
||||
|
||||
# typecheck that enums are valid on startup
|
||||
tst = car.CarControl.new_message()
|
||||
tst.hudControl.visualAlert = self.visual_alert
|
||||
|
||||
def __str__(self):
|
||||
return self.alert_text_1 + "/" + self.alert_text_2 + " " + str(self.alert_priority) + " " + str(
|
||||
self.visual_alert) + " " + str(self.audible_alert)
|
||||
|
||||
def __gt__(self, alert2):
|
||||
return self.alert_priority > alert2.alert_priority
|
||||
|
||||
|
||||
ALERTS = [
|
||||
# Miscellaneous alerts
|
||||
Alert(
|
||||
"enable",
|
||||
"",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.none,
|
||||
Priority.MID, VisualAlert.none, AudibleAlert.chimeEngage, .2, 0., 0.),
|
||||
|
||||
Alert(
|
||||
"disable",
|
||||
"",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.none,
|
||||
Priority.MID, VisualAlert.none, AudibleAlert.chimeDisengage, .2, 0., 0.),
|
||||
|
||||
Alert(
|
||||
"fcw",
|
||||
"BRAKE!",
|
||||
"Risk of Collision",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.chimeWarningRepeat, 1., 2., 2.),
|
||||
|
||||
Alert(
|
||||
"fcwStock",
|
||||
"BRAKE!",
|
||||
"Risk of Collision",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 1., 2., 2.), # no EON chime for stock FCW
|
||||
|
||||
Alert(
|
||||
"steerSaturated",
|
||||
"TAKE CONTROL",
|
||||
"Turn Exceeds Steering Limit",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 2., 3.),
|
||||
|
||||
Alert(
|
||||
"steerTempUnavailable",
|
||||
"TAKE CONTROL",
|
||||
"Steering Temporarily Unavailable",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning1, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"steerTempUnavailableMute",
|
||||
"TAKE CONTROL",
|
||||
"Steering Temporarily Unavailable",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, .2, .2, .2),
|
||||
|
||||
Alert(
|
||||
"preDriverDistracted",
|
||||
"KEEP EYES ON ROAD: Driver Distracted",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
|
||||
Alert(
|
||||
"promptDriverDistracted",
|
||||
"KEEP EYES ON ROAD",
|
||||
"Driver Appears Distracted",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, .1, .1, .1),
|
||||
|
||||
Alert(
|
||||
"driverDistracted",
|
||||
"DISENGAGE IMMEDIATELY",
|
||||
"Driver Was Distracted",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1),
|
||||
|
||||
Alert(
|
||||
"preDriverUnresponsive",
|
||||
"TOUCH STEERING WHEEL: No Face Detected",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
|
||||
Alert(
|
||||
"promptDriverUnresponsive",
|
||||
"TOUCH STEERING WHEEL",
|
||||
"Driver Is Unresponsive",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, .1, .1, .1),
|
||||
|
||||
Alert(
|
||||
"driverUnresponsive",
|
||||
"DISENGAGE IMMEDIATELY",
|
||||
"Driver Was Unresponsive",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1),
|
||||
|
||||
Alert(
|
||||
"driverMonitorLowAcc",
|
||||
"CHECK DRIVER FACE VISIBILITY",
|
||||
"Driver Monitor Model Output Uncertain",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .4, 0., 1.),
|
||||
|
||||
Alert(
|
||||
"geofence",
|
||||
"DISENGAGEMENT REQUIRED",
|
||||
"Not in Geofenced Area",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1),
|
||||
|
||||
Alert(
|
||||
"startup",
|
||||
"Be ready to take over at any time",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
|
||||
Alert(
|
||||
"startupMaster",
|
||||
"WARNING: This branch is not tested",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
|
||||
Alert(
|
||||
"startupNoControl",
|
||||
"Dashcam mode",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
|
||||
Alert(
|
||||
"startupNoCar",
|
||||
"Dashcam mode for unsupported car",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
|
||||
Alert(
|
||||
"ethicalDilemma",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Ethical Dilemma Detected",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 1., 3., 3.),
|
||||
|
||||
Alert(
|
||||
"steerTempUnavailableNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Steering Temporarily Unavailable",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 0., 3.),
|
||||
|
||||
Alert(
|
||||
"manualRestart",
|
||||
"TAKE CONTROL",
|
||||
"Resume Driving Manually",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"resumeRequired",
|
||||
"STOPPED",
|
||||
"Press Resume to Move",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"belowSteerSpeed",
|
||||
"TAKE CONTROL",
|
||||
"Steer Unavailable Below ",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.none, 0., 0.4, .3),
|
||||
|
||||
Alert(
|
||||
"debugAlert",
|
||||
"DEBUG ALERT",
|
||||
"",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, .1, .1, .1),
|
||||
Alert(
|
||||
"preLaneChangeLeft",
|
||||
"Steer Left to Start Lane Change",
|
||||
"Monitor Other Vehicles",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
|
||||
Alert(
|
||||
"preLaneChangeRight",
|
||||
"Steer Right to Start Lane Change",
|
||||
"Monitor Other Vehicles",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
|
||||
Alert(
|
||||
"laneChange",
|
||||
"Changing Lane",
|
||||
"Monitor Other Vehicles",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1),
|
||||
|
||||
Alert(
|
||||
"posenetInvalid",
|
||||
"TAKE CONTROL",
|
||||
"Vision Model Output Uncertain",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning1, .4, 2., 3.),
|
||||
|
||||
# Non-entry only alerts
|
||||
Alert(
|
||||
"wrongCarModeNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Main Switch Off",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 0., 3.),
|
||||
|
||||
Alert(
|
||||
"dataNeededNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Calibration Needs Data. Upload Drive, Try Again",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 0., 3.),
|
||||
|
||||
Alert(
|
||||
"outOfSpaceNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Out of Storage Space",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 0., 3.),
|
||||
|
||||
Alert(
|
||||
"pedalPressedNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Pedal Pressed During Attempt",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, "brakePressed", AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"speedTooLowNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Speed Too Low",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"brakeHoldNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Brake Hold Active",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"parkBrakeNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Park Brake Engaged",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"lowSpeedLockoutNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Cruise Fault: Restart the Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"lowBatteryNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Low Battery",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"sensorDataInvalidNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"No Data from Device Sensors",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"soundsUnavailableNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Speaker not found",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"tooDistractedNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Distraction Level Too High",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
# Cancellation alerts causing soft disabling
|
||||
Alert(
|
||||
"overheat",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"System Overheated",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"wrongGear",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Gear not D",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"calibrationInvalid",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Calibration Invalid: Reposition Device and Recalibrate",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"calibrationIncomplete",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Calibration in Progress",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"doorOpen",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Door Open",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"seatbeltNotLatched",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Seatbelt Unlatched",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"espDisabled",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"ESP Off",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"lowBattery",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Low Battery",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"commIssue",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Communication Issue between Processes",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"radarCommIssue",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Radar Communication Issue",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"radarCanError",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Radar Error: Restart the Car",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
Alert(
|
||||
"radarFault",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Radar Error: Restart the Car",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
|
||||
Alert(
|
||||
"lowMemory",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Low Memory: Reboot Your Device",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
# Cancellation alerts causing immediate disabling
|
||||
Alert(
|
||||
"controlsFailed",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Controls Failed",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"controlsMismatch",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Controls Mismatch",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"canError",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"CAN Error: Check Connections",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"steerUnavailable",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"LKAS Fault: Restart the Car",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"brakeUnavailable",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Cruise Fault: Restart the Car",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"gasUnavailable",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Gas Fault: Restart the Car",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"reverseGear",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Reverse Gear",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"cruiseDisabled",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Cruise Is Off",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"plannerError",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Planner Solution Error",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"relayMalfunction",
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
"Harness Malfunction",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
Alert(
|
||||
"speedTooHigh",
|
||||
"Speed Too High",
|
||||
"Slow down to resume operation",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, 2.2, 3., 4.),
|
||||
|
||||
|
||||
# not loud cancellations (user is in control)
|
||||
Alert(
|
||||
"noTarget",
|
||||
"openpilot Canceled",
|
||||
"No close lead car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"speedTooLow",
|
||||
"openpilot Canceled",
|
||||
"Speed too low",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
|
||||
|
||||
# Cancellation alerts causing non-entry
|
||||
Alert(
|
||||
"overheatNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"System overheated",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"wrongGearNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Gear not D",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"calibrationInvalidNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Calibration Invalid: Reposition Device & Recalibrate",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"calibrationIncompleteNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Calibration in Progress",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"doorOpenNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Door open",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"seatbeltNotLatchedNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Seatbelt unlatched",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"espDisabledNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"ESP Off",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"geofenceNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Not in Geofenced Area",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"radarCanErrorNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Radar Error: Restart the Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"radarFaultNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Radar Error: Restart the Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"posenetInvalidNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Vision Model Output Uncertain",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"controlsFailedNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Controls Failed",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"canErrorNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"CAN Error: Check Connections",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"steerUnavailableNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"LKAS Fault: Restart the Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"brakeUnavailableNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Cruise Fault: Restart the Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"gasUnavailableNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Gas Error: Restart the Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"reverseGearNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Reverse Gear",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"cruiseDisabledNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Cruise is Off",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"noTargetNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"No Close Lead Car",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"plannerErrorNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Planner Solution Error",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"commIssueNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Communication Issue between Processes",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"radarCommIssueNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Radar Communication Issue",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"internetConnectivityNeededNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Please Connect to Internet",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"lowMemoryNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Low Memory: Reboot Your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"speedTooHighNoEntry",
|
||||
"Speed Too High",
|
||||
"Slow down to engage",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
Alert(
|
||||
"relayMalfunctionNoEntry",
|
||||
"openpilot Unavailable",
|
||||
"Harness Malfunction",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
|
||||
# permanent alerts
|
||||
Alert(
|
||||
"steerUnavailablePermanent",
|
||||
"LKAS Fault: Restart the car to engage",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"brakeUnavailablePermanent",
|
||||
"Cruise Fault: Restart the car to engage",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"lowSpeedLockoutPermanent",
|
||||
"Cruise Fault: Restart the car to engage",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"calibrationIncompletePermanent",
|
||||
"Calibration in Progress: ",
|
||||
"Drive Above ",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"invalidGiraffeToyotaPermanent",
|
||||
"Unsupported Giraffe Configuration",
|
||||
"Visit comma.ai/tg",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"invalidLkasSettingPermanent",
|
||||
"Stock LKAS is turned on",
|
||||
"Turn off stock LKAS to engage",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"internetConnectivityNeededPermanent",
|
||||
"Please connect to Internet",
|
||||
"An Update Check Is Required to Engage",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"communityFeatureDisallowedPermanent",
|
||||
"Community Feature Detected",
|
||||
"Enable Community Features in Developer Settings",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2), # LOW priority to overcome Cruise Error
|
||||
|
||||
Alert(
|
||||
"sensorDataInvalidPermanent",
|
||||
"No Data from Device Sensors",
|
||||
"Reboot your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"soundsUnavailablePermanent",
|
||||
"Speaker not found",
|
||||
"Reboot your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"lowMemoryPermanent",
|
||||
"RAM Critically Low",
|
||||
"Reboot your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"carUnrecognizedPermanent",
|
||||
"Dashcam Mode",
|
||||
"Car Unrecognized",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"relayMalfunctionPermanent",
|
||||
"Harness Malfunction",
|
||||
"Please Check Hardware",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
Alert(
|
||||
"vehicleModelInvalid",
|
||||
"Vehicle Parameter Identification Failed",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWEST, VisualAlert.steerRequired, AudibleAlert.none, .0, .0, .1),
|
||||
|
||||
Alert(
|
||||
"ldwPermanent",
|
||||
"TAKE CONTROL",
|
||||
"Lane Departure Detected",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 2., 3.),
|
||||
]
|
|
@ -1,4 +1,3 @@
|
|||
from cereal import car
|
||||
from common.numpy_fast import clip, interp
|
||||
from selfdrive.config import Conversions as CV
|
||||
|
||||
|
@ -23,34 +22,6 @@ class MPC_COST_LONG:
|
|||
JERK = 20.0
|
||||
|
||||
|
||||
class EventTypes:
|
||||
ENABLE = 'enable'
|
||||
PRE_ENABLE = 'preEnable'
|
||||
NO_ENTRY = 'noEntry'
|
||||
WARNING = 'warning'
|
||||
USER_DISABLE = 'userDisable'
|
||||
SOFT_DISABLE = 'softDisable'
|
||||
IMMEDIATE_DISABLE = 'immediateDisable'
|
||||
PERMANENT = 'permanent'
|
||||
|
||||
|
||||
def create_event(name, types):
|
||||
event = car.CarEvent.new_message()
|
||||
event.name = name
|
||||
for t in types:
|
||||
setattr(event, t, True)
|
||||
return event
|
||||
|
||||
|
||||
def get_events(events, types):
|
||||
out = []
|
||||
for e in events:
|
||||
for t in types:
|
||||
if getattr(e, t):
|
||||
out.append(e.name)
|
||||
return out
|
||||
|
||||
|
||||
def rate_limit(new_value, last_value, dw_step, up_step):
|
||||
return clip(new_value, last_value + dw_step, last_value + up_step)
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
from common.numpy_fast import interp
|
||||
from math import atan2, sqrt
|
||||
from common.realtime import DT_DMON
|
||||
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET
|
||||
from common.filter_simple import FirstOrderFilter
|
||||
from common.stat_live import RunningStatFilter
|
||||
|
||||
from cereal import car
|
||||
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
# ******************************************************************************************
|
||||
# NOTE: To fork maintainers.
|
||||
# Disabling or nerfing safety features may get you and your users banned from our servers.
|
||||
|
@ -219,13 +222,13 @@ class DriverStatus():
|
|||
self.awareness = 1.
|
||||
self.awareness_active = 1.
|
||||
self.awareness_passive = 1.
|
||||
return events
|
||||
return
|
||||
|
||||
driver_attentive = self.driver_distraction_filter.x < 0.37
|
||||
awareness_prev = self.awareness
|
||||
|
||||
if self.face_detected and self.hi_stds * DT_DMON > _HI_STD_TIMEOUT:
|
||||
events.append(create_event('driverMonitorLowAcc', [ET.WARNING]))
|
||||
events.add(EventName.driverMonitorLowAcc)
|
||||
|
||||
if (driver_attentive and self.face_detected and self.pose.low_std and self.awareness > 0):
|
||||
# only restore awareness when paying attention and alert is not red
|
||||
|
@ -234,7 +237,7 @@ class DriverStatus():
|
|||
self.awareness_passive = min(self.awareness_passive + self.step_change, 1.)
|
||||
# don't display alert banner when awareness is recovering and has cleared orange
|
||||
if self.awareness > self.threshold_prompt:
|
||||
return events
|
||||
return
|
||||
|
||||
# should always be counting if distracted unless at standstill and reaching orange
|
||||
if (not (self.face_detected and self.hi_stds * DT_DMON <= _HI_STD_FALLBACK_TIME) or (self.driver_distraction_filter.x > 0.63 and self.driver_distracted and self.face_detected)) and \
|
||||
|
@ -244,18 +247,16 @@ class DriverStatus():
|
|||
alert = None
|
||||
if self.awareness <= 0.:
|
||||
# terminal red alert: disengagement required
|
||||
alert = 'driverDistracted' if self.active_monitoring_mode else 'driverUnresponsive'
|
||||
alert = EventName.driverDistracted if self.active_monitoring_mode else EventName.driverUnresponsive
|
||||
self.terminal_time += 1
|
||||
if awareness_prev > 0.:
|
||||
self.terminal_alert_cnt += 1
|
||||
elif self.awareness <= self.threshold_prompt:
|
||||
# prompt orange alert
|
||||
alert = 'promptDriverDistracted' if self.active_monitoring_mode else 'promptDriverUnresponsive'
|
||||
alert = EventName.promptDriverDistracted if self.active_monitoring_mode else EventName.promptDriverUnresponsive
|
||||
elif self.awareness <= self.threshold_pre:
|
||||
# pre green alert
|
||||
alert = 'preDriverDistracted' if self.active_monitoring_mode else 'preDriverUnresponsive'
|
||||
alert = EventName.preDriverDistracted if self.active_monitoring_mode else EventName.preDriverUnresponsive
|
||||
|
||||
if alert is not None:
|
||||
events.append(create_event(alert, [ET.WARNING]))
|
||||
|
||||
return events
|
||||
events.add(alert)
|
||||
|
|
|
@ -0,0 +1,694 @@
|
|||
from cereal import log, car
|
||||
|
||||
from selfdrive.config import Conversions as CV
|
||||
|
||||
from selfdrive.locationd.calibration_helpers import Filter
|
||||
|
||||
AlertSize = log.ControlsState.AlertSize
|
||||
AlertStatus = log.ControlsState.AlertStatus
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
# Alert priorities
|
||||
class Priority:
|
||||
LOWEST = 0
|
||||
LOWER = 1
|
||||
LOW = 2
|
||||
MID = 3
|
||||
HIGH = 4
|
||||
HIGHEST = 5
|
||||
|
||||
# Event types
|
||||
class ET:
|
||||
ENABLE = 'enable'
|
||||
PRE_ENABLE = 'preEnable'
|
||||
NO_ENTRY = 'noEntry'
|
||||
WARNING = 'warning'
|
||||
USER_DISABLE = 'userDisable'
|
||||
SOFT_DISABLE = 'softDisable'
|
||||
IMMEDIATE_DISABLE = 'immediateDisable'
|
||||
PERMANENT = 'permanent'
|
||||
|
||||
# get event name from enum
|
||||
EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()}
|
||||
|
||||
class Events:
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
self.static_events = []
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
return self.events
|
||||
|
||||
def __len__(self):
|
||||
return len(self.events)
|
||||
|
||||
def add(self, event_name, static=False):
|
||||
if static:
|
||||
self.static_events.append(event_name)
|
||||
self.events.append(event_name)
|
||||
|
||||
def clear(self):
|
||||
self.events = self.static_events.copy()
|
||||
|
||||
def any(self, event_type):
|
||||
for e in self.events:
|
||||
if event_type in EVENTS.get(e, {}).keys():
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_alerts(self, event_types, callback_args=[]):
|
||||
ret = []
|
||||
for e in self.events:
|
||||
types = EVENTS[e].keys()
|
||||
for et in event_types:
|
||||
if et in types:
|
||||
alert = EVENTS[e][et]
|
||||
if not isinstance(alert, Alert):
|
||||
alert = alert(*callback_args)
|
||||
alert.alert_type = EVENT_NAME[e]
|
||||
ret.append(alert)
|
||||
return ret
|
||||
|
||||
def add_from_msg(self, events):
|
||||
for e in events:
|
||||
self.events.append(e.name.raw)
|
||||
|
||||
def to_msg(self):
|
||||
ret = []
|
||||
for event_name in self.events:
|
||||
event = car.CarEvent.new_message()
|
||||
event.name = event_name
|
||||
for event_type in EVENTS.get(event_name, {}).keys():
|
||||
setattr(event, event_type , True)
|
||||
ret.append(event)
|
||||
return ret
|
||||
|
||||
class Alert:
|
||||
def __init__(self,
|
||||
alert_text_1,
|
||||
alert_text_2,
|
||||
alert_status,
|
||||
alert_size,
|
||||
alert_priority,
|
||||
visual_alert,
|
||||
audible_alert,
|
||||
duration_sound,
|
||||
duration_hud_alert,
|
||||
duration_text,
|
||||
alert_rate=0.):
|
||||
|
||||
self.alert_type = ""
|
||||
self.alert_text_1 = alert_text_1
|
||||
self.alert_text_2 = alert_text_2
|
||||
self.alert_status = alert_status
|
||||
self.alert_size = alert_size
|
||||
self.alert_priority = alert_priority
|
||||
self.visual_alert = visual_alert
|
||||
self.audible_alert = audible_alert
|
||||
|
||||
self.duration_sound = duration_sound
|
||||
self.duration_hud_alert = duration_hud_alert
|
||||
self.duration_text = duration_text
|
||||
|
||||
self.start_time = 0.
|
||||
self.alert_rate = alert_rate
|
||||
|
||||
# typecheck that enums are valid on startup
|
||||
tst = car.CarControl.new_message()
|
||||
tst.hudControl.visualAlert = self.visual_alert
|
||||
|
||||
def __str__(self):
|
||||
return self.alert_text_1 + "/" + self.alert_text_2 + " " + str(self.alert_priority) + " " + str(
|
||||
self.visual_alert) + " " + str(self.audible_alert)
|
||||
|
||||
def __gt__(self, alert2):
|
||||
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.):
|
||||
super().__init__("openpilot Unavailable", alert_text_2, AlertStatus.normal,
|
||||
AlertSize.mid, Priority.LOW, visual_alert,
|
||||
audible_alert, .4, duration_hud_alert, 3.)
|
||||
|
||||
|
||||
class SoftDisableAlert(Alert):
|
||||
def __init__(self, alert_text_2):
|
||||
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.MID, VisualAlert.steerRequired,
|
||||
AudibleAlert.chimeWarningRepeat, .1, 2., 2.),
|
||||
|
||||
|
||||
class ImmediateDisableAlert(Alert):
|
||||
def __init__(self, alert_text_2, alert_text_1="TAKE CONTROL IMMEDIATELY"):
|
||||
super().__init__(alert_text_1, alert_text_2,
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.steerRequired,
|
||||
AudibleAlert.chimeWarningRepeat, 2.2, 3., 4.),
|
||||
|
||||
class EngagementAlert(Alert):
|
||||
def __init__(self, audible_alert=True):
|
||||
super().__init__("", "",
|
||||
AlertStatus.normal, AlertSize.none,
|
||||
Priority.MID, VisualAlert.none,
|
||||
audible_alert, .2, 0., 0.),
|
||||
|
||||
def below_steer_speed_alert(CP, sm, metric):
|
||||
speed = CP.minSteerSpeed * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)
|
||||
unit = "kph" if metric else "mph"
|
||||
return Alert(
|
||||
"TAKE CONTROL",
|
||||
"Steer Unavailable Below %d %s" % (speed, unit),
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.none, 0., 0.4, .3),
|
||||
|
||||
def calibration_incomplete_alert(CP, sm, metric):
|
||||
speed = int(Filter.MIN_SPEED * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))
|
||||
unit = "kph" if metric else "mph"
|
||||
return Alert(
|
||||
"Calibration in Progress: %d" % sm['liveCalibration'].calPerc,
|
||||
"Drive Above %d %s" % (speed, unit),
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
|
||||
EVENTS = {
|
||||
# ********** events with no alerts **********
|
||||
|
||||
EventName.gasPressed: {ET.PRE_ENABLE: None},
|
||||
|
||||
# ********** events only containing alerts displayed in all states **********
|
||||
|
||||
EventName.debugAlert: {
|
||||
ET.PERMANENT: Alert(
|
||||
"DEBUG ALERT",
|
||||
"",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, .1, .1, .1),
|
||||
},
|
||||
|
||||
EventName.startup: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Be ready to take over at any time",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
},
|
||||
|
||||
EventName.startupMaster: {
|
||||
ET.PERMANENT: Alert(
|
||||
"WARNING: This branch is not tested",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
},
|
||||
|
||||
EventName.startupNoControl: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Dashcam mode",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
},
|
||||
|
||||
EventName.startupNoCar: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Dashcam mode for unsupported car",
|
||||
"Always keep hands on wheel and eyes on road",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.),
|
||||
},
|
||||
|
||||
EventName.invalidGiraffeToyota: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Unsupported Giraffe Configuration",
|
||||
"Visit comma.ai/tg",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
},
|
||||
|
||||
EventName.invalidLkasSetting: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Stock LKAS is turned on",
|
||||
"Turn off stock LKAS to engage",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
},
|
||||
|
||||
EventName.communityFeatureDisallowed: {
|
||||
# LOW priority to overcome Cruise Error
|
||||
ET.PERMANENT: Alert(
|
||||
"",
|
||||
"Community Feature Detected",
|
||||
"Enable Community Features in Developer Settings",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
},
|
||||
|
||||
EventName.carUnrecognized: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Dashcam Mode",
|
||||
"Car Unrecognized",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
},
|
||||
|
||||
EventName.stockAeb: {
|
||||
ET.PERMANENT: Alert(
|
||||
"BRAKE!",
|
||||
"Stock AEB: Risk of Collision",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 1., 2., 2.),
|
||||
},
|
||||
|
||||
EventName.stockFcw: {
|
||||
ET.PERMANENT: Alert(
|
||||
"BRAKE!",
|
||||
"Stock FCW: Risk of Collision",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.none, 1., 2., 2.),
|
||||
},
|
||||
|
||||
EventName.fcw: {
|
||||
ET.PERMANENT: Alert(
|
||||
"BRAKE!",
|
||||
"Risk of Collision",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.chimeWarningRepeat, 1., 2., 2.),
|
||||
},
|
||||
|
||||
EventName.ldw: {
|
||||
ET.PERMANENT: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Lane Departure Detected",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 2., 3.),
|
||||
},
|
||||
|
||||
# ********** events only containing alerts that display while engaged **********
|
||||
|
||||
EventName.vehicleModelInvalid: {
|
||||
ET.WARNING: Alert(
|
||||
"Vehicle Parameter Identification Failed",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWEST, VisualAlert.steerRequired, AudibleAlert.none, .0, .0, .1),
|
||||
},
|
||||
|
||||
EventName.steerTempUnavailableMute: {
|
||||
ET.WARNING: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Steering Temporarily Unavailable",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, .2, .2, .2),
|
||||
},
|
||||
|
||||
EventName.preDriverDistracted: {
|
||||
ET.WARNING: Alert(
|
||||
"KEEP EYES ON ROAD: Driver Distracted",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
},
|
||||
|
||||
EventName.promptDriverDistracted: {
|
||||
ET.WARNING: Alert(
|
||||
"KEEP EYES ON ROAD",
|
||||
"Driver Appears Distracted",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, .1, .1, .1),
|
||||
},
|
||||
|
||||
EventName.driverDistracted: {
|
||||
ET.WARNING: Alert(
|
||||
"DISEventName.AGE IMMEDIATELY",
|
||||
"Driver Was Distracted",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1),
|
||||
},
|
||||
|
||||
EventName.preDriverUnresponsive: {
|
||||
ET.WARNING: Alert(
|
||||
"TOUCH STEERING WHEEL: No Face Detected",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
},
|
||||
|
||||
EventName.promptDriverUnresponsive: {
|
||||
ET.WARNING: Alert(
|
||||
"TOUCH STEERING WHEEL",
|
||||
"Driver Is Unresponsive",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, .1, .1, .1),
|
||||
},
|
||||
|
||||
EventName.driverUnresponsive: {
|
||||
ET.WARNING: Alert(
|
||||
"DISEventName.AGE IMMEDIATELY",
|
||||
"Driver Was Unresponsive",
|
||||
AlertStatus.critical, AlertSize.full,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeat, .1, .1, .1),
|
||||
},
|
||||
|
||||
EventName.driverMonitorLowAcc: {
|
||||
ET.WARNING: Alert(
|
||||
"CHECK DRIVER FACE VISIBILITY",
|
||||
"Driver Monitor Model Output Uncertain",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .4, 0., 1.),
|
||||
},
|
||||
|
||||
EventName.manualRestart: {
|
||||
ET.WARNING: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Resume Driving Manually",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
},
|
||||
|
||||
EventName.resumeRequired: {
|
||||
ET.WARNING: Alert(
|
||||
"STOPPED",
|
||||
"Press Resume to Move",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
},
|
||||
|
||||
EventName.belowSteerSpeed: {
|
||||
ET.WARNING: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Steer Unavailable Below ",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.none, 0., 0.4, .3),
|
||||
},
|
||||
|
||||
EventName.preLaneChangeLeft: {
|
||||
ET.WARNING: Alert(
|
||||
"Steer Left to Start Lane Change",
|
||||
"Monitor Other Vehicles",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
},
|
||||
|
||||
EventName.preLaneChangeRight: {
|
||||
ET.WARNING: Alert(
|
||||
"Steer Right to Start Lane Change",
|
||||
"Monitor Other Vehicles",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1, alert_rate=0.75),
|
||||
},
|
||||
|
||||
EventName.laneChange: {
|
||||
ET.WARNING: Alert(
|
||||
"Changing Lane",
|
||||
"Monitor Other Vehicles",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .0, .1, .1),
|
||||
},
|
||||
|
||||
EventName.steerSaturated: {
|
||||
ET.WARNING: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Turn Exceeds Steering Limit",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1., 2., 3.),
|
||||
},
|
||||
|
||||
# ********** events that affect controls state transitions **********
|
||||
|
||||
EventName.pcmEnable: {
|
||||
ET.ENABLE: EngagementAlert(AudibleAlert.chimeEngage),
|
||||
},
|
||||
|
||||
EventName.buttonEnable: {
|
||||
ET.ENABLE: EngagementAlert(AudibleAlert.chimeEngage),
|
||||
},
|
||||
|
||||
EventName.pcmDisable: {
|
||||
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage),
|
||||
},
|
||||
|
||||
EventName.buttonCancel: {
|
||||
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage),
|
||||
},
|
||||
|
||||
EventName.brakeHold: {
|
||||
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage),
|
||||
ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"),
|
||||
},
|
||||
|
||||
EventName.parkBrake: {
|
||||
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage),
|
||||
ET.NO_ENTRY: NoEntryAlert("Park Brake Engaged"),
|
||||
},
|
||||
|
||||
EventName.pedalPressed: {
|
||||
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage),
|
||||
ET.NO_ENTRY: NoEntryAlert("Pedal Pressed During Attempt",
|
||||
visual_alert=VisualAlert.brakePressed),
|
||||
},
|
||||
|
||||
EventName.wrongCarMode: {
|
||||
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage),
|
||||
ET.NO_ENTRY: NoEntryAlert("Main Switch Off",
|
||||
duration_hud_alert=0.),
|
||||
},
|
||||
|
||||
EventName.steerTempUnavailable: {
|
||||
ET.WARNING: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Steering Temporarily Unavailable",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning1, .4, 2., 3.),
|
||||
ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable",
|
||||
duration_hud_alert=0.),
|
||||
},
|
||||
|
||||
EventName.posenetInvalid: {
|
||||
ET.WARNING: Alert(
|
||||
"TAKE CONTROL",
|
||||
"Vision Model Output Uncertain",
|
||||
AlertStatus.userPrompt, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning1, .4, 2., 3.),
|
||||
ET.NO_ENTRY: NoEntryAlert("Vision Model Output Uncertain"),
|
||||
},
|
||||
|
||||
EventName.outOfSpace: {
|
||||
ET.NO_ENTRY: NoEntryAlert("Out of Storage Space",
|
||||
duration_hud_alert=0.),
|
||||
},
|
||||
|
||||
EventName.sensorDataInvalid: {
|
||||
ET.PERMANENT: Alert(
|
||||
"No Data from Device Sensors",
|
||||
"Reboot your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("No Data from Device Sensors"),
|
||||
},
|
||||
|
||||
EventName.soundsUnavailable: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Speaker not found",
|
||||
"Reboot your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("Speaker not found"),
|
||||
},
|
||||
|
||||
EventName.tooDistracted: {
|
||||
ET.NO_ENTRY: NoEntryAlert("Distraction Level Too High"),
|
||||
},
|
||||
|
||||
EventName.overheat: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("System Overheated"),
|
||||
ET.NO_ENTRY: NoEntryAlert("System overheated"),
|
||||
},
|
||||
|
||||
EventName.wrongGear: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Gear not D"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Gear not D"),
|
||||
},
|
||||
|
||||
EventName.calibrationInvalid: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Calibration Invalid: Reposition Device and Recalibrate"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Calibration Invalid: Reposition Device & Recalibrate"),
|
||||
},
|
||||
|
||||
EventName.calibrationIncomplete: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Calibration in Progress"),
|
||||
ET.PERMANENT: calibration_incomplete_alert,
|
||||
ET.NO_ENTRY: NoEntryAlert("Calibration in Progress"),
|
||||
},
|
||||
|
||||
EventName.doorOpen: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Door Open"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Door open"),
|
||||
},
|
||||
|
||||
EventName.seatbeltNotLatched: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Seatbelt Unlatched"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Seatbelt unlatched"),
|
||||
},
|
||||
|
||||
EventName.espDisabled: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("ESP Off"),
|
||||
ET.NO_ENTRY: NoEntryAlert("ESP Off"),
|
||||
},
|
||||
|
||||
EventName.lowBattery: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Low Battery"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Low Battery"),
|
||||
},
|
||||
|
||||
EventName.commIssue: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Communication Issue between Processes"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Communication Issue between Processes",
|
||||
audible_alert=AudibleAlert.chimeDisengage),
|
||||
},
|
||||
|
||||
EventName.radarCommIssue: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Radar Communication Issue"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Radar Communication Issue",
|
||||
audible_alert=AudibleAlert.chimeDisengage),
|
||||
},
|
||||
|
||||
EventName.radarCanError: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Radar Error: Restart the Car"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"),
|
||||
},
|
||||
|
||||
EventName.radarFault: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Radar Error: Restart the Car"),
|
||||
ET.NO_ENTRY : NoEntryAlert("Radar Error: Restart the Car"),
|
||||
},
|
||||
|
||||
EventName.lowMemory: {
|
||||
ET.SOFT_DISABLE: SoftDisableAlert("Low Memory: Reboot Your Device"),
|
||||
ET.PERMANENT: Alert(
|
||||
"RAM Critically Low",
|
||||
"Reboot your Device",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY : NoEntryAlert("Low Memory: Reboot Your Device",
|
||||
audible_alert=AudibleAlert.chimeDisengage),
|
||||
},
|
||||
|
||||
EventName.controlsFailed: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Failed"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Controls Failed"),
|
||||
},
|
||||
|
||||
EventName.controlsMismatch: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch"),
|
||||
},
|
||||
|
||||
EventName.canError: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("CAN Error: Check Connections"),
|
||||
ET.NO_ENTRY: NoEntryAlert("CAN Error: Check Connections"),
|
||||
},
|
||||
|
||||
EventName.steerUnavailable: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("LKAS Fault: Restart the Car"),
|
||||
ET.PERMANENT: Alert(
|
||||
"LKAS Fault: Restart the car to engage",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("LKAS Fault: Restart the Car"),
|
||||
},
|
||||
|
||||
EventName.brakeUnavailable: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Fault: Restart the Car"),
|
||||
ET.PERMANENT: Alert(
|
||||
"Cruise Fault: Restart the car to engage",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"),
|
||||
},
|
||||
|
||||
EventName.gasUnavailable: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Gas Fault: Restart the Car"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Gas Error: Restart the Car"),
|
||||
},
|
||||
|
||||
EventName.reverseGear: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Reverse Gear"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
|
||||
},
|
||||
|
||||
EventName.cruiseDisabled: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Cruise Is Off"),
|
||||
},
|
||||
|
||||
EventName.plannerError: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Planner Solution Error"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Planner Solution Error"),
|
||||
},
|
||||
|
||||
EventName.relayMalfunction: {
|
||||
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Harness Malfunction"),
|
||||
ET.PERMANENT: Alert(
|
||||
"Harness Malfunction",
|
||||
"Please Check Hardware",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("Harness Malfunction"),
|
||||
},
|
||||
|
||||
EventName.noTarget: {
|
||||
ET.IMMEDIATE_DISABLE: Alert(
|
||||
"openpilot Canceled",
|
||||
"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"),
|
||||
},
|
||||
|
||||
EventName.speedTooLow: {
|
||||
ET.IMMEDIATE_DISABLE: Alert(
|
||||
"openpilot Canceled",
|
||||
"Speed too low",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, .4, 2., 3.),
|
||||
ET.NO_ENTRY: NoEntryAlert("Speed Too Low"),
|
||||
},
|
||||
|
||||
EventName.speedTooHigh: {
|
||||
ET.IMMEDIATE_DISABLE: Alert(
|
||||
"Speed Too High",
|
||||
"Slow down to resume operation",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarning2Repeat, 2.2, 3., 4.),
|
||||
ET.NO_ENTRY: Alert(
|
||||
"Speed Too High",
|
||||
"Slow down to engage",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.),
|
||||
},
|
||||
|
||||
EventName.internetConnectivityNeeded: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Please connect to Internet",
|
||||
"An Update Check Is Required to Engage",
|
||||
AlertStatus.normal, AlertSize.mid,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("Please Connect to Internet",
|
||||
audible_alert=AudibleAlert.chimeDisengage),
|
||||
},
|
||||
|
||||
EventName.lowSpeedLockout: {
|
||||
ET.PERMANENT: Alert(
|
||||
"Cruise Fault: Restart the car to engage",
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2),
|
||||
ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"),
|
||||
},
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import unittest
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from cereal import log
|
||||
from common.basedir import BASEDIR
|
||||
from selfdrive.controls.lib.alerts import ALERTS
|
||||
|
||||
AlertSize = log.ControlsState.AlertSize
|
||||
|
||||
FONT_PATH = os.path.join(BASEDIR, "selfdrive/assets/fonts")
|
||||
REGULAR_FONT_PATH = os.path.join(FONT_PATH, "opensans_semibold.ttf")
|
||||
BOLD_FONT_PATH = os.path.join(FONT_PATH, "opensans_semibold.ttf")
|
||||
SEMIBOLD_FONT_PATH = os.path.join(FONT_PATH, "opensans_semibold.ttf")
|
||||
|
||||
MAX_TEXT_WIDTH = 1920 - 300 # full screen width is useable, minus sidebar
|
||||
# TODO: get exact scale factor. found this empirically, works well enough
|
||||
FONT_SIZE_SCALE = 1.85 # factor to scale from nanovg units to PIL
|
||||
|
||||
class TestAlerts(unittest.TestCase):
|
||||
|
||||
# ensure alert text doesn't exceed allowed width
|
||||
def test_alert_text_length(self):
|
||||
draw = ImageDraw.Draw(Image.new('RGB', (0, 0)))
|
||||
|
||||
fonts = {
|
||||
AlertSize.small: [ImageFont.truetype(SEMIBOLD_FONT_PATH, int(40*FONT_SIZE_SCALE))],
|
||||
AlertSize.mid: [ImageFont.truetype(BOLD_FONT_PATH, int(48*FONT_SIZE_SCALE)),
|
||||
ImageFont.truetype(REGULAR_FONT_PATH, int(36*FONT_SIZE_SCALE))],
|
||||
}
|
||||
|
||||
for alert in ALERTS:
|
||||
# for full size alerts, both text fields wrap the text,
|
||||
# so it's unlikely that they would go past the max width
|
||||
if alert.alert_size in [AlertSize.none, AlertSize.full]:
|
||||
continue
|
||||
|
||||
for i, txt in enumerate([alert.alert_text_1, alert.alert_text_2]):
|
||||
if i >= len(fonts[alert.alert_size]): break
|
||||
|
||||
font = fonts[alert.alert_size][i]
|
||||
w, h = draw.textsize(txt, font)
|
||||
msg = "type: %s msg: %s" % (alert.alert_type, txt)
|
||||
self.assertLessEqual(w, MAX_TEXT_WIDTH, msg=msg)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import unittest
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from cereal import log, car
|
||||
from common.basedir import BASEDIR
|
||||
from selfdrive.controls.lib.events import Alert, EVENTS
|
||||
|
||||
AlertSize = log.ControlsState.AlertSize
|
||||
|
||||
class TestAlerts(unittest.TestCase):
|
||||
|
||||
def test_events_defined(self):
|
||||
# Ensure all events in capnp schema are defined in events.py
|
||||
events = car.CarEvent.EventName.schema.enumerants
|
||||
|
||||
for name, e in events.items():
|
||||
if not name.endswith("DEPRECATED"):
|
||||
fail_msg = "%s @%d not in EVENTS" % (name, e)
|
||||
self.assertTrue(e in EVENTS.keys(), msg=fail_msg)
|
||||
|
||||
# ensure alert text doesn't exceed allowed width
|
||||
def test_alert_text_length(self):
|
||||
font_path = os.path.join(BASEDIR, "selfdrive/assets/fonts")
|
||||
regular_font_path = os.path.join(font_path, "opensans_semibold.ttf")
|
||||
bold_font_path = os.path.join(font_path, "opensans_semibold.ttf")
|
||||
semibold_font_path = os.path.join(font_path, "opensans_semibold.ttf")
|
||||
|
||||
max_text_width = 1920 - 300 # full screen width is useable, minus sidebar
|
||||
# TODO: get exact scale factor. found this empirically, works well enough
|
||||
font_scale_factor = 1.85 # factor to scale from nanovg units to PIL
|
||||
|
||||
draw = ImageDraw.Draw(Image.new('RGB', (0, 0)))
|
||||
|
||||
fonts = {
|
||||
AlertSize.small: [ImageFont.truetype(semibold_font_path, int(40*font_scale_factor))],
|
||||
AlertSize.mid: [ImageFont.truetype(bold_font_path, int(48*font_scale_factor)),
|
||||
ImageFont.truetype(regular_font_path, int(36*font_scale_factor))],
|
||||
}
|
||||
|
||||
alerts = []
|
||||
for event_types in EVENTS.values():
|
||||
for alert in event_types.values():
|
||||
if isinstance(alert, Alert):
|
||||
alerts.append(alert)
|
||||
|
||||
for alert in alerts:
|
||||
# for full size alerts, both text fields wrap the text,
|
||||
# so it's unlikely that they would go past the max width
|
||||
if alert.alert_size in [AlertSize.none, AlertSize.full]:
|
||||
continue
|
||||
|
||||
for i, txt in enumerate([alert.alert_text_1, alert.alert_text_2]):
|
||||
if i >= len(fonts[alert.alert_size]): break
|
||||
|
||||
font = fonts[alert.alert_size][i]
|
||||
w, h = draw.textsize(txt, font)
|
||||
msg = "type: %s msg: %s" % (alert.alert_type, txt)
|
||||
self.assertLessEqual(w, max_text_width, msg=msg)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -1,12 +1,16 @@
|
|||
import unittest
|
||||
import numpy as np
|
||||
from cereal import car
|
||||
from common.realtime import DT_DMON
|
||||
from selfdrive.controls.lib.events import Events
|
||||
from selfdrive.controls.lib.driver_monitor import DriverStatus, MAX_TERMINAL_ALERTS, \
|
||||
_AWARENESS_TIME, _AWARENESS_PRE_TIME_TILL_TERMINAL, \
|
||||
_AWARENESS_PROMPT_TIME_TILL_TERMINAL, _DISTRACTED_TIME, \
|
||||
_DISTRACTED_PRE_TIME_TILL_TERMINAL, _DISTRACTED_PROMPT_TIME_TILL_TERMINAL, \
|
||||
_POSESTD_THRESHOLD, _HI_STD_TIMEOUT
|
||||
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
_TEST_TIMESPAN = 120 # seconds
|
||||
_DISTRACTED_SECONDS_TO_ORANGE = _DISTRACTED_TIME - _DISTRACTED_PROMPT_TIME_TILL_TERMINAL + 1
|
||||
_DISTRACTED_SECONDS_TO_RED = _DISTRACTED_TIME + 1
|
||||
|
@ -59,13 +63,14 @@ def run_DState_seq(driver_state_msgs, driver_car_interaction, openpilot_status,
|
|||
DS = DriverStatus()
|
||||
events_from_DM = []
|
||||
for idx in range(len(driver_state_msgs)):
|
||||
e = Events()
|
||||
DS.get_pose(driver_state_msgs[idx], [0,0,0], 0, openpilot_status[idx])
|
||||
# cal_rpy and car_speed don't matter here
|
||||
|
||||
event_per_state = DS.update([], driver_car_interaction[idx], openpilot_status[idx], car_standstill_status[idx])
|
||||
events_from_DM.append(event_per_state) # evaluate events at 10Hz for tests
|
||||
|
||||
assert len(events_from_DM)==len(driver_state_msgs), 'somethings wrong'
|
||||
# evaluate events at 10Hz for tests
|
||||
DS.update(e, driver_car_interaction[idx], openpilot_status[idx], car_standstill_status[idx])
|
||||
events_from_DM.append(e)
|
||||
assert len(events_from_DM) == len(driver_state_msgs), 'somethings wrong'
|
||||
return events_from_DM, DS
|
||||
|
||||
class TestMonitoring(unittest.TestCase):
|
||||
|
@ -79,11 +84,11 @@ class TestMonitoring(unittest.TestCase):
|
|||
events_output, d_status = run_DState_seq(always_distracted, always_false, always_true, always_false)
|
||||
self.assertTrue(len(events_output[int((_DISTRACTED_TIME-_DISTRACTED_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME-_DISTRACTED_PRE_TIME_TILL_TERMINAL+\
|
||||
((_DISTRACTED_PRE_TIME_TILL_TERMINAL-_DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)][0].name, 'preDriverDistracted')
|
||||
((_DISTRACTED_PRE_TIME_TILL_TERMINAL-_DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.preDriverDistracted)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME-_DISTRACTED_PROMPT_TIME_TILL_TERMINAL+\
|
||||
((_DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)][0].name, 'promptDriverDistracted')
|
||||
((_DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.promptDriverDistracted)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME+\
|
||||
((_TEST_TIMESPAN-10-_DISTRACTED_TIME)/2))/DT_DMON)][0].name, 'driverDistracted')
|
||||
((_TEST_TIMESPAN-10-_DISTRACTED_TIME)/2))/DT_DMON)].names[0], EventName.driverDistracted)
|
||||
self.assertIs(type(d_status.awareness), float)
|
||||
|
||||
# 2. op engaged, no face detected the whole time, no action
|
||||
|
@ -91,11 +96,11 @@ class TestMonitoring(unittest.TestCase):
|
|||
events_output = run_DState_seq(always_no_face, always_false, always_true, always_false)[0]
|
||||
self.assertTrue(len(events_output[int((_AWARENESS_TIME-_AWARENESS_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_AWARENESS_TIME-_AWARENESS_PRE_TIME_TILL_TERMINAL+\
|
||||
((_AWARENESS_PRE_TIME_TILL_TERMINAL-_AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)][0].name, 'preDriverUnresponsive')
|
||||
((_AWARENESS_PRE_TIME_TILL_TERMINAL-_AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.preDriverUnresponsive)
|
||||
self.assertEqual(events_output[int((_AWARENESS_TIME-_AWARENESS_PROMPT_TIME_TILL_TERMINAL+\
|
||||
((_AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)][0].name, 'promptDriverUnresponsive')
|
||||
((_AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.promptDriverUnresponsive)
|
||||
self.assertEqual(events_output[int((_AWARENESS_TIME+\
|
||||
((_TEST_TIMESPAN-10-_AWARENESS_TIME)/2))/DT_DMON)][0].name, 'driverUnresponsive')
|
||||
((_TEST_TIMESPAN-10-_AWARENESS_TIME)/2))/DT_DMON)].names[0], EventName.driverUnresponsive)
|
||||
|
||||
# 3. op engaged, down to orange, driver pays attention, back to normal; then down to orange, driver touches wheel
|
||||
# - should have short orange recovery time and no green afterwards; should recover rightaway on wheel touch
|
||||
|
@ -107,9 +112,9 @@ class TestMonitoring(unittest.TestCase):
|
|||
[car_interaction_DETECTED] * (int(_TEST_TIMESPAN/DT_DMON)-int(_DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON))
|
||||
events_output = run_DState_seq(ds_vector, interaction_vector, always_true, always_false)[0]
|
||||
self.assertTrue(len(events_output[int(_DISTRACTED_SECONDS_TO_ORANGE*0.5/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE-0.1)/DT_DMON)][0].name, 'promptDriverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted)
|
||||
self.assertTrue(len(events_output[int(_DISTRACTED_SECONDS_TO_ORANGE*1.5/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE*3-0.1)/DT_DMON)][0].name, 'promptDriverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE*3-0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted)
|
||||
self.assertTrue(len(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE*3+0.1)/DT_DMON)])==0)
|
||||
|
||||
# 4. op engaged, down to orange, driver dodges camera, then comes back still distracted, down to red, \
|
||||
|
@ -125,9 +130,9 @@ class TestMonitoring(unittest.TestCase):
|
|||
interaction_vector[int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+0.5)/DT_DMON):int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)] = [True] * int(1/DT_DMON)
|
||||
op_vector[int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+2.5)/DT_DMON):int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3)/DT_DMON)] = [False] * int(0.5/DT_DMON)
|
||||
events_output = run_DState_seq(ds_vector, interaction_vector, op_vector, always_false)[0]
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE+0.5*_invisible_time)/DT_DMON)][0].name, 'promptDriverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_RED+1.5*_invisible_time)/DT_DMON)][0].name, 'driverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)][0].name, 'driverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_ORANGE+0.5*_invisible_time)/DT_DMON)].names[0], EventName.promptDriverDistracted)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_RED+1.5*_invisible_time)/DT_DMON)].names[0], EventName.driverDistracted)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)].names[0], EventName.driverDistracted)
|
||||
self.assertTrue(len(events_output[int((_DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3.5)/DT_DMON)])==0)
|
||||
|
||||
# 5. op engaged, invisible driver, down to orange, driver touches wheel; then down to orange again, driver appears
|
||||
|
@ -141,13 +146,13 @@ class TestMonitoring(unittest.TestCase):
|
|||
interaction_vector[int((_INVISIBLE_SECONDS_TO_ORANGE)/DT_DMON):int((_INVISIBLE_SECONDS_TO_ORANGE+1)/DT_DMON)] = [True] * int(1/DT_DMON)
|
||||
events_output = run_DState_seq(ds_vector, interaction_vector, 2*always_true, 2*always_false)[0]
|
||||
self.assertTrue(len(events_output[int(_INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)][0].name, 'promptDriverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0], EventName.promptDriverUnresponsive)
|
||||
self.assertTrue(len(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE+0.1)/DT_DMON)])==0)
|
||||
if _visible_time == 1:
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)][0].name, 'promptDriverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)][0].name, 'preDriverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)].names[0], EventName.promptDriverUnresponsive)
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)].names[0], EventName.preDriverUnresponsive)
|
||||
elif _visible_time == 10:
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)][0].name, 'promptDriverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)].names[0], EventName.promptDriverUnresponsive)
|
||||
self.assertTrue(len(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)])==0)
|
||||
else:
|
||||
pass
|
||||
|
@ -164,10 +169,10 @@ class TestMonitoring(unittest.TestCase):
|
|||
op_vector[int((_INVISIBLE_SECONDS_TO_RED+_visible_time+1)/DT_DMON):int((_INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)] = [False] * int(0.5/DT_DMON)
|
||||
events_output = run_DState_seq(ds_vector, interaction_vector, op_vector, always_false)[0]
|
||||
self.assertTrue(len(events_output[int(_INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)][0].name, 'promptDriverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_RED-0.1)/DT_DMON)][0].name, 'driverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_RED+0.5*_visible_time)/DT_DMON)][0].name, 'driverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)][0].name, 'driverUnresponsive')
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0], EventName.promptDriverUnresponsive)
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_RED-0.1)/DT_DMON)].names[0], EventName.driverUnresponsive)
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_RED+0.5*_visible_time)/DT_DMON)].names[0], EventName.driverUnresponsive)
|
||||
self.assertEqual(events_output[int((_INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)].names[0], EventName.driverUnresponsive)
|
||||
self.assertTrue(len(events_output[int((_INVISIBLE_SECONDS_TO_RED+_visible_time+1+0.1)/DT_DMON)])==0)
|
||||
|
||||
# 7. op not engaged, always distracted driver
|
||||
|
@ -183,9 +188,9 @@ class TestMonitoring(unittest.TestCase):
|
|||
standstill_vector = always_true[:]
|
||||
standstill_vector[int(_redlight_time/DT_DMON):] = [False] * int((_TEST_TIMESPAN-_redlight_time)/DT_DMON)
|
||||
events_output = run_DState_seq(always_distracted, always_false, always_true, standstill_vector)[0]
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME-_DISTRACTED_PRE_TIME_TILL_TERMINAL+1)/DT_DMON)][0].name, 'preDriverDistracted')
|
||||
self.assertEqual(events_output[int((_redlight_time-0.1)/DT_DMON)][0].name, 'preDriverDistracted')
|
||||
self.assertEqual(events_output[int((_redlight_time+0.5)/DT_DMON)][0].name, 'promptDriverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME-_DISTRACTED_PRE_TIME_TILL_TERMINAL+1)/DT_DMON)].names[0], EventName.preDriverDistracted)
|
||||
self.assertEqual(events_output[int((_redlight_time-0.1)/DT_DMON)].names[0], EventName.preDriverDistracted)
|
||||
self.assertEqual(events_output[int((_redlight_time+0.5)/DT_DMON)].names[0], EventName.promptDriverDistracted)
|
||||
|
||||
# 9. op engaged, model is extremely uncertain. driver first attentive, then distracted
|
||||
# - should only pop the green alert about model uncertainty
|
||||
|
@ -197,9 +202,9 @@ class TestMonitoring(unittest.TestCase):
|
|||
interaction_vector = always_false[:]
|
||||
events_output = run_DState_seq(ds_vector, interaction_vector, always_true, always_false)[0]
|
||||
self.assertTrue(len(events_output[int(_UNCERTAIN_SECONDS_TO_GREEN*0.5/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_UNCERTAIN_SECONDS_TO_GREEN-0.1)/DT_DMON)][0].name, 'driverMonitorLowAcc')
|
||||
self.assertEqual(events_output[int((_UNCERTAIN_SECONDS_TO_GREEN-0.1)/DT_DMON)].names[0], EventName.driverMonitorLowAcc)
|
||||
self.assertTrue(len(events_output[int((_UNCERTAIN_SECONDS_TO_GREEN+_DISTRACTED_SECONDS_TO_ORANGE-0.5)/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_TEST_TIMESPAN-5.)/DT_DMON)][0].name, 'driverMonitorLowAcc')
|
||||
self.assertEqual(events_output[int((_TEST_TIMESPAN-5.)/DT_DMON)].names[0], EventName.driverMonitorLowAcc)
|
||||
|
||||
# 10. op engaged, model is somehow uncertain and driver is distracted
|
||||
# - should slow down the alert countdown but it still gets there
|
||||
|
@ -208,11 +213,11 @@ class TestMonitoring(unittest.TestCase):
|
|||
interaction_vector = always_false[:]
|
||||
events_output = run_DState_seq(ds_vector, interaction_vector, always_true, always_false)[0]
|
||||
self.assertTrue(len(events_output[int(_UNCERTAIN_SECONDS_TO_GREEN*0.5/DT_DMON)])==0)
|
||||
self.assertEqual(events_output[int((_UNCERTAIN_SECONDS_TO_GREEN)/DT_DMON)][0].name, 'driverMonitorLowAcc')
|
||||
self.assertEqual(events_output[int((2.5*(_DISTRACTED_TIME-_DISTRACTED_PRE_TIME_TILL_TERMINAL))/DT_DMON)][1].name, 'preDriverDistracted')
|
||||
self.assertEqual(events_output[int((2.5*(_DISTRACTED_TIME-_DISTRACTED_PROMPT_TIME_TILL_TERMINAL))/DT_DMON)][1].name, 'promptDriverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME+1)/DT_DMON)][1].name, 'promptDriverDistracted')
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME*2.5)/DT_DMON)][1].name, 'promptDriverDistracted') # set_timer blocked
|
||||
self.assertEqual(events_output[int((_UNCERTAIN_SECONDS_TO_GREEN)/DT_DMON)].names[0], EventName.driverMonitorLowAcc)
|
||||
self.assertEqual(events_output[int((2.5*(_DISTRACTED_TIME-_DISTRACTED_PRE_TIME_TILL_TERMINAL))/DT_DMON)].names[1], EventName.preDriverDistracted)
|
||||
self.assertEqual(events_output[int((2.5*(_DISTRACTED_TIME-_DISTRACTED_PROMPT_TIME_TILL_TERMINAL))/DT_DMON)].names[1], EventName.promptDriverDistracted)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME+1)/DT_DMON)].names[1], EventName.promptDriverDistracted)
|
||||
self.assertEqual(events_output[int((_DISTRACTED_TIME*2.5)/DT_DMON)].names[1], EventName.promptDriverDistracted) # set_timer blocked
|
||||
|
||||
if __name__ == "__main__":
|
||||
print('MAX_TERMINAL_ALERTS', MAX_TERMINAL_ALERTS)
|
||||
|
|
|
@ -1 +1 @@
|
|||
1050a84363baf1e7910d3f8f9a01e201e6041e70
|
||||
76e577b86d113139167275b4a7379f3591abfa02
|
Loading…
Reference in New Issue