From f831010290b44376ec0091ff6f84ce45a21b6e87 Mon Sep 17 00:00:00 2001 From: whoisdomi Date: Sat, 7 Nov 2026 17:37:09 -0600 Subject: [PATCH] force stop edge case --- openpilot/starpilot/longitudinal/force_stop.py | 4 ++-- .../longitudinal/tests/test_force_stop.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/openpilot/starpilot/longitudinal/force_stop.py b/openpilot/starpilot/longitudinal/force_stop.py index cc2d7f27c5..5508139bf0 100644 --- a/openpilot/starpilot/longitudinal/force_stop.py +++ b/openpilot/starpilot/longitudinal/force_stop.py @@ -141,7 +141,7 @@ class ForceStop: permitted = (available and now >= self.override_until_ns and not frame.driving_in_curve and not turn and not frame.tracking_lead and not lead) model_path = frame.light and model_active and permitted and not curved - sign_path = frame.sign and permitted and speed < SIGN_MAX_SPEED_MPS and horizon < SIGN_MODEL_AGREE_M + sign_path = frame.sign and permitted and speed < SIGN_MAX_SPEED_MPS and (horizon < SIGN_MODEL_AGREE_M or self.forcing) if sign_path: self.sign_confirmed = True active = model_path or sign_path @@ -232,7 +232,7 @@ class ForceStop: self.distance_cap = max(0.0, self.distance_cap - speed * dt) self.distance = min(self.distance, self.distance_cap + 15 * min(self.distance_cap / 60, 1)) if frame.sign and available: - if horizon < SIGN_MODEL_AGREE_M: + if horizon < SIGN_MODEL_AGREE_M or self.sign_confirmed: self.distance = min(self.distance, SIGN_SEED_M) effective = self.distance + offset + tune.distance_bias_m decel = SIGN_APPROACH_DECEL if frame.sign else MODEL_APPROACH_DECEL diff --git a/openpilot/starpilot/longitudinal/tests/test_force_stop.py b/openpilot/starpilot/longitudinal/tests/test_force_stop.py index 664b310cfd..42c0f7a534 100644 --- a/openpilot/starpilot/longitudinal/tests/test_force_stop.py +++ b/openpilot/starpilot/longitudinal/tests/test_force_stop.py @@ -244,6 +244,23 @@ class ForceStopTests(unittest.TestCase): plan = self.owner.step(frame(50, speed_mps=8., horizon_m=20., sign=True), self.tune) self.assertLessEqual(plan.tracked_distance_m, previous.tracked_distance_m) + def test_sign_latches_and_seeds_over_long_path_once_light_committed(self): + for tick in range(15): + plan = self.owner.step(frame(tick, horizon_m=60.), self.tune) + self.assertTrue(plan.forcing) + self.assertGreater(plan.tracked_distance_m, 50.) + plan = self.owner.step(frame(15, horizon_m=60., sign=True), self.tune) + self.assertTrue(self.owner.sign_confirmed) + self.assertLessEqual(plan.tracked_distance_m, 27.) + plan = self.owner.step(frame(16, horizon_m=60., light=False), self.tune) + self.assertLess(plan.tracked_distance_m, 27.) + + def test_long_path_sign_alone_does_not_commit(self): + for tick in range(20): + plan = self.owner.step(frame(tick, horizon_m=60., light=False, sign=True), self.tune) + self.assertFalse(plan.forcing) + self.assertFalse(self.owner.sign_confirmed) + def test_positive_offset_keeps_stop_target_until_native_stop(self): for tick in range(40): plan = self.owner.step(frame(tick, horizon_m=12.), self.tune, 8)