运动规划实战案例 | 基于四叉树分解的路径规划(附ROS C++/Python仿真)

目录

  • 1 为什么需要四叉树?
  • 2 基于四叉树的路径规划
    • 2.1 分层抽象
    • 2.2 路图搜索
    • 2.3 动态剪枝
  • 3 算法仿真
    • 3.1 ROS C++算法仿真
    • 3.2 Python算法仿真

1 为什么需要四叉树?

路径规划的本质是在给定环境中寻找从起点到终点的最优或可行路径,其核心挑战在于如何高效处理环境信息。传统栅格法将环境均匀划分为等大小的网格,虽直观但面临“维度灾难”——环境规模扩大时,计算量呈指数级增长。例如,一个100×100的二维栅格地图需维护10000个节点,而三维环境下这一数字将飙升至百万级别。拓扑图路径规划算法的核心思想在于将复杂的高维连续空间抽象为低维的离散图结构。基于障碍边界的可视图法(运动规划实战案例 | 基于可视图的路径规划算法(附ROS C++/Python仿真))以及维诺图法(路径规划 | 详解维诺图Voronoi算法(附ROS C++/Python/Matlab仿真))都属于这类基于拓扑图的路径规划。

本文介绍的四叉树(Quadtree)这一数据结构,源于计算机图形学中对二维空间的层次化分割思想:通过递归地将空间划分为四个象限(即“四叉”),仅在需要时对特定区域进行细化,从而实现对空间信息的自适应管理。例如,在开阔区域使用粗粒度划分以节省内存,在障碍物密集区域则进行多级细分以提升路径精度。这种按需分配的特性,使得四叉树在面对非均匀分布的环境时,能够显著降低数据存储与处理的冗余度

在这里插入图片描述

接下来,将详细介绍基于四叉树路径规划的算法原理

2 基于四叉树的路径规划

基于四叉树的路径规划(Quartree-based Path Planning)分为三个核心模块:分层抽象、路图搜索与动态剪枝

2.1 分层抽象

四叉树的构建始于将整个环境空间视为一个根节点,随后递归执行分割-判断过程:若当前节点对应的区域完全自由或完全被障碍物占据,则停止分割;若区域同时包含自由空间与障碍物,则将其均等分为四个子节点。这一过程持续至达到预设分割深度或满足精度要求,最终形成一棵层次化的树结构。例如,在机器人导航场景中,四叉树会在走廊等简单区域保持顶层粗划分,而在办公室桌椅排列的复杂区域自动生成多层细粒度节点。这种分层抽象的特性,为路径规划算法提供了天然的优化通道,因为四叉树可通过节点状态快速排除无效区域:若某个父节点被标记为完全障碍,其所有子节点无需进一步探查;若父节点完全自由,则可直接将其作为整体加入开放列表。这种“剪枝”机制大幅减少了状态扩展次数。

在这里插入图片描述

2.2 路图搜索

首先,四叉树路图利用已有的树节点作为路图顶点。具体而言,选择满足以下条件的四叉树节点作为路图节点:

  • 叶子节点中的自由节点,保证路径可达性
  • 非叶子节点中的自由父节点,用于跨层路径跳跃

两个四叉树节点被判定为相邻需满足几何边界接触条件:

  • 水平相邻节点需满足纵向坐标范围存在重叠且横向边界间距在容差内(下图红框)
  • 垂直相邻节点需满足横向坐标范围存在重叠且纵向边界间距在容差内(下图蓝框)

每个四叉树节点的中心点作为路径搜索的潜在航路点,相邻节点间建立带权无向边,边的权重通常取两节点中心点的欧氏距离。最终形成的路图本质上是一个由自由空间节点及其连接关系构成的图结构,其中节点密度在障碍物边界区域显著增加,确保路径能够精确绕行复杂障碍。

在这里插入图片描述

路径搜索过程依托构建完成的路图展开,采用Dijkstra算法即可。

2.3 动态剪枝

当环境中出现临时障碍物(如突然出现的行人或车辆)时,传统方法往往需要全局重新规划,而四叉树只需更新受影响区域的局部节点。具体而言,系统仅需回溯至包含障碍物变化的父节点层级,重新评估其子节点状态,并选择性触发局部重构。这种增量式更新策略使得路径规划系统能够以非常低的代价响应环境变化

3 算法仿真

3.1 ROS C++算法仿真

核心代码如下所示

bool QuartreePathPlanner::plan(const Point3d& start, const Point3d& goal, Points3d& path, Points3d& expand)
{double m_start_x, m_start_y, m_goal_x, m_goal_y;if ((!validityCheck(start.x(), start.y(), m_start_x, m_start_y)) ||(!validityCheck(goal.x(), goal.y(), m_goal_x, m_goal_y))){return false;}// construct quartree only onceif (!is_quartree_valid_){const size_t grids_num = quar_grids_.size();roadmap_.resize(grids_num);valid_edges_.reserve(grids_num * 4);for (size_t i = 0; i < grids_num; ++i){const auto& current = quar_grids_[i];for (size_t j = i + 1; j < grids_num; ++j){const auto& neighbor = quar_grids_[j];if ((std::abs(current.min_x() - neighbor.max_x()) <= 1.0 ||std::abs(current.max_x() - neighbor.min_x()) <= 1.0)){if (current.min_y() < neighbor.max_y() && current.max_y() > neighbor.min_y()){valid_edges_.emplace_back(current.center(), neighbor.center());const double dist = (current.center() - neighbor.center()).length();roadmap_[i].emplace_back(j, dist);roadmap_[j].emplace_back(i, dist);}}else if ((std::abs(current.min_y() - neighbor.max_y()) <= 1.0 ||std::abs(current.max_y() - neighbor.min_y()) <= 1.0)){if (current.min_x() < neighbor.max_x() && current.max_x() > neighbor.min_x()){valid_edges_.emplace_back(current.center(), neighbor.center());const double dist = (current.center() - neighbor.center()).length();roadmap_[i].emplace_back(j, dist);roadmap_[j].emplace_back(i, dist);}}}}is_quartree_valid_ = true;}...// search on quartree mapstd::vector<int> q_path;if (_searchOnRoadMap(roadmap_, start_idx, goal_idx, q_path)){path.clear();path.emplace_back(start);if (q_path.size() > 2){for (size_t i = 1; i < q_path.size() - 1; ++i){const auto& pt = vertices_[q_path[i]];// convert to world framedouble wx, wy;costmap_->mapToWorld(pt.x(), pt.y(), wx, wy);path.emplace_back(wx, wy);}}path.emplace_back(goal);return true;}else{return false;}
}

在这里插入图片描述

3.2 Python算法仿真

核心代码如下所示

def partition(self, grid: np.ndarray, x: int, y: int, size: int, square_size_threshold: int = 5) -> List[Square]:"""Recursively divides the grid into four parts. If a part contains obstacles it further divied into four more parts.Parameters:grid (np.ndarray): 2D array representing the grid.x, y (int): Top-left coordinates of the current partition.size (int): Size of the current partition.Returns:List of Square objects representing free spaces in the grid."""if size < square_size_threshold:bottom_left = Point3d(x, y)top_right = Point3d(x + size, y + size)if self.isFree(grid, bottom_left, top_right):return [Square(bottom_left, top_right)]else:return []else:# Check if the area is homogeneous and free spaceif self.isFree(grid, Point3d(x, y), Point3d(x + size, y + size)):return [Square(Point3d(x, y), Point3d(x + size, y + size))]else:# Divide the area into quadrants and partition recursivelynew_size = size // 2quad1 = self.partition(grid, x, y, new_size)quad2 = self.partition(grid, x, y + new_size, new_size)quad3 = self.partition(grid, x + new_size, y, new_size)quad4 = self.partition(grid, x + new_size, y + new_size, new_size)return quad1 + quad2 + quad3 + quad4

在这里插入图片描述

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


🔥 更多精彩专栏

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

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

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

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

相关文章

docker快捷打包脚本(ai版)

直接进入主题&#xff1a; 用这个脚本前提是你本地可以拉镜像仓库的镜像&#xff0c;并且在 本地有了&#xff0c;然后将所有的镜像tag写在一个文件中&#xff0c;和下面docker_tags.txt 对应&#xff0c;文件叫什么&#xff0c;脚本里对应改什么&#xff0c;给小白说的 #!/bi…

WinMerge下载及使用教程(附安装包)

文章目录 一、WinMerge安装步骤1.WinMerge下载&#xff1a;2.解压&#xff1a;3.启动&#xff1a; 二、WinMerge使用步骤1.添加文件或文件夹2.查看差异3.格式选择 WinMerge v2.16.36 是一款免费开源的文件与文件夹比较、合并工具&#xff0c;能帮您快速找出差异&#xff0c;提高…

Jmeter性能测试之生成测试报告

结构 测试计划 测试计划是顶级的层级⽬录的结构&#xff0c; 那么在这样的⽬录结构中&#xff0c;⾥⾯可以包含很多线程组 线程组 线程组我们可以简单的理解为postman测试⼯具⾥⾯的collection&#xff0c;那么在整体线程组⾥⾯&#xff0c;可以添加很多的测试 ⽤例 简单控…

CSS中的inline-flex与flex的区别

在CSS中&#xff0c;flex 和 inline-flex 都是用于实现弹性布局&#xff08;Flexbox&#xff09;的显示属性&#xff0c;但它们在布局行为上有所不同。 flex 属性会使元素表现为块级弹性容器&#xff0c;这意味着元素会在页面上占据一整行的空间&#xff0c;无论其内部内容的大…

Linux的那些基础常用命令汇总

目录 前言&#xff1a; 用户命令&#xff1a; 管理后台作业命令&#xff1a; 文件目录操作命令&#xff1a; 运维高频使用命令&#xff1a; 磁盘管理以及文件系统命令: 用户、组操作命令&#xff1a; 权限控制命令&#xff1a; 网络配置命令&#xff1a; 软件管理命令…

高效深度学习lecture03

lecture_03 **剪枝&#xff1a;**pruning basically turns a dense neural network into a sparse neural network. you can remove those redundant synapses, and also you can remove those redundant neurons. 剪枝的本质上是将稠密的神经网络转变成稀疏的神经网络&#…

Nextjs15 实战 - React Notes 项目初始化

current branch 对应如下文档 redis ioredis 本专栏内容均可在Github&#xff1a;notes_01 找到 一、效果 完整项目使用技术栈&#xff1a; Nextjs15 MySQL Redis Auth Prisma i18n strapi Docker vercel 二、修改根布局和其他页面 修改 app/page.tsx&#xff1a…

Flutter PopupMenuButton 深度解析:从入门到架构级实战

在移动应用交互设计中&#xff0c;上下文菜单如同隐形的魔法师&#xff0c;在有限屏幕空间中优雅地扩展操作维度。作为Flutter框架中的核心交互组件&#xff0c;PopupMenuButton绝非简单的菜单触发器&#xff0c;其背后蕴含着Material Design的交互哲学、声明式UI的架构智慧以及…

C++——清明

#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory> #include <ctime>using namespace std;class Weapon; // 前置声明class Hero{ pr…

es --- 集群数据迁移

目录 1、需求2、工具elasticdump2.1 mac安装问题解决 2.2 elasticdump文档 3、迁移 1、需求 迁移部分新集群没有的索引和数据 2、工具elasticdump Elasticdump 的工作原理是将输入发送到输出 。两者都可以是 elasticsearch URL 或 File 2.1 mac安装 前置&#xff1a;已经安装…

鸿蒙开发_ARKTS快速入门_语法说明_组件声明_组件手册查看---纯血鸿蒙HarmonyOS5.0工作笔记010

然后我们来看如何使用组件 可以看到组件的组成 可以看到我们使用的组件 然后看一下组件的语法.组件中可以使用子组件. 然后组件中可以有参数,来修改组件的样式等 可以看到{},这种方式可以设置组件参数,当然在下面. 的方式也可以的 然后再来

【GEE学习笔记】报错解决:Sentinel-2 数据集分为 L1C(大气顶层)和 L2A(地表反射率),如何选择波段进行去云处理?

【GEE学习笔记】报错解决&#xff1a;Sentinel-2 数据集分为 L1C&#xff08;大气顶层&#xff09;和 L2A&#xff08;地表反射率&#xff09;&#xff0c;如何选择波段进行去云处理&#xff1f; 【GEE学习笔记】报错解决&#xff1a;Sentinel-2 数据集分为 L1C&#xff08;大…

OpenVLA-OFT——微调VLA时加快推理的三大关键设计:支持动作分块的并行解码、连续动作表示以及L1回归(含输入灵活化及对指令遵循的加强)

前言 25年3.26日&#xff0c;这是一个值得纪念的日子&#xff0c;这一天&#xff0c;我司「七月在线」的定位正式升级为了&#xff1a;具身智能的场景落地与定制开发商 &#xff0c;后续则从定制开发 逐步过渡到 标准产品化 比如25年q2起&#xff0c;在定制开发之外&#xff0…

IDEA 使用Maven打包时内存溢出

IDEA 使用Maven打包时内存溢出 解决办法&#xff1a; File -> settings -> Build,Excetion,Deployment-> Compiler 中添加配置“-Djps.track.ap.dependenciesfalse” 如图&#xff1a;

随机产生4位随机码(java)

Random类&#xff1a; 用于生成随机数 import java.util.Random; 导入必要的类 generateVerificationCode()方法&#xff1a; 这是一个静态方法&#xff0c;可以直接通过类名调用 返回一个6位数字的字符串&#xff0c;首位不为0 生成首位数字&#xff1a; random.nextInt…

C#调用C++动态库时出现`System.DllNotFoundException`错误的解决思路

文章目录 1. DLL文件路径问题2. 依赖的运行时库缺失3. 平台不匹配&#xff08;x86/x64&#xff09;4. 导出函数名称不匹配5. DLL文件损坏或权限问题6. 运行时库冲突&#xff08;MT/MD不匹配&#xff09;7. 使用DLLImport时的常见错误总结步骤 在C#中调用C动态库时出现System.Dl…

免费Deepseek-v3接口实现Browser-Use Web UI:浏览器自动化本地模拟抓取数据实录

源码 https://github.com/browser-use/web-ui 我们按照官方教程&#xff0c;修订几个环节&#xff0c;更快地部署 步骤 1&#xff1a;克隆存储库 git clone https://github.com/browser-use/web-ui.git cd web-ui Step 2: Set Up Python Environment 第 2 步&#xff1a;设置…

ES 参数调优

1、refresh_interval 控制索引刷新的时间间隔。增大这个值可以减少I/O操作&#xff0c;从而提升写入性能&#xff0c;但会延迟新文档的可见性 查看 GET /content_erp_nlp_help_202503191453/_settings?include_defaultstrue 动态修改&#xff1a;refresh_interval 是一个动态…

【Easylive】视频删除方法详解:重点分析异步线程池使用

【Easylive】项目常见问题解答&#xff08;自用&持续更新中…&#xff09; 汇总版 方法整体功能 这个deleteVideo方法是一个综合性的视频删除操作&#xff0c;主要完成以下功能&#xff1a; 权限验证&#xff1a;检查视频是否存在及用户是否有权限删除核心数据删除&…

《比特信使的七重试炼:从数据丢失到CA认证的守护史诗》

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 第一章&#xff1a;初现危机——数据丢失的阴云 比特城的清晨总是被数据流的光芒点亮&#xff0c;但这一天&#xff0c;工程师艾琳的实验室却笼罩在阴霾中。她刚刚尝试通过古老的“疾风…