赞
踩
参考资料:
(1)B样条曲线(B-spline Curves)
(2)B样条曲线法
B-样条基函数
(给定区间上的所有样条函数组成一个线性空间)的线性组合。贝塞尔曲线的缺陷:
① 确定了多边形的顶点数(n+1个),也就决定了所定义的Bezier曲线的阶次(n次),这样很不灵活。
② 当顶点数( n+1 )较大时,曲线的次数较高,曲线的导数次数也会较高,因此曲线会出现较多的峰谷值。
③ 贝塞尔曲线无法进行局部修改。
引出了B样条曲线,除了保持Bezier曲线所具有的优点外,还弥补了上述所有的缺陷。
B样条曲线:
(1)可以指定阶次;
(2)移动控制点仅仅改变曲线的部分形状,而不是整体
B样条曲线是贝塞尔曲线的一般化,贝塞尔曲线可以认为是B样条曲线的特例
设
n
+
1
n+1
n+1个控制点:
P
0
,
P
1
,
P
2
,
.
.
.
,
P
n
P_0,P_1,P_2,...,P_n
P0,P1,P2,...,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[
式中,
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
k≥1 ;
u
u
u是自变量。
式中,
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]
段数:一个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个节点
根据递推式,当阶数k=1时,不同基函数的非零域如下图:
所有节点区间列在左边(第一)列,所有零次基函数在第二列,使用如下三角计算格式。
均匀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样条曲线用途最为广泛。
许多论文中的分类是open
、clamped
、closed
(1)如果节点向量没有任何特别的结构,那么产生的曲线不会与控制曲线的第一边和最后一边接触,曲线也不会分别与第一个控制点和最后一个控制点的第一边和最后一边相切。如下面图a所示。这种类型的B-样条曲线称为开(open )B-样条曲线。对于开(open)B-样条曲线,u的定义域是
[
u
k
−
1
,
u
n
+
2
]
[u_{k-1},u_{n+2}]
[uk−1,un+2]
(2)clamped B-样条曲线即准均匀B样条曲线,如下图b。
(3)通过重复某些节点和控制点,产生的曲线会是 闭(closed)曲线。 这种情况,产生的曲线的开始和结尾连接在一起形成了一个闭环如下边图c所示。
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; else Bik_u = 0; end else Length1 = NodeVector(i+k+1) - NodeVector(i+1); Length2 = NodeVector(i+k+2) - NodeVector(i+2); % 支撑区间的长度 if Length1 == 0 % 规定0/0 = 0 Length1 = 1; end if Length2 == 0 Length2 = 1; end Bik_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 = k for i = k+2 : n+k+2 NodeVector(1, i) = 1; end else flag = 1; % 不止一段曲线时 while flag ~= piecewise NodeVector(1, k+flag+1) = NodeVector(1, k + flag) + 1/piecewise; flag = flag + 1; end NodeVector(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 : n Bik(i+1, 1) = BaseFunction(i, k-1 , u, NodeVector); end p_u = P * Bik; path = [path; [p_u(1,1),p_u(2,1)]]; end elseif flag == 2 % 准均匀B样条 NodeVector = U_quasi_uniform(n, k-1); % 准均匀B样条的节点矢量 for u = 0 : 0.005 : 1-0.005 for i = 0 : 1 : n Bik(i+1, 1) = BaseFunction(i, k-1 , u, NodeVector); end p_u = P * Bik; path=[path; [p_u(1),p_u(2)]]; end else fprintf('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-1 for(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; //控制点个数-1 int 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 figure const char* filename = "./b_spline_demo.png"; plt::save(filename); plt::show(); return 0; }
运行结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。