算法的基本架构
我们在最开始直接给出规划决策算法架构框图,然后一一介绍每个框图结构的细节:
- 模块的入口是 PlanningComponent,在 Cyber 中注册模块,订阅和发布消息,并且注册对应的 Planning 类。
- Planning 的过程之前是定时器触发,即每隔一段固定的时间执行一次,现已经改为事件触发,即只要收集完成对应 TOPIC 的消息,就会触发执行,这样的好处是提高的实时性。
- Planning 类主要实现了 2 个功能,一个是启动 ReferenceLineProvider 来提供参考线,后面生成的轨迹都是在参考线的基础上做优化,ReferenceLineProvider 启动了一个单独的线程,每隔 50ms 执行一次,和 Planning 主流程并行执行。Planning 类另外的一个功能是执行 Planning 主流程。
- Planning 主流程先是选择对应的 Planner,我们这里主要分析 PublicRoadPlanner,在配置文件中定义了 Planner 支持的场景(Scenario),把规划分为具体的几个场景来执行,每个场景又分为几个阶段(Stage),每个阶段会执行多个任务(Task),任务执行完成后,对应的场景就完成了。不同场景间的切换是由一个状态机(ScenarioDispatch)来控制的。规划控制器根据 ReferenceLineProvider 提供的参考线,在不同的场景下做切换,生成一条车辆可以行驶的轨迹,并且不断重复上述过程直到到达目的地。
PlanningComponent
规划模块的入口是 PlanningComponent。PlanningComponent 的两大核心函数是 Init 和 Proc.
在初始化中,我们可以看到如下的代码,FLAGS_use_navigation_mode 的标志位直接决定了我们等等实例化的 planning_base_ 的具体 planner,到底是 NaviPlanning 还是 OnLanePlanning:
if (FLAGS_use_navigation_mode) {planning_base_ = std::make_unique<NaviPlanning>(injector_);} else {planning_base_ = std::make_unique<OnLanePlanning>(injector_);}
其实在更早的版本中有更多选项,7.0 为什么简化了呢?
if (FLAGS_open_space_planner_switchable) {planning_base_ = std::make_unique<OpenSpacePlanning>();} else {if (FLAGS_use_navigation_mode) {planning_base_ = std::make_unique<NaviPlanning>();} else {planning_base_ = std::make_unique<OnLanePlanning>();}}
而在 Proc(…)函数中,最核心部分则是调用对应 planner 的 RunOnce 函数:
planning_base_->RunOnce(local_view_, &adc_trajectory_pb);
接下来的具体例子中我们都以这个 planing_base_被实例化为 OnLanePlanning 作为前提。
下图是几个规划器的结构。这里我们要讨论的是 PublicRoadPlanner,这里只是简单的给出结构,后面的对应小节会具体介绍函数中如何运行和管理。
OnLanePlanning
RunOnce 函数的三大重要函数是:
// 初始化frame
status = InitFrame(frame_num, stitching_trajectory.back(), vehicle_state);
......
//进行traffic decider
for (auto& ref_line_info : *frame_->mutable_reference_line_info()) {TrafficDecider traffic_decider;traffic_decider.Init(traffic_rule_configs_);auto traffic_status =traffic_decider.Execute(frame_.get(), &ref_line_info, injector_);if (!traffic_status.ok() || !ref_line_info.IsDrivable()) {ref_line_info.SetDrivable(false);AWARN << "Reference line " << ref_line_info.Lanes().Id()<< " traffic decider failed";}}......//plan主函数
status = Plan(start_timestamp, stitching_trajectory, ptr_trajectory_pb);
Plan
这个 plan 函数中主要核心则是:
auto status = planner_->Plan(stitching_trajectory.back(), frame_.get(),ptr_trajectory_pb);
而这个 planner_则是通过 DispatchPlanner 来指定:
// dispatch plannerplanner_ = planner_dispatcher_->DispatchPlanner(config_, injector_);
具体来说就是:
std::unique_ptr<Planner> OnLanePlannerDispatcher::DispatchPlanner(const PlanningConfig& planning_config,const std::shared_ptr<DependencyInjector>& injector) {return planner_factory_.CreateObject(planning_config.standard_planning_config().planner_type(0), injector);
}
//其中这个config是:
standard_planning_config {planner_type: PUBLIC_ROADplanner_public_road_config {}
}
Plan 函数中主要的几个步骤是:
Status PublicRoadPlanner::Plan(const TrajectoryPoint& planning_start_point,Frame* frame,ADCTrajectory* ptr_computed_trajectory) {//更新当前的scenarioscenario_manager_.Update(planning_start_point, *frame);// 获取当前场景scenario_ = scenario_manager_.mutable_scenario();// 执行当前场景的任务auto result = scenario_->Process(planning_start_point, frame);。。。。。。
}
Scenario 是 apollo 决策规划算法中的重要概念,apollo 可以应对自动驾驶所面临的不同道路场景,都是通过 Scenario 统一注册与管理;Scenario 通过一个有限状态机来选择不同场景。
更新当前的 scenario
scenario_manager_.Update(planning_start_point, *frame);
我们来看 Update,做了这么几件事:
void ScenarioManager::Update(const common::TrajectoryPoint& ego_point,const Frame& frame) {ACHECK(!frame.reference_line_info().empty());// 保留当前帧Observe(frame);// 场景分发 ScenarioDispatch(frame);
}
这里能够提供的场景库包括:
enum ScenarioType {LANE_FOLLOW = 0; // default scenario// intersection involvedBARE_INTERSECTION_UNPROTECTED = 2;STOP_SIGN_PROTECTED = 3;STOP_SIGN_UNPROTECTED = 4;TRAFFIC_LIGHT_PROTECTED = 5;TRAFFIC_LIGHT_UNPROTECTED_LEFT_TURN = 6;TRAFFIC_LIGHT_UNPROTECTED_RIGHT_TURN = 7;YIELD_SIGN = 8;// parkingPULL_OVER = 9;VALET_PARKING = 10;EMERGENCY_PULL_OVER = 11;EMERGENCY_STOP = 12;// miscNARROW_STREET_U_TURN = 13;PARK_AND_GO = 14;// learning model sampleLEARNING_MODEL_SAMPLE = 15;// turn aroundDEADEND_TURNAROUND = 16;
}
获取当前场景
//获取当前场景
Scenario* mutable_scenario() { return current_scenario_.get(); }
执行当前场景的任务
//通过Process函数进行场景运行
Scenario::ScenarioStatus Scenario::Process(const common::TrajectoryPoint& planning_init_point, Frame* frame)
场景下有 stage, 运行 stage 的 Process 函数:
auto ret = current_stage_->Process(planning_init_point, frame);
我们拿 LaneFollowStage 为例,则运行他的每个 task:
//首先进入函数:
auto cur_status =PlanOnReferenceLine(planning_start_point, frame, &reference_line_info);//然后是在PlanOnReferenceLine函数中遍历每个task
for (auto* task : task_list_) {const double start_timestamp = Clock::NowInSeconds();ret = task->Execute(frame, reference_line_info);。。。。。。}
以 lane follow 这个 stage 为例,这个 task list 包括:
scenario_type: LANE_FOLLOW
stage_type: LANE_FOLLOW_DEFAULT_STAGE
stage_config: {stage_type: LANE_FOLLOW_DEFAULT_STAGEenabled: truetask_type: LANE_CHANGE_DECIDERtask_type: PATH_REUSE_DECIDERtask_type: PATH_LANE_BORROW_DECIDERtask_type: PATH_BOUNDS_DECIDERtask_type: PIECEWISE_JERK_PATH_OPTIMIZERtask_type: PATH_ASSESSMENT_DECIDERtask_type: PATH_DECIDERtask_type: RULE_BASED_STOP_DECIDERtask_type: SPEED_BOUNDS_PRIORI_DECIDERtask_type: SPEED_HEURISTIC_OPTIMIZERtask_type: SPEED_DECIDERtask_type: SPEED_BOUNDS_FINAL_DECIDERtask_type: PIECEWISE_JERK_SPEED_OPTIMIZER# task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZERtask_type: RSS_DECIDER