plannerd Module Decision-Making Process in openpilot: Complete Technical Breakdown

The plannerd module is openpilot's core longitudinal planning daemon that processes modelV2 predictions, vehicle dynamics, and radar data through a Model Predictive Control (MPC) solver to generate safe acceleration trajectories and lane departure warnings.

The plannerd process serves as the brain of commaai/openpilot's autonomous driving stack, executing a continuous decision-making pipeline that transforms raw sensor inputs into actionable control commands. This real-time module orchestrates the complex interplay between neural network predictions and vehicle physics to ensure safe longitudinal control.

Initialization and Real-Time Configuration

On startup, plannerd configures itself as a low-priority real-time process using config_realtime_process(5, Priority.CTRL_LOW) in selfdrive/controls/plannerd.py (lines 12-13). The module loads persistent vehicle parameters via Params().get("CarParams") to instantiate the LongitudinalPlanner and LaneDepartureWarning helpers (lines 15-21). Messaging infrastructure initializes through PubMaster for broadcasting longitudinalPlan and driverAssistance topics, while SubMaster subscribes to critical inputs including modelV2, radarState, and carState (lines 21-23).

The Main Decision Loop

The core decision-making cycle triggers exclusively on fresh modelV2 predictions. When sm.updated['modelV2'] returns true (lines 25-28), the pipeline executes two parallel tracks: longitudinal trajectory planning and lane departure monitoring.

Sensor Data Acquisition and modelV2 Triggers

The loop blocks on sm.update() until new data arrives. The decision gate at line 25 ensures computational resources are only consumed when the neural network produces fresh lane and object predictions, maintaining synchronization between perception and planning layers.

Longitudinal Planning Pipeline

The LongitudinalPlanner.update(sm) method (lines 86-166 in selfdrive/controls/lib/longitudinal_planner.py) executes a sophisticated multi-stage decision flow:

  • Vehicle Pitch Compensation: Calculates coasting acceleration based on road gradient.
  • Cruise State Management: Handles speed setpoints and reset logic when openpilot disengages.
  • Throttle Gating: Applies safety limits based on model-predicted "gas press probability" to prevent unintended acceleration.
  • Lateral-Aware Acceleration Clipping: Reduces longitudinal acceleration during high-curvature turns to respect tire friction limits.
  • MPC Trajectory Optimization: Solves for optimal v_solution, a_solution, and j_solution (jerk) profiles through self.mpc.update.
  • Profile Smoothing: Applies first-order filters and final acceleration clipping to ensure drivable comfort.
  • Experimental Mode Blending: Selects between pure MPC output or minimum-of-MPC/E2E predictions when experimental features are enabled.

Lane Departure Warning System

Simultaneously, ldw.update() (lines 16-36 in selfdrive/controls/lib/ldw.py) evaluates lane boundary violations by checking:

  • Driver Intent: 5-second blinker cooldown period to suppress false positives during intentional lane changes.
  • Speed Thresholds: Minimum velocity requirements (vEgo > LDW_MIN_SPEED).
  • Lane Confidence: Model-predicted lane visibility and lane-change probability thresholds.

Message Publishing and Broadcasting

The pipeline concludes with dual message emission. longitudinal_planner.publish(sm, pm) (lines 69-93) constructs the longitudinalPlan message containing trajectory solutions, solver timing metrics, lead vehicle flags, and forward collision warnings. Separately, lines 31-36 in plannerd.py assemble the driverAssistance message with leftLaneDeparture and rightLaneDeparture Boolean flags, validated against carState, carControl, modelV2, and liveParameters checks.

Key Source Files and Architecture

Three primary files constitute the decision-making architecture according to the commaai/openpilot source code:

Minimal Implementation Example

The following standalone snippet mimics the core decision flow for testing or documentation purposes:

import cereal.messaging as messaging
from openpilot.selfdrive.controls.lib.ldw import LaneDepartureWarning
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority

def run_once():
    config_realtime_process(5, Priority.CTRL_LOW)

    # Load vehicle parameters

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

    # Helpers

    ldw = LaneDepartureWarning()
    planner = LongitudinalPlanner(CP)

    # Messaging setup

    pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance'])
    sm = messaging.SubMaster(['carControl', 'carState',
                              'controlsState', 'liveParameters',
                              'radarState', 'modelV2', 'selfdriveState'],
                             poll='modelV2')

    # One iteration (normally inside a while‑True loop)

    sm.update()
    if sm.updated['modelV2']:
        planner.update(sm)
        planner.publish(sm, pm)

        ldw.update(sm.frame, sm['modelV2'], sm['carState'], sm['carControl'])
        msg = messaging.new_message('driverAssistance')
        msg.valid = sm.all_checks(['carState', 'carControl', 'modelV2', 'liveParameters'])
        msg.driverAssistance.leftLaneDeparture = ldw.left
        msg.driverAssistance.rightLaneDeparture = ldw.right
        pm.send('driverAssistance', msg)

# Call run_once() repeatedly in production.

Summary

  • The plannerd module operates as a real-time process with Priority.CTRL_LOW timing constraints configured in selfdrive/controls/plannerd.py.
  • Decision cycles trigger exclusively on modelV2 updates to ensure perception-planning synchronization.
  • The LongitudinalPlanner.update() method fuses vehicle pitch, cruise settings, and MPC optimization to generate safe acceleration profiles.
  • Lane departure warnings run in parallel, applying blinker cooldowns and speed thresholds to reduce false positives.
  • The system outputs longitudinalPlan trajectories and driverAssistance alerts through cereal messaging.

Frequently Asked Questions

What triggers the plannerd decision-making cycle?

The cycle activates only when sm.updated['modelV2'] detects fresh neural network predictions in selfdrive/controls/plannerd.py (lines 25-28). This event-driven architecture ensures the longitudinal planner consumes the latest lane and object detection data before computing trajectories.

How does the longitudinal planner prevent dangerous acceleration during turns?

The system implements lateral-aware acceleration clipping inside LongitudinalPlanner.update() (selfdrive/controls/lib/longitudinal_planner.py lines 86-166). This logic reduces longitudinal acceleration limits based on current lateral dynamics, ensuring the vehicle maintains tire friction reserves for cornering.

What is the difference between the longitudinalPlan and driverAssistance outputs?

longitudinalPlan contains the MPC-computed speed and acceleration trajectory (v_solution, a_solution) for the vehicle controller, while driverAssistance carries binary lane departure flags (leftLaneDeparture, rightLaneDeparture) for the user interface warnings.

How does plannerd handle driver-initiated lane changes?

The LaneDepartureWarning class enforces a 5-second blinker cooldown period in selfdrive/controls/lib/ldw.py (lines 16-36). When recent turn signal activity is detected, the system suppresses departure warnings to avoid alerting during intentional maneuvers.

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 →