B样条曲线(B-spline)是一种常用的参数曲线,广泛应用于计算机图形学、CAD、动画等领域。B样条曲线具有局部控制性、光滑性等优点。

1. B样条曲线的基本概念

B样条曲线是由一组控制点和一组基函数(Basis Functions)定义的。B样条曲线的数学表达式为:

C(u)=∑i=0nNi,p(u)⋅Pi C(u) = \sum_{i=0}^{n} N_{i,p}(u) \cdot P_i C(u)=i=0nNi,p(u)Pi

其中:

  • (C(u))( C(u) )(C(u)) 是曲线上的点,(u)( u )(u) 是参数。
  • (Pi)( P_i )(Pi) 是控制点。
  • (Ni,p(u))( N_{i,p}(u) )(Ni,p(u)) 是第 (i)( i )(i)(p)( p )(p) 次B样条基函数。

2. B样条基函数的递推公式

B样条基函数 (Ni,p(u))( N_{i,p}(u) )(Ni,p(u)) 是通过递推公式定义的:

Ni,0(u)={1if ui≤u<ui+10otherwise N_{i,0}(u) = \begin{cases} 1 & \text{if } u_i \leq u < u_{i+1} \\ 0 & \text{otherwise} \end{cases} Ni,0(u)={10if uiu<ui+1otherwise

对于 (p>0)( p > 0 )(p>0),基函数的递推公式为:

Ni,p(u)=u−uiui+p−uiNi,p−1(u)+ui+p+1−uui+p+1−ui+1Ni+1,p−1(u) N_{i,p}(u) = \frac{u - u_i}{u_{i+p} - u_i} N_{i,p-1}(u) + \frac{u_{i+p+1} - u}{u_{i+p+1} - u_{i+1}} N_{i+1,p-1}(u) Ni,p(u)=ui+puiuuiNi,p1(u)+ui+p+1ui+1ui+p+1uNi+1,p1(u)

其中,(ui)( u_i )(ui) 是节点向量(Knot Vector)中的节点。

3. 节点向量

节点向量 (U={u0,u1,…,um})( U = \{u_0, u_1, \dots, u_{m}\} )(U={u0,u1,,um}) 是一个非递减序列,决定了B样条曲线的形状。节点向量的选择对曲线的性质有重要影响。

4. C++实现

下面是一个简单的C++实现,用于生成B样条曲线。

#include <iostream>
#include <vector>
#include <cmath>

// 计算B样条基函数
double BsplineBasis(int i, int p, double u, const std::vector<double>& knots) {
    if (p == 0) {
        if (knots[i] <= u && u < knots[i + 1]) {
            return 1.0;
        } else {
            return 0.0;
        }
    } else {
        double coef1 = (knots[i + p] - knots[i] == 0) ? 0 : (u - knots[i]) / (knots[i + p] - knots[i]);
        double coef2 = (knots[i + p + 1] - knots[i + 1] == 0) ? 0 : (knots[i + p + 1] - u) / (knots[i + p + 1] - knots[i + 1]);
        return coef1 * BsplineBasis(i, p - 1, u, knots) + coef2 * BsplineBasis(i + 1, p - 1, u, knots);
    }
}

// 计算B样条曲线上的点
std::vector<double> BsplineCurve(const std::vector<std::vector<double>>& controlPoints, int p, const std::vector<double>& knots, double u) {
    std::vector<double> point(controlPoints[0].size(), 0.0);
    for (size_t i = 0; i < controlPoints.size(); ++i) {
        double basis = BsplineBasis(i, p, u, knots);
        for (size_t j = 0; j < point.size(); ++j) {
            point[j] += basis * controlPoints[i][j];
        }
    }
    return point;
}

int main() {
    // 控制点
    std::vector<std::vector<double>> controlPoints = {
        {0, 0},
        {1, 2},
        {2, 3},
        {4, 1},
        {5, 0}
    };

    // 节点向量
    std::vector<double> knots = {0, 0, 0, 0, 1, 2, 2, 2, 2};

    // B样条次数
    int p = 3;

    // 生成曲线上的点
    for (double u = 0.0; u <= 2.0; u += 0.1) {
        std::vector<double> point = BsplineCurve(controlPoints, p, knots, u);
        std::cout << "u = " << u << ": (" << point[0] << ", " << point[1] << ")" << std::endl;
    }

    return 0;
}

5. 代码说明

  • BsplineBasis 函数用于计算B样条基函数。
  • BsplineCurve 函数用于计算B样条曲线上的点。
  • controlPoints 是控制点集合。
  • knots 是节点向量。
  • p 是B样条的次数。

6. 运行结果

运行上述代码,程序将输出B样条曲线在不同参数 (u)( u )(u) 下的点坐标。

7. 总结

B样条曲线的生成依赖于控制点、节点向量和基函数的计算。通过递推公式可以高效地计算基函数,进而生成曲线。C++实现提供了一个简单的框架,可以根据需要进行扩展和优化。

其他说明:节点向量说明

B样条曲线的节点向量(Knot Vector) 是定义B样条的核心基础要素(与控制顶点、次数并列),本质是一组有序的实数序列,直接决定了B样条基函数的形状、支撑区间、分段特性,进而完全约束B样条曲线的分段拼接位置、光滑度、局部性、参数化范围等关键属性,没有节点向量就无法构造B样条基函数,也无法生成对应的B样条曲线。

简单来说:控制顶点决定曲线的“大致轮廓”,次数决定曲线的“光滑程度上限”,而节点向量决定曲线的“分段方式、光滑度实际值、局部影响范围”

一、先明确节点向量的基本定义

对于p次B样条曲线,若有n+1个控制顶点P0,P1,...,PnP_0,P_1,...,P_nP0,P1,...,Pn),则对应的节点向量为有序实数序列
U=[u0,u1,...,um],m=n+p+1U = [u_0, u_1, ..., u_m], \quad m = n + p + 1U=[u0,u1,...,um],m=n+p+1
满足单调性u0≤u1≤...≤umu_0 ≤ u_1 ≤ ... ≤ u_mu0u1...um,其中每个uiu_iui称为节点,相邻节点的区间[ui,ui+1][u_i, u_{i+1}][ui,ui+1]称为节点区间,参数uuu的取值范围为[u0,um][u_0, u_m][u0,um](通常归一化到[0,1][0,1][0,1],称为标准节点向量)。

节点按重复次数可分为:

  • 单节点ui<ui+1u_i < u_{i+1}ui<ui+1,无重复;
  • 重节点ui=ui+1=...=ui+ku_i = u_{i+1} = ... = u_{i+k}ui=ui+1=...=ui+k,重复次数为k+1k+1k+1(最大重复次数≤p+1,否则基函数无定义)。

二、节点向量的6大核心作用(从基础到进阶)

1. 定义B样条基函数的支撑区间,决定基函数的非零范围

B样条基函数Ni,p(u)N_{i,p}(u)Ni,p(u)局部非零的(这是B样条优于贝塞尔曲线的核心特性之一),而其非零的参数区间完全由节点向量决定
Ni,p(u)N_{i,p}(u)Ni,p(u)仅在节点区间[ui,ui+p+1][u_i, u_{i+p+1}][ui,ui+p+1]内非零,在区间外恒为0。
直接影响:某一控制顶点PiP_iPi仅通过基函数Ni,p(u)N_{i,p}(u)Ni,p(u)影响曲线在[ui,ui+p+1][u_i, u_{i+p+1}][ui,ui+p+1]的部分,修改单个控制顶点,仅改变曲线的局部形状,这就是B样条的局部性,而节点向量是局部性的“边界定义者”。

2. 划分B样条曲线的分段区间,决定曲线的分段拼接位置

p次B样条曲线是分段p次多项式曲线,其分段的分界点(拼接点) 恰好是节点向量中的非单节点(重节点/内部节点),且分段区间与节点向量的子区间一一对应:
曲线在参数区间[uk,uk+1][u_k, u_{k+1}][uk,uk+1]上是单一的p次多项式,当uuu跨越重节点时,曲线切换到下一个p次多项式段。
举例:3次(p=3)B样条,节点向量U=[0,0,0,0,0.5,1,1,1,1]U=[0,0,0,0,0.5,1,1,1,1]U=[0,0,0,0,0.5,1,1,1,1],则曲线在[0,0.5][0,0.5][0,0.5][0.5,1][0.5,1][0.5,1]各为一段3次多项式,在u=0.5u=0.5u=0.5处拼接。

3. 控制曲线在节点处的光滑度,重节点降低光滑度

B样条曲线在节点u=uku=u_ku=uk处的光滑度由节点的重复次数r决定,这是节点向量最核心的功能之一:
设p为B样条次数,节点uku_kuk的重复次数为r(1≤r≤p+11≤r≤p+11rp+1),则曲线在uku_kuk处的光滑度为Cp−rC^{p-r}Cpr(即p-r阶连续可导)。
核心结论:重节点次数越高,光滑度越低;单节点光滑度最高,重节点次数=p+1时,光滑度为C−1C^{-1}C1(曲线在该点不连续,出现折点/断点)。
经典举例(p=3,3次B样条)

  • 单节点(r=1):C2C^2C2光滑(二阶导数连续,曲线极光滑);
  • 二重节点(r=2):C1C^1C1光滑(一阶导数连续,曲线光滑,一阶导有折点);
  • 三重节点(r=3):C0C^0C0光滑(仅位置连续,一阶导不连续,曲线有明显折点);
  • 四重节点(r=4=p+1):C−1C^{-1}C1不连续(曲线在该点断开)。
4. 决定B样条的参数化范围,归一化实现通用控制

节点向量的首尾节点u0u_0u0umu_mum定义了曲线参数uuu取值范围,工程中通常将其归一化到[0,1](标准节点向量),使得曲线的参数化与实际几何尺寸解耦,方便统一计算和控制。
例如:无论曲线实际长度是100mm还是1000mm,都可通过归一化节点向量将参数u限定在[0,1],基函数的计算规则完全一致,仅需对控制顶点做坐标缩放即可。

5. 区分B样条的类型(均匀/非均匀,开/闭)

节点向量的分布方式直接决定了B样条的类型,不同类型的B样条曲线具有截然不同的特性,适配不同的工程场景:

节点向量类型分布特征曲线特性适用场景
均匀开B样条节点等间距,首尾重节点次数=p+1(如p=3:[0,0,0,0,1,2,3,3,3,3])基函数平移不变,曲线两端插值首末控制顶点,局部性强机械建模、路径规划(需贴合端点)
均匀闭B样条节点等间距,无重节点(如p=3:[0,1,2,3,4,5,6])曲线闭合,基函数周期重复,光滑度一致圆形/环形轮廓建模、闭合轨迹
非均匀B样条节点间距不等,可按需设置重节点可灵活调整分段位置和光滑度,局部可控性极致自由曲面建模、复杂轮廓设计(如汽车车身)
准均匀B样条内部节点等间距,首尾重节点次数=p+1兼顾均匀B样条的简洁性和非均匀的端点插值特性常规工业设计、简单路径生成

最常用非均匀有理B样条(NURBS),其节点向量为非均匀分布,是CAD/CAM、机器人路径规划的核心基础,而节点向量的非均匀性是实现复杂轮廓精细控制的关键。

6. 约束控制顶点的影响范围,实现精准的局部修改

如前所述,p次B样条的第i个基函数Ni,p(u)N_{i,p}(u)Ni,p(u)的支撑区间为[ui,ui+p+1][u_i, u_{i+p+1}][ui,ui+p+1],因此控制顶点PiP_iPi仅影响曲线在该区间的部分,影响范围的长度由节点向量的间距决定

  • 节点间距越小,控制顶点的影响范围越窄,曲线的局部修改越精准;
  • 节点间距越大,控制顶点的影响范围越宽,曲线的整体平滑性越好。

工程价值:在机器人路径规划、CAD建模中,可通过加密局部节点向量(减小间距)实现对曲线局部段的精细调整(如路径的拐角处),而不影响曲线的其他部分,这是贝塞尔曲线(无局部性,修改一个控制点影响整条曲线)无法实现的。

三、节点向量与贝塞尔曲线的关联(理解本质)

贝塞尔曲线是B样条曲线的特殊情况:当p次B样条的节点向量为首尾重节点次数=p+1,且仅有一个节点区间时(如p=3:[0,0,0,0,1,1,1,1][0,0,0,0,1,1,1,1][0,0,0,0,1,1,1,1]),B样条基函数退化为贝塞尔基函数,B样条曲线退化为p次贝塞尔曲线。
本质:贝塞尔曲线的“节点向量”是极端特殊的重节点分布,导致其失去了B样条的局部性,而B样条通过通用的节点向量实现了对贝塞尔曲线的泛化和优化。

四、工程应用中的节点向量设计原则(实操要点)

  1. 首尾重节点:工程中多采用开B样条,首尾节点重复次数=p+1,确保曲线插值首末控制顶点(贴合起始/终止位置,如机器人路径的起点和终点);
  2. 局部加密:在曲线需要精细调整/拐角处,减小节点间距、增加节点数量,让控制顶点的影响范围收窄,实现精准控制;
  3. 光滑度匹配:在需要高光滑度的段(如机器人匀速路径)用单节点/二重节点,在需要折点的段(如路径的拐角)用三重节点(p=3),实现C0C^0C0连续;
  4. 避免过度重节点:重节点次数不超过p+1(否则基函数无定义),且非必要不使用p+1次重节点(避免曲线不连续);
  5. 归一化:优先将节点向量归一化到[0,1],方便后续的参数插值、速度规划(如将B样条的参数u与运动时间关联)。

五、一句话总结节点向量的核心价值

节点向量是B样条曲线的**“分段与光滑度控制器”**,通过定义节点的位置、间距和重复次数,既决定了B样条基函数的局部性和形状,又实现了曲线分段拼接、光滑度灵活调整、局部精准修改的核心特性,让B样条成为比贝塞尔曲线更通用、更适合工程复杂场景的曲线建模工具。

延伸:机器人路径规划中的节点向量设计示例

3次(p=3)机器人路径规划为例,需生成从起点A到终点B,中间经过拐角C的路径,节点向量设计为:
U=[0,0,0,0,0.3,0.5,0.7,1,1,1,1]U = [0,0,0,0, 0.3, 0.5, 0.7, 1,1,1,1]U=[0,0,0,0,0.3,0.5,0.7,1,1,1,1]

  • 首尾[0,0,0,0]和[1,1,1,1]:3次重节点(p+1),确保曲线插值起点A和终点B;
  • 节点0.3/0.5/0.7:对应路径的平缓段/拐角C/平缓段,拐角C处(0.5)设二重节点,让曲线在该点为C1C^1C1连续,兼顾光滑度和拐角的精准性;
  • 节点间距:0.3-0.5间距小,加密拐角处节点,实现拐角的精细控制。

这种设计通过节点向量实现了**“端点贴合+拐角精准+平缓段光滑”**的路径需求,是机器人B样条路径规划的典型做法。

更多推荐