源码分析
在 apollo/modules/planning/planner/planner.h 文件中,定义了2个类:Planner类和 PlannerWithReferenceLine类。
Planner
- Planner类是所有规划器的基类(纯虚类),重要函数有Init() 和 Plan()。
最重要的是virtual bool Plan(). 可以使得在运行时可以执行不同的planning算法。
- 参数1:planning起始点
- 参数2:当前帧
- 参数3:路径规划结果集,由一系列离散的轨迹点组成。
- 目的:根据历史行驶的一系列轨迹节点,并结合Perception模块+Prediction模块+Decision模块+Localization地图定位模块,来进行推算未来一段时间的行驶轨迹。本质是路径规划算法
Planner有三个成员变量: PlanningConfig, ScenarioManager, Scenario.
- PlanningConfig是定义在planning_config.proto中, 由planning_config.pb.txt文件配置。
PlannerWithReferenceLine
-
PlannerWithReferenceLine类 也是继承自 Planner类
-
重要函数有PlanOnReferenceLine()。
继承它的:
class LatticePlanner : public PlannerWithReferenceLine {
};
/**
* @class NaviPlanner
* @brief NaviPlanner is a planner based on real-time relative maps. It uses the
* vehicle's FLU (Front-Left-Up) coordinate system to accomplish tasks such as
* cruising, following, overtaking, nudging, changing lanes and stopping.
* Note that NaviPlanner is only used in navigation mode (turn on navigation
* mode by setting "FLAGS_use_navigation_mode" to "true") and do not use it in
* standard mode.
*/
class NaviPlanner : public PlannerWithReferenceLine {
};
/**
* @class PublicRoadPlanner
* @brief PublicRoadPlanner is an expectation maximization planner.
*/
class PublicRoadPlanner : public PlannerWithReferenceLine{
};
/**
* @class RTKReplayPlanner
* @brief RTKReplayPlanner is a derived class of Planner.
* It reads a recorded trajectory from a trajectory file and
* outputs proper segment of the trajectory according to vehicle
* position.
*/
class RTKReplayPlanner : public PlannerWithReferenceLine{
};
两大流程
初始化流程
- launch命令启动,由cyber的Init()触发。简化流程如下:
PlanningComponent::Init() //由Cyber系统初始化调用
OnLanePlanning::OnLanePlanning() //在PlanningComponent::Init()中调用,由use_navigation_mode标志位决定
OnLanePlanning::Init() //在PlanningComponent::Init()中调用
LatticePlanner::LatticePlanner() //在OnLanePlanning::init()中间接调用,由PlannerDispatcher::RegisterPlanners()直接调用
LatticePlanner::Init() //在OnLanePlanning::init()中调用
消息响应流程(运动规划)
由Cyber中的Timer触发,简化流程如下:
PlanningComponent::Proc() //由Cyber系统定时器调用
OnLanePlanning::RunOnce() //由PlanningComponent::Proc()调用
OnLanePlanning::Plan() //由OnLanePlanning::RunOnce()调用
LatticePlanner::Plan() //由OnLanePlanning::Plan()调用
LatticePlanner::PlanOnReferenceLine() //由LatticePlanner::Plan()调用,具体的基于参考线的规划工作
实现
4种规划器
这两个类都是纯虚类,那么谁来实现它们呢?
从上面可以看出,它们是用来生成规划轨迹的。简单来说就是规划轨迹的具体实现。
我们先看下Planner目录结构,一共实现了5种Planner:
open_space // open space planner
.
├── BUILD
├── navi_planner_dispatcher.cc
├── navi_planner_dispatcher.h
├── navi_planner_dispatcher_test.cc
├── on_lane_planner_dispatcher.cc
├── on_lane_planner_dispatcher.h
├── on_lane_planner_dispatcher_test.cc
├── planner_dispatcher.cc
├── planner_dispatcher.h
├── planner.h
├── lattice // lattice planner
├── navi // navi planner
├── public_road // public road planner
└── rtk // rtk planner
它们的结构如下图所示:
这5个Planner的说明如下表所示:每个规划器针对不同的场景和问题。
名称 | 加入版本 | 类型 | 说明 |
---|---|---|---|
RTKReplayPlanner | 1.0 | RTK |
|
PublicRoadPlanner | 1.5 | PUBLIC_ROAD |
|
LatticePlanner | 2.5 | LATTICE |
|
NaviPlanner | 3.0 | NAVI |
|
OpenSpacePlanner | 3.5 | OPEN_SPACE |
|
Apollo公开课里对两个较为成熟的Planner:EM Planner和Lattice Planner做了对比,我们可以一起来看一下:
EM Planner | Lattice Planner |
---|---|
横向纵向分开求解 横向纵向同时求解 | |
参数较多(DP/QP, Path/Speed) | 参数较少且统一 |
流程复杂 | 流程简单 |
单周期解空间受限 | 简单场景解空间较大 |
能适应复杂场景 | 适合简单场景 |
适合城市道路 | 适合高速场景 |
RTKReplayPlanner,PublicRoadPlanner,NaviPlanner,LatticePlanner,都实现了继承自Planner类的Plan()函数和继承自PlannerWithReferenceLine类的PlanOnReferenceLine()函数。在执行具体的规划任务时,都是在Plan()中调用PlanOnReferenceLine(),从而获得规划的轨迹结果。也就是说,最底层的规划方法,是在各规划器的PlanOnReferenceLine()中实现。
其大致流程如下:
- PlanningComponent在cyber中注册
- 选择Planning
- 根据不同的Dispatcher,分发Planner
具体类图如下所示:
补充
在apollo中,规划分为三种模式:
- OnLanePlanning(车道规划,可用于城区及高速公路各种复杂道路)
- NaviPlanning(导航规划,主要用于高速公路)
- OpenSpacePlanning (自主泊车和狭窄路段的掉头)
这三种模式负责调用上面算法
问题是代码中是怎么选择相应的planner呢?
Planner配置
每个Planner都会有一个字符串描述的唯一类型,在配置文件中(见下文)通过这个类型来选择相应的Planner。
Planner的配置文件路径是在/modules/planning/dag/planning.dag中指定的,相关内容如下:
// modules/planning/dag/planning.dag
config {
name: "planning"
config_file_path: "/apollo/modules/planning/conf/planning_config.pb.txt"
接下来我们可以看一下planning_config.pb.txt中的内容:
standard_planning_config {
planner_type: PUBLIC_ROAD
planner_public_road_config {
}
}
怎么在代码中调用上面配置,具体参见目录【分配器初始化】,可以看到默认使用PUBLIC_ROAD规划器。
要想使用LatticePlanner,需要修改启动配置参数,即将modules/planning/conf/planning_config.pb.txt中的planner_type:PUBLIC_ROAD改为planner_type:LATTICE