4. 贝塞尔曲线的计算

贝塞尔曲线是计算机辅助几何设计、路径规划与轨迹平滑中最基础的参数曲线形式。无论是机械臂末端轨迹、自动驾驶路径平滑,还是动画插值,其核心计算都围绕“给定控制点,如何高效、稳定地求出曲线上任意参数 t 对应的点坐标”展开。本章聚焦于贝塞尔曲线的数值计算方法,重点讲解 de Casteljau 算法的几何递推本质、代数递推求值公式、曲线细分策略以及工程绘制中的采样与步长控制。理解这些内容,是后续进行轨迹优化与样条拼接的前提。

贝塞尔曲线计算体系 控制点 P0..Pn n+1 个点 de Casteljau 算法 几何递推 / 线性插值 O(n²) 稳定 代数递推求值 伯恩斯坦多项式 O(n²) 直接计算 曲线细分 二分 / 递归 局部控制 绘制方法:参数采样 → 步长控制 → 折线逼近 → 渲染 自适应步长 / 弦长误差 / 曲率估计 de Casteljau 递推示意(n=3) P0 P1 P2 P3 P01 P12 P23 P012 P123 B(0.5)

上图展示了贝塞尔曲线计算的核心知识框架。从控制点输入开始,de Casteljau 算法通过反复线性插值直接构造曲线上的点,而代数递推求值则基于伯恩斯坦基函数展开。两者在数学上等价,但数值稳定性与计算开销存在差异。曲线细分利用 de Casteljau 算法的副产品——子曲线控制点,实现局部加密与递归逼近。绘制方法则关注如何将连续曲线离散为可渲染的折线,并控制逼近误差。

4.1 核心原理:de Casteljau 算法的几何递推

de Casteljau 算法是计算贝塞尔曲线上任意参数 t 处点的最直观方法。其核心思想是:对于 n 次贝塞尔曲线,给定参数 t(0 ≤ t ≤ 1),对相邻控制点进行 t:(1-t) 的线性插值,得到 n 个新点;再对这 n 个新点重复相同操作,直到只剩下一个点,该点即为曲线上的点 B(t)。

以三次贝塞尔曲线(n=3)为例,控制点为 P0、P1、P2、P3,计算过程如下:

第一层插值:
P01 = (1-t)·P0 + t·P1
P12 = (1-t)·P1 + t·P2
P23 = (1-t)·P2 + t·P3

第二层插值:
P012 = (1-t)·P01 + t·P12
P123 = (1-t)·P12 + t·P23

第三层插值:
B(t) = (1-t)·P012 + t·P123

这一过程在几何上等价于在控制多边形上反复切割。每次插值都生成一个更短的多边形,最终收敛到曲线上的点。该算法的数值稳定性优于直接使用伯恩斯坦多项式求值,因为它避免了高次幂运算中的舍入误差累积。对于高次曲线(n ≥ 10),de Casteljau 算法是工程中的首选。

关键性质:de Casteljau 算法不仅给出曲线上的点,还同时产生两条子曲线的控制点。例如,第一层插值得到的 P0, P01, P012, B(t) 构成左半段子曲线的控制点,而 B(t), P123, P23, P3 构成右半段子曲线的控制点。这一性质是曲线细分的基础。

4.2 递推求值:代数形式与计算复杂度

贝塞尔曲线的代数表达式为:

B(t) = Σ_{i=0}^{n} C(n,i) · (1-t)^(n-i) · t^i · Pi

其中 C(n,i) 为二项式系数。直接按此公式计算需要计算每个基函数的值,复杂度为 O(n²)(若每个基函数独立计算幂次)。更高效的方式是采用递推关系:

令 B_i^{(0)} = Pi
对于 k = 1 到 n:
    B_i^{(k)} = (1-t)·B_i^{(k-1)} + t·B_{i+1}^{(k-1)},其中 i = 0 到 n-k
最终 B_0^{(n)} 即为曲线上的点

这种递推形式与 de Casteljau 算法完全一致,但以代数形式表达更便于程序实现。在代码层面,可以使用一维数组原地更新,避免额外内存分配:

def de_casteljau(points, t):
    pts = points[:]  # 复制控制点
    n = len(pts) - 1
    for k in range(1, n+1):
        for i in range(n - k + 1):
            pts[i] = (1-t) * pts[i] + t * pts[i+1]
    return pts[0]

该算法的时间复杂度为 O(n²),空间复杂度为 O(n)。对于实时性要求高的场景(如机器人轨迹插补),可预先计算所有控制点,然后对每个插值周期执行一次递推。

方法时间复杂度数值稳定性适用场景
直接伯恩斯坦求值O(n²)(含幂运算)较差(高次时误差大)低次曲线(n≤5)
de Casteljau 递推O(n²)(仅乘加)优秀任意次曲线,尤其 n≥5
霍纳法(Horner)O(n)良好需预计算伯恩斯坦系数

4.3 曲线细分:递归二分与局部加密

曲线细分是指将一条贝塞尔曲线分割为两条或多条子曲线,且每条子曲线仍为贝塞尔曲线。最常用的细分方法是二分法,即取 t=0.5 进行 de Casteljau 递推。根据 4.1 节提到的性质,一次二分即可得到两条子曲线的控制点:

左子曲线控制点:P0, P01, P012, B(0.5)
右子曲线控制点:B(0.5), P123, P23, P3

细分的主要用途包括:

  • 递归逼近:反复细分直到子曲线足够平直,然后用直线段连接端点,实现曲线绘制。
  • 局部编辑:只修改某一段子曲线的控制点,而不影响整体形状。
  • 碰撞检测:将曲线细分为多个小段,用包围盒快速剔除不相交区域。
  • 并行计算:将曲线分割为多个子段,分配到不同线程并行求值。

细分的终止条件通常基于曲率或弦高误差。若子曲线两端点连线与曲线最大距离小于阈值 ε,则停止细分。该误差可通过计算子曲线中点与弦中点的距离来估计。

工程经验:对于三次贝塞尔曲线,通常细分 4-6 层即可达到视觉平滑效果。若用于高精度加工(如 CNC 路径),建议结合弦高误差控制,而非固定层数。

4.4 绘制方法:采样、步长控制与折线逼近

在屏幕上或绘图设备上绘制贝塞尔曲线,本质上是将连续曲线离散为一系列点,然后用折线连接。关键问题是如何选择采样点,使得折线在视觉上与真实曲线足够接近,同时计算量可控。

固定步长采样是最简单的方法:在 [0,1] 区间内均匀取 N+1 个参数值 t_i = i/N,计算对应的 B(t_i),依次连线。步长 Δt = 1/N 的选择需权衡精度与性能。对于曲率较大的区域,固定步长可能导致折线明显偏离曲线;对于平坦区域,则可能过度采样。

自适应步长根据局部曲率调整采样密度。常用策略包括:

  1. 计算相邻采样点之间的距离 d,若 d 超过阈值则加密;若 d 过小则稀疏。
  2. 利用 de Casteljau 递推的中间点估计曲率,曲率大的区域自动增加采样点。
  3. 采用递归细分:先均匀采样少量点,对每段检查弦高误差,超限则细分。

以下为自适应绘制的伪代码:

function draw_bezier(points, tolerance):
    stack = [(0, 1)]  # 参数区间栈
    while stack not empty:
        (t0, t1) = stack.pop()
        mid = (t0 + t1) / 2
        p0 = de_casteljau(points, t0)
        p1 = de_casteljau(points, t1)
        pm = de_casteljau(points, mid)
        # 计算弦高误差
        chord = distance(p0, p1)
        if chord == 0: continue
        dist = point_to_line_distance(pm, p0, p1)
        if dist > tolerance:
            stack.push((t0, mid))
            stack.push((mid, t1))
        else:
            draw_line(p0, p1)

该方法在曲率大的区域自动细分,平坦区域则用长线段,兼顾效率与精度。

4.5 案例拆解:机器人路径平滑中的贝塞尔曲线绘制

背景与约束:某六轴工业机器人需要沿一条三次贝塞尔曲线轨迹运动,控制点由路径规划模块给出:P0=(0,0),P1=(50,80),P2=(120,60),P3=(180,20)(单位:mm)。机器人控制器要求轨迹点间距不超过 2mm,且插补周期为 4ms,即每 4ms 需计算一个目标位置。

初始现象:若采用固定步长 N=50 采样,相邻点间距在曲线中部(曲率较大处)达到 4.5mm,超出 2mm 限制,导致机器人运动不平滑,出现抖动。

分析过程:计算曲线在 t=0.5 处的曲率半径约为 85mm,而两端曲率较小。固定步长无法适应曲率变化,必须采用自适应策略。

实际操作:

  1. 设定弦高误差阈值为 0.1mm(对应 2mm 点距的 5%)。
  2. 使用 4.4 节的自适应细分算法,初始区间 [0,1]。
  3. 递归细分后,共生成 37 个采样点,点距最大 1.8mm,最小 0.6mm。
  4. 将采样点按参数 t 顺序排列,作为插补目标位置。

结果对比:固定步长 N=50 时,最大点距 4.5mm,轨迹误差 RMS 为 0.32mm;自适应方法最大点距 1.8mm,轨迹误差 RMS 为 0.08mm,满足精度要求,且采样点数减少 26%,计算量更低。

风险与代价:自适应细分需要额外的曲率估计计算,在 CPU 资源受限的控制器上可能增加 10-15% 的耗时。但通过预先离线计算轨迹点,可避免实时开销。

复盘经验:对于轨迹平滑,应优先采用基于弦高误差的自适应采样,而非盲目增加固定采样密度。同时,de Casteljau 算法在递推过程中产生的中间点可直接用于误差估计,无需额外计算。

4.6 常见误区与总结复盘

误区一:认为 de Casteljau 算法只适用于低次曲线。实际上,该算法对任意次曲线均有效,且数值稳定性优于直接多项式求值。高次曲线(n>10)虽不常用,但 de Casteljau 仍可稳定计算。
误区二:绘制时盲目增加采样点数量。固定步长 N=1000 看似精确,但计算量大且可能引入冗余点。正确做法是结合曲率自适应调整步长。
误区三:忽略 de Casteljau 算法产生的子曲线控制点。这些控制点不仅用于细分,还可用于曲线拼接、局部修改和碰撞检测,是工程中的宝贵副产品。

总结:本章围绕贝塞尔曲线的计算展开,核心要点如下:

  • de Casteljau 算法通过反复线性插值求值,时间复杂度 O(n²),数值稳定,是工程首选。
  • 递推求值公式与 de Casteljau 算法等价,适合编程实现,可原地更新数组。
  • 曲线细分利用 t=0.5 的 de Casteljau 结果,将曲线一分为二,支持递归逼近与局部编辑。
  • 绘制方法应基于弦高误差自适应采样,平衡精度与计算量。

掌握这些计算方法,是后续学习 B 样条、NURBS 以及轨迹优化算法的基础。在实际项目中,建议优先使用 de Casteljau 算法配合自适应细分,以获得稳定、高效的曲线计算与绘制能力。