4. 逐点比较法(二):圆弧插补原理、象限处理、终点判别
好,咱们接着聊逐点比较法。上一章我们把直线插补讲透了,这一章轮到圆弧了。
说实话,圆弧插补比直线要稍微绕一点。为什么?因为直线只有一个方向偏差,而圆弧的偏差跟半径、象限都有关系。我刚开始做数控系统那会儿,第一次写圆弧插补就栽了个跟头——四个象限的符号处理错了,结果画出来的圆像个被踩扁的易拉罐。
嗯,咱们今天就把这个坑填平。
4.1 圆弧插补的基本原理
先想一个问题:怎么判断一个点是在圆内、圆外,还是在圆上?
很简单,看它到圆心的距离跟半径比一下就行。
假设我们要插补第一象限的逆圆(逆时针方向),圆心在原点,半径为 R。当前刀具位置是 P(x, y),那么偏差函数就是:
F = x² + y² - R²
- F = 0:点在圆上,完美
- F > 0:点在圆外,需要向圆内方向走
- F < 0:点在圆内,需要向圆外方向走
那问题来了:向哪个方向走?这取决于圆弧的方向——是逆圆还是顺圆。
核心口诀(我个人习惯这么记):
- 逆圆:F ≥ 0 走 -X,F < 0 走 +Y
- 顺圆:F ≥ 0 走 -Y,F < 0 走 +X
说白了,偏差大就往里收,偏差小就往外扩。方向反了,圆就画歪了。
4.2 象限处理——四个象限的符号规则
第一象限搞定了,那第二、第三、第四象限呢?
你想想看,刀具在不同象限,坐标的符号不一样,进给方向自然也要跟着变。我见过不少新手直接套第一象限的公式,结果画出来的圆在象限交界处断开了。
咱们先看逆圆四个象限的进给规则:
| 象限 | F ≥ 0 进给方向 | F < 0 进给方向 |
|---|---|---|
| 第一象限 | -X | +Y |
| 第二象限 | +Y | -X |
| 第三象限 | +X | -Y |
| 第四象限 | -Y | +X |
再看顺圆的规则:
| 象限 | F ≥ 0 进给方向 | F < 0 进给方向 |
|---|---|---|
| 第一象限 | -Y | +X |
| 第二象限 | -X | -Y |
| 第三象限 | +Y | -X |
| 第四象限 | +X | +Y |
我的小技巧:别死记硬背。你只需要记住第一象限的规则,然后根据坐标轴的对称性推导其他象限。比如逆圆在第二象限,X 是负的,Y 是正的,进给方向自然要跟着坐标符号翻转。我曾经在调试一台五轴机床时,就是用这个对称性快速排查了一个象限处理 bug。
4.3 终点判别——怎么知道圆画完了?
圆弧插补的终点判别比直线复杂一些。直线可以用总步数,但圆弧呢?
常用的方法有三种:
- 总步数法:计算从起点到终点需要走的总步数,每走一步减一,减到零就停。
- 坐标比较法:每次进给后判断当前坐标是否到达终点坐标。
- 偏差值法:当偏差值 F 回到某个阈值范围内时认为到达终点。
我个人最常用的是总步数法。为什么?简单、可靠、不依赖坐标精度。
总步数的计算公式:
n = |Xe - Xs| + |Ye - Ys|
其中 (Xs, Ys) 是起点,(Xe, Ye) 是终点。注意,这里用的是曼哈顿距离,不是欧氏距离。因为逐点比较法每一步只走一个轴,曼哈顿距离正好等于总步数。
注意:总步数法在跨象限时可能会出问题。比如从第一象限走到第二象限,步数算对了,但实际路径可能因为象限切换而多走或少走。我曾经在一个项目中遇到过这个问题——圆画到最后差了一个脉冲,查了半天才发现是终点判别没考虑象限切换。
解决方案:在象限切换时重新计算剩余步数,或者改用坐标比较法做双重校验。
4.4 圆弧插补的完整流程
把上面这些串起来,一个完整的圆弧插补流程是这样的:
- 输入起点、终点、圆心、圆弧方向(逆圆/顺圆)
- 计算初始偏差值 F = Xs² + Ys² - R²
- 计算总步数 n = |Xe - Xs| + |Ye - Ys|
- 判断当前象限
- 根据象限和 F 值决定进给方向
- 更新坐标和偏差值
- 步数减一,判断是否到达终点
- 如果没到,回到步骤 4;如果到了,结束
下面我用一个 SVG 流程图把整个过程可视化一下:
4.5 代码实现——C 语言版圆弧插补
光说不练假把式。下面我给出一段完整的 C 语言实现,注释写得很清楚,你可以直接拿去用:
// 圆弧插补函数
// 参数:起点 (xs, ys),终点 (xe, ye),圆心 (cx, cy),方向 dir(0=逆圆,1=顺圆)
void arc_interpolate(float xs, float ys, float xe, float ye,
float cx, float cy, int dir) {
float x = xs - cx; // 相对坐标
float y = ys - cy;
float R2 = x*x + y*y; // 半径平方
float F = x*x + y*y - R2; // 初始偏差
// 计算总步数
int n = (int)(fabs(xe - xs) + fabs(ye - ys));
while (n > 0) {
// 判断象限
int quad = 0;
if (x >= 0 && y >= 0) quad = 1;
else if (x < 0 && y >= 0) quad = 2;
else if (x < 0 && y < 0) quad = 3;
else quad = 4;
// 根据象限和方向决定进给
int step_x = 0, step_y = 0;
if (dir == 0) { // 逆圆
switch (quad) {
case 1: step_x = (F >= 0) ? -1 : 0; step_y = (F >= 0) ? 0 : 1; break;
case 2: step_x = (F >= 0) ? 0 : -1; step_y = (F >= 0) ? 1 : 0; break;
case 3: step_x = (F >= 0) ? 1 : 0; step_y = (F >= 0) ? 0 : -1; break;
case 4: step_x = (F >= 0) ? 0 : 1; step_y = (F >= 0) ? -1 : 0; break;
}
} else { // 顺圆
switch (quad) {
case 1: step_x = (F >= 0) ? 0 : 1; step_y = (F >= 0) ? -1 : 0; break;
case 2: step_x = (F >= 0) ? -1 : 0; step_y = (F >= 0) ? 0 : -1; break;
case 3: step_x = (F >= 0) ? 0 : -1; step_y = (F >= 0) ? 1 : 0; break;
case 4: step_x = (F >= 0) ? 1 : 0; step_y = (F >= 0) ? 0 : 1; break;
}
}
// 更新坐标和偏差
x += step_x;
y += step_y;
F = x*x + y*y - R2;
n--;
// 输出当前点(实际项目中这里发脉冲给驱动器)
printf("Step: (%f, %f)\n", x + cx, y + cy);
}
}
使用建议:这段代码是教学版,实际工程中你需要加上象限切换检测和终点双重校验。我一般在生产代码里还会加一个超时保护——如果步数走完了但偏差还没归零,强制终止并报错。这个习惯帮我避免过好几次刀具撞工件的事故。
4.6 常见问题与避坑指南
- 象限边界处理:当刀具正好在坐标轴上时,属于哪个象限?我建议归到相邻象限中偏差较小的那个,或者单独处理轴上的情况。
- 圆弧过象限:如果圆弧跨越多个象限,终点判别要特别小心。我曾经用总步数法没处理好,结果圆在象限交界处多走了两步,工件直接报废。
- 偏差累积:浮点数运算会有误差,长时间插补后偏差可能漂移。建议每走若干步做一次强制校正。
一个血的教训:有一次我在调试一台三轴雕刻机,圆弧插补在第三象限总是偏一点。查了两天,最后发现是象限判断的条件写成了 x > 0 而不是 x >= 0,导致坐标轴上的点被漏判了。就这一个等号,耽误了两天工期。所以,边界条件一定要覆盖全。