自动驾驶控制与规划——Project 6: A* Route Planning

目录

  • 零、任务介绍
  • 一、算法原理
    • 1.1 A* Algorithm
    • 1.2 启发函数
  • 二、代码实现
  • 三、结果分析
  • 四、效果展示
    • 4.1 Dijkstra距离
    • 4.2 Manhatten距离
    • 4.3 欧几里德距离
    • 4.4 对角距离
  • 五、后记

零、任务介绍

  1. carla-ros-bridge/src/ros-bridge/carla_shenlan_projects/carla_shenlan_a_star_planner/src/Astar_searcher.cpp
    中的 TODO部分
  2. 对⽐分析不同的启发函数的计算耗时,每次运⾏后在终端内会打印计算时间,需要截图放⼊⽂档中上传。

一、算法原理

1.1 A* Algorithm

A*算法的步骤如下:
在这里插入图片描述

启发函数有如下两条性质

  1. 可采纳性:启发式函数h(n)必须永远不超过从节点n到终点的实际成本(即h(n) ≤ h*(n),其中h*(n)是从节点n到终点的真实成本)。
  2. 一致性:对于任意两个节点n和m,若存在一条从n到m的边,则启发式函数满足h(n) ≤ c(n, m) + h(m),其中c(n, m)是从n到m的边成本。

当启发函数满足上述两条性质时,A算法能够保证路径的最优性,但实际应用中,启发函数可能不满足性质。若启发函数过于保守(远小于h),则会导致搜索效率低下,若启发函数过于激进(远大于h*),则会导致算法得到次优路径。比较极端的情况有两种:

  1. h=0:Dijkstra
  2. 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)dmaxdmin=3 dmin+2 (dmiddmin)+(dmaxdmid)

二、代码实现

计算启发函数

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.45651.3047
右下259.77639.7067
左上194.33834.1463

Manhatten距离

目标点位置规划时间(ms)路径长度(m)
右上1.1548551.8406
右下3.2998741.3636
左上3.4943236.7279

欧几里德距离

目标点位置规划时间(ms)路径长度(m)
右上5.2185551.3047
右下12.243339.8031
左上1.3394334.2426

对角距离

目标点位置规划时间(ms)路径长度(m)
右上3.054651.8406
右下7.6939841.9993
左上4.6761336.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,往期文章也可以在本专栏中阅读。
所谓师傅领进门,修行在个人,后续还需要进一步广泛的阅读相关文献,将我之前在多智能体博弈方面积累的经验和自动驾驶结合起来,努力推进自动驾驶技术和我个人的发展。
想说的就这么多,也衷心祝愿看到这里的朋友们在各自的领域坚持下去,早日实现理想!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/504159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

闲谭SpringBoot--ShardingSphere分库分表探究

文章目录 1. 背景2. 创建数据库3. 修改yml配置文件4. 分片算法类5. 测试6 小结 1. 背景 接上文&#xff0c;我们对日志表&#xff0c;进行了按月的分表&#xff0c;这样每个月几百万条数据量还是扛得住的。 但是如果数据再多呢&#xff0c;除了提高硬件性能&#xff0c;还有一…

IT面试求职系列主题-Jenkins

想成功求职&#xff0c;必要的IT技能一样不能少&#xff0c;先说说Jenkins的必会知识吧。 1) 什么是Jenkins Jenkins 是一个用 Java 编写的开源持续集成工具。它跟踪版本控制系统&#xff0c;并在发生更改时启动和监视构建系统。 2&#xff09;Maven、Ant和Jenkins有什么区别…

STM32供电参考设计

STM32供电参考设计 ​ 在图中有VDD&#xff0c;VSS和VDDA&#xff0c;VSSA两种类型的供电引脚&#xff0c;其数据手册解释如下&#xff1a; ​ 令我不解的是&#xff1a;VDDA和VSSA必须分别连接到VDD和VSS&#xff0c;这是什么意思&#xff1f;有大佬能够解答一下吗&#xff1f…

和为0的四元组-蛮力枚举(C语言实现)

目录 一、问题描述 二、蛮力枚举思路 1.初始化&#xff1a; 2.遍历所有可能的四元组&#xff1a; 3.检查和&#xff1a; 4.避免重复&#xff1a; 5.更新计数器&#xff1a; 三、代码实现 四、运行结果 五、 算法复杂度分析 一、问题描述 给定一个整数数组 nums&…

嵌入式系统 (2.嵌入式硬件系统基础)

2.嵌入式硬件系统基础 2.1嵌入式硬件系统的组成 嵌入式硬件系统以嵌入式微处理器为核心&#xff0c;主要由嵌入式微处理器、总线、存储器、输入/输出接口和设备组成。 嵌入式微处理器 嵌入式微处理器采用冯诺依曼结构或哈佛结构&#xff1a;前者指令和数据共享同一存储空间…

对快速由表及里说拜拜/如何正确运用由表及里

你是不是还&#xff1a;看到一男子拖走一女子就以为小情侣吵架而已&#xff08;可能人贩子&#xff09;&#xff1b;看到男友对你好个几次就从此死心塌地&#xff08;可能有手就行&#xff0c;细节装装而已&#xff09;结果耽误终身&#xff1b;看到女同事对你微笑不排斥就以为…

(七)人工智能进阶之人脸识别:从刷脸支付到智能安防的奥秘,小白都可以入手的MTCNN+Arcface网络

零、开篇趣谈 还记得第一次用支付宝"刷脸"时的新奇感吗&#xff1f;或者被抖音的人脸特效逗乐的瞬间&#xff1f;这些有趣的应用背后&#xff0c;其实藏着一个精妙的AI世界。今天&#xff0c;就让我们开启一段奇妙的人脸识别技术探索之旅吧&#xff01; 一、人脸识…

腾讯云AI代码助手编程挑战赛-图片转换工具

作品简介&#xff1a; 解决了人们学习生活中的图片格式转换问题&#xff0c; 制作该脚本&#xff0c;省去了打开在线编辑器操作的时间&#xff0c; 免费为用户提供图片格式的转换的实用小工具 技术架构 python语言的tk库来完成的GUI页面设计&#xff0c; 引用PIL包转换图…

【VUE 指令学习笔记】

v-bind :单向绑定解析表达式&#xff0c;可简写为:xxx v-model :双向数据绑定。 v-for&#xff1a;遍历数组/对象/字符串 v-on&#xff1a;绑定事件监听&#xff0c;可简写为。 v-if:条件渲染(动态控制节点是否存存在) v-else:条件渲染(动态控制节点是否存存在) v-show:条件渲染…

高山旅游景区有效降低成本,无人机山下到山上物资吊运技术详解

在高山旅游景区&#xff0c;传统的物资运输方式往往面临人力成本高昂、效率低下等问题&#xff0c;而无人机技术的引入为这一难题提供了新的解决方案。以下是对无人机从山下到山上进行物资吊运技术的详细解析&#xff1a; 一、无人机物资吊运技术的优势 1. 降低人力成本&#…

【Linux】shell脚本编程

目录 概念&#xff1a; shell脚本的本质&#xff1a; shell脚本编程&#xff1a; shell变量&#xff1a; 变量的定义格式&#xff1a; 变量的分类 自定义变量&#xff1a; 环境变量&#xff1a; 命令变量与命令行参数&#xff1a; 预定义变量&#xff1a; shell中的…

(长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)

城市三维建模与分析 三维城市模型已经成为一种非常普遍的地理空间数据资源,成为城市的必需品,对城市能化管理至关重要。语义信息丰富的三维城市模型可以有效实现不同领域数据与IS相信息的高层次集成及互操作,从而在城市规划、环境模拟、应急响应和辅助决策等众多领域公挥作用、…

接口测试-postman(使用postman测试接口笔记)

一、设置全局变量 1. 点击右上角设置按钮-》打开管理环境窗口-》选择”全局“-》设置变量名称&#xff0c;初始值和当前值设置一样的&#xff0c;放host放拼接的url&#xff0c;key放鉴权那一串字符&#xff0c;然后保存-》去使用全局变量&#xff0c;用{{变量名称}}形式 二、…

Django学习笔记之数据库(一)

文章目录 安装一、数据库配置二、基本操作步骤1.增加2.查看3.排序4.更新5.删除数据 三、一对多&#xff0c;多对多&#xff0c;一对一1.一对多1.一对一1.多对多 四、查询操作五、聚合操作六、F和Q操作 安装 首先就是安装Mysql和Navicat。 一、数据库配置 其实整个就是连接前端…

【工具变量】统计行业锦标赛激励数据集(2008-2023年)

一、数据简介 坚持创新驱动发展&#xff0c;要强化企业创新主体地位&#xff0c;发挥企业家在技术创新中的重要作用。作为企业组织内部最具有影响力的角色&#xff0c;高级管理人员拥有企业经营管理的自由裁量权&#xff0c;对企业战略决策及由此产生的经营绩效具有举足轻重的…

DuckDB:PRAGMA语句动态配置数据库行为

PRAGMA语句是DuckDB从SQLite中采用的SQL扩展。PRAGMA命令可能会改变数据库引擎的内部状态&#xff0c;并可能影响引擎的后续执行或行为。本文介绍PRAGMA命令及其典型应用场景。 DuckDB PRAGMA介绍 在 DuckDB 中&#xff0c;PRAGMA 是一种编译指示&#xff08;compiler directi…

【QT-QTableView实现鼠标悬浮(hover)行高亮显示+并设置表格样式】

1、自定义委托类 HoverDelegate hoverdelegate.h #ifndef HOVERDELEGATE_H #define HOVERDELEGATE_H#include <QObject> #include <QStyledItemDelegate>class hoverdelegate : public QStyledItemDelegate {Q_OBJECT // 添加 Q_OBJECT 宏public:explicit hoverde…

Improving Language Understanding by Generative Pre-Training GPT-1详细讲解

Improving Language Understanding by Generative Pre-Training 2018.06 GPT-1 0.有监督、半监督、无监督 CV&#xff1a;ImageNet pre-trained model NLP&#xff1a;pre-trained model? 在计算机视觉中任务包含分类、检测、分割&#xff0c;任务类别数少&#xff0c;对应…

大数据技术 指令笔记1

3.cd命令 cd命令用来切换工作目录至DirName。其中DirName表示法可为绝对路径或相对路径 例如&#xff1a; cd/ 切换到根目录 cd 切换到家目录 cd /etc/sysconfig/ 切换到/etc/sysconfig目录 cd .. 返回到父目录 4.Is命令 Is命令用来列出文件或…

创建Java项目,并添加MyBatis包和驱动包

一 : Mybatis和jsp使用上,只有Dao层有区别 Mybatis 使用方法: 测试类的7步骤 1.读取核心配置文件 2.构建sql会话工厂 3.开启sql会话 4.获取mapper接口 5.调用相对应的增删改查方法 6.打印 7.关闭回话 /*** 用户列表* throws IOException*/Testpublic void roleList() throws IO…