4. 逐点比较法(三):圆弧插补原理,偏差计算公式推导,象限处理与过象限问题
好,咱们接着聊逐点比较法。前两章讲了直线插补,这一章轮到圆弧了。说实话,圆弧插补比直线要绕一点,但核心思路没变——还是那四个字:逐点比较。
你想想看,直线插补时,我们比较的是当前点与理想直线的偏离。圆弧呢?比较的是当前点到圆心的距离,跟理想半径比一比。说白了,就是看你是跑到了圆里面,还是圆外面。
核心思想:每走一步,算一次偏差,根据偏差决定下一步往哪个方向走。偏差为零,说明正好在圆弧上;偏差为正,说明在圆外;偏差为负,说明在圆内。
4.1 圆弧插补的偏差函数
假设我们要加工第一象限的逆圆弧。起点是 A(x₀, y₀),终点是 B(xₑ, yₑ),圆心在原点,半径为 R。
圆弧上任意一点 (x, y) 满足:
x² + y² = R²
那么,对于当前刀具所在点 P(xᵢ, yⱼ),我们定义偏差函数:
Fᵢⱼ = xᵢ² + yⱼ² - R²
这个 F 值就是判断依据:
- F = 0:点在圆弧上
- F > 0:点在圆弧外侧
- F < 0:点在圆弧内侧
嗯,这里要注意:偏差的正负决定了进给方向。对于逆圆弧,F ≥ 0 时我们向 -X 方向走一步,F < 0 时向 +Y 方向走一步。为什么?因为要往圆弧靠拢嘛。
4.2 偏差递推公式推导
直接算 x² + y² - R² 当然可以,但每次都要算平方,计算量太大。我早期做嵌入式数控系统时,CPU 算力有限,平方运算简直是噩梦。所以必须用递推公式。
咱们分两种情况推导:
情况一:F ≥ 0,向 -X 方向走一步
新坐标:xᵢ₊₁ = xᵢ - 1,yⱼ 不变
新偏差:
Fᵢ₊₁,ⱼ = (xᵢ - 1)² + yⱼ² - R²
= xᵢ² - 2xᵢ + 1 + yⱼ² - R²
= (xᵢ² + yⱼ² - R²) - 2xᵢ + 1
= Fᵢⱼ - 2xᵢ + 1
情况二:F < 0,向 +Y 方向走一步
新坐标:xᵢ 不变,yⱼ₊₁ = yⱼ + 1
新偏差:
Fᵢ,ⱼ₊₁ = xᵢ² + (yⱼ + 1)² - R²
= xᵢ² + yⱼ² + 2yⱼ + 1 - R²
= (xᵢ² + yⱼ² - R²) + 2yⱼ + 1
= Fᵢⱼ + 2yⱼ + 1
你看,递推公式里只有加减法和移位操作,没有平方。这在老式单片机系统上跑起来飞快。我记得有一次调试一个 8 位机上的圆弧插补,用递推公式后,插补周期从 2ms 降到了 0.3ms,效果立竿见影。
个人习惯:我写圆弧插补时,会把初始偏差 F₀ 先算好。起点在圆弧上,所以 F₀ = 0。但如果起点不在圆弧上(比如从直线切过来),F₀ 就得老老实实算 x₀² + y₀² - R²。
4.3 象限处理
第一象限搞定了,其他象限呢?其实原理一样,只是进给方向变了。我整理了一张表,方便你对照:
| 象限 | 圆弧方向 | F ≥ 0 进给方向 | F < 0 进给方向 |
|---|---|---|---|
| 一 | 逆圆 | -X | +Y |
| 一 | 顺圆 | -Y | +X |
| 二 | 逆圆 | -Y | -X |
| 二 | 顺圆 | +X | -Y |
| 三 | 逆圆 | +X | -Y |
| 三 | 顺圆 | +Y | +X |
| 四 | 逆圆 | +Y | +X |
| 四 | 顺圆 | -X | +Y |
你可能会问:这 8 种情况怎么记?我有个笨办法——记住第一象限逆圆,然后根据对称性推。但实际写代码时,我建议直接用查表法,把进给方向存成数组,运行时根据象限和方向索引。这样代码清晰,不容易出错。
4.4 过象限问题
过象限,是圆弧插补里最容易翻车的地方。什么叫过象限?就是圆弧从当前象限跨到相邻象限。比如从第一象限逆圆走到第二象限,中间要经过 90° 边界。
为什么会出问题?因为跨象限时,进给方向要变。如果程序没处理好,刀具会在边界上犹豫不决,甚至跑偏。
我曾经踩过的坑:有一次做雕刻机控制系统,加工一个整圆。第一象限跑得好好的,一到 90° 边界,电机突然抖了一下,工件上留下一道刀痕。查了两天才发现,是过象限时偏差递推公式里的坐标符号没更新,导致进给方向算错了。
解决过象限问题,通常有两种思路:
- 象限切换法:检测到当前点到达象限边界(x=0 或 y=0),立即切换象限标志,更新进给方向和偏差公式中的符号。
- 统一坐标法:把坐标取绝对值,始终按第一象限的规则算,进给方向由象限和圆弧方向共同决定。这种方法代码更简洁,但逻辑要小心。
我个人更推荐第二种。你看,不管在哪个象限,偏差递推公式的形式是一样的——F ± 2|x| + 1 或 F ± 2|y| + 1。区别只在于加减号和进给轴。把坐标取绝对值后,递推公式统一了,进给方向单独查表,逻辑分离,调试起来方便很多。
4.5 圆弧插补流程图
下面这张图是我当年画给团队培训用的,把整个流程串了一遍:
流程其实不复杂:初始化 → 判终点 → 判偏差 → 走步 → 更新偏差 → 回环。关键就两点:一是偏差递推公式要写对,二是过象限时坐标符号和进给方向要同步切换。
4.6 代码示例
最后,给一段我常用的圆弧插补核心代码。这是第一象限逆圆的实现,其他象限加上象限判断逻辑即可:
// 第一象限逆圆插补
// 起点(x0,y0),终点(xe,ye),圆心在原点
void arc_interp_ccw_q1(int x0, int y0, int xe, int ye) {
int x = x0, y = y0;
int F = 0; // 起点在圆弧上,偏差为0
while (1) {
// 判断是否到达终点
if (x == xe && y == ye) break;
if (F >= 0) {
// 向 -X 方向走一步
x--;
F = F - 2*x - 1; // 注意:这里 x 已经更新,所以是 -2*x - 1
} else {
// 向 +Y 方向走一步
y++;
F = F + 2*y - 1; // 注意:这里 y 已经更新,所以是 +2*y - 1
}
// 输出脉冲或更新位置
// output_step();
}
}
一个小细节:递推公式里,F = F - 2x - 1 中的 x 是更新后的值。如果你先算 F 再更新坐标,公式要写成 F = F - 2*(x+1) + 1。两种写法等价,但容易搞混。我习惯先更新坐标再算 F,这样公式更直观。