百度 Apollo 10 Planning 运行框架源码解析

发布于 2026-04-28  475 次阅读


百度 Apollo 10 Planning 运行框架源码解析

入口函数

Planning 中常见的入口函数可以分成几类:

入口函数 所在层级 主要职责
Init() 模块 / 插件 / 任务初始化 加载配置、初始化依赖对象、注册插件
RunOnce() Planning 周期入口 接收当前周期输入,构造 Frame,触发一次规划
Plan() Planner 入口 从通用调度进入具体 Planner 逻辑
ApplyRule() TrafficRule 入口 执行交通规则,向参考线注入约束
IsTransferable() Scenario 入口判断 判断是否可以切换到某个场景
Enter() Scenario 进入入口 写入场景状态,初始化场景上下文
Process() Scenario / Stage / Task 业务入口 执行当前层级的核心逻辑
ExecuteTaskOnReferenceLine() Stage 调度 Task 的入口 按 pipeline 顺序执行 Task 列表
Exit() Scenario 退出入口 清理场景状态

这些函数对应了 Planning 的几个核心层次:

层级 代表模块 关注点
OnLanePlanning Init()、RunOnce()、Plan() 周期调度、输入组织、输出轨迹
Planner Plan() 具体规划器执行入口
TrafficDecider Execute() 调度交通规则
TrafficRule ApplyRule() 生成交通规则约束
Scenario IsTransferable()、Enter()、Process()、Exit() 场景切换与场景生命周期
Stage Process()、ExecuteTaskOnReferenceLine() 场景内部阶段与 Task 调度
Task Execute()、Process() 路径、速度、决策等具体规划任务

从入口函数阅读 Planning,可以避免只停留在“有哪些类”的层面,而是进一步理解这些类在运行时如何被调用、如何共享数据、如何影响最终轨迹。

Planning主运行链路

从入口函数的角度,Apollo 10 Planning 的主链路可以压缩为:

PlanningComponent / Cyber RT
        ↓
OnLanePlanning::Init()
        ↓
OnLanePlanning::RunOnce()
        ↓
OnLanePlanning::Plan()
        ↓
Planner::Plan()
        ↓
ScenarioManager 选择 Scenario
        ↓
Scenario::IsTransferable()
        ↓
Scenario::Enter() / Scenario::Process() / Scenario::Exit()
        ↓
Stage::Process()
        ↓
Stage::ExecuteTaskOnReferenceLine()
        ↓
Task::Execute()
        ↓
Task::Process()
        ↓
PathData + SpeedData + Decision
        ↓
ADCTrajectory

交通规则链路则在正式进入 Planner 之前执行:

OnLanePlanning::RunOnce()
        ↓
Frame 初始化完成
        ↓
遍历 ReferenceLineInfo
        ↓
TrafficDecider::Execute()
        ↓
TrafficRule::ApplyRule()
        ↓
向 ReferenceLineInfo 注入停车墙、虚拟障碍物、交通规则约束
        ↓
OnLanePlanning::Plan()
        ↓
Planner / Scenario / Stage / Task

这一点很关键。ApplyRule() 通常不直接生成最终轨迹,而是提前把交通规则转换为 Planning 可处理的约束。后续的路径、速度、决策 Task 会在这些约束的基础上继续计算。

机制 作用
配置驱动 通过 pipeline.pb.txt、default_conf.pb.txt 等配置组织 Stage、Task、Rule
插件化 Scenario、Task、TrafficRule 可以按包和插件形式扩展
场景状态机 Scenario 内部通过 Stage 表达复杂交通行为
Task 流水线 每个 Stage 按配置顺序执行路径、速度、决策任务
数据容器共享 Frame、ReferenceLineInfo、PlanningContext 串联不同层级

Init初始化链路

Init() 是 Planning 运行前的初始化入口。它不负责生成轨迹,而是把地图、参考线、Planner、TrafficRule、Scenario、Task 等运行依赖准备好。

OnLanePlanning::Init() 主初始化流程

OnLanePlanning::Init() 是车道级 Planning 的初始化入口。其核心流程可以概括为:

OnLanePlanning::Init(config)
        ↓
检查 Planning 配置
        ↓
初始化 PlanningBase
        ↓
清空 History / PlanningStatus
        ↓
加载 HDMap
        ↓
创建 ReferenceLineProvider
        ↓
启动 ReferenceLineProvider
        ↓
LoadPlanner()
        ↓
TrafficDecider::Init()
        ↓
planner_->Init()

其中,地图、参考线和具体 Planner 是三个关键依赖。

初始化对象 作用
HDMap 提供车道、停止线、路口、交通灯、KeepClear 区域等地图元素
ReferenceLineProvider 根据 Routing 和地图生成候选参考线
planner_ 指向具体 Planner,例如公共道路规划器
TrafficDecider 加载并初始化交通规则插件
PlanningContext / History 保存跨周期状态和历史轨迹相关信息

Planning 后续的路径和速度计算并不是直接在整张地图上进行,而是围绕一条或多条 ReferenceLine 展开。因此 ReferenceLineProvider 的初始化非常关键。它为后续 Frame 中的 ReferenceLineInfo 提供基础数据。

LoadPlanner() 与具体 Planner

LoadPlanner() 根据配置创建具体 Planner。OnLanePlanning 本身更偏向周期调度,真正的场景选择、Stage 执行和 Task 流水线会在具体 Planner 内部继续展开。

OnLanePlanning
        ↓
LoadPlanner()
        ↓
PublicRoadPlanner / 其他 Planner
        ↓
ScenarioManager
        ↓
Scenario / Stage / Task

这说明 OnLanePlanning 与具体规划逻辑之间存在一层抽象边界。调度层负责组织输入输出,具体 Planner 负责展开规划策略。

TrafficDecider::Init() 与规则插件加载

TrafficRule 不是写死在主流程中的一组 if-else,而是由 TrafficDecider 按配置加载和初始化。常见规则包括:

TrafficRule 作用
KeepClear 处理禁止滞留区域
StopSign 处理停车标志
TrafficLight 处理红绿灯停车逻辑
Crosswalk 处理人行横道让行
Destination 处理目的地附近停车
ReferenceLineEnd 处理参考线终点停车

TrafficDecider::Init() 完成规则对象创建后,每个规划周期会通过 TrafficDecider::Execute() 逐个调用规则的 ApplyRule()。

TrafficRule::Init() 与 ApplyRule() 的分工

以 KeepClear 为例,初始化与运行执行是分开的:

KeepClear::Init(name, injector)
        ↓
TrafficRule::Init(name, injector)
        ↓
加载 keep_clear 配置
        ↓
保存配置参数
        ↓
等待每个周期由 ApplyRule() 执行

Init() 只加载配置和准备依赖,ApplyRule() 才是真正规则生效的入口。这种设计让规则模块在启动阶段完成配置解析,在周期运行阶段只关注当前 Frame 和 ReferenceLineInfo。

Task、Scenario、Stage 的初始化

Scenario、Stage、Task 依赖 pipeline.pb.txt 组织。配置文件定义了某个 Scenario 中有哪些 Stage,每个 Stage 中按什么顺序执行哪些 Task。

pipeline.pb.txt
        ↓
Scenario 配置
        ↓
Stage 配置
        ↓
Task 顺序
        ↓
运行时 task_list_

这一步建立了“配置文件中的任务顺序”和“运行时函数调用”的映射关系。后续 Stage::ExecuteTaskOnReferenceLine() 会按这个顺序执行 Task。

RunOnce周期入口

OnLanePlanning::RunOnce() 是 Apollo 10 Planning 最重要的周期入口。每个规划周期都会调用它一次。

函数形式可以概括为:

void OnLanePlanning::RunOnce(
    const LocalView& local_view,
    ADCTrajectory* const ptr_trajectory_pb);

两个核心参数分别代表本周期输入和最终输出:

参数 含义
local_view 当前周期 Planning 接收到的输入视图
ptr_trajectory_pb 当前周期最终填充的轨迹结果

RunOnce() 更像是一个周期调度器,而不是某个路径优化算法。它负责把一次规划周期需要的输入、状态和中间对象组织起来。

OnLanePlanning::RunOnce(local_view, trajectory)
        ↓
保存 local_view
        ↓
检查输入数据是否 ready
        ↓
更新 VehicleState
        ↓
处理新的 PlanningCommand / Routing
        ↓
更新 ReferenceLineProvider
        ↓
计算 stitching trajectory
        ↓
构造 Frame
        ↓
执行 TrafficDecider
        ↓
调用 Plan()
        ↓
填充 ADCTrajectory
        ↓
发布 Planning 结果

LocalView 固定本周期输入

LocalView 是 Planning 当前周期看到的世界。典型输入包括:

输入 作用
localization_estimate 当前车辆位姿
chassis 底盘状态、驾驶模式、速度等
prediction_obstacles 障碍物预测结果
planning_command 导航、靠边、泊车等规划命令
traffic_light 交通灯检测结果
control_interactive_msg Control 与 Planning 的交互信息

RunOnce() 首先要保存这份输入视图,使本周期后续逻辑使用同一份数据。这样可以避免一个周期内上游数据变化导致状态不一致。

车辆状态与规划起点

Planning 会从定位和底盘信息中构造车辆状态。车辆状态是后续几个步骤的基础:

  • 参考线生成;
  • stitching trajectory 计算;
  • Frame 初始化;
  • SL 坐标转换;
  • 路径边界生成;
  • 速度规划。

如果车辆状态无效,Planning 通常无法继续正常规划,系统会进入错误处理或生成停车轨迹。

Routing / PlanningCommand 更新

当系统接收到新的 Routing 或 PlanningCommand 时,Planning 需要重置部分跨周期状态,例如历史轨迹、参考线提供器、Planner 状态和 PlanningContext。这个动作说明 Planning 是一个连续运行系统,不是每帧完全独立的规划脚本。

Trajectory Stitching

TrajectoryStitcher 的作用是把上一周期已经发布的轨迹和当前车辆真实状态拼接起来,形成当前周期的规划起点。

上一周期已发布轨迹
        +
当前车辆真实状态
        ↓
当前周期 planning_init_point

如果每个周期都直接从车辆当前位置重新规划,轨迹容易不连续。Stitching 机制能够让输出轨迹在周期之间保持连续性,降低 Control 端跟踪压力。

Frame 初始化

Frame 是一次规划周期的数据容器。RunOnce() 初始化 Frame 后,后续 TrafficRule、Scenario、Stage、Task 基本都围绕这个对象运行。

LocalView + VehicleState + ReferenceLine + Obstacles
        ↓
Frame
        ↓
TrafficDecider / Scenario / Stage / Task

TrafficDecider 执行位置

TrafficDecider 在正式调用 Plan() 前执行:

InitFrame()
        ↓
TrafficDecider::Execute(frame, reference_line_info)
        ↓
OnLanePlanning::Plan()

因此,交通规则是在路径和速度 Task 执行之前注入约束的。这个顺序解释了为什么 KeepClear::ApplyRule() 可以影响后续速度规划在虚拟墙前停车。

Planner 分发

OnLanePlanning::Plan() 是从周期调度进入具体 Planner 的桥梁。

OnLanePlanning::RunOnce()
        ↓
OnLanePlanning::Plan()
        ↓
planner_->Plan(planning_init_point, frame, trajectory)

planner_ 是初始化阶段由 LoadPlanner() 创建出来的具体 Planner 对象。OnLanePlanning::Plan() 自身不展开所有场景和任务,而是把当前规划起点、当前周期 Frame 和轨迹输出指针交给具体 Planner。

在公共道路规划中,Planner 内部会结合当前 Frame、参考线信息和 PlanningContext 选择合适的 Scenario。

Planner::Plan()
        ↓
ScenarioManager 更新当前 Scenario
        ↓
Scenario::Process(planning_init_point, frame)
        ↓
当前 Stage::Process(planning_init_point, frame)
        ↓
Task 流水线

从这一层开始,Planning 的执行逻辑进入 Scenario / Stage / Task 三层框架。

TrafficDecider/ApplyRule

TrafficDecider 是 Apollo Planning 中容易被忽略但非常关键的一层。它位于 RunOnce() 和 Plan() 之间,负责把交通规则转换成后续规划可使用的约束。

TrafficRule 的定位

TrafficRule 不直接代表某个交通场景,也不一定直接生成路径或速度。它主要负责把地图规则、交通规则和停车约束写入 ReferenceLineInfo。

TrafficRule 典型处理内容 对后续规划的影响
KeepClear 禁止滞留区域 生成虚拟墙或停车约束
StopSign 停车标志 生成停止线前的 stop decision
TrafficLight 红绿灯状态 红灯或黄灯时生成停车约束
Crosswalk 人行横道 对行人、非机动车进行让行约束
Destination 目的地停车 在目的地附近生成停车点
ReferenceLineEnd 参考线终点 防止车辆驶出参考线末端

TrafficDecider::Execute() 调度规则

TrafficDecider 可以理解为 TrafficRule 的统一调度器。它按配置遍历规则插件,并对每条候选参考线执行规则。

TrafficDecider::Execute(frame, reference_line_info)
        ↓
for rule in rule_list:
        ↓
rule->ApplyRule(frame, reference_line_info)

规则执行时,主要输入是 Frame 和 ReferenceLineInfo:

输入对象 作用
Frame 提供本周期车辆状态、障碍物、PlanningContext 等信息
ReferenceLineInfo 提供当前参考线、地图 overlap、路径/速度/决策容器
TrafficRule 配置 提供规则阈值、距离参数、开关等

ApplyRule() 的职责

ApplyRule() 是交通规则真正生效的入口。它通常完成以下动作:

  1. 从 ReferenceLineInfo 中读取当前参考线;
  2. 从地图路径中读取 overlap 或特殊区域;
  3. 判断 ego vehicle 与规则区域的相对位置;
  4. 判断当前是否需要停车、避让、限速或生成虚拟障碍物;
  5. 创建虚拟墙、虚拟障碍物或 stop decision;
  6. 写回 ReferenceLineInfo;
  7. 供后续 Path / Speed / Decision Task 使用。

数据流可以概括为:

HD Map / ReferenceLine / VehicleState / Obstacles
        ↓
TrafficRule::ApplyRule()
        ↓
Virtual Obstacle / Stop Decision / Constraint
        ↓
ReferenceLineInfo
        ↓
Path Task / Speed Task / Decision Task

ApplyRule() 本身不是最终规划算法,但它改变了后续算法面对的约束空间。后续 Task 不需要分别理解每一种交通规则,只要读取 ReferenceLineInfo 中的障碍物、决策和约束即可。

TrafficDecider 与 Task 的关系

TrafficDecider 与 Task 并不是并列替代关系,而是前后衔接关系:

TrafficDecider / TrafficRule
        ↓
提前注入规则约束
        ↓
Stage / Task
        ↓
在约束基础上生成路径和速度

例如 KeepClear::ApplyRule() 生成的虚拟墙,会被后续速度边界、速度决策和速度优化任务使用。路径和速度 Task 不需要知道这个虚拟墙来自 KeepClear,只需要按障碍物或 stop decision 处理即可。

Scenario生命周期

Scenario 是 Apollo Planning 中的交通场景层,负责表达当前车辆处于哪类交通情境中。

Scenario 场景含义
lane_follow 正常车道跟随
stop_sign_unprotected 无保护 Stop Sign
traffic_light_protected 受保护红绿灯
traffic_light_unprotected_left_turn 无保护左转
pull_over 靠边停车
valet_parking 代客泊车
park_and_go 泊车后起步

Scenario 的关键入口不是单个函数,而是一组生命周期函数:

函数 作用
IsTransferable() 判断是否可以切换到该场景
Enter() 进入场景时写入场景状态
Process() 执行当前场景逻辑,通常调度当前 Stage
Exit() 离开场景时清理上下文

IsTransferable()

IsTransferable() 判断当前是否可以从已有场景切换到目标场景。以 stop_sign_unprotected 为例,它通常围绕 Stop Sign overlap、车辆位置、距离阈值和当前 PlanningContext 进行判断。

当前 Frame + ReferenceLineInfo + PlanningContext
        ↓
Scenario::IsTransferable()
        ↓
true:可以进入目标 Scenario
false:继续当前 Scenario 或保持 lane_follow

它只负责场景切换判断,不负责停车,也不负责生成轨迹。

Enter()

Enter() 在 Scenario 被选中后执行。它通常把场景相关状态写入 PlanningContext。

以 Stop Sign 场景为例,进入场景时需要记录当前处理的 stop_sign_overlap_id。这个 ID 后续会被多个 Stage 读取,用于判断车辆是否已经停车、是否进入 creep、是否完成路口通过。

查找当前参考线上的 stop_sign_overlap
        ↓
确认当前要处理的 stop_sign_id
        ↓
写入 PlanningContext
        ↓
初始化场景状态

Scenario 不是无状态函数。复杂交通场景往往要持续多个规划周期,必须依赖 PlanningContext 保存跨周期状态。

Process()

Scenario 层的 Process() 一般不直接执行所有路径和速度算法,而是调度当前 Stage。

Scenario::Process(planning_init_point, frame)
        ↓
获取当前 Stage
        ↓
current_stage->Process(planning_init_point, frame)
        ↓
根据 StageResult 判断是否切换 Stage
        ↓
如果场景完成则退出

这一层的职责是维护“当前场景进行到哪个阶段”,并将控制权交给 Stage。

Exit()

Exit() 用于清理场景状态。以 Stop Sign 为例,车辆通过路口后要清除当前 stop sign 状态,避免后续周期继续使用旧的 stop_sign_id 或停车状态。

Stage阶段状态机

Stage 是 Scenario 内部的阶段。许多交通场景不是一次函数调用就能完成,而是由多个阶段组成的状态机。

以 stop_sign_unprotected 为例,阶段可以表示为:

PRE_STOP
        ↓
STOP
        ↓
CREEP
        ↓
INTERSECTION_CRUISE
        ↓
完成场景
Stage 主要职责
PRE_STOP 接近停止线,准备停车
STOP 在停止线前完成停车和观察
CREEP 低速探出,扩大观察视野
INTERSECTION_CRUISE 通过路口并判断场景结束

Stage::Process() 是阶段业务入口。它通常同时承担两个职责:

  1. 调用 Task 流水线生成本周期规划结果;
  2. 判断当前阶段是否完成,并设置下一个 Stage。

典型结构可以抽象为:

StageResult SomeStage::Process(
    const common::TrajectoryPoint& planning_init_point,
    Frame* frame) {
  // 执行当前阶段特有逻辑

  StageResult result = ExecuteTaskOnReferenceLine(planning_init_point, frame);

  // 根据车辆位置、速度、交通状态、PlanningContext 判断阶段状态
  if (stage_done) {
    return FinishStage();
  }

  return result.SetStageStatus(StageStatusType::RUNNING);
}

Stage::Process() 不只是 Task 的包装层。它还会结合当前场景状态判断是否切换阶段。例如在 Stop Sign 场景中,PRE_STOP 阶段需要判断车辆是否到达停车线附近,STOP 阶段需要判断是否已停稳并满足观察条件,CREEP 阶段需要判断是否可以继续通过路口。

输出类型 说明
规划中间结果 由 Task 写入 ReferenceLineInfo,包括路径、速度、障碍物决策
阶段状态 RUNNING、FINISHED、ERROR 等
下一阶段 通过 next_stage_ 或 FinishStage() 触发切换

Task 流水线

ExecuteTaskOnReferenceLine() 是 Stage 到 Task 的分发入口。它按照当前 Stage 的 pipeline 配置,在当前参考线上依次执行 Task。

ReferenceLineInfo 是 Task 执行对象

公共道路 Planning 基本围绕参考线展开。每条参考线对应一个 ReferenceLineInfo,其中包含:

内容 说明
ReferenceLine 参考线几何信息
Obstacles 当前参考线相关障碍物
PathData 路径规划结果
SpeedData 速度规划结果
Decisions 障碍物决策、停车决策等
Cost / Drivable 参考线代价和可驾驶状态
Debug 信息 规划过程中的调试数据

Stage 执行 Task 时,不是对抽象世界执行,而是对每条候选参考线的 ReferenceLineInfo 执行。

pipeline 配置到 Task 顺序

每个 Scenario 的 conf/pipeline.pb.txt 中会配置 Stage 及其 Task 顺序。以 Stop Sign 相关 Stage 为例,常见任务包括:

LANE_FOLLOW_PATH
LANE_BORROW_PATH
FALLBACK_PATH
PATH_DECIDER
RULE_BASED_STOP_DECIDER
SPEED_BOUNDS_PRIORI_DECIDER
SPEED_HEURISTIC_OPTIMIZER
SPEED_DECIDER
SPEED_BOUNDS_FINAL_DECIDER
PIECEWISE_JERK_SPEED

这些配置在初始化阶段加载为运行时 task_list_,然后由 ExecuteTaskOnReferenceLine() 顺序调用。

pipeline.pb.txt
        ↓
Stage 配置
        ↓
task_list_
        ↓
ExecuteTaskOnReferenceLine()
        ↓
Task::Execute()
        ↓
Task::Process()

抽象执行流程

Stage::ExecuteTaskOnReferenceLine(planning_init_point, frame)
        ↓
for reference_line_info in frame->reference_line_info:
        ↓
  if reference_line_info 不可驾驶:跳过或 fallback
        ↓
  for task in task_list_:
        ↓
      task->Execute(frame, reference_line_info)
        ↓
      task->Process(frame, reference_line_info)
        ↓
      记录 debug 信息和耗时
        ↓
  判断 reference_line_info 是否有效
        ↓
返回 StageResult

这个函数是 Apollo Planning “配置驱动任务流水线”的具体落点。源码阅读时,只看 pipeline.pb.txt 只能知道任务顺序;顺着 ExecuteTaskOnReferenceLine() 才能看到这些配置如何转化为实际函数调用。

Task具体规划算法

Task 是 Apollo Planning 中最接近具体算法的一层。不同 Task 的 Process() 执行不同职责,大体可以分为 Decider 和 Optimizer 两类。

Task 类型 代表模块 职责
Decider PathDecider、SpeedDecider、RuleBasedStopDecider 做决策,标记障碍物、停车、让行、跟车等
Optimizer LaneFollowPath、PiecewiseJerkSpeed 做路径或速度优化

很多 Task 通过统一的 Execute() 入口被调用,然后进入子类实现的 Process()。

Stage::ExecuteTaskOnReferenceLine()
        ↓
Task::Execute(frame, reference_line_info)
        ↓
具体 Task::Process(frame, reference_line_info)

典型 Process() 的输入输出如下:

项目 说明
输入 Frame、ReferenceLineInfo、规划起点、配置参数
输出 PathData、SpeedData、障碍物决策、stop decision、debug 信息
写入位置 多数结果写入 ReferenceLineInfo
副作用 修改当前参考线上的规划结果或约束

Task 并不总是返回一个完整轨迹。更多情况下,Task 是逐步修改 ReferenceLineInfo。路径 Task 写入路径,速度 Task 写入速度,决策 Task 修改障碍物决策,最终由 Planner 汇总生成 ADCTrajectory。

举例LaneFollowPath解析

LaneFollowPath 是理解 Apollo 路径规划 Task 的默认入口,毕竟基础的规划主要走这里。它位于:

modules/planning/tasks/lane_follow_path/

典型文件结构:

lane_follow_path/
├── conf/
│   └── default_conf.pb.txt
├── proto/
│   └── lane_follow_path.proto
├── lane_follow_path.cc
├── lane_follow_path.h
├── plugins.xml
├── cyberfile.xml
├── BUILD
└── README_cn.md

LaneFollowPath::Process() 是它的核心入口。它基于当前参考线、车辆起点、道路边界和障碍物信息,生成可行路径。

LaneFollowPath::Process(frame, reference_line_info)
        ↓
判断是否需要复用路径
        ↓
GetStartPointSLState()
        ↓
DecidePathBounds()
        ↓
OptimizePath()
        ↓
AssessPath()
        ↓
写入 reference_line_info->path_data()

GetStartPointSLState()

Planning 内部大量使用 SL 坐标系:

坐标 含义
s 沿参考线方向的纵向距离
l 相对于参考线的横向偏移

GetStartPointSLState() 将当前规划起点从笛卡尔坐标转换到参考线坐标系中。后续路径边界和路径优化通常都在 SL 坐标系下展开。

DecidePathBounds()

DecidePathBounds() 负责生成当前车辆可行驶的横向边界。它会综合考虑:

约束来源 作用
当前参考线 提供路径中心和纵向展开方向
道路边界 限制车辆不能驶出道路
车道边界 限制或影响车道内行驶区域
ADC 车辆尺寸 通过车宽、安全余量收缩可行区域
静态障碍物 在路径边界中切除不可通行空间
借道或绕障需求 在特定条件下扩展或调整边界
TrafficRule 注入的虚拟障碍物 将交通规则转化为路径/速度约束

其本质是先构建可行空间:

地图边界 + 车道边界 + 车辆尺寸 + 障碍物 + 规则约束
        ↓
PathBoundary

OptimizePath()

OptimizePath() 在路径边界内求解一条平滑、可行的路径。目标通常包括:

  • 尽量靠近参考线;
  • 横向偏移平滑;
  • 曲率变化平滑;
  • 不碰撞;
  • 不越界;
  • 满足车辆运动学约束。

如果 DecidePathBounds() 解决的是“能走哪里”,OptimizePath() 解决的是“在可行空间中选择哪条路径”。

AssessPath()

AssessPath() 用于筛选和评估候选路径。它会排除不可用路径,例如:

异常类型 说明
与障碍物冲突 路径与静态障碍物或虚拟障碍物冲突
超出边界 路径点超过道路或车道允许范围
路径不连续 几何或曲率变化不合理
横向偏移异常 偏离参考线过大
不满足可驾驶性 不适合作为当前参考线上的可执行路径

最终选出的路径会写入 ReferenceLineInfo,供后续速度规划 Task 使用。

LaneFollowPath 在整体链路中的位置

Stage::Process()
        ↓
ExecuteTaskOnReferenceLine()
        ↓
LaneFollowPath::Process()
        ↓
PathDecider::Process()
        ↓
SpeedBoundsDecider / SpeedOptimizer / SpeedDecider
        ↓
最终轨迹

LaneFollowPath 只解决路径部分,速度和最终轨迹还需要后续 Task 继续处理。

举例StopSignUnprotected解析

stop_sign_unprotected 是理解 Scenario / Stage 状态机的典型案例。目录通常位于:

modules/planning/scenarios/stop_sign_unprotected/

典型结构:

stop_sign_unprotected/
├── conf/
│   ├── pipeline.pb.txt
│   └── scenario_conf.pb.txt
├── proto/
│   └── stop_sign_unprotected.proto
├── stop_sign_unprotected_scenario.cc
├── stop_sign_unprotected_scenario.h
├── stage_pre_stop.cc
├── stage_pre_stop.h
├── stage_stop.cc
├── stage_stop.h
├── stage_creep.cc
├── stage_creep.h
├── stage_intersection_cruise.cc
├── stage_intersection_cruise.h
├── plugins.xml
├── cyberfile.xml
└── README_cn.md

这个场景的主线由 Scenario 生命周期和多个 Stage 组成:

StopSignUnprotectedScenario::IsTransferable()
        ↓
StopSignUnprotectedScenario::Enter()
        ↓
StagePreStop::Process()
        ↓
StageStop::Process()
        ↓
StageCreep::Process()
        ↓
StageIntersectionCruise::Process()
        ↓
StopSignUnprotectedScenario::Exit()

StopSignUnprotectedScenario::IsTransferable()

这是场景切换判断入口。主要判断当前是否应该进入无保护 Stop Sign 场景。

读取当前 Frame
        ↓
读取当前 ReferenceLineInfo
        ↓
查找 stop_sign overlap
        ↓
判断 stop sign 是否在车辆前方
        ↓
计算 ego 到 stop line 的距离
        ↓
检查距离是否满足进入阈值
        ↓
返回 true / false

IsTransferable() 不生成停车轨迹。它只判断是否应该让 ScenarioManager 切换到该场景。

StopSignUnprotectedScenario::Enter()

进入该场景后,Enter() 会把当前要处理的 stop sign 状态写入 PlanningContext。

进入 StopSignUnprotectedScenario
        ↓
找到当前要处理的 stop_sign_overlap_id
        ↓
写入 PlanningContext
        ↓
初始化 stop sign 场景状态

Stop Sign 场景通常跨多个周期执行。车辆可能需要先接近停止线,再停车,再观察,再低速 creep,最后通过路口。这些状态必须跨周期保存。

StagePreStop::Process()

PRE_STOP 阶段处理车辆到达停止线前的逻辑。

StagePreStop::Process(planning_init_point, frame)
        ↓
ExecuteTaskOnReferenceLine(planning_init_point, frame)
        ↓
获取当前 ReferenceLineInfo
        ↓
读取 PlanningContext 中的 stop_sign_overlap_id
        ↓
判断车辆与停止线的关系
        ↓
判断是否满足进入 STOP 阶段条件
        ↓
未完成则保持 RUNNING
        ↓
完成则设置 next_stage 并 FinishStage()

这一阶段既会执行 Task pipeline,也会判断阶段是否完成。Task 负责生成路径、速度和停车约束,Stage 负责决定是否从 PRE_STOP 切换到 STOP。

StageStop::Process()

STOP 阶段关注车辆是否已经在停止线前完成停车,以及是否满足继续前进的条件。

典型判断包括:

判断项 作用
车辆速度 判断是否已经停稳
车辆位置 判断是否位于停止线前合理位置
停车时间 判断是否满足 stop sign 停车要求
冲突对象 判断是否存在需要观察或让行的车辆
下一阶段条件 决定进入 CREEP 或 INTERSECTION_CRUISE

抽象流程:

StageStop::Process()
        ↓
ExecuteTaskOnReferenceLine()
        ↓
检查停车状态
        ↓
检查等待 / 观察条件
        ↓
满足条件则切换到 CREEP 或 INTERSECTION_CRUISE
        ↓
否则保持 RUNNING

StageCreep::Process()

CREEP 阶段用于车辆低速探出,以获得更好的路口视野。无保护 Stop Sign 场景下,车辆停稳后未必立即具备安全通过条件。Creep 允许车辆缓慢前移,同时继续观察冲突对象。

StageCreep::Process()
        ↓
ExecuteTaskOnReferenceLine()
        ↓
检查 creep 距离 / creep 速度
        ↓
检查路口冲突对象
        ↓
满足通过条件则切换到 INTERSECTION_CRUISE
        ↓
否则继续 CREEP

StageIntersectionCruise::Process()

INTERSECTION_CRUISE 阶段表示车辆已经进入或正在通过路口。该阶段继续执行 Task,并判断车辆是否已经通过 stop sign 影响区域。

StageIntersectionCruise::Process()
        ↓
ExecuteTaskOnReferenceLine()
        ↓
检查是否已通过 stop sign 影响区域
        ↓
完成则 FinishScenario
        ↓
否则继续 RUNNING

StopSignUnprotectedScenario::Exit()

场景完成后调用 Exit(),清理 PlanningContext 中与当前 stop sign 相关的状态。这个动作可以防止旧的 stop sign id、停车状态或观察状态影响后续规划周期。

举例KeepClear规则解析

KeepClear 是理解 TrafficRule 的典型案例。它通常位于:

modules/planning/traffic_rules/keepclear/

典型结构:

keepclear/
├── conf/
│   └── default_conf.pb.txt
├── proto/
│   └── keep_clear.proto
├── keep_clear.cc
├── keep_clear.h
├── plugins.xml
├── cyberfile.xml
└── README_cn.md

KeepClear 由 Init() 和 ApplyRule() 串联:

KeepClear::Init()
        ↓
加载规则配置
        ↓
每个规划周期
        ↓
KeepClear::ApplyRule(frame, reference_line_info)
        ↓
向 ReferenceLineInfo 注入约束

KeepClear::Init()

Init() 用于加载 KeepClear 规则配置。

KeepClear::Init(name, injector)
        ↓
TrafficRule::Init(name, injector)
        ↓
LoadConfig<KeepClearConfig>()
        ↓
保存 keepclear_config

初始化阶段不判断当前车辆是否需要停车,也不生成虚拟墙,只准备运行参数。

KeepClear::ApplyRule()

ApplyRule() 是 KeepClear 真正影响 Planning 的入口。

KeepClear::ApplyRule(frame, reference_line_info)
        ↓
从 ReferenceLineInfo 读取当前参考线
        ↓
查找 clear_area / keepclear overlap
        ↓
判断 ego 与 KeepClear 区域的位置关系
        ↓
判断是否需要阻止车辆进入该区域
        ↓
构造虚拟障碍物 / 虚拟墙
        ↓
向 ReferenceLineInfo 写入 stop decision
        ↓
后续 Task 根据该约束规划路径和速度

虚拟墙的作用

KeepClear 区域本身不是一个真实障碍物,但在特定交通状态下,车辆不能停在该区域内。Apollo 会把这类规则转换为虚拟障碍物或虚拟停止墙,使后续规划模块可以用统一方式处理。

好处 说明
统一障碍物模型 后续 Task 不需要专门理解每一种交通规则
便于路径/速度约束 虚拟墙可以参与 stop decision 或速度边界计算
规则与算法解耦 TrafficRule 负责规则判断,Task 负责轨迹生成

KeepClear 的完整链路如下:

OnLanePlanning::RunOnce()
        ↓
InitFrame()
        ↓
TrafficDecider::Execute()
        ↓
KeepClear::ApplyRule()
        ↓
ReferenceLineInfo 中出现虚拟障碍物 / stop decision
        ↓
Stage::Process()
        ↓
Task::Process()
        ↓
速度规划在虚拟墙前停车或减速

这说明 ApplyRule() 的执行早于 Task pipeline,因此它可以影响后续 LaneFollowPath、SpeedDecider、PiecewiseJerkSpeed 等任务。

数据流转

入口函数只是控制流,真正贯穿整个 Planning 的是数据对象。Frame、ReferenceLineInfo 和 PlanningContext 是阅读源码时最需要跟踪的三个对象。

Frame:一次规划周期的数据容器

Frame 贯穿整个周期:

RunOnce()
        ↓
InitFrame()
        ↓
TrafficDecider::Execute(frame, reference_line_info)
        ↓
Scenario::Process(planning_init_point, frame)
        ↓
Stage::Process(planning_init_point, frame)
        ↓
Task::Process(frame, reference_line_info)
特征 说明
生命周期 单个规划周期
内容 当前输入、车辆状态、参考线、障碍物、规划上下文入口
使用者 TrafficRule、Scenario、Stage、Task
作用 将本周期规划所需信息集中管理

ReferenceLineInfo:围绕参考线组织的规划结果

每条参考线对应一个 ReferenceLineInfo。规划过程中,它会不断被 TrafficRule 和 Task 修改。

ReferenceLineProvider 生成 ReferenceLine
        ↓
Frame 构造 ReferenceLineInfo
        ↓
TrafficRule::ApplyRule() 注入规则约束
        ↓
Path Task 写入 PathData
        ↓
Speed Task 写入 SpeedData
        ↓
Decision Task 写入障碍物决策
        ↓
最终合成轨迹
写入者 写入内容
TrafficRule 虚拟障碍物、stop decision、规则约束
Path Task PathData、路径边界、路径评估结果
Speed Task SpeedData、速度边界、速度曲线
Decision Task 障碍物横纵向决策
Planner 选择最终可驾驶参考线并合成轨迹

ReferenceLineInfo 是 TrafficRule、Task 和最终轨迹之间的重要中介。

PlanningContext:跨周期状态容器

Frame 管当前周期,PlanningContext 管跨周期状态。

Scenario::Enter()
        ↓
写入当前 stop_sign_overlap_id
        ↓
Stage::Process()
        ↓
读取 stop sign 状态
        ↓
状态持续多个周期
        ↓
Scenario::Exit()
        ↓
清理状态
使用场景 示例
Stop Sign 当前处理的 stop sign、是否已停车、是否正在 creep
Traffic Light 当前信号灯状态相关的规划状态
Pull Over 靠边停车阶段状态
Park and Go 泊车后起步状态

没有 PlanningContext,Scenario 很难表达“已经停过车”“正在 creep”“当前处理的是哪个 stop sign”这类状态。

完整调用链

保留最关键的入口函数后,Apollo 10 Planning 的运行链路可以写成:

OnLanePlanning::Init()
        ↓
TrafficDecider::Init()
        ↓
planner_->Init()
        ↓
OnLanePlanning::RunOnce()
        ↓
InitFrame()
        ↓
TrafficDecider::Execute()
        ↓
TrafficRule::ApplyRule()
        ↓
OnLanePlanning::Plan()
        ↓
planner_->Plan()
        ↓
Scenario::IsTransferable()
        ↓
Scenario::Enter()
        ↓
Scenario::Process()
        ↓
Stage::Process()
        ↓
Stage::ExecuteTaskOnReferenceLine()
        ↓
Task::Execute()
        ↓
Task::Process()
        ↓
Scenario::Exit()
        ↓
ADCTrajectory

对应到重点模块:

KeepClear
  Init()       → 加载规则配置
  ApplyRule()  → 生成虚拟墙 / stop decision

StopSignUnprotected
  IsTransferable() → 判断是否进入 Stop Sign 场景
  Enter()          → 写入 stop_sign_overlap_id
  Process()        → 调度当前 Stage
  Exit()           → 清理 stop sign 状态

StagePreStop / StageStop / StageCreep / StageIntersectionCruise
  Process()        → 执行阶段逻辑 + 调用 Task pipeline

LaneFollowPath
  Process()              → 路径生成主入口
  GetStartPointSLState() → 规划起点 SL 转换
  DecidePathBounds()     → 生成路径边界
  OptimizePath()         → 路径优化
  AssessPath()           → 路径评估
QQ:2219349024
最后更新于 2026-04-28