Files
craic/docs/arm.md
FallenSigh d716ed5e67 docs: 重写机械臂运动学文档
- 清晰的章节结构:几何参数、正运动学、逆运动学
- 完整的数学推导(带 LaTeX 公式)
- Python 代码实现示例
- 工作空间分析和奇异点说明
- 运动插值算法
- 实用示例和测试命令

替换了原有的混乱结构,现在更易理解和参考
2026-06-16 19:05:09 +08:00

8.6 KiB
Raw Permalink Blame History

机械臂运动学推导

6-DOF 机械臂逆运动学和正运动学完整推导。

机械臂结构

关节配置

J1: 线性滑轨(垂直运动,高度 d1
J2: 基座旋转(绕 Z 轴XY 平面)
J3: 肘关节(绕 Z 轴XY 平面)
J4: 腕关节(绕 Z 轴XY 平面)
J5: 末端俯仰0° = 水平)
J6: 夹爪旋转

几何参数

参数 值 (mm) 说明
L1 125 J2-J3 连杆长度
L2 125 J3-J4 连杆长度
x4 110 J4-TCP 水平偏移
z4 80 J4-TCP 垂直偏移(夹爪状态相关:张开 55mm闭合 -100mm

坐标系

基坐标系右手系Z 轴朝上):

        Z↑ (高度)
        |
        |
        o----→ X (前)
       /
      /
     Y (左)
  • 原点J1 滑轨底部
  • d1 范围:[-290, 0] mm负值表示在基准面以下

正运动学FK

已知关节状态 → 计算 TCP 位姿

输入

  • d1: 高度J1 线性位移)
  • θ2, θ3, θ4: J2/J3/J4 角度(度)

输出

  • (x, y, z): TCP 位置mm
  • φ: TCP 偏航角(度)

推导

步骤 1:计算 J4 中心位置XY 平面)

J2 和 J3 形成二连杆机构:


x_{j4} = L_1 \cos\theta_2 + L_2 \cos(\theta_2 + \theta_3)

y_{j4} = L_1 \sin\theta_2 + L_2 \sin(\theta_2 + \theta_3)

步骤 2:计算 TCP 偏航角


\phi = \theta_2 + \theta_3 + \theta_4

步骤 3:计算 TCP 位置

从 J4 中心沿 φ 方向偏移 x4垂直偏移 z4


x = x_{j4} + x_4 \cos\phi

y = y_{j4} + x_4 \sin\phi

z = d_1 - z_4

Python 实现

def forward_kinematics(d1, theta2_deg, theta3_deg, theta4_deg, L1=125, L2=125, x4=110, z4=80):
    """正运动学"""
    theta2 = math.radians(theta2_deg)
    theta3 = math.radians(theta3_deg)
    theta4 = math.radians(theta4_deg)
    phi = theta2 + theta3 + theta4
    
    # J4 中心
    x_j4 = L1 * math.cos(theta2) + L2 * math.cos(theta2 + theta3)
    y_j4 = L1 * math.sin(theta2) + L2 * math.sin(theta2 + theta3)
    
    # TCP 位置
    x = x_j4 + x4 * math.cos(phi)
    y = y_j4 + x4 * math.sin(phi)
    z = d1 - z4
    
    return x, y, z, math.degrees(phi)

逆运动学IK

已知 TCP 目标位姿 → 计算关节角度

输入

  • (x, y, z): 目标位置mm
  • φ: 目标偏航角(度)

输出

  • d1: 高度
  • θ2, θ3, θ4: 关节角度(度)

推导

步骤 1:计算 J4 中心目标位置

从 TCP 位置反向计算 J4 位置:


x_{j4} = x - x_4 \cos\phi

y_{j4} = y - x_4 \sin\phi

z_{j4} = z + z_4

d_1 = z_{j4}

步骤 2:计算平面距离


r = \sqrt{x_{j4}^2 + y_{j4}^2}

步骤 3:求解 θ3余弦定理

二连杆机构的标准解法:


\cos\theta_3 = \frac{r^2 - L_1^2 - L_2^2}{2L_1L_2}

检查工作空间:


-1 \leq \cos\theta_3 \leq 1 \quad \Rightarrow \quad |L_1 - L_2| \leq r \leq L_1 + L_2

否则目标超出工作空间。

有两个解(肘部朝上 / 朝下):


\theta_3 = \pm \arccos\left(\frac{r^2 - L_1^2 - L_2^2}{2L_1L_2}\right)

步骤 4:求解 θ2


\theta_2 = \arctan2(y_{j4}, x_{j4}) - \arctan2\left(L_2\sin\theta_3, L_1 + L_2\cos\theta_3\right)

步骤 5:求解 θ4


\theta_4 = \phi - \theta_2 - \theta_3

Python 实现

def inverse_kinematics(x, y, z, phi_deg, elbow_up=False, L1=125, L2=125, x4=110, z4=80):
    """逆运动学"""
    phi = math.radians(phi_deg)
    
    # 步骤 1: 计算 J4 中心
    x_j4 = x - x4 * math.cos(phi)
    y_j4 = y - x4 * math.sin(phi)
    z_j4 = z + z4
    d1 = z_j4
    
    # 步骤 2: 平面距离
    r2 = x_j4**2 + y_j4**2
    r = math.sqrt(r2)
    
    # 步骤 3: 求解 theta3
    cos_theta3 = (r2 - L1**2 - L2**2) / (2 * L1 * L2)
    
    if cos_theta3 < -1 or cos_theta3 > 1:
        raise ValueError(f"目标超出工作空间r={r:.1f}mm")
    
    # 肘部朝上:负角度,肘部朝下:正角度
    sin_theta3 = -math.sqrt(1 - cos_theta3**2) if elbow_up else math.sqrt(1 - cos_theta3**2)
    theta3 = math.atan2(sin_theta3, cos_theta3)
    
    # 步骤 4: 求解 theta2
    theta2 = math.atan2(y_j4, x_j4) - math.atan2(
        L2 * sin_theta3,
        L1 + L2 * cos_theta3
    )
    
    # 步骤 5: 求解 theta4
    theta4 = phi - theta2 - theta3
    
    # 角度归一化到 [-180, 180)
    def normalize(angle):
        a = (angle + math.pi) % (2 * math.pi) - math.pi
        return a if a != -math.pi or angle <= 0 else math.pi
    
    return (
        d1,
        math.degrees(normalize(theta2)),
        math.degrees(normalize(theta3)),
        math.degrees(normalize(theta4))
    )

零点偏移

物理零点(机械对齐)与数学零点(直线构型)存在偏差:

关节 零点偏移
J2 +3°
J3 +7°
J4 +25°

转换关系

命令角度 ↔ 数学角度


\theta_{\text{command}} = \theta_{\text{math}} + \text{offset}

\theta_{\text{math}} = \theta_{\text{command}} - \text{offset}

使用

  • 正运动学:先减去偏移(命令 → 数学),再计算
  • 逆运动学:先计算(数学),再加上偏移(数学 → 命令)

工作空间

高度范围


z \in [-290 - z_4, 0 - z_4] = [-370, -80] \text{ mm}

水平范围


r_{\min} = |L_1 - L_2| + x_4 = 0 + 110 = 110 \text{ mm}

r_{\max} = L_1 + L_2 + x_4 = 125 + 125 + 110 = 360 \text{ mm}

可达圆环:半径 110mm 到 360mm

关节限位

关节 最小值 最大值 单位
height -290 0 mm
J2 -110 115 °
J3 -120 145 °
J4 -90 130 °
J5 -180 180 °
J6 -180 180 °

奇异点

1. 肩部奇异点

当 J4 中心在原点正上方:


x_{j4} = y_{j4} = 0 \quad \Rightarrow \quad r = 0

此时 θ2 无定义(可取任意值)。

避免:保持 r > 10mm

2. 肘部奇异点

当机械臂完全伸直或完全折叠:


\theta_3 = 0° \text{ 或 } \pm 180°

此时运动控制不稳定。

避免:保持 |θ3| > 5°

运动插值

关节空间线性插值,避免笛卡尔空间的复杂路径规划。

算法

给定起点 q_start 和终点 q_end,生成 N 个中间点:


q_i = q_{\text{start}} + \frac{i}{N}(q_{\text{end}} - q_{\text{start}}), \quad i = 1, 2, \ldots, N

参数

  • duration: 运动时长(秒)
  • rate: 插值频率Hz
  • steps = ceil(duration × rate): 插值点数

示例duration=2.0s, rate=20Hzsteps=40

Python 实现

def interpolate_joints(start, end, duration=2.0, rate=20.0):
    """关节空间插值"""
    steps = max(1, int(math.ceil(duration * rate)))
    trajectory = []
    
    for i in range(1, steps + 1):
        t = i / steps
        state = {
            key: int(round(start[key] + t * (end[key] - start[key])))
            for key in start.keys()
        }
        trajectory.append(state)
    
    return trajectory

完整示例

示例 1前方抓取

目标:抓取前方 250mm右侧 50mm高度 -150mm 的物体

# 逆运动学
d1, theta2, theta3, theta4 = inverse_kinematics(
    x=250, y=50, z=-150, phi_deg=0
)
# 输出: d1=-70, theta2=11.3°, theta3=-48.6°, theta4=37.3°

# 验证:正运动学
x, y, z, phi = forward_kinematics(d1, theta2, theta3, theta4)
# 输出: (250.0, 50.0, -150.0, 0.0°) ✓

示例 2多段轨迹

# 初始位置
start = {'height': 0, 'j2': 0, 'j3': 0, 'j4': 0, 'j5': 81, 'j6': 30}

# 目标 1: 上方
_, theta2, theta3, theta4 = inverse_kinematics(200, 100, -50, 45)
waypoint1 = {'height': -50, 'j2': theta2, 'j3': theta3, 'j4': theta4, 'j5': 81, 'j6': 30}

# 目标 2: 抓取位置
_, theta2, theta3, theta4 = inverse_kinematics(200, 100, -150, 45)
waypoint2 = {'height': -150, 'j2': theta2, 'j3': theta3, 'j4': theta4, 'j5': -100, 'j6': -5}

# 生成轨迹
traj1 = interpolate_joints(start, waypoint1, duration=2.0)      # 2秒到上方
traj2 = interpolate_joints(waypoint1, waypoint2, duration=1.0)  # 1秒下降抓取

参考

UDP 命令格式

JXB:<height>:<J2>:<J3>:<J4>:<J5>:<J6>:0:0:EZHY\n
  • 所有角度值已包含零点偏移
  • 直接发送到 ESP32 的 UDP 端口 8888

相关工具

  • tools/udp_control.py - 命令行控制(支持 jointspose 模式)
  • ros2/src/udp_teleop/udp_teleop/arm_control.py - ROS 节点
  • docs/vision_calibration_horizontal.md - 相机坐标变换

测试

# 测试逆运动学
python tools/udp_control.py pose --x 200 --y 100 --z -100 --phi 45 --dry-run

# 发送命令
python tools/udp_control.py pose --x 200 --y 100 --z -100 --phi 45 --duration 2.0