自动驾驶控制与规划——Project 6: A* Route Planning
目录
- 零、任务介绍
- 一、算法原理
- 1.1 A* Algorithm
- 1.2 启发函数
- 二、代码实现
- 三、结果分析
- 四、效果展示
- 4.1 Dijkstra距离
- 4.2 Manhatten距离
- 4.3 欧几里德距离
- 4.4 对角距离
- 五、后记
零、任务介绍
carla-ros-bridge/src/ros-bridge/carla_shenlan_projects/carla_shenlan_a_star_planner/src/Astar_searcher.cpp
中的 TODO部分- 对⽐分析不同的启发函数的计算耗时,每次运⾏后在终端内会打印计算时间,需要截图放⼊⽂档中上传。
一、算法原理
1.1 A* Algorithm
A*算法的步骤如下:
启发函数有如下两条性质
- 可采纳性:启发式函数h(n)必须永远不超过从节点n到终点的实际成本(即h(n) ≤ h*(n),其中h*(n)是从节点n到终点的真实成本)。
- 一致性:对于任意两个节点n和m,若存在一条从n到m的边,则启发式函数满足h(n) ≤ c(n, m) + h(m),其中c(n, m)是从n到m的边成本。
当启发函数满足上述两条性质时,A算法能够保证路径的最优性,但实际应用中,启发函数可能不满足性质。若启发函数过于保守(远小于h),则会导致搜索效率低下,若启发函数过于激进(远大于h*),则会导致算法得到次优路径。比较极端的情况有两种:
- h=0:Dijkstra
- h>>h*:Greedy
1.2 启发函数
本项目中使用了三种常见的启发函数,分别为曼哈顿距离、欧氏距离和对角距离。
记起点到终点沿直角坐标系坐标轴方向的距离分别为 ( d x , d y , d z ) (d_x, d_y, d_z) (dx,dy,dz),曼哈顿距离定义为每一步只能向坐标轴方向移动
d M a n h a t t e n = d x + d y + d z d_{Manhatten} = d_x + d_y + d_z dManhatten=dx+dy+dz
欧式距离定义为空间中的直线距离
d E u c l i d e a n = d x 2 + d y 2 + d z 2 d_{Euclidean} = \sqrt{d_x^2 + d_y^2 + d_z^2} dEuclidean=dx2+dy2+dz2
三维的对角距离可以理解为先按照x,y,z三个方向的距离中最短的一个构成的正方体的体对角线移动到和终点相同的平面内,然后在面内沿正方形的对角线移动,最后沿一个坐标轴方向移动。
d max = max ( d x , d y , d z ) d min = min ( d x , d y , d z ) d m i d = ( d x + d y + d z ) − d max − d min d D i a g o n a l = 3 d min + 2 ( d m i d − d min ) + ( d max − d m i d ) \begin{aligned} d_{\max} &= \max(d_x, d_y, d_z) \\ d_{\min} &= \min(d_x, d_y, d_z) \\ d_{mid} &= (d_x + d_y + d_z) - d_{\max} - d_{\min} \\ d_{Diagonal} &= \sqrt{3}d_{\min} + \sqrt{2} (d_{mid} - d_{\min}) + (d_{\max} - d_{mid}) \end{aligned} dmaxdmindmiddDiagonal=max(dx,dy,dz)=min(dx,dy,dz)=(dx+dy+dz)−dmax−dmin=3dmin+2(dmid−dmin)+(dmax−dmid)
二、代码实现
计算启发函数
double AstarPathFinder::getHeu(GridNodePtr node1, GridNodePtr node2) {/*choose possible heuristic function you wantManhattan, Euclidean, Diagonal, or 0 (Dijkstra)Remember tie_breaker learned in lecture, add it here ?*/bool tie_breaker = true;double distance_heuristic;Eigen::Vector3d node1_coordinate = node1->coord;Eigen::Vector3d node2_coordinate = node2->coord;// auto start = std::chrono::high_resolution_clock::now();Eigen::VectorXd abs_vec = (node1_coordinate - node2_coordinate).cwiseAbs();// **** TODO: Manhattan *****distance_heuristic = abs_vec.sum();// **** TODO: Euclidean *****distance_heuristic = abs_vec.norm();// **** TODO: Diagonal *****double min_coeff = abs_vec.minCoeff();double max_coeff = abs_vec.maxCoeff();double mid_coeff = abs_vec.sum() - min_coeff - max_coeff;distance_heuristic = 0.0;distance_heuristic += min_coeff * sqrt(3.0);distance_heuristic += (mid_coeff - min_coeff) * sqrt(2.0);distance_heuristic += (max_coeff - mid_coeff);if (tie_breaker) {distance_heuristic = distance_heuristic * (1.0 + 1.0 / 100.0);}return distance_heuristic;
}
A*路径搜索函数
void AstarPathFinder::AstarGraphSearch(Vector3d start_pt, Vector3d end_pt) {// ros::Time time_1 = ros::Time::now();rclcpp::Time time_1 = this->now(); // 计时开始时间// ->now();// rclcpp::Time end_mpc;// start_mpc = this->now();// RVIZ中点选的目标点坐标不一定是体素的中心// index of start_point and end_pointVector3i start_idx = coord2gridIndex(start_pt); // 坐标和体素地图索引互转Vector3i end_idx = coord2gridIndex(end_pt);goalIdx = end_idx;// 此处转换完成后的start_pt和end_pt是体素中心的坐标// position of start_point and end_pointstart_pt = gridIndex2coord(start_idx);end_pt = gridIndex2coord(end_idx);// Initialize the pointers of struct GridNode which represent start node and goal nodeGridNodePtr startPtr = new GridNode(start_idx, start_pt);GridNodePtr endPtr = new GridNode(end_idx, end_pt);// openSet is the open_list implemented through multimap in STL library// openSet的类型是std::multimap<double, GridNodePtr>openSet.clear();// currentPtr represents the node with lowest f(n) in the open_listGridNodePtr currentPtr = NULL;GridNodePtr neighborPtr = NULL;// put start node in open setstartPtr->gScore = 0; // 起点到起点的距离为0startPtr->fScore = getHeu(startPtr, endPtr); // 计算起点到终点的启发函数startPtr->id = 1;startPtr->coord = start_pt;openSet.insert(make_pair(startPtr->fScore, startPtr)); // 将起点加入openSetGridNodeMap[start_idx[0]][start_idx[1]][start_idx[2]]->id = 1; // 更新起点的状态为已加入openSetvector<GridNodePtr> neighborPtrSets; // 存储邻居节点vector<double> edgeCostSets;// this is the main loopwhile (!openSet.empty()) {/* Remove the node with lowest cost function from open set to closed set */// 将openSet中f最小的节点从openSet移动到closedSetcurrentPtr = openSet.begin()->second; // 从openSet移除当前节点openSet.erase(openSet.begin());Eigen::Vector3i current_index = currentPtr->index;GridNodeMap[current_index[0]][current_index[1]][current_index[2]]->id = -1; // 将当前节点标记为已扩展(ClosedSet)// if the current node is the goalif (currentPtr->index == goalIdx) {// ros::Time time_2 = ros::Time::now();rclcpp::Time time_2 = this->now(); // 计时结束时间terminatePtr = currentPtr;std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;std::cout << "[A*]{sucess} Time in A* is " << (time_2 - time_1).nanoseconds() / 1000000.0 << "ms, path cost is " << currentPtr->gScore * resolution << "m." << std::endl;;std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;return;}// get the successionAstarGetSucc(currentPtr, neighborPtrSets, edgeCostSets);/*For all unexpanded neigbors "m" of node "n"*/for (int i = 0; i < (int)neighborPtrSets.size(); i++) {/*Judge if the neigbors have been expandedIMPORTANT NOTE!!!neighborPtrSets[i]->id = -1 : expanded, equal to this node is in close setneighborPtrSets[i]->id = 1 : unexpanded, equal to this node is in open set*/neighborPtr = neighborPtrSets[i];if (neighborPtr->id == 0) // discover a new node, which is not in the closed set and open set{/*As for a new node, put neighbor in open set and record it*/neighborPtr->gScore = currentPtr->gScore + edgeCostSets[i];neighborPtr->fScore = neighborPtr->gScore + getHeu(neighborPtr, endPtr);neighborPtr->cameFrom = currentPtr;openSet.insert(make_pair(neighborPtr->fScore, neighborPtr));neighborPtr->id = 1;continue;} else if (neighborPtr->id == 1) // this node is in open set and need to judge if it needs to update{/*As for a node in open set, update it , maintain the openset ,and then put neighbor in open set and record it*/if (neighborPtr->gScore > (currentPtr->gScore + edgeCostSets[i])) {neighborPtr->gScore = currentPtr->gScore + edgeCostSets[i];neighborPtr->fScore = neighborPtr->gScore + getHeu(neighborPtr, endPtr);neighborPtr->cameFrom = currentPtr;}continue;} else // this node is in closed set{continue;}}}
}
三、结果分析
本次实验中使用的地图如下:
Dijkstra算法的结果为最优解,作为下面不同启发函数的对照。
目标点位置 | 规划时间(ms) | 路径长度(m) |
---|---|---|
右上 | 341.456 | 51.3047 |
右下 | 259.776 | 39.7067 |
左上 | 194.338 | 34.1463 |
Manhatten距离
目标点位置 | 规划时间(ms) | 路径长度(m) |
---|---|---|
右上 | 1.15485 | 51.8406 |
右下 | 3.29987 | 41.3636 |
左上 | 3.49432 | 36.7279 |
欧几里德距离
目标点位置 | 规划时间(ms) | 路径长度(m) |
---|---|---|
右上 | 5.21855 | 51.3047 |
右下 | 12.2433 | 39.8031 |
左上 | 1.33943 | 34.2426 |
对角距离
目标点位置 | 规划时间(ms) | 路径长度(m) |
---|---|---|
右上 | 3.0546 | 51.8406 |
右下 | 7.69398 | 41.9993 |
左上 | 4.67613 | 36.7276 |
对比上述启发函数,可以发现,欧氏距离作为其中最保守的距离,最终规划得到的路径长度接近Dijkstra算法得到的最优解,而曼哈顿距离和对角距离得到的路径长度略大于最优解,但是规划耗时较小。三种启发函数的A*规划时间都远远小于Dijkstra。
规划耗时:曼哈顿距离<对角距离<欧几里德距离
路径长度:欧几里德距离<对角距离 ≈ \approx ≈曼哈顿距离
四、效果展示
Dijkstra演示视频
自动驾驶控制与规划——Project 5(Dijkstra)
A*演示视频
自动驾驶控制与规划——Project 5(A*)
4.1 Dijkstra距离
4.2 Manhatten距离
Manhatten距离,目标点右上
Manhatten距离,目标点右下
Manhatten距离,目标点左上
4.3 欧几里德距离
欧几里德距离,目标点右上
欧几里德距离,目标点右下
欧几里德距离,目标点左上
4.4 对角距离
对角距离,目标点右上
对角距离,目标点右下
对角距离,目标点左上
五、后记
自动驾驶控制与规划是我在深蓝学院完完整整学完的第一门课,能坚持下来和我从无人机到航天器再到自动驾驶的心路历程转变密不可分,其中有的原因牵涉单位利益,不便多说。总而言之,无论在个人发展还是薪资待遇等方面,自动驾驶这个行业的前景还是非常可观的。
在短短一个多月的时间内熟练掌握自动驾驶必然是不可能的,但是这门课程让我了解了自动驾驶的常用动力学、控制、规划等基本原理和设计思路,已经成功入门了。学习过程中的代码全部开源在我的github上:AutonomousVehiclePnCCourseProjects,往期文章也可以在本专栏中阅读。
所谓师傅领进门,修行在个人,后续还需要进一步广泛的阅读相关文献,将我之前在多智能体博弈方面积累的经验和自动驾驶结合起来,努力推进自动驾驶技术和我个人的发展。
想说的就这么多,也衷心祝愿看到这里的朋友们在各自的领域坚持下去,早日实现理想!!!
相关文章:

自动驾驶控制与规划——Project 6: A* Route Planning
目录 零、任务介绍一、算法原理1.1 A* Algorithm1.2 启发函数 二、代码实现三、结果分析四、效果展示4.1 Dijkstra距离4.2 Manhatten距离4.3 欧几里德距离4.4 对角距离 五、后记 零、任务介绍 carla-ros-bridge/src/ros-bridge/carla_shenlan_projects/carla_shenlan_a_star_p…...

通俗易懂之线性回归时序预测PyTorch实践
线性回归(Linear Regression)是机器学习中最基本且广泛应用的算法之一。它不仅作为入门学习的经典案例,也是许多复杂模型的基础。本文将全面介绍线性回归的原理、应用,并通过一段PyTorch代码进行实践演示,帮助读者深入…...

[离线数仓] 总结二、Hive数仓分层开发
接 [离线数仓] 总结一、数据采集 5.8 数仓开发之ODS层 ODS层的设计要点如下: (1)ODS层的表结构设计依托于从业务系统同步过来的数据结构。 (2)ODS层要保存全部历史数据,故其压缩格式应选择压缩比率,较高的,此处选择gzip。 CompressedStorage - Apache Hive - Apac…...

页面顶部导航栏(Navbar)的功能(Navbar/index.vue)
这段代码是一个 Vue.js 组件,实现了页面顶部导航栏(Navbar)的功能。我将分块分析它的各个部分: 模板 (Template): <!-- spid-admin/src/layout/components/Navbar/index.vue --> <template><div class"navb…...
thinnkphp5.1和 thinkphp6以及nginx,apache 解决跨域问题
ThinkPHP 5.1 使用中间件设置响应头 ThinkPHP 5.1 及以上版本支持中间件,可以通过中间件统一设置跨域响应头。 步骤: 创建一个中间件文件,例如 CorsMiddleware.php: namespace app\middleware;class CorsMiddleware {public fu…...
vue2新增删除
(只是页面实现,不涉及数据库) list组件: <button click"onAdd">新增</button><el-table:header-cell-style"{ textAlign: center }" :cell-style"{ textAlign: center }":data&quo…...

测试ip端口-telnet开启与使用
前言 开发过程中我们总会要去测试ip通不通,或者ip下某个端口是否可以联通,为此我们可以使用telnet 命令来实现。 一、telnet 开启 可能有些人使用telnet报错,不是内部命令,可以如下开启: 1、打开控制面板ÿ…...
Python爬虫基础——XPath表达式
首先说一下这节内容在学习过程中存在的问题吧,在爬取百度网页文字时,出现了问题,就是通过表达式在网页搜索中可以定位,但是通过代码无法定位,请教了一位老师,他说是动态链接,目前这部分内容比较…...

ansible-性能优化
一. 简述: 搞过运维自动化工具的人,肯定会发现很多运维伙伴们经常用saltstack和ansible做比较,单从执行效率上来说,ansible确实比不上saltstack(ansible使用的是ssh,salt使用的是zeromq消息队列[暂没深入了解]),但其实…...
高等数学学习笔记 ☞ 一元函数微分的基础知识
1. 微分的定义 (1)定义:设函数在点的某领域内有定义,取附近的点,对应的函数值分别为和, 令,若可以表示成,则称函数在点是可微的。 【 若函数在点是可微的,则可以表达为】…...

前后端实现防抖节流实现
在前端和 Java 后端中实现防抖(Debounce)和节流(Throttle)主要用于减少频繁请求或事件触发对系统的压力。前端和后端的实现方式有些不同,以下是两种方法的具体实现: 1. 前端实现防抖和节流 在前端中&…...
【笔记】算法记录
1、求一个数的素因子(试除法) // 获取一个数的所有素因子 set<int> getPrimeFactors(int num) {set<int> primeFactors;for (int i 2; i * i < num; i) {while (num % i 0) {primeFactors.insert(i);num / i;}}if (num > 1) {prime…...
【网络云SRE运维开发】2025第2周-每日【2025/01/08】小测-【第8章 STP生成树协议】理论和实操解析
文章目录 一、选择题二、理论题三、实操题 【网络云SRE运维开发】2025第2周-每日【2025/01/08】小测-【第8章 STP生成树协议】理论和实操解析 一、选择题 生成树协议的主要作用是 B. 防止网络环路解释:生成树协议(STP)的主要目的是防止网络中…...
git push -f 指定分支
要将本地代码推送到指定的远程分支,你可以使用以下步骤和命令: 确认远程仓库: 确保你的本地仓库已经与远程仓库关联。你可以使用以下命令查看当前的远程仓库状态: git remote -v查看本地分支: 使用命令查看当前存在的本…...
CTF知识点总结(二)
异或注入:两个条件相同(同真或同假)即为假。 http://120.24.86.145:9004/1ndex.php?id1^(length(union)!0)-- 如上,如果union被过滤,则 length(union)!0 为假,那么返回页面正常。 2|0updatexml() 函数报…...
解决Edge打开PDF总是没有焦点
【问题描述】 使用Edge浏览器作为默认PDF阅读器打开本地PDF文件,Edge窗口总是不获得焦点,而是在任务栏以橙色显示,需要再手动点击一次才能查看文件内容。 本强迫症来治一治这个问题! 【解决方法】 GPT老师指出问题出在Edge的启动…...

69.基于SpringBoot + Vue实现的前后端分离-家乡特色推荐系统(项目 + 论文PPT)
项目介绍 在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用,其中包括家乡特色推荐的网络应用,在外国家乡特色推荐系统已经是很普遍的方式,不过国内的管理网站可能还处于起步阶段。家乡特色推荐系统采用java技术&…...

计算机视觉目标检测-DETR网络
目录 摘要abstractDETR目标检测网络详解二分图匹配和损失函数 DETR总结总结 摘要 DETR(DEtection TRansformer)是由Facebook AI提出的一种基于Transformer架构的端到端目标检测方法。它通过将目标检测建模为集合预测问题,摒弃了锚框设计和非…...

《自动驾驶与机器人中的SLAM技术》ch1:自动驾驶
目录 1.1 自动驾驶技术 1.2 自动驾驶中的定位与地图 1.1 自动驾驶技术 1.2 自动驾驶中的定位与地图 L2 在技术实现上会更倾向于实时感知,乃至可以使用感知结果直接构建鸟瞰图(bird eye view, BEV),而 L4 则依赖离线地图。 高精地…...

【UE5 C++课程系列笔记】23——多线程基础——AsyncTask
目录 概念 函数说明 注意事项 (1)线程安全问题 (2)依赖特定线程执行的任务限制 (3)任务执行顺序和时间不确定性 使用示例 概念 AsyncTask 允许开发者将一个函数或者一段代码逻辑提交到特定的线程去执…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...