From 819e6c497cb775f94973eb0e44959084f8a76999 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:09:51 -0500 Subject: [PATCH] Volt Long Tune --- opendbc_repo/opendbc/car/gm/interface.py | 14 +++++------ opendbc_repo/opendbc/car/gm/tests/test_gm.py | 5 +--- selfdrive/controls/lib/longcontrol.py | 11 ++++----- selfdrive/controls/tests/test_longcontrol.py | 9 ++------ starpilot/common/testing_grounds.py | 23 +++++++++++++++---- .../common/tests/test_testing_grounds.py | 10 +++++++- starpilot/system/the_pond/the_pond.py | 8 ++++--- 7 files changed, 46 insertions(+), 34 deletions(-) diff --git a/opendbc_repo/opendbc/car/gm/interface.py b/opendbc_repo/opendbc/car/gm/interface.py index e6e2fc2ea..6819db0fb 100755 --- a/opendbc_repo/opendbc/car/gm/interface.py +++ b/opendbc_repo/opendbc/car/gm/interface.py @@ -24,7 +24,6 @@ from opendbc.car.gm.values import ( from opendbc.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, LateralAccelFromTorqueCallbackType from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.common.params import Params, UnknownKeyName -from openpilot.starpilot.common.testing_grounds import testing_ground TransmissionType = structs.CarParams.TransmissionType NetworkLocation = structs.CarParams.NetworkLocation @@ -77,7 +76,7 @@ VOLT_LIKE_CARS = { CAR.CHEVROLET_MALIBU_HYBRID_CC, } -VOLT_LONG_TEST_TUNE_CARS = { +VOLT_LONG_TUNE_CARS = { CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, @@ -566,15 +565,14 @@ class CarInterface(CarInterfaceBase): ret.pcmCruise = False ret.openpilotLongitudinalControl = not disable_openpilot_long - volt_test_tune_active = ( - testing_ground.use_2 and + volt_long_tune_active = ( ret.openpilotLongitudinalControl and - candidate in VOLT_LONG_TEST_TUNE_CARS + candidate in VOLT_LONG_TUNE_CARS ) - if volt_test_tune_active: + if volt_long_tune_active: # Volt long can still fall back to an all-I tune on both the interceptor and - # ASCM-int paths. The test-ground tune adds a modest P term, trims - # mid/high-speed I memory, and uses a dedicated starting state so + # ASCM-int paths. Use the promoted tune by default: add a modest P term, + # trim mid/high-speed I memory, and keep a dedicated starting state so # stop-and-go launches do not wind up the PID. ret.longitudinalTuning.kpBP = [0.0, 4.0, 12.0, 35.0] ret.longitudinalTuning.kpV = [0.10, 0.072, 0.050, 0.040] diff --git a/opendbc_repo/opendbc/car/gm/tests/test_gm.py b/opendbc_repo/opendbc/car/gm/tests/test_gm.py index 9ad923a75..ae4559c60 100644 --- a/opendbc_repo/opendbc/car/gm/tests/test_gm.py +++ b/opendbc_repo/opendbc/car/gm/tests/test_gm.py @@ -4,7 +4,6 @@ from parameterized import parameterized from opendbc.car.car_helpers import interfaces from opendbc.car.gm.carcontroller import should_spoof_dash_speed -import opendbc.car.gm.interface as gm_interface from opendbc.car.common.conversions import Conversions as CV from opendbc.car.gm.fingerprints import FINGERPRINTS from opendbc.car.gm.values import CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, GM_RX_OFFSET @@ -61,15 +60,13 @@ class TestGMInterface: ("interceptor", True), ("ascm_int", False), ]) - def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present, monkeypatch): + def test_volt_default_long_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present): CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM] fingerprint = _empty_fingerprint() if pedal_present: fingerprint[0][0x201] = 8 # pedal detected fingerprint[0][0x2FF] = 8 # SASCM detected - monkeypatch.setattr(gm_interface.testing_ground, "use_2", True, raising=False) - car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False, starpilot_toggles=_test_starpilot_toggles()) diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 454f6779d..574d5a13e 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -6,7 +6,6 @@ from openpilot.common.pid import PIDController from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.common.filter_simple import FirstOrderFilter from opendbc.car.gm.values import CarControllerParams, GMFlags -from openpilot.starpilot.common.testing_grounds import testing_ground CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] clip = np.clip @@ -155,9 +154,9 @@ class LongControl: self.integrator_hold_frames = 0 def _get_pedal_long_freeze(self, a_target, error, v_ego, accel_limits): - volt_test_tune_handoff = self.is_volt and testing_ground.use_2 + volt_long_handoff = self.is_volt - if not self.is_gm_pedal_long and not volt_test_tune_handoff: + if not self.is_gm_pedal_long and not volt_long_handoff: self.last_a_target = a_target self.integrator_hold_frames = 0 return False @@ -184,8 +183,8 @@ class LongControl: return self.integrator_hold_frames > 0 or sat_pushing_lower or sat_pushing_upper - def _shape_volt_test_tune_integrator(self, error, v_ego): - if not (self.is_volt and testing_ground.use_2): + def _shape_volt_integrator(self, error, v_ego): + if not self.is_volt: return # Bleed stale I quickly when the target reverses against stored integrator. @@ -224,7 +223,7 @@ class LongControl: else: # LongCtrlState.pid error = a_target - CS.aEgo self.update_mpc_mode(self.experimental_mode) - self._shape_volt_test_tune_integrator(error, CS.vEgo) + self._shape_volt_integrator(error, CS.vEgo) feedforward = a_target * self.feedforward_gain freeze_integrator = self._get_pedal_long_freeze(a_target, error, CS.vEgo, accel_limits) raw_output_accel = self.pid.update(error, speed=CS.vEgo, feedforward=feedforward, diff --git a/selfdrive/controls/tests/test_longcontrol.py b/selfdrive/controls/tests/test_longcontrol.py index 11633d7fb..adc5aeab0 100644 --- a/selfdrive/controls/tests/test_longcontrol.py +++ b/selfdrive/controls/tests/test_longcontrol.py @@ -2,7 +2,6 @@ from types import SimpleNamespace from cereal import car -import openpilot.selfdrive.controls.lib.longcontrol as longcontrol from openpilot.selfdrive.controls.lib.longcontrol import LongControl, LongCtrlState, long_control_state_trans @@ -125,7 +124,7 @@ def test_starting_accel_obeys_a_target_cap_when_custom_profile_enabled(): assert output_accel == 0.1 -def test_volt_testing_ground_handoff_freezes_integrator(monkeypatch): +def test_volt_pedal_handoff_freezes_integrator(): CP = car.CarParams.new_message() CP.brand = "gm" CP.enableGasInterceptorDEPRECATED = True @@ -135,8 +134,6 @@ def test_volt_testing_ground_handoff_freezes_integrator(monkeypatch): CP.longitudinalTuning.kiBP = [0.0] CP.longitudinalTuning.kiV = [0.03] - monkeypatch.setattr(longcontrol.testing_ground, "use_2", True, raising=False) - lc = LongControl(CP) freeze = lc._get_pedal_long_freeze(a_target=0.7, error=0.7, v_ego=8.0, accel_limits=(-3.0, 2.0)) @@ -144,7 +141,7 @@ def test_volt_testing_ground_handoff_freezes_integrator(monkeypatch): assert lc.integrator_hold_frames > 0 -def test_non_interceptor_volt_testing_ground_handoff_freezes_integrator(monkeypatch): +def test_non_interceptor_volt_handoff_freezes_integrator(): CP = car.CarParams.new_message() CP.brand = "gm" CP.enableGasInterceptorDEPRECATED = False @@ -154,8 +151,6 @@ def test_non_interceptor_volt_testing_ground_handoff_freezes_integrator(monkeypa CP.longitudinalTuning.kiBP = [0.0] CP.longitudinalTuning.kiV = [0.03] - monkeypatch.setattr(longcontrol.testing_ground, "use_2", True, raising=False) - lc = LongControl(CP) freeze = lc._get_pedal_long_freeze(a_target=0.7, error=0.7, v_ego=8.0, accel_limits=(-3.0, 2.0)) diff --git a/starpilot/common/testing_grounds.py b/starpilot/common/testing_grounds.py index ddde0e603..e92062feb 100644 --- a/starpilot/common/testing_grounds.py +++ b/starpilot/common/testing_grounds.py @@ -40,10 +40,9 @@ TESTING_GROUNDS_SLOT_DEFINITIONS = ( }, { "id": TESTING_GROUND_2, - "name": "Volt Long Tune", - "description": "Volt longitudinal tuning sandbox.", - "aLabel": "A - Installed tune", - "bLabel": "B - Firestar Tune", + "name": "Unused", + "description": "", + "aLabel": "A", }, { "id": TESTING_GROUND_3, @@ -149,6 +148,18 @@ def _normalize_selection(slot_id, variant): return normalized_slot_id, _normalize_variant(variant, normalized_slot_id) +def migrate_testing_ground_selection(slot_id, variant, default_slot_id=None): + normalized_slot_id = str(slot_id or "").strip() + normalized_variant = str(variant or "").strip().upper() + fallback_slot_id = str(default_slot_id or _DEFAULT_ACTIVE_SLOT).strip() or _DEFAULT_ACTIVE_SLOT + + # Retire the Volt B sandbox cleanly when the tune graduates to default behavior. + if normalized_slot_id == TESTING_GROUND_2 and normalized_variant == TESTING_GROUND_TEST_VARIANT: + return fallback_slot_id, DEFAULT_TESTING_GROUND_VARIANT, True + + return normalized_slot_id, normalized_variant, False + + def _write_testing_ground_selection(payload, slot_id, variant): normalized_payload = dict(payload) if isinstance(payload, dict) else {} normalized_payload["schemaVersion"] = TESTING_GROUNDS_SCHEMA_VERSION @@ -197,10 +208,12 @@ def get_testing_ground_selection(refresh_interval_s=0.5): raw_slot_id = str(payload.get("activeSlot") or "").strip() raw_variant = payload.get("activeVariant") - normalized_slot_id, normalized_variant = _normalize_selection(raw_slot_id, raw_variant) + migrated_slot_id, migrated_variant, selection_migrated = migrate_testing_ground_selection(raw_slot_id, raw_variant) + normalized_slot_id, normalized_variant = _normalize_selection(migrated_slot_id, migrated_variant) raw_variant_text = str(raw_variant or "").strip().upper() if ( payload.get("schemaVersion") != TESTING_GROUNDS_SCHEMA_VERSION or + selection_migrated or raw_slot_id != normalized_slot_id or raw_variant_text != normalized_variant ): diff --git a/starpilot/common/tests/test_testing_grounds.py b/starpilot/common/tests/test_testing_grounds.py index 5e09fcbd2..002b2e1d5 100644 --- a/starpilot/common/tests/test_testing_grounds.py +++ b/starpilot/common/tests/test_testing_grounds.py @@ -5,7 +5,7 @@ import pytest from openpilot.starpilot.common import testing_grounds as tg -@pytest.mark.parametrize("hidden_slot_id", [tg.TESTING_GROUND_5, tg.TESTING_GROUND_6]) +@pytest.mark.parametrize("hidden_slot_id", [tg.TESTING_GROUND_2, tg.TESTING_GROUND_5, tg.TESTING_GROUND_6]) def test_hidden_testing_ground_selection_is_migrated(tmp_path, monkeypatch, hidden_slot_id): state_path = tmp_path / "slots.json" state_path.write_text(json.dumps({ @@ -28,3 +28,11 @@ def test_hidden_testing_ground_selection_is_migrated(tmp_path, monkeypatch, hidd payload = json.loads(state_path.read_text(encoding="utf-8")) assert payload["activeSlot"] == tg.TESTING_GROUND_1 assert payload["activeVariant"] == tg.DEFAULT_TESTING_GROUND_VARIANT + + +def test_retired_volt_b_selection_is_explicitly_migrated_to_default_a(): + slot_id, variant, migrated = tg.migrate_testing_ground_selection(tg.TESTING_GROUND_2, tg.TESTING_GROUND_TEST_VARIANT, tg.TESTING_GROUND_1) + + assert migrated + assert slot_id == tg.TESTING_GROUND_1 + assert variant == tg.DEFAULT_TESTING_GROUND_VARIANT diff --git a/starpilot/system/the_pond/the_pond.py b/starpilot/system/the_pond/the_pond.py index 5c36c6365..80183ac41 100644 --- a/starpilot/system/the_pond/the_pond.py +++ b/starpilot/system/the_pond/the_pond.py @@ -71,6 +71,7 @@ from openpilot.starpilot.common.testing_grounds import ( TESTING_GROUNDS_SCHEMA_VERSION as SHARED_TESTING_GROUNDS_SCHEMA_VERSION, TESTING_GROUNDS_SLOT_DEFINITIONS as SHARED_TESTING_GROUNDS_SLOT_DEFINITIONS, TESTING_GROUNDS_STATE_PATH as SHARED_TESTING_GROUNDS_STATE_PATH, + migrate_testing_ground_selection as migrate_shared_testing_ground_selection, ) from openpilot.starpilot.system.the_pond import utilities @@ -2936,6 +2937,8 @@ def _load_testing_grounds_state_unlocked(): } default_slot_id = _get_first_selectable_testing_ground_slot_id(state["slots"]) active_slot = str(raw_state.get("activeSlot") or "").strip() + raw_active_variant = str(raw_state.get("activeVariant") or "").strip().upper() + active_slot, migrated_variant, selection_migrated = migrate_shared_testing_ground_selection(active_slot, raw_active_variant, default_slot_id) active_slot_migrated = active_slot not in fallback_slot_ids or active_slot not in selectable_slot_ids if active_slot_migrated: active_slot = default_slot_id @@ -2943,11 +2946,10 @@ def _load_testing_grounds_state_unlocked(): state["activeSlot"] = active_slot active_slot_data = _find_testing_ground_slot(state, active_slot) - raw_active_variant = str(raw_state.get("activeVariant") or "").strip().upper() - if active_slot_migrated: + if active_slot_migrated or selection_migrated: active_variant = _TESTING_GROUNDS_DEFAULT_VARIANT else: - active_variant = _normalize_testing_ground_variant(active_slot, raw_active_variant, active_slot_data) + active_variant = _normalize_testing_ground_variant(active_slot, migrated_variant, active_slot_data) if raw_active_variant != active_variant: needs_write = True state["activeVariant"] = active_variant