3. 逐点比较法(二):圆弧插补原理,偏差计算与进给规则,终点判别方法
上一章我们聊了直线插补,说白了就是让刀具走一条笔直的路径。但实际加工中,圆弧才是最常见的轮廓——倒角、圆孔、弧形曲面,哪个都离不开它。逐点比较法处理圆弧的思路,其实和直线一脉相承:每一步都判断当前位置在圆弧的哪一侧,然后决定下一步往哪走。
我个人习惯把圆弧插补比作「走钢丝」——你每一步都要知道自己偏了没有,偏了就赶紧纠正。今天我们就把这个过程拆开来看。
3.1 圆弧插补的基本原理
先想一个问题:怎么判断一个点是在圆弧上、圆弧内还是圆弧外?
假设我们要加工第一象限的逆圆弧,圆心在原点,半径为 R。对于任意点 P(x, y),它到圆心的距离平方是 x² + y²。和 R² 比一比:
- 如果 x² + y² = R²,点在圆弧上
- 如果 x² + y² > R²,点在圆弧外
- 如果 x² + y² < R²,点在圆弧内
嗯,这就是偏差判断的数学基础。我们定义偏差函数:
F = x² + y² - R²
F = 0 时在圆上,F > 0 在圆外,F < 0 在圆内。就这么简单。
核心思想:逐点比较法圆弧插补的本质,就是通过偏差 F 的符号,决定下一步进给方向,使刀具始终逼近理想圆弧。
3.2 进给规则:怎么走才能不跑偏?
知道了偏差,下一步就是「怎么走」。对于第一象限逆圆弧,规则很直观:
- F ≥ 0:点在圆上或圆外,需要向圆内走,也就是沿 -X 方向进给一步
- F < 0:点在圆内,需要向圆外走,也就是沿 +Y 方向进给一步
你想想看,这个逻辑其实和直线插补很像——都是「偏哪补哪」。只不过圆弧的偏差计算稍微复杂一点,因为每走一步,坐标变了,偏差也要跟着更新。
我的经验:刚开始学的时候,我总把进给方向和象限搞混。后来我总结了一个口诀:「逆圆外走 -X,内走 +Y;顺圆反过来」。记牢这个,写代码时就不容易晕。
3.3 偏差计算的递推公式
如果每一步都重新算 x² + y² - R²,计算量太大了。实际工程中,我们用的是递推公式——根据上一步的偏差和进给方向,快速算出下一步的偏差。
假设当前点 P(xᵢ, yⱼ),偏差 Fᵢⱼ = xᵢ² + yⱼ² - R²。
情况一:沿 -X 方向进给一步
新坐标:xᵢ₊₁ = xᵢ - 1,yⱼ 不变
新偏差:
Fᵢ₊₁,ⱼ = (xᵢ - 1)² + yⱼ² - R²
= xᵢ² - 2xᵢ + 1 + yⱼ² - R²
= Fᵢⱼ - 2xᵢ + 1
情况二:沿 +Y 方向进给一步
新坐标:xᵢ 不变,yⱼ₊₁ = yⱼ + 1
新偏差:
Fᵢ,ⱼ₊₁ = xᵢ² + (yⱼ + 1)² - R²
= xᵢ² + yⱼ² + 2yⱼ + 1 - R²
= Fᵢⱼ + 2yⱼ + 1
你看,只需要一次加法和一次乘法,比重新算平方快多了。我在项目中用过这个递推公式,在 8 位单片机上跑圆弧插补,速度完全够用。
注意:递推公式中的 xᵢ 和 yⱼ 是当前坐标值,不是步数。每次更新偏差后,别忘了同步更新坐标值,否则下一步的递推就会出错。我曾经在这个坑里栽过一次,调试了一下午才发现是坐标没更新。
3.4 终点判别方法
圆弧插补不能一直走,得知道什么时候停。终点判别有三种常用方法:
| 方法 | 原理 | 特点 |
|---|---|---|
| 总步数法 | 计算 X 方向和 Y 方向总步数 n = |Xₑ - Xₛ| + |Yₑ - Yₛ|,每走一步减 1 | 简单可靠,最常用 |
| 坐标比较法 | 实时比较当前坐标是否到达终点坐标 | 直观,但需注意象限切换 |
| 偏差归零法 | 当偏差 F = 0 且已走完理论弧长时停止 | 精度高,但实现复杂 |
我个人最推荐总步数法。为什么?因为它和进给方向无关,不管你怎么走,总步数是固定的。在项目里,我一般初始化时就算好总步数,然后每进给一步就减 1,减到 0 就停。简单、稳定、不出错。
避坑指南:我曾经在一个四轴联动项目里用了坐标比较法,结果因为象限切换时坐标值有微小误差,导致终点判断提前触发,圆弧缺了一小段。后来换成总步数法,问题立刻解决。所以,除非你有特殊需求,否则无脑选总步数法。
3.5 完整插补流程与 SVG 图解
把上面这些串起来,一个完整的圆弧插补流程是这样的:
- 初始化:设置起点坐标、终点坐标、圆心、半径,计算总步数 n
- 偏差判断:根据当前偏差 F 的符号,决定进给方向
- 进给一步:沿选定方向走一个脉冲当量
- 偏差更新:用递推公式计算新偏差
- 坐标更新:更新当前坐标值
- 终点判别:总步数减 1,若不为 0 则回到步骤 2
- 结束:到达终点,插补完成
下面这张图把这个流程画清楚了:
3.6 代码实现示例
理论说完了,上代码。下面是一个第一象限逆圆弧插补的 C 语言实现片段:
// 圆弧插补:第一象限逆圆弧
// 起点 (xs, ys),终点 (xe, ye),圆心 (0, 0)
void arc_interp(int xs, int ys, int xe, int ye) {
int x = xs, y = ys;
int F = x*x + y*y - R*R; // 初始偏差
int n = abs(xe - xs) + abs(ye - ys); // 总步数
while (n > 0) {
if (F >= 0) {
// 圆外/圆上,向 -X 走
x--;
F = F - 2*x - 1; // 注意:x 已更新,公式用新 x
} else {
// 圆内,向 +Y 走
y++;
F = F + 2*y - 1; // 注意:y 已更新,公式用新 y
}
n--;
// 这里可以输出脉冲信号给电机
}
}
注意:上面代码中递推公式的符号和前面推导略有不同,因为我在更新坐标后才计算偏差。如果你先算偏差再更新坐标,公式要反过来。这个细节很容易搞混,我建议你写代码时统一用一种顺序,并在注释里写清楚。
3.7 不同象限的进给规则
第一象限搞定了,其他象限怎么办?其实原理一样,只是进给方向不同。我整理了一张表:
| 象限 | 圆弧方向 | F ≥ 0 进给 | F < 0 进给 |
|---|---|---|---|
| 第一象限 | 逆圆弧 | -X | +Y |
| 第一象限 | 顺圆弧 | -Y | +X |
| 第二象限 | 逆圆弧 | +Y | -X |
| 第二象限 | 顺圆弧 | +X | -Y |
| 第三象限 | 逆圆弧 | +X | -Y |
| 第三象限 | 顺圆弧 | +Y | -X |
| 第四象限 | 逆圆弧 | -Y | +X |
| 第四象限 | 顺圆弧 | -X | +Y |
嗯,这张表看起来有点复杂。但如果你仔细观察,会发现一个规律:逆圆弧的进给方向,其实就是把坐标轴往圆内方向推;顺圆弧则相反。记住这个规律,比死记硬背强多了。
我的习惯:在实际项目中,我不会为每个象限单独写一套逻辑。我会把坐标做象限变换,统一映射到第一象限处理,最后再把结果映射回去。这样代码量少一半,也不容易出错。