4、B样条曲线:节点向量、基函数递归定义、德布尔算法
好,咱们进入B样条的世界。
说实话,从Bezier曲线到B样条曲线,这一步跨过去,你才真正摸到了数控插补的硬核。Bezier曲线虽然漂亮,但有个硬伤——移动一个控制点,整条曲线都跟着抖。这在工程里是致命的。你想想看,加工一个复杂曲面,我只想微调局部形状,结果整个刀轨都变了,这谁受得了?
B样条曲线就是来解决这个问题的。它把曲线分成一段一段的,每段只受附近几个控制点影响。这就是所谓的“局部支撑性”。嗯,这是B样条最核心的优势。
4.1 节点向量——B样条的“骨架”
要理解B样条,先得搞懂节点向量。我个人习惯把它比作一把尺子,这把尺子定义了曲线参数u的“地盘”怎么划分。
节点向量是一个非递减的实数序列:
U = {u₀, u₁, u₂, ..., uₘ}
其中,m = n + p + 1。n是控制点个数,p是曲线次数。
举个例子,一条3次B样条曲线,有5个控制点(n=4),那么节点向量长度m = 4 + 3 + 1 = 8。节点向量可能是这样的:
U = {0, 0, 0, 0, 0.25, 0.5, 0.75, 1, 1, 1, 1}
注意看,两端各有4个重复节点(p+1个)。这叫“clamped”B样条,曲线会经过首尾控制点。我在做五轴刀路规划时,几乎都用这种形式。
- 均匀节点向量:节点等距分布,如{0, 0.2, 0.4, 0.6, 0.8, 1.0}。计算简单,但曲线形状不够灵活。
- 非均匀节点向量:节点间距不等。可以控制曲线在某处的“松紧”程度。我在做高速加工时,经常在曲率大的区域加密节点。
- 准均匀节点向量:两端重复,中间均匀。这是工程中最常用的。
4.2 基函数的递归定义——Cox-de Boor公式
B样条的基函数是用递归方式定义的。别被“递归”两个字吓到,说白了就是“从低次往高次推”。
零次基函数(p=0)很简单:
Nᵢ,₀(u) = 1 如果 uᵢ ≤ u < uᵢ₊₁
Nᵢ,₀(u) = 0 否则
高次基函数由低次递推得到:
Nᵢ,ₚ(u) = ((u - uᵢ) / (uᵢ₊ₚ - uᵢ)) * Nᵢ,ₚ₋₁(u)
+ ((uᵢ₊ₚ₊₁ - u) / (uᵢ₊ₚ₊₁ - uᵢ₊₁)) * Nᵢ₊₁,ₚ₋₁(u)
为什么会这样?其实这个公式保证了基函数在节点区间上的连续性。你想想看,两个低次函数加权平均,得到高次函数,天然就光滑了。
- 局部支撑性:Nᵢ,ₚ(u)只在区间[uᵢ, uᵢ₊ₚ₊₁]上非零。这就是B样条“局部修改”的数学根源。
- 非负性:所有基函数都≥0。
- 单位分解性:对于任意u,所有基函数之和为1。
4.3 德布尔算法——B样条的“求值引擎”
有了基函数,理论上就能算曲线点了。但直接递归计算效率太低。德布尔算法就是用来高效计算B样条曲线上点的。
它的思路很巧妙:把控制点看作“种子”,通过线性插值一层层“生长”出曲线上的点。
算法步骤:
- 找到参数u所在的节点区间[uₖ, uₖ₊₁]。
- 取p+1个相关控制点:Pₖ₋ₚ, Pₖ₋ₚ₊₁, ..., Pₖ。
- 逐层线性插值,直到只剩一个点。
伪代码如下:
function deBoor(u, k, p, U, P):
// 复制控制点
for i = 0 to p:
Q[i] = P[k - p + i]
// 逐层递推
for r = 1 to p:
for i = 0 to p - r:
alpha = (u - U[k - p + i + r]) / (U[k + i + 1] - U[k - p + i + r])
Q[i] = (1 - alpha) * Q[i] + alpha * Q[i + 1]
return Q[0]
4.4 知识体系总览
下面这张图,是我自己总结的B样条知识结构。你看一眼,心里就有谱了。
4.5 工程中的实际考量
在实际的数控系统中,B样条插补通常分两步走:
- 预处理阶段:解析G代码中的B样条定义,提取控制点和节点向量。
- 实时插补阶段:在每个插补周期,用德布尔算法计算当前参数u对应的曲线点。
这里有个性能问题。德布尔算法虽然高效,但每个周期都要做p(p+1)/2次线性插值。对于3次B样条,就是6次。如果插补周期是1ms,CPU还能扛得住。但如果你做5次甚至7次B样条,计算量就上来了。
好了,B样条的核心内容就这些。节点向量是骨架,基函数是血肉,德布尔算法是心脏。三者缺一不可。下次你看到一段B样条插补代码,心里应该能画出这张图了。