How Openpilot Performs Real-Time Car Parameter Estimation Using liveParameters

Openpilot continuously refines steer ratio, stiffness factor, and angle offsets through a Kalman-filter-based learner running in the locationd daemon, publishing validated estimates as liveParameters messages for lateral control.

The commaai/openpilot repository implements real-time vehicle dynamics estimation through the locationd (Location Daemon) service. This system fuses live sensor streams to adapt to changing vehicle characteristics—such as tire wear or load variations—without requiring manual recalibration, storing persistent estimates in the LiveParametersV2 parameter key.

Core Architecture Components

The parameter estimation pipeline centers on three primary components orchestrated within selfdrive/locationd/paramsd.py:

  • VehicleParamsLearner – Implements the Extended Kalman Filter (EKF) logic and manages the state vector
  • CarKalman – Generates the filter equations and observation models defined in selfdrive/locationd/models/car_kf.py
  • PoseCalibrator – Aligns raw pose data with the vehicle reference frame using utilities in selfdrive/locationd/helpers.py

The system persists estimates using the Params database (common/params.py) under the binary key LiveParametersV2, enabling warm-start initialization across ignition cycles.

Data Flow and Estimation Pipeline

Input Sensor Fusion

The daemon subscribes to three critical message streams via SubMaster:

  1. livePose – Raw GNSS and vision-based pose estimates (position, orientation, angular velocity)
  2. liveCalibration – Camera-to-vehicle calibration offsets and status
  3. carState – Steering angle, vehicle speed, and pose validity flags

Preprocessing occurs through Pose.from_live_pose and PoseCalibrator.feed_live_calib to transform measurements into the vehicle coordinate system before filtering.

Kalman Filter Update Cycle

The CarKalman EKF processes observations at approximately 20 Hz, synchronized with livePose updates. The filter ingests:

  • ROAD_FRAME_YAW_RATE and ROAD_ROLL extracted from calibrated pose data
  • STEER_ANGLE and ROAD_FRAME_X_SPEED derived from vehicle CAN signals
  • High-noise observations of the current STEER_RATIO and STIFFNESS states to bound estimation variance

The state vector x maintains estimates for STEER_RATIO, STIFFNESS, ANGLE_OFFSET, ANGLE_OFFSET_FAST, and ROAD_ROLL, while the covariance matrix P provides real-time uncertainty quantification for each parameter.

Validation and Persistence

Hysteresis-based checks in check_valid_with_hysteresis enforce safety constraints before publishing:

  • Angle offset limits (OFFSET_MAX, OFFSET_LOWERED_MAX)
  • Roll boundaries (ROLL_MAX, ROLL_LOWERED_MAX)
  • Sensor consistency validation for yaw-rate magnitude and lateral acceleration

Every iteration constructs a liveParameters protobuf via get_msg(), publishing immediately through PubMaster. Once per minute (every 1200 frames), the daemon serializes the message to persistent storage using params.put_nonblocking("LiveParametersV2", msg.to_bytes()).

Real-Time Operational Safeguards

The implementation includes specific mechanisms to prevent estimator divergence during non-representative driving conditions:

Active-Mode Gating restricts filter updates to periods when vehicle speed exceeds MIN_ACTIVE_SPEED (1 m/s) and absolute steering angle remains below 45 degrees. This prevents parameter drift during parking maneuvers or standstill.

Adaptive Noise Modeling applies sensor-specific observation noise caps—yaw-rate standard deviation limited to 10 rad/s and roll standard deviation to 1 rad—maintaining filter stability under noisy GNSS or vision conditions.

State Recovery on startup invokes retrieve_initial_vehicle_params() to load cached values from LiveParametersV2, validate them against the current CarParams configuration, and seed the learner. Corrupted or vehicle-mismatched data triggers automatic fallback to default specifications.

Implementation Example

The following excerpt from selfdrive/locationd/paramsd.py illustrates the main daemon loop initializing the learner and managing the estimation cycle:

pm = messaging.PubMaster(['liveParameters'])
sm = messaging.SubMaster(['livePose', 'liveCalibration', 'carState'], poll='livePose')

params = Params()
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)

# Load previous estimates if available

steer_ratio, stiffness, angle_offset_deg, p_initial = retrieve_initial_vehicle_params(
    params, CP, REPLAY=False, DEBUG=False)

learner = VehicleParamsLearner(
    CP,
    steer_ratio,
    stiffness,
    np.radians(angle_offset_deg),
    p_initial)

while True:
    sm.update()
    if sm.all_checks():
        for which in sorted(sm.updated.keys(), key=lambda x: sm.logMonoTime[x]):
            if sm.updated[which]:
                t = sm.logMonoTime[which] * 1e-9
                learner.handle_log(t, which, sm[which])

    if sm.updated['livePose']:
        msg = learner.get_msg(sm.all_checks())
        # Persist once per minute (1200 frames at 20 Hz)

        if sm.frame % 1200 == 0:
            params.put_nonblocking("LiveParametersV2", msg.to_bytes())
        pm.send('liveParameters', msg.to_bytes())

Summary

  • VehicleParamsLearner in selfdrive/locationd/paramsd.py orchestrates real-time estimation using an EKF defined in selfdrive/locationd/models/car_kf.py
  • The system fuses livePose, liveCalibration, and carState at 20 Hz to estimate steer ratio, stiffness factor, angle offsets, and road roll
  • Estimates undergo hysteresis-based validity checks before publishing and persist to LiveParametersV2 for cross-drive initialization
  • Active-mode gating prevents parameter drift during low-speed or high-steering-angle maneuvers

Frequently Asked Questions

What specific vehicle parameters does liveParameters estimate?

The system estimates steer ratio (steering wheel to road wheel angle ratio), stiffness factor (tire compliance coefficient), angle offset (slow-varying steering bias), angle offset fast (rapid calibration changes), and road roll (lateral road inclination). These values feed directly into lateral control algorithms to compensate for vehicle-specific dynamics and road geometry.

How does openpilot prevent liveParameters estimates from diverging?

The implementation employs active-mode gating that requires vehicle speed above 1 m/s and steering magnitude below 45 degrees before processing updates. Additionally, the filter receives high-noise observations of steer ratio and stiffness to artificially bound variance, while check_valid_with_hysteresis enforces maximum limits on angle offsets and roll before inclusion in the published message.

Where does openpilot store estimated parameters between drives?

Parameters serialize to the Params key-value store (common/params.py) under the binary key LiveParametersV2 once per minute. During startup, retrieve_initial_vehicle_params() validates cached values against the current CarParams vehicle configuration before seeding the Kalman filter, ensuring estimates remain valid across ignition cycles unless the vehicle hardware changes.

Which source files contain the liveParameters implementation?

The core logic resides in selfdrive/locationd/paramsd.py (daemon orchestration and learner), selfdrive/locationd/models/car_kf.py (EKF equations and state definitions), and selfdrive/locationd/helpers.py (pose calibration utilities). The persistence layer uses common/params.py for the LiveParametersV2 storage interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →