路径规划 | 图解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.rhkb.cn/news/108811.html

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

相关文章

iTunes怎么备份?1招教你轻松搞定

相比于苹果手机的iCloud备份&#xff0c;使用iTunes备份具有以下优点&#xff1a;1、备份容量不受限制&#xff1b;2、备份后的文件就像普通文档一样&#xff0c;可以随时进行查看和管理。本文将为大家介绍itunes怎么备份、如何对备份进行加密以及怎么删除备份的方法&#xff0…

Redis全局命令

"那篝火在银河尽头~" Redis-cli命令启动 现如今&#xff0c;我们已经启动了Redis服务&#xff0c;下⾯将介绍如何使⽤redis-cli连接、操作Redis服务。客户端与服务端交互的方式有两种: ● 第⼀种是交互式⽅式: 后续所有的操作都是通过交互式的⽅式实现&#xff0c;…

【Unity3D赛车游戏】【六】如何在Unity中为汽车添加发动机和手动挡变速?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

Vue2向Vue3过度核心技术编程式导航

目录 1 编程式导航-两种路由跳转方式1.问题2.方案3.语法4.path路径跳转语法5.代码演示 path跳转方式6.name命名路由跳转7.代码演示通过name命名路由跳转8.总结 2 编程式导航-path路径跳转传参1.问题2.两种传参方式3.传参4.path路径跳转传参&#xff08;query传参&#xff09;5.…

基于ssm+vue德云社票务系统源码和论文

基于ssmvue德云社票务系统源码和论文063 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 1.选题的依据和意义 互联网时代&#xff0c;随着生活节奏的加快和不断上升的压力&#xff0c;人们急需寻找到情绪的宣泄…

opencv 进阶20-随机森林示例

OpenCV中的随机森林是一种强大的机器学习算法&#xff0c;旨在解决分类和回归问题。随机森林使用多个决策树来进行预测&#xff0c;每个决策树都是由随机选择的样本和特征组成的。在分类问题中&#xff0c;随机森林通过投票来确定最终的类别&#xff1b;在回归问题中&#xff0…

aws PinPoint发附件demo

php 版aws PinPoint发附件demo Laravel8框架&#xff0c;安装了"aws/aws-sdk-php": "^3.257" 主要代码&#xff1a; public function sendRawMail(Request $request) {$file $request->file(attachment);/*echo count($file);dd($file);*/$filenam…

E8267D 是德科技矢量信号发生器

描述 最先进的微波信号发生器 安捷伦E8267D PSG矢量信号发生器是业界首款集成式微波矢量信号发生器&#xff0c;I/Q调制最高可达44 GHz&#xff0c;典型输出功率为23 dBm&#xff0c;最高可达20 GHz&#xff0c;对于10 GHz信号&#xff0c;10 kHz偏移时的相位噪声为-120 dBc/…

【持续更新中】QAGroup1

OVERVIEW Q&AGroup1一、语言基础1.C语言&#xff08;1&#xff09;含参数的宏与函数的不同点&#xff08;2&#xff09;sizeof与strlen的区别&#xff08;3&#xff09;大/小端&#xff08;4&#xff09;strcpy与memcpy的区别&#xff08;5&#xff09;extern与static的区别…

SIP网络对讲终端 双键求助终端 防水求助终端

SV-6005TP SIP网络对讲终端 双键求助终端 防水求助终端 SIP对讲终端SV-6005TP是一款采用了ARMDSP架构&#xff1b;配置了麦克风输入和扬声器输出&#xff0c;SV-6005TP带一路寻呼按键&#xff0c;可实现SIP对讲功能&#xff0c;作为SIP对讲的终端&#xff0c;主要用于银行、部…

macOS 安装 Homebrew 详细过程

文章目录 macOS 安装 Homebrew 详细过程Homebrew 简介Homebrew 安装过程设置环境变量安装 Homebrew安装完成后续设置(重要)设置环境变量homebrew 镜像源设置macOS 安装 Homebrew 详细过程 本文讲解了如何使用中科大源安装 Homebrew 的安装过程,文章里面的所有步骤都是必要的,需…

洁净区环境监测如何操作?

洁净区环境监测 如何操作 洁净区洁净等级划分为&#xff1a; A级&#xff1a;指高风险操作区&#xff0c;如&#xff1a;灌装、放置胶塞桶、敞口安瓿瓶、敞口西林瓶的区域及无菌装配或连接操作的区域。通常用层流操作台&#xff08;罩&#xff09;来维持该区的环境状态。 B级…

AI 绘画Stable Diffusion 研究(十五)SD Embedding详解

大家好&#xff0c;我是风雨无阻。 本期内容&#xff1a; Embedding是什么&#xff1f;Embedding有什么作用&#xff1f;Embedding如何下载安装&#xff1f;如何使用Embedding&#xff1f; 大家还记得 AI 绘画Stable Diffusion 研究&#xff08;七&#xff09; 一文读懂 Stab…

【Springboot】| 从深入自动配置原理到实现 自定义Springboot starter

目录 一. &#x1f981; 前言二. &#x1f981; Spring-boot starter 原理实现分析2.1 自动配置原理 三. &#x1f981; 操作实践3.1 项目场景3.2 搭建项目3.3 添加相关依赖3.4 删除一些不需要的东西3.5 发邮件工具类逻辑编写3.6 创建相关配置类3.7 创建 Spring.factories 文件…

spark中排查Premature EOF: no length prefix available

报错信息 /07/22 10:20:28 WARN DFSClient: Error Recovery for block BP-888461729-172.16.34.148-1397820377004:blk_15089246483_16183344527 in pipeline 172.16.34.64:50010, 172.16.34.223:50010: bad datanode 172.16.34.64:50010 [DataStreamer for file /bdp/data/u9…

docker高级(mysql主从复制)

数据库密码需要设置成自己的&#xff01;&#xff01;&#xff01; 1、创建容器master13307 #docker pulldocker run -p 13307:3306 --name mysql-master \ --privilegedtrue \ -v /mysql/mysql-master/log:/var/log/mysql \ -v /mysql/mysql-master/data:/var/lib/mysql \ -…

python3对接godaddy API,实现自动更改域名解析(DDNS)

python3对接godaddy API&#xff0c;实现自动更改域名解析&#xff08;DDNS&#xff09; 文章开始前&#xff0c;先解释下如下问题&#xff1a; ①什么是域名解析&#xff1f; 域名解析一般是指通过一个域名指向IP地址&#xff08;A解析&#xff09;&#xff0c;然后我们访问…

C++图形界面编程-MFC

C控制台程序是命令行黑框&#xff0c;如果要写一个图形界面&#xff0c;VS也提供了图形界面编程MFC。建项目的时候选如下选项&#xff1a; 类似于QT。 问&#xff1a;那么MFC项目的运行入口main()或WinMain()在哪里呢&#xff1f; 答&#xff1a;其实&#xff0c;在MFC应用程…

【1++的数据结构】之map与set(一)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的数据结构】 文章目录 一&#xff0c;关联式容器与键值对二&#xff0c;setset的使用 三&#xff0c;mapmap的使用 四&#xff0c;multiset与multimap 一&#xff0c;关联式容器与键值对 像l…

视频云存储/安防监控视频AI智能分析网关V3:抽烟/打电话功能详解

人工智能技术已经越来越多地融入到视频监控领域中&#xff0c;近期我们也发布了基于AI智能视频云存储/安防监控视频AI智能分析平台的众多新功能&#xff0c;该平台内置多种AI算法&#xff0c;可对实时视频中的人脸、人体、物体等进行检测、跟踪与抓拍&#xff0c;支持口罩佩戴检…