路径规划 | 图解Theta*算法(附ROS C++/Python/Matlab仿真)

目录

  • 0 专栏介绍
  • 1 A*算法的局限性
  • 2 Theta*算法原理图解
  • 3 Bresenham视线法
  • 4 算法仿真测试
    • 4.1 算法流程图
    • 4.2 ROS C++ 实现
    • 4.3 Python实现
    • 4.4 Matlab实现

0 专栏介绍

🔥附C++/Python/Matlab全套代码🔥课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。

🚀详情:图解自动驾驶中的运动规划(Motion Planning),附几十种规划算法


1 A*算法的局限性

A*算法的局限性在于其搜索路径的可行角度被网格形状固定。因此,A* 算法搜索的路径往往不是实际地形下真正的最短路径(由8邻域二维栅格边缘形成的最短路径可能比连续环境中真实的最短路径长多达8%),如图所示。

在这里插入图片描述

Theta*算法的核心原理是去除依赖于网格形状的角度约束,不限制路径仅由栅格边缘组成,以提升路径平滑性和最优性

2 Theta*算法原理图解

Theta*算法主体流程与A*相同,区别在于扩展节点数据的更新方式。

  • A*算法利用邻接节点更新信息(path1),即若 g ( w ) > g ( v ) + d ( v , w ) g\left( w \right) >g\left( v \right) +\mathrm{d}\left( v,w \right) g(w)>g(v)+d(v,w),则令 g ( w ) = g ( v ) + d ( v , w ) g\left( w \right) =g\left( v \right) +\mathrm{d}\left( v,w \right) g(w)=g(v)+d(v,w)
  • Theta*额外考虑当前节点的父节点信息(path2),即若 g ( w ) > g ( v . p a r e n t ) + d ( v . p a r e n t , w ) g\left( w \right) >g\left( v.\mathrm{parent} \right) +\mathrm{d}\left( v.\mathrm{parent},w \right) g(w)>g(v.parent)+d(v.parent,w),则令 g ( w ) = g ( v . p a r e n t ) + d ( v . p a r e n t , w ) g\left( w \right) =g\left( v.\mathrm{parent} \right) +\mathrm{d}\left( v.\mathrm{parent},w \right) g(w)=g(v.parent)+d(v.parent,w)

根据三角形两边之和大于第三边有

d ( v . p a r e n t , w ) < d ( v . p a r e n t , v ) + d ( v , w ) \mathrm{d}\left( v.\mathrm{parent},w \right) <\mathrm{d}\left( v.\mathrm{parent},v \right) +\mathrm{d}\left( v,w \right) d(v.parent,w)<d(v.parent,v)+d(v,w)

因此当 v . p a r e n t v.\mathrm{parent} v.parent w w w间不存在障碍物时,算法必然采用path2,实现锯齿路径的平滑

在这里插入图片描述

3 Bresenham视线法

在Theta*中,对障碍物的碰撞检测采用Bresenham算法。Bresenham碰撞测试在三种类型的移动中访问单元格:

  • x x x方向移动
  • y y y方向移动
  • 对角线移动

在栅格地图中,碰撞检测点连线经过若干离散栅格,因此每次移动都将产生非连续误差,Bresenham算法要求下一个移动偏差最小。通过迭代即可访问检测线经过的所有栅格,判断这些栅格的代价是否超过阈值即可完成碰撞检测。

算法流程如下所示

在这里插入图片描述

4 算法仿真测试

4.1 算法流程图

算法流程与核心函数如下所示

在这里插入图片描述

在这里插入图片描述

4.2 ROS C++ 实现

核心代码如下

bool ThetaStar::plan(const unsigned char* global_costmap, const Node& start, const Node& goal, std::vector<Node>& path,std::vector<Node>& expand)
{// initializecosts_ = global_costmap;path.clear();expand.clear();// open list and closed liststd::priority_queue<Node, std::vector<Node>, compare_cost> open_list;std::unordered_set<Node, NodeIdAsHash, compare_coordinates> closed_list;open_list.push(start);// get all possible motionsconst std::vector<Node> motion = getMotion();// main processwhile (!open_list.empty()){// pop current node from open listNode current = open_list.top();open_list.pop();// current node does not exist in closed listif (closed_list.find(current) != closed_list.end())continue;closed_list.insert(current);expand.push_back(current);// goal foundif (current == goal){path = _convertClosedListToPath(closed_list, start, goal);return true;}// explore neighbor of current nodefor (const auto& m : motion){Node node_new = current + m;  // add the x_, y_, g_// current node do not exist in closed listif (closed_list.find(node_new) != closed_list.end())continue;// explore a new node// path 1node_new.h_ = dist(node_new, goal);node_new.id_ = grid2Index(node_new.x_, node_new.y_);node_new.pid_ = current.id_;// next node hit the boundary or obstacleif ((node_new.id_ < 0) || (node_new.id_ >= ns_) || (costs_[node_new.id_] >= lethal_cost_ * factor_))continue;// get the coordinate of parent nodeNode parent;parent.id_ = current.pid_;index2Grid(parent.id_, parent.x_, parent.y_);// update g valueauto find_parent = closed_list.find(parent);if (find_parent != closed_list.end()){parent = *find_parent;_updateVertex(parent, node_new);}open_list.push(node_new);}}return false;
}

效果如下

在这里插入图片描述

4.3 Python实现

核心代码如下

def plan(self):# OPEN set with priority and CLOSED setOPEN = []heapq.heappush(OPEN, self.start)CLOSED = []while OPEN:node = heapq.heappop(OPEN)# exists in CLOSED setif node in CLOSED:continue# goal foundif node == self.goal:CLOSED.append(node)return self.extractPath(CLOSED), CLOSEDfor node_n in self.getNeighbor(node):                # exists in CLOSED setif node_n in CLOSED:continue# path1node_n.parent = node.currentnode_n.h = self.h(node_n, self.goal)try:p_index = CLOSED.index(Node(node.parent))node_p = CLOSED[p_index]except:node_p = Noneif node_p:self.updateVertex(node_p, node_n)# goal foundif node_n == self.goal:heapq.heappush(OPEN, node_n)break# update OPEN setheapq.heappush(OPEN, node_n)CLOSED.append(node)return ([], []), []

效果如下

在这里插入图片描述

4.4 Matlab实现

核心代码如下

while ~isempty(OPEN)% popf = OPEN(:, 3) + OPEN(:, 4);[~, index] = min(f);cur_node = OPEN(index, :);OPEN(index, :) = [];% exists in CLOSED setif loc_list(cur_node, CLOSED, [1, 2])continueend% update expand zoneif ~loc_list(cur_node, EXPAND, [1, 2])EXPAND = [EXPAND; cur_node(1:2)];end% goal foundif cur_node(1) == goal(1) && cur_node(2) == goal(2)CLOSED = [cur_node; CLOSED];goal_reached = true;cost = cur_node(3);breakendif (cur_node(1) ==17) &&(cur_node(2) == 26)cur_node(1);end% explore neighborsfor i = 1:motion_num% path 1node_n = [cur_node(1) + motion(i, 1), ...cur_node(2) + motion(i, 2), ...cur_node(3) + motion(i, 3), ...0, ...cur_node(1), cur_node(2)];node_n(4) = h(node_n(1:2), goal);% exists in CLOSED setif loc_list(node_n, CLOSED, [1, 2])continueend% obstacleif map(node_n(1), node_n(2)) == 2continueendp_index = loc_list(cur_node(5: 6), CLOSED, [1, 2]);if p_indexnode_p = CLOSED(p_index, :);elsenode_p = 0;endif node_p ~= 0node_n = update_vertex(map, node_p, node_n);end% update OPEN setOPEN = [OPEN; node_n];endCLOSED = [cur_node; CLOSED];

效果如下
在这里插入图片描述

完整工程代码请联系下方博主名片获取


🔥 更多精彩专栏

  • 《ROS从入门到精通》
  • 《Pytorch深度学习实战》
  • 《机器学习强基计划》
  • 《运动规划实战精讲》

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

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

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

相关文章

Elasticsearch中倒排索引、分词器、DSL语法使用介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

Go语言入门记录:从基础到变量、函数、控制语句、包引用、interface、panic、go协程、Channel、sync下的waitGroup和Once等

程序入口文件的包名必须是main&#xff0c;但主程序文件所在文件夹名称不必须是main&#xff0c;即我们下图hello_world.go在main中&#xff0c;所以感觉package main写顺理成章&#xff0c;但是如果我们把main目录名称改成随便的名字如filename也是可以运行的&#xff0c;所以…

Python采集关键词结果辅助写作

大家好&#xff01;在进行学术研究和 写作时&#xff0c;获取准确、全面的文献资料和相关研究成果是非常重要的。在本文中&#xff0c;我将与你分享使用Python爬虫 采集 学术关键词结果来辅助 写作的方法&#xff0c;帮助你快速获取与研究主题相关的学术文献和 。 **1. 设置搜索…

SqlServer2019—解决SQL Server 无法连接127.0.0.1的问题

1、打开SQL Server 2019配置管理器 2、SQL Servere 网络配置(启用 Named Pipes 和 TCP/IP) 3、修改TCP/IP协议(右键选择属性—IP地址)&#xff0c;具体如下图所示&#xff1a; 4、重启SQL Server服务

LLMs参数高效微调(PEFT) Parameter efficient fine-tuning (PEFT)

正如你在课程的第一周所看到的&#xff0c;训练LLMs需要大量的计算资源。完整的微调不仅需要内存来存储模型&#xff0c;还需要在训练过程中使用的各种其他参数。 即使你的计算机可以容纳模型权重&#xff0c;最大模型的权重现在已经达到几百GB&#xff0c;你还必须能够为优化…

Blender给一个对象添加多个动画

最近在做一个类似元宇宙的项目&#xff0c;需要使用3D建模软件来给3D模型添加动画&#xff0c;3D建模软件选择Blender&#xff08;因为开源免费…&#xff09;&#xff0c;版本: V3.5 遇到的需求是同一个对象要添加多个动画&#xff0c;然后在代码里根据需要调取动画来执行。本…

兄弟,王者荣耀的段位排行榜是通过Redis实现的?

目录 一、排行榜设计方案1、数据库直接排序2、王者荣耀好友排行 二、Redis实现计数器1、什么是计数器功能&#xff1f;2、Redis实现计数器的原理&#xff08;1&#xff09;使用INCR命令实现计数器&#xff08;2&#xff09;使用INCRBY命令实现计数器 三、通过Redis实现“王者荣…

nginx-获取客户端IP地址

上有服务器与客户端中间是有nginx代理服务器的&#xff0c;上游服务器如何获取客户端真实ip地址&#xff1f; nginx代理服务器设置X-Forwarded-For的header参数&#xff0c;代理服务器通过remote_addr获取客户端ip地址&#xff0c;将ip地址写入nginx代理服务器的X-Forwarded-Fo…

ELK之LogStash介绍及安装配置

一、logstash简介 集中、转换和存储数据 Logstash 是免费且开放的服务器端数据处理管道&#xff0c;能够从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到您最喜欢的“存储库”中。 Logstash 能够动态地采集、转换和传输数据&#xff0c;不受格式或复杂度的…

Linux系统USB摄像头测试程序(四)_视频旋转及缩放

下面的程序实现了视频的旋转及缩放&#xff0c;窗口中点击鼠标左键视频向左旋转&#xff0c;点击鼠标右键视频向右旋转并且视频缩小了二分之一。程序中首先把yvyv422转换成了RGB24&#xff0c;然后利用opencv进行了旋转和缩放&#xff0c;其后用sdl2进行了渲染。使用了ffmpeg、…

f4v如何格式转换mp4格式?分享几种好用转换方法

为了使视频文件格式更加通用&#xff0c;更容易在不同设备和平台上播放&#xff0c;需要将F4V格式转换为MP4格式。F4V是Adobe Flash Player使用的一种视频文件格式&#xff0c;而MP4格式是一种更通用的视频文件格式&#xff0c;几乎所有设备和平台都支持它。此外&#xff0c;MP…

微积分基本概念

微分 函数的微分是指对函数的局部变化的一种线性描述。微分可以近似地描述当函数自变量的取值作足够小的改变时&#xff0c;函数的值是怎样改变的。。对于函数 y f ( x ) y f(x) yf(x) 的微分记作&#xff1a; d y f ′ ( x ) d x d_y f^{}(x)d_x dy​f′(x)dx​ 微分和…