From 45038112024a1c8d074ea2535cac8322cbc84d8d Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:01:46 -0500 Subject: [PATCH] DM and volt auto hold --- opendbc_repo/opendbc/car/gm/carcontroller.py | 65 ++++++++++++------ opendbc_repo/opendbc/car/gm/interface.py | 19 +++++ opendbc_repo/opendbc/safety/modes/gm.h | 52 ++++++++++++-- opendbc_repo/opendbc/safety/tests/test_gm.py | 58 ++++++++++++++++ .../onroad/driver_monitoring/dm_cone.png | Bin 3631 -> 1862 bytes selfdrive/ui/mici/onroad/driver_state.py | 13 +++- starpilot/common/starpilot_variables.py | 1 - .../tools/device_settings_layout.json | 2 +- 8 files changed, 182 insertions(+), 28 deletions(-) diff --git a/opendbc_repo/opendbc/car/gm/carcontroller.py b/opendbc_repo/opendbc/car/gm/carcontroller.py index 58f3e1f7c..2a23fb215 100644 --- a/opendbc_repo/opendbc/car/gm/carcontroller.py +++ b/opendbc_repo/opendbc/car/gm/carcontroller.py @@ -7,7 +7,7 @@ from opendbc.car.gm import gmcan from opendbc.car.common.conversions import Conversions as CV from opendbc.car.gm.values import ( ASCM_INT, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams, - CruiseButtons, GMFlags, + CruiseButtons, GMFlags, GMSafetyFlags, ) from opendbc.car.interfaces import CarControllerBase from openpilot.common.params import Params, UnknownKeyName @@ -117,9 +117,12 @@ def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int: def supports_volt_auto_hold(CP, starpilot_toggles): + safety_cfg = getattr(CP, "safetyConfigs", ()) + safety_param = safety_cfg[0].safetyParam if safety_cfg else 0 + stock_hold_safety_ready = CP.openpilotLongitudinalControl or bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value) return ( getattr(starpilot_toggles, "gm_auto_hold", False) and - CP.openpilotLongitudinalControl and + stock_hold_safety_ready and CP.carFingerprint in AUTO_HOLD_VOLT_CARS ) @@ -130,6 +133,23 @@ def estimate_auto_hold_brake(driver_brake: float, op_brake: float) -> int: return int(round(np.clip(hold_brake, AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MAX_BRAKE))) +def get_friction_brake_bus(CP): + volt_gateway_alt_brake = ( + CP.carFingerprint == CAR.CHEVROLET_VOLT and + CP.networkLocation == NetworkLocation.gateway and + bool(CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value) + ) + if volt_gateway_alt_brake: + return CanBus.POWERTRAIN + + if CP.networkLocation == NetworkLocation.fwdCamera: + if CP.carFingerprint in SDGM_CAR: + return CanBus.CAMERA + return CanBus.POWERTRAIN + + return CanBus.CHASSIS + + class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) @@ -333,6 +353,7 @@ class CarController(CarControllerBase): accel = actuators.accel press_regen_paddle = False auto_hold_enabled = supports_volt_auto_hold(self.CP, starpilot_toggles) + stock_hold_apply_brake = self.apply_brake if self.CP.openpilotLongitudinalControl else 0 hold_ready = ( auto_hold_enabled and @@ -349,8 +370,8 @@ class CarController(CarControllerBase): if CS.out.vEgo > 0.1 or CS.out.gasPressed or CS.out.gearShifter not in AUTO_HOLD_DRIVE_GEARS: self.auto_hold_brake = 0 - elif CS.out.brakePressed or self.apply_brake > 0: - self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, self.apply_brake) + elif CS.out.brakePressed or stock_hold_apply_brake > 0: + self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake) if self.frame % 25 == 0: try: @@ -432,6 +453,13 @@ class CarController(CarControllerBase): paddle_sched_feed_active = False paddle_spoof_pressed = raw_regen_active and (CS.out.vEgo > 2.68) + auto_hold_active = ( + hold_ready and + CS.auto_hold_armed and + not CC.longActive and + not CS.out.regenBraking and + CS.out.vEgo < 0.02 + ) # Steering (Active: 50Hz, inactive: 10Hz) steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP @@ -481,13 +509,6 @@ class CarController(CarControllerBase): interceptor_gas_cmd = 0 at_full_stop = CC.longActive and CS.out.standstill near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE) - auto_hold_active = ( - hold_ready and - CS.auto_hold_armed and - not CC.longActive and - not CS.out.regenBraking and - CS.out.vEgo < 0.02 - ) if not CC.longActive: # ASCM sends max regen when not enabled self.apply_gas = self.params.INACTIVE_REGEN @@ -607,19 +628,11 @@ class CarController(CarControllerBase): if self.CP.enableGasInterceptorDEPRECATED: can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx)) if self.CP.carFingerprint not in CC_ONLY_CAR: - volt_gateway_alt_brake = ( - self.CP.carFingerprint == CAR.CHEVROLET_VOLT and - self.CP.networkLocation == NetworkLocation.gateway and - bool(self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value) - ) - friction_brake_bus = CanBus.POWERTRAIN if volt_gateway_alt_brake else CanBus.CHASSIS + friction_brake_bus = get_friction_brake_bus(self.CP) # GM Camera exceptions # TODO: can we always check the longControlState? if self.CP.networkLocation == NetworkLocation.fwdCamera: at_full_stop = at_full_stop and stopping - friction_brake_bus = CanBus.POWERTRAIN - if self.CP.carFingerprint in SDGM_CAR: - friction_brake_bus = CanBus.CAMERA if self.CP.autoResumeSng: resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume @@ -704,6 +717,18 @@ class CarController(CarControllerBase): can_sends.append(gmcan.create_buttons(self.packer_pt, cancel_bus, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL)) else: + if self.frame % 4 == 0 and auto_hold_active: + idx = (self.frame // 4) % 4 + hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake) + hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL + hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE + can_sends.append(gmcan.create_friction_brake_command( + self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP)) + CS.auto_hold_engaged = True + CS.auto_hold_fault_suppression_timer = 1.0 + elif self.frame % 4 == 0: + CS.auto_hold_engaged = False + # While car is braking, cancel button causes ECM to enter a soft disable state with a fault status. # A delayed cancellation allows camera to cancel and avoids a fault when user depresses brake quickly self.cancel_counter = self.cancel_counter + 1 if CC.cruiseControl.cancel else 0 diff --git a/opendbc_repo/opendbc/car/gm/interface.py b/opendbc_repo/opendbc/car/gm/interface.py index e3573b947..09e4d7f6a 100755 --- a/opendbc_repo/opendbc/car/gm/interface.py +++ b/opendbc_repo/opendbc/car/gm/interface.py @@ -189,6 +189,10 @@ class CarInterface(CarInterfaceBase): disable_openpilot_long = params.get_bool("DisableOpenpilotLongitudinal") except UnknownKeyName: disable_openpilot_long = False + try: + gm_auto_hold = params.get_bool("GMAutoHold") + except UnknownKeyName: + gm_auto_hold = False ret.brand = "gm" ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)] @@ -632,6 +636,21 @@ class CarInterface(CarInterfaceBase): if remote_start_boots_comma: ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value + volt_stock_auto_hold_safety = ( + gm_auto_hold and + not ret.openpilotLongitudinalControl and + candidate in { + CAR.CHEVROLET_VOLT, + CAR.CHEVROLET_VOLT_2019, + CAR.CHEVROLET_VOLT_ASCM, + CAR.CHEVROLET_VOLT_CAMERA, + } + ) + if volt_stock_auto_hold_safety: + # Reuse the paddle-scheduler safety bit as a stock-Volt auto-hold marker on + # non-pedal paths. The scheduler logic remains inactive without pedal-long. + ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value + use_panda_3d1_sched = ( ret.openpilotLongitudinalControl and ret.enableGasInterceptorDEPRECATED and diff --git a/opendbc_repo/opendbc/safety/modes/gm.h b/opendbc_repo/opendbc/safety/modes/gm.h index 9e91b2cc1..59878e58d 100644 --- a/opendbc_repo/opendbc/safety/modes/gm.h +++ b/opendbc_repo/opendbc/safety/modes/gm.h @@ -60,6 +60,7 @@ static bool gm_panda_3d1_sched = false; static bool gm_panda_paddle_sched = false; static bool gm_bolt_2022_pedal = false; static bool gm_alt_brake = false; +static bool gm_volt_auto_hold = false; static bool gm_cc_long = false; static bool gm_has_acc = true; @@ -96,6 +97,7 @@ static const uint32_t GM_PADDLE_TX_OFFSET_US = 0U; static const uint32_t GM_PADDLE_LOCK_TOLERANCE_US = 5000U; static const uint32_t GM_PADDLE_STALE_US = 100000U; static const uint32_t GM_PADDLE_FEED_STALE_US = 100000U; +static const int GM_VOLT_AUTO_HOLD_MAX_BRAKE = 240; void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook); void can_set_checksum(CANPacket_t *packet); @@ -363,7 +365,15 @@ static bool gm_tx_hook(const CANPacket_t *msg) { if (msg->addr == 0x315U) { int brake = ((msg->data[0] & 0xFU) << 8) + msg->data[1]; brake = (0x1000 - brake) & 0xFFF; - if (longitudinal_brake_checks(brake, *gm_long_limits)) { + bool stock_auto_hold_brake_allowed = gm_volt_auto_hold && !vehicle_moving && !gas_pressed_prev; + bool violation = false; + violation |= !(get_longitudinal_allowed() || stock_auto_hold_brake_allowed) && (brake != 0); + if (stock_auto_hold_brake_allowed && !get_longitudinal_allowed()) { + violation |= brake > GM_VOLT_AUTO_HOLD_MAX_BRAKE; + } else { + violation |= brake > gm_long_limits->max_brake; + } + if (violation) { tx = false; } } @@ -602,12 +612,22 @@ static safety_config gm_init(uint16_t param) { {0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false}, {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus + static const CanMsg GM_CAM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus + {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus + {0x200, 0, 6, .check_relay = false}, + {0x1E1, 0, 7, .check_relay = false}, + {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus static const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, // pt bus {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus {0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false}, {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus + static const CanMsg GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, // pt bus + {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, {0x315, 2, 5, .check_relay = false}, // camera bus + {0x200, 0, 6, .check_relay = false}, + {0x1E1, 0, 7, .check_relay = false}, + {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus static const CanMsg GM_CAM_NO_CAMERA_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, // pt bus {0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false}, @@ -615,6 +635,12 @@ static safety_config gm_init(uint16_t param) { {0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false}, {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus + static const CanMsg GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus + {0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false}, + {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus + {0x200, 0, 6, .check_relay = false}, + {0x1E1, 0, 7, .check_relay = false}, + {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus static RxCheck gm_no_acc_rx_checks[] = { GM_COMMON_RX_CHECKS @@ -671,6 +697,8 @@ static safety_config gm_init(uint16_t param) { gm_remote_start_boots_comma = GET_FLAG(param, GM_PARAM_REMOTE_START_BOOTS_COMMA); gm_panda_3d1_sched = GET_FLAG(param, GM_PARAM_PANDA_3D1_SCHED) && gm_pedal_long && !gm_has_acc && !gm_bolt_2022_pedal; gm_panda_paddle_sched = GET_FLAG(param, GM_PARAM_PANDA_PADDLE_SCHED) && gm_pedal_long && enable_gas_interceptor; + // Reuse the paddle-scheduler bit as a stock-Volt auto-hold marker on non-pedal ACC paths. + gm_volt_auto_hold = GET_FLAG(param, GM_PARAM_PANDA_PADDLE_SCHED) && !gm_pedal_long && !gm_cc_long && gm_has_acc; gm_alt_brake = GET_FLAG(param, GM_PARAM_NO_CAMERA) && (gm_hw == GM_ASCM) && !gm_sdgm && !gm_ascm_int; gm_3d1_spoof_valid = false; @@ -714,7 +742,11 @@ static safety_config gm_init(uint16_t param) { const bool gm_sdgm_stock = gm_sdgm && !gm_cc_long && !gm_cam_long && !gm_no_camera; // SDGM behaves like a forwarding camera path for whitelist/forwarding purposes. if (gm_sdgm_stock) { - ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS); + if (gm_volt_auto_hold) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS); + } } else if ((gm_hw == GM_CAM) || gm_sdgm) { // FIXME: cppcheck thinks that gm_cam_long is always false. This is not true // if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG @@ -732,10 +764,20 @@ static safety_config gm_init(uint16_t param) { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_LONG_TX_MSGS); } } else { - if (gm_no_camera) { - ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS); + if (gm_volt_auto_hold && gm_sdgm) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS); + } else if (gm_no_camera) { + if (gm_volt_auto_hold) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS); + } } else { - ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS); + if (gm_volt_auto_hold) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_VOLT_AUTO_HOLD_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS); + } } } } else if (gm_alt_brake) { diff --git a/opendbc_repo/opendbc/safety/tests/test_gm.py b/opendbc_repo/opendbc/safety/tests/test_gm.py index cf0e0f6a2..32d08d60b 100755 --- a/opendbc_repo/opendbc/safety/tests/test_gm.py +++ b/opendbc_repo/opendbc/safety/tests/test_gm.py @@ -533,6 +533,64 @@ class TestGmCcLongitudinalPandaSchedSafety(TestGmCcLongitudinalSafety): for btn in range(8): self.assertEqual(enabled and btn in allowed_btns, self._tx(self._button_msg(btn))) + +class TestGmVoltAutoHoldCameraSafety(TestGmCameraSafetyBase): + TX_MSGS = TestGmCameraSafety.TX_MSGS + [[0x315, 0]] + EXTRA_SAFETY_PARAM = GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED + + def setUp(self): + self.packer = CANPackerSafety("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerSafety("gm_global_a_chassis") + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks(CarParams.SafetyModel.gm, GMSafetyFlags.HW_CAM | self.EXTRA_SAFETY_PARAM) + self.safety.init_tests() + + def _send_brake_msg(self, brake): + values = {"FrictionBrakeCmd": -brake} + return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 0, values) + + def test_standstill_brake_allowed_without_controls(self): + self._rx(self._speed_msg(0)) + self.safety.set_controls_allowed(False) + self.assertTrue(self._tx(self._send_brake_msg(100))) + + def test_moving_brake_blocked_without_controls(self): + self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1)) + self.safety.set_controls_allowed(False) + self.assertFalse(self._tx(self._send_brake_msg(100))) + + def test_gas_blocks_standstill_brake_without_controls(self): + self._rx(self._speed_msg(0)) + self._rx(self._user_gas_msg(True)) + self.safety.set_controls_allowed(False) + self.assertFalse(self._tx(self._send_brake_msg(100))) + + +class TestGmVoltAutoHoldSdgmSafety(TestGmSafetyBase): + TX_MSGS = TestGmSdgmSafety.TX_MSGS + [[0x315, 2]] + EXTRA_SAFETY_PARAM = GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED + + def setUp(self): + self.packer = CANPackerSafety("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerSafety("gm_global_a_chassis") + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks(CarParams.SafetyModel.gm, GMSafetyFlags.HW_SDGM | self.EXTRA_SAFETY_PARAM) + self.safety.init_tests() + + def _send_brake_msg(self, brake): + values = {"FrictionBrakeCmd": -brake} + return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 2, values) + + def test_standstill_brake_allowed_without_controls(self): + self._rx(self._speed_msg(0)) + self.safety.set_controls_allowed(False) + self.assertTrue(self._tx(self._send_brake_msg(100))) + + def test_moving_brake_blocked_without_controls(self): + self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1)) + self.safety.set_controls_allowed(False) + self.assertFalse(self._tx(self._send_brake_msg(100))) + def test_3d1_feed_frame_allowed(self): self.assertTrue(self._tx(self._pcm_status_msg(True))) self.assertTrue(self._tx(self._pcm_status_msg(False))) diff --git a/selfdrive/assets/icons_mici/onroad/driver_monitoring/dm_cone.png b/selfdrive/assets/icons_mici/onroad/driver_monitoring/dm_cone.png index 56d4bddc45b79f86936ff8f93376b4437de89cdf..2c4e10c40bb4fce975dfe37faca54ab66ccc6c54 100644 GIT binary patch literal 1862 zcmchY`#;kQ1INGn8V!vNBbOROO_bc8Txu>E(u?MDLO6y_C>@u_C6C*}SmaU<;bCpL zgp;*(b4iD>T#B!{s#O004krbaC`N zXwv@zlQ|#<7u*j3hu{|-?Y(b9)+_s;2H+7r9j)c9nB6IgD_#-p#Iuz&!|W6R$UZkF&9ef6BA+>8gD6 zZJ*A^osew~TV4BObyZI{AZy^uk5tncgM`o*hTYq>Rs(!1g9PqG!`4O%uF$DEw59Jz z@L$NVAbn0~NFrsnpl5USo8ODpmlAQf#(v@%)6k~;t&O)6vgR4P0fI8hVsfL&O#>k2 zrSl1i#fp;rA{oiUPbVGcI3xZqb z+=`Het@3i+vgZ3#hVPeBD64lMU2V@!upW=RFbcXX?6+PkeH^oBJ~?pj{v;OIx-9h% zIo}+q{(>FM;N00B2ZSvpRVRVIZ0=DDcv8~+1UPf|u|S+zHOgKu3oB}_g%|^xknd&P zMbvd&_OY|*y4C^>l)~5%)E`Que~Ox(3y3y57z)OYh8Z;L+PDFHy&V;5W}AN;qvfRU ztwSoF3qrQ3$)AV{Uif5`g_Zs^6V;8dvbB|3=kk`aE0TMs2hr(kLi*yzBpb~3YLP{kHh89c z_ENZ38<`6BDbQqisdAl7Dui69<_dLgUpTq3i-kKrw#@}5k4L1qIYt9gtXN1*prl}e z5v~eY%k?InGBToB_1-_lXjjs_TSfmz+EAsogoSPt$LKC0t4EEX|Ak#4lOZ@z?3n1EYqNe9iJ+ z&-;WO=LUMQOcjh(;ehb1A@Q+CW+AWeK!uWky8~itNdYjNp256xV^N7SAS4Cl!4%$a z#cV0tuXq!0f`(c3ii5vE#ck%rUUoj4VVrb@9Y3I*Us3kjeZX3gF(v#!%-2SC+N-~s zzw2pV0gJq3xvG$Whm^G{?g;}Mqy^Nd#5C2+DDf^8jVJwkQB-#ZxF9!wRsEx*vN#fo z#owtvA9)$9bzqGs{CZX4ryA)BG}LGgoGSV%O(e4-KJFji&VbFIILsbo5E+La^>T;p z^gu%rt`4&H&GRTCIIrCWI=K>{x)FeNFFSu zLMEzu+GZv1(d5uS^$Q0~w&fkJvTj3!nBIeM0BKqM`bS*HaFUSRJHSMPle@AM+0dB1AO;c^VV^vt7yob7DZQ}t_`RQAw@@nH9L=82huA+*Yl+Col+=$ z?KycKZDAeMU&YNiDaDggDf$F7YCewzH<;-p7+D zqdXaaN=(m5aIxSo7zQuAnyv8F!{!`b31Pz@m hgahpViayCQnj?Z9D*cZbcJPnDMZCM?GY4YEe*vPeTBHB~ literal 3631 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 diff --git a/selfdrive/ui/mici/onroad/driver_state.py b/selfdrive/ui/mici/onroad/driver_state.py index 44a248dae..a7a37304e 100644 --- a/selfdrive/ui/mici/onroad/driver_state.py +++ b/selfdrive/ui/mici/onroad/driver_state.py @@ -16,11 +16,15 @@ DEBUG = False LOOKING_CENTER_THRESHOLD_UPPER = math.radians(6) LOOKING_CENTER_THRESHOLD_LOWER = math.radians(3) +CONE_COLOR_GREEN = (0, 255, 64) +CONE_COLOR_ORANGE = (255, 115, 0) + class DriverStateRenderer(Widget): BASE_SIZE = 60 LINES_ANGLE_INCREMENT = 5 LINES_STALE_ANGLES = 3.0 # seconds + AWARENESS_UNFULL_THRESHOLD = 0.95 # ~0.5s def __init__(self, lines: bool = False, inset: bool = False): super().__init__() @@ -38,8 +42,10 @@ class DriverStateRenderer(Widget): self._should_draw = False self._force_active = False self._looking_center = False + self._awareness_unfull = False self._fade_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps) + self._color_fade_filter = FirstOrderFilter(1.0, 0.05, 1 / gui_app.target_fps) self._pitch_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) self._yaw_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) self._rotation_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps, initialized=False) @@ -97,6 +103,7 @@ class DriverStateRenderer(Widget): self._rect.y + (self._rect.height - self._dm_person.height) / 2), 0.0, 1.0, rl.Color(255, 255, 255, int(255 * 0.9 * self._fade_filter.x))) + green_amount = self._color_fade_filter.update(0.0 if self._awareness_unfull else 1.0) if self.effective_active: source_rect = rl.Rectangle(0, 0, self._dm_cone.width, self._dm_cone.height) dest_rect = rl.Rectangle( @@ -107,13 +114,16 @@ class DriverStateRenderer(Widget): ) if not self._lines: + r = int(round(CONE_COLOR_GREEN[0] * green_amount + CONE_COLOR_ORANGE[0] * (1 - green_amount))) + g = int(round(CONE_COLOR_GREEN[1] * green_amount + CONE_COLOR_ORANGE[1] * (1 - green_amount))) + b = int(round(CONE_COLOR_GREEN[2] * green_amount + CONE_COLOR_ORANGE[2] * (1 - green_amount))) rl.draw_texture_pro( self._dm_cone, source_rect, dest_rect, rl.Vector2(dest_rect.width / 2, dest_rect.height / 2), self._rotation_filter.x - 90, - rl.Color(255, 255, 255, int(255 * self._fade_filter.x)), + rl.Color(r, g, b, int(255 * self._fade_filter.x)), ) else: @@ -156,6 +166,7 @@ class DriverStateRenderer(Widget): self._is_active = dm_state.isActiveMode self._is_rhd = dm_state.isRHD self._face_detected = dm_state.faceDetected + self._awareness_unfull = self.effective_active and dm_state.awarenessStatus < self.AWARENESS_UNFULL_THRESHOLD driverstate = sm["driverStateV2"] driver_data = driverstate.rightDriverData if self._is_rhd else driverstate.leftDriverData diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index 13cb15fc6..71bb08993 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -1197,7 +1197,6 @@ class StarPilotVariables: GM_CAR.CHEVROLET_VOLT_ASCM, GM_CAR.CHEVROLET_VOLT_CAMERA, } - gm_auto_hold_supported &= toggle.openpilot_longitudinal toggle.gm_auto_hold = self.get_value("GMAutoHold", condition=gm_auto_hold_supported) toggle.volt_sng = self.get_value("VoltSNG", condition=toggle.car_model == "CHEVROLET_VOLT") diff --git a/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json b/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json index dbb2206e9..fe9a2f535 100644 --- a/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json +++ b/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json @@ -2189,7 +2189,7 @@ { "key": "GMAutoHold", "label": "Volt Auto Hold", - "description": "Hold the car at a stop on supported non-CC-only Chevy Volt alpha-longitudinal setups until the gas pedal is pressed.", + "description": "Hold the car at a stop on supported non-CC-only Chevy Volts until the gas pedal is pressed.", "data_type": "bool", "ui_type": "toggle" },