4、数字积分法(DDA):基本原理、直线DDA插补、圆弧DDA插补、溢出脉冲与累加器设计
数字积分法,圈内人一般叫 DDA(Digital Differential Analyzer)。这名字听着挺唬人,说白了就是「用累加代替积分」。我当年刚接触运动控制时,觉得这玩意儿不就是个加法器吗?后来在调试一台三轴雕刻机时,才真正体会到它的精妙之处——计算量小、脉冲均匀、硬件实现简单,至今仍是很多嵌入式运动控制方案的首选。
核心思想:将直线或圆弧的位移增量,分解为 X、Y 轴上的微小步长,通过累加器不断累加,当累加值超过某个阈值(通常为 1)时,就输出一个脉冲,驱动电机走一步。
4.1 基本原理:从积分到累加
先回忆一下高等数学里的积分。一个函数 f(t) 在 [0, T] 上的积分,几何意义是曲线下的面积。DDA 的做法很简单:把时间 T 切成 n 个小段 Δt,每段里用矩形面积近似。
公式长这样:
S = ∫₀ᵀ f(t) dt ≈ Σᵢ₌₁ⁿ f(tᵢ) · Δt
在运动控制里,f(t) 就是速度 v(t),积分结果 S 就是位移。我们让 Δt 等于一个固定的时间间隔(比如 1ms),每次累加 v(t)·Δt,累加器满了就输出一个脉冲。
嗯,这里要注意:累加器的位数决定了插补精度。我见过不少新手工程师,为了省资源用 8 位累加器,结果走出来的直线歪歪扭扭。我个人习惯,至少用 16 位,条件允许直接上 24 位。
4.2 直线 DDA 插补
直线插补是 DDA 最经典的应用场景。假设我们要从起点 (0,0) 走到终点 (Xₑ, Yₑ),总步数为 N。那么 X 轴和 Y 轴每步的增量分别是:
ΔX = Xₑ / N
ΔY = Yₑ / N
实际实现时,我们不用除法,而是用累加器。把 Xₑ 和 Yₑ 分别放到两个累加器里,每个时钟周期加一次,溢出就发脉冲。
具体流程如下:
- 初始化:设累加器 Jᵪ = 0, Jᵧ = 0;累加上限 M = 2ⁿ(n 为累加器位数)。
- 累加:每个周期 Jᵪ += Xₑ, Jᵧ += Yₑ。
- 溢出判断:若 Jᵪ ≥ M,则 X 轴发一个脉冲,Jᵪ -= M;同理处理 Y 轴。
- 结束:累计发出 N 个脉冲后停止。
这里有个关键点:N 取多少? 通常取 N = M,也就是累加器的最大值。这样每个周期累加一次,总共 M 个周期后,X 轴累计走了 Xₑ 步,Y 轴走了 Yₑ 步,刚好到达终点。
避坑指南:我曾经在一个项目中,直接用终点坐标值作为累加值,结果发现走出来的直线末端总是差几个脉冲。后来排查发现,是因为累加器位数和终点坐标不匹配。正确的做法是:将终点坐标左移 n 位(即乘以 2ⁿ),再作为累加值,这样能保证精度。
4.3 圆弧 DDA 插补
圆弧插补比直线复杂一些。为什么?因为圆弧上每一点的速度方向都在变。DDA 处理圆弧的思路很巧妙:用切线方向的速度分量来驱动坐标轴。
对于第一象限的逆圆,圆心在原点,半径为 R,当前点为 (X, Y)。速度方向是切线方向,X 轴分量为 -Y,Y 轴分量为 +X。所以累加公式变成:
Jᵪ += Yₑ(注意这里是 Y 坐标)
Jᵧ += Xₑ(注意这里是 X 坐标)
等等,你是不是觉得符号不对?我刚开始学的时候也迷糊。你想想看,圆弧上某点的切线方向,X 分量确实和 Y 坐标有关。具体推导我就不展开了,记住这个结论:圆弧 DDA 的累加值是交叉的,X 累加器加 Y 坐标,Y 累加器加 X 坐标。
圆弧插补的步骤:
- 初始化:设起点坐标 (X₀, Y₀),累加器 Jᵪ = 0, Jᵧ = 0。
- 累加:每个周期 Jᵪ += Yᵢ, Jᵧ += Xᵢ(Xᵢ, Yᵢ 为当前坐标)。
- 溢出判断:Jᵪ ≥ M 时 X 轴发脉冲,Xᵢ 减 1(逆圆方向);Jᵧ ≥ M 时 Y 轴发脉冲,Yᵢ 加 1。
- 终点判断:到达终点坐标 (Xₑ, Yₑ) 时停止。
不同象限的符号规则不一样,我整理了一个表格:
| 象限 | 圆弧方向 | X 轴脉冲方向 | Y 轴脉冲方向 | 累加值符号 |
|---|---|---|---|---|
| 第一象限 | 逆圆 | -X | +Y | Jᵪ+=Y, Jᵧ+=X |
| 第一象限 | 顺圆 | +X | -Y | Jᵪ-=Y, Jᵧ-=X |
| 第二象限 | 逆圆 | +X | +Y | Jᵪ+=Y, Jᵧ-=X |
| 第二象限 | 顺圆 | -X | -Y | Jᵪ-=Y, Jᵧ+=X |
注意:圆弧插补的终点判断不能只看累加次数,必须实时比较当前坐标与终点坐标。我曾经遇到过因为终点判断逻辑写错,导致圆弧多走了 90° 的情况,差点把刀具撞坏。
4.4 溢出脉冲与累加器设计
溢出脉冲是 DDA 的核心输出机制。累加器本质上是一个 n 位寄存器,每次累加后,如果结果超过了 2ⁿ-1,就会产生一个进位,这个进位就是脉冲信号。
累加器的设计有几个关键参数:
- 位数 n:决定插补精度。n 越大,累加步长越细,轨迹越平滑。16 位是底线,24 位是推荐值。
- 累加频率 f:决定插补速度。f 越高,单位时间内发出的脉冲越多,电机跑得越快。
- 累加值 K:决定脉冲密度。K 越大,每个周期累加的量越多,溢出越快。
溢出脉冲的生成逻辑:
// 伪代码:16位累加器
uint16_t accumulator = 0;
uint16_t increment = 100; // 累加值
uint16_t threshold = 65535; // 2^16 - 1
void clock_tick() {
uint16_t old = accumulator;
accumulator += increment;
if (accumulator < old) { // 发生了溢出
output_pulse();
}
}
你可能会问:为什么用 accumulator < old 来判断溢出?因为无符号整数加法溢出后,结果会回绕,比原来的值小。这是嵌入式开发里常用的技巧,比比较阈值更高效。
实际项目中,我通常会把累加器设计成可配置的:
- 累加器位数:通过寄存器选择 16/24/32 位。
- 累加值:由插补算法实时计算并写入。
- 溢出标志:每个轴独立,支持中断或轮询。
个人经验:在 FPGA 上实现 DDA 时,我习惯用移位寄存器代替乘法器。比如累加值左移 n 位,相当于乘以 2ⁿ,这样能省掉一个乘法器资源。对于资源紧张的芯片,这个优化很实用。
4.5 DDA 插补整体流程
下面这张图展示了 DDA 插补的完整数据流,从参数输入到脉冲输出:
从图上可以看出,DDA 插补是一个闭环迭代过程:每次累加后检查是否溢出,溢出则发脉冲并更新坐标,然后判断是否到达终点。没到就继续累加,到了就停止。
4.6 性能优化与工程实践
DDA 虽然简单,但用不好也会出问题。我总结了几条优化经验:
- 累加器位数的选择:不是越大越好。位数太大,累加频率就得降低,否则脉冲输出太快,电机跟不上。我一般根据最高进给速度来反推累加器位数。
- 多轴同步:直线插补时,X 和 Y 轴的累加器必须同步累加,否则轨迹会偏。我习惯用一个全局时钟驱动所有累加器,保证同时更新。
- 速度控制:通过改变累加频率或累加值来实现加减速。比如加速阶段,逐步增大累加值;减速阶段,逐步减小累加值。
- 误差补偿:圆弧插补存在累积误差,每走一段可以修正一次坐标。我通常在每 90° 弧长后做一次强制修正。
重要提醒:DDA 插补的脉冲输出是均匀的,但实际电机响应可能有滞后。如果发现电机运行时有抖动,先检查脉冲频率是否超过了电机的最大响应频率,而不是怀疑 DDA 算法本身。
好了,数字积分法的核心内容就这些。从直线到圆弧,从累加器到溢出脉冲,每一步都有它的物理意义和工程考量。你想想看,一个简单的加法器,配上合适的逻辑,就能驱动电机走出精确的轨迹——这就是 DDA 的魅力所在。