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:
livePose– Raw GNSS and vision-based pose estimates (position, orientation, angular velocity)liveCalibration– Camera-to-vehicle calibration offsets and statuscarState– 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_RATEandROAD_ROLLextracted from calibrated pose dataSTEER_ANGLEandROAD_FRAME_X_SPEEDderived from vehicle CAN signals- High-noise observations of the current
STEER_RATIOandSTIFFNESSstates 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.pyorchestrates real-time estimation using an EKF defined inselfdrive/locationd/models/car_kf.py - The system fuses
livePose,liveCalibration, andcarStateat 20 Hz to estimate steer ratio, stiffness factor, angle offsets, and road roll - Estimates undergo hysteresis-based validity checks before publishing and persist to
LiveParametersV2for 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →