逐点比较法(一):逐点比较法原理、偏差函数计算、直线插补的推导与流程
各位同学,今天我们来聊聊插补算法里最经典、也最直观的一种——逐点比较法。
说实话,我刚入行那会儿,第一次接触数控系统,看到的就是这个算法。当时带我的老工程师跟我说:“你把这个搞懂了,插补就算入门了。” 后来我做了十几年运动控制,回头再看,这话一点不假。
逐点比较法,说白了就是“走一步,看一步”。每走一步,都算算当前位置跟理想轨迹差了多少,然后决定下一步往哪走。你想想看,这不就跟我们小时候走迷宫一样吗?摸着墙走,走歪了就往回掰一点。
逐点比较法的核心思想
逐点比较法的原理其实就四个字:步步逼近。
具体来说,它把一条连续的轨迹,拆成无数个离散的步长。每走一步,系统都会问自己三个问题:
- 我现在在哪儿? —— 计算当前位置的偏差
- 我偏了没有? —— 判断偏差的正负
- 下一步往哪走? —— 根据偏差方向决定进给方向
这个过程循环往复,直到终点。我习惯把它叫做“三步一循环”,你记住这个口诀,后面就好理解了。
核心要点: 逐点比较法不是一次性算出整条路径,而是每走一步实时决策。这种“边走边算”的特性,让它天生适合硬件实现,响应快、占用资源少。
偏差函数:判断位置的“尺子”
要实现“走一步看一步”,首先得有一把尺子,能量出当前位置跟理想轨迹的偏差。这把尺子,就是偏差函数。
我们先从最简单的直线插补说起。假设我们要从原点 (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 都是正整数(第一象限)。如果你要做多象限插补,需要把坐标取绝对值后再算总步数。我曾经在这个地方吃过亏——没做绝对值处理,结果总步数算出来是负数,电机永远停不下来。
完整流程:从起点到终点
好了,我们把所有环节串起来,看看完整的直线插补流程:
- 初始化:设置起点 (0,0),终点 (Xe, Ye),总步数 N = |Xe| + |Ye|,初始偏差 F = 0
- 偏差判断:如果 F ≥ 0,走 +X;如果 F < 0,走 +Y
- 偏差更新:根据走的轴,用递推公式更新 F
- 步数更新:总步数 N = N - 1
- 终点判断:如果 N = 0,停止;否则回到步骤 2
就这么简单。五步搞定一个插补周期。
下面我画了一张流程图,帮你把整个逻辑串起来:
一个具体的例子
光说不练假把式。我们拿一个实际例子走一遍流程。
假设终点是 (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 一定要用绝对值。我见过有人直接用了带符号的坐标值,结果递推公式完全乱套,轨迹画出来像一团乱麻。
好了,逐点比较法的原理、偏差函数、直线插补的推导和流程,今天就讲到这里。你只要把“三步一循环”和“递推公式”这两个核心点吃透,后面的圆弧插补、多象限扩展,都是在这个基础上做加法。