曲线生成 | 图解三次样条曲线生成原理(附ROS C++/Python/Matlab仿真)

目录

  • 0 专栏介绍
  • 1 什么是样条?
  • 2 三次样条曲线原理
    • 2.1 曲线插值
    • 2.2 边界条件
    • 2.3 系数反解
  • 3 算法仿真
    • 3.1 ROS C++仿真
    • 3.2 Python仿真
    • 3.3 Matlab仿真

0 专栏介绍

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

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


1 什么是样条?

样条(Spline)早期来源于工程制图,为了将一些固定点连成一条光滑曲线,采用具有弹性的木条固定在这些点上,通过样条作出的曲线经过各固定点且连续光滑,如图所示

在这里插入图片描述

后来,样条发展成一种平滑曲线的数学表示方法。它通过连接一系列给定的数据点(节点)来构建曲线,以便在这些节点上产生平滑的过渡。通常情况下,样条曲线是由多个连续的二次或三次函数组成,每个函数都在相邻节点之间定义。这些连续的函数被称为样条段,它们共同组成了整个曲线

样条是在各个领域中广泛应用的一种技术,例如计算机图形学、物理学模拟、金融和经济分析等。在计算机图形学中,样条通常用于创建平滑的曲线和曲面,以便在三维场景中呈现出更真实的效果。在物理学模拟中,样条可用于描述物体的运动轨迹和变形过程。在金融和经济分析中,样条可用于拟合和预测时间序列数据,例如股票价格和货币汇率

本节介绍常见的三次样条曲线(Cubic Splines)原理

2 三次样条曲线原理

2.1 曲线插值

给定一系列插值点

X = { ( x 0 , y 0 ) , ( x 1 , y 1 ) , ⋯ , ( x n − 1 , y n − 1 ) } X=\left\{ \left( x_0,y_0 \right) ,\left( x_1,y_1 \right) ,\cdots ,\left( x_{n-1},y_{n-1} \right) \right\} X={(x0,y0),(x1,y1),,(xn1,yn1)}

相邻两点间通过多项式曲线连接,因此共需要拼接 n − 1 n-1 n1段曲线。定义三次多项式曲线为

f i ( x ) = a i + b i ( x − x i ) + c i ( x − x i ) 2 + d i ( x − x i ) 3 i = 0 , 1 , ⋯ , n − 1 f_i\left( x \right) =a_i+b_i\left( x-x_i \right) +c_i\left( x-x_i \right) ^2+d_i\left( x-x_i \right) ^3\,\,i=0,1,\cdots ,n-1 fi(x)=ai+bi(xxi)+ci(xxi)2+di(xxi)3i=0,1,,n1

其中,当 i = n − 1 i=n-1 i=n1时的曲线是辅助曲线,用于计算前 n − 1 n-1 n1段曲线而不参与实际拼接。对于三次曲线,给出四个约束条件为

{ 过插值点 : f i ( x i ) = y i 曲线连续 : f i ( x i + 1 ) = y i + 1 一阶连续 : f ˙ i ( x i + 1 ) = f ˙ i + 1 ( x i + 1 ) 二阶连续 : f ¨ i ( x i + 1 ) = f ¨ i + 1 ( x i + 1 ) ⇒ h i = x i + 1 − x i { a i = y i a i + b i h i + c i h i 2 + d i h i 3 = y i + 1 b i + 2 c i h i + 3 d i h i 2 = b i + 1 c i + 3 d i h i = c i + 1 \begin{cases} \text{过插值点}: f_i\left( x_i \right) =y_i\\ \text{曲线连续}: f_i\left( x_{i+1} \right) =y_{i+1}\\ \text{一阶连续}: \dot{f}_i\left( x_{i+1} \right) =\dot{f}_{i+1}\left( x_{i+1} \right)\\ \text{二阶连续}: \ddot{f}_i\left( x_{i+1} \right) =\ddot{f}_{i+1}\left( x_{i+1} \right)\\\end{cases}\xRightarrow{h_i=x_{i+1}-x_i}\begin{cases} a_i=y_i\\ a_i+b_ih_i+c_ih_{i}^{2}+d_ih_{i}^{3}=y_{i+1}\\ b_i+2c_ih_i+3d_ih_{i}^{2}=b_{i+1}\\ c_i+3d_ih_i=c_{i+1}\\\end{cases} 过插值点:fi(xi)=yi曲线连续:fi(xi+1)=yi+1一阶连续:f˙i(xi+1)=f˙i+1(xi+1)二阶连续:f¨i(xi+1)=f¨i+1(xi+1)hi=xi+1xi ai=yiai+bihi+cihi2+dihi3=yi+1bi+2cihi+3dihi2=bi+1ci+3dihi=ci+1

联立上式,用系数 统一表示其他参数可得

h i c i + 2 ( h i + h i + 1 ) c i + 1 + h i + 1 c i + 2 = 3 ( y i + 2 − y i + 1 h i + 1 − y i + 1 − y i h i ) h_ic_i+2\left( h_i+h_{i+1} \right) c_{i+1}+h_{i+1}c_{i+2}=3\left( \frac{y_{i+2}-y_{i+1}}{h_{i+1}}-\frac{y_{i+1}-y_i}{h_i} \right) hici+2(hi+hi+1)ci+1+hi+1ci+2=3(hi+1yi+2yi+1hiyi+1yi)

其他参数表示为

{ a i = y i b i = y i + 1 − y i h i − c i + 1 + 2 c i 3 h i d i = c i + 1 − c i 3 h i \begin{cases} a_i=y_i\\ b_i=\frac{y_{i+1}-y_i}{h_i}-\frac{c_{i+1}+2c_i}{3}h_i\\ d_i=\frac{c_{i+1}-c_i}{3h_i}\\\end{cases} ai=yibi=hiyi+1yi3ci+1+2cihidi=3hici+1ci

2.2 边界条件

注意到关于 c i c_i ci的线性方程仅有 n − 2 n-2 n2个,而未知向量

c = [ c 0 c 1 ⋯ c n − 2 c n − 1 ] T \boldsymbol{c}=\left[ \begin{matrix} c_0& c_1& \cdots& c_{n-2}& c_{n-1}\\\end{matrix} \right] ^T c=[c0c1cn2cn1]T

共有 n n n个元素,欠定方程组不足以进行求解。这是因为曲线首末处没有拼接约束,需要人为设定边界条件,常用的边界条件有

  • 自然边界(Natural Spline):令端点二阶导为零,即
    f 0 ′ ′ ( x 0 ) = f n − 1 ′ ′ ( x n − 1 ) = 0 f_{0}^{''}\left( x_0 \right) =f_{n-1}^{''}\left( x_{n-1} \right) =0 f0′′(x0)=fn1′′(xn1)=0
  • 固定边界(Clamped Spline):令端点一阶导为常数,即
    f 0 ′ ( x 0 ) = A , f n − 1 ′ ( x n − 1 ) = B f_{0}^{'}\left( x_0 \right) =A,f_{n-1}^{'}\left( x_{n-1} \right) =B f0(x0)=A,fn1(xn1)=B
  • 非扭结边界(Not-A-Knot Spline):令前两个点与最后两个点的三阶导值相等,即
    f 0 ′ ′ ′ ( x 0 ) = f 1 ′ ′ ′ ( x 1 ) , f n − 2 ′ ′ ′ ( x n − 2 ) = f n − 1 ′ ′ ′ ( x n − 1 ) f_{0}^{'''}\left( x_0 \right) =f_{1}^{'''}\left( x_1 \right) , f_{n-2}^{'''}\left( x_{n-2} \right) =f_{n-1}^{'''}\left( x_{n-1} \right) f0′′′(x0)=f1′′′(x1),fn2′′′(xn2)=fn1′′′(xn1)

2.3 系数反解

本节选择自然边界,则 c 0 = c n − 1 = 0 c_0=c_{n-1}=0 c0=cn1=0,将关于 c i c_i ci的线性方程改写为矩阵形式

[ 1 h 0 2 ( h 0 + h 1 ) h 1 h 1 2 ( h 1 + h 2 ) h 2 h 2 2 ( h 2 + h 3 ) h 3 ⋱ 1 ] [ c 0 c 1 c 2 c 3 ⋮ c n − 1 ] = 3 [ 0 y 2 − y 1 h 1 − y 1 − y 0 h 0 y 3 − y 2 h 2 − y 2 − y 1 h 1 y 4 − y 3 h 3 − y 3 − y 2 h 2 ⋮ 0 ] \left[ \begin{matrix} 1& & & & & \\ h_0& 2\left( h_0+h_1 \right)& h_1& & & \\ & h_1& 2\left( h_1+h_2 \right)& h_2& & \\ & & h_2& 2\left( h_2+h_3 \right)& h_3& \\ & & & & \ddots& \\ & & & & & 1\\\end{matrix} \right] \left[ \begin{array}{c} c_0\\ c_1\\ c_2\\ c_3\\ \vdots\\ c_{n-1}\\\end{array} \right] =3\left[ \begin{array}{c} 0\\ \frac{y_2-y_1}{h_1}-\frac{y_1-y_0}{h_0}\\ \frac{y_3-y_2}{h_2}-\frac{y_2-y_1}{h_1}\\ \frac{y_4-y_3}{h_3}-\frac{y_3-y_2}{h_2}\\ \vdots\\ 0\\\end{array} \right] 1h02(h0+h1)h1h12(h1+h2)h2h22(h2+h3)h31 c0c1c2c3cn1 =3 0h1y2y1h0y1y0h2y3y2h1y2y1h3y4y3h2y3y20

该方程组有唯一解

3 算法仿真

3.1 ROS C++仿真

核心代码如下所示

std::vector<double> CubicSpline::spline(std::vector<double> s_list, std::vector<double> dir_list, std::vector<double> t)
{// cubic polynomial functionsstd::vector<double> a = dir_list;std::vector<double> b, d;size_t num = s_list.size();std::vector<double> h;for (size_t i = 0; i < num - 1; i++)h.push_back(s_list[i + 1] - s_list[i]);// calculate coefficient matrixEigen::MatrixXd A = Eigen::MatrixXd::Zero(num, num);for (size_t i = 1; i < num - 1; i++){A(i, i - 1) = h[i - 1];A(i, i) = 2.0 * (h[i - 1] + h[i]);A(i, i + 1) = h[i];}A(0, 0) = 1.0;A(num - 1, num - 1) = 1.0;Eigen::MatrixXd B = Eigen::MatrixXd::Zero(num, 1);for (size_t i = 1; i < num - 1; i++)B(i, 0) = 3.0 * (a[i + 1] - a[i]) / h[i] - 3.0 * (a[i] - a[i - 1]) / h[i - 1];Eigen::MatrixXd c = A.lu().solve(B);for (size_t i = 0; i < num - 1; i++){b.push_back((a[i + 1] - a[i]) / h[i] - h[i] * (c(i + 1) + 2.0 * c(i)) / 3.0);d.push_back((c(i + 1) - c(i)) / (3.0 * h[i]));}// calculate spline value and its derivativestd::vector<double> p;for (const auto it : t){auto iter = std::find_if(s_list.begin(), s_list.end(), [it](double val) { return val > it; });if (iter != s_list.end()){size_t idx = std::distance(s_list.begin(), iter) - 1;double ds = it - s_list[idx];p.push_back(a[idx] + b[idx] * ds + c(idx) * std::pow(ds, 2) + d[idx] * std::pow(ds, 3));}}return p;
}

3.2 Python仿真

核心代码如下所示

def spline(self, x_list: list, y_list: list, t: list):# cubic polynomial functionsa, b, c, d = y_list, [], [], []h = np.diff(x_list)num = len(x_list)# calculate coefficient matrixA = np.zeros((num, num))for i in range(1, num - 1):A[i, i - 1] = h[i - 1]A[i, i] = 2.0 * (h[i - 1] + h[i])A[i, i + 1] = h[i]A[0, 0] = 1.0A[num - 1, num - 1] = 1.0B = np.zeros(num)for i in range(1, num - 1):B[i] = 3.0 * (a[i + 1] - a[i]) / h[i] - \3.0 * (a[i] - a[i - 1]) / h[i - 1]c = np.linalg.solve(A, B)for i in range(num - 1):d.append((c[i + 1] - c[i]) / (3.0 * h[i]))b.append((a[i + 1] - a[i]) / h[i] - h[i] * (c[i + 1] + 2.0 * c[i]) / 3.0)# calculate spline value and its derivativep, dp = [], []for it in t:if it < x_list[0] or it > x_list[-1]:continuei = bisect.bisect(x_list, it) - 1dx = it - x_list[i]p.append(a[i] + b[i] * dx + c[i] * dx**2 + d[i] * dx**3)dp.append(b[i] + 2.0 * c[i] * dx + 3.0 * d[i] * dx**2)return p, dp

在这里插入图片描述

3.3 Matlab仿真

核心代码如下所示

function p = spline(s_list, dir_list, t)% cubic polynomial functionsa = dir_list;[num, ~] = size(s_list);h = diff(s_list);% calculate coefficient matrixA = zeros(num, num);for i=2:num - 1A(i, i - 1) = h(i - 1);A(i, i) = 2.0 * (h(i - 1) + h(i));A(i, i + 1) = h(i);endA(1, 1) = 1.0;A(num, num) = 1.0;B = zeros(num, 1);for i=2:num - 1B(i, 1) = 3.0 * (a(i + 1) - a(i)) / h(i) - 3.0 * (a(i) - a(i - 1)) / h(i - 1);endc = A \ B;b = zeros(num - 1, 1); d = zeros(num - 1, 1);for i=1:num - 1b(i) = (a(i + 1) - a(i)) / h(i) - h(i) * (c(i + 1) + 2.0 * c(i)) / 3.0;d(i) = (c(i + 1) - c(i)) / (3.0 * h(i));end% calculate spline value and its derivativep = [];for i =1:length(t)idx = find(s_list > t(i));if ~isempty(idx)id = idx(1) - 1;ds = t(i) - s_list(id);p = [p; a(id) + b(id) * ds + c(id) * power(ds, 2) + d(id) * power(ds, 3)];endend
end

在这里插入图片描述

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


🔥 更多精彩专栏

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

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

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

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

相关文章

研学活动报名系统源码开发方案

一、项目背景与目标 &#xff08;一&#xff09;项目背景&#xff1a; 随着教育水平的提高和人们对综合素质培养的需求增加&#xff0c;研学活动作为一种教育方式受到了广大家长和学生的青睐。为了更好地组织和管理研学活动&#xff0c;需要建立一个研学活动报名系统&#xf…

C#,入门教程(22)——函数的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(21)——命名空间&#xff08;namespace&#xff09;与程序结构的基础知识https://blog.csdn.net/beijinghorn/article/details/124140653 一、函数的基本概念 一个软件的结构大体如下&#xff1a; 大厦application: a plaza { --…

【C++】stack与queue的模拟实现

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 前言 stack与queue的实现比较简…

快速上手Flask(三) 在 Flask应用中使用Flask-SQLAlchemy(flask SQLAlchemy模型对象如何json序列化输出)

文章目录 快速上手Flask(三) 在 Flask应用中使用Flask-SQLAlchemySQLAlchemy什么是Flask-SQLAlchemyFlask-SQLAlchemy 和 SQLAlchemy的区别&#xff1f;Flask-SQLAlchemy基本使用安装&#xff1a;快速入门创建模型flask模型对象如何json序列化输出 数据库的增删改查 工作常用总…

轮胎侧偏刚度线性插值方法

一、trucksim取数据 步骤一 步骤二 二、数据导入到matlab中 利用simulink的look up table模块 1是侧偏角&#xff1b;2是垂直载荷&#xff1b;输出是侧向力。 侧向力除以侧偏角就是实时的侧偏刚度。

【LeetCode】每日一题 2024_1_21 分割数组的最大值(二分)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;分割数组的最大值题目描述代码与解题思路 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 今天是 hard&#xff0c;难受&#xff0c;还好有题解大哥的清晰讲解 题目&a…

C#实现基于Word保护性模板文件的修改

目录 制作一个保护性模板文件 给文件设置保护密码 设计模板内容 限制编辑 进一步的需求 范例运行环境 Office DCOM 配置 设计实现 进一步修改模板文件 设置和取消保护 遍历WORD内容控件 总结 制作一个保护性模板文件 在类似一些OA的自动化处理或审批类系统里&a…

一文读懂 c++ 容器

容器根据不同的使用场景和需求,有序序列、无序集合以及专门的适配器等各种形式。了解如何根据场景选择合适的容器并使用它们,是写出高效可读性强的 C++ 代码的关键所在,C++ 标准库提供了一系列标准容器来存储和操作数据集合。这些容器被设计为通用、高效且易于使用。它们都是…

【 文本到上下文 #4】NLP 与 ML

一、说明 欢迎回到我们的 NLP 博客系列&#xff01;当我们进入第四部分时&#xff0c;焦点转移到机器学习 &#xff08;ML&#xff09; 和自然语言处理 &#xff08;NLP&#xff09; 之间的动态相互作用上。在本章中&#xff0c;我们将深入探讨 ML 和 NLP 的迷人协同作用&#…

CGAL::Plane_3<K>平面结构

CGAL::Plane_3<K> 是 CGAL&#xff08;Computational Geometry Algorithms Library&#xff09;中的一个类&#xff0c;代表三维空间中的一个平面。在这个类中&#xff0c;K 是一个内核类型参数&#xff0c;通常代表了一组几何对象的类型和操作&#xff0c;比如点、向量、…

【Linux】Shell 命令以及运行原理

Shell 命令以及运行原理 当用户登录 Linux 系统的时候&#xff0c;系统会给用户创建一个新的进程&#xff0c;一般叫做 bash&#xff08;命令行解释器&#xff09;。 Linux 严格意义上说的是一个操作系统&#xff0c;我们称之为 “核心&#xff08; kernel &#xff09;” &…

如何在 Linux 服务器上设置定时任务?

定时任务&#xff0c;也称为计划任务或cron作业&#xff0c;是在指定的时间间隔内自动执行特定任务的一种方法。在Linux服务器上设置定时任务可以帮助您自动化许多常见的系统管理任务&#xff0c;例如备份数据、清理日志文件、发送通知等。下面是在Linux服务器上设置定时任务的…

Leetcode 3013. Divide an Array Into Subarrays With Minimum Cost II

Leetcode 3013. Divide an Array Into Subarrays With Minimum Cost II 1. 解题思路2. 代码实现 题目链接&#xff1a;3013. Divide an Array Into Subarrays With Minimum Cost II 1. 解题思路 这一题的话思路上的话我一开始是想着偷懒直接用动态规划&#xff0c;结果果然还…

Kafka 问题排查

订单宽表数据不同步 事情的起因是专员在 ze app 上查不到订单了&#xff0c;而订单数据是从 mysql 的 order_search_info 查询的&#xff0c;order_search_info 表的数据是从 oracel 的 BZ_ORDER_INFO 表同步过来的&#xff0c;查不到说明同步有问题 首先重启&#xff0c;同步…

ICMP控制消息 汇总

控制消息由 类型 字段中的值标识。代码 字段给出了消息的附加上下文信息。自协议首次引入以来&#xff0c;一些控制消息已被弃用。 重要的ICMP Control Message控制信息 类型码状态描述0 –回声回复&#xff1a;140回声回复&#xff08;用于ping&#xff09;1和2未分配已预留3 …

0004.电脑开机提示按F1

常用的电脑主板不知道什么原因&#xff0c;莫名其妙的启动不了了。尝试了很多方法&#xff0c;没有奏效。没有办法我就只能把硬盘拆了下来&#xff0c;装到了另一台电脑上面。但是开机以后却提示F1&#xff0c;如下图&#xff1a; 根据上面的提示&#xff0c;应该是驱动有问题…

Spring Security工作原理(三)

在认证之间保存请求 如处理安全异常中所示,当请求没有认证且需要认证资源时,需要保存请求以便在认证成功后重新请求受保护的资源。在Spring Security中,这是通过使用RequestCache实现来保存HttpServletRequest来实现的。 RequestCache HttpServletRequest被保存在Request…

可替代Allegro A3901的国产GC3901 5V H 桥驱动器,驱动电流能力更强,且低成本,大电流

步进电机驱动的应用方案目前市场上大多选用国外品牌的电机驱动器&#xff0c;其中ALLEGRO的A3901在这一块的应用很广泛。但是由于市场越来越成熟&#xff0c;当前对于产品开发成本要求也越来越低&#xff0c;国产品牌也推出了相应的镜头驱动器&#xff0c;因此ALLEGRO的A3901已…

Redis和RediSearch的安装及使用

1. 安装要求 ReadiSearch要求Redis的版本在6.0以上RediSearch 要求使用 GNU Make 4.0 或更高版本 2. Redis的安装 查看redis的版本&#xff1a; redis-server --version或者&#xff0c;如果你已经启动了Redis服务器&#xff0c;你也可以使用redis-cli工具来获取版本信息&a…

Win10/11中VMware Workstation设置网络桥接模式

文章目录 一、添加VMware Bridge Protocol服务二、配置桥接参数1.启用系统Device Install Service服务2.配置VMware 需要确认物理网卡是否有添加VMware Bridge Protocol服务 添加VMware Bridge Protocol服务 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参…