第3章:正运动学实战:2自由度机械臂正解计算、代码实现与验证

好,咱们直接进入正题。

上一章我们把DH参数法讲透了,现在该真刀真枪地算一把了。我选一个最简单的2自由度机械臂来做演示——为什么选它?因为手算能算清楚,代码能跑通,你也能一眼看出结果对不对。说白了,这是正运动学的“Hello World”。

3.1 2自由度机械臂的几何模型

想象一个肩膀关节和一个肘关节。肩膀固定在底座上,肘关节连着前臂。两个关节都是旋转的,没有滑动。

我习惯把这种臂叫做“RR构型”——两个旋转副。你在项目里见到的桌面小机械臂,十有八九都是这个结构。

参数定义如下:

  • L1:大臂长度(从肩膀到肘)
  • L2:小臂长度(从肘到腕/末端)
  • θ1:肩膀关节角度
  • θ2:肘关节角度

嗯,这里要注意:θ2是相对于大臂的夹角,不是绝对角度。很多新手在这里栽跟头,我当年也犯过这个错。

3.2 正解公式推导

正运动学要干什么?给你θ1和θ2,算出末端执行器的(x, y)坐标。说白了就是:

x = L1 * cos(θ1) + L2 * cos(θ1 + θ2)
y = L1 * sin(θ1) + L2 * sin(θ1 + θ2)

为什么会这样?

你想想看,大臂末端的位置是(L1*cosθ1, L1*sinθ1)。然后从小臂末端相对大臂末端,再转一个(θ1+θ2)的角度,走L2的距离。两个向量一加,就是最终位置。

我在项目中遇到过有人把第二项写成L2*cos(θ2),结果画出来的机械臂像断了似的。记住:角度是累加的!

核心公式(记住这个)

末端位置 = 大臂向量 + 小臂向量

小臂的方向 = 大臂方向 + 肘关节转角

3.3 代码实现

我习惯用Python写原型,因为调试快。咱们直接上代码:

import numpy as np
import matplotlib.pyplot as plt

def forward_kinematics_2dof(theta1, theta2, L1, L2):
    """
    2自由度机械臂正运动学计算
    theta1, theta2: 弧度制
    L1, L2: 臂长
    返回: (x, y) 末端坐标
    """
    # 大臂末端位置
    x1 = L1 * np.cos(theta1)
    y1 = L1 * np.sin(theta1)
    
    # 小臂末端位置(相对大臂末端)
    x2 = x1 + L2 * np.cos(theta1 + theta2)
    y2 = y1 + L2 * np.sin(theta1 + theta2)
    
    return (x2, y2)

# 测试一下
L1 = 1.0
L2 = 0.8

# 情况1:两个关节都伸直
theta1 = 0
theta2 = 0
x, y = forward_kinematics_2dof(theta1, theta2, L1, L2)
print(f"伸直状态: x={x:.3f}, y={y:.3f}")
# 预期: x=1.8, y=0.0

# 情况2:肩膀45度,肘关节-90度
theta1 = np.pi/4
theta2 = -np.pi/2
x, y = forward_kinematics_2dof(theta1, theta2, L1, L2)
print(f"弯曲状态: x={x:.3f}, y={y:.3f}")

跑一下这段代码,你会看到:

  • 伸直时,末端在(1.8, 0.0)——两个臂长加起来,合理
  • 弯曲时,末端在某个位置——你可以手算验证一下

我的调试小技巧

写正运动学代码时,先测几个特殊角度:0°、90°、180°。这些角度三角函数值简单,手算快。如果这些点都对了,其他角度基本不会错。

3.4 可视化验证

光看数字不够直观。我习惯画出来——眼睛比数字更敏感。

def plot_arm(theta1, theta2, L1, L2):
    # 计算各关节位置
    x0, y0 = 0, 0  # 底座
    x1 = L1 * np.cos(theta1)
    y1 = L1 * np.sin(theta1)
    x2, y2 = forward_kinematics_2dof(theta1, theta2, L1, L2)
    
    # 绘制
    plt.figure(figsize=(6, 6))
    plt.plot([x0, x1], [y0, y1], 'b-o', linewidth=3, label='大臂')
    plt.plot([x1, x2], [y1, y2], 'r-o', linewidth=3, label='小臂')
    plt.scatter(x2, y2, color='green', s=100, label='末端')
    
    plt.xlim(-2, 2)
    plt.ylim(-2, 2)
    plt.grid(True)
    plt.axis('equal')
    plt.legend()
    plt.title(f'θ1={np.degrees(theta1):.0f}°, θ2={np.degrees(theta2):.0f}°')
    plt.show()

# 画几个典型姿态
plot_arm(0, 0, L1, L2)                    # 伸直
plot_arm(np.pi/4, -np.pi/2, L1, L2)       # 弯曲
plot_arm(np.pi/2, np.pi/4, L1, L2)        # 向上举

看到图了吗?大臂蓝色,小臂红色,末端绿色点。一眼就能看出机械臂的姿态对不对。

避坑指南

我曾经在可视化时忘记设置plt.axis('equal'),结果画出来的臂长看起来不对——因为x轴和y轴比例不一致。嗯,这种bug查了我半小时。

3.5 验证:用几何法交叉检验

代码写完了,怎么确认它是对的?我推荐用几何法做交叉验证。

举个具体例子:

  • L1 = 1.0, L2 = 0.8
  • θ1 = 30° (π/6), θ2 = 60° (π/3)

手算步骤:

  1. 大臂末端:x1 = 1.0 * cos(30°) = 0.866, y1 = 1.0 * sin(30°) = 0.5
  2. 小臂相对角度:θ1+θ2 = 90°
  3. 小臂末端:x2 = 0.866 + 0.8 * cos(90°) = 0.866, y2 = 0.5 + 0.8 * sin(90°) = 1.3

用代码算一下:

theta1 = np.pi/6
theta2 = np.pi/3
x, y = forward_kinematics_2dof(theta1, theta2, 1.0, 0.8)
print(f"代码结果: x={x:.3f}, y={y:.3f}")
# 输出: x=0.866, y=1.300

完全吻合。嗯,这就放心了。

3.6 工作空间分析

正运动学还有一个重要应用:看你的机械臂能到达哪些位置。这就是工作空间。

对于2自由度臂,工作空间是一个圆环:

  • 外径 = L1 + L2(完全伸直)
  • 内径 = |L1 - L2|(完全折叠)

我习惯用蒙特卡洛法来可视化:随机生成一堆角度,算末端位置,然后画点。

# 蒙特卡洛法绘制工作空间
points = []
for _ in range(5000):
    t1 = np.random.uniform(-np.pi, np.pi)
    t2 = np.random.uniform(-np.pi, np.pi)
    x, y = forward_kinematics_2dof(t1, t2, L1, L2)
    points.append((x, y))

xs, ys = zip(*points)
plt.figure(figsize=(6, 6))
plt.scatter(xs, ys, s=1, alpha=0.5)
plt.axis('equal')
plt.title('2自由度机械臂工作空间')
plt.show()

看到那个圆环了吗?中间有个空洞——那是机械臂够不到的区域。这个空洞的大小取决于L1和L2的差值。差值越大,空洞越大。

设计启示

如果你想让机械臂能覆盖中心区域,就让L1和L2尽量接近。我在设计一个桌面臂时,L1=0.5m, L2=0.45m,空洞很小,效果不错。

3.7 本章小结

正运动学说白了就是:给角度,算位置。公式简单,但意义重大——它是所有运动规划的基础。

我建议你亲手敲一遍代码,改几个参数看看效果。把L1改成2.0,或者把θ2范围改成0到π,观察工作空间怎么变。动手试过的东西,才是你自己的。

2自由度机械臂正运动学知识体系 输入 θ1, θ2, L1, L2 核心公式 x = L1·cosθ1 + L2·cos(θ1+θ2) y = L1·sinθ1 + L2·sin(θ1+θ2) 输出 末端坐标 (x, y) 验证方法1:几何法 手算特殊角度,与代码对比 验证方法2:可视化 绘制机械臂姿态,目视检查 应用:工作空间分析 应用:运动规划基础

这张图把本章的知识脉络串起来了。从左到右:输入参数 → 核心公式 → 输出结果。下面两条分支是验证方法和应用场景。你照着这个结构学,思路会很清楚。

专注资料整理