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

news2024/12/23 1:54:07

目录

  • 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)
{
  // initialize
  costs_ = global_costmap;
  path.clear();
  expand.clear();

  // open list and closed list
  std::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 motions
  const std::vector<Node> motion = getMotion();

  // main process
  while (!open_list.empty())
  {
    // pop current node from open list
    Node current = open_list.top();
    open_list.pop();

    // current node does not exist in closed list
    if (closed_list.find(current) != closed_list.end())
      continue;

    closed_list.insert(current);
    expand.push_back(current);

    // goal found
    if (current == goal)
    {
      path = _convertClosedListToPath(closed_list, start, goal);
      return true;
    }

    // explore neighbor of current node
    for (const auto& m : motion)
    {
      Node node_new = current + m;  // add the x_, y_, g_

      // current node do not exist in closed list
      if (closed_list.find(node_new) != closed_list.end())
        continue;

      // explore a new node
      // path 1
      node_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 obstacle
      if ((node_new.id_ < 0) || (node_new.id_ >= ns_) || (costs_[node_new.id_] >= lethal_cost_ * factor_))
        continue;

      // get the coordinate of parent node
      Node parent;
      parent.id_ = current.pid_;
      index2Grid(parent.id_, parent.x_, parent.y_);

      // update g value
      auto 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 set
	OPEN = []
	heapq.heappush(OPEN, self.start)
	CLOSED = []
	
	while OPEN:
	    node = heapq.heappop(OPEN)
	
	    # exists in CLOSED set
	    if node in CLOSED:
	        continue
	
	    # goal found
	    if node == self.goal:
	        CLOSED.append(node)
	        return self.extractPath(CLOSED), CLOSED
	
	    for node_n in self.getNeighbor(node):                
	        # exists in CLOSED set
	        if node_n in CLOSED:
	            continue
	        
	        # path1
	        node_n.parent = node.current
	        node_n.h = self.h(node_n, self.goal)
	
	        try:
	            p_index = CLOSED.index(Node(node.parent))
	            node_p = CLOSED[p_index]
	        except:
	            node_p = None
	
	        if node_p:
	            self.updateVertex(node_p, node_n)
	
	        # goal found
	        if node_n == self.goal:
	            heapq.heappush(OPEN, node_n)
	            break
	        
	        # update OPEN set
	        heapq.heappush(OPEN, node_n)
	    
	    CLOSED.append(node)
	return ([], []), []

效果如下

在这里插入图片描述

4.4 Matlab实现

核心代码如下

while ~isempty(OPEN)
    % pop
    f = OPEN(:, 3) + OPEN(:, 4);
    [~, index] = min(f);
    cur_node = OPEN(index, :);
    OPEN(index, :) = [];

    % exists in CLOSED set
    if loc_list(cur_node, CLOSED, [1, 2])
        continue
    end

    % update expand zone
    if ~loc_list(cur_node, EXPAND, [1, 2])
        EXPAND = [EXPAND; cur_node(1:2)];
    end

    % goal found
    if cur_node(1) == goal(1) && cur_node(2) == goal(2)
        CLOSED = [cur_node; CLOSED];
        goal_reached = true;
        cost = cur_node(3);
        break
    end
    if (cur_node(1) ==17) &&(cur_node(2) == 26)
        cur_node(1);
    end
    % explore neighbors
    for i = 1:motion_num
        % path 1
        node_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 set
        if loc_list(node_n, CLOSED, [1, 2])
            continue
        end

        % obstacle
        if map(node_n(1), node_n(2)) == 2
            continue
        end

        p_index = loc_list(cur_node(5: 6), CLOSED, [1, 2]);
        if p_index
            node_p = CLOSED(p_index, :);
        else
            node_p = 0;
        end
        
        if node_p ~= 0
            node_n = update_vertex(map, node_p, node_n);
        end
        
        % update OPEN set
        OPEN = [OPEN; node_n];
    end
    CLOSED = [cur_node; CLOSED];

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

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


🔥 更多精彩专栏

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么这3类人,一定要选择无代码开发?

一个人也能开发出一个软件&#xff1f;这或许难以想象的&#xff0c;但无代码技术的问世&#xff0c;让这一切都成为现实。 可能很多人对“无代码”还是不太熟悉&#xff0c;但大家一定都听说过“码农”这个词&#xff0c;而无代码开发技术的出现&#xff0c;可以让我们摆脱这…

Node.js @zurmokeeper/exceljs 如何快速导出多表头的excel文件

Node.js 如何快速导出嵌套列&#xff08;多表头&#xff09;的excel文件。效果图如下&#xff1a; 1&#xff1a;使用 zurmokeeper/exceljs&#xff0c; V4.4.1以上 安装&#xff1a; npm i zurmokeeper/exceljs 2: 有一个 worksheet.makeColumns 方法&#xff0c;API文档&am…

第六章:数据结构与算法-par1:典型数据结构

文章目录 一、典型数据结构介绍1.1 基本概念和术语1、基本数据概念2、抽象数据类型3、算法4、算法复杂度5、数据结构 二、数据的存储结构2.1 线性结构1、线性表&#xff08;一般线性表&#xff09;2、栈和队列&#xff08;受限线性表&#xff09;1) 栈 Stack2&#xff09; 队列…

怎么在线制作思维导图?分享几个好用的方法和注意事项

思维导图是一种非常有用的工具&#xff0c;它可以帮助我们整理和梳理思路&#xff0c;提高学习和工作效率。现在&#xff0c;越来越多的人开始使用在线工具来制作思维导图&#xff0c;因为它们不仅方便易用&#xff0c;而且可以随时随地进行编辑和共享。本文将介绍几个好用的在…

生产环境部署与协同开发 Git

目录 一、前言——Git概述 1.1 Git是什么 1.2 为什么要使用Git 什么是版本控制系统 1.3 Git和SVN对比 SVN集中式 Git分布式 1.4 Git工作流程 四个工作区域 工作流程 1.5 Git下载安装 1.6 环境配置 设置用户信息 查看配置信息 二、git基础 2.1 本地初始化仓库 ​编辑…

C语言二——C++编写一段代码,求一元三次方程的根

这段代码是用来解决一元三次方程的程序。它使用了复数运算&#xff0c;并根据判别式的值进行不同分支的处理&#xff0c;输出方程的根。 您可以在程序中输入一元三次方程的系数a、b、c和d&#xff0c;然后调用solve_cubic_equation函数进行求解。函数会根据判别式的值进行不同…

云计算和Docker分别适用场景

在大规模网络爬虫系统中&#xff0c;通过使用云计算和Docker技术&#xff0c;可以实现大规模网络爬虫系统的高效架构设计和部署。这种架构能够提供可扩展性、高可用性和灵活性&#xff0c;为爬虫系统的运行和管理带来便利。 云计算和Docker在大规模网络爬虫系统中有不同的业务…

【C语言】用函数递归的方法解决汉诺塔问题

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn&#x1f493; ⏩专栏分类&#xff1a;C语言 &#x1f69a;代码仓库&#xff1a;小小unicorn的学习足迹&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 汉诺塔 1.问题起源:2.汉诺塔游戏规…

可扩展的单核至四核Cortex-A53@1.4GHz工业级核心板规格书

1 核心板简介 创龙科技SOM-TL62x是一款基于TI Sitara系列AM62x单/双/四核ARM Cortex-A53 + 单核ARM Cortex-M4F多核处理器设计的高性能低功耗工业核心板,通过工业级B2B连接器引出2x TSN Ethernet、9x UART、3x CAN-FD、GPMC、2x USB2.0、CSI、DISPLAY等接口。处理器ARM Corte…

什么软件可以文字转语音?来试试这几种工具

将文字转换成语音可以帮助那些有视觉障碍或者阅读困难的人更容易地获取信息。这些人可能无法看到屏幕上的文字&#xff0c;但是能够通过听声音来理解信息。其次&#xff0c;它还可以作为一种语言学习工具&#xff0c;例如帮助人们提高语言发音能力&#xff0c;因为它可以提供标…

使用Python爬虫采集网络热点

在当今信息爆炸的时代&#xff0c;了解网络热搜词和热点事件对于我们保持时事敏感性和把握舆论动向非常重要。在本文中&#xff0c;我将与你分享使用Python爬虫采集网络热搜词和热点事件的方法&#xff0c;帮助你及时获取热门话题和热点新闻。 1. 网络热搜词采集 网络热搜词是人…

生成地图展示-广东省【Python思路】

在【生成图片展示】博客的基础上&#xff0c;进一步的确定某个省份的数据。 # 以下数据均为人造数据&#xff0c;仅供学习# 1.导包 from pyecharts.options import * from pyecharts.charts import Map import json# 2.打开、读取、关闭文件 f open("D:/Typora 记事本/n…

【C/C++】虚函数调用流程 | 虚函数和一般函数的区别

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

C++信息学奥赛1144:单词翻转

#include <iostream> #include <string> using namespace std; int main() {string str;// 输入一行字符串getline(cin, str);string arr;for (int i 0; i < str.length(); i){if (str[i] ! ){arr str[i]; // 将非空格字符添加到临时存储的字符串中}else{for…

spring5.x-声明式事务原理及源码实现

上文&#xff1a;spring5.x-AOP实现原理及源码分析 本系列文章&#xff1a; spring5.x-AOP实现原理及源码分析 spring5.x-监听器原理及源码实现 spring5.x-解决循环依赖分析 spring5.x-IOC模块源码学习 spring5.x介绍及搭配spring源码阅读环境 基础知识 请先学习…

高职教育应对ChatGPT应用的策略

一、完善顶层设计&#xff0c;提升技术水平 在推广ChatGPT平台的过程中&#xff0c;高职院校需要关注技术本身的问题。这就需要在国家和地方政府的引导下&#xff0c;引入更完善的技术顶层设计&#xff0c;提高人工智能在高职教育中的运用水平。具体来说&#xff0c;一方面需要…

信息化发展3

信息化体系 国家信息化体系包括信息技术应用&#xff08;龙头&#xff09; 、信息资源&#xff08;核心&#xff09; 、信息网络&#xff08;基础设施&#xff09; 、信息技术和产业&#xff08;国家信息化建设基础&#xff09; 、信息化人才&#xff08;关键&#xff09; 、信…

Android常用组件:空布局empty_view,占位图封装

最近在做一件费劲不讨好的事情&#xff0c;那就是把项目中无关业务的代码功能模块抽出来&#xff0c;供以后使用。 既然费劲心机&#xff0c;不妨分享出来&#xff0c;大家可以一块学习借鉴。 直接来看看空布局的界面效果&#xff1a; 基本的布局就是以下结构&#xff1a; xm…

Python中的装饰器介绍

装饰器是Python编程语言中一种强大的特性&#xff0c;用于修改或增强函数或类的行为&#xff0c;而无需对它们本身进行修改。装饰器通常被用于在不改变原始代码的情况下&#xff0c;向函数或方法添加额外的功能&#xff0c;如日志记录、权限检查、数据格式转换等。装饰器本质上…

从小学到高考:讲述我与儿子的成长故事

三年前&#xff0c;当儿子考上高中的时候&#xff0c;我就想着写一篇文章做纪念&#xff0c;然后写了一半就放弃了&#xff0c;因为初中阶段充满了遗憾。在儿子高一拿到好成绩时&#xff0c;我又想着写一篇文章以作鼓励&#xff0c;写了一半也放弃了&#xff0c;因为革命尚未成…