From 83e6132b6db447e96cd55288e426c2dbe649d1d5 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sat, 31 Oct 2026 17:56:27 -0500 Subject: [PATCH] Prefer another FLM coordinate after rejected oscillation --- openpilot/starpilot/flm/live_tracking.py | 4 +- .../flm/tests/test_rejected_batch_recovery.py | 51 +++++++++++++++++++ prebuilt.json | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 openpilot/starpilot/flm/tests/test_rejected_batch_recovery.py diff --git a/openpilot/starpilot/flm/live_tracking.py b/openpilot/starpilot/flm/live_tracking.py index 20c8f8c67c..9d5b989018 100644 --- a/openpilot/starpilot/flm/live_tracking.py +++ b/openpilot/starpilot/flm/live_tracking.py @@ -1248,7 +1248,9 @@ class LiveTrackingOptimizer: if ( state['phase'] == 'trial' and self.before - and reason in ('independent_future_tracking_not_improved', 'matched_tracking_not_consistently_improved', 'trial_tracking_or_oscillation_worse') + and reason in ( + 'independent_future_tracking_not_improved', 'matched_tracking_not_consistently_improved', + 'trial_tracking_or_oscillation_worse', 'trial_oscillation_batch_worse') ): for name in state['names']: direction = 1 if state['target'][name] > state['baseline'][name] else -1 diff --git a/openpilot/starpilot/flm/tests/test_rejected_batch_recovery.py b/openpilot/starpilot/flm/tests/test_rejected_batch_recovery.py new file mode 100644 index 0000000000..c730358469 --- /dev/null +++ b/openpilot/starpilot/flm/tests/test_rejected_batch_recovery.py @@ -0,0 +1,51 @@ +"""Numerical recovery regression; native/road qualification is separate.""" +from dataclasses import replace +import pytest + +from openpilot.starpilot.flm.live_tracking import RETRY_NS +from openpilot.starpilot.flm.tests.test_live_tracking import optimizer, ready, episode, refresh, acknowledge + + +@pytest.mark.parametrize('rates', [(2.5, 0.1, 0.1), (1.4, 1.4, 1.4)]) +def test_scored_oscillation_rejection_prefers_another_qualified_coordinate(rates): + owner = optimizer() + _, now = ready(owner) + owner.before = tuple(replace(row, reversal_hz=rate) for row, rate in zip(owner.before, (0.46, 0.46, 2.32), strict=True)) + offer = owner.offer(now) + assert acknowledge(owner, offer) + for i, rate in enumerate(rates): + row = episode(owner, 40+i, 'ff_gain_left', profile=offer.target_sha256, + error=0.001, reversal=rate, start=now+1_000_000_000+i*1_000_000_000) + refresh(owner, row.end_ns) + owner.observe_episode(row, now_ns=row.end_ns) + if owner.offer_state['phase'] == 'rollback': + break + assert owner.offer_state['phase'] == 'rollback' + rollback = owner.offer(row.end_ns) + assert acknowledge(owner, rollback) + assert not any(owner.accepted.values()) + after = max(row.end_ns+1_000_000_000, owner.last_attempt+RETRY_NS) + for i in range(6): + row = episode(owner, 100+i, 'ff_gain_left', start=after+i*1_000_000_000) + preferred = row.sensitivity[0] + alternate = replace(preferred, name='turn_in_boost_left', error_feature_sum=0.04) + row = replace(row, sensitivity=(preferred, alternate)) + refresh(owner, row.end_ns) + owner.observe_episode(row, now_ns=row.end_ns) + if i == 2: + assert owner.discovery['name'] == 'turn_in_boost_left' + assert owner.offer_state['phase'] == 'trial' + assert owner.offer_state['names'] == ('turn_in_boost_left',) + alternative_offer = owner.offer(row.end_ns) + assert acknowledge(owner, alternative_offer) + after = row.end_ns+1_000_000_000 + for i in range(3): + row = episode(owner, 200+i, 'turn_in_boost_left', profile=alternative_offer.target_sha256, + error=0.001, start=after+i*1_000_000_000) + refresh(owner, row.end_ns) + owner.observe_episode(row, now_ns=row.end_ns) + confirmed = owner.offer(row.end_ns) + assert confirmed.phase == 'confirmed' + assert acknowledge(owner, confirmed) + assert owner.accepted['turn_in_boost_left'] > 0 + assert owner.accepted['ff_gain_left'] == 0 diff --git a/prebuilt.json b/prebuilt.json index 5a142bbef1..f5745201e2 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "aa4f3a0ac2f8cff257c3ad058e50911052f3bd91f6c022b9bfb3d0c1e4c2ac28", "schema_version": 1} +{"protected_inventory_sha256": "c9147a754b7378497d2782ffada0d207dc3dea8db35176613291b87ed99444c0", "schema_version": 1}