当前位置: 首页 > news >正文

1.Apollo Planning 模块总结

1.整体架构

1.1 Planning 的上下游

在这里插入图片描述
Planning 模块的输入为:
HMI : 人机交互界面,也就是司机开启了自动驾驶
External Comand:转发的导航命令
Localization : 位置信息
Planning 模块的输出为:
control:输出方向盘和踏板

1.2 Planning 具体运行机制

在这里插入图片描述
Planning 模块由 Planning 基础库和 Planning 插件组成,基础库中包含了一些父类方法,比如 scenario.cc 是一种通用类,插件就是该类的子类
Planning component :是 Planning 模块的外部接口,Init 函数用于初始化组件
TrafficRule :主要运行 ApplyRule 函数对交通规则进行处理
PublicRoadPlanner : 对开放道路进行规划
Plan():使用场景机制进行规划
ScenarioManager:Update() 场景选择,切换
Scenario:具体轨迹规划

在这里插入图片描述

  • planning_interface_base 是 planning 插件的父类接口,也保存了 scenario tasks traffic_rules 的父类文件
  • palnner 文件夹中包含几种规划器
  • pnc_map 是用来生成参考线的插件
  • scenario 是场景插件
  • task 是任务插件
  • traffic_rule 是交通规则插件

1.3 工程架构

架构图

1.3.1 PlanningComponent::Init()

在这里插入图片描述
(1) 定义 Planning 的两种模式

if (FLAGS_use_navigation_mode) {planning_base_ = std::make_unique<NaviPlanning>(injector_);
} else {planning_base_ = std::make_unique<OnLanePlanning>(injector_);
}

一共有两种规划模式:
NaviPlanning 比较简单用于规划高速场景
OnLanePlanning 用于规划城市道路场,泊车
(2) 订阅模块的初始化
在 planing_component.cc 中对规划的输入和输出进行初始化,也就是调用他们的 Reader 和 Writer 方法
在这里插入图片描述

1.3.2 PlanningComponent::Proc()

在这里插入图片描述

1.4 Senario Stage Task 机制

在不同的 Planner 下有不同的 Scenario,在不同的 Scenario 下又会有不同的动作也就是 Stage ,在不同的 Stage 下又有不同的 task,他们之间相互调用
每个 Senario 使用双层状态机机制,双层状态机 Top Layer 是 Scenario 状态机,BottomLayer 是 Stage 状态机
最外层状态机:
Pull Over 靠边
Parking 泊车
Junction
在这里插入图片描述

2.Planning Base

在这里插入图片描述
这两个 Planner 主要保存在:
在这里插入图片描述
navi_planning 和 on_lane_planning 都继承 PlanningBase,这里以 on_lane_planning 为例

2.1 Init 函数

S1:启动参考线

// 参考线提供器,每 50ms 提供一次参考线
reference_line_provider_ = std::make_unique<ReferenceLineProvider>(injector_->vehicle_state(), reference_line_config);
reference_line_provider_->Start();

S2:创建 Planner
OnLanePlanning 继承于 public PlanningBase 在该类中有一个 std::shared_ptr<Planner> planner_; 的成员变量
Planner 是具体执行规划的类,在 planner 文件夹下 planner 一共有四种

在这里插入图片描述
每个 Planner 的作用如下:

  • rtk- 根据录制的轨迹来规划行车路线,
  • public_road- 开放道路的轨迹规划器,
  • lattice- 基于网格算法的轨迹规划器,
  • navi- 基于实时相对地图的规划器。
    这里根据不同 config 和 injector_ 创建不同的具体 Planner

首先创建具体 Planner

LoadPlanner();

在该函数中根据 config 创建具体的 Planner ,默认是 PublicRoadPlanner

void PlanningBase::LoadPlanner() {// Use PublicRoadPlanner as default Plannerstd::string planner_name = "apollo::planning::PublicRoadPlanner";if ("" != config_.planner()) {planner_name = config_.planner();planner_name = ConfigUtil::GetFullPlanningClassName(planner_name);}planner_ =cyber::plugin_manager::PluginManager::Instance()->CreateInstance<Planner>(planner_name);
}

Planner 的 Init 主要是对 Planner 的 injector 进行赋值

return planner_->Init(injector_, FLAGS_planner_config_path);
virtual apollo::common::Status Init(const std::shared_ptr<DependencyInjector>& injector,const std::string& config_path = "") {injector_ = injector;return common::Status::OK();
}

2.2 RunOnce 函数

S1:初始化 Frame

// 初始化Frame   
status = InitFrame(frame_num, stitching_trajectory.back(), vehicle_state);

S2:判断是否符合交通规则

// 判断是否符合交通规则for (auto& ref_line_info : *frame_->mutable_reference_line_info()) {auto traffic_status =traffic_decider_.Execute(frame_.get(), &ref_line_info);if (!traffic_status.ok() || !ref_line_info.IsDrivable()) {ref_line_info.SetDrivable(false);AWARN << "Reference line " << ref_line_info.Lanes().Id()<< " traffic decider failed";}}

S3:Plan
1.调用 OnLanePlanning 的 Plan 方法

// Plan
status = Plan(start_timestamp, stitching_trajectory, ptr_trajectory_pb);

2.在具体的 Plan 函数中,planner 调用自己的 Plan 函数

// 调用具体的 planner 执行
auto status = planner_->Plan(stitching_trajectory.back(), frame_.get(),ptr_trajectory_pb);

3.PublicRoadPlanner

OnLanePlanning 类使用的 planner_ 为 PublicRoadPlanner 这个 Planner 也是默认的 Planner

3.1 配置文件

所有 planner 的配置文件都在目标文件夹下的 conf 子文件夹下,定义了每个 Planner 的 scenario
在这里插入图片描述

2.2 PublicRoadPlanner::Init

会对 Senario Manager 进行初始化,也就是创建多个 scenario 放在一个 list 中

scenario_manager_.Init(injector, config_);

这个 planner_ 支持的场景如下:
在这里插入图片描述

2.2 Plan

在 Plan 中会对 Senario 进行更新,然后执行

Status PublicRoadPlanner::Plan(const TrajectoryPoint& planning_start_point,Frame* frame,ADCTrajectory* ptr_computed_trajectory) {// 更新场景scenario_manager_.Update(planning_start_point, frame);// 得到场景scenario_ = scenario_manager_.mutable_scenario();if (!scenario_) {return Status(apollo::common::ErrorCode::PLANNING_ERROR,"Unknown Scenario");}// 执行场景auto result = scenario_->Process(planning_start_point, frame);if (FLAGS_enable_record_debug) {auto scenario_debug = ptr_computed_trajectory->mutable_debug()->mutable_planning_data()->mutable_scenario();scenario_debug->set_scenario_plugin_type(scenario_->Name());scenario_debug->set_stage_plugin_type(scenario_->GetStage());scenario_debug->set_msg(scenario_->GetMsg());}// 更新执行的状态if (result.GetScenarioStatus() == ScenarioStatusType::STATUS_DONE) {// only updates scenario manager when previous scenario's status is// STATUS_DONEscenario_manager_.Update(planning_start_point, frame);} else if (result.GetScenarioStatus() == ScenarioStatusType::STATUS_UNKNOWN) {return Status(common::PLANNING_ERROR,result.GetTaskStatus().error_message());}return Status(common::OK, result.GetTaskStatus().error_message());
}

3.Scenario

场景介绍
有如下的 Scenario 场景,所有的 Senario 都用 SenarioManager 进行管理,所有的 Scenario 都会继承 Scenario 类重写里面的方法
在这里插入图片描述
在 SenarioManager 中有一个 list 用于存放需要运行的 senario

std::vector<std::shared_ptr<Scenario>> scenario_list_;

3.1 SenarioManager Init

将场景加载到 list 中
在这里插入图片描述

3.2 SenarioManager Update

在 scenario.cc 文件中 Process 函数会对 Stage 进行调用,首先先对当前 Stage 进行处理,然后更新 Stage 的状态,然后在创建下一个 Stage
在这里插入图片描述

在这个函数中主要是调用 ,判断是否可以进行该 scenario 的场景奇切换

scenario->IsTransferable(current_scenario_.get(), *frame)

3.3 Scenario IsTransferable

这里拿最简单的 Lane_follow 的场景来说,下面是判断是否可以 Lane follow 的条件:

bool LaneFollowScenario::IsTransferable(const Scenario* other_scenario,const Frame& frame) {if (!frame.local_view().planning_command->has_lane_follow_command()) {return false;}if (frame.reference_line_info().empty()) {return false;}if (other_scenario == nullptr) {return true;}return true;
}

lane follow 比较简单,下面是判断是否可以靠边停车的代码:
判断是否可以靠边停车
代码中主要判断 Reference 的点是否与红绿灯,停止等标志有 Overlap

auto first_encountered_overlaps =frame.reference_line_info().front().FirstEncounteredOverlaps();
// 如果处于红绿灯路口、具有停止标志、避让标志,则不允许靠边停车
if (pull_over_scenario) {static constexpr double kDistanceToAvoidJunction = 8.0;  // meterfor (const auto& overlap : first_encountered_overlaps) {if (overlap.first == ReferenceLineInfo::PNC_JUNCTION ||overlap.first == ReferenceLineInfo::SIGNAL ||overlap.first == ReferenceLineInfo::STOP_SIGN ||overlap.first == ReferenceLineInfo::YIELD_SIGN) {const double distance_to = overlap.second.start_s - dest_sl.s();const double distance_passed = dest_sl.s() - overlap.second.end_s;if ((distance_to > 0.0 && distance_to < kDistanceToAvoidJunction) ||(distance_passed > 0.0 &&distance_passed < kDistanceToAvoidJunction)) {pull_over_scenario = false;break;}}}
}

3.4 Scenario::Process

会运行具体的 Stage

auto ret = current_stage_->Process(planning_init_point, frame);

3.5 Enter

场景的进⼊函数继承于基类的Enter()函数,在⾸次进⼊场景前调⽤做⼀些预处理的⼯作,重置场景内变量。 ⽐如,在停⽌标记场景的Enter()函数,⾸先寻找参考线的停⽌标记id保存到上下⽂变量中,然后重置停⽌标记的全局变量。

3.6 Exit

场景的退出函数继承于基类的Exit()函数,在场景切出时会被调⽤,可以⽤来清除⼀些全局变量,⽐如停⽌标记场景的切出函数

bool PullOverScenario::Exit(Frame* frame) {injector_->planning_context()->mutable_planning_status()->clear_pull_over();return true;
}

3.7 Scenario::Init()

场景的初始化需要继承Scenario的Init()函数,场景基类的Init函数主要是从场景插件中加载场景的流⽔线,将加载的 Stage 实例保存到stage_pipeline_map_中
下面以 pull_over_scenario.cc 为例

if (!Scenario::Init(injector, name)) {AERROR << "failed to init scenario" << Name();return false;
}

如果场景⾃身还有配置⽂件,则可以调⽤Scenario::LoadConfig()函数加载场景⾃身的配置⽂件,保存到场景实例上下⽂变量中context_。

if (!Scenario::LoadConfig<ScenarioPullOverConfig>(&context_.scenario_config)) {AERROR << "fail to get config of scenario" << Name();return false;
}

4.Stage

每个场景⼜分为⼀个或者多个阶段(stage),每个阶段⼜由不同的任务(task)组成,

4.1 配置文件

当场景中存在先后顺序的业务逻辑时,可以将其划分成多个 Stage。⽐如,在红绿灯⽆保护左转场景中可以划分为三个阶段,第⼀个阶段是接近⾏驶到红绿灯停⽌线前的过程,第⼆个是红绿灯为绿灯时慢速观望的过程,第三个是对向直⾏⻋道通畅快速通过的过程

可以根据 Scenario 的 conf 文件中定义不同的 task 文件,
下面以 lane follow stage 为例:
对于 lane follow 来讲他会有 lane follow 的 stage,lane change ,lane borrow 等等的状态
在这里插入图片描述
下面是 stop sign unprotected Stage ,在开边停车的 Scenario 中就可以看出

stage: { name: "STOP_SIGN_UNPROTECTED_PRE_STOP" // 车辆到达停止牌之前type: "StopSignUnprotectedStagePreStop"enabled: truetask {name: "LANE_FOLLOW_PATH"type: "LaneFollowPath"}....
}
stage: {name: "STOP_SIGN_UNPROTECTED_STOP" // 到达车辆停止牌处如何处理type: "StopSignUnprotectedStageStop"enabled: truetask {name: "LANE_FOLLOW_PATH"type: "LaneFollowPath"}......
}
stage: {name: "STOP_SIGN_UNPROTECTED_CREEP" // 在停牌处停好车,起步,缓慢前行type: "StopSignUnprotectedStageCreep"enabled: truetask {name: "LANE_FOLLOW_PATH"type: "LaneFollowPath"}.......
}
stage: {name: "STOP_SIGN_UNPROTECTED_INTERSECTION_CRUISE" // 通过了停牌路口,进入路口巡航阶段type: "StopSignUnprotectedStageIntersectionCruise"enabled: truetask {name: "LANE_FOLLOW_PATH"type: "LaneFollowPath"......
}

4.2 Stage 的状态机

RUNNING:表示当前状态正在运⾏,Scenario:将继续维持当前阶段运⾏;
FINISHED:表示当前阶段完成,Scenario将会切⼊下⼀个阶段运⾏;
ERROR:表示当前规划存在严重故障。Scenario 会将其上报,主⻋将会刹停。

4.3 LaneFollowStage::Process

这里会对每一条参考线进行处理,这里会对每条参考线调用 task 方法

result =PlanOnReferenceLine(planning_start_point, frame, &reference_line_info);

4.4 LaneFollowStage::PlanOnReferenceLine

在这个函数中会逐个调用 task 执行

for (auto task : task_list_) {const double start_timestamp = Clock::NowInSeconds();const auto start_planning_perf_timestamp =std::chrono::duration<double>(std::chrono::system_clock::now().time_since_epoch()).count();ret.SetTaskStatus(task->Execute(frame, reference_line_info));

5.Task

task 可以理解为具体步骤,如 path 求解器的定义,boundery 函数的定义,以及速度求解器的定义等等
在这里插入图片描述

5.1 配置文件

在这里插入图片描述

5.2 Init()

5.3 Excute()

所有的 task 都会继承 Task 父类,每个子类实现 Execute 函数
在这里插入图片描述

6.Traffic rule

交通规则插件 TrafficRule 主要是在规划模块执行 Scenario 前对交通规则进⾏处理
在这里插入图片描述

6.1 配置文件

配置文件在每个文件夹下的 conf 子文件夹下

6.2 Init()

这里以 Crosswalk 类为例,Init 函数主要是将 conf 文件加载进来

// Load the config this task.
return TrafficRule::LoadConfig<CrosswalkConfig>(&config_);

6.3 Crosswalk::ApplyRule

traffic rule 的运行函数为 applyRule 函数,该函数会将判断结果保存在 reference line 中

...if (!FindCrosswalks(reference_line_info)) {injector_->planning_context()->mutable_planning_status()->clear_crosswalk();return Status::OK();}
...
}

7.参数设置

7.1 全局参数设置

全局参数定义在 plannning.conf 文件夹下
在这里插入图片描述
在 plannig_gflags.cc 中进行初始化,使用 FLAGS_XXX 的方式进行调用

http://www.lryc.cn/news/617755.html

相关文章:

  • 打破内网枷锁!TRAE SOLO + cpolar 让AI开发告别“孤岛困境”
  • 2025.8.6 图论(1)Solution
  • CI/CD渗透测试靶场
  • JavaEE初阶2.0
  • 基于MongoDB/HBase的知识共享平台的设计与实现
  • HBase BlockCache:LRU Cache
  • SQL179 每个6/7级用户活跃情况
  • 数学建模——灰色预测(GM11)
  • window显示驱动开发—创建多平面覆盖资源
  • 微服务架构下的精准测试与环境复制实践指南
  • PTE之路--04文
  • 使用Pytest进行接口自动化测试(三)
  • 集团型企业如何统一管控子公司权限?
  • 机器学习中数据集的划分难点及实现
  • 计算机网络:1、OSI参考模型和TCP/IP模型
  • scikit-learn/sklearn学习|岭回归解读
  • 高并发场景下分布式ID生成方案对比与实践指南
  • Mini-Omni: Language Models Can Hear, Talk While Thinking in Streaming
  • Mining of Real-world Hypergraphs part1-2 逐字翻译解读
  • react中父子数据流动和事件互相调用(和vue做比较)
  • 剑桥大学最新研究:基于大语言模型(LLM)的分子动力学模拟框架,是MD的GPT时刻还是概念包装?
  • 机器翻译:Bahdanau注意力和Luong注意力详解
  • HarmonyOS AI辅助编程工具(CodeGenie)概述
  • 鸿蒙flutter项目接入极光推送
  • golang包管理工具中 GOPATH 与 Go Modules 的区别总结
  • 新人如何简化学习Vue3文件
  • while循环结合列表或字典
  • YOLOv6深度解析:实时目标检测的新突破
  • 企业架构工具篇之ArchiMate的HelloWorld(2)
  • Eino中的两种应用模式:“单独使用”和“在编排中使用”