逐点比较法(一):逐点比较法原理、偏差函数计算、直线插补的推导与流程

各位同学,今天我们来聊聊插补算法里最经典、也最直观的一种——逐点比较法。

说实话,我刚入行那会儿,第一次接触数控系统,看到的就是这个算法。当时带我的老工程师跟我说:“你把这个搞懂了,插补就算入门了。” 后来我做了十几年运动控制,回头再看,这话一点不假。

逐点比较法,说白了就是“走一步,看一步”。每走一步,都算算当前位置跟理想轨迹差了多少,然后决定下一步往哪走。你想想看,这不就跟我们小时候走迷宫一样吗?摸着墙走,走歪了就往回掰一点。

逐点比较法的核心思想

逐点比较法的原理其实就四个字:步步逼近。

具体来说,它把一条连续的轨迹,拆成无数个离散的步长。每走一步,系统都会问自己三个问题:

  1. 我现在在哪儿? —— 计算当前位置的偏差
  2. 我偏了没有? —— 判断偏差的正负
  3. 下一步往哪走? —— 根据偏差方向决定进给方向

这个过程循环往复,直到终点。我习惯把它叫做“三步一循环”,你记住这个口诀,后面就好理解了。

核心要点: 逐点比较法不是一次性算出整条路径,而是每走一步实时决策。这种“边走边算”的特性,让它天生适合硬件实现,响应快、占用资源少。

偏差函数:判断位置的“尺子”

要实现“走一步看一步”,首先得有一把尺子,能量出当前位置跟理想轨迹的偏差。这把尺子,就是偏差函数。

我们先从最简单的直线插补说起。假设我们要从原点 (0,0) 走到终点 (Xe, Ye),画一条直线。

直线上任意一点 (x, y) 满足什么关系?

斜率相等:y / x = Ye / Xe

整理一下:y * Xe - x * Ye = 0

好,那把左边的式子定义成偏差函数 F:

F(x, y) = y * Xe - x * Ye

这个 F 值就是我们的“尺子”:

  • F = 0:点在直线上
  • F > 0:点在直线上方
  • F < 0:点在直线下方

嗯,这里要注意:F 的正负取决于你定义的坐标系和进给方向。我在项目中遇到过有人把符号搞反了,结果电机往反方向跑,差点撞了限位。所以,符号定义一定要跟你的进给方向统一。

个人习惯: 我一般把偏差函数写成 F = Xe * y - Ye * x,这样当点在直线上方时 F 为正,进给方向往 X 正方向走。你只要保持一致,正负无所谓。

直线插补的推导:从偏差到进给

有了偏差函数,下一步就是决定怎么走。逐点比较法每次只走一个轴,要么走 X,要么走 Y。

规则很简单:

  • 如果 F ≥ 0(点在直线上或上方),下一步往 +X 方向走一步
  • 如果 F < 0(点在直线下方),下一步往 +Y 方向走一步

为什么这么定?你想想看,直线是从原点往右上角走的。如果点偏上了,就往右拉回来;如果点偏下了,就往上提一提。这不就是“走歪了就往回掰”吗?

但问题来了——每次走完一步,难道都要重新算一遍 F = y*Xe - x*Ye?那计算量也太大了。要知道,早期的数控系统 CPU 主频才几兆赫兹,哪扛得住这么算?

所以,这里有一个非常巧妙的优化——递推公式。

递推公式:让计算飞起来

我们不需要每次都从头算 F,只需要根据上一步的 F 值,加上一个增量就行。

情况一:上一步走了 +X

假设上一步在 (x, y),走了 +X 后到了 (x+1, y)。新偏差 F' 为:

F' = y * Xe - (x+1) * Ye
   = (y * Xe - x * Ye) - Ye
   = F - Ye

情况二:上一步走了 +Y

假设上一步在 (x, y),走了 +Y 后到了 (x, y+1)。新偏差 F' 为:

F' = (y+1) * Xe - x * Ye
   = (y * Xe - x * Ye) + Xe
   = F + Xe

看到了吗?递推公式只需要一次加减法,连乘法都省了。这在当年可是了不得的优化。

递推公式总结:

  • 走 +X 方向:F_new = F_old - Ye
  • 走 +Y 方向:F_new = F_old + Xe

记住这个,你就能手写一个直线插补器了。

终点判断:什么时候停?

光知道往哪走还不够,还得知道什么时候该停。不然电机就一直跑下去了。

终点判断有两种常用方法:

方法 原理 特点
总步数法 总步数 = |Xe| + |Ye|,每走一步减1,减到0为止 简单可靠,最常用
坐标比较法 实时比较当前坐标与终点坐标 需要额外存储终点坐标,比较次数多

我个人习惯用总步数法。为什么?因为只需要一个计数器,硬件实现极其简单。我曾经在一个 8 位单片机上跑过逐点比较法,总步数法几乎不占资源。

避坑指南: 总步数法要求 Xe 和 Ye 都是正整数(第一象限)。如果你要做多象限插补,需要把坐标取绝对值后再算总步数。我曾经在这个地方吃过亏——没做绝对值处理,结果总步数算出来是负数,电机永远停不下来。

完整流程:从起点到终点

好了,我们把所有环节串起来,看看完整的直线插补流程:

  1. 初始化:设置起点 (0,0),终点 (Xe, Ye),总步数 N = |Xe| + |Ye|,初始偏差 F = 0
  2. 偏差判断:如果 F ≥ 0,走 +X;如果 F < 0,走 +Y
  3. 偏差更新:根据走的轴,用递推公式更新 F
  4. 步数更新:总步数 N = N - 1
  5. 终点判断:如果 N = 0,停止;否则回到步骤 2

就这么简单。五步搞定一个插补周期。

下面我画了一张流程图,帮你把整个逻辑串起来:

逐点比较法直线插补流程图 逐点比较法直线插补流程图 开始 初始化:起点(0,0),终点(Xe,Ye) 总步数 N = |Xe|+|Ye|,F = 0 F ≥ 0 ? 是 走 +X 方向 否 走 +Y 方向 更新偏差: 走+X: F = F - Ye | 走+Y: F = F + Xe N = N - 1 N = 0 ? 否,继续 是 结束 说明 每次循环只走一步 直到总步数 N 减到 0

一个具体的例子

光说不练假把式。我们拿一个实际例子走一遍流程。

假设终点是 (5, 3),即 Xe=5, Ye=3。

总步数 N = 5 + 3 = 8。初始 F = 0。

步数 当前 F 判断 进给方向 更新 F 剩余步数
0 0 F ≥ 0 +X 0 - 3 = -3 7
1 -3 F < 0 +Y -3 + 5 = 2 6
2 2 F ≥ 0 +X 2 - 3 = -1 5
3 -1 F < 0 +Y -1 + 5 = 4 4
4 4 F ≥ 0 +X 4 - 3 = 1 3
5 1 F ≥ 0 +X 1 - 3 = -2 2
6 -2 F < 0 +Y -2 + 5 = 3 1
7 3 F ≥ 0 +X 3 - 3 = 0 0

走完 8 步,F 回到 0,正好到达终点 (5, 3)。完美。

你发现没有?整个过程中,X 方向走了 5 步,Y 方向走了 3 步,跟终点坐标完全吻合。这就是逐点比较法的精妙之处——它天然保证了每个轴走的步数跟目标值一致。

一个小技巧: 如果你在调试时发现轨迹明显偏离直线,先检查递推公式的符号。我遇到过好几次,都是因为把 F - Ye 写成了 F + Ye,结果轨迹往反方向偏了。

第一象限的局限与扩展思路

上面讲的都是第一象限的直线插补。实际工程中,四个象限都要支持。

怎么扩展?其实很简单:

  • 象限判断:根据终点坐标的符号,确定当前在哪个象限
  • 进给方向映射:不同象限下,F ≥ 0 和 F < 0 对应的进给轴方向不同
  • 坐标取绝对值:偏差计算时用绝对值,进给方向根据象限决定

举个例子,第二象限的终点 (-5, 3):

  • F ≥ 0 时走 -X(往左)
  • F < 0 时走 +Y(往上)

总步数还是 |Xe| + |Ye| = 5 + 3 = 8。你看,核心逻辑没变,只是进给方向根据象限做了映射。

避坑指南: 多象限插补时,偏差递推公式里的 Xe 和 Ye 一定要用绝对值。我见过有人直接用了带符号的坐标值,结果递推公式完全乱套,轨迹画出来像一团乱麻。

好了,逐点比较法的原理、偏差函数、直线插补的推导和流程,今天就讲到这里。你只要把“三步一循环”和“递推公式”这两个核心点吃透,后面的圆弧插补、多象限扩展,都是在这个基础上做加法。