逐点比较法(一):直线插补原理、偏差计算公式、进给方向判别

各位工程师朋友,今天我们来聊聊多轴联动里最基础、也最经典的一种插补方法——逐点比较法。先讲直线插补。

说实话,我刚入行那会儿,第一次看到“插补”这个词,还以为是某种补丁程序。后来才明白,插补就是让运动轨迹“看起来”是连续的。你想想看,步进电机或者伺服电机,本质上只能走离散的步距,但我们要画一条平滑的直线,怎么办?

逐点比较法,说白了就是“走一步,看一步”。每走一步,算一下当前位置偏离理想直线有多远,然后决定下一步往哪个方向走。像个盲人探路,摸一下,走一步,再摸一下。

一、直线插补的基本原理

假设我们要从原点 (0,0) 走到终点 (Xe, Ye),画一条直线。理想轨迹是一条斜线。但实际走的是一系列阶梯状的折线。

逐点比较法的核心思想是:每走一步,比较当前点与理想直线的位置关系,然后决定下一步的进给方向

我在项目中遇到过一台老式的数控铣床,用的就是这种算法。当时调试时发现,如果进给速度太快,轮廓误差会明显变大。后来我加了一个速度前瞻处理,才把精度提上去。嗯,这里先不展开,后面讲速度控制时会细说。

二、偏差计算公式

偏差计算是整个方法的核心。怎么判断当前点是在直线上方、下方,还是正好在线上?

我们设当前点为 (Xi, Yi),终点为 (Xe, Ye)。定义偏差函数:

F = Xi * Ye - Yi * Xe

这个公式怎么来的?其实就是向量叉积的几何意义。你想想看,向量 (Xi, Yi) 和 (Xe, Ye) 的叉积,等于它们围成的平行四边形面积。如果 F = 0,说明当前点在直线上;F > 0,点在直线上方;F < 0,点在直线下方。

我个人习惯把这个公式记成“交叉相乘再相减”。简单好记,不容易搞混。

偏差判别的三种情况:

  • F = 0:当前点在直线上
  • F > 0:当前点在直线上方(或左侧)
  • F < 0:当前点在直线下方(或右侧)

这里要注意,偏差的正负号取决于我们定义的坐标系和进给方向。不同象限的判别规则会略有不同,后面我会专门讲象限处理。

三、进给方向判别

有了偏差值,下一步就好办了。逐点比较法的进给规则很简单:

  • 当 F ≥ 0 时:沿 X 轴正方向走一步(+X)
  • 当 F < 0 时:沿 Y 轴正方向走一步(+Y)

为什么这样定?因为对于第一象限的直线,如果当前点在直线上方(F > 0),说明 Y 方向走多了,下一步应该往 X 方向拉回来。如果点在直线下方(F < 0),说明 X 方向走多了,下一步应该往 Y 方向补上去。

我曾经犯过一个低级错误:把进给方向搞反了。结果电机直接往反方向跑,差点撞到限位开关。从那以后,我每次写插补程序,都会先在仿真环境里跑一遍,确认方向正确再上机。

小技巧:调试时可以在每个进给步骤后打印当前偏差值,观察偏差是否在零附近来回振荡。如果偏差一直为正或一直为负,说明进给方向可能搞反了。

四、递推计算——避免重复乘法

每次计算偏差都用乘法,效率太低了。工业现场讲究实时性,CPU 资源宝贵。所以我们要用递推公式。

假设当前点 (Xi, Yi) 的偏差为 Fi,下一步的偏差 Fi+1 可以通过简单的加减法得到:

// 沿 X 方向走一步后:
// Xi+1 = Xi + 1, Yi+1 = Yi
// 新偏差:
Fi+1 = (Xi + 1) * Ye - Yi * Xe
     = Xi * Ye + Ye - Yi * Xe
     = Fi + Ye

// 沿 Y 方向走一步后:
// Xi+1 = Xi, Yi+1 = Yi + 1
// 新偏差:
Fi+1 = Xi * Ye - (Yi + 1) * Xe
     = Xi * Ye - Yi * Xe - Xe
     = Fi - Xe

你看,只需要加一个 Ye 或者减一个 Xe 就行了。这个优化在几十年前的单片机上特别重要。我记得最早用 8051 做插补时,乘法指令要几十个周期,而加法只要几个周期。用递推公式,性能直接翻倍。

注意:递推公式只适用于步长为 1 的情况。如果你的系统步长不是 1,记得把公式里的 1 换成实际步长。另外,终点判断也要小心,别走过头了。

五、完整流程与流程图

整个逐点比较法直线插补的流程,可以用下面这张图来概括:

逐点比较法直线插补流程图 开始 初始化:Xi=0, Yi=0 终点(Xe, Ye),F=0 是否到达终点? (Xi==Xe 且 Yi==Ye) 结束 偏差 F ≥ 0? X方向进给一步 Y方向进给一步 更新偏差:F = F ± Ye/Xe 更新坐标:Xi, Yi

这张图把整个流程串起来了。你仔细看,核心就是那个菱形判断框——偏差 F 的正负,决定了下一步往哪个方向走。走完之后更新偏差,再判断是否到终点。如此循环,直到终点。

六、代码实现示例

下面是一个简单的 C 语言实现,只针对第一象限:

void line_interpolate(int Xe, int Ye) {
    int Xi = 0, Yi = 0;
    int F = 0;  // 初始偏差
    
    while (1) {
        // 判断是否到达终点
        if (Xi == Xe && Yi == Ye) {
            break;
        }
        
        // 根据偏差决定进给方向
        if (F >= 0) {
            // X方向进给
            Xi++;
            F = F - Ye;  // 递推公式
        } else {
            // Y方向进给
            Yi++;
            F = F + Xe;  // 递推公式
        }
        
        // 输出当前步进指令(实际项目中发送给驱动器)
        // step_x(Xi);
        // step_y(Yi);
    }
}

这段代码很简洁,但实际工程中要考虑更多细节。比如终点判断,如果 Xe 或 Ye 是负数怎么办?那就涉及到象限处理了。这部分内容,我们下一节再详细展开。

调试建议:在 while 循环里加一个计数器,防止死循环。我曾经遇到过因为终点判断条件写错,程序一直跑不停的情况。加个最大步数限制,至少不会让电机一直转下去。

七、总结

逐点比较法直线插补,核心就三点:

  1. 偏差公式:F = Xi * Ye - Yi * Xe,判断点在直线的哪一侧
  2. 进给规则:F ≥ 0 走 X,F < 0 走 Y(第一象限)
  3. 递推优化:用加减法代替乘法,提高实时性

这个方法虽然古老,但思想很巧妙。它不需要复杂的三角函数计算,只用简单的加减法和比较就能实现直线插补。在资源受限的嵌入式系统里,至今仍有广泛应用。

好了,这一节就到这里。下一节我们讲逐点比较法的圆弧插补,以及如何处理不同象限的情况。到时候我会分享一个我在调试圆弧插补时遇到的“诡异”问题,保证让你印象深刻。


公众号:蓝海资料掘金营,微信deep3321