from parameterized import parameterized from types import SimpleNamespace from cereal import car, custom, log import openpilot.selfdrive.controls.lib.latcontrol_torque as latcontrol_torque from opendbc.car.car_helpers import interfaces from opendbc.car.honda.values import CAR as HONDA from opendbc.car.toyota.values import CAR as TOYOTA from opendbc.car.nissan.values import CAR as NISSAN from opendbc.car.gm.values import CAR as GM from opendbc.car.vehicle_model import VehicleModel from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_torque import ( LatControlTorque, get_bolt_2017_center_taper_scale, get_friction_threshold, get_bolt_2017_base_torque_scale, get_bolt_2017_steer_ratio_scale, get_bolt_2017_torque_scale, get_bolt_2022_2023_ff_scale, get_bolt_2022_2023_friction_scale, get_bolt_2022_2023_friction_threshold, get_bolt_2018_2021_dynamic_torque_scale, get_bolt_2018_2021_friction_scale, get_bolt_2018_2021_friction_threshold, get_bolt_2018_2021_torque_scale, get_silverado_trailer_ff_scale, get_silverado_trailer_friction_scale, get_silverado_trailer_friction_threshold, ) class TestLatControl: @staticmethod def _build_torque_controller(car_name): CarInterface = interfaces[car_name] CP = CarInterface.get_non_essential_params(car_name) CI = CarInterface(CP, custom.StarPilotCarParams.new_message()) controller = LatControlTorque(CP.as_reader(), CI, DT_CTRL) VM = VehicleModel(CP) CS = car.CarState.new_message() CS.vEgo = 22 CS.steeringPressed = False CS.steeringAngleDeg = 1.0 params = log.LiveParametersData.new_message() params.steerRatio = CP.steerRatio params.stiffnessFactor = 1.0 params.roll = 0.0 params.angleOffsetDeg = 0.0 starpilot_toggles = SimpleNamespace() return controller, VM, CS, params, starpilot_toggles def test_bolt_2017_testing_ground_scale_curve(self): assert get_bolt_2017_base_torque_scale(0.1) == 1.0 assert get_bolt_2017_base_torque_scale(-0.1) == 1.0 assert get_bolt_2017_base_torque_scale(0.5) > get_bolt_2017_base_torque_scale(-0.5) assert 1.0 < get_bolt_2017_base_torque_scale(1.2) < get_bolt_2017_base_torque_scale(0.5) assert get_bolt_2017_base_torque_scale(-2.5) < 1.0 assert 1.0 < get_bolt_2017_steer_ratio_scale(10.0 * 0.44704) < get_bolt_2017_steer_ratio_scale(20.0 * 0.44704) < get_bolt_2017_steer_ratio_scale(30.0 * 0.44704) assert get_bolt_2017_steer_ratio_scale(5.0 * 0.44704) < 1.01 assert get_bolt_2017_steer_ratio_scale(35.0 * 0.44704) > 1.04 assert get_bolt_2017_center_taper_scale(0.0, 30.0 * 0.44704) < get_bolt_2017_center_taper_scale(0.10, 30.0 * 0.44704) < get_bolt_2017_center_taper_scale(0.20, 30.0 * 0.44704) <= 1.0 assert get_bolt_2017_center_taper_scale(0.0, 30.0 * 0.44704) < get_bolt_2017_center_taper_scale(0.0, 10.0 * 0.44704) assert get_bolt_2017_torque_scale(0.0, 0.0, 30.0 * 0.44704) < 1.0 assert get_bolt_2017_torque_scale(0.6, 0.6, 8.0) > get_bolt_2017_torque_scale(0.6, 0.0, 8.0) > get_bolt_2017_torque_scale(0.6, -0.6, 8.0) assert get_bolt_2017_torque_scale(-0.6, -0.6, 8.0) > get_bolt_2017_torque_scale(-0.6, 0.0, 8.0) > get_bolt_2017_torque_scale(-0.6, 0.6, 8.0) assert get_bolt_2017_torque_scale(0.6, 0.6, 8.0) > get_bolt_2017_torque_scale(-0.6, -0.6, 8.0) def test_bolt_2018_2021_testing_ground_scale_curve(self): assert get_bolt_2018_2021_torque_scale(0.0) == 1.0 assert get_bolt_2018_2021_torque_scale(0.2) > get_bolt_2018_2021_torque_scale(0.08) assert get_bolt_2018_2021_torque_scale(0.4) > get_bolt_2018_2021_torque_scale(-0.4) assert get_bolt_2018_2021_torque_scale(2.0) < get_bolt_2018_2021_torque_scale(0.8) assert get_bolt_2018_2021_dynamic_torque_scale(0.08, 0.0, 25.0) < get_bolt_2018_2021_dynamic_torque_scale(0.08, 0.0, 8.0) assert get_bolt_2018_2021_dynamic_torque_scale(0.4, 0.8, 20.0) < get_bolt_2018_2021_dynamic_torque_scale(0.4, 0.1, 20.0) assert get_bolt_2018_2021_dynamic_torque_scale(0.6, -0.6, 8.0) < get_bolt_2018_2021_dynamic_torque_scale(0.6, 0.6, 8.0) assert get_bolt_2018_2021_dynamic_torque_scale(-0.6, 0.6, 8.0) < get_bolt_2018_2021_dynamic_torque_scale(-0.6, -0.6, 8.0) def test_bolt_2018_2021_friction_threshold_curve(self): base = get_friction_threshold(6.0) left_turn_in = get_bolt_2018_2021_friction_threshold(6.0, 0.7, 0.8) right_turn_in = get_bolt_2018_2021_friction_threshold(6.0, -0.7, -0.8) left_unwind = get_bolt_2018_2021_friction_threshold(6.0, 0.7, -0.8) right_unwind = get_bolt_2018_2021_friction_threshold(6.0, -0.7, 0.8) assert left_turn_in < right_turn_in < base < left_unwind < right_unwind assert get_bolt_2018_2021_friction_threshold(25.0, 0.7, 0.8) > left_turn_in def test_bolt_2018_2021_friction_scale_curve(self): base = get_bolt_2018_2021_friction_scale(25.0, 0.7, 0.8) center_base = get_bolt_2018_2021_friction_scale(25.0, 0.0, 0.0) left_turn_in = get_bolt_2018_2021_friction_scale(6.0, 0.7, 0.8) right_turn_in = get_bolt_2018_2021_friction_scale(6.0, -0.7, -0.8) left_unwind = get_bolt_2018_2021_friction_scale(6.0, 0.7, -0.8) right_unwind = get_bolt_2018_2021_friction_scale(6.0, -0.7, 0.8) assert center_base < 1.02 assert left_turn_in > right_turn_in > base assert base > left_unwind > right_unwind def test_bolt_2022_2023_ff_scale_curve(self): assert get_bolt_2022_2023_ff_scale(0.0, 0.0, 20.0) == 1.0 assert get_bolt_2022_2023_ff_scale(0.5, 0.0, 20.0) > get_bolt_2022_2023_ff_scale(-0.5, 0.0, 20.0) assert get_bolt_2022_2023_ff_scale(0.6, 0.7, 8.0) > get_bolt_2022_2023_ff_scale(-0.6, -0.7, 8.0) assert get_bolt_2022_2023_ff_scale(-0.6, -0.7, 8.0) > get_bolt_2022_2023_ff_scale(-0.6, 0.0, 8.0) assert get_bolt_2022_2023_ff_scale(0.6, -0.7, 8.0) < get_bolt_2022_2023_ff_scale(0.6, 0.0, 8.0) assert get_bolt_2022_2023_ff_scale(0.6, -0.7, 6.0) < get_bolt_2022_2023_ff_scale(0.6, -0.7, 20.0) def test_bolt_2022_2023_friction_threshold_curve(self): base = get_friction_threshold(6.0) left_turn_in = get_bolt_2022_2023_friction_threshold(6.0, 0.7, 0.8) right_turn_in = get_bolt_2022_2023_friction_threshold(6.0, -0.7, -0.8) left_unwind = get_bolt_2022_2023_friction_threshold(6.0, 0.7, -0.8) right_unwind = get_bolt_2022_2023_friction_threshold(6.0, -0.7, 0.8) assert left_turn_in < right_turn_in < base < right_unwind < left_unwind def test_bolt_2022_2023_friction_scale_curve(self): base = get_bolt_2022_2023_friction_scale(25.0, 0.7, 0.8) left_turn_in = get_bolt_2022_2023_friction_scale(6.0, 0.7, 0.8) right_turn_in = get_bolt_2022_2023_friction_scale(6.0, -0.7, -0.8) left_unwind = get_bolt_2022_2023_friction_scale(6.0, 0.7, -0.8) right_unwind = get_bolt_2022_2023_friction_scale(6.0, -0.7, 0.8) assert left_turn_in > right_turn_in > base assert base > right_unwind > left_unwind def test_silverado_trailer_ff_scale_curve(self): assert get_silverado_trailer_ff_scale(0.0, 0.0, 55.0 * 0.44704) == 1.0 assert get_silverado_trailer_ff_scale(0.7, 0.7, 60.0 * 0.44704) > get_silverado_trailer_ff_scale(-0.7, 0.7, 60.0 * 0.44704) assert get_silverado_trailer_ff_scale(0.7, 0.7, 60.0 * 0.44704) > get_silverado_trailer_ff_scale(0.7, -0.7, 60.0 * 0.44704) assert get_silverado_trailer_ff_scale(0.7, 0.7, 70.0 * 0.44704) < get_silverado_trailer_ff_scale(0.7, 0.7, 60.0 * 0.44704) def test_silverado_trailer_friction_threshold_curve(self): base = get_friction_threshold(60.0 * 0.44704) left_turn_in = get_silverado_trailer_friction_threshold(60.0 * 0.44704, 0.7, 0.8) right_turn_in = get_silverado_trailer_friction_threshold(60.0 * 0.44704, -0.7, -0.8) left_unwind = get_silverado_trailer_friction_threshold(60.0 * 0.44704, 0.7, -0.8) right_unwind = get_silverado_trailer_friction_threshold(60.0 * 0.44704, -0.7, 0.8) assert left_turn_in < right_turn_in < base < left_unwind < right_unwind def test_silverado_trailer_friction_scale_curve(self): base = get_silverado_trailer_friction_scale(60.0 * 0.44704, 0.7, 0.0) left_turn_in = get_silverado_trailer_friction_scale(60.0 * 0.44704, 0.7, 0.8) right_turn_in = get_silverado_trailer_friction_scale(60.0 * 0.44704, -0.7, -0.8) left_unwind = get_silverado_trailer_friction_scale(60.0 * 0.44704, 0.7, -0.8) right_unwind = get_silverado_trailer_friction_scale(60.0 * 0.44704, -0.7, 0.8) assert left_turn_in > right_turn_in > base assert base > left_unwind > right_unwind def test_bolt_2017_testing_ground_update_path(self, monkeypatch): controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(GM.CHEVROLET_BOLT_CC_2017) monkeypatch.setattr(latcontrol_torque, "bolt_2017_lateral_testing_ground_active", lambda: True) _, _, lac_log = controller.update(True, CS, VM, params, False, 0.0025, False, 0.2, None, None, starpilot_toggles) assert lac_log.active def test_bolt_2018_2021_testing_ground_update_path(self, monkeypatch): controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(GM.CHEVROLET_BOLT_CC_2018_2021) monkeypatch.setattr(latcontrol_torque, "bolt_2018_2021_lateral_testing_ground_active", lambda: True) _, _, lac_log = controller.update(True, CS, VM, params, False, 0.0025, False, 0.2, None, None, starpilot_toggles) assert lac_log.active def test_bolt_2022_2023_testing_ground_update_path(self, monkeypatch): controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(GM.CHEVROLET_BOLT_ACC_2022_2023) monkeypatch.setattr(latcontrol_torque, "bolt_2022_2023_lateral_testing_ground_active", lambda: True) _, _, lac_log = controller.update(True, CS, VM, params, False, 0.0025, False, 0.2, None, None, starpilot_toggles) assert lac_log.active def test_silverado_trailer_testing_ground_update_path(self, monkeypatch): controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(GM.CHEVROLET_SILVERADO) monkeypatch.setattr(latcontrol_torque, "silverado_trailer_lateral_testing_ground_active", lambda: True) _, _, lac_log = controller.update(True, CS, VM, params, False, 0.0025, False, 0.2, None, None, starpilot_toggles) assert lac_log.active @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), (NISSAN.NISSAN_LEAF, LatControlAngle), (GM.CHEVROLET_BOLT_ACC_2022_2023, LatControlTorque)]) def test_saturation(self, car_name, controller): CarInterface = interfaces[car_name] CP = CarInterface.get_non_essential_params(car_name) CI = CarInterface(CP, custom.StarPilotCarParams.new_message()) VM = VehicleModel(CP) controller = controller(CP.as_reader(), CI, DT_CTRL) CS = car.CarState.new_message() CS.vEgo = 30 CS.steeringPressed = False params = log.LiveParametersData.new_message() starpilot_toggles = SimpleNamespace() # Saturate for curvature limited and controller limited for _ in range(1000): _, _, lac_log = controller.update(True, CS, VM, params, False, 0, True, 0.2, None, None, starpilot_toggles) assert lac_log.saturated for _ in range(1000): _, _, lac_log = controller.update(True, CS, VM, params, False, 0, False, 0.2, None, None, starpilot_toggles) assert not lac_log.saturated for _ in range(1000): _, _, lac_log = controller.update(True, CS, VM, params, False, 1, False, 0.2, None, None, starpilot_toggles) assert lac_log.saturated