diff --git a/openpilot/selfdrive/controls/lib/longitudinal_planner.py b/openpilot/selfdrive/controls/lib/longitudinal_planner.py index 30ad35f801..44868dab45 100755 --- a/openpilot/selfdrive/controls/lib/longitudinal_planner.py +++ b/openpilot/selfdrive/controls/lib/longitudinal_planner.py @@ -37,7 +37,10 @@ def get_coast_accel(pitch): def get_cruise_accel(e2e, v_cruise, v_ego, a_cruise_prev, angle_steers, CP, dt, accel_coast, allow_throttle, max_accel_override=None, min_accel_override=None): - max_accel = ACCEL_MAX if e2e else (get_max_accel(v_ego) if max_accel_override is None else max_accel_override) + if max_accel_override is not None: + max_accel = max_accel_override + else: + max_accel = ACCEL_MAX if e2e else get_max_accel(v_ego) min_accel = A_CRUISE_MIN if e2e or min_accel_override is None else min_accel_override if not e2e: @@ -146,7 +149,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP): is_e2e = self.is_e2e(sm) - max_accel_override = self.get_max_accel_override(v_ego, is_e2e) + max_accel_override = self.get_max_accel_override(v_ego) min_accel_override = self.get_min_accel_override(v_ego, is_e2e, force_decel) self.accel_controller_active = max_accel_override is not None or min_accel_override is not None self.a_cruise = get_cruise_accel(is_e2e, v_cruise, v_ego, diff --git a/openpilot/sunnypilot/selfdrive/controls/lib/accel_controller/tests/test_accel_controller.py b/openpilot/sunnypilot/selfdrive/controls/lib/accel_controller/tests/test_accel_controller.py index 9f676b0c8e..298fc12b81 100644 --- a/openpilot/sunnypilot/selfdrive/controls/lib/accel_controller/tests/test_accel_controller.py +++ b/openpilot/sunnypilot/selfdrive/controls/lib/accel_controller/tests/test_accel_controller.py @@ -13,6 +13,11 @@ still force full ACCEL_MIN braking regardless. Lead-relevance checks, an SLC-sha floor beyond that, and controller-internal Params writes are NOT ported from the reference designs this was built from - do not backfill them here without revisiting scope. + +Ceiling vs floor apply on different policies: ACC (non-e2e) uses the controller's +ceiling AND floor; blended (e2e) uses the controller's ceiling but always the stock +floor (A_CRUISE_MIN), never the controller's -- final, don't try to make the floor +work under blended again. """ import unittest @@ -141,6 +146,10 @@ class TestOffEqualsStock(OpenpilotTestCase): planner = _bare_planner() self.assertIsNone(planner.get_min_accel_override(v_ego=5.0, e2e=False, force_decel=False)) + def test_disabled_max_accel_override_is_none(self): + planner = _bare_planner() + self.assertIsNone(planner.get_max_accel_override(v_ego=5.0)) + def test_force_decel_excludes_min_accel_override_even_when_enabled(self): self.params.put_bool("AccelPersonalityEnabled", True, block=True) planner = _bare_planner() @@ -158,6 +167,33 @@ class TestOffEqualsStock(OpenpilotTestCase): self.assertIsNotNone(override) self.assertLess(override, 0.0) + def test_enabled_max_accel_override_applies_in_acc_and_blended(self): + # Policy: max ceiling comes from AccelController in both ACC and blended (e2e) modes -- + # only the min floor is blended-vs-stock. get_max_accel_override no longer takes an e2e + # arg because of this; the caller applies it unconditionally. + self.params.put_bool("AccelPersonalityEnabled", True, block=True) + planner = _bare_planner() + override = planner.get_max_accel_override(v_ego=5.0) + self.assertIsNotNone(override) + self.assertGreater(override, 0.0) + + def test_blended_min_accel_uses_stock_not_controller(self): + # e2e/blended braking floor is deliberately left at stock's A_CRUISE_MIN, never the + # controller's floor -- this is the "acc policy = controller min+max, blended policy = + # controller max + stock min" split, final per product decision. + from openpilot.selfdrive.controls.lib.longitudinal_planner import get_cruise_accel, A_CRUISE_MIN + args = dict(v_cruise=-100.0, v_ego=20.0, a_cruise_prev=0.0, angle_steers=0.0, CP=_fake_cp(), + dt=DT_MDL, accel_coast=1.0, allow_throttle=True) + target = get_cruise_accel(True, **args, min_accel_override=-0.3) + self.assertAlmostEqual(target, A_CRUISE_MIN, places=6) + + def test_blended_max_accel_uses_controller_override(self): + from openpilot.selfdrive.controls.lib.longitudinal_planner import get_cruise_accel + args = dict(v_cruise=100.0, v_ego=20.0, a_cruise_prev=0.0, angle_steers=0.0, CP=_fake_cp(), + dt=DT_MDL, accel_coast=1.0, allow_throttle=True) + target = get_cruise_accel(True, **args, max_accel_override=0.4) + self.assertAlmostEqual(target, 0.4, places=6) + class TestLeadGapWiden(OpenpilotTestCase): def setUp(self): diff --git a/openpilot/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py b/openpilot/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py index cccb8c324a..85a9fabd02 100644 --- a/openpilot/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py +++ b/openpilot/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py @@ -44,8 +44,8 @@ class LongitudinalPlannerSP: return experimental_mode and self.dec.mode() == "blended" - def get_max_accel_override(self, v_ego: float, e2e: bool) -> float | None: - if e2e or not self.accel_controller.is_enabled(): + def get_max_accel_override(self, v_ego: float) -> float | None: + if not self.accel_controller.is_enabled(): return None return self.accel_controller.get_max_accel(v_ego)