3. 空间圆弧插补

任务目标

给定三维空间中不共线的三个点,确定其所在平面上的圆弧,并按指定步长在圆弧上插补输出一系列三维坐标点。本案例实现完整的几何求解与插补计算,不依赖第三方几何库。

实现原理

三点确定一个空间圆:首先计算三点所在平面的法向量,进而求出圆心坐标与半径。建立以圆心为原点、法向量为z轴的局部坐标系,将空间圆弧映射为二维平面圆弧,在局部坐标系中按角度步长插补后,再通过旋转矩阵变换回世界坐标系。

关键完整代码


import numpy as np

def space_circle_arc(p1, p2, p3, step_angle_deg=1.0):
    """
    三点确定空间圆弧,返回插补点列表(含起点和终点)
    p1, p2, p3: 三维坐标,np.array 或 list,不共线
    step_angle_deg: 插补步长(度)
    """
    p1 = np.array(p1, dtype=float)
    p2 = np.array(p2, dtype=float)
    p3 = np.array(p3, dtype=float)

    # 1. 计算平面法向量
    v1 = p2 - p1
    v2 = p3 - p1
    n = np.cross(v1, v2)
    norm_n = np.linalg.norm(n)
    if norm_n < 1e-12:
        raise ValueError("三点共线,无法确定圆弧")
    n = n / norm_n

    # 2. 计算圆心(利用垂直平分面交线)
    # 圆心满足 |c-p1|^2 = |c-p2|^2 和 |c-p1|^2 = |c-p3|^2
    # 即 2*(p2-p1)·c = |p2|^2 - |p1|^2
    A = np.array([
        2 * (p2 - p1),
        2 * (p3 - p1),
        n
    ])
    b = np.array([
        np.dot(p2, p2) - np.dot(p1, p1),
        np.dot(p3, p3) - np.dot(p1, p1),
        np.dot(n, p1)   # 圆心在平面内: n·c = n·p1
    ])
    center = np.linalg.solve(A, b)

    # 3. 半径与局部坐标系基向量
    r = np.linalg.norm(p1 - center)
    # u: 从圆心指向p1的单位向量
    u = (p1 - center) / r
    # w: 法向量, v = w × u 构成右手系
    w = n
    v = np.cross(w, u)

    # 4. 计算p2、p3在局部坐标系中的角度
    def local_angle(pt):
        d = pt - center
        x = np.dot(d, u)
        y = np.dot(d, v)
        return np.arctan2(y, x)

    theta1 = 0.0
    theta2 = local_angle(p2)
    theta3 = local_angle(p3)

    # 5. 确定圆弧方向(从p1经p2到p3,保持方向一致)
    # 将theta2、theta3调整到相对theta1的[0, 2π)区间
    def norm_angle(th):
        th = th - theta1
        while th < 0:
            th += 2 * np.pi
        while th >= 2 * np.pi:
            th -= 2 * np.pi
        return th

    a2 = norm_angle(theta2)
    a3 = norm_angle(theta3)

    # 若a3 < a2,说明跨过2π边界,a3加2π保证连续递增
    if a3 < a2:
        a3 += 2 * np.pi

    # 6. 按步长插补
    step = np.deg2rad(step_angle_deg)
    n_steps = max(1, int(np.ceil(a3 / step)))
    angles = np.linspace(0, a3, n_steps + 1)

    # 7. 局部坐标转世界坐标
    points = []
    for th in angles:
        local = np.array([r * np.cos(th), r * np.sin(th), 0.0])
        world = center + local[0] * u + local[1] * v + local[2] * w
        points.append(world)
    return np.array(points)

if __name__ == "__main__":
    # 示例:三点位于z=1平面上的一个半圆
    p1 = [2.0, 0.0, 1.0]
    p2 = [0.0, 2.0, 1.0]
    p3 = [-2.0, 0.0, 1.0]

    pts = space_circle_arc(p1, p2, p3, step_angle_deg=30.0)
    print("插补点数:", len(pts))
    for pt in pts:
        print(f"({pt[0]:.4f}, {pt[1]:.4f}, {pt[2]:.4f})")

运行与验证

运行方式:保存为 arc_interp.py,执行 python arc_interp.py。需要NumPy环境(pip install numpy)。

验证方法:

  • 示例输入三点在z=1平面且共圆,输出点应满足z坐标恒为1.0,且x²+y²≈4(半径2)。
  • 插补点从(2,0,1)开始,经(0,2,1)到(-2,0,1),共7个点(步长30°,覆盖180°)。
  • 可自行修改三点坐标验证任意平面圆弧,检查输出点是否共面且到圆心距离恒等于半径。

预期输出(示例输入)


插补点数: 7
(2.0000, 0.0000, 1.0000)
(1.7321, 1.0000, 1.0000)
(1.0000, 1.7321, 1.0000)
(0.0000, 2.0000, 1.0000)
(-1.0000, 1.7321, 1.0000)
(-1.7321, 1.0000, 1.0000)
(-2.0000, 0.0000, 1.0000)
X Y P1(2,0) P2(0,2) P3(-2,0) 圆心(0,0) r=2 插补方向 图:示例圆弧在XY平面的投影(z=1),红/橙/绿点为输入三点,紫点为插补输出