起因

三线合拢了:几何侧的逐点比较法在详解篇讲过来历,执行侧的脉冲和电机在电机篇备好了合同,速度侧的变频入口在放电篇通了——这篇写插补本体,把段表变成脉冲。插补这个词本身是什么、为什么非有它不可,前置的插补基础一篇已从零垫平(问题、三笔账、记分牌直觉),这里不再重复铺概念,直接进算法。

插补是个带着年代味的问题:数字计算机还没便宜下来的时候,怎么让一台机床走出不是直角折线的轨迹?五十年代的硬接线数控柜给出的答案是把轨迹问题化成"每步走一个脉冲当量、每步判一次方向"——逐点比较法。七十年代国产线切割的数控柜用分立元件实现了它,3B 纸带上的五个字段(X、Y、J、G、Z)就是这套硬件逻辑的寄存器投影:J 是计数器、G 是判终轴、Z 是象限查表的索引。同一时期,IBM 的 Bresenham 为绘图仪独立推导出同一套数学——两个世界互不知晓,得到同一条偏差递推。HF 时代用 x86 汇编把同一算法第三次写进软件;我们用 Rust 加 ISR 加 FPGA 第四次重写,每次重写,时代给的条件都在变:硬件时代的门电路按个数算,今天的乘法器在 GHz 面前近乎免费——这次的改法就藏在这个变化里,第二节见。

插补实战全景:三线合拢、偏差递推、逐点比较、节拍分离、寄存器合同

插画:插补实战的全景图解,五个区块正好是这篇的五段路。①是起因的三线汇合——几何侧逐点比较法、执行侧脉冲合同、速度侧变频入口三股线拧成插补本体,左边那截阶梯轨迹贴着理想斜线的样子,就是"每步走一个脉冲当量、每步判一次方向"的直观像。②是第二节的直线账——偏差函数 F = Xe·Y−Ye·X、两个前向候选挑 |F| 小的那条带符号规则(四张象限表塌缩成一行就是它)、dF 递推只剩加减、总步数 |ΔX|+|ΔY| 对账。③是第三节的圆弧账——F = x²+y²−R²、切向定前向候选、sgn 过象限自然翻符号、F 每拍重算换断电续切的幂等恢复。④是第四节的节拍分离——0xD0 一批算 256 个 tick 的步进位图进模式环,FPGA 拿变频频率当播放钟逐 tick 驱 PUL/DIR,83kHz 的重活全在硬件侧,变速只改频率不动几何。⑤是第五到七节的收尾账——段流转与短路回退(换目标不问方向)、四轴 DDA 均匀分配、给硬件篇的七项寄存器表合同。

一、两类插补与我们的选择

先把插补的两大门派认全。脉冲增量式:每输出一个脉冲,工作台走一个脉冲当量,插补器逐个脉冲地决定"哪个轴、朝哪边"——逐点比较法、数字积分法(DDA)、最小偏差法全属此门。数据采样式:插补器每毫秒级算一次"下一时刻各轴应该到哪"的位置设定值,由闭环伺服去跟——现代伺服 CNC 的主流。

我们没得选也不需要选:开环步进加脉冲方向接口(电机篇),只能吃脉冲增量式——接口决定算法。这不是将就:脉冲增量式的每一步都由几何递推决定、位置天然量化到脉冲当量,和开环步进"一个脉冲一个位置"的世界观严丝合缝;伺服闭环反而是采样式的地盘。

二、直线:偏差函数与增量递推

教科书讲直线插补,先在四个象限列四张进给方向表(L1–L4),因为逐点比较法的正统形态靠 F 的符号查表定方向——硬件时代省一次比较就是省几个门。软件时代不需要省这一下,四张表可以塌缩成一条带符号的规则。

偏差函数取 F = Xe·Y − Ye·X(Xe、Ye 是全段增量,X、Y 是当前点,都相对段起点、单位 µm):F = 0 在线上,F > 0 在线一方,F < 0 在另一方。每步朝终点方向走一个轴,两个前向候选里挑让 |F| 变小的那个——就这一条,象限没了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pub struct LineGen {
    rem_x: i64, rem_y: i64,    // 剩余量(µm,带符号,双轴归零即到)
    f: i64,                     // 偏差 F = Xe·y − Ye·x
    tot_x: i64, tot_y: i64,     // 全段增量(dF 递推用)
}

impl LineGen {
    /// 一次 tick 的步进判定:bit0 X+、bit1 X−、bit2 Y+、bit3 Y−
    pub fn tick(&mut self) -> u8 {
        if self.rem_x == 0 && self.rem_y == 0 { return 0; }
        let (sx, sy) = (self.tot_x.signum(), self.tot_y.signum());
        let dfx = -self.tot_y * sx;        // X 前进一步后 F 的增量
        let dfy =  self.tot_x * sy;        // Y 前进一步后 F 的增量
        if self.rem_x != 0 && (self.rem_y == 0
            || (self.f + dfx).abs() <= (self.f + dfy).abs()) {
            self.f += dfx; self.rem_x -= sx;
            if sx > 0 { 1 } else { 2 }
        } else {
            self.f += dfy; self.rem_y -= sy;
            if sy > 0 { 4 } else { 8 }
        }
    }
}

几个性质一眼可验:dF 递推只有加减(dfx、dfy 在段内是常数);终点判别是双轴剩余量归零,总步数恰为 |ΔX|+|ΔY|——这个数可以和解析器那边对账,多一步少一步都说明有 bug;轨迹对理想直线的偏差不超过一个脉冲当量,1µm 当量下几何误差小于机械误差一个数量级,驱动不是精度的短板(电机篇的结论在这里兑现)。教科书把一次循环分成四拍——偏差判别、进给、偏差计算、终点判别——tick() 里三行就是这四拍,判别和计算在"比较两个候选"里合了。

三、圆弧:F = x²+y²−R²,两个前向候选挑小的

圆弧的偏差函数换成到圆心的距离差:F = x²+y²−R²(x、y 是当前点相对圆心)。前向候选由切向的轴向分量给出——逆圆(CCW)时切向是 (−y, x),所以 X 的前向步是 −sgn(y)、Y 的前向步是 +sgn(x);顺圆反号。两个候选里还是挑 |F| 小的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pub fn tick(&mut self) -> u8 {           // ArcGen 字段:x,y 当前点相对圆心;
    if self.rem_x == 0 && self.rem_y == 0 { return 0; }   // ex,ey 终点,ccw,r2
    let s: i64 = if self.ccw { 1 } else { -1 };
    let dx = if self.y != 0 { -s * self.y.signum() } else { 0 };
    let dy = if self.x != 0 {  s * self.x.signum() } else { 0 };
    let f = self.x * self.x + self.y * self.y - self.r2;  // 直接重算,幂等
    let fx = f + 2 * self.x * dx + 1;     // X 步后的 F(走一步 r² 变 2x·dx+1)
    let fy = f + 2 * self.y * dy + 1;     // Y 步后的 F
    if dx != 0 && (dy == 0 || fx.abs() <= fy.abs()) {
        self.x += dx; self.rem_x -= 1;
        if dx > 0 { 1 } else { 2 }
    } else {
        self.y += dy; self.rem_y -= 1;
        if dy > 0 { 4 } else { 8 }
    }
}

三条要说的。过象限不用特判:教科书逆圆顺圆乘四象限列十六张表,是因为方向靠查表;这里 sgn(x)、sgn(y) 过轴时自然翻符号,表就没了。收敛性有保票:两个前向候选的角进度分别是 s·|y| 和 s·|x|(叉积一步就能算),都非负——扫角单调,算法只会绕着走不会兜圈,双轴剩余量(初始化为 |ex−x|、|ey−y|)必然归零。F 每拍直接重算:两次乘法在现代 CPU 上近乎免费,换来的是 tick() 不依赖历史状态——断电续切时快照只需要段号加绝对位置,F 从几何重算(参数篇 500ms 快照的"幂等恢复",在这里兑现成代码)。

3B 的 J 判终是单轴计数(G 方向那只轴),我们两个轴都数,更严;J 的校验职责留在解析器导入时(详解篇的口径),运行时各干各的。容量账:行程 ±2m 折 ±2×10⁶ µm,x² 上限 4×10¹²,加上 R² 的加减,离 i64 的 9.2×10¹⁸ 还有九个数量级余量——放心用。

四、节拍与批量:几何与节奏分离

tick() 只回答"下一步走哪",没回答"什么时候走"。节拍是速度的事:步频 = 进给速度 ÷ 脉冲当量,1µm 当量下 5m/min 空走就是 83kHz(电机篇算过)。83kHz 的中断每秒打断 CPU 八万次,每次进出栈加分发就是一两微秒,CPU 花掉一成还带抖动——软件节拍伺候不了这个频率,这是脉冲必须 FPGA 发的根本原因。

但不能让 FPGA 也算几何——几何是递推状态机,硬件实现是七十年代的老路(要走好多门)。解法是把模式和节拍拆开

  • 模式(几何):0xD0 的 ISR 一次算一批——比如 256 个 tick 的步进位图(一个 u8 一个 tick,四轴正负各一位),写进卡上的模式环
  • 节拍(变频):FPGA 拿当前频率当播放时钟,逐 tick 从环里弹出字节、驱 PUL/DIR;水位低于一半,卡发"补货"中断,0xD0 再算一批

批量的账:256 步每步十几个周期(递推全是加减和比较),一批几微秒;83kHz 下 256 tick 撑 3.1ms,补货周期毫秒级——CPU 占用千分之一量级。更值钱的是解耦本身:0xE0 的变频修正只改播放频率,不动模式——放电反馈调快调慢,几何纹丝不动;操作工拨倍率同理。切到一半想快想慢是这台机器的常态,轨迹不能因为变速重算,这个分离是插补器抗压的根。

五、段的流转:从段表到脉冲,和回退

段的供给链:Core1 协作层把段表按序喂进 SPSC 环,0xD0 消费。段边界的处置在这一拍完成:下一段是 Comp/Taper 记录就转给补偿层挂账(解析器定的"只记录不展开"在这里兑现),Stop/OptStop 就通报 planner 暂停,规准组号变化的段边界通知 0xE0 转写放电模块寄存器(放电篇的段边界自动换组)。

短路回退是插补器必须直着腰接下的需求(放电篇:短路持续几十 ms 沿轨迹反向退)。位置基的生成让回退退化了:把当前段的目标从段终点换成段起点,重新初始化 rem_x/rem_y——生成器不问方向,只问"从现在这点到目标怎么走"。回退用固定慢速(播放频率压到切割速度的零头),解除后目标再换回终点。往返一回事,位置归零可验。

还有个白捡的遗产:插补生成器是纯函数(进状态出脉冲位),跟解析器一样能在开发机上 cargo test——全系统第二个 host 可测模块。测试断言三件套:终点精确命中、总步数对账(直线 |ΔX|+|ΔY|、圆弧双轴剩余量)、步进序列重构出的轨迹包络在 1 个脉冲当量内贴住理论线。golden 文件攒起来,改递推先跑测试再上机。

六、四轴与锥度:主从联动

XYUV 四轴直线不能拿偏差法硬扩(它生在二维)。用 DDA 的思路:每轴一个累加器,同一节拍钟下把各轴的步数均匀摊到总 tick 里——acc_u += ΔU; 若 acc_u 超过总步数就出一步并回减。两轴情形这个分配和偏差法等价(同一条阶梯),四轴直接扩展,锥度直线的 UV 随动就是 XY 的从动分配器。圆弧带锥度时上轨迹也是圆弧(同圆心异半径),ArcGen 各跑一份、共用节拍。上下异形那类真三维的活,留给锥形切割篇——插补器这层只需保证:任意 tick,四根轴谁出步都由同一个几何决定。

七、卡的寄存器表:给硬件篇的合同

运动控制卡是自研的,寄存器表就是我们自己定的规格——软件先立合同,硬件篇照着实现,跟 PCIe 篇"设备框架不写死卡"是同一套哲学的两端。先把这份合同的边界注明:它只管脉冲卡这一种形态(自研 GD32F4+FPGA,PUL/DIR 脉冲输出一族)。更高端的 PCIe EtherCAT 主站卡(一卡几十上百轴)、机柜外的独立控制器,吐的是总线报文不是脉冲,接不进这张表,但接得进卡适配层——对上暴露同一组运动命令,对下按卡的形态翻译。系统不会只支持一种卡,这张表是第一份合同,不是唯一一份;EtherCAT 那份的报文侧,硬件篇展开。

偏移 名称 方向 含义
0x00 ID/VER R 卡标识与版本(对齐设备框架的枚举注册)
0x08 CTRL W 启动/停止/急停/回退模式
0x10 PACE W 播放频率 Hz(变频修正经 0xD0 落到这里,0xE0 不直接摸卡)
0x18 PAT_WPTR W 模式环写指针(批量步进位图的落点)
0x20 PAT_CNT R 环内剩余 tick 数(补货水位)
0x28 STAT R 段完成/断丝输入/限位输入/故障位
0x30+ POS_X/Y/U/V R 卡内脉冲计数器(命令侧位置,回读对账用)

POS 计数器值得一句:开环系统的命令侧也能对账——卡数自己发出去的脉冲,主机比对命令意图与发出数,命令通路上的丢失当场现形(机床侧的丢步是另一回事,靠电机篇的回零流程兜)。断丝、限位输入落在卡上,直接进 0xF0 安全链——硬件信号走硬件的路,不绕软件。

总结

实战清单:门派选型(脉冲增量式配开环步进,接口决定算法)+ 三个时代四次重写的来历;直线偏差 F = Xe·Y−Ye·X、双前向候选挑 |F| 小、四张象限表塌缩成带符号一行、终点双轴剩余量+总步数对账;圆弧 F = x²+y²−R²、切向定前向候选、十六张表消融、扫角单调保收敛、F 每拍重算换幂等恢复(断电续切只需段号+位置)、i64 容量账;模式与节拍分离(0xD0 批量 256 tick 进模式环、FPGA 按变频频率播放、CPU 千分之一、变速不动几何);段流转(SPSC 供段、边界处置 Comp/Taper/规准换组)+ 位置基生成让回退=换目标 + host 可测第二模块(终点/步数/包络三断言);四轴 DDA 均匀分配与锥度随动框架;自研卡寄存器表七项当硬件篇的合同。跑通标志:矩形件每段脉冲账 |ΔX|+|ΔY| 核对全过、圆弧双轴剩余量归零且终点命中;切到一半拨倍率 80%↔120%,轨迹无缝不变形;断电重启续切,回位精度在脉冲当量级;强制短路触发回退,解除后往返位置归零;卡上 POS 计数器与命令意图全程一致;UV 联动比例在仿真屏可验。

下一篇顺藤摸瓜:四轴联动长出来的第一颗瓜是锥形切割——G51/G52 的角度怎么变成上下导轮的轨迹差,锥度件的工艺口径和 U/V 轴的边界条件,都在那篇展开。

参考链接