From 88c259a666799800dc3a2c8d459f4157f0083c3a Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Mon, 8 Jun 2026 19:15:50 -0500 Subject: [PATCH] Prioritize Smooth Follow --- common/params_keys.h | 1 + .../controls/lib/longitudinal_planner.py | 20 +++++---- .../controls/tests/test_starpilot_planner.py | 41 +++++++++++++++++++ .../settings/starpilot/longitudinal.py | 8 ++-- starpilot/common/safe_mode.py | 2 +- starpilot/common/starpilot_variables.py | 10 +++-- starpilot/controls/lib/starpilot_following.py | 3 +- starpilot/controls/starpilot_planner.py | 14 ++++--- .../tools/device_settings_layout.json | 6 +-- .../ui/qt/offroad/longitudinal_settings.cc | 2 +- .../ui/qt/offroad/longitudinal_settings.h | 2 +- system/manager/manager.py | 35 +++++++++++----- system/manager/test/test_manager.py | 32 +++++++++++---- tools/StarPilot/feasibleparams.txt | 2 +- 14 files changed, 129 insertions(+), 49 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index dc13a754a..a7f8490bd 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -335,6 +335,7 @@ inline static std::unordered_map keys = { {"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0}}, {"HumanAcceleration", {PERSISTENT, BOOL, "0", "0", 2}}, {"CoastUpToLeads", {PERSISTENT, BOOL, "1", "1", 2}}, + {"PrioritizeSmoothFollowing", {PERSISTENT, BOOL, "0", "0", 2}}, {"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}}, {"IconPack", {PERSISTENT, STRING, "stock", "stock", 0}}, {"IconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}}, diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 324253c0b..f4268877e 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -1886,8 +1886,11 @@ class LongitudinalPlanner: uncert_slope > UNCERT_SLOPE_TRIG or uncertainty >= UNCERT_MAG_TRIG ) + prioritize_smooth_following = bool(getattr(starpilot_toggles, "prioritize_smooth_following", False)) + allow_complex_follow_logic = not prioritize_smooth_following + steady_follow_filter_floor = 0.0 - if lead_one_active and desired_gap is not None and not panic_bypass: + if allow_complex_follow_logic and lead_one_active and desired_gap is not None and not panic_bypass: lead_brake = max(0.0, -float(getattr(self.lead_one, "aLeadK", 0.0))) lead_radar = bool(getattr(self.lead_one, "radar", False)) lead_prob = float(getattr(self.lead_one, "modelProb", 1.0 if lead_radar else 0.0)) @@ -1945,11 +1948,10 @@ class LongitudinalPlanner: dec_mpc_mode = self.get_mpc_mode() if not self.mlsim: self.mpc.mode = dec_mpc_mode - optional_far_lead_comfort = getattr(starpilot_toggles, "coast_up_to_leads", True) self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, sm['starpilotPlan'].dangerFactor, effective_t_follow, personality=personality, tracking_lead=lead_control_active, - optional_far_lead_comfort=optional_far_lead_comfort) + optional_far_lead_comfort=allow_complex_follow_logic) self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution) self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution) @@ -1975,7 +1977,7 @@ class LongitudinalPlanner: self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 # Anticipatory pre-brake to avoid "coming in hot" when closing on a lead - if lead_one_active: + if allow_complex_follow_logic and lead_one_active: rel_v = max(0.0, v_ego - self.lead_one.vLead) # dynamic time headway adds a small buffer when uncertainty is elevated base_th = max(1.6, effective_t_follow) @@ -2244,7 +2246,7 @@ class LongitudinalPlanner: lead_control_active, scene_v_ego, effective_t_follow, - allow_optional_far_lead_logic=optional_far_lead_comfort, + allow_optional_far_lead_logic=allow_complex_follow_logic, ) if follow_control_lead is not None and not panic_bypass: if not output_should_stop and not vision_low_speed_stop_active: @@ -2259,7 +2261,7 @@ class LongitudinalPlanner: self.a_desired = min(self.a_desired, tracked_vision_model_brake_floor) output_a_target = min(output_a_target, tracked_vision_model_brake_floor) - if optional_far_lead_comfort: + if allow_complex_follow_logic: matched_follow_brake_cap = self.get_matched_follow_brake_cap(follow_control_lead, scene_v_ego, effective_t_follow) if matched_follow_brake_cap is not None: self.a_desired = max(self.a_desired, matched_follow_brake_cap) @@ -2278,7 +2280,7 @@ class LongitudinalPlanner: output_a_target = max(output_a_target, low_speed_transition_brake_cap) comfort_lead = self.lead_two if self.mpc.source == 'lead1' and self.lead_two.status else self.lead_one - if optional_far_lead_comfort and comfort_lead is not None and not panic_bypass: + if allow_complex_follow_logic and comfort_lead is not None and not panic_bypass: far_lead_brake_cap = self.get_far_lead_brake_cap(comfort_lead, scene_v_ego, effective_t_follow) if far_lead_brake_cap is not None: self.a_desired = max(self.a_desired, far_lead_brake_cap) @@ -2295,7 +2297,7 @@ class LongitudinalPlanner: self.a_desired = max(self.a_desired, tracked_vision_model_brake_cap) output_a_target = max(output_a_target, tracked_vision_model_brake_cap) - if optional_far_lead_comfort and follow_control_lead is not None and not panic_bypass and not output_should_stop and not vision_low_speed_stop_active: + if allow_complex_follow_logic and follow_control_lead is not None and not panic_bypass and not output_should_stop and not vision_low_speed_stop_active: matched_follow_transition_target = self.get_matched_follow_transition_target( follow_control_lead, scene_v_ego, @@ -2312,7 +2314,7 @@ class LongitudinalPlanner: self.a_desired = max(self.a_desired, matched_follow_transition_target) output_a_target = matched_follow_transition_target - if optional_far_lead_comfort and comfort_lead is not None and not panic_bypass and not output_should_stop and not vision_low_speed_stop_active: + if allow_complex_follow_logic and comfort_lead is not None and not panic_bypass and not output_should_stop and not vision_low_speed_stop_active: near_duplicate_transition_target = self.get_near_duplicate_lead_transition_target( comfort_lead, scene_v_ego, diff --git a/selfdrive/controls/tests/test_starpilot_planner.py b/selfdrive/controls/tests/test_starpilot_planner.py index 6df046b29..cfa995c5b 100644 --- a/selfdrive/controls/tests/test_starpilot_planner.py +++ b/selfdrive/controls/tests/test_starpilot_planner.py @@ -117,3 +117,44 @@ def test_lateral_resume_delay_ignores_signal_cycles_that_never_slow_enough(monke assert planner.blinker_delay_active is False finally: planner.shutdown() + + +def test_prioritize_smooth_following_skips_radarless_follow_hold(monkeypatch): + planner = StarPilotPlanner(Path("/tmp/nonexistent"), DummyThemeManager()) + + try: + monkeypatch.setattr(starpilot_planner_module.time, "monotonic", lambda: 100.0) + planner.model_length = 30.0 + planner.tracking_lead = True + planner.starpilot_following.t_follow = 1.45 + planner.lead_one = SimpleNamespace( + status=True, + dRel=46.0, + vLead=27.0, + aLeadK=0.0, + modelProb=0.98, + radar=False, + ) + + planner.update_lead_status(27.5, stop_distance=6.0, prioritize_smooth_following=False) + assert planner.radarless_follow_hold_until > 100.0 + + planner_smooth = StarPilotPlanner(Path("/tmp/nonexistent"), DummyThemeManager()) + planner_smooth.model_length = 30.0 + planner_smooth.tracking_lead = True + planner_smooth.starpilot_following.t_follow = 1.45 + planner_smooth.lead_one = SimpleNamespace( + status=True, + dRel=46.0, + vLead=27.0, + aLeadK=0.0, + modelProb=0.98, + radar=False, + ) + + planner_smooth.update_lead_status(27.5, stop_distance=6.0, prioritize_smooth_following=True) + assert planner_smooth.radarless_follow_hold_until == 0.0 + finally: + planner.shutdown() + if 'planner_smooth' in locals(): + planner_smooth.shutdown() diff --git a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py index b73a13339..9758ecae3 100644 --- a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py +++ b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py @@ -351,10 +351,10 @@ class StarPilotLongitudinalTuneLayout(_SettingsPage): visible=self._longitudinal_enabled), ]), SettingSection(tr_noop("Human-Like Driving"), [ - SettingRow("CoastUpToLeads", "toggle", tr_noop("Coast Up To Leads"), - subtitle=tr_noop("Keep optional far-lead comfort logic on. Recommended unless your car shows lead-follow stutter or springy gas/brake behavior."), - get_state=lambda: self._params.get_bool("CoastUpToLeads"), - set_state=lambda s: self._params.put_bool("CoastUpToLeads", s), + SettingRow("PrioritizeSmoothFollowing", "toggle", tr_noop("Prioritize Smooth Following"), + subtitle=tr_noop("Disables the newer far-lead follow logic on cars that show lead-follow stutter. Tradeoff: it may react later in some edge-case lead stops."), + get_state=lambda: self._params.get_bool("PrioritizeSmoothFollowing"), + set_state=lambda s: self._params.put_bool("PrioritizeSmoothFollowing", s), visible=self._longitudinal_enabled), ], column_pair="human_driving"), SettingSection(tr_noop("Lane Changes"), [ diff --git a/starpilot/common/safe_mode.py b/starpilot/common/safe_mode.py index e0a22c528..7f4057f0f 100644 --- a/starpilot/common/safe_mode.py +++ b/starpilot/common/safe_mode.py @@ -71,7 +71,7 @@ SAFE_MODE_MANAGED_KEYS = ( "AccelerationProfile", "DecelerationProfile", "HumanAcceleration", - "CoastUpToLeads", + "PrioritizeSmoothFollowing", "HumanLaneChanges", "LeadDetectionThreshold", "RecoveryPower", diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index 392679525..56c66b77d 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -1083,9 +1083,13 @@ class StarPilotVariables: else: toggle.custom_accel_profile_values = [custom_accel_defaults[key] for key in CUSTOM_ACCEL_PROFILE_PARAM_KEYS] toggle.human_acceleration = self.get_value("HumanAcceleration", condition=longitudinal_tuning) - toggle.coast_up_to_leads = self.get_value("CoastUpToLeads", condition=longitudinal_tuning) - if longitudinal_tuning and self.params.get("CoastUpToLeads") is None: - toggle.coast_up_to_leads = True + toggle.prioritize_smooth_following = self.get_value("PrioritizeSmoothFollowing", condition=longitudinal_tuning) + if longitudinal_tuning and self.params_raw.get("PrioritizeSmoothFollowing") is None: + legacy_coast_value = self.params_raw.get("CoastUpToLeads") + if legacy_coast_value is not None: + toggle.prioritize_smooth_following = not self.params_raw.get_bool("CoastUpToLeads") + else: + toggle.prioritize_smooth_following = False toggle.human_lane_changes = has_radar and self.get_value("HumanLaneChanges", condition=longitudinal_tuning) toggle.nav_longitudinal_allowed = toggle.openpilot_longitudinal and self.get_value("NavLongitudinalAllowed", condition=longitudinal_tuning) # Keep lead detection sensitivity normalized even when longitudinal tuning is disabled. diff --git a/starpilot/controls/lib/starpilot_following.py b/starpilot/controls/lib/starpilot_following.py index bdc47c9fc..5e229f2cd 100644 --- a/starpilot/controls/lib/starpilot_following.py +++ b/starpilot/controls/lib/starpilot_following.py @@ -84,8 +84,9 @@ class StarPilotFollowing: if self.starpilot_planner.starpilot_weather.weather_id != 0: self.t_follow = min(self.t_follow + self.starpilot_planner.starpilot_weather.increase_following_distance, MAX_T_FOLLOW) + prioritize_smooth_following = bool(getattr(starpilot_toggles, "prioritize_smooth_following", False)) self.disable_throttle = False - if getattr(starpilot_toggles, "coast_up_to_leads", True) and self.starpilot_planner.tracking_lead and self.starpilot_planner.lead_one.status: + if not prioritize_smooth_following and self.starpilot_planner.tracking_lead and self.starpilot_planner.lead_one.status: lead_distance = self.starpilot_planner.lead_one.dRel v_lead = self.starpilot_planner.lead_one.vLead closing_speed = max(0.0, v_ego - v_lead) diff --git a/starpilot/controls/starpilot_planner.py b/starpilot/controls/starpilot_planner.py index ffdeaeed4..60582dc6b 100644 --- a/starpilot/controls/starpilot_planner.py +++ b/starpilot/controls/starpilot_planner.py @@ -192,7 +192,11 @@ class StarPilotPlanner: self.road_curvature_detected = (1 / abs(self.road_curvature))**0.5 < v_ego > CRUISING_SPEED and not (sm["carState"].leftBlinker or sm["carState"].rightBlinker) if not sm["carState"].standstill: - self.tracking_lead = self.update_lead_status(v_ego, starpilot_toggles.stop_distance) + self.tracking_lead = self.update_lead_status( + v_ego, + starpilot_toggles.stop_distance, + prioritize_smooth_following=bool(getattr(starpilot_toggles, "prioritize_smooth_following", False)), + ) self.starpilot_following.update(controls_enabled, v_ego, sm, starpilot_toggles) @@ -219,7 +223,7 @@ class StarPilotPlanner: else: self.starpilot_weather.weather_id = 0 - def update_lead_status(self, v_ego, stop_distance=STOP_DISTANCE): + def update_lead_status(self, v_ego, stop_distance=STOP_DISTANCE, prioritize_smooth_following=False): following_lead = should_track_lead( self.lead_one.status, self.lead_one.dRel, @@ -241,12 +245,12 @@ class StarPilotPlanner: lead_brake=max(0.0, -float(getattr(self.lead_one, "aLeadK", 0.0))), lead_prob=float(getattr(self.lead_one, "modelProb", 0.0)), ) - if matched_follow_window and (following_lead or self.tracking_lead or self.tracking_lead_filter.x >= THRESHOLD * 0.6): + if not prioritize_smooth_following and matched_follow_window and (following_lead or self.tracking_lead or self.tracking_lead_filter.x >= THRESHOLD * 0.6): self.radarless_follow_hold_until = now_t + RADARLESS_TRACK_HOLD_TIME - elif lead_radar or not self.lead_one.status: + elif prioritize_smooth_following or lead_radar or not self.lead_one.status: self.radarless_follow_hold_until = 0.0 - if not following_lead and matched_follow_window and now_t < self.radarless_follow_hold_until: + if not prioritize_smooth_following and not following_lead and matched_follow_window and now_t < self.radarless_follow_hold_until: following_lead = True self.tracking_lead_filter.update(following_lead) 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 a808107b6..ae4c8a6e5 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 @@ -1001,9 +1001,9 @@ "parent_key": "LongitudinalTune" }, { - "key": "CoastUpToLeads", - "label": "Coast Up To Leads", - "description": "Keep the optional far-lead comfort logic enabled, including gentle coasting and matched-follow smoothing for distant leads. Recommended to leave on unless your vehicle shows lead-follow stutter or springy gas/brake behavior.", + "key": "PrioritizeSmoothFollowing", + "label": "Prioritize Smooth Following", + "description": "Disable the newer far-lead follow logic on cars that show lead-follow stutter or springy gas/brake behavior. Tradeoff: the car may react later and brake less proactively in some edge-case lead approaches.", "data_type": "bool", "ui_type": "toggle", "parent_key": "LongitudinalTune" diff --git a/starpilot/ui/qt/offroad/longitudinal_settings.cc b/starpilot/ui/qt/offroad/longitudinal_settings.cc index f1577d7f6..44f9bd679 100644 --- a/starpilot/ui/qt/offroad/longitudinal_settings.cc +++ b/starpilot/ui/qt/offroad/longitudinal_settings.cc @@ -158,7 +158,7 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * {"AccelerationProfile", tr("Acceleration Profile"), tr("How quickly openpilot speeds up. \"Eco\" is gentle and efficient, \"Sport\" is firmer and more responsive, and \"Sport+\" accelerates at the maximum rate allowed."), ""}, {"DecelerationProfile", tr("Deceleration Profile"), tr("How firmly openpilot slows down. \"Eco\" favors coasting, \"Sport\" applies stronger braking."), ""}, {"HumanAcceleration", tr("Human-Like Acceleration"), tr("Acceleration that mimics human behavior by easing the throttle at low speeds and adding extra power when taking off from a stop."), ""}, - {"CoastUpToLeads", tr("Coast Up To Leads"), tr("Keep the optional far-lead comfort logic enabled, including gentle coasting and matched-follow smoothing for distant leads. Recommended to leave on unless your vehicle shows lead-follow stutter or springy gas/brake behavior."), ""}, + {"PrioritizeSmoothFollowing", tr("Prioritize Smooth Following"), tr("Disable the newer far-lead follow logic that can cause springy gas/brake behavior on some cars. Enable this if your vehicle shows lead-follow stutter or oscillation.

Tradeoff: the car may react later and brake less proactively in some edge-case lead approaches."), ""}, {"HumanLaneChanges", tr("Human-Like Lane Changes"), tr("Lane-change behavior that mimics human drivers by anticipating and tracking adjacent vehicles during lane changes."), ""}, {"LeadDetectionThreshold", tr("Lead Detection Sensitivity"), tr("How sensitive openpilot is to detecting vehicles. Higher sensitivity allows quicker detection at longer distances but may react to non-vehicle objects; lower sensitivity is more conservative and reduces false detections."), ""}, {"TacoTune", tr("\"Taco Bell Run\" Turn Speed Hack"), tr("The turn-speed hack from comma's 2022 \"Taco Bell Run\". Designed to slow down for left and right turns."), ""}, diff --git a/starpilot/ui/qt/offroad/longitudinal_settings.h b/starpilot/ui/qt/offroad/longitudinal_settings.h index a65a9d9a6..fd2ed78da 100644 --- a/starpilot/ui/qt/offroad/longitudinal_settings.h +++ b/starpilot/ui/qt/offroad/longitudinal_settings.h @@ -34,7 +34,7 @@ private: QSet conditionalExperimentalKeys = {"PersistExperimentalState", "CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CESignalSpeed", "CEStopLights", "ShowCEMStatus"}; QSet curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"}; QSet customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"}; - QSet longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "CoastUpToLeads", "HumanLaneChanges", "LeadDetectionThreshold", "TacoTune", "NavLongitudinalAllowed"}; + QSet longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "PrioritizeSmoothFollowing", "HumanLaneChanges", "LeadDetectionThreshold", "TacoTune", "NavLongitudinalAllowed"}; QSet qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStops", "ForceStopDistanceOffset", "ForceStandstill", "RadarTakeoffs", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset", "WeatherPresets"}; QSet relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedFollowHigh", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"}; QSet speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"}; diff --git a/system/manager/manager.py b/system/manager/manager.py index 22704ec62..337191ddd 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -36,7 +36,7 @@ LEGACY_BOLT_FP_MIGRATION_FLAG = Path("/data") / "legacy_bolt_fp_migration_v1" STARPILOT_DEFAULTS_PARITY_MIGRATION_FLAG = Path("/data") / "starpilot_defaults_parity_v1" STARPILOT_HUMANLIKE_DISABLE_MIGRATION_FLAG = Path("/data") / "starpilot_humanlike_disable_v1" STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG = Path("/data") / "starpilot_cluster_offset_v1" -STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG = Path("/data") / "starpilot_coast_up_to_leads_v1" +STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG = Path("/data") / "starpilot_prioritize_smooth_following_v1" STARPILOT_PARAM_RENAME_MIGRATION_FLAG = Path("/data") / "starpilot_param_rename_v1" STARPILOT_PARAM_CANONICALIZATION_MIGRATION_FLAG = Path("/data") / "starpilot_param_canonicalization_v1" STARPILOT_PC_ROOT_MIGRATION_FLAG = Path("/data") / "starpilot_pc_root_v1" @@ -463,20 +463,33 @@ def migrate_cluster_offset_default(params: Params, params_cache: Params) -> None cloudlog.exception(f"Failed to write migration flag: {STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG}") -def migrate_coast_up_to_leads_default(params: Params, params_cache: Params) -> None: - if STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG.exists(): +def migrate_prioritize_smooth_following_default(params: Params, params_cache: Params) -> None: + if STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG.exists(): return - if not _has_persisted_param_file(params, "CoastUpToLeads") and not _has_persisted_param_file(params_cache, "CoastUpToLeads"): - params.put_bool("CoastUpToLeads", True) - params_cache.put_bool("CoastUpToLeads", True) - cloudlog.warning("Seeded CoastUpToLeads to default enabled") + if not (_has_persisted_param_file(params, "PrioritizeSmoothFollowing") or _has_persisted_param_file(params_cache, "PrioritizeSmoothFollowing")): + migrated_from_legacy = False + for params_obj in (params, params_cache): + if not _has_persisted_param_file(params_obj, "CoastUpToLeads"): + continue + + prioritize_smooth_following = not params_obj.get_bool("CoastUpToLeads") + params.put_bool("PrioritizeSmoothFollowing", prioritize_smooth_following) + params_cache.put_bool("PrioritizeSmoothFollowing", prioritize_smooth_following) + cloudlog.warning(f"Migrated CoastUpToLeads to PrioritizeSmoothFollowing={int(prioritize_smooth_following)}") + migrated_from_legacy = True + break + + if not migrated_from_legacy: + params.put_bool("PrioritizeSmoothFollowing", False) + params_cache.put_bool("PrioritizeSmoothFollowing", False) + cloudlog.warning("Seeded PrioritizeSmoothFollowing to default disabled") try: - STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG.parent.mkdir(parents=True, exist_ok=True) - STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG.write_text(f"{datetime.datetime.now(datetime.UTC).isoformat()}\n") + STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG.parent.mkdir(parents=True, exist_ok=True) + STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG.write_text(f"{datetime.datetime.now(datetime.UTC).isoformat()}\n") except Exception: - cloudlog.exception(f"Failed to write migration flag: {STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG}") + cloudlog.exception(f"Failed to write migration flag: {STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG}") def _read_raw_param_bytes(params: Params, key: str | bytes): @@ -648,7 +661,7 @@ def manager_init() -> None: migrate_starpilot_default_parity(params, params_cache) migrate_disable_humanlike_defaults(params, params_cache) migrate_cluster_offset_default(params, params_cache) - migrate_coast_up_to_leads_default(params, params_cache) + migrate_prioritize_smooth_following_default(params, params_cache) # set unset params to their default value for k in params.all_keys(): diff --git a/system/manager/test/test_manager.py b/system/manager/test/test_manager.py index 3e49b77b5..6eb624462 100644 --- a/system/manager/test/test_manager.py +++ b/system/manager/test/test_manager.py @@ -224,28 +224,42 @@ class TestManager: assert params.get("ClusterOffset") == "1.02" assert params_cache.get("ClusterOffset") is None - def test_migrate_coast_up_to_leads_default_seeds_enabled(self, tmp_path, monkeypatch): - monkeypatch.setattr(manager, "STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG", tmp_path / "starpilot_coast_up_to_leads_v1") + def test_migrate_prioritize_smooth_following_default_seeds_disabled(self, tmp_path, monkeypatch): + monkeypatch.setattr(manager, "STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG", tmp_path / "starpilot_prioritize_smooth_following_v1") params = FileBackedFakeParams(tmp_path / "params", {}) params_cache = FileBackedFakeParams(tmp_path / "cache", {}) - manager.migrate_coast_up_to_leads_default(params, params_cache) + manager.migrate_prioritize_smooth_following_default(params, params_cache) - assert params.get_bool("CoastUpToLeads") - assert params_cache.get_bool("CoastUpToLeads") + assert not params.get_bool("PrioritizeSmoothFollowing") + assert not params_cache.get_bool("PrioritizeSmoothFollowing") - def test_migrate_coast_up_to_leads_default_preserves_existing_values(self, tmp_path, monkeypatch): - monkeypatch.setattr(manager, "STARPILOT_COAST_UP_TO_LEADS_MIGRATION_FLAG", tmp_path / "starpilot_coast_up_to_leads_v1") + def test_migrate_prioritize_smooth_following_default_inverts_legacy_coast_toggle(self, tmp_path, monkeypatch): + monkeypatch.setattr(manager, "STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG", tmp_path / "starpilot_prioritize_smooth_following_v1") params = FileBackedFakeParams(tmp_path / "params", { "CoastUpToLeads": False, }) params_cache = FileBackedFakeParams(tmp_path / "cache", {}) - manager.migrate_coast_up_to_leads_default(params, params_cache) + manager.migrate_prioritize_smooth_following_default(params, params_cache) - assert not params.get_bool("CoastUpToLeads") + assert params.get_bool("PrioritizeSmoothFollowing") + assert params_cache.get_bool("PrioritizeSmoothFollowing") + + def test_migrate_prioritize_smooth_following_default_preserves_existing_values(self, tmp_path, monkeypatch): + monkeypatch.setattr(manager, "STARPILOT_PRIORITIZE_SMOOTH_FOLLOWING_MIGRATION_FLAG", tmp_path / "starpilot_prioritize_smooth_following_v1") + + params = FileBackedFakeParams(tmp_path / "params", { + "PrioritizeSmoothFollowing": True, + "CoastUpToLeads": True, + }) + params_cache = FileBackedFakeParams(tmp_path / "cache", {}) + + manager.migrate_prioritize_smooth_following_default(params, params_cache) + + assert params.get_bool("PrioritizeSmoothFollowing") @pytest.mark.skip("this test is flaky the way it's currently written, should be moved to test_onroad") def test_clean_exit(self, subtests): diff --git a/tools/StarPilot/feasibleparams.txt b/tools/StarPilot/feasibleparams.txt index cdbef6660..968ada822 100644 --- a/tools/StarPilot/feasibleparams.txt +++ b/tools/StarPilot/feasibleparams.txt @@ -57,11 +57,11 @@ CalibrationParams CalibrationProgress CameraView CancelButtonControl -CoastUpToLeads ColorScheme CommunityFavorites ConditionalChill ConditionalExperimental +PrioritizeSmoothFollowing CurveSpeedController CustomAlerts CustomCruise