aubo-i5机械臂(2)-逆运动学求解与Matlab实现
1. 从“指哪打哪”到“怎么打哪”:为什么我们需要逆运动学
上次我们聊了aubo-i5机械臂的正运动学,说白了就是“我告诉你每个关节转了多少度,你来算算它的‘手’(末端执行器)现在在哪儿”。这就像你开车,知道方向盘打了多少、油门踩了多深,就能算出车的位置。这个“正”的过程,是机械臂控制里最基础的一步。
但实际干活的时候,我们脑子里想的往往是反过来的。比如,我想让机械臂的“手”去抓取桌子上那个坐标为 (X, Y, Z) 的杯子,并且让“手”的姿态(是平着抓还是竖着抓)也摆好。这时候,我脑子里先有的是“手”的最终位置和姿态,然后我头疼的问题是:我那六个关节,每个到底该转多少度,才能让“手”精准地到达那个位置并摆好那个姿势?
这个过程,就是逆运动学。如果说正运动学是“因”到“果”的推导,那逆运动学就是从“果”反推“因”。对于aubo-i5这样的六轴机械臂,这个问题尤其有趣也尤其复杂。因为理论上,同一个末端位姿,可能对应多组、甚至无数组关节角度解(专业点叫“多解性”)。这就像你想从北京到上海,可以坐高铁、飞机、开车,甚至骑车(虽然不现实),路径不止一条。
我刚开始玩aubo-i5的时候,就卡在这儿了。正运动学的代码跑得挺顺,感觉自己已经掌控了机械臂。结果一到实际规划路径,想让机械臂画个圆或者走个复杂轨迹,立刻就傻眼——我知道“手”要经过的每一个点,但我不知道每个点对应的关节角度是多少。没有这些角度,我就没法给机械臂下命令。所以,逆运动学是机械臂从“能动”到“能干精细活”的关键一跳,是路径规划、轨迹跟踪、力控等高级应用的地基。
2. 解谜的钥匙:理解逆运动学的核心思路
逆运动学求解,听起来很高深,但我们可以把它想象成一个解多元方程的过程。还记得正运动学那个齐次变换矩阵 H 吗?它是一个4x4的矩阵,包含了末端执行器相对于基座标系的位置(3个值)和姿态(3个值,通常用旋转矩阵的9个元素表示,但有约束)。这个 H 矩阵,是六个关节角度 θ1, θ2, ..., θ6 的函数。
逆运动学问题就是:给定一个目标 H 矩阵,求解出满足 H = f(θ1, θ2, ..., θ6) 的关节角度 θ。
对于aubo-i5这种结构比较经典(腕部三个轴线相交于一点,这叫球形腕)的六轴机械臂,业内通常采用一种叫几何法与代数法相结合的方法来求解。这也是最主流、最有效的方法。它的核心思想是“分而治之”:
- 分离位置和姿态:利用球形腕的特点,我们可以先把问题拆开。机械臂末端的位置,实际上只由前三个关节(大臂、小臂等)决定。腕部关节(后三个关节)主要控制末端的姿态旋转。这样,我们可以先不管姿态,只根据末端想要到达的位置点,反解出前三个关节的角度。
- 求解位置子问题:这步是难点。我们需要根据末端执行器中心点(即腕部中心点)的目标坐标 (X, Y, Z),解出
θ1, θ2, θ3。这个过程常常需要利用机械臂的几何关系,比如在平面内投影,利用余弦定理求解三角形等。aubo-i5的连杆参数(DH参数)决定了这个几何模型。 - 求解姿态子问题:一旦知道了前三个关节的角度,前三个连杆的位姿就确定了。此时,腕部中心点的位置和从基座到腕部的旋转矩阵
R03就已知了。我们的目标末端姿态旋转矩阵是R。那么,腕部三个关节需要提供的旋转矩阵就是R36 = (R03)^T * R。对于这种结构,R36有标准的解算公式,可以相对容易地解出θ4, θ5, θ6。
听起来还是有点绕?我给你打个比方:你想用你的手臂(肩、肘、腕)去摸墙上的一个点,并且让手心朝某个方向。
- 第一步(位置):你大脑会先计算,肩关节和肘关节该怎么动,才能让你的手腕中心点贴到那个墙上的目标点。这个过程可能有多解:你可以伸直手够,也可以弯着手够。
- 第二步(姿态):手腕中心点到位后,你再转动手腕,让手心朝向目标方向。
Matlab的强大之处就在于,它能帮我们把复杂的几何关系和三角函数计算,用清晰、可调试的代码实现出来。我们不需要徒手去解那一堆可怕的方程,而是把求解逻辑步骤化。
3. 动手之前:准备好你的“地图”DH参数表
工欲善其事,必先利其器。解逆运动学,我们手里必须有一份准确的aubo-i5机械臂的DH参数表。这是描述机械臂几何结构的“地图”,正逆运动学都离不开它。这里我强调一下,必须使用和正运动学求解时完全同一套DH参数(包括是标准DH还是改进DH),否则你的正逆解会对不上,机械臂会“精神分裂”。
根据aubo-i5的官方技术文档和常见的建模方式,我们通常采用改进DH参数(Modified DH Parameters)。这里我给出我验证过的一份aubo-i5的改进DH参数表,你可以直接拿去用:
| 连杆 i | α_{i-1} (弧度) | a_{i-1} (米) | d_i (米) | θ_i (弧度,变量) |
|---|---|---|---|---|
| 1 | 0 | 0 | 0.1215 | θ1 |
| 2 | -π/2 | 0 | 0 | θ2 |
| 3 | 0 | 0.408 | 0 | θ3 |
| 4 | -π/2 | 0.376 | 0 | θ4 |
| 5 | π/2 | 0 | 0 | θ5 |
| 6 | -π/2 | 0 | 0.1025 | θ6 |
注意:这份表中的
θ_i是关节变量。d_i中的 0.1215 和 0.1025 分别是基座到关节1的偏移,以及腕部中心到末端法兰盘的长度(通常计入连杆6)。a_{i-1}中的 0.408 和 0.376 分别对应大臂和小臂的长度。这些数值是核心,务必核对准确。
在Matlab里,我们可以先把这个表定义好:
% aubo_i5 改进DH参数表 [alpha, a, d, theta_offset]
% 注意:这里的theta列存放的是关节零位偏移,实际关节角 q = theta_offset + 输入关节变量
aubo_DH = [
0, 0, 0.1215, 0; % 连杆1
-pi/2, 0, 0, 0; % 连杆2
0, 0.408, 0, 0; % 连杆3
-pi/2, 0.376, 0, 0; % 连杆4
pi/2, 0, 0, 0; % 连杆5
-pi/2, 0, 0.1025, 0; % 连杆6
];
这里有个关键点需要理解:DH表里的 θ 这一列,我通常用来存放关节的零位偏移。什么意思呢?机械臂的“零位”姿态是厂家定义的一个初始姿态,此时各个关节的编码器读数不一定是0。我们在做正运动学时,输入的关节角度 q 应该是 (DH表中的theta_offset)+ (你想要的关节转动角度)。在逆运动学求解时,我们求解出的 θ 也是相对于这个DH模型坐标系的角度,最终发给实际机械臂的控制指令,可能还需要减去这个零位偏移。这一点非常容易混淆,是很多初学者调试不通的“坑”。我在代码里会通过注释特别强调。
4. 庖丁解牛:一步步实现aubo-i5的逆运动学求解
好了,地图有了,思路也清楚了,我们现在就用Matlab把逆运动学算法实现出来。我会把整个过程拆解成几个关键函数,并配上详细的注释。
4.1 第一步:定义目标位姿
首先,我们要明确想让机械臂末端达到什么样的位置和姿态。在Matlab中,我们通常用一个4x4的齐次变换矩阵 T_target 来表示。
% 示例:定义目标末端位姿
% 假设我们希望末端执行器到达位置 [0.5, 0.2, 0.3] 米处
desired_position = [0.5; 0.2; 0.3]; % 单位:米
% 定义目标姿态:这里用一个绕Z轴旋转45度的旋转矩阵为例
desired_rotation = rotz(45); % rotz是Matlab Robotics Toolbox的函数,生成绕Z轴旋转的矩阵
% 如果你没有Robotics Toolbox,可以手动定义:
% angle = deg2rad(45);
% desired_rotation = [cos(angle), -sin(angle), 0;
% sin(angle), cos(angle), 0;
% 0, 0, 1];
% 组合成齐次变换矩阵
T_target = [desired_rotation, desired_position;
0, 0, 0, 1];
4.2 第二步:求解关节1的角度(θ1)
对于aubo-i5这类结构,关节1是绕基座Z轴旋转的。从几何上看,末端腕部中心点 P_wrist(注意,不是末端点,是腕部中心,即连杆4、5、6的交点)在XY平面上的投影点,决定了关节1需要转多少度才能让大臂对准这个方向。
首先,我们需要从目标位姿中提取腕部中心点坐标。因为我们的DH参数中,d6=0.1025 是沿着连杆6的Z轴方向(腕部指向末端)的偏移。所以腕部中心点 P_wrist = P_effector - d6 * (R_target的第3列向量)。R_target 是 T_target 的旋转矩阵部分。
function theta1_solutions = solve_theta1(T_target, d6)
% 从目标变换矩阵提取位置和姿态
P_eff = T_target(1:3, 4); % 末端执行器位置
R_eff = T_target(1:3, 1:3); % 末端执行器旋转矩阵
% 计算腕部中心位置 (Wrist Center)
% R_eff(:,3) 是末端坐标系Z轴的单位向量(指向末端前进方向)
P_wrist = P_eff - d6 * R_eff(:,3);
x = P_wrist(1);
y = P_wrist(2);
% 关节1的解:atan2(y, x) 和 atan2(y, x) + pi (或 - pi)
% 通常有两个可能解,相差180度,对应机械臂“左手”和“右手”构型。
theta1_1 = atan2(y, x);
theta1_2 = atan2(y, x) + pi; % 另一种构型
% 将角度归一化到 [-pi, pi] 区间,方便后续处理
theta1_solutions = [wrapToPi(theta1_1), wrapToPi(theta1_2)];
end
wrapToPi 是一个自定义函数,将角度约束在[-π, π]之间。Matlab Robotics Toolbox里有同名函数,没有的话可以自己写一个简单的:angle = mod(angle+pi, 2*pi) - pi;。
4.3 第三步:求解关节2和关节3的角度(θ2, θ3)
知道了 θ1 和腕部中心点 P_wrist 的坐标后,我们可以将问题简化到由关节1、2、3所在的平面内。这本质上是一个平面二连杆机械臂(关节2和关节3)的逆运动学问题,已知连杆长度(a2, a3)和末端点(腕部中心投影到该平面的坐标),求两个关节角。
这里需要用到余弦定理。我们计算腕部中心点相对于关节2所在坐标系的位置。然后根据几何关系,可以解出 θ3 和 θ2。
function [theta2_sol, theta3_sol] = solve_theta2_theta3(P_wrist, theta1, a2, a3)
% 将腕部中心坐标转换到关节1坐标系(绕Z轴旋转-theta1)
R_z_inv = rotz(-theta1);
P_1 = R_z_inv * P_wrist; % 现在P_1的x分量就是关节2到腕部中心的平面距离投影
% 提取在关节1坐标系下,腕部中心在X-Z平面上的坐标(忽略Y,因为关节1已对齐)
x = P_1(1);
z = P_1(3) - d1; % d1是DH表中连杆1的d参数,即基座高度偏移
% 计算腕部中心到关节2原点的平面距离
r = sqrt(x^2 + z^2);
% 使用余弦定理求解 theta3
% cos(theta3) = (a2^2 + a3^2 - r^2) / (2*a2*a3)
cos_theta3 = (a2^2 + a3^2 - r^2) / (2 * a2 * a3);
% 由于数值计算误差,需要将cos值钳制在[-1,1]之间
cos_theta3 = max(-1, min(1, cos_theta3));
theta3_1 = acos(cos_theta3); % 肘部“向上”解
theta3_2 = -theta3_1; % 肘部“向下”解
% 对应每个theta3,求解theta2
% theta2 = atan2(z, x) - atan2(a3*sin(theta3), a2 + a3*cos(theta3))
for i = 1:2
theta3 = [theta3_1, theta3_2](i);
phi = atan2(z, x);
psi = atan2(a3 * sin(theta3), a2 + a3 * cos(theta3));
theta2_sol(i) = phi - psi;
end
% 同样,将角度归一化
theta2_sol = wrapToPi(theta2_sol);
theta3_sol = [theta3_1, theta3_2];
end
这里 a2 和 a3 对应DH表中的 a 参数(分别是0.408和0.376)。d1 是基座偏移(0.1215)。这段代码会为每一组 (θ1, 肘部构型) 产生对应的 θ2 和 θ3。
4.4 第四步:求解腕部关节角度(θ4, θ5, θ6)
当前三个关节的角度确定后,从基座到关节3的变换矩阵 T03 就可以通过正运动学计算出来(复用我们上一篇文章写的正运动学函数)。那么,从关节3到末端(关节6)所需的变换矩阵就是 T36 = inv(T03) * T_target。
对于球形腕,T36 的旋转矩阵 R36 具有特定的形式,我们可以从中直接提取出 θ4, θ5, θ6。常用的方法是使用 atan2 函数来求解,避免象限判断错误。
function [theta4, theta5, theta6] = solve_spherical_wrist(R36)
% R36 是从连杆3坐标系到连杆6坐标系的旋转矩阵
% 对于常用的Z-Y-Z欧拉角(或类似)解算
% 注意:解不唯一,通常有两组解(“腕部翻转”)
% 方法:从旋转矩阵元素中提取角度
% 假设机械臂后三轴符合常见的配置,我们可以用以下公式:
% 注意:这里需要根据你的DH模型和坐标系定义来调整符号和公式顺序
% 以下是一种常见情况的推导:
% 求解 theta5
theta5_1 = atan2(sqrt(R36(1,3)^2 + R36(2,3)^2), R36(3,3));
theta5_2 = -theta5_1;
% 对应每个theta5求解theta4和theta6
for i = 1:2
th5 = [theta5_1, theta5_2](i);
if abs(sin(th5)) > 1e-6 % 避免奇异点(theta5接近0或pi)
th4 = atan2(R36(2,3)/sin(th5), R36(1,3)/sin(th5));
th6 = atan2(R36(3,2)/sin(th5), -R36(3,1)/sin(th5));
else
% 处于奇异位形,theta4和theta6不独立,通常设定一个为默认值,求解另一个
th4 = 0; % 或当前角度
th6 = atan2(R36(1,2), R36(1,1)) - th4;
end
theta4(i) = wrapToPi(th4);
theta5(i) = wrapToPi(th5);
theta6(i) = wrapToPi(th6);
end
end
重要提示:腕部解算公式
solve_spherical_wrist是与你的DH模型定义强相关的。上面的代码是一个示例框架。你必须根据自己从T36中推导出的确切公式来编写。推导过程需要耐心,建议在纸上画一下坐标系,根据R = rotz(θ4)*roty(θ5)*rotz(θ6)(或你的顺序)的矩阵乘法,反解出每个角度的表达式。这是逆运动学编程中最需要细心和验证的部分。
4.5 第五步:整合与多解处理
现在我们把所有部分组合起来。对于aubo-i5,理论上最多有 8组解(2种肩部构型 × 2种肘部构型 × 2种腕部构型)。我们需要一个主函数来遍历这些组合,并调用上述子函数。
function all_solutions = aubo_ikine(T_target, DH_table)
% 输入:目标齐次变换矩阵 T_target (4x4)
% 输入:改进DH参数表 DH_table (6x4)
% 输出:所有可能的关节角解 (N x 6),N <= 8
% 提取DH参数
d = DH_table(:, 3); % d参数列
a = DH_table(:, 2); % a参数列
d1 = d(1); d4 = d(4); d6 = d(6); % 常用的d参数
a2 = a(3); a3 = a(4); % 常用的a参数 (注意索引,DH表a是a_{i-1})
% 1. 求解 theta1
theta1_options = solve_theta1(T_target, d6);
all_solutions = []; % 存储所有有效解
% 2. 对每个theta1解,求解theta2, theta3
for t1 = theta1_options
P_wrist = ... % 计算腕部中心(同上)
[t2_opts, t3_opts] = solve_theta2_theta3(P_wrist, t1, a2, a3, d1);
% 3. 对每组(t1, t2, t3),计算T03,并求解腕部关节
for idx = 1:length(t2_opts)
t2 = t2_opts(idx);
t3 = t3_opts(idx);
% 计算正运动学 T03 (使用前三个DH参数)
T03 = ... % 调用你的正运动学函数,输入前三个关节角度
% 计算 T36
T36 = inv(T03) * T_target;
R36 = T36(1:3, 1:3);
% 求解腕部角度
[t4_opts, t5_opts, t6_opts] = solve_spherical_wrist(R36);
% 4. 组合所有解,并检查是否在关节限位内
for w_idx = 1:length(t4_opts)
one_solution = [t1, t2, t3, t4_opts(w_idx), t5_opts(w_idx), t6_opts(w_idx)];
% 可选:检查关节角度是否在物理限位内
if check_joint_limits(one_solution)
all_solutions = [all_solutions; one_solution];
end
end
end
end
% 如果无解,返回空矩阵
if isempty(all_solutions)
warning('未找到逆运动学可行解!');
end
end
这个主函数 aubo_ikine 就是我们的核心。它系统地组合了前面各个步骤的解,并最终输出所有在物理上可行的关节角度集合。在实际使用中,我们可能会从这多组解中,根据“最接近当前姿态”、“能量最小”、“避开奇异点”等原则,选择最优的一组发给机械臂。
5. 验证与调试:让你的算法真正可靠
代码写完了,千万别以为就大功告成了。逆运动学算法的调试,是真正考验耐心和细致的时候。下面是我总结的验证“三部曲”:
第一步:闭环验证(最重要)
这是最直接的验证方法。你随机生成(或在关节限位内选择)一组关节角度 q_test。
- 用你的正运动学函数,根据
q_test计算出末端位姿T_computed。 - 将这个
T_computed作为目标,输入给你的逆运动学函数aubo_ikine。 - 在逆解得到的多组解中,寻找与原始
q_test最接近的一组解(注意角度周期性,相差2π的应视为相同)。 - 比较两者的差异。理想情况下,误差应该在
1e-6弧度或更小的量级。如果误差很大,说明你的逆解算法有bug。
第二步:可视化辅助
在Matlab中,你可以用简单的线条画出机械臂的连杆。对于每一组逆解,都用正运动学算出每个关节的位置,然后用 plot3 函数画出来。直观地看看,机械臂是不是真的以你期望的构型到达了目标点。这能帮你快速发现是肩部、肘部还是腕部的解选错了。
第三步:奇异点测试
机械臂在某些特殊位形下(比如手臂完全伸直,θ5 接近0),会失去一个或多个方向的自由度,这就是奇异点。在奇异点附近,逆运动学求解可能会数值不稳定(出现极大值或NaN)。你需要测试这些边界情况,确保你的代码能稳健处理(例如,在 solve_spherical_wrist 函数中,我们对 sin(theta5) 接近0的情况做了特殊处理)。
我自己的调试过程就踩过不少坑。最常见的是符号错误:DH参数中角度的正负号、atan2 函数的参数顺序、腕部解算公式的符号,任何一个错了,解出来的姿态都会莫名其妙。我的经验是,每写一个子函数,就立刻用几个简单例子测试一下。比如测试 solve_theta1 时,手动设定一个简单的 P_wrist,看看算出来的角度是不是你几何直观上期望的角度。
6. 从理论到实战:在路径规划中的应用
逆运动学求解本身不是目的,它是个工具。它的最大用武之地是笛卡尔空间路径规划。举个例子,你想让aubo-i5的末端画一个圆,或者走一条直线。
- 路径点生成:首先,你在笛卡尔空间(就是三维空间)里规划好路径。比如画圆,你可以用参数方程生成一系列圆上的点
[X(k), Y(k), Z(k)],以及每个点处末端的姿态(比如始终保持垂直向下)。 - 逆解计算:对于路径上的每一个目标位姿
T_target(k),调用aubo_ikine函数,从得到的多组逆解中,根据“与上一时刻关节角度最接近”的原则选择一组,得到对应的关节角度序列q(k, :)。这个过程叫做逆解选择,它能保证机械臂运动连续、平滑,不会在两种构型间突然跳跃。 - 关节空间插值:得到了离散的关节角度序列
q(k, :)后,你可以在关节空间进行插值(比如三次样条插值),生成更密集、更平滑的关节角度指令,最后发送给机械臂控制器执行。
这样,你就实现了让机械臂在三维空间中沿着预定轨迹运动。没有逆运动学,这一切都无法实现。我在做一个简单的拾放demo时,就是先示教了抓取点和放置点,然后在这两点间进行直线插值,对每一个插值点求逆解,从而让机械臂平稳地直线运动过去,效果非常棒。
最后,我想说,逆运动学的实现确实比正运动学复杂不少,会涉及到更多的几何和三角计算,调试也需要耐心。但一旦你亲手把它调通,看到机械臂精准地按照你的空间指令运动时,那种成就感是无与伦比的。这份Matlab代码不仅仅是一个算法实现,更是你理解和掌控机械臂空间运动能力的桥梁。建议你对照着aubo-i5的模型,一步步推导,一步步编码测试,遇到问题就画图分析,这才是学习机器人学最扎实的方式。
更多推荐
所有评论(0)