运动规划实战案例 | 图解基于状态晶格(State Lattice)的路径规划(附ROS C++/Python仿真)

目录

  • 1 控制采样 vs 状态采样
  • 2 State Lattice路径规划
    • 2.1 算法流程
    • 2.2 Lattice运动基元生成
    • 2.3 几何代价函数
    • 2.4 运动学约束启发式
  • 3 算法仿真
    • 3.1 ROS C++仿真
    • 3.2 Python仿真

1 控制采样 vs 状态采样

控制采样的技术路线源自经典的运动学建模思想。这种方法将机器人的控制指令空间进行离散化,预设一组基础运动模式(如固定转向角、恒定速度等),通过前向积分生成候选路径。以差速驱动机器人为例,算法可能预设

  • 左转30度
  • 直行
  • 右转30度

三种基础控制指令,在规划时将这些指令按不同时长组合,形成扇形展开的候选路径集,如下图(a)所示。这种方法的优势在于物理可解释性强,易于求解。但其局限性同样显著:当环境障碍复杂时,由于缺乏目标导向,规划效率较低

在这里插入图片描述

状态采样则直接在目标状态空间(如位置、朝向)中离散化采样点,通过最优控制或数值优化反向求解连接当前状态与目标状态的可行轨迹,如上图(b)所示。例如在自动驾驶场景中,算法可能在车辆前方50米处均匀采样多个目标位姿,再通过多项式曲线或回旋曲线连接起点与终点。这种方法的优势在于解空间覆盖度高,能够生成形态多样的候选路径,特别适合结构化道路中需要精确贴合车道线的场景。但代价是计算复杂度显著增加,反向轨迹求解可能涉及大量迭代优化,实时性面临挑战。

两种方法在工程实践中的博弈,折射出路径规划领域的核心矛盾——解空间完备性与计算效率的平衡。本节介绍的状态晶格State Lattice路径规划就属于状态空间采样类的方法,下面详细阐述

2 State Lattice路径规划

2.1 算法流程

先宏观地展示算法流程

在这里插入图片描述

其中的核心概念总结如下:

  • ​​状态晶格(State Lattice)​​ 将机器人的状态空间(位置、方向等)离散化为一系列相连的状态点,形成网格状结构
  • ​​开节点表(Open List)​​ 存储待评估探索的节点集合,按照代价排序
  • ​​闭节点表(Closed List) 存储已经评估处理过的节点集合
  • ​​节点(Node) 表示状态空间中的一个点,包含位置、方向、代价等信息
  • ​​运动基元(Motion Primitive)​​ 预定义的合法移动方式,确保路径满足动力学约束

可以看到,State Lattice同样遵守A*算法框架,可以对比:

  • 路径规划 | 图解A*、Dijkstra、GBFS算法的异同(附C++/Python/Matlab仿真)
  • 路径规划 | 详解混合A*算法Hybrid A*(附ROS C++/Python/Matlab仿真)

不同在于,State Lattice规划器在状态空间采样一系列节点生成运动基元,而A*或混合A*算法则是在控制空间采样。那么,状态空间运动基元是如何生成的呢?

2.2 Lattice运动基元生成

设机器人状态空间为

s = [ x , y , θ ] T s=[x,y,\theta]^T s=[x,y,θ]T

如下图所示,机器人用绿色矩形框表示,在圆周上等距离地采样一系列点作为状态采样点 [ x i , y i , θ i ] [x_i,y_i, \theta_i] [xi,yi,θi],问题转变为已知起始状态 [ x 0 , y 0 , θ 0 ] [x_0,y_0,\theta_0] [x0,y0,θ0]和各个终止状态 [ x i , y i , θ i ] [x_i,y_i, \theta_i] [xi,yi,θi],如何生成一条连接两个状态的运动学可行路径的问题,即如何生成下图所示的蓝色与红色路径

在这里插入图片描述

求解的方法有很多,例如转化为两点边值问题、最优控制优化问题等,但为了简明起见,本节介绍一种解析几何的方法。如下图所示,设首末方向向量交点为 P P P,首末端点分别为 A A A B B B,不失一般性假定 ∣ P A ∣ ≥ ∣ P B ∣ |PA|≥|PB| PAPB,则在线段 P A PA PA上从 P P P出发截取 ∣ P B ∣ |PB| PB长度的子线段 P C PC PC,以 B B B C C C为两个端点构造圆弧,产生由 A C AC AC C B ⏠ \overgroup{CB} CB 组成的单线段单圆弧轨迹;特别地,当 ∣ P A ∣ = ∣ P B ∣ \left| PA \right|=\left| PB \right| PA=PB ∣ A C ∣ = 0 \left| AC \right|=0 AC=0,退化为单圆弧轨迹

在这里插入图片描述

2.3 几何代价函数

基于上述几何关系可以定义基本代价函数

C a d j u s t = { C b a s e i f 直线运动 C b a s e ⋅ P n o n s t r a i g h t i f 同向转弯 C b a s e ⋅ ( P n o n s t r a i g h t + P c h a n g e ) i f 转向切换 C_{\mathrm{adjust}}=\begin{cases} C_{\mathrm{base}}\,\,& \mathrm{if} \text{直线运动}\\ C_{\mathrm{base}}\cdot P_{\mathrm{nonstraight}}& \mathrm{if} \text{同向转弯}\\ C_{\mathrm{base}}\cdot \left( P_{\mathrm{nonstraight}}+P_{\mathrm{change}} \right)& \mathrm{if} \text{转向切换}\\\end{cases} Cadjust= CbaseCbasePnonstraightCbase(Pnonstraight+Pchange)if直线运动if同向转弯if转向切换

其中 C b a s e = L p r i m ⋅ P t r a v e l C_{\mathrm{base}}=L_{\mathrm{prim}}\cdot P_{\mathrm{travel}} Cbase=LprimPtravel L p r i m L_{\mathrm{prim}} Lprim是运动基元路径长度。为了考虑纯转向和反向运动,进一步修正代价函数为

C = { P r o t a t e i f L p r i m < ϵ C a d j u s t ⋅ P r e v e r s e i f 反向运动 C a d j u s t o t h e r w i s e C=\begin{cases} P_{\mathrm{rotate}}\,\,& \mathrm{if} L_{\mathrm{prim}}<\epsilon\\ C_{\mathrm{adjust}}\cdot P_{\mathrm{reverse}}& \mathrm{if} \text{反向运动}\\ C_{\mathrm{adjust}}& \mathrm{otherwise}\\\end{cases} C= ProtateCadjustPreverseCadjustifLprim<ϵif反向运动otherwise

2.4 运动学约束启发式

A*算法的启发式函数一般采用当前点到目标点的欧氏距离,State Lattice算法则向启发式函数进一步引入运动学约束

h ( n ) = max ⁡ { C o n s t r a i n e d C o s t , U n c o n s t r a i n e d C o s t } h\left( n \right) =\max \left\{ \mathrm{ConstrainedCost},\mathrm{UnconstrainedCost} \right\} h(n)=max{ConstrainedCost,UnconstrainedCost}

其中:

  • C o n s t r a i n e d C o s t \mathrm{ConstrainedCost} ConstrainedCost:只考虑车辆的非完整运动学约束而不考虑障碍物的有约束启发项(Constrained heuristics),通常采用Dubins或Reeds-Shepp曲线计算该项损失。
    • Dubins曲线是指由美国数学家 Lester Dubins 在20世纪50年代提出的一种特殊类型的最短路径曲线。这种曲线通常用于描述在给定转弯半径下的无人机、汽车或船只等载具的最短路径,其特点是起始点和终点处的切线方向和曲率都是已知的,Dubins曲线包括直线段和最大转弯半径下的圆弧组成,通过合适的组合可以实现从一个姿态到另一个姿态的最短路径规划。更详细的算法原理请看曲线生成 | 图解Dubins曲线生成原理(附ROS C++/Python/Matlab仿真);
    • Reeds-Shepp曲线是一种用于描述在平面上从一个点到另一个点最优路径的数学模型。这种曲线是由美国数学家 J. A. Reeds 和 L. A. Shepp 在1990年提出的,它被广泛应用于路径规划和运动规划问题中,具有最优性、约束性和多样性,更详细的算法原理请看曲线生成 | 图解Reeds-Shepp曲线生成原理(附ROS C++/Python/Matlab仿真);
  • 只考虑障碍物信息而不考虑车辆运动学特性的无约束启发项(Unconstrained heuristics),通常采用Dijkstra或A*算法计算该项损失。

如图所示,可视化了不同类型的启发项。当环境障碍不影响规划路径时,有约束启发项损失往往大于无约束,因为后者没有考虑朝向和运动限制;当环境障碍影响规划路径时,有约束启发项损失往往小于无约束,因为后者会进行避障。因此对两项取 max ⁡ \max max算子可以综合障碍影响和运动学特性,更符合真实情况。

在这里插入图片描述

3 算法仿真

3.1 ROS C++仿真

核心代码如下所示

bool StateLatticePathPlanner::createPath(const Point3d& start, const Point3d& goal, Points3d& path, Points3d& expand)
{clearGraph();clearQueue();auto start_node = addToGraph(getIndex(start));auto goal_node = addToGraph(getIndex(goal));precomputeObstacleHeuristic(goal_node);// 0) Add starting point to the open setaddToQueue(0.0, start_node);start_node->setAccumulatedCost(0.0);std::vector<NodeLattice::NodePtr> neighbors;  // neighbors of current nodeNodeLattice::NodePtr neighbor = nullptr;// main loopint iterations = 0, approach_iterations = 0;while (iterations < search_info_.max_iterations && !queue_.empty()){// 1) Pick the best node (Nbest) from open listNodeLattice::NodePtr current_node = queue_.top().second;queue_.pop();// Save current node coordinates for debugexpand.emplace_back(current_node->pose().x(), current_node->pose().y(),motion_table_.getAngleFromBin(current_node->pose().theta()));// Current node exists in closed listif (current_node->is_visited()){continue;}iterations++;// 2) Mark Nbest as visitedcurrent_node->visited();// 2.1) Use an analytic expansion (if available) to generate a pathNodeLattice::NodePtr expansion_result = tryAnalyticExpansion(current_node, goal_node);if (expansion_result != nullptr){current_node = expansion_result;}// 3) Goal foundif (current_node == goal_node){return backtracePath(current_node, path);}// 4) Expand neighbors of Nbest not visitedneighbors.clear();getNeighbors(current_node, neighbors);for (auto neighbor_iterator = neighbors.begin(); neighbor_iterator != neighbors.end(); ++neighbor_iterator){neighbor = *neighbor_iterator;// 4.1) Compute the cost to go to this nodedouble g_cost = current_node->accumulated_cost() + current_node->getTraversalCost(neighbor, motion_table_);// 4.2) If this is a lower cost than prior, we set this as the new cost and new approachif (g_cost < neighbor->accumulated_cost()){neighbor->setAccumulatedCost(g_cost);neighbor->parent = current_node;// 4.3) Add to queue with heuristic costaddToQueue(g_cost + search_info_.lamda_h * getHeuristicCost(neighbor, goal_node), neighbor);}}}return false;
}

在这里插入图片描述

3.2 Python仿真

核心代码如下所示

def plan(self, start: Point3d, goal: Point3d) -> Tuple[List[Point3d], List[Dict]]:"""State Lattice motion plan function"""start_node = Point3d(start.x(), start.y(), self.motion_table.getOrientationBin(start.theta()))goal_node = Point3d(goal.x(), goal.y(), self.motion_table.getOrientationBin(goal.theta()))self.start = self.addToGraph(self.getIndex(start_node))self.goal = self.addToGraph(self.getIndex(goal_node))self.obstacle_htable = self.precomputeObstacleHeuristic(goal)# 0) Add starting point to the open setself.queue.clear();heapq.heappush(self.queue, QueueNode(0.0, self.start))self.start.setAccumulatedCost(0.0)# main loopiterations = 0path, expand = [], []while iterations < self.max_iterations and self.queue:# 1) Pick the best node (Nbest) from open listcurr_queue_node = heapq.heappop(self.queue)current = curr_queue_node.node# Save current node coordinates for debugif current.parent:curr_pose, parent_pose = current.pose, current.parent.posechild = Point3d(curr_pose.x(), curr_pose.y(), self.motion_table.getAngleFromBin(curr_pose.theta()))parent = Point3d(parent_pose.x(), parent_pose.y(), self.motion_table.getAngleFromBin(parent_pose.theta()))expand.append((child, parent))# Current node exists in closed listif current.is_visited:continueiterations += 1# 2) Mark Nbest as visitedcurrent.is_visited = True# 2.1) Use an analytic expansion (if available) to generate a pathexpansion_result = self.tryAnalyticExpansion(current)if expansion_result:current = expansion_result[-1]# 3) Goal foundif self.isReachGoal(current):cost, path = self.backtracePath(current)return path# 4) Expand neighbors of Nbest not visitedneighbors = current.getNeighbors(neighborGetter, self.getIndex, self.isCollision, self.motion_table)for neighbor in neighbors:# 4.1) Compute the cost to go to this nodeg_cost = current.accumulated_cost + current.getTraversalCost(neighbor, self.motion_table)# 4.2) If this is a lower cost than prior, we set this as the new cost and new approachif g_cost < neighbor.accumulated_cost:neighbor.accumulated_cost = g_costneighbor.parent = current# 4.3) Add to queue with heuristic costf_cost = g_cost + 1.5 * self.getHeuristicCost(neighbor.pose)heapq.heappush(self.queue, QueueNode(f_cost, neighbor))LOG.INFO("Planning Failed.")return []

在这里插入图片描述

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


🔥 更多精彩专栏

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

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

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

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

相关文章

BERT框架:自然语言处理的革命性突破

引言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;2018年Google推出的BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;框架无疑是一场革命。作为基于Transformer架构的双向编码器表示模型&#xff0c;BERT通过预训练学习…

【Fifty Project - D31】

结束了一个超级消耗周末&#xff0c;满安排之健身梅溪湖游泳做饭喝酒羽毛球赛 完全力竭了&#xff0c;久久不能恢复过来&#xff0c;暂停健身安排了 端午后再继续 今日完成记录 TimePlan完成情况7&#xff1a;30 - 8&#xff1a;10有氧爬坡√9&#xff1a;00 - 11&#xff1a;…

信息学奥赛一本通 1547:【 例 1】区间和

【题目链接】 ybt 1547&#xff1a;【 例 1】区间和 【题目考点】 1. 线段树 2. 树状数组 【解题思路】 本题要求维护区间和&#xff0c;实现单点修改、区间查询。 解法1&#xff1a;线段树 线段树原理&#xff0c;及实现方法见&#xff1a;洛谷 P3374 【模板】树状数组…

力扣面试150题--求根节点到叶节点数字之和

Day 48 题目描述 思路 我们利用sum这个全局变量来保存总和值&#xff0c;递归函数sum来计算每个根到叶子节点路径所代表的数&#xff0c;由于我们需要遍历到每条根到叶子节点的路径&#xff0c;所有我采取了前序遍历&#xff0c;如果不是叶子节点&#xff0c;就计算到该节点代…

DJI上云API官方demo学习

1、websocket&#xff0c;所在位置如下图&#xff0c;调用的可以用//websocket搜索 2、用到的http客户端&#xff0c;axios 3、很多和后端交互都是走的http请求

uniapp开发小程序,如何根据权限动态配置按钮或页面内容

前言 写了好几个项目&#xff0c;发现小程序对权限控制非常麻烦&#xff0c;于是有了这个想法&#xff0c;但是网上找了一圈没有一个比较完善的讲解&#xff0c;因为小程序不支持自定义指令&#xff0c;所以不能像后台那样方便&#xff0c;于是就将几个博主的想法结合。 思路就…

LSTM+Transformer混合模型架构文档

LSTMTransformer混合模型架构文档 模型概述 本项目实现了一个LSTMTransformer混合模型&#xff0c;用于超临界机组协调控制系统的数据驱动建模。该模型结合了LSTM的时序建模能力和Transformer的自注意力机制&#xff0c;能够有效捕捉时间序列数据中的长期依赖关系和变量间的复…

测量尺子:多功能测量工具,科技改变生活

测量尺子是一款专业的测距仪测量万能工具箱类型手机APP&#xff0c;旨在为用户提供最贴心的测量助手。它拥有和现实测量仪器一样的测量标准&#xff0c;更简单便捷且精准的测量方式&#xff0c;最新AR科技测量更是大大拓宽了可以被测量的高度和深度。无论是日常使用、学习还是工…

结课作业01. 用户空间 MPU6050 体感鼠标驱动程序

目录 一. qt界面实现 二. 虚拟设备模拟模拟鼠标实现体感鼠标 2.1 函数声明 2.2 虚拟鼠标实现 2.2.1 虚拟鼠标创建函数 2.2.2 鼠标移动函数 2.2.3 鼠标点击函数 2.3 mpu6050相关函数实现 2.3.1 i2c设备初始化 2.3.2 mpu6050寄存器写入 2.3.3 mpu6050寄存器读取 2.3.…

深入浅出 Python Testcontainers:用容器优雅地编写集成测试

在现代软件开发中&#xff0c;自动化测试已成为敏捷开发与持续集成中的关键环节。单元测试可以快速验证函数或类的行为是否符合预期&#xff0c;而集成测试则确保多个模块协同工作时依然正确。问题是&#xff1a;如何让集成测试可靠、可重复且易于维护&#xff1f; 这时&#…

JVM 的垃圾回收器

新生代回收器 通性 会触发StW&#xff0c;暂停所有应用线程复制算法 Serial 单线程回收适合单线程系统 ParNew 多线程回收优先保证响应速度&#xff0c;降低 STW&#xff08;STW 越大&#xff0c;执行垃圾回收的时间越长&#xff0c;回收的垃圾越多&#xff0c;减少垃圾回…

【笔记】排查并解决Error in LLM call after 3 attempts: (status code: 502)

#工作记录 一、问题描述 在部署运行部署对冲基金分析工具 ai-hedge-fund 时&#xff0c;不断出现以下报错&#xff0c;导致项目运行异常&#xff1a; Error in LLM call after 3 attempts: (status code: 502) Error in LLM call after 3 attempts: [WinError 10054] 远程主…

GO 语言进阶之 Template 模板使用

更多个人笔记见&#xff1a; github个人笔记仓库 gitee 个人笔记仓库 个人学习&#xff0c;学习过程中还会不断补充&#xff5e; &#xff08;后续会更新在github上&#xff09; 文章目录 Template 模板基本示例语法1. 基本输出语法2. 控制结构3. 空白字符控制4. Must函数 Temp…

origin绘图之【如何将多条重叠、高度重叠的点线图、折线图分开】

在日常的数据可视化工作中&#xff0c;Origin 作为一款功能强大的科研绘图软件&#xff0c;广泛应用于实验数据处理、结果展示与论文图表制作等领域。然而&#xff0c;在处理多组数据、特别是绘制多条曲线的折线图或点线图时&#xff0c;常常会遇到这样一个困扰&#xff1a;多条…

Java基础 Day19

一、泛型&#xff08;JDK5引入&#xff09; 1、基本概念 在编译阶段约束操作的数据类型&#xff0c;并进行检查 好处&#xff1a;统一数据类型&#xff0c;将运行期的错误提升到了编译期 泛型的默认类型是 Object 2、泛型类 在创建类的时候写上泛型 在创建具体对象的时候…

Gitlab-Runner安装

文章目录 helm方式安装在K8S上参考gitlab CI/CD 文件变量缓存服务器K8S部署 docker镜像mavendocker安装docker buildx minionodehelmkubectlsonar-scanner-cli 问题清除cachehelm执行时无权限 下载镜像失败下载gitlab-runner镜像失败 Gitlab-ci中使用java前端 helm方式安装在K8…

在 Ubuntu linux系统中设置时区的方案

查看时区 在 Ubuntu 系统中&#xff0c;可以通过以下方法查看当前时区设置&#xff1a; 1. 使用 timedatectl 命令&#xff08;推荐&#xff09; 在终端运行以下命令&#xff1a; timedatectl输出示例&#xff1a; Local time: Sun 2025-05-25 10:30:00 CST Universal t…

YOLOv8模型剪枝笔记(DepGraph和Network Slimming网络瘦身)

文章目录 一、DepGraph剪枝(1)项目准备1)剪枝基础知识2)DepGraph剪枝论文解读12)DepGraph剪枝论文解读23)YOLO目标检测系列发展史4)YOLO网络架构(2)项目实战(YOLOv8应用DepGraph剪枝+finetune)1)安装软件环境(基础环境、Pytorch、YOLOv8)Windows1)安装软件环境(…

MySQL:11_事务

事务 一.CURD不加控制&#xff0c;会有什么问题&#xff1f; 二.什么是事务&#xff1f; 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&#xff0c;要么全部失败&#xff0c;是一个整体。MySQL提供一种机制&#xf…

【notepad++如何设置成中文界面呢?】

“Notepad”是一款非常强大的文本编辑软件&#xff0c;将其界面设置成中文的方法如下&#xff1a; 一、工具&#xff0f;原料&#xff1a; 华为 Matebook 15、Windows 10、Notepad 8.4.6。 二 、具体步骤&#xff1a; 1、找到任意一个文本文件&#xff0c;比如 txt 格式的文…