Back to Repositories

Testing Lateral Control Saturation Behavior in openpilot

This test suite validates lateral control functionality in the openpilot system across different vehicle models and control strategies. It focuses on testing steering control saturation limits and behavior for various car manufacturers including Honda, Toyota, and Nissan.

Test Coverage Overview

The test coverage encompasses lateral control validation across multiple vehicle platforms and control algorithms. Key functionality tested includes:

  • PID-based lateral control for Honda vehicles
  • Torque-based control for Toyota vehicles
  • Angle-based control for Nissan vehicles
  • Steering saturation behavior validation
  • Vehicle-specific parameter handling

Implementation Analysis

The testing approach utilizes parameterized testing to evaluate different control strategies across vehicle models. The implementation leverages Python’s parameterized testing framework to iterate through vehicle-controller combinations, testing each with simulated vehicle state data and pose information.

The test validates controller behavior over 1000 iterations to ensure proper saturation detection.

Technical Details

Testing tools and components include:

  • Parameterized test framework
  • Cereal for message serialization
  • Mock generators for LivePose data
  • Vehicle model simulation
  • Car interface abstractions for different manufacturers
  • Custom lateral control implementations (PID, Torque, Angle)

Best Practices Demonstrated

The test implementation showcases several testing best practices:

  • Parameterized test cases for multiple configurations
  • Proper isolation of vehicle-specific parameters
  • Comprehensive iteration count for stability verification
  • Clear separation of control strategies
  • Mock data generation for consistent testing

commaai/openpilot

selfdrive/controls/lib/tests/test_latcontrol.py

            
from parameterized import parameterized

from cereal import car, log
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 openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
from openpilot.selfdrive.locationd.helpers import Pose
from openpilot.common.mock.generators import generate_livePose


class TestLatControl:

  @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque),  (NISSAN.NISSAN_LEAF, LatControlAngle)])
  def test_saturation(self, car_name, controller):
    CarInterface, CarController, CarState, RadarInterface = interfaces[car_name]
    CP = CarInterface.get_non_essential_params(car_name)
    CI = CarInterface(CP, CarController, CarState)
    VM = VehicleModel(CP)

    controller = controller(CP.as_reader(), CI)

    CS = car.CarState.new_message()
    CS.vEgo = 30
    CS.steeringPressed = False

    params = log.LiveParametersData.new_message()

    lp = generate_livePose()
    pose = Pose.from_live_pose(lp.livePose)

    for _ in range(1000):
      _, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose)

    assert lac_log.saturated