2、MATLAB基础操作:矩阵运算、脚本与函数编写、绘图基础、数据结构(cell、struct)在机械臂建模中的应用
机械臂运动学与轨迹仿真的核心,本质上是大量坐标变换矩阵的连乘、位姿数据的组织与可视化。MATLAB 作为矩阵实验室(Matrix Laboratory),其一切运算均以数组为基本单元,这与机械臂的 D-H 参数表、齐次变换矩阵、雅可比矩阵等数学表达天然契合。本章将聚焦于四个关键技能:矩阵运算的矢量化思维、脚本与函数的工程化分工、绘图基础在轨迹可视化中的运用,以及 cell 与 struct 在管理多连杆参数时的数据建模方法。这些基础操作将直接服务于后续章节中正逆运动学求解与轨迹规划的代码实现。
上图展示了本章四个核心模块与机械臂建模之间的逻辑关系。矩阵运算是底层数学工具,脚本与函数是代码组织方式,绘图是结果验证手段,而 cell 与 struct 则是连接数据与算法的桥梁。下面我们将逐一深入剖析每个模块在机械臂建模中的具体应用。
2.1 矩阵运算:机械臂坐标变换的数学引擎
核心原理
机械臂运动学的本质是坐标系的平移与旋转。一个六自由度机械臂的末端位姿,可以通过六个齐次变换矩阵的连乘获得:T_06 = T_01 * T_12 * T_23 * T_34 * T_45 * T_56。在 MATLAB 中,矩阵乘法直接使用 * 运算符,而矩阵的逆(用于逆运动学求解)使用 inv() 或 pinv()。特别需要注意的是,MATLAB 中的 .* 表示逐元素乘法,而 * 是矩阵乘法,两者在机械臂雅可比矩阵计算中极易混淆。
关键矩阵操作清单:
- 齐次变换矩阵构建:
T = [R t; 0 0 0 1],其中 R 为 3×3 旋转矩阵,t 为 3×1 平移向量。 - 旋转矩阵生成:使用
rotx()、roty()、rotz()函数(需 Robotics Toolbox)或自行构建。 - 矩阵连乘:
T_total = T1 * T2 * T3,注意顺序不可交换。 - 矩阵求逆:
T_inv = inv(T),用于将末端位姿变换回基座坐标系。 - 逐元素运算:
theta_vec = [0:0.1:pi]配合sin(theta_vec)实现批量角度计算。
操作流程:从 D-H 参数到末端位姿
- 定义 D-H 参数表:
a = [0, 0.4, 0.3, 0, 0, 0],alpha = [0, -pi/2, 0, pi/2, -pi/2, 0],d = [0.2, 0, 0, 0.1, 0, 0.05]。 - 编写单连杆变换函数:
function T = dh_transform(theta, d, a, alpha)。 - 循环或矢量化计算:
T_06 = eye(4); for i=1:6; T_06 = T_06 * dh_transform(theta(i), d(i), a(i), alpha(i)); end。 - 提取末端位置:
pos = T_06(1:3, 4)。
案例拆解:六自由度机械臂正运动学批量计算
假设我们需要计算机械臂在关节角 theta = [0, pi/4, -pi/3, 0, pi/6, 0] 下的末端位置,并验证不同关节角序列下的轨迹。使用矢量化方法,我们可以一次性计算 100 个时间点的末端轨迹:
% 定义时间向量
t = linspace(0, 2, 100);
% 关节角轨迹(梯形速度规划)
theta1 = 0.5 * t.^2;
theta2 = pi/4 * sin(t);
% 预分配位置矩阵
pos_traj = zeros(3, 100);
for i = 1:100
T_06 = eye(4);
theta_vec = [theta1(i), theta2(i), -pi/3, 0, pi/6, 0];
for j = 1:6
T_06 = T_06 * dh_transform(theta_vec(j), d(j), a(j), alpha(j));
end
pos_traj(:, i) = T_06(1:3, 4);
end
% 绘制末端轨迹
plot3(pos_traj(1,:), pos_traj(2,:), pos_traj(3,:), 'b-', 'LineWidth', 1.5);
此案例展示了矩阵运算在批量计算中的高效性。通过预分配矩阵和循环复用,避免了重复计算,且代码结构清晰。
常见误区:初学者常将 inv(A)*B 与 A\B 混用。在机械臂逆运动学中,推荐使用左除 A\B,因为其数值稳定性更好,计算速度更快。此外,矩阵乘法顺序错误会导致末端位姿完全错误,务必遵循“从基座到末端”的连乘顺序。
2.2 脚本与函数:模块化机械臂代码的基石
核心原理
脚本(Script)适合一次性执行的流程,而函数(Function)则适合封装可复用的算法模块。在机械臂建模中,D-H 变换、逆运动学求解、轨迹规划等都应封装为函数,便于调试和复用。函数拥有独立的工作区,避免变量污染,且支持输入参数校验和默认值设置。
| 特性 | 脚本 | 函数 |
|---|---|---|
| 变量作用域 | 共享基础工作区 | 独立工作区 |
| 代码复用 | 低,需复制粘贴 | 高,可调用 |
| 调试难度 | 变量易冲突 | 隔离性好 |
| 典型应用 | 主程序流程控制 | DH变换、逆解、轨迹生成 |
操作流程:编写第一个机械臂函数
- 创建函数文件:
function T = dh_transform(theta, d, a, alpha)。 - 构建旋转矩阵 R 和平移向量 t。
- 组合齐次矩阵并返回。
- 在主脚本中调用:
T01 = dh_transform(theta1, d1, a1, alpha1)。
function T = dh_transform(theta, d, a, alpha)
% DH变换矩阵生成函数
% 输入:theta-关节角(rad), d-连杆偏距, a-连杆长度, alpha-连杆扭转角
% 输出:4x4齐次变换矩阵
Rz = [cos(theta), -sin(theta), 0, 0;
sin(theta), cos(theta), 0, 0;
0, 0, 1, 0;
0, 0, 0, 1];
Tz = [1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, d;
0, 0, 0, 1];
Tx = [1, 0, 0, a;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1];
Rx = [1, 0, 0, 0;
0, cos(alpha), -sin(alpha), 0;
0, sin(alpha), cos(alpha), 0;
0, 0, 0, 1];
T = Rz * Tz * Tx * Rx;
end
工程建议:在函数开头添加注释块,说明输入输出、单位、示例调用。使用 validateattributes 校验输入维度,避免因参数错误导致静默失败。对于多连杆机械臂,建议将 D-H 参数封装为一个结构体,作为函数输入。
2.3 绘图基础:让机械臂轨迹“看得见”
核心原理
MATLAB 绘图是验证运动学算法正确性的直观手段。对于机械臂,我们主要关注:末端轨迹的三维曲线(plot3)、关节角随时间的变化(plot)、工作空间散点图(scatter3)。绘图的核心原则是“先算后画”,即先完成所有数值计算,再一次性绘制,避免在循环中反复绘图导致性能下降。
操作流程:绘制末端轨迹与关节角曲线
- 生成时间向量和关节角序列。
- 调用正运动学函数计算末端位置。
- 使用
subplot分屏显示轨迹和关节角。 - 添加网格、标签、图例。
figure('Color', 'w');
subplot(1,2,1);
plot3(pos_traj(1,:), pos_traj(2,:), pos_traj(3,:), 'r-', 'LineWidth', 2);
grid on; xlabel('X (m)'); ylabel('Y (m)'); zlabel('Z (m)');
title('末端三维轨迹');
axis equal;
subplot(1,2,2);
plot(t, theta1, 'b-', t, theta2, 'r--', 'LineWidth', 1.5);
grid on; xlabel('时间 (s)'); ylabel('关节角 (rad)');
legend('关节1', '关节2'); title('关节角变化曲线');
案例拆解:工作空间可视化
通过蒙特卡洛方法随机生成 5000 组关节角,计算末端位置并绘制散点图,可以直观展示机械臂的可达工作空间。此案例综合运用了矩阵运算、循环和绘图:
num_samples = 5000;
theta_samples = rand(num_samples, 6) * [2*pi, pi, pi, 2*pi, pi, 2*pi] - [0, pi/2, pi/2, 0, pi/2, 0];
pos_samples = zeros(num_samples, 3);
for i = 1:num_samples
T_06 = eye(4);
for j = 1:6
T_06 = T_06 * dh_transform(theta_samples(i,j), d(j), a(j), alpha(j));
end
pos_samples(i,:) = T_06(1:3, 4)';
end
scatter3(pos_samples(:,1), pos_samples(:,2), pos_samples(:,3), 1, 'filled', 'MarkerFaceAlpha', 0.3);
axis equal; grid on;
性能陷阱:在循环中调用 plot 会创建大量图形对象,导致内存暴涨。正确做法是预先计算所有数据,循环结束后统一绘图。若必须实时显示,可使用 animatedline 或 drawnow limitrate。
2.4 数据结构:cell 与 struct 管理多连杆参数
核心原理
机械臂建模涉及大量异构数据:D-H 参数表(数值矩阵)、连杆名称(字符串)、传感器数据(混合类型)。struct 适合存储有名字段的参数集,而 cell 适合存储不同类型、不同大小的数据容器。在机械臂建模中,推荐使用 struct 数组管理多连杆参数,每个元素对应一个连杆。
| 数据结构 | 适用场景 | 访问方式 | 示例 |
|---|---|---|---|
| struct | 有命名字段的参数集 | robot.links(1).a | D-H 参数、关节限位 |
| cell | 混合类型、变长数据 | data{1} | 轨迹点集、不同维度的矩阵 |
| table | 表格化数据 | tbl.theta | 仿真日志、实验数据 |
操作流程:构建机械臂参数结构体
% 定义机械臂模型
robot.name = '六轴机械臂';
robot.n_joints = 6;
robot.links = struct('theta', 0, 'd', 0, 'a', 0, 'alpha', 0, 'offset', 0);
% 填充 D-H 参数
robot.links(1).a = 0; robot.links(1).alpha = 0; robot.links(1).d = 0.2;
robot.links(2).a = 0.4; robot.links(2).alpha = -pi/2; robot.links(2).d = 0;
robot.links(3).a = 0.3; robot.links(3).alpha = 0; robot.links(3).d = 0;
% 使用 cell 存储不同长度的轨迹数据
traj_data = {linspace(0, pi, 50), linspace(0, pi/2, 80), rand(3, 100)};
案例拆解:使用 struct 重构正运动学函数
将 D-H 参数封装为结构体后,正运动学函数可以更简洁,且易于扩展:
function T_06 = fkine(robot, theta_vec)
% 基于结构体的正运动学求解
T_06 = eye(4);
for i = 1:robot.n_joints
L = robot.links(i);
T_i = dh_transform(theta_vec(i) + L.offset, L.d, L.a, L.alpha);
T_06 = T_06 * T_i;
end
end
此设计使得添加新连杆只需修改结构体,无需改动函数逻辑。同时,cell 可用于存储多次仿真的结果,例如:results{1} = fkine(robot, theta1)。
最佳实践:在项目初期就定义好数据结构,避免后期重构。使用 disp(robot) 或 fieldnames(robot) 检查结构体内容。对于大型仿真,考虑使用 table 存储时间序列数据,便于统计分析。
总结与复盘
本章的核心在于建立“矩阵思维”和“数据建模意识”。矩阵运算的矢量化写法能显著提升代码效率,函数封装保证了算法的可维护性,绘图验证是发现算法错误的“照妖镜”,而合理的数据结构则是复杂系统设计的基石。在实际机械臂项目中,建议遵循以下流程:先用 struct 定义机器人模型,再用函数封装运动学算法,最后用脚本组织仿真流程并绘图验证。
本章要点回顾:
- 矩阵乘法
*与逐元素乘法.*的区别是机械臂雅可比计算中的高频错误点。 - 函数文件应包含完整的帮助注释和输入校验,提高代码健壮性。
- 绘图前先完成所有数值计算,避免循环内绘图。
- struct 适合存储有命名的参数,cell 适合存储异构数据集合。
- 使用
tic/toc分析代码性能,矢量化通常比循环快 10 倍以上。
掌握这些基础操作后,读者应能独立完成一个简单二连杆机械臂的正运动学建模与轨迹绘制。下一阶段,我们将把这些技能应用于 D-H 参数建模与正运动学求解的完整流程中。