逐点比较法圆弧插补:从原理到代码

逐点比较法做圆弧插补,说白了就是让刀具沿着圆弧走,每一步都问自己三个问题:我现在在圆弧的哪一侧?下一步该往哪个方向走?走到头了没有?

我刚开始做数控系统时,觉得直线插补好理解,圆弧插补绕来绕去的。后来亲手推导了一遍偏差公式,才发现其实逻辑非常清晰。今天我们就拿第一象限逆圆来开刀。

基本原理:走一步,看一步

逐点比较法的核心思想就八个字:逐点比较,步步逼近。每走一步,计算当前点与理想圆弧的偏差,根据偏差决定下一步的方向。

你想想看,这就像蒙着眼睛走圆弧——每走一步摸一下,偏里了往外拐,偏外了往里收。虽然每一步都是直线,但整体轨迹逼近圆弧。

核心流程:

  1. 偏差判别:当前点在圆内、圆上还是圆外?
  2. 坐标进给:根据偏差决定向X或Y方向走一步
  3. 偏差计算:计算新位置的偏差值
  4. 终点判别:是否到达终点?没到就继续
逐点比较法圆弧插补核心流程(第一象限逆圆) 开始 偏差判别 F ≥ 0 ? F ≥ 0:-X 方向进给 F < 0:+Y 方向进给 终点判别 每走一步,计算新偏差 → 再判别 → 再进给 → 直到终点

偏差计算公式推导(第一象限逆圆)

设圆弧起点为 (X₀, Y₀),终点为 (Xₑ, Yₑ),圆心在原点。圆弧半径为 R。

对于圆弧上任意一点 (Xᵢ, Yᵢ),理想情况下满足:

Xᵢ² + Yᵢ² = R²

我们定义偏差函数 Fᵢ:

Fᵢ = Xᵢ² + Yᵢ² - R²

这个公式很直观:

  • Fᵢ = 0:点在圆弧上
  • Fᵢ > 0:点在圆弧外侧(离圆心远)
  • Fᵢ < 0:点在圆弧内侧(离圆心近)

我的小经验:刚开始学的时候,我老把正负搞反。后来记住一句话——"大外小内":偏差大于0在圆外,小于0在圆内。这个口诀帮我省了不少调试时间。

进给方向判定

第一象限逆圆的进给规则是这样的:

偏差状态 当前位置 进给方向 说明
Fᵢ ≥ 0 圆上或圆外 -X 方向 向圆内逼近
Fᵢ < 0 圆内 +Y 方向 向圆外逼近

为什么会这样?你想想看,第一象限逆圆是从右上往左上走。如果当前点在圆外,说明X方向走过头了,需要往-X方向回调。如果点在圆内,说明Y方向还没走够,需要往+Y方向补一步。

递推偏差计算

每次计算 X² + Y² - R² 太慢了。实际工程中我们用递推公式,只做加减法。

情况1:Fᵢ ≥ 0,向 -X 方向走一步

Xᵢ₊₁ = Xᵢ - 1
Yᵢ₊₁ = Yᵢ

Fᵢ₊₁ = (Xᵢ - 1)² + Yᵢ² - R²
      = Xᵢ² - 2Xᵢ + 1 + Yᵢ² - R²
      = Fᵢ - 2Xᵢ + 1

情况2:Fᵢ < 0,向 +Y 方向走一步

Xᵢ₊₁ = Xᵢ
Yᵢ₊₁ = Yᵢ + 1

Fᵢ₊₁ = Xᵢ² + (Yᵢ + 1)² - R²
      = Xᵢ² + Yᵢ² + 2Yᵢ + 1 - R²
      = Fᵢ + 2Yᵢ + 1

关键结论:

  • F ≥ 0 时:F ← F - 2X + 1,X ← X - 1
  • F < 0 时:F ← F + 2Y + 1,Y ← Y + 1

全程只用加减法,没有乘法,没有开方。这就是逐点比较法能在老式步进电机系统上跑得飞快的原因。

终点判别方法

终点判别我见过三种做法:

方法 原理 优缺点
总步数法 n = |Xₑ - X₀| + |Yₑ - Y₀| 简单可靠,我最常用
坐标比较法 当前坐标与终点坐标逐项比较 容易受累积误差影响
偏差归零法 回到圆弧上且到达终点附近 逻辑复杂,不推荐

我个人习惯用总步数法。先算出总共要走多少步,每走一步计数器减1,减到0就停。简单、稳定、不出错。

我曾经踩过的坑:用坐标比较法时,由于累积误差,刀具走到终点附近但坐标不完全相等,导致永远停不下来。后来全部改用总步数法,再也没出过问题。

Python 代码实现与仿真

下面是我写的一个完整实现。代码不长,但包含了所有核心逻辑。

import matplotlib.pyplot as plt
import numpy as np

def arc_interpolation_ccw(x0, y0, xe, ye):
    """
    第一象限逆圆逐点比较法插补
    :param x0, y0: 起点坐标
    :param xe, ye: 终点坐标
    :return: 插补路径点列表
    """
    R2 = x0**2 + y0**2  # 半径平方
    x, y = x0, y0
    F = 0  # 初始偏差,起点在圆弧上
    # 总步数
    n = abs(xe - x0) + abs(ye - y0)
    
    path = [(x, y)]
    step = 0
    
    while step < n:
        if F >= 0:
            # 向 -X 方向进给
            F = F - 2*x + 1
            x = x - 1
        else:
            # 向 +Y 方向进给
            F = F + 2*y + 1
            y = y + 1
        
        path.append((x, y))
        step += 1
    
    return path

# 示例:从 (5,0) 到 (0,5) 的逆圆
path = arc_interpolation_ccw(5, 0, 0, 5)

# 可视化
xs, ys = zip(*path)
plt.figure(figsize=(8, 8))
plt.plot(xs, ys, 'b-o', markersize=4, label='插补路径')

# 绘制理想圆弧
theta = np.linspace(0, np.pi/2, 100)
plt.plot(5*np.cos(theta), 5*np.sin(theta), 'r--', label='理想圆弧')

plt.grid(True, alpha=0.3)
plt.axis('equal')
plt.xlim(-1, 6)
plt.ylim(-1, 6)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('逐点比较法圆弧插补(第一象限逆圆)')
plt.legend()
plt.show()

运行建议:把这段代码复制到 Jupyter Notebook 里跑一下。你会看到蓝色折线紧密贴合红色虚线,每一步都走得很规矩。这就是逐点比较法的魅力——用最简单的加减法,走出漂亮的圆弧。

仿真结果分析

从 (5,0) 到 (0,5) 走一个90度逆圆弧,总共需要走10步。我截取中间几步的数据:

步数 当前坐标 偏差 F 进给方向
0 (5, 0) 0 -X
1 (4, 0) -9 +Y
2 (4, 1) -6 +Y
3 (4, 2) -1 +Y
4 (4, 3) 6 -X
5 (3, 3) -2 +Y
... ... ... ...
10 (0, 5) 0 终点

你看,偏差 F 在正负之间来回摆动,刀具就在圆弧内外交替逼近。虽然每一步都有误差,但整体轨迹始终贴着圆弧走。这就是逐点比较法的精髓——用有误差的每一步,走出无误差的整体。

本章核心要点:

  • 偏差公式 F = X² + Y² - R²,大外小内
  • 第一象限逆圆:F≥0走-X,F<0走+Y
  • 递推计算只用加减法,效率极高
  • 终点判别推荐总步数法,简单可靠