局部路径规划算法 - 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

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

hdu2544(单源最短路径)

模板题: //题意:求1到n的最短路径,模板题#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#i

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl