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

2024-02-26 12:20

本文主要是介绍曲线生成 | 图解B样条曲线生成原理(附ROS C++/Python/Matlab仿真),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 0 专栏介绍
  • 1 控制点计算之插值
  • 2 控制点计算之近似
  • 3 仿真实现
    • 3.1 ROS C++实现
    • 3.2 Python实现
    • 3.3 Matlab实现

0 专栏介绍

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

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


在曲线生成 | 图解B样条曲线生成原理(基本概念与节点生成算法)中,我们介绍了B样条曲线的基本概念,例如基函数的递推、曲线支撑性原理、节点生成公式等。本文进一步计算控制点计算和曲线生成原理。

1 控制点计算之插值

设B样条曲线为

P ( t ) = ∑ i = 0 n − 1 p i N i , k ( t ) \boldsymbol{P}\left( t \right) =\sum_{i=0}^{n-1}{\boldsymbol{p}_iN_{i,k}\left( t \right)} P(t)=i=0n1piNi,k(t)

其中 p i \boldsymbol{p}_i pi是待求的控制节点。令参数向量满足其映射为数据点

D j = P ( u j ) = ∑ i = 0 n − 1 p i N i , k ( u j ) , j = 0 , 1 , ⋯ , n − 1 \boldsymbol{D}_j=\boldsymbol{P}\left( u_j \right) =\sum_{i=0}^{n-1}{\boldsymbol{p}_iN_{i,k}\left( u_j \right)}, j=0,1,\cdots ,n-1 Dj=P(uj)=i=0n1piNi,k(uj),j=0,1,,n1

这里 N N N个数据点可求解出 N N N个控制点,所以 n = N n=N n=N,可以矩阵化为

D = N P \boldsymbol{D}=\boldsymbol{NP} D=NP

其中

D = [ d 0 T d 1 T ⋮ d n − 1 T ] , P = [ p 0 T p 1 T ⋮ p n − 1 T ] , N = [ N 0 , k ( u 0 ) N 1 , k ( u 0 ) ⋯ N n − 1 , k ( u 0 ) N 0 , k ( u 1 ) N 1 , k ( u 1 ) ⋯ N n − 1 , k ( u 1 ) ⋮ ⋮ ⋱ ⋮ N 0 , k ( u n − 1 ) N 1 , k ( u n − 1 ) ⋯ N n − 1 , k ( u n − 1 ) ] \boldsymbol{D}=\left[ \begin{array}{c} \boldsymbol{d}_{0}^{T}\\ \boldsymbol{d}_{1}^{T}\\ \vdots\\ \boldsymbol{d}_{n-1}^{T}\\\end{array} \right] , \boldsymbol{P}=\left[ \begin{array}{c} \boldsymbol{p}_{0}^{T}\\ \boldsymbol{p}_{1}^{T}\\ \vdots\\ \boldsymbol{p}_{n-1}^{T}\\\end{array} \right] , \boldsymbol{N}=\left[ \begin{matrix} N_{0,k}\left( u_0 \right)& N_{1,k}\left( u_0 \right)& \cdots& N_{n-1,k}\left( u_0 \right)\\ N_{0,k}\left( u_1 \right)& N_{1,k}\left( u_1 \right)& \cdots& N_{n-1,k}\left( u_1 \right)\\ \vdots& \vdots& \ddots& \vdots\\ N_{0,k}\left( u_{n-1} \right)& N_{1,k}\left( u_{n-1} \right)& \cdots& N_{n-1,k}\left( u_{n-1} \right)\\\end{matrix} \right] D= d0Td1Tdn1T ,P= p0Tp1Tpn1T ,N= N0,k(u0)N0,k(u1)N0,k(un1)N1,k(u0)N1,k(u1)N1,k(un1)Nn1,k(u0)Nn1,k(u1)Nn1,k(un1)

求解该方程即可得到控制点。

2 控制点计算之近似

在插值问题中,插值曲线可能会在数据点间波动,而非紧密遵循数据多边形。为克服这个问题,可以放宽曲线必须穿过所有数据点的硬约束。除了第一个和最后一个数据点,曲线不必包含任何其他点,通过约束最小二乘误差来实现最优近似。考虑到节点向量中首末节点重复度为 k + 1 k+1 k+1时,B样条曲线穿过首末控制点,所以令 p 0 = d 0 \boldsymbol{p}_{0}=\boldsymbol{d}_{0} p0=d0 p n − 1 = d n − 1 \boldsymbol{p}_{n-1}=\boldsymbol{d}_{n-1} pn1=dn1,则

P ( t ) = N 0 , k ( t ) p 0 + ∑ i = 1 n − 2 p i N i , k ( t ) + N n − 1 , k ( t ) p n − 1 \boldsymbol{P}\left( t \right) =N_{0,k}\left( t \right) \boldsymbol{p}_0+\sum_{i=1}^{n-2}{\boldsymbol{p}_iN_{i,k}\left( t \right)}+N_{n-1,k}\left( t \right) \boldsymbol{p}_{n-1} P(t)=N0,k(t)p0+i=1n2piNi,k(t)+Nn1,k(t)pn1

从而可以计算最小二乘误差

f ( p 1 , p 2 , ⋯ , p n − 2 ) = ∑ i = 1 n − 2 [ d i − P ( u i ) ] 2 = ∑ i = 1 n − 2 [ ( d i − N 0 , k ( u i ) p 0 − N n − 1 , k ( u i ) p n − 1 ) ⏟ q i − ∑ j = 1 n − 2 p j N j , k ( u i ) ] 2 = ∑ i = 1 n − 2 [ q i T q i − 2 q i ∑ j = 1 n − 2 p j N j , k ( u i ) + ∑ j = 1 n − 2 p j N j , k ( u i ) ∑ j = 1 n − 2 p j N j , k ( u i ) ] \begin{aligned}f\left( \boldsymbol{p}_1,\boldsymbol{p}_2,\cdots ,\boldsymbol{p}_{n-2} \right) &=\sum_{i=1}^{n-2}{\left[ \boldsymbol{d}_i-\boldsymbol{P}\left( u_i \right) \right] ^2}\\&=\sum_{i=1}^{n-2}{\left[ \underset{{ \boldsymbol{q}_i}}{\underbrace{\left( \boldsymbol{d}_i-N_{0,k}\left( u_i \right) \boldsymbol{p}_0-N_{n-1,k}\left( u_i \right) \boldsymbol{p}_{n-1} \right) }}-\sum_{j=1}^{n-2}{\boldsymbol{p}_jN_{j,k}\left( u_i \right)} \right] ^2}\\&=\sum_{i=1}^{n-2}{\left[ \boldsymbol{q}_{i}^{T}\boldsymbol{q}_i-2\boldsymbol{q}_i\sum_{j=1}^{n-2}{\boldsymbol{p}_jN_{j,k}\left( u_i \right)}+\sum_{j=1}^{n-2}{\boldsymbol{p}_jN_{j,k}\left( u_i \right)}\sum_{j=1}^{n-2}{\boldsymbol{p}_jN_{j,k}\left( u_i \right)} \right]}\end{aligned} f(p1,p2,,pn2)=i=1n2[diP(ui)]2=i=1n2 qi (diN0,k(ui)p0Nn1,k(ui)pn1)j=1n2pjNj,k(ui) 2=i=1n2[qiTqi2qij=1n2pjNj,k(ui)+j=1n2pjNj,k(ui)j=1n2pjNj,k(ui)]

将误差函数对 p g ( g = 1 , 2 , ⋯ , n − 2 ) \boldsymbol{p}_g\left( g=1,2,\cdots ,n-2 \right) pg(g=1,2,,n2)求偏导,可得

∂ f ( p 1 , p 2 , ⋯ , p n − 2 ) ∂ p g = ∑ i = 1 n − 2 [ − 2 q i N g , k ( u i ) + 2 N g , k ( u i ) ∑ j = 1 n − 2 p j N j , k ( u i ) ] \frac{\partial f\left( \boldsymbol{p}_1,\boldsymbol{p}_2,\cdots ,\boldsymbol{p}_{n-2} \right)}{\partial \boldsymbol{p}_g}=\sum_{i=1}^{n-2}{\left[ -2\boldsymbol{q}_iN_{g,k}\left( u_i \right) +2N_{g,k}\left( u_i \right) \sum_{j=1}^{n-2}{\boldsymbol{p}_jN_{j,k}\left( u_i \right)} \right]} pgf(p1,p2,,pn2)=i=1n2[2qiNg,k(ui)+2Ng,k(ui)j=1n2pjNj,k(ui)]

∂ f ( p 1 , p 2 , ⋯ , p n − 2 ) / ∂ p g = 0 {{\partial f\left( \boldsymbol{p}_1,\boldsymbol{p}_2,\cdots ,\boldsymbol{p}_{n-2} \right)}/{\partial \boldsymbol{p}_g}}=0 f(p1,p2,,pn2)/pg=0可得

∑ i = 1 n − 2 [ ∑ j = 1 n − 2 N g , k ( u i ) N j , k ( u i ) ] p j = ∑ i = 1 n − 2 q i N g , k ( u i ) \sum_{i=1}^{n-2}{\left[ \sum_{j=1}^{n-2}{N_{g,k}\left( u_i \right) N_{j,k}\left( u_i \right)} \right] \boldsymbol{p}_j}=\sum_{i=1}^{n-2}{\boldsymbol{q}_iN_{g,k}\left( u_i \right)} i=1n2[j=1n2Ng,k(ui)Nj,k(ui)]pj=i=1n2qiNg,k(ui)

改写为矩阵形式

( N T N ) P = Q \left( \boldsymbol{N}^T\boldsymbol{N} \right) \boldsymbol{P}=\boldsymbol{Q} (NTN)P=Q

其中

P = [ p 1 T p 2 T ⋮ p n − 2 T ] , Q = [ ∑ i = 1 n − 2 q i N 1 , k ( u i ) ∑ i = 1 n − 2 q i N 2 , k ( u i ) ⋮ ∑ i = 1 n − 2 q i N n − 2 , k ( u i ) ] , N = [ N 1 , k ( u 1 ) N 2 , k ( u 1 ) ⋯ N n − 2 , k ( u 1 ) N 1 , k ( u 2 ) N 2 , k ( u 2 ) ⋯ N n − 2 , k ( u 2 ) ⋮ ⋮ ⋱ ⋮ N 1 , k ( u n − 2 ) N 2 , k ( u n − 2 ) ⋯ N n − 2 , k ( u n − 2 ) ] \boldsymbol{P}=\left[ \begin{array}{c} \boldsymbol{p}_{1}^{T}\\ \boldsymbol{p}_{2}^{T}\\ \vdots\\ \boldsymbol{p}_{n-2}^{T}\\\end{array} \right] , \boldsymbol{Q}=\left[ \begin{array}{c} \sum_{i=1}^{n-2}{\boldsymbol{q}_iN_{1,k}\left( u_i \right)}\\ \sum_{i=1}^{n-2}{\boldsymbol{q}_iN_{2,k}\left( u_i \right)}\\ \vdots\\ \sum_{i=1}^{n-2}{\boldsymbol{q}_iN_{n-2,k}\left( u_i \right)}\\\end{array} \right] , \boldsymbol{N}=\left[ \begin{matrix} N_{1,k}\left( u_1 \right)& N_{2,k}\left( u_1 \right)& \cdots& N_{n-2,k}\left( u_1 \right)\\ N_{1,k}\left( u_2 \right)& N_{2,k}\left( u_2 \right)& \cdots& N_{n-2,k}\left( u_2 \right)\\ \vdots& \vdots& \ddots& \vdots\\ N_{1,k}\left( u_{n-2} \right)& N_{2,k}\left( u_{n-2} \right)& \cdots& N_{n-2,k}\left( u_{n-2} \right)\\\end{matrix} \right] P= p1Tp2Tpn2T ,Q= i=1n2qiN1,k(ui)i=1n2qiN2,k(ui)i=1n2qiNn2,k(ui) ,N= N1,k(u1)N1,k(u2)N1,k(un2)N2,k(u1)N2,k(u2)N2,k(un2)Nn2,k(u1)Nn2,k(u2)Nn2,k(un2)

求解该方程即可得到控制点。

3 仿真实现

3.1 ROS C++实现

核心代码如下所示:

Points2d BSpline::interpolation(const Points2d points, const std::vector<double> param, const std::vector<double> knot)
{size_t n = points.size();Eigen::MatrixXd N = Eigen::MatrixXd::Zero(n, n);Eigen::MatrixXd D(n, 2);for (size_t i = 0; i < n; i++)for (size_t j = 0; j < n; j++)N(i, j) = baseFunction(j, order_, param[i], knot);N(n - 1, n - 1) = 1;for (size_t i = 0; i < n; i++){D(i, 0) = points[i].first;D(i, 1) = points[i].second;}Eigen::MatrixXd C = N.inverse() * D;std::vector<std::pair<double, double>> control_points(n);for (size_t i = 0; i < n; i++)control_points[i] = { C(i, 0), C(i, 1) };return control_points;
}

3.2 Python实现

核心代码如下所示:

def approximation(self, points: list, param: list, knot: list):n = len(points)D = np.array(points)# heuristically setting the number of control pointsh = n - 1N = np.zeros((n, h))for i in range(n):for j in range(h):N[i][j] = self.baseFunction(j, self.k, param[i], knot)N_ = N[1 : n - 1, 1 : h - 1]qk = np.zeros((n - 2, 2))for i in range(1, n - 1):qk[i - 1] = D[i, :] - N[i][0] * D[0, :] - N[i][h - 1] * D[-1, :]Q = N_.T @ qkP = np.linalg.inv(N_.T @ N_) @ QP = np.insert(P, 0, D[0, :], axis=0)P = np.insert(P, len(P), D[-1, :], axis=0)return P

在这里插入图片描述

3.3 Matlab实现

核心代码如下所示:

function points = generation(knot, control_pts, param)n = ceil(1.0 / param.step);t = (0 : n - 1) / (n - 1);[m, ~] = size(control_pts);N = zeros(n, m);for i=1:nfor j=1:mN(i, j) = baseFunction(j, param.order, t(i), knot);endendN(n, m) = 1.0;points = N * control_pts;
end

在这里插入图片描述

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


🔥 更多精彩专栏

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

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

这篇关于曲线生成 | 图解B样条曲线生成原理(附ROS C++/Python/Matlab仿真)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/748867

相关文章

关于C++中的虚拟继承的一些总结(虚拟继承,覆盖,派生,隐藏)

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:pu

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

C++的模板(八):子系统

平常所见的大部分模板代码,模板所传的参数类型,到了模板里面,或实例化为对象,或嵌入模板内部结构中,或在模板内又派生了子类。不管怎样,最终他们在模板内,直接或间接,都实例化成对象了。 但这不是唯一的用法。试想一下。如果在模板内限制调用参数类型的构造函数会发生什么?参数类的对象在模板内无法构造。他们只能从模板的成员函数传入。模板不保存这些对象或者只保存他们的指针。因为构造函数被分离,这些指针在模板外

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

Python 字符串占位

在Python中,可以使用字符串的格式化方法来实现字符串的占位。常见的方法有百分号操作符 % 以及 str.format() 方法 百分号操作符 % name = "张三"age = 20message = "我叫%s,今年%d岁。" % (name, age)print(message) # 我叫张三,今年20岁。 str.format() 方法 name = "张三"age

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

C++入门01

1、.h和.cpp 源文件 (.cpp)源文件是C++程序的实际实现代码文件,其中包含了具体的函数和类的定义、实现以及其他相关的代码。主要特点如下:实现代码: 源文件中包含了函数、类的具体实现代码,用于实现程序的功能。编译单元: 源文件通常是一个编译单元,即单独编译的基本单位。每个源文件都会经过编译器的处理,生成对应的目标文件。包含头文件: 源文件可以通过#include指令引入头文件,以使