Planning模块包括决策和运动规划,Apollo6.0使用的主要规划方法为PublicRoadPlanner
,划分了场景状态机(scenarios
),在每个场景下依次运行stage
,在每个stage
下依次执行task
,通过可配置的stage
和task
实现程序的扩展性。
1 planning模块的主要类成员
PlanningComponent
继承于cyber::Component
,是事件触发机制,即当同时收到预测、底盘和定位信息时,planning模块才会运行。Prediction
和Localization
同样是事件触发的,Chassis
模块是时间触发的。预测模块的频率是10Hz,因此规划模块的频率也是10Hz。
class PlanningComponent final
: public cyber::Component<prediction::PredictionObstacles, canbus::Chassis,
localization::LocalizationEstimate> {
public:
PlanningComponent() = default;
~PlanningComponent() = default;
public:
bool Init() override;
bool Proc(const std::shared_ptr<prediction::PredictionObstacles>&
prediction_obstacles,
const std::shared_ptr<canbus::Chassis>& chassis,
const std::shared_ptr<localization::LocalizationEstimate>&
localization_estimate) override;
private:
void CheckRerouting();
bool CheckInput();
...
}
通过PlannerDispatcher
的配置来选择是OnLanPlanning
还是NaviPlanning
,在Apollo6.0
中NaviPlanning
已经被摒弃了。OnLanPlanning
是基于参考线在规划的,Apollo6.0
中默认的是PublicRoadPlanner
,是由EM Planner
演变而来。
2 planning模块主要的计算流程
PlanningComponent
继承于cyber::Component
,重载了Init()
和Proc()
函数。
2.1 Init()初始化函数
在Init()
函数中,会根据flags
配置来选择OnLanPlanning
还是NaviPlanning
,在两者构造时,会分别构造NaviPlannerDispatcher
和OnLanePlannerDispatcher
,在OnLanePlannerDispatcher
的构造中会通过载参来选择具体是哪一个Planner
(RTKReplayPlanner
、NaviPlanner
、LatticePlanner
和PublicRoadPlanner
)。在Apollo6.0
中,NaviPlanning
已经被摒弃,因此planning_base_
会实例化为其基类的对象OnLanePlanning
,在其初始化函数中,会进行一些载参和状态变量的初始化工作,获取HD Map
,开启参考线的多线程计算等。此外,设置各种reader
和writer
,类似于ROS
中的subscriber
和publisher
。
## palnning_component.cc
bool PlanningComponent::Init() {
injector_ = std::make_shared<DependencyInjector>();
// default: FLAGS_use_navigation_mode = false
// 此flags的设置在modules/coomon/configs/config_flags.h中
if (FLAGS_use_navigation_mode) {
planning_base_ = std::make_unique<NaviPlanning>(injector_);
} else {
// 在显式构造时planner_dispatcher_指向OnLanePlannerDispatcher
planning_base_ = std::make_unique<OnLanePlanning>(injector_);
}
// 在其父类Component::Initialize中会调用LoadConfigFiles(config),会根据ComponentConfig中
// 配置的config_file_path和flag_file_path进行载参
// 配置文件在 modules/planning/dag/planning.dag
// GetProtoConfig函数将config_file_path设置的文件赋值给config_
ACHECK(ComponentBase::GetProtoConfig(&config_))
<< "failed to load planning config file "
<< ComponentBase::ConfigFilePath();
// default: FLAGS_planning_offline_learning = false
if (FLAGS_planning_offline_learning ||
config_.learning_mode() != PlanningConfig::NO_LEARNING) {
if (!message_process_.Init(config_, injector_)) {
AERROR << "failed to init MessageProcess";
return false;
}
}
planning_base_->Init(config_);
routing_reader_ = node_->CreateReader<RoutingResponse>(
config_.topic_config().routing_response_topic(),
[this](const std::shared_ptr<RoutingResponse>& routing) {
AINFO << "Received routing data: run routing callback."
<< routing->header().DebugString();
std::lock_guard<std::mutex> lock(mutex_);
routing_.CopyFrom(*routing);
});
...
planning_writer_ = node_->CreateWriter<ADCTrajectory>(
config_.topic_config().planning_trajectory_topic());
..
return true;
}
2.2 Proc()计算函数
在此函数中,会进行输入数据的检查,主要的作用是进行决策规划,运行planning_base_->RunOnce(local_view_, &adc_trajectory_pb)
。如图2所示,会进行规划前数据的准备,比如参考线计算、时间戳对齐、航迹推算、轨迹拼接、障碍物处理、交通规则处理等等,然后进行规划,包括场景状态机跳转、stage
和task
的执行等等,在后续详细介绍。