From 33f60615ea49e7737e5abbfa41ddf558bbe8b6cb Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:57:51 -0600 Subject: [PATCH] Damp 2025 Ioniq 6 highway steering oscillation --- openpilot/starpilot/lateral/ioniq6_policy.py | 4 + .../lateral/tests/test_ioniq6_policy.py | 79 +++++++++++++++++++ prebuilt.json | 2 +- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/openpilot/starpilot/lateral/ioniq6_policy.py b/openpilot/starpilot/lateral/ioniq6_policy.py index a4364f7c41..f4ecb65b77 100644 --- a/openpilot/starpilot/lateral/ioniq6_policy.py +++ b/openpilot/starpilot/lateral/ioniq6_policy.py @@ -29,6 +29,8 @@ from openpilot.starpilot.lateral.torque_shaping import ( ) HKG_CANFD_BASE_FRICTION_THRESHOLD = 0.39 +IONIQ_6_2025_HIGHWAY_FRICTION_THRESHOLD_INCREASE = 0.50 + IONIQ_6_CARS = (CAR.HYUNDAI_IONIQ_6,) @@ -881,6 +883,8 @@ class Ioniq6TorquePolicy: if self.surface is None and not self.is_2025 and not CS.steeringPressed and not self.prev_steering_pressed: friction_threshold += get_ioniq_6_2023_center_friction_increment( CS.vEgo, setpoint, measurement, desired_lateral_jerk, future_desired_lateral_accel) / max(center_taper, 1e-3) + if self.is_2025 and not CS.steeringPressed: + friction_threshold *= 1.0 + IONIQ_6_2025_HIGHWAY_FRICTION_THRESHOLD_INCREASE * float(np.interp(CS.vEgo, [20.0, 27.0], [0.0, 1.0])) friction_scale = get_ioniq_6_friction_scale(CS.vEgo, setpoint, desired_lateral_jerk) friction_scale = 1.0 + ((friction_scale - 1.0) * center_taper) friction_scale *= get_ioniq_6_friction_center_fade_scale(setpoint, CS.vEgo) diff --git a/openpilot/starpilot/lateral/tests/test_ioniq6_policy.py b/openpilot/starpilot/lateral/tests/test_ioniq6_policy.py index e764be237f..98677e3729 100644 --- a/openpilot/starpilot/lateral/tests/test_ioniq6_policy.py +++ b/openpilot/starpilot/lateral/tests/test_ioniq6_policy.py @@ -184,3 +184,82 @@ class Ioniq6PolicyTests(unittest.TestCase): for row, expected in zip(actual, parent[str(car)], strict=True): for value, reference in zip(row, expected, strict=True): self.assertAlmostEqual(value, reference, places=7) + + +class Ioniq6HighwayFrictionTests(unittest.TestCase): + def controller(self, *, firmware_2025=True, long=False): + cp = interfaces[HYUNDAI.HYUNDAI_IONIQ_6].get_non_essential_params(HYUNDAI.HYUNDAI_IONIQ_6) + cp.openpilotLongitudinalControl = long + cp.pcmCruise = not long + if firmware_2025: + fw = cp.init('carFw', 2) + fw[0].fwVersion = b'IONIQ6-230915' + fw[1].fwVersion = b'IONIQ6-240206' + controller = LatControlTorque(cp.as_reader(), interfaces[HYUNDAI.HYUNDAI_IONIQ_6](cp), DT_CTRL) + return controller, VehicleModel(cp) + + def trace(self, *, speed=29.0, firmware_2025=True, long=False, tuning=True, pressed=False, center=-3.0): + controller, vm = self.controller(firmware_2025=firmware_2025, long=long) + params = SimpleNamespace(angleOffsetDeg=0.0, roll=0.0) + cs = structs.CarState.new_message(vEgo=speed, steeringPressed=pressed, + gearShifter=structs.CarState.GearShifter.drive) + rows = [] + with mock.patch.object(tune, 'IONIQ_6_2025_HIGHWAY_FRICTION_THRESHOLD_INCREASE', .50 if tuning else 0.0): + for index in range(400): + cs.steeringAngleDeg = center + .5 * math.sin(index * DT_CTRL * 2 * math.pi) + output, _, state = controller.update(index != 0, cs, vm, params, True, .001, False, .322) + rows.append((output, state.f, state.desiredLateralAccel, state.desiredLateralJerk, state.p, state.i)) + self.assertLessEqual(abs(output), 1.0) + self.assertTrue(all(math.isfinite(x) for x in rows[-1])) + return controller, rows + + def test_highway_feedback_is_softer_without_changing_path_or_pid(self): + _, tuned = self.trace() + _, baseline = self.trace(tuning=False) + tuned_f = [row[1] for row in tuned[100:]] + original_f = [row[1] for row in baseline[100:]] + self.assertLess(max(tuned_f) - min(tuned_f), .9 * (max(original_f) - min(original_f))) + for row, old in zip(tuned, baseline, strict=True): + self.assertEqual(row[2:], old[2:]) + + def test_low_speed_and_older_firmware_are_unchanged(self): + for kwargs in ({'speed': 20.0}, {'speed': 5.0}, {'firmware_2025': False}): + with self.subTest(**kwargs): + self.assertEqual(self.trace(**kwargs)[1], self.trace(tuning=False, **kwargs)[1]) + + def test_stock_and_alpha_long_use_identical_steering(self): + self.assertEqual(self.trace(long=False)[1], self.trace(long=True)[1]) + + def test_driver_override_is_unchanged_and_inactive_resets(self): + controller, rows = self.trace(pressed=True) + self.assertEqual(rows, self.trace(pressed=True, tuning=False)[1]) + vm = VehicleModel(interfaces[HYUNDAI.HYUNDAI_IONIQ_6].get_non_essential_params(HYUNDAI.HYUNDAI_IONIQ_6)) + cs = structs.CarState.new_message(vEgo=29.0, steeringAngleDeg=5.0) + output, _, state = controller.update(False, cs, vm, SimpleNamespace(angleOffsetDeg=0.0, roll=0.0), + False, .001, False, .322) + self.assertEqual(output, 0.0) + self.assertFalse(state.active) + self.assertEqual(selected_policy(controller).measurement_rate_filter.x, 0.0) + + def test_constant_turn_strength_live_parameters_and_limits_are_preserved(self): + results = [] + for tuning in (False, True): + controller, vm = self.controller() + controller.update_torque_parameters(3.2, .01, .08) + self.assertAlmostEqual(controller.torque_params.latAccelFactor, 3.2 * 1.22, places=6) + self.assertAlmostEqual(controller.torque_params.friction, .08, places=6) + self.assertEqual(controller.pid.pos_limit, controller.lateral_accel_from_torque(1.0, controller.torque_params)) + cs = structs.CarState.new_message(vEgo=29.0, steeringAngleDeg=-3.0, + gearShifter=structs.CarState.GearShifter.drive) + curve = -vm.calc_curvature(math.radians(cs.steeringAngleDeg), cs.vEgo, 0.0) + with mock.patch.object(tune, 'IONIQ_6_2025_HIGHWAY_FRICTION_THRESHOLD_INCREASE', .50 if tuning else 0.0): + for index in range(400): + output, _, _ = controller.update(index != 0, cs, vm, SimpleNamespace(angleOffsetDeg=0.0, roll=0.0), + True, curve, False, .322) + results.append(output) + self.assertAlmostEqual(*results, places=12) + + def test_large_tracking_error_keeps_full_correction(self): + _, tuned = self.trace(center=20.0) + _, original = self.trace(center=20.0, tuning=False) + self.assertEqual(tuned[100:], original[100:]) diff --git a/prebuilt.json b/prebuilt.json index 99c5b97ea5..f2a58bcffe 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "c0f79b07a47da3d1d798256c11737fc629f41db0d933e4bad584cf9a5e56e840", "schema_version": 1} +{"protected_inventory_sha256": "eddae796df726f04bf15b69bba8b0c93b4e98f67f50e368627bb5bd385ec01a9", "schema_version": 1}