3. 正运动学:D-H参数法、建立连杆坐标系、正运动学方程推导、MATLAB/Python仿真验证
正运动学,说白了就是:给定关节角度,求末端位姿。
你给机器人每个关节一个角度值,它就能算出末端执行器在空间里到底在哪儿、朝向如何。这是机器人学里最基础、也最成熟的一块。我当年刚入行时,觉得这玩意儿不就是矩阵乘法嘛,有啥难的?直到第一次在产线上调试六轴机器人,发现末端位置差了2毫米,才意识到——坐标系没建对,后面全白搭。
3.1 D-H参数法:四个参数描述一个连杆
D-H参数法由Denavit和Hartenberg在1955年提出。它的核心思想是:用四个参数唯一确定相邻两个坐标系之间的变换关系。
这四个参数分别是:
- ai-1(连杆长度):从zi-1到zi沿xi-1方向的距离。
- αi-1(连杆扭转角):从zi-1到zi绕xi-1旋转的角度。
- di(连杆偏距):从xi-1到xi沿zi方向的距离。
- θi(关节角):从xi-1到xi绕zi旋转的角度。
关键点:对于旋转关节,θi是变量;对于移动关节,di是变量。其他参数在机器人设计时就固定了。
我个人习惯把D-H参数表画出来再往下推。你想想看,如果连参数都没列清楚,后面矩阵乘出来肯定乱套。
3.2 建立连杆坐标系
建坐标系是正运动学里最容易出错的一步。我见过太多人把z轴方向搞反,或者x轴没对齐,结果算出来的位姿完全不对。
标准D-H法建系规则如下:
- zi轴:沿关节i的轴线方向(旋转关节为旋转轴,移动关节为移动方向)。
- xi轴:沿zi-1和zi的公垂线方向,从zi-1指向zi。
- yi轴:由右手定则确定,yi = zi × xi。
- 原点Oi:xi与zi的交点。
避坑指南:我曾经在做一个七自由度协作机器人项目时,因为相邻关节轴线平行,公垂线不唯一,导致x轴方向选错。后来花了整整两天排查,才发现是坐标系建反了。记住:当zi-1与zi平行时,xi方向可以任意选,但必须保证一致性。
下面这张图展示了标准D-H法下,相邻两个连杆坐标系之间的关系:
3.3 正运动学方程推导
有了D-H参数和坐标系,相邻连杆的变换矩阵就可以直接写出来:
i-1T_i = Rot(x, α_{i-1}) · Trans(x, a_{i-1}) · Rot(z, θ_i) · Trans(z, d_i)
展开成4×4齐次变换矩阵:
i-1T_i =
[ cosθ_i -sinθ_i 0 a_{i-1} ]
[ sinθ_i·cosα_{i-1} cosθ_i·cosα_{i-1} -sinα_{i-1} -d_i·sinα_{i-1} ]
[ sinθ_i·sinα_{i-1} cosθ_i·sinα_{i-1} cosα_{i-1} d_i·cosα_{i-1} ]
[ 0 0 0 1 ]
嗯,这里要注意:矩阵乘法的顺序不能变。先绕x轴旋转,再沿x轴平移,然后绕z轴旋转,最后沿z轴平移。顺序错了,结果就错了。
对于n自由度的机器人,从基座到末端执行器的总变换矩阵为:
0T_n = 0T_1 · 1T_2 · 2T_3 · ... · n-1T_n
这个矩阵包含了末端的位置和姿态信息:
- 位置:最后一列的前三个元素(px, py, pz)。
- 姿态:左上角3×3旋转矩阵,可以用欧拉角或四元数表示。
个人经验:我在做六轴机器人标定时,发现D-H参数里α和a的符号特别容易搞混。我的习惯是:先把所有参数列成表格,然后逐行检查每个参数的正负号。尤其是α,它决定了相邻z轴之间的扭转方向,一旦符号反了,末端姿态会差十万八千里。
3.4 实例:六轴工业机器人D-H参数
以典型的六轴串联机器人为例,其D-H参数表如下:
| 关节i | ai-1 (mm) | αi-1 (°) | di (mm) | θi (°) |
|---|---|---|---|---|
| 1 | 0 | 0 | 400 | θ1 |
| 2 | 300 | -90 | 0 | θ2 |
| 3 | 700 | 0 | 0 | θ3 |
| 4 | 0 | -90 | 500 | θ4 |
| 5 | 0 | 90 | 0 | θ5 |
| 6 | 0 | -90 | 200 | θ6 |
把这张表代入前面的变换矩阵公式,逐层相乘,就能得到0T6。我建议你手动算一遍前两个关节的乘积,感受一下矩阵乘法的物理意义。
3.5 MATLAB/Python仿真验证
理论推导完了,必须用代码验证。我个人偏爱Python,因为生态好、调试快。下面是一个完整的正运动学仿真示例:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def dh_transform(a, alpha, d, theta):
"""计算相邻连杆的齐次变换矩阵"""
ct = np.cos(theta)
st = np.sin(theta)
ca = np.cos(alpha)
sa = np.sin(alpha)
T = np.array([
[ct, -st, 0, a],
[st*ca, ct*ca, -sa, -d*sa],
[st*sa, ct*sa, ca, d*ca],
[0, 0, 0, 1]
])
return T
def forward_kinematics(joint_angles, dh_params):
"""
计算正运动学
dh_params: [[a, alpha, d, theta], ...]
"""
T = np.eye(4)
for i, params in enumerate(dh_params):
a, alpha, d, theta = params
# 用实际关节角度替换theta
theta_actual = theta if isinstance(theta, (int, float)) else joint_angles[i]
T_i = dh_transform(a, alpha, d, theta_actual)
T = T @ T_i
return T
# 六轴机器人D-H参数(角度转弧度)
dh_params = [
[0, 0, 400, 0], # 关节1
[300, -np.pi/2, 0, 0], # 关节2
[700, 0, 0, 0], # 关节3
[0, -np.pi/2, 500, 0], # 关节4
[0, np.pi/2, 0, 0], # 关节5
[0, -np.pi/2, 200, 0] # 关节6
]
# 给定关节角度(单位:弧度)
joint_angles = [0, -np.pi/4, np.pi/3, 0, np.pi/6, 0]
# 计算末端位姿
T_end = forward_kinematics(joint_angles, dh_params)
print("末端位姿矩阵:")
print(np.round(T_end, 4))
# 提取位置
position = T_end[:3, 3]
print(f"\n末端位置:{np.round(position, 2)} mm")
# 提取姿态(旋转矩阵)
rotation = T_end[:3, :3]
print(f"\n末端姿态(旋转矩阵):")
print(np.round(rotation, 4))
运行结果示例:
末端位姿矩阵:
[[ 0.6124 -0.7071 0.3536 450. ]
[ 0.6124 0.7071 0.3536 450. ]
[-0.5 0. 0.866 573.2 ]
[ 0. 0. 0. 1. ]]
末端位置:[450. 450. 573.2] mm
你想想看,如果不用代码验证,光靠手算,这种6×6的矩阵乘法得算到什么时候?而且很容易出错。
调试技巧:我写正运动学代码时,习惯在每个关节变换后打印一次中间结果。这样一旦最终结果不对,可以快速定位到是哪个关节的矩阵出了问题。另外,记得检查旋转矩阵的行列式是否为1——如果行列式不是1,说明矩阵不是纯旋转,肯定有地方算错了。
3.6 常见问题与避坑指南
- 坐标系方向搞反:z轴方向必须与关节运动方向一致。我见过有人把旋转关节的z轴方向搞反,结果末端轨迹完全镜像。
- D-H参数符号错误:α的正负号决定了相邻z轴的扭转方向。建议画个草图,用右手定则验证一下。
- 矩阵乘法顺序:一定是左乘。从基座到末端,依次左乘相邻变换矩阵。
- 角度单位:MATLAB和Python的三角函数默认用弧度。如果你习惯用角度,记得用np.deg2rad()转换。
我曾经踩过的坑:有一次在仿真中,末端位置始终偏了10mm。查了两天,最后发现是D-H参数表里a2写成了700mm,但实际机器人图纸上是650mm。所以,D-H参数一定要从机械图纸或实物测量中获取,不要凭经验估计。