局部路径规划算法 - B样条曲线(B-spline Curves)

2024-03-20 05:36

本文主要是介绍局部路径规划算法 - B样条曲线(B-spline Curves),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考资料:
(1)B样条曲线(B-spline Curves)
(2)B样条曲线法

1 算法概述

1.1 算法简介
  • 样条是一根富有弹性的细木条或塑料条,在应用CAD/CAM技术以前,航空、船舶和汽车制造业普遍采用手工绘制自由曲线。绘制员用压铁压住样条,使其通过所有给定的型值点,再适当地调整压铁,改变样条形态,直到符合设计要求。
  • B样条曲线是B-样条基函数(给定区间上的所有样条函数组成一个线性空间)的线性组合。
1.2 算法思想

贝塞尔曲线的缺陷:
① 确定了多边形的顶点数(n+1个),也就决定了所定义的Bezier曲线的阶次(n次),这样很不灵活。
② 当顶点数( n+1 )较大时,曲线的次数较高,曲线的导数次数也会较高,因此曲线会出现较多的峰谷值。
③ 贝塞尔曲线无法进行局部修改。

引出了B样条曲线,除了保持Bezier曲线所具有的优点外,还弥补了上述所有的缺陷。
B样条曲线:
(1)可以指定阶次;
(2)移动控制点仅仅改变曲线的部分形状,而不是整体
B样条曲线是贝塞尔曲线的一般化,贝塞尔曲线可以认为是B样条曲线的特例
在这里插入图片描述

2 算法原理

2.1 B样条曲线方程

n + 1 n+1 n+1个控制点: P 0 , P 1 , P 2 , . . . , P n P_0,P_1,P_2,...,P_n P0P1P2...Pn,这些控制点用于定义样条曲线的走向、界限范围,则具有 n + 1 n+1 n+1个控制点的 k k k B B B样条曲线的定义:
p ( u ) = [ P 0 P 1 ⋯ P n ] [ B 0 , k ( u ) B 1 , k ( u ) ⋮ B n , k ( u ) ] = ∑ i = 0 n P i B i , k ( u ) p(u)=\left[\begin{array}{llll} P_{0} & P_{1} & \cdots & P_{n} \end{array}\right]\left[\begin{array}{c} B_{0, k}(u) \\ B_{1, k}(u) \\ \vdots \\ B_{n, k}(u) \end{array}\right]=\sum_{i=0}^{n} P_{i} B_{i, k}(u) p(u)=[P0P1Pn] B0,k(u)B1,k(u)Bn,k(u) =i=0nPiBi,k(u)
式中, B i , k ( u ) B_{i,k}(u) Bi,k(u)是第 i i i k k k B B B样条基函数,与控制点 P i P_i Pi相对应, k ≥ 1 k ≥ 1 k1 u u u是自变量。

  • 基函数具有如下德布尔-考克斯递推式:
    B i , k ( u ) = { { 1 , u i ≤ u < u i + 1 0 , 其他  k = 1 u − u i u i + k − 1 − u i B i , k − 1 ( u ) + u i + k − u u i + k − u i + 1 B i + 1 , k − 1 ( u ) , k ≥ 2 B_{i, k}(u)=\left\{\begin{array}{ll} \left\{\begin{array}{ll} 1, & u_{i} \leq u<u_{i+1} \\ 0, \text { 其他 } \end{array}\right. & k=1 \\ \frac{u-u_{i}}{u_{i+k-1}-u_{i}} B_{i, k-1}(u)+\frac{u_{i+k}-u}{u_{i+k}-u_{i+1}} B_{i+1, k-1}(u), & k \geq 2 \end{array}\right. Bi,k(u)= {1,0, 其他 uiu<ui+1ui+k1uiuuiBi,k1(u)+ui+kui+1ui+kuBi+1,k1(u),k=1k2
    如果遇到分母为 0的情况:0/0=0;如果此时分子不为0,则约定分母为1 。

式中, u i u_i ui是一组被称为节点矢量的非递减序列的连续变化值,首末值一般定义为 0 和 1 ,该序列如下:
[ u 0 , u 1 , ⋯ , u k , u k + 1 , ⋯ , u n , u n + 1 , ⋯ , u n + k ] \left[u_{0}, u_{1}, \cdots, u_{k}, u_{k+1}, \cdots, u_{n}, u_{n+1}, \cdots, u_{n+k}\right] [u0,u1,,uk,uk+1,,un,un+1,,un+k]

  • K阶B样条是关于u的 k − 1 k-1 k1次曲线(即基函数的次数为 k − 1 k-1 k1
  • 段数=控制点个数-次数= ( n + 1 ) − ( k − 1 ) = n − k + 2 (n+1)-(k-1)=n-k+2 (n+1)(k1)=nk+2

段数:一个B样条曲线含由几段贝塞尔曲线

B i , k ( u ) B_{i,k}(u) Bi,k(u)涉及的节点为 u i , u i + 1 , . . . , u i + k u_i,u_{i+1},...,u_{i+k} ui,ui+1,...,ui+k一共 k + 1 k+1 k+1个节点, k k k个区间,因此从 B 0 , k ( u ) B_{0,k}(u) B0,k(u) B n , k ( u ) B_{n,k}(u) Bn,k(u)共涉及 n + k + 1 n+k+1 n+k+1个节点

2.2 B样条计算

根据递推式,当阶数k=1时,不同基函数的非零域如下图:
在这里插入图片描述
所有节点区间列在左边(第一)列,所有零次基函数在第二列,使用如下三角计算格式。
在这里插入图片描述

2.3 B样条分类
  • 均匀B样条曲线:当节点沿参数轴均匀等距分布,为均匀B样均匀B样条曲线:当节点沿参数轴均匀等距分布,为均匀B样条曲线,如 U = U= U={ 0 , 1 , 2 , 3 , 4 , 5 , 6 0,1,2,3,4,5,6 0,1,2,3,4,5,6 }。当n和k一定时,均匀B样条的基函数呈周期性,所有基函数有相同形状,每个后续基函数仅仅是前面基函数在新位置上的重复
  • 准均匀B样条曲线:两端节点具有重复度k,中间节点非递减的序列,如 U = U= U={ 0 , 0 , 0 , 1 , 2 , 3 , 4 , 5 , 5 , 5 0,0,0,1,2,3,4,5,5,5 0,0,0,1,2,3,4,5,5,5 }。准均匀B样条曲线保留了贝塞尔曲线在两个端点处的性质:样条曲线在端点处的切线即为倒数两个端点的连线。准均匀B样条曲线用途最为广泛
    (1)一般来说,次数越高,则曲线的导数次数也会较高,那么将会有很多零点存在,较多的导数零点就导致原曲线存在较多的极值,使曲线出现较多的峰谷值;次数越低,样条曲线逼近控制点效果越好
    (2)另一方面,三次B样条曲线能够实现二阶导数连续,故最终选择准均匀三次B样条曲线作为轨迹规划的曲线比较合适
    k = ∣ f ′ ′ ( 1 + f ′ 2 ) 3 2 ∣ k=\left|\frac{f^{\prime \prime}}{\left(1+f^{\prime 2}\right)^{\frac{3}{2}}}\right| k= (1+f′2)23f′′
  • 分段B样条曲线:其节点矢量中两端节点的重复度与准均匀B样条曲线相同,为k。不同的是内节点(即除去两端节点后的剩余中间节点)重复度为k-1。该类型有限制条件,控制顶点数减1必须等于次数的正整数倍,即 n k − 1 \frac{n}{k-1} k1n=正整数。
  • 一般非均匀B样条曲线:对任意分布的节点矢量 U = [ u 0 , u 1 , . . . , u n + k ] U=[u_0,u_1,...,u_{n+k}] U=[u0,u1,...,un+k]
2.4 总结

在这里插入图片描述
许多论文中的分类是openclampedclosed
(1)如果节点向量没有任何特别的结构,那么产生的曲线不会与控制曲线的第一边和最后一边接触,曲线也不会分别与第一个控制点和最后一个控制点的第一边和最后一边相切。如下面图a所示。这种类型的B-样条曲线称为开(open )B-样条曲线。对于开(open)B-样条曲线,u的定义域是 [ u k − 1 , u n + 2 ] [u_{k-1},u_{n+2}] [uk1,un+2]
(2)clamped B-样条曲线即准均匀B样条曲线,如下图b。
(3)通过重复某些节点和控制点,产生的曲线会是 闭(closed)曲线。 这种情况,产生的曲线的开始和结尾连接在一起形成了一个闭环如下边图c所示。
在这里插入图片描述

3 代码实现

MATLAB(Ally)
BaseFunction.m

function Bik_u = BaseFunction(i, k , u, NodeVector)if k == 0       % 0次B样条if u >= NodeVector(i+1) && u < NodeVector(i+2)Bik_u = 1;elseBik_u = 0;end
elseLength1 = NodeVector(i+k+1) - NodeVector(i+1);Length2 = NodeVector(i+k+2) - NodeVector(i+2);      % 支撑区间的长度if Length1 == 0       % 规定0/0 = 0Length1 = 1;endif Length2 == 0Length2 = 1;endBik_u = (u - NodeVector(i+1)) / Length1 * BaseFunction(i, k-1, u, NodeVector) ...+ (NodeVector(i+k+2) - u) / Length2 * BaseFunction(i+1, k-1, u, NodeVector);
end

U_quasi_uniform.m

function NodeVector = U_quasi_uniform(n, k)
% 准均匀B样条的节点向量计算,共n+1个控制顶点,k次B样条,k+1阶
NodeVector = zeros(1, n+k+2);
piecewise = n - k + 1;       % 曲线的段数
if piecewise == 1            % 只有一段曲线时,n = kfor i = k+2 : n+k+2NodeVector(1, i) = 1;end
elseflag = 1;           % 不止一段曲线时while flag ~= piecewiseNodeVector(1, k+flag+1) = NodeVector(1, k + flag) + 1/piecewise;flag = flag + 1;endNodeVector(1, n+2 : n+k+2) = 1;   % 节点向量前面和后面有(k+1)个重复值(阶数)
end

main.m

clc
clear
close all%% 数据定义
k = 4;                                    % k阶、k-1次B样条
flag = 2;                                  %1,2分别绘制均匀B样条曲线、准均匀B样条曲线
d = 3.5;
P=[0, 10, 25, 25, 40, 50;-d/2,-d/2,-d/2+0.5,d/2-0.5,d/2,d/2 ];   %n=5, 6个控制点,可以满足曲率连续
n = size(P,2)-1;                          % n是控制点个数,从0开始计数%% 生成B样条曲线path=[];
Bik = zeros(n+1, 1);if flag == 1     % 均匀B样条NodeVector = linspace(0, 1, n+k+1); %节点矢量for u = (k-1)/(n+k+1) : 0.001 : (n+2)/(n+k+1)for i = 0 : 1 : nBik(i+1, 1) = BaseFunction(i, k-1 , u, NodeVector);endp_u = P * Bik;path = [path; [p_u(1,1),p_u(2,1)]];endelseif flag == 2  % 准均匀B样条NodeVector = U_quasi_uniform(n, k-1); % 准均匀B样条的节点矢量for u = 0 : 0.005 : 1-0.005for i = 0 : 1 : nBik(i+1, 1) = BaseFunction(i, k-1 , u, NodeVector);endp_u = P * Bik;path=[path; [p_u(1),p_u(2)]];end
elsefprintf('error!\n');
end%% 画图
d = 3.5;               % 道路标准宽度
W = 1.8;               % 汽车宽度
L = 4.7;               % 车长
figure
len_line = 50;
P0 = [0, -d/2];% 画灰色路面图
GreyZone = [-5,-d-0.5; -5,d+0.5; len_line,d+0.5; len_line,-d-0.5];
fill(GreyZone(:,1),GreyZone(:,2),[0.5 0.5 0.5]);
hold on
fill([P0(1),P0(1),P0(1)-L,P0(1)-L],[-d/2-W/2,-d/2+W/2,-d/2+W/2,-d/2-W/2],'b')  % 画分界线
plot([-5, len_line],[0, 0], 'w--', 'linewidth',2);  %分界线
plot([-5,len_line],[d,d],'w','linewidth',2);     %左边界线
plot([-5,len_line],[-d,-d],'w','linewidth',2);  %左边界线% 设置坐标轴显示范围
axis equal
set(gca, 'XLim',[-5 len_line]); 
set(gca, 'YLim',[-4 4]); % 绘制路径
scatter(path(:,1),path(:,2),100, '.b');%路径点
scatter(P(1,:),P(2,:),'g')
plot(P(1,:),P(2,:),'r');%路径点

运行结果:
在这里插入图片描述
C++:
BSpline.h

#pragma once 
#include <iostream>
#include <Eigen/Dense>
#include <vector>
#include <cmath>
#include <algorithm>using namespace std;
using namespace Eigen;double baseFunction(int i, int k, double u, vector<double>& node_vector);vector<double> u_quasi_uniform(int n,int k);vector<double> u_piecewise_B_Spline(int n,int k);

BSpline.cpp

#include "BSpline.h"// 基函数定义
// node_vector 节点向量 array([u0,u1,u2,...,u_n+k],shape=[1,n+k+1].
double baseFunction(int i, int k, double u, vector<double>& node_vector) {double Bik_u;if (k == 1) {if(u >= node_vector[i] && u < node_vector[i + 1]) Bik_u = 1;else Bik_u = 0;} else {// 公式中的两个分母double denominator_1  = node_vector[i + k - 1] - node_vector[i];double denominator_2  = node_vector[i + k] - node_vector[i + 1];// 如果遇到分母为 0的情况:// 1. 如果此时分子也为0,约定这一项整体为0;// 2. 如果此时分子不为0,则约定分母为1 if(denominator_1 < 1e-15) denominator_1 = 1;if(denominator_2 < 1e-15) denominator_2 = 1;Bik_u = (u - node_vector[i]) / denominator_1 * baseFunction(i, k-1, u, node_vector) + (node_vector[i + k] - u) / denominator_2 * baseFunction(i + 1, k - 1, u, node_vector);}return Bik_u;
}// 准均匀B样条的节点向量计算
// 首末值定义为 0 和 1
vector<double> u_quasi_uniform(int n, int k) {vector<double> node_vector(n + k + 1); //准均匀B样条的节点向量计算,共n+1个控制顶点,k-1次B样条,k阶double piecewise = n - k + 2; //B样条曲线的段数:控制点个数-次数if (piecewise == 1) {//只有一段曲线时,n = k-1for(int i = n + 1; i < n + k + 1; i++) node_vector[i] = 1;} else {//中间段内节点均匀分布:两端共2k个节点,中间还剩(n+k+1-2k=n-k+1)个节点for(int i = 0; i < n - k + 1; i++) {node_vector[k + i] = node_vector[k + i - 1] + 1 / piecewise;}for(int i = n + 1; i < n + k + 1; i++) node_vector[i] = 1;//末尾重复度k}return node_vector;
}// 分段B样条
// 分段Bezier曲线的节点向量计算,共n+1个控制顶点,k阶B样条,k-1次曲线
// 分段Bezier端节点重复度为k,内间节点重复度为k-1,且满足n/(k-1)为正整数
vector<double> u_piecewise_B_Spline(int n, int k) {vector<double> node_vector(n + k + 1);if (n % (k - 1) == 0 && (k - 1) > 0) {//满足n是k-1的整数倍且k-1为正整数for (int i = n + 1; i < n + k + 1; i++) node_vector[i] = 1;//末尾n+1到n+k+1的数重复int piecewise = n / (k-1);  //设定内节点的值if (piecewise > 1) {//内节点重复k-1次for (int i = 1; i < piecewise; i++) {for(int j = 0; j < k - 1; j++) node_vector[(k - 1) * i + j + 1] = i / piecewise;}}} else {cout<<"error!需要满足n是k-1的整数倍且k-1为正整数"<<endl;}return node_vector;
}

main.cpp

#include "BSpline.h"
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;int main(int argc, char** argv) {vector<Vector2d> Ps{Vector2d (9.036145, 51.779661),Vector2d(21.084337, 70.084746),Vector2d(37.607573, 50.254237),Vector2d(51.893287, 69.745763),Vector2d(61.187608,  49.576271)};vector<double> x_ref, y_ref;for (int i = 0; i < Ps.size(); i++){x_ref.push_back(Ps[i][0]);y_ref.push_back(Ps[i][1]);}vector<double> x_, y_;int n = Ps.size() - 1; //控制点个数-1int k = 3; //k阶、k-1次B样条Vector2d p_u(0,0);vector<double> bik_u(n+1);int flag;cout<<"请选择: 1. 均匀B样条 2.准均匀B样条  3.分段B样条 0. 退出 "<<endl;cin>>flag;vector<double> node_vector;switch (flag) {case 1://均匀B样条for(int i = 0; i < n + k + 1; i++) {node_vector.push_back((double)i / (n + k + 1));}break;case 2:node_vector = u_quasi_uniform(n, k);break;case 3:node_vector = u_piecewise_B_Spline(n, k);break;default:return 0;}if(flag == 1) {for (double u = (double)(k - 1) / (n + k + 1); u < (double)(n + 2) / (n + k+ 1); u += 0.005) {for(int i = 0; i < n + 1; i++){bik_u[i]= baseFunction(i, k, u, node_vector);//cout<<bik_u[i]<<endl;}for(int i = 0; i < Ps.size(); i++) {p_u = p_u + Ps[i] * bik_u[i];}//cout<<p_u<<endl;x_.push_back(p_u[0]);y_.push_back(p_u[1]);p_u = Vector2d (0,0);}} else {for(double u = 0; u < 1; u += 0.005) {for(int i = 0; i < n + 1; i++) {bik_u[i]= baseFunction(i, k, u, node_vector);}for(int i = 0; i < Ps.size(); i++) {p_u = p_u + Ps[i] * bik_u[i];//cout<<p_u<<","<<endl;}x_.push_back(p_u[0]);y_.push_back(p_u[1]);p_u=Vector2d (0,0);}}//画图//plt::xlim(0,1);plt::plot(x_, y_,"r");plt::plot(x_ref, y_ref);plt::pause(0.01);// save figureconst char* filename = "./b_spline_demo.png";plt::save(filename);plt::show();return 0;
}

运行结果:
在这里插入图片描述

这篇关于局部路径规划算法 - B样条曲线(B-spline Curves)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

随想录 Day 69 并查集 107. 寻找存在的路径

随想录 Day 69 并查集 107. 寻找存在的路径 理论基础 int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好vector<int> father = vector<int> (n, 0); // C++里的一种数组结构// 并查集初始化void init() {for (int i = 0; i < n; ++i) {father[i] = i;}

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

SQL Server中,用Restore DataBase把数据库还原到指定的路径

restore database 数据库名 from disk='备份文件路径' with move '数据库文件名' to '数据库文件放置路径', move '日志文件名' to '日志文件存放置路径' Go 如: restore database EaseWe from disk='H:\EaseWe.bak' with move 'Ease

大林 PID 算法

Dahlin PID算法是一种用于控制和调节系统的比例积分延迟算法。以下是一个简单的C语言实现示例: #include <stdio.h>// DALIN PID 结构体定义typedef struct {float SetPoint; // 设定点float Proportion; // 比例float Integral; // 积分float Derivative; // 微分flo

LeetCode 算法:二叉树的中序遍历 c++

原题链接🔗:二叉树的中序遍历 难度:简单⭐️ 题目 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1] 输出:[1] 提示: 树中节点数目在范围 [0, 100] 内 -100 <= Node.

【Java算法】滑动窗口 下

​ ​    🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🦌一.水果成篮 题目链接:904.水果成篮 ​ 算法原理 算法原理是使用“滑动窗口”(Sliding Window)策略,结合哈希表(Map)来高效地统计窗口内不同水果的种类数量。以下是详细分析: 初始化:创建一个空的哈希表 map 用来存储每种水果的数量,初始化左右指针 left

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插