一、DDA直线插补原理:数字积分法的前世今生

各位同学,今天我们来聊聊直线插补里一个非常经典的方法——数字积分法(DDA)

说实话,我刚入行那会儿,第一次看到DDA这个缩写,还以为是某个芯片型号。后来才知道,这玩意儿在数控系统里已经用了好几十年了。它的核心思想其实特别朴素:把直线运动看成是坐标轴上的连续积分过程

你想想看,一条直线从起点到终点,x轴和y轴各自要走多少步?如果能让两个轴按照某种比例同时走,那轨迹不就是直线了吗?DDA干的就是这件事。

1.1 数学推导:从微分方程到累加器

我们先从数学上捋一捋。假设一条直线从点P₀(x₀, y₀)到点Pₑ(xₑ, yₑ),那么它在x方向和y方向上的增量分别是:

Δx = xₑ - x₀
Δy = yₑ - y₀

如果我们把整个运动过程分成n个小步,每一步的增量就是:

dx = Δx / n
dy = Δy / n

嗯,这里要注意:n不是随便取的。n必须足够大,才能保证每一步的增量小于一个脉冲当量。说白了,就是让每个轴每次最多走一步。

我个人习惯把n取成2的幂次,比如2^m。为什么?因为二进制移位方便啊,硬件实现起来特别快。

那么,每一步的积分公式就是:

xᵢ₊₁ = xᵢ + dx
yᵢ₊₁ = yᵢ + dy

但问题是,dx和dy通常不是整数。比如Δx=10,Δy=7,n=16,那dx=0.625,dy=0.4375。计算机怎么处理小数?

答案就是——累加器

1.2 累加器与余数寄存器:小数的处理艺术

这里我要分享一个我在项目中踩过的坑。有一次做三轴雕刻机,我直接用浮点数做累加,结果跑着跑着轨迹就偏了。后来才发现,浮点数的精度问题在长时间运行时会累积误差。

DDA的做法很巧妙:用整数累加器来处理小数

具体来说,我们把dx和dy放大2^m倍,变成整数:

Kx = Δx * 2^m / n = Δx   (因为n=2^m)
Ky = Δy * 2^m / n = Δy

然后设置两个累加器:

累加器x += Kx
累加器y += Ky

每次累加后,检查累加器是否溢出(即超过2^m)。如果溢出,就输出一个脉冲,让对应轴走一步,同时把溢出的部分减掉(相当于保留余数)。

你看,这不就是把小数积分变成了整数累加吗?

核心要点:

  • 累加器宽度 = m位(通常取16位或32位)
  • 每次累加值 = 对应轴的增量(Δx或Δy)
  • 溢出条件 = 累加器值 ≥ 2^m
  • 余数 = 累加器值 - 2^m(保留在寄存器中)

1.3 终点判断逻辑:什么时候该停?

这个问题看似简单,但做不好会出大问题。我曾经见过一个设备,因为终点判断逻辑写错了,电机一直转不停,最后撞了限位开关才停下来。

DDA的终点判断通常有两种方式:

方法 原理 优缺点
总步数计数法 预先计算总步数,每走一步减1 简单可靠,但需要预计算
坐标比较法 实时比较当前位置与终点坐标 灵活,但需要额外比较逻辑

我个人更推荐总步数计数法。为什么?因为DDA的累加过程本身就有规律可循——两个轴的溢出脉冲总数,就是直线在x和y方向上的总步数。

举个例子:Δx=10,Δy=7,那么x轴要走10步,y轴要走7步,总共17步。我们设置一个计数器,初始值为17,每输出一个脉冲就减1,减到0就停止。

避坑指南:

我曾经遇到过一个问题:当Δx或Δy为0时(即直线平行于坐标轴),累加器永远不会溢出。这时候需要特殊处理——直接让另一个轴走完所有步数即可。

1.4 溢出脉冲与进给:电机是怎么动起来的?

好了,现在累加器溢出了,我们得到了一个脉冲。这个脉冲怎么变成电机的运动?

这里要区分两个概念:溢出脉冲进给脉冲

  • 溢出脉冲:累加器溢出的瞬间产生的信号,表示「该走一步了」
  • 进给脉冲:实际发送给驱动器的脉冲,控制电机转动

在大多数系统中,溢出脉冲直接作为进给脉冲使用。但有些高端系统会做脉冲整形——比如把脉冲宽度调整到驱动器要求的范围,或者做加减速处理。

我记得有一次调试一个步进电机系统,发现电机在低速时抖动得厉害。后来查了半天,发现是脉冲宽度太窄,驱动器没识别到。把脉冲宽度从2μs改成5μs,问题就解决了。

1.5 DDA直线插补的完整流程

说了这么多,我们来画个流程图,把整个逻辑串起来。

DDA直线插补流程图 开始插补 初始化:设置累加器=0,总步数=Δx+Δy 加载Kx=Δx,Ky=Δy 累加:累加器x += Kx 累加器y += Ky 累加器溢出? 输出脉冲,走一步 更新余数寄存器 总步数=0? 插补结束

这个流程图看起来复杂,其实核心就三个步骤:累加 → 判断溢出 → 判断终点。循环往复,直到走完所有步数。

1.6 一个完整的例子

光说不练假把式。我们拿一个具体例子跑一遍:

假设Δx=5,Δy=3,累加器宽度m=4(即2^4=16)。

初始化:
  累加器x = 0,累加器y = 0
  总步数 = 5 + 3 = 8
  Kx = 5,Ky = 3

第1步:
  累加器x = 0 + 5 = 5(无溢出)
  累加器y = 0 + 3 = 3(无溢出)
  总步数 = 8

第2步:
  累加器x = 5 + 5 = 10(无溢出)
  累加器y = 3 + 3 = 6(无溢出)
  总步数 = 8

第3步:
  累加器x = 10 + 5 = 15(无溢出)
  累加器y = 6 + 3 = 9(无溢出)
  总步数 = 8

第4步:
  累加器x = 15 + 5 = 20 → 溢出!输出x脉冲,余数=20-16=4
  累加器y = 9 + 3 = 12(无溢出)
  总步数 = 7

第5步:
  累加器x = 4 + 5 = 9(无溢出)
  累加器y = 12 + 3 = 15(无溢出)
  总步数 = 7

...(继续直到总步数=0)

你看,x轴在第4步就输出了第一个脉冲,而y轴要到后面才会输出。这就是DDA的「按比例分配脉冲」的本质。

重要提醒:

DDA的精度受累加器宽度影响很大。宽度越大,每一步的增量越精细,轨迹越平滑。但宽度太大也会带来计算延迟。我一般建议用16位累加器,精度和速度比较均衡。

1.7 小结

好了,DDA直线插补的原理就讲到这里。总结几个关键点:

  • 数学本质:把直线运动分解成两个坐标轴上的积分
  • 实现手段:用整数累加器模拟小数积分,溢出即走步
  • 终点判断:总步数计数法最可靠
  • 脉冲输出:溢出脉冲直接或整形后送给驱动器

说实话,DDA这个方法虽然老,但真的很巧妙。它用最简单的整数运算,解决了连续轨迹控制的问题。现在很多高端数控系统还在用它的变种。

下一节我们会讲DDA的误差分析和改进方法,到时候再聊。


公众号:蓝海资料掘金营,微信deep3321