4. 数字积分法(DDA):基本原理、直线DDA插补、圆弧DDA插补、溢出与累加器处理
数字积分法,圈里人习惯叫它 DDA(Digital Differential Analyzer)。
这名字听起来挺唬人,其实说白了就是——用累加代替乘法,用时间换精度。
我刚入行那会儿,厂里的老系统用的就是 DDA。那时候 CPU 慢得可怜,乘除法能省则省。DDA 正好,只需要加法器和寄存器,硬件成本低,速度还快。到现在,很多专用运动控制芯片里,DDA 依然是核心算法之一。
核心思想: 将刀具的移动轨迹,看作一个连续积分过程。用一系列小步长的累加,逼近理想轨迹。每走一步,累加器溢出一回,就发一个脉冲。
4.1 基本原理:从微分到累加
先回忆一下微积分。一个函数 y = f(t),它在 [0, t] 上的积分,就是曲线下的面积。
DDA 的做法很粗暴:把时间 t 切成无数小段 Δt,每段里用矩形面积 y × Δt 去近似。只要 Δt 足够小,误差就能接受。
放到插补里,就是:
- 被积函数:各坐标轴的速度分量(
Vx,Vy) - 自变量:时间
t - 积分结果:各轴的位移(
Δx,Δy)
数学表达式长这样:
X = ∫ Vx dt ≈ Σ Vx × Δt
Y = ∫ Vy dt ≈ Σ Vy × Δt
每次累加,其实就是算一小步。累加器满了(溢出),就说明该走一步了。
我的经验: DDA 的精度取决于累加器的位数和 Δt 的大小。位数越多,步长越细,轨迹越平滑。但位数太多,溢出频率降低,速度会变慢。一般 16 位或 24 位比较常用,得在精度和速度之间找个平衡点。
4.2 直线 DDA 插补
直线插补是 DDA 最经典的应用。我当年调试第一台雕刻机,用的就是它。
4.2.1 原理与公式
假设要从起点 (0, 0) 走到终点 (Xe, Ye)。总步数 N 通常取 2^m(m 是累加器位数)。
各轴的速度分量就是:
Vx = Xe / N
Vy = Ye / N
每次累加:
Σx += Vx
Σy += Vy
当 Σx ≥ 1 时,X 轴走一步,Σx -= 1。Y 轴同理。
4.2.2 具体步骤
- 初始化:累加器
Σx = 0,Σy = 0。总步数N = 2^m。 - 计算增量:
Δx = Xe / N,Δy = Ye / N。实际用整数表示,比如左移 m 位。 - 循环累加:每来一个时钟脉冲,执行
Σx += Δx,Σy += Δy。 - 溢出判断:如果
Σx ≥ 1,X 轴发一个脉冲,Σx -= 1。Y 轴同理。 - 结束:累加 N 次后,插补完成。
注意: 如果 Xe 或 Ye 为 0,对应的累加器永远不会溢出。这时候要特殊处理,直接让该轴不动作。我曾经见过有人忘了这个,结果电机一直嗡嗡响,就是不走。
4.2.3 代码示例
// 直线 DDA 插补(16位累加器)
void line_dda(int Xe, int Ye) {
int acc_x = 0, acc_y = 0; // 累加器
int step_x = Xe; // 左移16位后的增量
int step_y = Ye;
int total_steps = 65536; // 2^16
for (int i = 0; i < total_steps; i++) {
acc_x += step_x;
acc_y += step_y;
if (acc_x >= 65536) { // 溢出
output_pulse(X_AXIS);
acc_x -= 65536;
}
if (acc_y >= 65536) {
output_pulse(Y_AXIS);
acc_y -= 65536;
}
}
}
4.3 圆弧 DDA 插补
圆弧比直线麻烦一点。因为速度方向一直在变。
4.3.1 原理与公式
圆弧插补的核心是:动点坐标的变化,满足圆的参数方程。
对于第一象限逆圆,从 (R, 0) 到 (0, R):
Vx = -Y
Vy = X
注意这里的 X 和 Y 是动点的当前坐标,不是终点坐标。所以每次累加后,坐标值会变,增量也跟着变。
4.3.2 具体步骤
- 初始化:
X = R,Y = 0。累加器Σx = 0,Σy = 0。 - 计算增量:
Δx = -Y,Δy = X。 - 累加:
Σx += Δx,Σy += Δy。 - 溢出判断:同直线,溢出就走一步。
- 更新坐标:如果 X 轴走了,
X -= 1。如果 Y 轴走了,Y += 1。 - 结束:走到终点
(0, R)为止。
避坑指南: 圆弧 DDA 的误差会累积。因为每次更新坐标后,增量才变化,存在一个步长的滞后。我曾经在加工一个精密圆环时,发现接刀处有台阶,就是这个问题。后来加了误差补偿,才搞定。
4.3.3 象限处理
不同象限,速度分量的符号不一样。我习惯用查表法:
| 象限 | 顺圆 Vx | 顺圆 Vy | 逆圆 Vx | 逆圆 Vy |
|---|---|---|---|---|
| 一 | +Y | -X | -Y | +X |
| 二 | -X | -Y | +X | +Y |
| 三 | -Y | +X | +Y | -X |
| 四 | +X | +Y | -X | -Y |
4.4 溢出与累加器处理
溢出处理是 DDA 的精髓。处理不好,轨迹会跑偏。
4.4.1 溢出原理
累加器就像一个水桶。每次往里倒水(加增量),满了就溢出来(发脉冲)。
桶的容量就是 2^m。溢出的频率,决定了电机的速度。
4.4.2 常见问题与对策
- 同时溢出:X 和 Y 同时溢出时,先处理哪个?我建议先处理 X,再处理 Y。顺序固定,避免抖动。
- 多次溢出:一次累加,可能溢出多次(增量很大时)。要循环判断,直到累加器小于容量。
- 负增量:反向运动时,增量是负的。累加器会减少,不会溢出。这时候要判断下溢(小于 0),发反向脉冲。
重要: 累加器溢出后,一定要减去容量值,而不是清零。否则会丢失余数,造成累积误差。我见过有人直接清零,结果加工出来的圆变成了多边形。
4.4.3 代码示例(带溢出处理)
// 带溢出处理的累加
void process_accumulator(int *acc, int step, int *pulse_count) {
*acc += step;
// 正向溢出
while (*acc >= 65536) {
(*pulse_count)++;
*acc -= 65536;
}
// 负向下溢
while (*acc < 0) {
(*pulse_count)--;
*acc += 65536;
}
}
4.5 知识体系总览
下面这张图,把 DDA 的核心脉络串起来了。我每次培训新人,都会先让他们看这个。
4.6 小结与个人体会
DDA 是个老算法,但老不代表过时。它的思想——用累加代替乘法,用时间换精度——在很多嵌入式场景里依然好用。
我这些年调试过的系统,从三轴雕刻到五轴联动,底层或多或少都有 DDA 的影子。它简单、可靠、容易硬件化。
如果你刚开始学插补,我建议从 DDA 入手。把累加器和溢出的逻辑吃透,后面学逐点比较法、时间分割法,都会轻松很多。
最后说一句: 别小看这个“老古董”。很多新算法,都是在 DDA 的基础上改进的。基础打牢了,才能走得更远。