正运动学求解:从DH参数到工作空间分析

正运动学,说白了就是:给定关节角度,算出末端位姿。这是机器人学里最基础、也最绕不开的一块。我刚开始学的时候,总觉得公式推导太枯燥,直到第一次在真机上看到末端执行器按我算出的轨迹走起来——那种感觉,嗯,确实不一样。

DH参数:机器人建模的通用语言

DH参数法(Denavit-Hartenberg)是描述相邻连杆之间位姿关系的标准方法。每个关节对应一组四个参数:

参数 符号 物理含义 常见取值
连杆长度 ai-1 沿xi-1轴,从zi-1到zi的距离 固定值(机械设计决定)
连杆扭角 αi-1 绕xi-1轴,从zi-1到zi的转角 通常为0°、±90°
连杆偏距 di 沿zi轴,从xi-1到xi的距离 关节变量(移动关节)或固定值
关节转角 θi 绕zi轴,从xi-1到xi的转角 关节变量(旋转关节)
我的习惯:建立DH表时,先把所有关节的坐标系画出来,再填参数。坐标系画错了,后面全白搭。我曾经在项目里因为一个α的正负号搞反,调试了整整两天。

正运动学推导:一步步来

有了DH参数,相邻连杆的变换矩阵就是标准公式:

T = [cosθ    -sinθ·cosα    sinθ·sinα    a·cosθ
     sinθ     cosθ·cosα   -cosθ·sinα    a·sinθ
       0        sinα          cosα         d
       0          0            0           1   ]

把每个关节的T矩阵连乘,就得到末端相对于基座的位姿:

T0n = T01 · T12 · ... · Tn-1n

举个六轴工业机器人的例子。我常用的一个模型是:

  • 关节1:绕基座旋转(θ1)
  • 关节2:肩关节俯仰(θ2)
  • 关节3:肘关节俯仰(θ3)
  • 关节4:前臂旋转(θ4)
  • 关节5:腕关节俯仰(θ5)
  • 关节6:末端旋转(θ6)

你想想看,六个矩阵连乘,符号展开后能有几十项。手算?不现实。所以我们用计算机代劳。

MATLAB仿真实现

我个人偏爱用MATLAB的Robotics Toolbox,它把DH建模封装得很干净:

% 定义六轴机器人DH参数(标准DH)
L(1) = Link([0    0      0.3    0      0], 'standard');
L(2) = Link([0    0.15   0      0.5    0], 'standard');
L(3) = Link([0    0      0      0.4    0], 'standard');
L(4) = Link([0    0.4    0      0      0], 'standard');
L(5) = Link([0    0      0      0.3    0], 'standard');
L(6) = Link([0    0      0      0      0], 'standard');

% 创建机器人对象
robot = SerialLink(L, 'name', '六轴机械臂');

% 给定关节角度(单位:弧度)
q = [0.5, -0.3, 0.8, 0, 0.2, 0];

% 正运动学求解
T = robot.fkine(q);

% 显示末端位姿
disp('末端位姿矩阵:');
disp(T);
关键点:fkine()函数内部就是连乘运算。但如果你要自己实现,记得检查每个T矩阵的维度——4×4,别写成3×4。

Python仿真实现

Python这边,我推荐用roboticstoolbox-python库。语法和MATLAB版本很像,但更灵活:

import roboticstoolbox as rtb
import numpy as np

# 定义DH参数(修正DH)
robot = rtb.DHRobot([
    rtb.RevoluteDH(d=0.3, a=0, alpha=0),
    rtb.RevoluteDH(d=0, a=0.15, alpha=np.pi/2),
    rtb.RevoluteDH(d=0, a=0.4, alpha=0),
    rtb.RevoluteDH(d=0.4, a=0, alpha=-np.pi/2),
    rtb.RevoluteDH(d=0, a=0, alpha=np.pi/2),
    rtb.RevoluteDH(d=0, a=0, alpha=-np.pi/2)
], name='六轴机械臂')

# 给定关节角度
q = [0.5, -0.3, 0.8, 0, 0.2, 0]

# 正运动学求解
T = robot.fkine(q)

print("末端位姿矩阵:")
print(T)
避坑指南:我曾经在Python里把标准DH和修正DH搞混了。标准DH的变换顺序是绕x轴转α、沿x轴移a、绕z轴转θ、沿z轴移d;修正DH的顺序不同。用之前,一定确认你的库用的是哪种约定。

工作空间分析

工作空间,就是机器人末端能到达的所有点的集合。分析它,说白了就是回答一个问题:我的机器人能碰到那个位置吗?

常用的方法是蒙特卡洛法——随机采样关节角度,计算末端位置,然后画点云。我习惯这样做:

import numpy as np
import matplotlib.pyplot as plt

# 随机采样10000组关节角度
num_samples = 10000
q_samples = np.random.uniform(
    low=[-np.pi, -np.pi/2, -np.pi, -np.pi, -np.pi/2, -np.pi],
    high=[np.pi, np.pi/2, np.pi, np.pi, np.pi/2, np.pi],
    size=(num_samples, 6)
)

# 计算末端位置
positions = []
for q in q_samples:
    T = robot.fkine(q)
    positions.append(T.t)  # 提取平移向量

positions = np.array(positions)

# 绘制工作空间(3D散点图)
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(positions[:,0], positions[:,1], positions[:,2], 
           s=1, c='blue', alpha=0.3)
ax.set_xlabel('X (m)')
ax.set_ylabel('Y (m)')
ax.set_zlabel('Z (m)')
ax.set_title('六轴机械臂工作空间(蒙特卡洛法)')
plt.show()

为什么会用蒙特卡洛法?因为解析法求工作空间边界太复杂了,尤其是六轴机器人。随机采样虽然粗糙,但足够工程使用。

我的经验:采样点数不是越多越好。10000点足够看出工作空间的形状和空洞。如果你发现点云里有明显的空白区域,那可能是关节限位导致的——检查一下你的关节范围设置。

核心逻辑:一张图说清楚

下面这张图概括了正运动学求解的完整流程:

正运动学求解核心流程 DH参数定义 a, α, d, θ 变换矩阵计算 T = f(a, α, d, θ) 矩阵连乘 T₀ⁿ = T₀¹·T₁²·...·Tₙ₋₁ⁿ 末端位姿 位置 + 姿态 工作空间分析 蒙特卡洛法 / 解析法 ⚠ 常见问题 • DH参数符号搞反 • 标准DH vs 修正DH混淆 💡 工程建议 • 先用仿真验证 • 再上真机测试

工作空间的工程意义

工作空间分析不是纸上谈兵。我在做产线布局时,经常用它来回答三个问题:

  1. 可达性:工件放在这个位置,机器人能碰到吗?
  2. 灵巧性:在某个区域,机器人有多少种姿态可以到达?
  3. 奇异性:工作空间内部有没有“死点”?

举个例子。有一次我在调试一个焊接机器人,发现末端在某个位置总是抖动。后来一查工作空间,那个点正好在奇异位形附近——关节速度瞬间变得极大。嗯,从那以后,我每次做轨迹规划前,都会先跑一遍工作空间分析。

总结一下:正运动学是机器人学的“算术”,DH参数是“语法”,工作空间分析是“地图”。这三样东西,你搞懂了,后面逆运动学、轨迹规划、动力学,学起来就顺了。