第四章 圆弧插补原理:逐点比较法、DDA与角度逼近法

各位同学,今天我们来聊聊圆弧插补。说实话,直线插补搞明白了,圆弧插补才是真正考验功底的地方。我在做数控系统那几年,圆弧插补这块踩过的坑,比直线插补多得多。为什么?因为圆弧涉及方向判断、象限切换、误差累积,稍不留神,画出来的圆就变成了椭圆。

圆弧插补的核心任务,说白了就是:给定起点、终点、圆心和半径,让刀具沿着圆弧轨迹走,误差控制在允许范围内。今天我要讲的三种方法——逐点比较法、DDA法、角度逼近法,各有各的脾气,咱们一个一个来拆解。

核心要点:圆弧插补的本质是实时计算当前点与理想圆弧的偏差,并据此决定下一步的进给方向。三种方法的区别在于偏差计算的方式和进给策略。

圆弧插补方法 逐点比较法 偏差判别 → 进给控制 偏差计算 → 终点判别 四拍循环,逐点逼近 DDA圆弧插补 积分累加器原理 X/Y方向积分器 溢出脉冲驱动进给 角度逼近法 角度增量计算 三角函数查表 CORDIC算法加速 FPGA实现对比 逐点比较:资源少、速度慢 | DDA:速度快、精度高 | 角度逼近:精度最高、资源最多

4.1 逐点比较法圆弧插补

逐点比较法,我习惯叫它「走一步看一步」法。每次走一步,看看当前位置在圆弧的哪一侧,然后决定下一步往哪走。这个方法逻辑简单,FPGA实现起来资源消耗很少,但速度嘛...嗯,确实慢了点。

基本原理:

假设我们要插补第一象限的逆圆弧。圆心在原点,半径为R,当前点坐标为P(x,y)。偏差函数定义为:

F = x² + y² - R²

为什么这么定义?你想想看:

  • 如果F = 0,点在圆弧上
  • 如果F > 0,点在圆弧外侧
  • 如果F < 0,点在圆弧内侧

对于逆圆弧,进给规则是:

  • F ≥ 0:向圆内走一步(-X方向)
  • F < 0:向圆外走一步(+Y方向)

我的经验:逐点比较法最坑的地方在于象限切换。我在一个项目中,就因为象限判断逻辑写错了,结果圆弧在跨象限时出现了「跳变」。后来我加了一个象限状态机,专门处理边界情况,问题才解决。

FPGA实现要点:

逐点比较法在FPGA里实现,核心就是一个状态机,四个状态循环:

// 伪代码 - 逐点比较法状态机
always @(posedge clk) begin
  case(state)
    IDLE: begin
      // 初始化偏差值 F = 0
      // 加载起点坐标
      state <= DEVIATION;
    end
    DEVIATION: begin
      // 根据F值判断当前位置
      if(F >= 0) direction <= X_NEG;
      else       direction <= Y_POS;
      state <= FEED;
    end
    FEED: begin
      // 根据方向进给一步
      case(direction)
        X_NEG: x <= x - 1;
        Y_POS: y <= y + 1;
      endcase
      state <= CALC;
    end
    CALC: begin
      // 计算新偏差值
      // 注意:这里用递推公式,避免平方运算
      if(direction == X_NEG)
        F <= F - 2*x + 1;
      else
        F <= F + 2*y + 1;
      state <= CHECK;
    end
    CHECK: begin
      // 终点判别
      if(到达终点) state <= DONE;
      else         state <= DEVIATION;
    end
  endcase
end

注意:递推公式里的加减法,一定要用有符号数处理。我曾经因为用了无符号数,结果偏差值溢出,整个圆弧插补直接崩了。排查了整整两天才找到问题。

4.2 DDA圆弧插补

DDA法,全称是数字微分分析器。说白了,就是用积分的思想来逼近圆弧。这个方法比逐点比较法快得多,因为每一步都能同时产生X和Y方向的进给脉冲。

原理简述:

对于圆弧,参数方程是:

x = R * cos(θ)
y = R * sin(θ)

对时间求导:

dx/dt = -R * sin(θ) = -y
dy/dt =  R * cos(θ) =  x

你看,这个关系多漂亮!X方向的变化率等于-Y,Y方向的变化率等于X。DDA法就是利用这个关系,用两个积分器分别累加:

  • X积分器:累加 -Y 值,溢出时X方向进给
  • Y积分器:累加 +X 值,溢出时Y方向进给

FPGA实现结构:

// DDA圆弧插补核心逻辑
module dda_arc_interpolator (
    input  clk,
    input  rst_n,
    input  start,
    input  [15:0] radius,
    output reg x_pulse,
    output reg y_pulse,
    output reg done
);

    reg [15:0] x_reg, y_reg;      // 当前位置
    reg [15:0] x_acc, y_acc;      // 累加器
    reg [15:0] step_count;        // 步数计数

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            x_reg <= radius;      // 起点 (R, 0)
            y_reg <= 0;
            x_acc <= 0;
            y_acc <= 0;
            step_count <= 0;
        end else if(start) begin
            // X积分器:累加 -y
            x_acc <= x_acc - y_reg;
            // Y积分器:累加 +x
            y_acc <= y_acc + x_reg;

            // 溢出判断
            x_pulse <= x_acc[15];  // 最高位为1时溢出
            y_pulse <= y_acc[15];

            // 更新位置
            if(x_acc[15]) x_reg <= x_reg - 1;
            if(y_acc[15]) y_reg <= y_reg + 1;

            step_count <= step_count + 1;
            if(step_count == 90) done <= 1;  // 1/4圆弧
        end
    end

endmodule

关键点:DDA法的精度取决于累加器的位宽。我一般用24位累加器,这样既能保证精度,又不会消耗太多LUT。如果位宽太小,圆弧会明显「锯齿化」。

4.3 角度逼近法

角度逼近法,是我个人最喜欢的方法。为什么?因为它最接近数学本质。我们不直接控制X和Y,而是控制角度θ的增量,然后用三角函数算出坐标。

核心思想:

每次插补步长Δθ固定,然后:

θ_new = θ_old + Δθ
x_new = R * cos(θ_new)
y_new = R * sin(θ_new)

但问题来了——FPGA算三角函数很慢。怎么办?

这里就要请出CORDIC算法了。CORDIC(坐标旋转数字计算机)用移位和加法来逼近三角函数,非常适合FPGA实现。

CORDIC实现角度逼近:

// CORDIC核心迭代
for(i=0; i<N; i++) begin
    if(z >= 0) begin
        x_next = x - (y >> i);
        y_next = y + (x >> i);
        z_next = z - atan_table[i];
    end else begin
        x_next = x + (y >> i);
        y_next = y - (x >> i);
        z_next = z + atan_table[i];
    end
    x = x_next;
    y = y_next;
    z = z_next;
end

避坑指南:我曾经在CORDIC迭代次数上吃过亏。迭代次数太少,角度误差大,圆弧不圆;迭代次数太多,延迟太大,影响插补速度。经验值是16次迭代,精度和速度的平衡点。

4.4 三种方法对比

特性 逐点比较法 DDA法 角度逼近法
资源消耗 低(约200 LUT) 中(约500 LUT) 高(约1200 LUT)
插补速度 慢(每步1拍) 快(每步1拍,双轴同时) 中(每步N次迭代)
精度 中(±1步长) 高(取决于累加器位宽) 最高(取决于迭代次数)
适用场景 低速、低成本系统 中高速通用系统 高精度、高速系统
象限处理 复杂(需状态机) 简单(自动处理) 简单(角度连续)

说实话,没有哪种方法是绝对最好的。我在实际项目中,会根据需求灵活选择:

  • 如果做的是低成本雕刻机,逐点比较法就够了
  • 如果是通用数控系统,DDA法是主流选择
  • 如果是高精度加工中心,我会用角度逼近法+CORDIC

重要提醒:无论用哪种方法,一定要做仿真验证。我见过太多人直接上板调,结果烧了电机驱动器。先用ModelSim跑仿真,把圆弧轨迹画出来看看,确认没问题再上硬件。

好了,圆弧插补的三种方法就讲到这里。每种方法都有它的脾气,理解了原理,FPGA实现就是水到渠成的事。下一章我们会讲更复杂的螺旋线插补,那个更有意思。


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