【Apollo学习笔记】——规划模块TASK之PATH_DECIDER
文章目录
- 前言
- PATH_DECIDER功能简介
- PATH_DECIDER相关配置
- PATH_DECIDER总体流程
- 路径决策代码流程及框架
- MakeStaticObstacleDecision
- PATH_DECIDER相关子函数
- 参考
前言
在Apollo星火计划学习笔记——Apollo路径规划算法原理与实践与【Apollo学习笔记】——Planning模块讲到……Stage::Process的PlanOnReferenceLine函数会依次调用task_list中的TASK,本文将会继续以LaneFollow为例依次介绍其中的TASK部分究竟做了哪些工作。由于个人能力所限,文章可能有纰漏的地方,还请批评斧正。
在modules/planning/conf/scenario/lane_follow_config.pb.txt配置文件中,我们可以看到LaneFollow所需要执行的所有task。
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
本文将继续介绍LaneFollow的第7个TASK——PATH_DECIDER
PATH_DECIDER功能简介
根据选出的路径给出对障碍物的决策
若是绕行的路径,则产生绕行的决策;若前方有障碍物阻塞,则产生停止的决策。
PATH_DECIDER相关配置
modules/planning/conf/planning_config.pb.txt
default_task_config: {task_type: PATH_DECIDERpath_decider_config{static_obstacle_buffer: 0.3}
}
modules/planning/proto/task_config.proto
//
// PathDeciderConfigmessage PathDeciderConfig {// buffer for static obstacles (meter)optional double static_obstacle_buffer = 1 [default = 0.3];
}
PATH_DECIDER总体流程
输入:
Status PathDecider::Process(const ReferenceLineInfo *reference_line_info,const PathData &path_data,PathDecision *const path_decision) {
输出:
路径决策的信息都保存到了path_decision中。
路径决策代码流程及框架

在Process函数主要功能是调用了MakeObjectDecision函数。而在MakeObjectDecision函数中调用了MakeStaticObstacleDecision函数。
路径决策的主要功能都在MakeStaticObstacleDecision中。这部分代码还是比较清晰的。
Status PathDecider::Process(const ReferenceLineInfo *reference_line_info,const PathData &path_data,PathDecision *const path_decision) {// skip path_decider if reused pathif (FLAGS_enable_skip_path_tasks && reference_line_info->path_reusable()) {return Status::OK();}std::string blocking_obstacle_id;if (reference_line_info->GetBlockingObstacle() != nullptr) {blocking_obstacle_id = reference_line_info->GetBlockingObstacle()->Id();}// 调用MakeObjectDecision函数if (!MakeObjectDecision(path_data, blocking_obstacle_id, path_decision)) {const std::string msg = "Failed to make decision based on tunnel";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}return Status::OK();
}bool PathDecider::MakeObjectDecision(const PathData &path_data,const std::string &blocking_obstacle_id,PathDecision *const path_decision) {// path decider的主要功能在MakeStaticObstacleDecision中if (!MakeStaticObstacleDecision(path_data, blocking_obstacle_id,path_decision)) {AERROR << "Failed to make decisions for static obstacles";return false;}return true;
}
MakeStaticObstacleDecision
获取frenet坐标系下的坐标
... ...// 1.获取frenet坐标下的path路径const auto &frenet_path = path_data.frenet_frame_path();if (frenet_path.empty()) {AERROR << "Path is empty.";return false;}... ...
根据障碍物做决策
... ...// 2.遍历每个障碍物,做决策for (const auto *obstacle : path_decision->obstacles().Items()) {const std::string &obstacle_id = obstacle->Id();const std::string obstacle_type_name =PerceptionObstacle_Type_Name(obstacle->Perception().type());ADEBUG << "obstacle_id[<< " << obstacle_id << "] type["<< obstacle_type_name << "]";... ...
如果障碍物不是静态或virtual,则跳过
// 2.1 如果障碍物不是静态的或者是virtual的,就跳过if (!obstacle->IsStatic() || obstacle->IsVirtual()) { // (stop fence,各种fence)continue;}
如果障碍物有了ignore/stop决策,则跳过
// 2.2 如果障碍物已经有 ignore/stop 决策,就跳过if (obstacle->HasLongitudinalDecision() &&obstacle->LongitudinalDecision().has_ignore() &&obstacle->HasLateralDecision() &&obstacle->LateralDecision().has_ignore()) {continue;}if (obstacle->HasLongitudinalDecision() &&obstacle->LongitudinalDecision().has_stop()) {// STOP decisioncontinue;}
如果障碍物挡住了路径,加stop决策
// 2.3 如果障碍物挡住了路径,加stop决策if (obstacle->Id() == blocking_obstacle_id &&!injector_->planning_context()->planning_status().path_decider().is_in_path_lane_borrow_scenario()) {// Add stop decisionADEBUG << "Blocking obstacle = " << blocking_obstacle_id;ObjectDecisionType object_decision;*object_decision.mutable_stop() = GenerateObjectStopDecision(*obstacle);path_decision->AddLongitudinalDecision("PathDecider/blocking_obstacle",obstacle->Id(), object_decision);continue;}
如果是clear-zone,跳过
// 2.4 如果是clear-zone,跳过if (obstacle->reference_line_st_boundary().boundary_type() ==STBoundary::BoundaryType::KEEP_CLEAR) {continue;}
如果障碍物不在路径上,跳过
// 2.5 如果障碍物不在路径上,跳过ObjectDecisionType object_decision;object_decision.mutable_ignore();const auto &sl_boundary = obstacle->PerceptionSLBoundary();if (sl_boundary.end_s() < frenet_path.front().s() ||sl_boundary.start_s() > frenet_path.back().s()) {path_decision->AddLongitudinalDecision("PathDecider/not-in-s",obstacle->Id(), object_decision);path_decision->AddLateralDecision("PathDecider/not-in-s", obstacle->Id(),object_decision);continue;}
nudge判断
- 如果距离静态障碍物距离太远,则忽略。
- 如果静态障碍物距离车道中心太近,则停止。
- 如果横向方向很近,则避开。
// 2.6 nudge判断,如果距离静态障碍物距离太远,则忽略。// 如果静态障碍物距离车道中心太近,则停止。// 如果横向方向很近,则避开。if (curr_l - lateral_radius > sl_boundary.end_l() ||curr_l + lateral_radius < sl_boundary.start_l()) {// 1. IGNORE if laterally too far away.path_decision->AddLateralDecision("PathDecider/not-in-l", obstacle->Id(),object_decision);} else if (sl_boundary.end_l() >= curr_l - min_nudge_l &&sl_boundary.start_l() <= curr_l + min_nudge_l) {// 2. STOP if laterally too overlapping.*object_decision.mutable_stop() = GenerateObjectStopDecision(*obstacle);if (path_decision->MergeWithMainStop(object_decision.stop(), obstacle->Id(),reference_line_info_->reference_line(),reference_line_info_->AdcSlBoundary())) {path_decision->AddLongitudinalDecision("PathDecider/nearest-stop",obstacle->Id(), object_decision);} else {ObjectDecisionType object_decision;object_decision.mutable_ignore();path_decision->AddLongitudinalDecision("PathDecider/not-nearest-stop",obstacle->Id(), object_decision);}} else {// 3. NUDGE if laterally very close.if (sl_boundary.end_l() < curr_l - min_nudge_l) { // &&// sl_boundary.end_l() > curr_l - min_nudge_l - 0.3) {// LEFT_NUDGEObjectNudge *object_nudge_ptr = object_decision.mutable_nudge();object_nudge_ptr->set_type(ObjectNudge::LEFT_NUDGE);object_nudge_ptr->set_distance_l(config_.path_decider_config().static_obstacle_buffer());path_decision->AddLateralDecision("PathDecider/left-nudge",obstacle->Id(), object_decision);} else if (sl_boundary.start_l() > curr_l + min_nudge_l) { // &&// sl_boundary.start_l() < curr_l + min_nudge_l + 0.3) {// RIGHT_NUDGEObjectNudge *object_nudge_ptr = object_decision.mutable_nudge();object_nudge_ptr->set_type(ObjectNudge::RIGHT_NUDGE);object_nudge_ptr->set_distance_l(-config_.path_decider_config().static_obstacle_buffer());path_decision->AddLateralDecision("PathDecider/right-nudge",obstacle->Id(), object_decision);}}
PATH_DECIDER相关子函数
GenerateObjectStopDecision主要用以生成停止决策。
ObjectStop PathDecider::GenerateObjectStopDecision(const Obstacle &obstacle) const {ObjectStop object_stop;// Calculate stop distance with the obstacle using the ADC's minimum turning radiusdouble stop_distance = obstacle.MinRadiusStopDistance(VehicleConfigHelper::GetConfig().vehicle_param());object_stop.set_reason_code(StopReasonCode::STOP_REASON_OBSTACLE);object_stop.set_distance_s(-stop_distance);// 停止时的参考位置const double stop_ref_s =obstacle.PerceptionSLBoundary().start_s() - stop_distance;const auto stop_ref_point =reference_line_info_->reference_line().GetReferencePoint(stop_ref_s);object_stop.mutable_stop_point()->set_x(stop_ref_point.x());object_stop.mutable_stop_point()->set_y(stop_ref_point.y());object_stop.set_stop_heading(stop_ref_point.heading());return object_stop;
}
对于停止距离的计算,会调用MinRadiusStopDistance函数,
modules/planning/common/obstacle.cc
double Obstacle::MinRadiusStopDistance(const common::VehicleParam& vehicle_param) const {if (min_radius_stop_distance_ > 0) {return min_radius_stop_distance_;}// 定义一个停止距离的缓冲区0.5mstatic constexpr double stop_distance_buffer = 0.5;// 获取最小安全转弯半径const double min_turn_radius = VehicleConfigHelper::MinSafeTurnRadius();// 计算横向距离double lateral_diff =vehicle_param.width() / 2.0 + std::max(std::fabs(sl_boundary_.start_l()),std::fabs(sl_boundary_.end_l()));const double kEpison = 1e-5;lateral_diff = std::min(lateral_diff, min_turn_radius - kEpison);// 勾股定理求得停止距离double stop_distance =std::sqrt(std::fabs(min_turn_radius * min_turn_radius -(min_turn_radius - lateral_diff) *(min_turn_radius - lateral_diff))) +stop_distance_buffer;// 减掉车辆前端到后轴中心的距离stop_distance -= vehicle_param.front_edge_to_center();// 限幅stop_distance = std::min(stop_distance, FLAGS_max_stop_distance_obstacle); // 10.0stop_distance = std::max(stop_distance, FLAGS_min_stop_distance_obstacle); // 6.0return stop_distance;
}
计算示意图如下:

modules/common/configs/vehicle_config_helper.cc
double VehicleConfigHelper::MinSafeTurnRadius() {const auto ¶m = vehicle_config_.vehicle_param();double lat_edge_to_center =std::max(param.left_edge_to_center(), param.right_edge_to_center());double lon_edge_to_center =std::max(param.front_edge_to_center(), param.back_edge_to_center());return std::sqrt((lat_edge_to_center + param.min_turn_radius()) *(lat_edge_to_center + param.min_turn_radius()) +lon_edge_to_center * lon_edge_to_center);
}
MinSafeTurnRadius这段函数是获取当车辆以最大转向角转弯时的最大安全转弯半径。具体计算参考下图:

A , B , C , D A,B,C,D A,B,C,D分别是车辆的四个角, X O XO XO是车辆的最小转弯半径VehicleParam.min_turn_radius(), X X X与 A D AD AD之间的距离是左边缘到中心的距离left_edge_to_center, X X X与 A B AB AB之间的距离是前边缘到中心的距离front_edge_to_center。最大安全转弯半径则是 A O AO AO,定义中心到横向边缘最长的距离为 l l a t l_{lat} llat,到纵向边缘最长的距离为 l l o n l_{lon} llon, A O AO AO计算公式如下:
A O = ( X O + l l a t ) 2 + l l o n 2 AO=\sqrt{(XO+l_{lat})^2+{l_{lon}}^2} AO=(XO+llat)2+llon2
个人感觉这么做是为了获得足够的安全冗余量。
参考
[1] 路径决策
相关文章:
【Apollo学习笔记】——规划模块TASK之PATH_DECIDER
文章目录 前言PATH_DECIDER功能简介PATH_DECIDER相关配置PATH_DECIDER总体流程路径决策代码流程及框架MakeStaticObstacleDecision PATH_DECIDER相关子函数参考 前言 在Apollo星火计划学习笔记——Apollo路径规划算法原理与实践与【Apollo学习笔记】——Planning模块讲到……S…...
Lua学习(二)
Lua基础学习 7. lua函数8. lua运算符8.1 算数运算符8.2 关系运算符8.3 逻辑运算符8.4 其他运算符 9. lua字符串9.1 字符串格式化9.2 匹配模式 10. lua数组11. lua迭代器11.1 Lua table 12. lua 模块12.1 加载机制12.2 C 包 接着上一篇的内容。Lua学习(一)…...
制作鲜花商城小程序的详细步骤
如果你是一个新手商家,想要进入鲜花团购市场,但是不知道如何制作一个小程序商城,那么这篇文章就是为你准备的。以下是制作鲜花团购小程序商城的详细步骤: 1. 登录乔拓云平台后台,进入商城管理页面 首先,你需…...
Ubuntu20以上高版本如何安装低版本GCC
安装了Ubuntu 20.04之后,通过命令行 sudo apt-get install build-essential安装gcc,再通过命令行 gcc -v可查看gcc版本为gcc13 如果想用低版本的gcc,比如gcc4.8,尝试输入命令 sudo apt-get install gcc-4.8会提示找不到gcc4.8的…...
context.WithCancel()的使用
“ WithCancel可以将一个Context包装为cancelCtx,并提供一个取消函数,调用这个取消函数,可以Cancel对应的Context Go语言context包-cancelCtx[1] 疑问 context.WithCancel()取消机制的理解[2] 父母5s钟后出门,倒计时,父母在时要学习,父母一走…...
vue3中引入百度地图
话不多说直接开干 1.第一种方式 百度地图地址 打开 https://lbsyun.baidu.com/index.php?title%E9%A6%96%E9%A1%B5 然后点进去地图 然后再这个功能里面选择一个地图,然后跳转页面 然后一直下滑 滑到底部 点击这个 跳转到这个页面 然后点击进入demo这个 然后到这个…...
【Linux-Day8- 进程替换和信号】
进程替换和信号 问题引入 我们发现 终端输入的任意命令的父进程都是bash,这是因为Linux系统是用fork()复制出子进程,然后在子进程中调用替换函数进行进程替换,实现相关命令。 (1) exec 系列替换过程:pcb 使用以前的只…...
日志文件之间关系和介绍及应用
1.常用日志框架代码举例 Log4j: Log4j是Java中广泛使用的日志框架之一。它提供了灵活的配置选项和丰富的功能,支持日志级别、日志输出目标等。Log4j有1.x版本和2.x版本,其中Log4j 2.x是对1.x的升级和扩展。 Logback: Logback是由Log4j创始人设计的Log4…...
mac电脑屏幕录制Berrycast Mac屏幕录制软件
Berrycast是一款为Mac设计的优秀屏幕录制软件,它让屏幕录制变得简单而高效。以下是Berrycast的一些主要特点: 简单的用户界面:Berrycast拥有直观和简洁的用户界面,使得用户可以轻松上手。高质量的视频输出:Berrycast能…...
机器学习笔记之最优化理论与方法(一)最优化问题概述
机器学习笔记之最优化理论与方法——最优化问题概述 引言什么是最优化问题最优化问题的基本形式最优化问题的分类各分类最优化问题的数学表达约束优化VS无约束优化线性规划VS非线性规划连续优化VS离散优化单目标优化VS多目标优化 引言 从本节开始,将对最优化理论与…...
【ES5新特性一】 严格模式语法变化、全局的JSON对象、编码和解码的方法
前言 ECMAScript 和 JavaScript 的关系 一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? 要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准…...
Java【手撕滑动窗口】LeetCode 3. “无重复字符的最长子串“, 图文详解思路分析 + 代码
文章目录 前言一、长度最小子数组1, 题目2, 思路分析3, 代码 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: 📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 📗 Java数据结构: 顺序表, 链…...
学习哈哈哈哈
# 零、学习计划 * 数据库相关 * 索引 * [我以为我对数据库索引很了解,直到我遇到了阿里面试官 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/107487215) * [给我一分钟,让你彻底明白MySQL聚簇索引和非聚簇索引 - 知乎 (zhihu.com)](ht…...
05-基础例程5
基础例程5 1、超声波测距 实验介绍 HC-SR04超声波传感器是一款测量距离的传感器。其原理是利用声波在遇到障碍物反射接收结合声波在空气中传播的速度计算的得出。 外观 管脚功能的定义 VCC:供电电源;Trig:触发信号;Echo&a…...
双基证券:预计未来还会有更多政策来吸引增量资金
双基证券表示,8月27日,活泼资本商场五大方针出台:证券交易印花税折半征收;阶段性收紧IPO节奏;上市房企再融资不受破发、破净和亏本限制;标准控股股东与实际操控人减持行为;融资保证金最低份额由…...
前端:html实现页面切换、顶部标签栏,类似于浏览器的顶部标签栏(完整版)
效果 代码 <!DOCTYPE html> <html><head><style>/* 左侧超链接列表 */.link {display: block;padding: 8px;background-color: #f2f2f2;cursor: pointer;}/* 顶部标签栏 */#tabsContainer {width:98%;display: flex;align-items: center;overflow-x: …...
强化自主可控,润开鸿发布基于RISC-V架构的开源鸿蒙终端新品
2023 RISC-V中国峰会于8月23日至25日在北京召开,峰会以“RISC-V生态共建”为主题,结合当下全球新形势,把握全球新时机,呈现RISC-V全球新观点、新趋势。本次大会邀请了RISC-V国际基金会、业界专家、企业代表及社区伙伴等共同探讨RISC-V发展趋势与机遇,吸引超过百余家业界企业、高…...
软件设计师知识点·1
控制器: (1)指令寄存器(IR) : CPU执行一条指令时,从内存储器取到缓冲寄存器中,再送入IR暂存; (2)程序计数器(PC): 将要执行的下一条指令的地址; (3)地址寄存器(IR): 当前CPU所访问的内存单元地址; (4)指令译码器(ID): 对指令中的操作码字段进行分析解释; 多核CPU可以满足用户…...
修改Jupyter Notebook默认打开路径
这里我是重新下载的anaconda,打开Jupyter之后是默认在C盘的一个路径的,现在我们就来修改一下它的一个默认打开路径,这样在我们后续学习过程中,可以将ipynb后缀的文件放在这个目录下就能查看了。 1、先打开Anaconda Prompt&#x…...
经典卷积网络
目录 一、经典神经网络出现的时间线编辑 二、LeNet 三、AlexNet 四、VGGNet 五、InceptionNet 六、ResNet 总结: 一、经典神经网络出现的时间线 二、LeNet 背景:LeNet由Yann LeCun于1998年提出,卷积网络开篇之作。 解释࿱…...
当 SpringBoot 请求踏上“七层之旅”:OSI 模型与你的每一行代码
你在 Controller 里写了一个 GetMapping,浏览器敲下回车,数据就回来了。 可你有没有想过,这短短几十毫秒里,你的数据经历了多少次“变装”和“安检”? 从 HTTP 报文到 TCP 段,再到 IP 包、以太网帧——每一…...
如何用四探针精确测量半导体电阻率
在半导体行业中,准确测量晶圆电阻率是材料研发和制程质量控制的关键环节。随着工艺节点不断缩小,器件对电性一致性的要求日益严格,仅靠经验无法满足现代制造的需求。因此工程师们大量采用四探针方法对电阻率进行高精度测量。相比传统测量方式…...
手把手教你用示波器抓取Intel CPU的SVID时序(附读写判定与Intel送测指南)
实战指南:利用示波器精准解析Intel CPU的SVID通信时序 当一块新设计的服务器主板首次上电时,电源管理系统的稳定性往往决定了整个平台的可靠性。作为硬件工程师,我们常常需要直面这样的场景:主板虽然能点亮,但CPU与电压…...
如何在3分钟内搭建Excel MCP Server:无需安装Microsoft Excel的终极指南
如何在3分钟内搭建Excel MCP Server:无需安装Microsoft Excel的终极指南 【免费下载链接】excel-mcp-server A Model Context Protocol server for Excel file manipulation 项目地址: https://gitcode.com/gh_mirrors/ex/excel-mcp-server 还在为没有Micros…...
别再乱建索引了!用进销存系统的真实案例,聊聊MySQL索引优化与视图设计的那些坑
MySQL索引优化与视图设计实战:进销存系统的避坑指南 当你的进销存系统从几百条记录增长到数百万条时,那些曾经瞬间完成的查询开始变得迟缓,收银台前的顾客开始不耐烦地敲击柜台,而老板的脸色也随着系统响应时间的增加而越发阴沉。…...
Perplexity体育搜索冷启动难题终结方案:从数据源注册到热点事件自动聚类,全程12分钟极速上线(含CLI脚本)
更多请点击: https://intelliparadigm.com 第一章:Perplexity体育新闻搜索 Perplexity 是一款以实时网络检索与精准问答能力见长的 AI 搜索工具,其在体育新闻领域的应用显著区别于传统搜索引擎——它不依赖静态索引,而是动态调用…...
从Pooling到MetaFormer:深入解析PoolFormer如何用极简算子重塑视觉Transformer架构
1. 为什么说PoolFormer是Transformer的"极简主义革命"? 第一次看到PoolFormer的论文时,我正坐在咖啡馆调试一个复杂的Vision Transformer模型。当读到"用平均池化替代注意力机制"的设计时,差点把咖啡喷在键盘上——这简…...
Bilibili视频转文字完整指南:一键将B站视频转为可编辑文字稿
Bilibili视频转文字完整指南:一键将B站视频转为可编辑文字稿 【免费下载链接】bili2text Bilibili视频转文字,一步到位,输入链接即可使用 项目地址: https://gitcode.com/gh_mirrors/bi/bili2text 你是否曾为观看Bilibili视频时需要做…...
wlnmp一键安装包260520更新:多软件版本升级,支持多系统架构快速部署
wlnmp一键安装包更新:多软件版本升级wlnmp一键安装包在260520迎来更新,此次更新涉及多个重要软件的版本升级,包括nginx1.30.1、php8.2.31、php8.3.31等多个php版本,以及MySQL8.0.46、MySQL8.4.9。这些软件版本的更新,为…...
CAPL编程从入门到精通:车载网络自动化测试与仿真实战指南
1. 从零开始认识CAPL:不只是CANoe里的脚本 如果你正在从事汽车电子、车载网络相关的开发或测试工作,那么“CAPL”这个名字对你来说一定不陌生。它常常和Vector公司的CANoe、CANalyzer等工具绑定出现,被很多人简单地理解为“CANoe里的脚本语言…...
