第10章:正运动学求解——DH参数推导6轴机器人运动学方程
各位同学,今天我们来啃一块硬骨头——正运动学。说白了,就是给你6个关节角度,算出机器人末端到底在哪儿、朝哪个方向。这玩意儿是机器人控制的基石,你后面做轨迹规划、插补、避障,全得靠它。
我个人习惯,拿到一个机器人,第一件事就是建DH坐标系。为什么?因为DH参数是工业界通用的语言,你换台机器人,只要参数表一给,代码几乎不用改。我当年刚入行时,有个项目需要把ABB的机器人换成KUKA的,就是因为用了DH建模,两天就搞定了运动学移植。
10.1 DH参数建模——先搭骨架
先说说DH参数是哪四个:
- θ(关节角):绕Z轴旋转的角度
- d(连杆偏距):沿Z轴平移的距离
- a(连杆长度):沿X轴平移的距离
- α(连杆扭角):绕X轴旋转的角度
你想想看,这四个参数其实就描述了两个相邻坐标系之间的变换。从坐标系{i-1}到坐标系{i},先绕Z轴转θ,再沿Z轴移d,然后沿X轴移a,最后绕X轴转α。就这么简单。
核心公式:相邻连杆变换矩阵
T_i = Rot(z, θ_i) * Trans(z, d_i) * Trans(x, a_i) * Rot(x, α_i)
展开成4x4矩阵就是:
T_i = [cosθ_i -sinθ_i*cosα_i sinθ_i*sinα_i a_i*cosθ_i
sinθ_i cosθ_i*cosα_i -cosθ_i*sinα_i a_i*sinθ_i
0 sinα_i cosα_i d_i
0 0 0 1 ]
嗯,这里要注意:不同版本的DH定义略有差异,我用的这种是标准DH(Standard DH)。还有一种叫改进DH(Modified DH),区别在于坐标系附着的位置不同。我个人建议初学者先死磕标准DH,搞透了再去看别的。
10.2 6轴机器人DH参数表——以典型工业机器人为例
我拿一台典型的6轴串联机器人来举例。这种机器人的结构大致是:
- J1:腰部旋转(绕Z0)
- J2:大臂旋转(绕Z1)
- J3:小臂旋转(绕Z2)
- J4:腕部旋转1(绕Z3)
- J5:腕部旋转2(绕Z4)
- J6:腕部旋转3(绕Z5)
对应的DH参数表如下(单位:mm和度):
| 连杆 i | θ_i (关节角) | d_i (mm) | a_i (mm) | α_i (度) |
|---|---|---|---|---|
| 1 | θ1 | d1 = 400 | a1 = 150 | α1 = -90 |
| 2 | θ2 | d2 = 0 | a2 = 600 | α2 = 0 |
| 3 | θ3 | d3 = 0 | a3 = 200 | α3 = 90 |
| 4 | θ4 | d4 = 600 | a4 = 0 | α4 = -90 |
| 5 | θ5 | d5 = 0 | a5 = 0 | α5 = 90 |
| 6 | θ6 | d6 = 100 | a6 = 0 | α6 = 0 |
避坑指南:我曾经在给一个协作机器人建模时,把α3的符号搞反了,结果仿真出来的末端位置完全不对。后来花了整整一个下午才排查出来。所以建议你建完表后,先拿一个简单的姿态(比如所有关节归零)手动算一下末端位置,验证一下。
10.3 正运动学方程推导——连乘就完事了
有了DH参数表,正运动学就是简单的矩阵连乘:
T_06 = T_01 * T_12 * T_23 * T_34 * T_45 * T_56
其中T_06就是机器人基坐标系到末端坐标系的变换矩阵。它包含了位置信息(最后一列的前三个元素)和姿态信息(左上角3x3旋转矩阵)。
写成数学形式:
T_06 = [R_06 P_06
0 0 0 1 ]
这里R_06是3x3旋转矩阵,表示末端的朝向;P_06是3x1位置向量,表示末端在基坐标系下的坐标。
你想想看,这个矩阵里每个元素都是θ1到θ6的三角函数组合。实际展开后,表达式会非常长。我当年手算过一次,写了整整两页A4纸。现在嘛,都交给计算机了。
10.4 MATLAB验证——让代码说话
光说不练假把式。咱们用MATLAB来验证一下。我习惯用符号计算,这样能直观看到表达式。
% 正运动学求解 - MATLAB版本
clear; clc;
% 定义符号变量
syms theta1 theta2 theta3 theta4 theta5 theta6 real
% DH参数(单位:mm, rad)
d1 = 400; a1 = 150; alpha1 = -pi/2;
d2 = 0; a2 = 600; alpha2 = 0;
d3 = 0; a3 = 200; alpha3 = pi/2;
d4 = 600; a4 = 0; alpha4 = -pi/2;
d5 = 0; a5 = 0; alpha5 = pi/2;
d6 = 100; a6 = 0; alpha6 = 0;
% 构建DH变换矩阵函数
function T = dh_transform(theta, d, a, alpha)
T = [cos(theta), -sin(theta)*cos(alpha), sin(theta)*sin(alpha), a*cos(theta);
sin(theta), cos(theta)*cos(alpha), -cos(theta)*sin(alpha), a*sin(theta);
0, sin(alpha), cos(alpha), d;
0, 0, 0, 1];
end
% 计算各连杆变换矩阵
T01 = dh_transform(theta1, d1, a1, alpha1);
T12 = dh_transform(theta2, d2, a2, alpha2);
T23 = dh_transform(theta3, d3, a3, alpha3);
T34 = dh_transform(theta4, d4, a4, alpha4);
T45 = dh_transform(theta5, d5, a5, alpha5);
T56 = dh_transform(theta6, d6, a6, alpha6);
% 正运动学连乘
T06 = T01 * T12 * T23 * T34 * T45 * T56;
% 简化表达式
T06_simplified = simplify(T06);
% 显示结果
disp('正运动学变换矩阵 T06:');
disp(T06_simplified);
% 数值验证:取一组关节角
theta_vals = [0, pi/4, -pi/4, 0, pi/6, 0];
T06_num = subs(T06, {theta1,theta2,theta3,theta4,theta5,theta6}, theta_vals);
T06_num = double(T06_num);
disp('数值验证结果:');
disp(T06_num);
% 提取位置和姿态
position = T06_num(1:3, 4);
rotation = T06_num(1:3, 1:3);
disp(['末端位置: x=', num2str(position(1)), ', y=', num2str(position(2)), ', z=', num2str(position(3))]);
运行结果示例:
当关节角为 [0, 45°, -45°, 0, 30°, 0] 时:
末端位置: x = 150.0, y = 424.3, z = 800.0
这个结果你可以手算验证一下,基本是对的。
10.5 Python验证——换个口味
有些同学可能更习惯Python。没问题,咱们也用Python撸一遍。我推荐用numpy和sympy,跟MATLAB思路一模一样。
# 正运动学求解 - Python版本
import numpy as np
import sympy as sp
# 定义符号变量
theta1, theta2, theta3, theta4, theta5, theta6 = sp.symbols('theta1:7')
# DH参数
dh_params = [
(theta1, 400, 150, -np.pi/2),
(theta2, 0, 600, 0),
(theta3, 0, 200, np.pi/2),
(theta4, 600, 0, -np.pi/2),
(theta5, 0, 0, np.pi/2),
(theta6, 100, 0, 0)
]
def dh_transform(theta, d, a, alpha):
"""构建DH变换矩阵"""
return sp.Matrix([
[sp.cos(theta), -sp.sin(theta)*sp.cos(alpha), sp.sin(theta)*sp.sin(alpha), a*sp.cos(theta)],
[sp.sin(theta), sp.cos(theta)*sp.cos(alpha), -sp.cos(theta)*sp.sin(alpha), a*sp.sin(theta)],
[0, sp.sin(alpha), sp.cos(alpha), d],
[0, 0, 0, 1]
])
# 计算各连杆变换矩阵
T = [dh_transform(*params) for params in dh_params]
# 正运动学连乘
T06 = sp.eye(4)
for i in range(6):
T06 = T06 * T[i]
# 简化
T06_simplified = sp.simplify(T06)
print("正运动学变换矩阵 T06:")
sp.pprint(T06_simplified)
# 数值验证
theta_vals = {theta1:0, theta2:np.pi/4, theta3:-np.pi/4,
theta4:0, theta5:np.pi/6, theta6:0}
T06_num = T06_simplified.subs(theta_vals).evalf()
T06_num = np.array(T06_num).astype(np.float64)
print("\n数值验证结果:")
print(T06_num)
position = T06_num[:3, 3]
print(f"\n末端位置: x={position[0]:.1f}, y={position[1]:.1f}, z={position[2]:.1f}")
个人经验:我建议你在写代码时,把DH变换矩阵封装成一个函数。这样不仅代码整洁,而且万一要换DH定义(比如改成改进DH),只需要改这一个函数就行。我在实际项目中就是这么干的,后来客户要求换机器人型号,我改了参数表就完事了。
10.6 验证与调试——别让bug坑了你
代码写完了,怎么知道对不对?我分享几个我常用的验证方法:
- 零位验证:所有关节角为0时,末端应该在什么位置?根据你的机器人结构,应该能大致判断出来。
- 单关节运动:只动一个关节,看末端轨迹是不是一个圆弧。比如只动J1,末端应该在水平面画圆。
- 与已知结果对比:网上有很多标准机器人的DH参数和正解结果,拿来对比一下。
- 逆运动学反推:先正解算出一个位姿,再用逆解算回去,看关节角是否一致。
警告:千万不要以为代码跑通了就万事大吉。我曾经在一个项目中,正运动学算出来的位置看起来没问题,但姿态矩阵的符号反了。结果机器人一运动,末端工具直接撞上了工件。所以一定要做多组数值验证,特别是姿态部分。
10.7 小结——正运动学其实不难
说白了,正运动学就是四个步骤:
- 建DH坐标系,确定参数表
- 写出每个连杆的变换矩阵
- 连乘得到末端位姿
- 数值验证确保正确
你只要把这一步搞扎实了,后面学逆运动学、速度雅可比、动力学,都会轻松很多。我当年带团队时,要求每个新人都必须能手算一遍正运动学,再用代码验证。虽然过程痛苦,但基础打牢了,后面就顺了。
下一章咱们聊逆运动学,那才是真正考验数学功底的地方。做好准备!