From 0545016ddeb5f560e302f648392e33df3968eae9 Mon Sep 17 00:00:00 2001 From: Rick Lan Date: Mon, 31 Mar 2025 11:59:01 +0800 Subject: [PATCH] alka - init --- cereal/custom.capnp | 3 ++- cereal/log.capnp | 2 +- cereal/services.py | 1 + common/params_keys.h | 1 + opendbc_repo/opendbc/car/nissan/carcontroller.py | 2 +- opendbc_repo/opendbc/car/structs.py | 1 + opendbc_repo/opendbc/car/toyota/carcontroller.py | 2 +- opendbc_repo/opendbc/car/toyota/values.py | 2 ++ opendbc_repo/opendbc/safety/__init__.py | 2 ++ opendbc_repo/opendbc/safety/safety.h | 13 ++++++++----- .../opendbc/safety/safety/safety_honda.h | 3 ++- .../opendbc/safety/safety_declarations.h | 2 ++ selfdrive/car/card.py | 6 ++++++ selfdrive/controls/controlsd.py | 16 ++++++++++++++-- selfdrive/selfdrived/selfdrived.py | 4 +++- selfdrive/selfdrived/state.py | 5 +++-- selfdrive/ui/qt/offroad/dp_panel.cc | 5 +++++ selfdrive/ui/qt/onroad/onroad_home.cc | 2 +- selfdrive/ui/ui.cc | 2 ++ selfdrive/ui/ui.h | 3 +++ system/manager/manager.py | 1 + 21 files changed, 62 insertions(+), 16 deletions(-) diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 3348e859e..988938bff 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -10,7 +10,8 @@ $Cxx.namespace("cereal"); # DO rename the structs # DON'T change the identifier (e.g. @0x81c2f05a394cf4af) -struct CustomReserved0 @0x81c2f05a394cf4af { +struct DpControlsState @0x81c2f05a394cf4af { + alkaActive @0 :Bool; } struct CustomReserved1 @0xaedffd8f31e7b55d { diff --git a/cereal/log.capnp b/cereal/log.capnp index 7b6076311..2ef4ca4f5 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -2576,7 +2576,7 @@ struct Event { # DO change the name of the field and struct # DON'T change the ID (e.g. @107) # DON'T change which struct it points to - customReserved0 @107 :Custom.CustomReserved0; + dpControlsState @107 :Custom.DpControlsState; customReserved1 @108 :Custom.CustomReserved1; customReserved2 @109 :Custom.CustomReserved2; customReserved3 @110 :Custom.CustomReserved3; diff --git a/cereal/services.py b/cereal/services.py index aad83177b..54ca64958 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -91,6 +91,7 @@ _services: dict[str, tuple] = { "customReservedRawData0": (True, 0.), "customReservedRawData1": (True, 0.), "customReservedRawData2": (True, 0.), + "dpControlsState": (False, 100., 10), } SERVICE_LIST = {name: Service(*vals) for idx, (name, vals) in enumerate(_services.items())} diff --git a/common/params_keys.h b/common/params_keys.h index b576f1a97..4633163c2 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -119,4 +119,5 @@ inline static std::unordered_map keys = { {"Version", PERSISTENT}, {"dp_device_last_log", CLEAR_ON_MANAGER_START}, {"dp_device_reset_conf", CLEAR_ON_MANAGER_START}, + {"dp_lat_alka", PERSISTENT}, }; diff --git a/opendbc_repo/opendbc/car/nissan/carcontroller.py b/opendbc_repo/opendbc/car/nissan/carcontroller.py index 74c6041b4..17d6f427a 100644 --- a/opendbc_repo/opendbc/car/nissan/carcontroller.py +++ b/opendbc_repo/opendbc/car/nissan/carcontroller.py @@ -60,7 +60,7 @@ class CarController(CarControllerBase): # Below are the HUD messages. We copy the stock message and modify if self.CP.carFingerprint != CAR.NISSAN_ALTIMA: if self.frame % 2 == 0: - can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, + can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC.latActive, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)) if self.frame % 50 == 0: diff --git a/opendbc_repo/opendbc/car/structs.py b/opendbc_repo/opendbc/car/structs.py index 0959d5c75..80156fdf7 100644 --- a/opendbc_repo/opendbc/car/structs.py +++ b/opendbc_repo/opendbc/car/structs.py @@ -20,4 +20,5 @@ CarControlT = capnp.lib.capnp._StructModule CarParamsT = capnp.lib.capnp._StructModule class DPFlags: + LateralALKA = 1 pass diff --git a/opendbc_repo/opendbc/car/toyota/carcontroller.py b/opendbc_repo/opendbc/car/toyota/carcontroller.py index 8c8dfa1b3..80864c2cc 100644 --- a/opendbc_repo/opendbc/car/toyota/carcontroller.py +++ b/opendbc_repo/opendbc/car/toyota/carcontroller.py @@ -266,7 +266,7 @@ class CarController(CarControllerBase): if self.frame % 20 == 0 or send_ui: can_sends.append(toyotacan.create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, - hud_control.rightLaneDepart, CC.enabled, CS.lkas_hud)) + hud_control.rightLaneDepart, lat_active, CS.lkas_hud)) if (self.frame % 100 == 0 or send_ui) and (self.CP.enableDsu or self.CP.flags & ToyotaFlags.DISABLE_RADAR.value): can_sends.append(toyotacan.create_fcw_command(self.packer, fcw_alert)) diff --git a/opendbc_repo/opendbc/car/toyota/values.py b/opendbc_repo/opendbc/car/toyota/values.py index 889b9d0cb..4848a354e 100644 --- a/opendbc_repo/opendbc/car/toyota/values.py +++ b/opendbc_repo/opendbc/car/toyota/values.py @@ -76,6 +76,8 @@ class ToyotaFlags(IntFlag): RAISED_ACCEL_LIMIT = 1024 SECOC = 2048 + ALKA = 2 ** 12 + class Footnote(Enum): CAMRY = CarFootnote( "openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.", diff --git a/opendbc_repo/opendbc/safety/__init__.py b/opendbc_repo/opendbc/safety/__init__.py index ef90de3c5..077a16be8 100644 --- a/opendbc_repo/opendbc/safety/__init__.py +++ b/opendbc_repo/opendbc/safety/__init__.py @@ -9,3 +9,5 @@ class ALTERNATIVE_EXPERIENCE: DISABLE_STOCK_AEB = 2 RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX = 8 ALLOW_AEB = 16 + + ALKA = 2 ** 10 diff --git a/opendbc_repo/opendbc/safety/safety.h b/opendbc_repo/opendbc/safety/safety.h index ced9f3b88..e808ed4ac 100644 --- a/opendbc_repo/opendbc/safety/safety.h +++ b/opendbc_repo/opendbc/safety/safety.h @@ -641,8 +641,9 @@ bool longitudinal_brake_checks(int desired_brake, const LongitudinalLimits limit bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueSteeringLimits limits) { bool violation = false; uint32_t ts = microsecond_timer_get(); + bool alka = (alternative_experience & ALT_EXP_ALKA) != 0; - if (controls_allowed) { + if (controls_allowed || alka) { // *** global torque limit check *** violation |= max_limit_check(desired_torque, limits.max_steer, -limits.max_steer); @@ -669,7 +670,7 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueStee } // no torque if controls is not allowed - if (!controls_allowed && (desired_torque != 0)) { + if (!(controls_allowed || alka) && (desired_torque != 0)) { violation = true; } @@ -711,7 +712,7 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueStee } // reset to 0 if either controls is not allowed or there's a violation - if (violation || !controls_allowed) { + if (violation || !(controls_allowed || alka)) { valid_steer_req_count = 0; invalid_steer_req_count = 0; desired_torque_last = 0; @@ -727,7 +728,9 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueStee bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const AngleSteeringLimits limits) { bool violation = false; - if (controls_allowed && steer_control_enabled) { + bool alka = (alternative_experience & ALT_EXP_ALKA) != 0; + + if ((controls_allowed || alka) && steer_control_enabled) { // convert floating point angle rate limits to integers in the scale of the desired angle on CAN, // add 1 to not false trigger the violation. also fudge the speed by 1 m/s so rate limits are // always slightly above openpilot's in case we read an updated speed in between angle commands @@ -814,7 +817,7 @@ bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const } // No angle control allowed when controls are not allowed - violation |= !controls_allowed && steer_control_enabled; + violation |= !(controls_allowed || alka) && steer_control_enabled; return violation; } diff --git a/opendbc_repo/opendbc/safety/safety/safety_honda.h b/opendbc_repo/opendbc/safety/safety/safety_honda.h index 6361ba31f..6c952e3db 100644 --- a/opendbc_repo/opendbc/safety/safety/safety_honda.h +++ b/opendbc_repo/opendbc/safety/safety/safety_honda.h @@ -242,8 +242,9 @@ static bool honda_tx_hook(const CANPacket_t *to_send) { } // STEER: safety check + bool alka = (alternative_experience & ALT_EXP_ALKA) != 0; if ((addr == 0xE4) || (addr == 0x194)) { - if (!controls_allowed) { + if (!(controls_allowed || alka)) { bool steer_applied = GET_BYTE(to_send, 0) | GET_BYTE(to_send, 1); if (steer_applied) { tx = false; diff --git a/opendbc_repo/opendbc/safety/safety_declarations.h b/opendbc_repo/opendbc/safety/safety_declarations.h index c9fe9131a..26eb69321 100644 --- a/opendbc_repo/opendbc/safety/safety_declarations.h +++ b/opendbc_repo/opendbc/safety/safety_declarations.h @@ -255,6 +255,8 @@ extern struct sample_t angle_meas; // last 6 steer angles/curvatures // This flag allows AEB to be commanded from openpilot. #define ALT_EXP_ALLOW_AEB 16 +#define ALT_EXP_ALKA 1024 + extern int alternative_experience; // time since safety mode has been changed diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 714ca2009..26404dcaf 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -99,6 +99,9 @@ class Car: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params + if self.params.get_bool("dp_lat_alka"): + dp_params |= structs.DPFlags.LateralALKA + self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, dp_params, cached_params) self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP) self.CP = self.CI.CP @@ -115,6 +118,9 @@ class Car: if not disengage_on_accelerator: self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS + if dp_params & structs.DPFlags.LateralALKA: + self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALKA + openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index f6fabfc42..c7dc07975 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -38,7 +38,7 @@ class Controls: self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState', 'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput', 'driverMonitoringState', 'onroadEvents', 'driverAssistance'], poll='selfdriveState') - self.pm = messaging.PubMaster(['carControl', 'controlsState']) + self.pm = messaging.PubMaster(['carControl', 'controlsState', 'dpControlsState']) self.steer_limited_by_controls = False self.desired_curvature = 0.0 @@ -56,6 +56,9 @@ class Controls: elif self.CP.lateralTuning.which() == 'torque': self.LaC = LatControlTorque(self.CP, self.CI) + self.alka_enabled = self.params.get_bool("dp_lat_alka") + self.alka_active = False + def update(self): self.sm.update(15) if self.sm.updated["liveCalibration"]: @@ -88,7 +91,9 @@ class Controls: # Check which actuators can be enabled standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED) or CS.standstill - CC.latActive = self.sm['selfdriveState'].active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and not standstill + self.alka_active = self.alka_enabled and CS.cruiseState.available and not standstill and CS.gearShifter != car.CarState.GearShifter.reverse + lat_active = self.sm['selfdriveState'].active or self.alka_active + CC.latActive = lat_active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and not standstill CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and self.CP.openpilotLongitudinalControl actuators = CC.actuators @@ -169,6 +174,13 @@ class Controls: # TODO: both controlsState and carControl valids should be set by # sm.all_checks(), but this creates a circular dependency + # dpControlsState + dat = messaging.new_message('dpControlsState') + dat.valid = True + ncs = dat.dpControlsState + ncs.alkaActive = self.alka_active + self.pm.send('dpControlsState', dat) + # controlsState dat = messaging.new_message('controlsState') dat.valid = CS.canValid diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index ecf76fe7f..153c98211 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -58,6 +58,8 @@ class SelfdriveD: self.car_events = CarSpecificEvents(self.CP) self.disengage_on_accelerator = not (self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS) + self.alka = (self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALKA) + # Setup sockets self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents']) @@ -113,7 +115,7 @@ class SelfdriveD: self.experimental_mode = False self.personality = self.read_personality_param() self.recalibrating_seen = False - self.state_machine = StateMachine() + self.state_machine = StateMachine(self.alka) self.rk = Ratekeeper(100, print_delay_threshold=None) # Determine startup event diff --git a/selfdrive/selfdrived/state.py b/selfdrive/selfdrived/state.py index 073ddb56e..940fcb029 100644 --- a/selfdrive/selfdrived/state.py +++ b/selfdrive/selfdrived/state.py @@ -9,10 +9,11 @@ ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding) ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES) class StateMachine: - def __init__(self): + def __init__(self, alka = False): self.current_alert_types = [ET.PERMANENT] self.state = State.disabled self.soft_disable_timer = 0 + self.alka = alka def update(self, events: Events): # decrement the soft disable timer at every step, as it's reset on @@ -92,7 +93,7 @@ class StateMachine: # Check if openpilot is engaged and actuators are enabled enabled = self.state in ENABLED_STATES active = self.state in ACTIVE_STATES - if active: + if active or self.alka: self.current_alert_types.append(ET.WARNING) return enabled, active diff --git a/selfdrive/ui/qt/offroad/dp_panel.cc b/selfdrive/ui/qt/offroad/dp_panel.cc index 548b02be5..e4fbb7b59 100644 --- a/selfdrive/ui/qt/offroad/dp_panel.cc +++ b/selfdrive/ui/qt/offroad/dp_panel.cc @@ -106,6 +106,11 @@ void DPPanel::add_lateral_toggles() { QString::fromUtf8("🐉 ") + tr("Lateral Ctrl"), "", }, + { + "dp_lat_alka", + tr("Always-on Lane Keeping Assist (ALKA)"), + "", + }, }; QWidget *label = nullptr; diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 080f9bd50..88fb5ce8d 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -47,7 +47,7 @@ void OnroadWindow::updateState(const UIState &s) { alerts->updateState(s); nvg->updateState(s); - QColor bgColor = bg_colors[s.status]; + QColor bgColor = bg_colors[s.scene.alka_active && s.status == STATUS_DISENGAGED? STATUS_ALKA : s.status]; if (bg != bgColor) { // repaint border bg = bgColor; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index ec3d40961..9ec656ef0 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -60,6 +60,7 @@ static void update_state(UIState *s) { scene.light_sensor = -1; } scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition; + scene.alka_active = sm["dpControlsState"].getDpControlsState().getAlkaActive(); } void ui_update_params(UIState *s) { @@ -94,6 +95,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", + "dpControlsState", }); prime_state = new PrimeState(this); language = QString::fromStdString(Params().get("LanguageSetting")); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 6ff67cacd..5794b8bb9 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -42,12 +42,14 @@ typedef enum UIStatus { STATUS_DISENGAGED, STATUS_OVERRIDE, STATUS_ENGAGED, + STATUS_ALKA, } UIStatus; const QColor bg_colors [] = { [STATUS_DISENGAGED] = QColor(0x17, 0x33, 0x49, 0xc8), [STATUS_OVERRIDE] = QColor(0x91, 0x9b, 0x95, 0xf1), [STATUS_ENGAGED] = QColor(0x17, 0x86, 0x44, 0xf1), + [STATUS_ALKA] = QColor(0x7A, 0x89, 0xA0, 0xf1), }; typedef struct UIScene { @@ -60,6 +62,7 @@ typedef struct UIScene { float light_sensor = -1; bool started, ignition, is_metric; uint64_t started_frame; + bool alka_active; } UIScene; class UIState : public QObject { diff --git a/system/manager/manager.py b/system/manager/manager.py index 9fe81947f..410cdb278 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -41,6 +41,7 @@ def manager_init() -> None: ("OpenpilotEnabledToggle", "1"), ("LongitudinalPersonality", str(log.LongitudinalPersonality.standard)), ("DisableLogging", "0"), + ("dp_lat_alka", "0"), ] if params.get_bool("RecordFrontLock"):