diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 8e761c2030..2e83ce373d 100644 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -492,10 +492,7 @@ class Car: if self.starpilot_toggles.speed_limit_controller: overridden_speed = float(starpilot_plan.slcOverriddenSpeed) slc_limit = float(starpilot_plan.slcSpeedLimit) + float(starpilot_plan.slcSpeedLimitOffset) - allow_lower_override = ( - getattr(self.starpilot_toggles, "redneck_cruise", False) and - getattr(self.starpilot_toggles, "speed_limit_controller_override_set_speed", False) - ) + allow_lower_override = getattr(self.starpilot_toggles, "redneck_cruise", False) slc_target_speed = overridden_speed if allow_lower_override and overridden_speed > 0 else max(overridden_speed, slc_limit) # Use acceleration projection only when SLC has no resolved target. diff --git a/selfdrive/car/tests/test_redneck_cruise.py b/selfdrive/car/tests/test_redneck_cruise.py index aa2891bc50..0e281bb66c 100644 --- a/selfdrive/car/tests/test_redneck_cruise.py +++ b/selfdrive/car/tests/test_redneck_cruise.py @@ -265,7 +265,6 @@ class TestRedneckCruise(unittest.TestCase): starpilot_toggles=SimpleNamespace( speed_limit_controller=True, redneck_cruise=True, - speed_limit_controller_override_set_speed=True, ), ) car_state = SimpleNamespace( diff --git a/selfdrive/controls/tests/test_speed_limit_controller.py b/selfdrive/controls/tests/test_speed_limit_controller.py index 20009e5f8f..928e67a7b9 100644 --- a/selfdrive/controls/tests/test_speed_limit_controller.py +++ b/selfdrive/controls/tests/test_speed_limit_controller.py @@ -47,8 +47,6 @@ def make_toggles(**overrides): "slc_mapbox_filler": False, "speed_limit_confirmation_higher": False, "speed_limit_confirmation_lower": False, - "speed_limit_controller_override_manual": True, - "speed_limit_controller_override_set_speed": False, "redneck_cruise": False, "speed_limit_filler": False, "speed_limit_offset1": 0.0, @@ -317,8 +315,6 @@ def test_display_only_applies_large_delta_guard(): def test_set_speed_override_survives_source_changes_and_fallback_until_driver_clears(): controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, speed_limit_priority1="Map Data", speed_limit_priority2="Dashboard", slc_fallback_set_speed=True, @@ -444,10 +440,7 @@ def test_unconfirmed_lower_limit_keeps_existing_override(): def test_set_speed_override_handles_higher_limit_changes(): - controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, - ) + controller = make_controller() try: controller.source = "Dashboard" controller.target = mph(35) @@ -479,19 +472,20 @@ def test_set_speed_override_handles_higher_limit_changes(): controller.shutdown() -def test_set_speed_override_follows_driver_wheel_intent(): - controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, - ) +def test_pedal_and_set_speed_overrides_are_independent(): + controller = make_controller() try: controller.source = "Dashboard" controller.target = mph(45) controller.last_valid_limit = mph(45) - # Gas never creates a persistent wheel override. + # A pedal pass is temporary; a set-speed increase is the fixed persistent action. controller.update_override(mph(45), 0.0, mph(45), 0.0, make_sm(gas_pressed=False)) controller.update_override(mph(45), 0.0, mph(55), 0.0, make_sm(gas_pressed=True)) + assert controller.override_slc + assert controller.overridden_speed == pytest.approx(mph(55)) + + controller.update_override(mph(45), 0.0, mph(55), 0.0, make_sm(gas_pressed=False)) assert not controller.override_slc assert controller.overridden_speed == 0 @@ -500,6 +494,14 @@ def test_set_speed_override_follows_driver_wheel_intent(): assert controller.override_slc assert controller.overridden_speed == pytest.approx(mph(55)) + # Pedaling temporarily takes priority, then returns to the selected set speed. + controller.update_override(mph(55), 0.0, mph(60), 0.0, make_sm(gas_pressed=True)) + assert controller.overridden_speed == pytest.approx(mph(60)) + + controller.update_override(mph(55), 0.0, mph(60), 0.0, make_sm(gas_pressed=False)) + assert controller.override_slc + assert controller.overridden_speed == pytest.approx(mph(55)) + controller.update_override(mph(60), 0.0, mph(58), 0.0, make_sm(gas_pressed=False)) assert controller.override_slc assert controller.overridden_speed == pytest.approx(mph(60)) @@ -516,11 +518,9 @@ def test_set_speed_override_follows_driver_wheel_intent(): controller.shutdown() -def test_set_speed_mode_waits_until_above_slc_target_with_offset(): +def test_persistent_override_waits_until_above_slc_target_with_offset(): controller = make_controller( is_metric=True, - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, speed_limit_offset2=3 * CV.KPH_TO_MS, ) try: @@ -546,10 +546,7 @@ def test_set_speed_mode_waits_until_above_slc_target_with_offset(): def test_set_speed_override_clears_on_new_speed_zone(): # Entering a new (lower) posted limit clears the override; a steady high set speed must not # re-arm it. Only a fresh +/- press re-arms. - controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, - ) + controller = make_controller() try: controller.source = "Dashboard" controller.target = mph(45) @@ -579,8 +576,6 @@ def test_set_speed_override_clears_on_new_speed_zone(): def test_confirmation_accel_press_does_not_arm_set_speed_override(): controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, speed_limit_confirmation_higher=True, ) try: @@ -622,10 +617,7 @@ def test_confirmation_accel_press_does_not_arm_set_speed_override(): def test_adopt_speed_limit_clears_complete_override_state(): - controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, - ) + controller = make_controller() try: controller.source = "Dashboard" controller.target = mph(45) @@ -645,10 +637,8 @@ def test_adopt_speed_limit_clears_complete_override_state(): controller.shutdown() -def test_redneck_set_speed_mode_overrides_in_both_directions(): +def test_redneck_set_speed_override_is_bidirectional(): controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, redneck_cruise=True, ) try: @@ -670,10 +660,7 @@ def test_redneck_set_speed_mode_overrides_in_both_directions(): def test_manual_override_tracks_current_speed_and_ends_on_release(): - controller = make_controller( - speed_limit_controller_override_manual=True, - speed_limit_controller_override_set_speed=False, - ) + controller = make_controller() try: controller.source = "Dashboard" controller.target = mph(45) @@ -724,10 +711,7 @@ def test_manual_override_survives_brief_enabled_flicker(): def test_override_clears_after_sustained_disengage(): - controller = make_controller( - speed_limit_controller_override_manual=False, - speed_limit_controller_override_set_speed=True, - ) + controller = make_controller() try: controller.source = "Dashboard" controller.target = mph(45) diff --git a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py index ef96d49ca2..70b90afab3 100644 --- a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py +++ b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py @@ -77,13 +77,6 @@ SLC_FALLBACK_OPTIONS = [ (2, "Previous Limit"), ] -SLC_OVERRIDE_OPTIONS = [ - (0, "None"), - (1, "Set With Gas Pedal"), - (2, "Max Set Speed"), -] - - # ═══════════════════════════════════════════════════════════════ # AdaptiveSpeedView — nested panel with two adaptive speed tiles # ═══════════════════════════════════════════════════════════════ @@ -599,11 +592,6 @@ class StarPilotLongitudinalLayout(_SettingsPage): get_value=lambda: self._profile_label_for_value(self._params.get_int("SLCFallback"), SLC_FALLBACK_OPTIONS), on_click=lambda: self._show_labeled_select("Fallback Speed", "SLCFallback", SLC_FALLBACK_OPTIONS, self._params.get_int("SLCFallback"))), - SettingRow("SLCOverride", "value", tr_noop("Override Speed"), - subtitle="", - get_value=lambda: self._profile_label_for_value(self._params.get_int("SLCOverride"), SLC_OVERRIDE_OPTIONS), - on_click=lambda: self._show_labeled_select("Override Speed", "SLCOverride", SLC_OVERRIDE_OPTIONS, - self._params.get_int("SLCOverride"))), SettingRow("SLCPriority", "value", tr_noop("Source Priority"), subtitle="", get_value=self._get_priority_value, @@ -889,7 +877,7 @@ class StarPilotLongitudinalLayout(_SettingsPage): self, [SettingSection(title="", rows=self._slc_rows)], header_title=tr_noop("Speed Limit Controller"), - header_subtitle=tr_noop("Manage auto speed matching, confirmation, offsets, and source priority."), + header_subtitle=tr_noop("Press + above a limit for a persistent override; hold the gas pedal for a temporary override."), parent_toggle=pt_slc, panel_style=PANEL_STYLE, ) diff --git a/selfdrive/ui/translations/main_uk.ts b/selfdrive/ui/translations/main_uk.ts index e5e6f0f6c6..5b044f4a1d 100644 --- a/selfdrive/ui/translations/main_uk.ts +++ b/selfdrive/ui/translations/main_uk.ts @@ -1674,10 +1674,6 @@ Fallback Speed Резерв. дж. лімітів - - Override Speed - Ручна швидк. - Confirm New Speed Limits Підтверд. новий ліміт шв. @@ -1834,14 +1830,6 @@ None Нема - - Set With Gas Pedal - Педаль - - - Max Set Speed - Макс встан. швидк. - SELECT ОБРАТИ @@ -2346,10 +2334,6 @@ <b>The speed used by "Speed Limit Controller" when no speed limit is found.</b><br><br>- <b>Set Speed</b>: Use the cruise set speed<br>- <b>Experimental Mode</b>: Estimate the limit using the driving model<br>- <b>Previous Limit</b>: Keep using the last confirmed limit <b>Швидкість, яка використовується «Контролером обмеження швидкості», коли обмеження швидкості не виявлено.</b><br><br>- <b>Встановити швидкість</b>: Використовувати встановлену швидкість круїз-контролю<br>- <b>Експериментальний режим</b>: Оцінити обмеження за допомогою моделі водіння<br>- <b>Попереднє обмеження</b>: Продовжувати використовувати останнє підтверджене обмеження - - <b>The speed used by "Speed Limit Controller" after you manually drive faster than the posted limit.</b><br><br>- <b>Set with Gas Pedal</b>: Use the highest speed reached while pressing the gas<br>- <b>Max Set Speed</b>: Use the cruise set speed<br><br>Overrides clear when openpilot disengages. - <b>Швидкість, яку використовує «Контролер обмеження швидкості» після того, як ви вручну перевищили встановлене обмеження. </b><br><br>- <b>Встановлюється за допомогою педалі газу</b>: використовується найвища швидкість, досягнута під час натискання на педаль газу<br>- <b>Максимальна встановлена швидкість</b>: використовується встановлена швидкість круїз-контролю<br><br>Перезапис скасовується, коли OpenPilot деактивується. - <b>Miscellaneous "Speed Limit Controller" changes</b> to fine-tune how openpilot drives. <b>Різні зміни в «Контролері обмеження швидкості»</b> для точного налаштування керуваня openpilot. diff --git a/starpilot/common/assets/device_settings_layout.json b/starpilot/common/assets/device_settings_layout.json index a1fba4454c..4342fdf9ee 100644 --- a/starpilot/common/assets/device_settings_layout.json +++ b/starpilot/common/assets/device_settings_layout.json @@ -2106,8 +2106,8 @@ { "key": "SpeedLimitController", "label": "Speed Limit Controller", - "description": "Limit openpilot's maximum driving speed to the current speed limit from configured map, dashboard, and optional vision sources.", - "picker_description": "Limits speed using map, dashboard, or vision data.", + "description": "Limit openpilot's maximum driving speed using configured map, dashboard, and optional vision sources. Press + above a limit for a persistent override; hold the gas pedal for a temporary override.", + "picker_description": "Press + above a limit for a persistent override; hold the gas pedal for a temporary override.", "data_type": "bool", "ui_type": "toggle", "is_parent_toggle": true, @@ -2200,30 +2200,6 @@ "parent_key": "SpeedLimitController", "settings_tier": "advanced" }, - { - "key": "SLCOverride", - "label": "Override Speed", - "description": "Choose how SLC behaves after you manually drive faster than the posted speed limit.", - "picker_description": "Chooses how SLC responds after you exceed the limit.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "None" - }, - { - "value": 1, - "label": "Set With Gas Pedal" - }, - { - "value": 2, - "label": "Max Set Speed" - } - ], - "parent_key": "SpeedLimitController", - "settings_tier": "advanced" - }, { "key": "SLCMapboxFiller", "label": "Use Mapbox as Fallback", diff --git a/starpilot/controls/lib/speed_limit_controller.py b/starpilot/controls/lib/speed_limit_controller.py index 31d124a3ee..9952c21180 100644 --- a/starpilot/controls/lib/speed_limit_controller.py +++ b/starpilot/controls/lib/speed_limit_controller.py @@ -52,6 +52,7 @@ class SpeedLimitController: self.override_slc = False self.override_disable_timer = 0.0 self._prev_v_cruise = None + self._persistent_override_speed = 0.0 self._set_speed_override_input_consumed = False self.denied_target = 0 @@ -126,16 +127,20 @@ class SpeedLimitController: def clear_override(self): self.override_slc = False self.overridden_speed = 0 + self._persistent_override_speed = 0.0 + + def clear_persistent_override(self): + self._persistent_override_speed = 0.0 def clear_persistent_override_for_limit_change(self, previous_limit, new_limit): - if not self.starpilot_toggles.speed_limit_controller_override_set_speed or self.overridden_speed <= 0: + if self._persistent_override_speed <= 0: return if previous_limit <= 0 or new_limit <= 0 or abs(new_limit - previous_limit) < 0.1: return new_target_with_offset = new_limit + self.get_offset(new_limit) - if new_limit < previous_limit or self.overridden_speed <= new_target_with_offset: - self.clear_override() + if new_limit < previous_limit or self._persistent_override_speed <= new_target_with_offset: + self.clear_persistent_override() def get_mapbox_speed_limit(self, now, time_validated, v_ego, sm): if not self.starpilot_planner.gps_valid or not self.mapbox_token or abs(sm["carState"].steeringAngleDeg - sm["liveParameters"].angleOffsetDeg) >= 45: @@ -519,41 +524,32 @@ class SpeedLimitController: target_to_use = self.target_to_use target_with_offset = target_to_use + self.get_offset(target_to_use) - if self.starpilot_toggles.speed_limit_controller_override_manual: - if sm["carState"].gasPressed and v_ego > target_with_offset > 0: - self.override_slc = True - self.overridden_speed = v_ego + v_ego_diff - else: - self.clear_override() - return - - if not self.starpilot_toggles.speed_limit_controller_override_set_speed: - self.clear_override() - return - set_speed = v_cruise + v_cruise_diff bidirectional_set_speed = getattr(self.starpilot_toggles, "redneck_cruise", False) - if bidirectional_set_speed: - if self.override_slc and set_speed > 0: - self.overridden_speed = set_speed - elif target_with_offset > 0 and set_speed > 0 and set_speed_changed and not set_speed_input_consumed: - self.override_slc = True - self.overridden_speed = set_speed - else: - self.clear_override() - return - if self.override_slc: - # A fallback transition alone preserves the override; a fresh set-speed change may clear it. - if set_speed <= 0 or ( - target_with_offset > 0 and set_speed <= target_with_offset and - (self.source != "None" or set_speed_changed) - ): - self.clear_override() + if self._persistent_override_speed > 0: + if bidirectional_set_speed: + if set_speed <= 0: + self.clear_persistent_override() + else: + self._persistent_override_speed = set_speed + elif set_speed <= 0 or (target_with_offset > 0 and set_speed <= target_with_offset and (self.source != "None" or set_speed_changed)): + self.clear_persistent_override() else: - self.overridden_speed = set_speed - elif target_with_offset > 0 and set_speed_raised and set_speed > target_with_offset and not set_speed_input_consumed: + self._persistent_override_speed = set_speed + elif ( + target_with_offset > 0 + and set_speed > 0 + and not set_speed_input_consumed + and ((bidirectional_set_speed and set_speed_changed) or (not bidirectional_set_speed and set_speed_raised and set_speed > target_with_offset)) + ): + self._persistent_override_speed = set_speed + + if sm["carState"].gasPressed and v_ego > target_with_offset > 0: self.override_slc = True - self.overridden_speed = set_speed + self.overridden_speed = v_ego + v_ego_diff + elif self._persistent_override_speed > 0: + self.override_slc = True + self.overridden_speed = self._persistent_override_speed else: self.clear_override() diff --git a/starpilot/controls/lib/starpilot_acceleration.py b/starpilot/controls/lib/starpilot_acceleration.py index b90c34f10e..6e0e963eb4 100644 --- a/starpilot/controls/lib/starpilot_acceleration.py +++ b/starpilot/controls/lib/starpilot_acceleration.py @@ -221,8 +221,7 @@ class StarPilotAcceleration: getattr(self.starpilot_planner.starpilot_vcruise, "slc_offset", 0.0), getattr(getattr(self.starpilot_planner.starpilot_vcruise, "slc", None), "overridden_speed", 0.0), max(float(getattr(sm["carState"], "vEgoCluster", v_ego) or v_ego), v_ego) - v_ego, - allow_lower_override=(getattr(starpilot_toggles, "redneck_cruise", False) and - getattr(starpilot_toggles, "speed_limit_controller_override_set_speed", False)), + allow_lower_override=getattr(starpilot_toggles, "redneck_cruise", False), ) v_target = float(self.starpilot_planner.v_cruise or raw_v_cruise) if effective_slc_target > 0.0: @@ -277,8 +276,7 @@ class StarPilotAcceleration: getattr(self.starpilot_planner.starpilot_vcruise, "slc_offset", 0.0), getattr(getattr(self.starpilot_planner.starpilot_vcruise, "slc", None), "overridden_speed", 0.0), v_ego_diff, - allow_lower_override=(getattr(starpilot_toggles, "redneck_cruise", False) and - getattr(starpilot_toggles, "speed_limit_controller_override_set_speed", False)), + allow_lower_override=getattr(starpilot_toggles, "redneck_cruise", False), ) v_target = float(self.starpilot_planner.v_cruise or raw_v_cruise) if effective_slc_target > 0.0: @@ -319,8 +317,7 @@ class StarPilotAcceleration: getattr(self.starpilot_planner.starpilot_vcruise, "slc_offset", 0.0), getattr(getattr(self.starpilot_planner.starpilot_vcruise, "slc", None), "overridden_speed", 0.0), max(v_ego_cluster, v_ego) - v_ego, - allow_lower_override=(getattr(starpilot_toggles, "redneck_cruise", False) and - getattr(starpilot_toggles, "speed_limit_controller_override_set_speed", False)), + allow_lower_override=getattr(starpilot_toggles, "redneck_cruise", False), ) v_target = float(self.starpilot_planner.v_cruise or raw_v_cruise) if effective_slc_target > 0.0: diff --git a/starpilot/controls/lib/starpilot_vcruise.py b/starpilot/controls/lib/starpilot_vcruise.py index b41a47b523..c8268ca117 100644 --- a/starpilot/controls/lib/starpilot_vcruise.py +++ b/starpilot/controls/lib/starpilot_vcruise.py @@ -752,8 +752,7 @@ class StarPilotVCruise: self.slc_offset, self.slc.overridden_speed, v_ego_diff, - allow_lower_override=(getattr(starpilot_toggles, "redneck_cruise", False) and - getattr(starpilot_toggles, "speed_limit_controller_override_set_speed", False)), + allow_lower_override=getattr(starpilot_toggles, "redneck_cruise", False), ) slc_control_target = get_slc_lead_drop_relaxed_target( slc_control_target, diff --git a/starpilot/controls/tests/test_personality_longitudinal_profiles.py b/starpilot/controls/tests/test_personality_longitudinal_profiles.py index 213888fbd9..80ae7fcdc3 100644 --- a/starpilot/controls/tests/test_personality_longitudinal_profiles.py +++ b/starpilot/controls/tests/test_personality_longitudinal_profiles.py @@ -96,7 +96,6 @@ def _toggles(document): set_speed_limit=False, set_speed_offset=0.0, speed_limit_controller=False, - speed_limit_controller_override_set_speed=False, truck_tuning=False, ) diff --git a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py index 24741b335c..a26cc4e8a3 100644 --- a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py +++ b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py @@ -44,6 +44,20 @@ def test_galaxy_layout_removes_obsolete_and_duplicate_controls(): ) == 1 +def test_slc_override_method_is_not_exposed_in_either_settings_ui(): + layout = _layout() + galaxy_keys = { + param["key"] + for section in layout + for param in section.get("params", []) + } + device_ui = (REPO_ROOT / "selfdrive/ui/layouts/settings/starpilot/longitudinal.py").read_text(encoding="utf-8") + + assert "SLCOverride" not in galaxy_keys + assert 'SettingRow("SLCOverride"' not in device_ui + assert "SLC_OVERRIDE_OPTIONS" not in device_ui + + def test_galaxy_layout_contains_basic_mode_controls(): sections = _params_by_section(_layout())