曲线生成 | 图解Reeds-Shepp曲线生成原理(附ROS C++/Python/Matlab仿真)

news/2024/4/27 17:13:39/文章来源:https://blog.csdn.net/FRIGIDWINTER/article/details/136994820

目录

  • 0 专栏介绍
  • 1 什么是Reeds-Shepp曲线?
  • 2 Reeds-Shepp曲线的运动模式
  • 3 Reeds-Shepp曲线算法原理
    • 3.1 坐标变换
    • 3.2 时间翻转(time-flip)
    • 3.3 反射变换(reflect)
    • 3.4 后向变换(backwards)
  • 4 仿真实现
    • 4.1 ROS C++实现
    • 4.2 Python实现
    • 4.3 Matlab实现

0 专栏介绍

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

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


1 什么是Reeds-Shepp曲线?

Reeds-Shepp曲线是一种用于描述在平面上从一个点到另一个点最优路径的数学模型。这种曲线是由美国数学家 J. A. Reeds 和 L. A. Shepp 在1990年提出的,它被广泛应用于路径规划和运动规划问题中。Reeds-Shepp曲线的很多原理和Dubins曲线类似,可以先学习曲线生成 | 图解Dubins曲线生成原理(附ROS C++/Python/Matlab仿真)

在这里插入图片描述

Reeds-Shepp曲线具有以下特点:

  • 最优性:Reeds-Shepp曲线是连接两个点的最短路径之一,通常是沿着曲线长度最短的路径。相比于Dubins曲线只允许车辆向前运动,RS曲线同时允许车辆前向、后向运动,使得在某些情况下可以得出比 Dubins 曲线更优的解
  • 约束性:曲线遵循机器人或车辆的运动学约束,例如最大转角、最大速度等。
  • 多样性:存在不同类型的Reeds-Shepp曲线,例如直线-圆弧-直线(L-S-L)、直线-圆弧-反向圆弧-直线(L-S-R-S)等,以适应不同场景下的路径规划需求。

通过计算和生成Reeds-Shepp曲线,可以帮助机器人或车辆高效地规划路径并完成复杂的运动任务。

2 Reeds-Shepp曲线的运动模式

经过证明,RS曲线从起点到终点的最短路径一定是下面的组合之一

{ C ∣ C ∣ C , C C ∣ C , C ∣ C C , C S C , C C β ∣ C β C , C ∣ C β C β ∣ C , C ∣ C π / 2 S C , C S C π / 2 ∣ C , C ∣ C π / 2 S C π / 2 ∣ C } \left\{ \begin{array}{c} C|C|C, CC|C, C|CC, CSC, CC_{\beta}|C_{\beta}C, C|C_{\beta}C_{\beta}|C,\\ C|C_{{{\pi}/{2}}}SC, CSC_{{{\pi}/{2}}}|C, C|C_{{{\pi}/{2}}}SC_{{{\pi}/{2}}}|C\\\end{array} \right\} {CCC,CCC,CCC,CSC,CCβCβC,CCβCβC,CCπ/2SC,CSCπ/2C,CCπ/2SCπ/2C}

其中 C C C表示圆弧运动, S S S表示直线运动,|表示车辆运动朝向发生改变。带 π / 2 \pi/2 π/2下标表示该段轨迹弧长对应的角度为 π / 2 \pi/2 π/2,带 β \beta β下标表示相邻两段轨迹弧长对应的角度相等。将上述组合完整展开后对应如表所示的48种运动模式,其中+代表前行,-代表倒车。后续经过证明, ( L − R + L − ) \left( L^-R^+L^- \right) (LR+L) ( R − L + R − ) \left( R^-L^+R^- \right) (RL+R)两种序列是多余的。

在这里插入图片描述

RS曲线在实现上的复杂度远远高于只有6种组合的Dubins曲线,考虑到序列间的对称关系,引入下面的变换简化曲线求解过程。

3 Reeds-Shepp曲线算法原理

3.1 坐标变换

类似Dubins曲线的思想进行坐标变换。在全局坐标系 x O y xOy xOy中,设机器人起始位姿 p s \boldsymbol{p}_s ps、终止位姿 p g \boldsymbol{p}_g pg、最小转弯半径分别为 ( x s , y s , α ) \left( x_s,y_s,\alpha \right) (xs,ys,α) ( x g , y g , β ) \left( x_g,y_g,\beta \right) (xg,yg,β) R R R。以 p s \boldsymbol{p}_s ps为新坐标系原点,位姿角 α \alpha α方向为 x ′ x' x轴,垂直方向为 y ′ y' y轴建立新坐标系 ,同样考虑归一化最小转弯半径

p s ′ = [ 0 0 0 ] , p g ′ = [ ( x g cos ⁡ β + y g sin ⁡ β ) R ( − x g sin ⁡ β + y g cos ⁡ β ) R β − α ] \boldsymbol{p}_{s}^{'}=\left[ \begin{array}{c} 0\\ 0\\ 0\\\end{array} \right] , \boldsymbol{p}_{g}^{'}=\left[ \begin{array}{c} \left( x_g\cos \beta +y_g\sin \beta \right) R\\ \left( -x_g\sin \beta +y_g\cos \beta \right) R\\ \beta -\alpha\\\end{array} \right] ps= 000 ,pg= (xgcosβ+ygsinβ)R(xgsinβ+ygcosβ)Rβα

3.2 时间翻转(time-flip)

将计算曲线的运动方向全部取反,得到的新曲线与原曲线具有时间翻转关系。如图所示,以 L − R + S + L + ↔ L + R − S − L − L^-R^+S^+L^+\leftrightarrow L^+R^-S^-L^- LR+S+L+L+RSL为例解释时间翻转:设实现了对 L − R + S + L + L^-R^+S^+L^+ LR+S+L+的计算 f ( x , y , ϕ ) f\left( x,y,\phi \right) f(x,y,ϕ),若用同样的函数计算 f ( − x , y , − ϕ ) f\left( -x,y,-\phi \right) f(x,y,ϕ),并将各段路径取反,则等价于以轨迹 L + R − S − L − L^+R^-S^-L^- L+RSL到达 ( x , y , ϕ ) \left( x,y,\phi \right) (x,y,ϕ)

在这里插入图片描述

3.3 反射变换(reflect)

将计算曲线的圆周运动类型全部取反,得到的新曲线与原曲线具有反射变换关系。如图所示,以 L − R + S + L + ↔ R − L + S + R + L^-R^+S^+L^+\leftrightarrow R^-L^+S^+R^+ LR+S+L+RL+S+R+为例解释仿射变换:设实现了对 L − R + S + L + L^-R^+S^+L^+ LR+S+L+的计算 f ( x , y , ϕ ) f\left( x,y,\phi \right) f(x,y,ϕ),若用同样的函数计算 f ( x , − y , − ϕ ) f\left( x,-y,-\phi \right) f(x,y,ϕ),并将圆弧段类型取反,则等价于以轨迹 R − L + S + R + R^-L^+S^+R^+ RL+S+R+到达 ( x , y , ϕ ) \left( x,y,\phi \right) (x,y,ϕ)

在这里插入图片描述

3.4 后向变换(backwards)

将计算曲线的轨迹段逆序,得到的新曲线与原曲线具有后向变换关系。如图所示,以 L − R + S + L + ↔ L + S + R + L − L^-R^+S^+L^+\leftrightarrow L^+S^+R^+L^- LR+S+L+L+S+R+L为例解释后向变换:设实现了对 L − R + S + L + L^-R^+S^+L^+ LR+S+L+的计算 f ( x , y , ϕ ) f\left( x,y,\phi \right) f(x,y,ϕ),若用同样的函数计算 f ( x cos ⁡ ϕ + y sin ⁡ ϕ , x sin ⁡ ϕ − y cos ⁡ ϕ , ϕ ) f\left( x\cos \phi +y\sin \phi ,x\sin \phi -y\cos \phi ,\phi \right) f(xcosϕ+ysinϕ,xsinϕycosϕ,ϕ),并将计算曲线逆序,则等价于以轨迹 L + S + R + L − L^+S^+R^+L^- L+S+R+L到达 ( x , y , ϕ ) \left( x,y,\phi \right) (x,y,ϕ)

在这里插入图片描述

4 仿真实现

4.1 ROS C++实现

核心代码如下所示

Points2d ReedsShepp::generation(Pose2d start, Pose2d goal)
{...// coordinate transformation...// select the best motionRSPath best_path({ REEDS_SHEPP_MAX }, { REEDS_SHEPP_NONE });_update(SCS(x, y, dyaw), best_path);_update(CCC(x, y, dyaw), best_path);_update(CSC(x, y, dyaw), best_path);_update(CCCC(x, y, dyaw), best_path);_update(CCSC(x, y, dyaw), best_path);_update(CCSCC(x, y, dyaw), best_path);if (best_path.len() == REEDS_SHEPP_MAX)return path;// interpolationint points_num = int(best_path.len() / step_) + 6;int i = 0;for (size_t j = 0; j < best_path.size(); j++){int m;double seg_length;best_path.get(j, seg_length, m);// path incrementdouble d_l = seg_length > 0.0 ? step_ : -step_;double x = path_x[i];double y = path_y[i];double yaw = path_yaw[i];// current path lengthdouble l = d_l;while (fabs(l) <= fabs(seg_length)){i += 1;std::tie(path_x[i], path_y[i], path_yaw[i]) = interpolate(m, l, { x, y, yaw });l += d_l;}i += 1;std::tie(path_x[i], path_y[i], path_yaw[i]) = interpolate(m, seg_length, { x, y, yaw });}// remove unused data...// coordinate transformation...return path;
}

4.2 Python实现

核心代码如下所示

def generation(self, start_pose: tuple, goal_pose: tuple):sx, sy, syaw = start_posegx, gy, gyaw = goal_pose# coordinate transformation...# select the best motionplanners = [self.SCS, self.CCC, self.CSC, self.CCCC, self.CCSC, self.CCSCC]best_path, best_cost = None, float("inf")for planner in planners:paths = planner(x, y, dyaw)for path in paths:if path.path_length < best_cost:best_path, best_cost = path, path.path_length# interpolationpoints_num = int(best_cost / self.step) + len(best_path.lengths) + 3x_list = [0.0 for _ in range(points_num)]y_list = [0.0 for _ in range(points_num)]yaw_list = [0.0 for _ in range(points_num)]i = 0for mode_, seg_length in zip(best_path.ctypes, best_path.lengths):# path incrementd_length = self.step if seg_length > 0.0 else -self.stepx, y, yaw = x_list[i], y_list[i], yaw_list[i]# current path lengthlength = d_lengthwhile abs(length) <= abs(seg_length):i += 1x_list[i], y_list[i], yaw_list[i] = self.interpolate(mode_, length, (x, y, yaw))length += d_lengthi += 1x_list[i], y_list[i], yaw_list[i] = self.interpolate(mode_, seg_length, (x, y, yaw))# failed...# remove unused data...# coordinate transformation...return best_cost / self.max_curv, best_path.ctypes, x_list_, y_list_, yaw_list_

在这里插入图片描述

4.3 Matlab实现

核心代码如下所示

function [x_list, y_list, yaw_list] = generation(start_pose, goal_pose, param)  % coordinate transformation...% select the best motionplanners = ["SCS", "CCC", "CSC", "CCCC", "CCSC", "CCSCC"];best_cost = inf;best_path = [];for i=1:length(planners)planner = str2func(planners(i));paths = planner(x, y, dyaw);for j=1:length(paths)if paths(j).len < best_costbest_path = paths(j);best_cost = paths(j).len;endendend% interpolationpoints_num = floor(best_cost / param.step) + length(best_path.segs) + 3;x_list_ = zeros(points_num);y_list_ = zeros(points_num);yaw_list_ = zeros(points_num);i = 1;for j = 1:length(best_path.segs)m = best_path.ctypes(j);seg_length = best_path.segs(j);% path incrementif seg_length > 0.0d_length = param.step;elsed_length = -param.step;endx = x_list_(i); y = y_list_(i); yaw = yaw_list_(i);% current path lengthl = d_length;while abs(l) <= abs(seg_length)i = i + 1;new_pt = interpolate(m, l, [x, y, yaw], param);x_list_(i) = new_pt(1); y_list_(i) = new_pt(2); yaw_list_(i) = new_pt(3);l = l + d_length;endi = i + 1;new_pt = interpolate(m, seg_length, [x, y, yaw], param);x_list_(i) = new_pt(1); y_list_(i) = new_pt(2); yaw_list_(i) = new_pt(3);end% remove unused data...% coordinate transformation...
end

在这里插入图片描述

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


🔥 更多精彩专栏

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

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

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

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

相关文章

MP4如何把视频转MOV格式? MP4视频转MOV格式的技巧

在现代的数字媒体时代&#xff0c;视频格式转换成为了许多用户必须掌握的技能。特别是将MP4视频转换为MOV格式&#xff0c;这对于需要在Apple设备上播放或编辑视频的用户来说尤为重要。本文将详细介绍如何将MP4视频转换为MOV格式&#xff0c;帮助读者轻松应对不同设备和平台的需…

编程语言|C语言——C语言基本数据类型

前言 针对不同的数据&#xff0c;采取不同的存储方式和进行不同的处理。随着处理对象的复杂化&#xff0c;数据类型也要变得更丰富。数据类型的丰富程度直接反映了程序设计语言处理数据的能力。 C语言很重要的一个特点是它的数据类型十分丰富。因此&#xff0c;C语言程序数据处…

【Nuxt3】modules目录和nuxt3模块的简单介绍

简言 记录下nuxt3项目中module的用法 modules目录 使用 modules/ 目录在应用程序中自动注册本地模块。 这是一个很好的地方&#xff0c;可以放置您在构建应用程序时开发的任何本地nuxt模块。 nuxt模块相当于npm包&#xff0c;可以发布到npm社区中 在modules/ 目录下的本地模…

二叉树|654.最大二叉树

力扣题目地址 class Solution { public:TreeNode* constructMaximumBinaryTree(vector<int>& nums) {TreeNode* node new TreeNode(0);if (nums.size() 1) {node->val nums[0];return node;}// 找到数组中最大的值和对应的下标int maxValue 0;int maxValueIn…

Kubernetes生产集群部署指南

部署生产就绪的Kubernetes集群需要考虑到管理、负载均衡、安全、存储等很多细节&#xff0c;本文给出了一个生产就绪Kubernetes集群的完整部署流程&#xff0c;可以作为生产部署的有效参考。原文: Deploying a Production Kubernetes Cluster in 2023 — A Complete Guide Grow…

万兆车载以太网转换器 10G/2.5G多速车载以太网转换器-MC10GM

MC10GM转换器 一、产品简要分析 2.5G,5G,10G可切换万兆/多速车载以太网转换器。采用罗森博格H-MTD标准接口类型。实现将车载以太网标准2.5/5/10G BASE-T1转换为工业级2.5/5/10G 标准以太网&#xff0c;进而接入电脑或工控机. 产品实现2.5/5/10G Base-T1 和2.5/5/10G Base-R之间…

android Fragment 生命周期 方法调用顺序

文章目录 Introlog 及结论代码 Intro 界面设计&#xff1a;点击左侧按钮&#xff0c;会将右侧 青色的RightFragment 替换成 黄色的AnotherRightFragment&#xff0c;而这两个 Fragment 的生命周期方法都会打印日志。 所以只要看执行结果中的日志&#xff0c;就可以知道 Fragme…

CSS时钟案例

文章目录 1. 演示效果2. 分析思路3. 代码实现 1. 演示效果 2. 分析思路 背景是表盘&#xff0c;不用自己制作然后用CSS的定位做时针&#xff0c;分针和秒针黑点用伪元素::after生成转动用animation实现 3. 代码实现 <!DOCTYPE html> <html lang"en">&…

【详细讲解React 快速入门教程】

&#x1f525;博主&#xff1a;程序员不想YY啊&#x1f525; &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f4ab; &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 &#x1f308;希望本文对您有所裨益&#xff0c;如有…

Python中的变量与常量

变量&#xff1a;在程序运行过程中&#xff0c;值会发生变化的量&#xff0c; 常量&#xff1a;在程序运行过程中&#xff0c;值不会发生变化的量。 无论是变量还是常量&#xff0c;在创建时都会在内存中开辟一块空间&#xff0c;用于保存它的值。 Python 中的变量不需要声明…

数据链路层协议之以太网协议

以太网协议是通过网线/光纤进行通信。这和通过wifi&#xff08;无线&#xff09;&#xff0c;通过移动流量&#xff08;4G/5G&#xff09;通信不一样。以太网&#xff0c;横跨数据链路层和物理层 一.以太网数据帧格式 包括了帧头载荷(IP数据报)帧尾。 1.目的地址 源地址 分别…

初探Flink集群【持续更新】

周末下雨&#xff0c;倒杯茶&#xff0c;在家练习Flink相关。 开发工具&#xff1a;IntelliJ Idea 第一步、创建项目 打开Idea&#xff0c;新建Maven项目&#xff0c;包和项目命名 在pom.xml 文件中添加依赖 <properties><flink.version>1.13.0</flink.vers…

【Redis主从架构。主从工作原理psync、bgsave、部分数据复制、主从复制风暴解决方案】【Redis哨兵高可用架构。sentinel】

Redis主从架构 Redis主从工作原理数据部分复制 Redis哨兵高可用架构client连接哨兵规则主节点挂了&#xff0c;集群从新选择主节点&#xff0c;并且同步给sentinel 转自图灵课堂 redis主从架构搭建&#xff0c;配置从节点步骤&#xff1a; 1、复制一份redis.conf文件2、将相关…

六大原则与设计模式

1. 六大原则 1.1 单一原则&#xff08;SRP&#xff09; 应该有且仅有一个原因引起类的变更 1. 复杂性降低&#xff0c;可读性高&#xff0c;可维护性提高 2. 变更引起的风险降低&#xff0c;变更是必不可少的&#xff0c;如果接口的单一职责做得好&#xff0c;一个接口修改…

基于单片机多功能智能台灯设计

**单片机设计介绍&#xff0c;基于单片机多功能智能台灯设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的多功能智能台灯设计是一个集硬件与软件于一体的综合性项目&#xff0c;旨在为用户提供更加便捷、舒适和节…

如何借用 NTFS 交换数据流 实现隐藏文件?如何使用【文件包含】PHP伪协议?不同操作系统如何实现文件隐藏和木马伪装?

如何借用 NTFS 交换数据流 实现隐藏文件?如何使用【文件包含】PHP伪协议?不同操作系统如何实现文件隐藏和木马伪装? NTFS交换数据流(Alternate Data Streams, ADS)是NTFS文件系统特有的一种功能,它允许在同一个文件名下存储多个数据流。除了默认的数据流(通常用于存储文…

day3-QT

1>使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函。将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是…

Java SPI 机制

SPI 机制的定义 在Java中&#xff0c;SPI&#xff08;Service Provider Interface&#xff09;机制是一种用于实现软件组件之间松耦合的方式。它允许在应用程序中定义服务接口&#xff0c;并通过在类路径中发现和加载提供该服务的实现来扩展应用程序功能。 SPI 机制通常涉及三…

DBA工作经验总结

目录 一、MySQL8.0创建一张规范的表 1.表、字段全采用小写 2.int类型不再加上最大显示宽度 3.每张表必须显式定义自增int类型的主键 4.建表时增加comment来描述字段和表的含义&#xff08;防止以后忘记&#xff09; 5.建议包含create_time和update_time字段 6.核心业务增…

asp程序之“会话Cookie中缺少HttpOnly属性”

先在URL重新模块添加服务器变量&#xff1a; 添加变量名&#xff1a;Add HttpOnly 网站根目录web.config添加如下规则&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <configuration><system.webServer><rewrite><out…