简介:面向机器人控制与机器学习交叉领域学习者的MATLAB源码资源,聚焦二自由度机械臂的神经网络控制与运动学分析,可应用于自动化生产线中的运动规划与轨迹跟踪场景。压缩包内仅包含1个m文件,整体体积约3KB,属于轻量级示例代码,便于直接阅读和运行调试。该脚本演示了如何通过神经网络建立关节角度与末端位置之间的映射关系,涵盖数据准备、网络架构选择、训练及控制应用等关键环节,并提供了基于反向传播或Levenberg-Marquardt算法的可参考实现。目前已有1522人浏览/学习,尤其适合自动化专业学生及从事机械臂智能控制研究的工程师作为入门参考,借助该代码可快速复现仿真实验,也可为其他复杂系统的神经网络控制提供迁移思路。
1. 二自由度机械臂的运动学分析与神经网络控制到底在解决什么问题
做两自由度机械臂轨迹跟踪时,最常见的做法是每个控制周期先求逆运动学,把笛卡尔空间的目标点解成关节角,再交给底层位置环。解析解算得快,但工作空间边界、多解选择、奇异位形附近数值不稳定,都会让轨迹断点。神经网络控制的思路是换一个角度:把“从末端坐标到关节角度”的映射直接学习出来,控制周期内只做一次前向推理,避免在线解方程的不确定性。两连杆机械臂的逆运动学有解析解,但它非常适合作为理解神经网络控制的入门对象,原因在于数据生成简单、物理边界直观、结果可以用正运动学反推验证。本文顺着两自由度机械臂运动学分析这条主线,给出在 MATLAB 里从建模、数据生成到神经网络训练和轨迹跟踪的完整落地路径,同时把训练集覆盖、损失函数选择、前馈反馈混合控制这些实际工程中容易踩的坑说清楚。
2. 两自由度机械臂运动学分析:DH建模、正逆解与训练数据集生成
2.1 用DH参数建立两连杆平面臂的正运动学模型
机械臂运动学分析的第一步是确定连杆坐标系。两自由度平面机械臂没有偏置距离,标准 DH 参数只有两行,表里每一项都对应四个要素:杆件扭转角、连杆长度、偏距和关节角。设两连杆长度分别为 l1=0.4m、l2=0.3m,两个关节角 q1、q2 的单位全部用弧度,DH 参数表如下:
| 关节 | a_{i-1} / m | α_{i-1} / rad | d_i / m | θ_i |
|---|---|---|---|---|
| 1 | 0 | 0 | 0 | q1(绕 Z0 轴) |
| 2 | 0.4 | 0 | 0 | q2(绕 Z1 轴) |
正运动学是每个机械臂控制里最不该手滑的部分,因为它直接决定后面数据集的正确性。常见的做法是直接用齐次变换矩阵连乘,我一般把两段变换写在一个函数里返回末端位置,方便后面批量调用:
function [x, y, T_02] = fk_2dof(q1, q2, l1, l2) % 两自由度平面机械臂正运动学 T_01 = [cos(q1) -sin(q1) 0 l1*cos(q1); sin(q1) cos(q1) 0 l1*sin(q1); 0 0 1 0; 0 0 0 1]; T_12 = [cos(q2) -sin(q2) 0 l2*cos(q2); sin(q2) cos(q2) 0 l2*sin(q2); 0 0 1 0; 0 0 0 1]; T_02 = T_01 * T_12; x = T_02(1, 4); y = T_02(2, 4); end代码里第二次变换的第三列没有出现第二根杆的投影,因为第二位姿矩阵只描述了末端相对于第二关节的平移。把变量定义成输出形式,可以在命令行直接用[x,y] = fk_2dof(pi/4, -pi/4, 0.4, 0.3)验证结果。机械臂从基准位形旋转,末端坐标公式等价于x = l1*cos(q1) + l2*cos(q1+q2)与y = l1*sin(q1) + l2*sin(q1+q2),这是后续所有逆解和轨迹代码的地基。
2.2 逆运动学解析解与多解选择策略
逆解是控制用得最多的部分。两连杆机械臂的逆解有明确的几何解析式,不需要数值迭代。先由末端位置算第二关节的余弦值,再用 atan2 区分两种臂型。判断的核心公式是余弦定理:
function [q1, q2, sol] = ik_2dof(xd, yd, l1, l2, arm_choice) r2 = xd^2 + yd^2; if r2 > (l1+l2)^2 || r2 < (l1-l2)^2 error('目标点不可达'); end cos_q2 = (r2 - l1^2 - l2^2) / (2*l1*l2); cos_q2 = max(-1, min(1, cos_q2)); % 数值裁剪 q2_pos = atan2(sqrt(1 - cos_q2^2), cos_q2); q2_neg = -q2_pos; % elbow-up 与 elbow-down k1 = l1 + l2*cos(q2_pos); k2 = l2*sin(q2_pos); q1_pos = atan2(yd, xd) - atan2(k2, k1); k1 = l1 + l2*cos(q2_neg); k2 = l2*sin(q2_neg); q1_neg = atan2(yd, xd) - atan2(k2, k1); if strcmp(arm_choice, 'up') q1 = q1_pos; q2 = q2_pos; sol = 'up'; else q1 = q1_neg; q2 = q2_neg; sol = 'down'; end end参数arm_choice是关系到一致性的大坑。神经网络控制预测的是关节角,但如果训练数据里同一个末端点既出现 elbow-up 又出现 elbow-down,网络会学出一个“平均解”,这个平均解大概率不在两者之间连通,导致预测出的角度对应错误臂型。所以生成数据集时,固定一个臂型选择策略,整个训练集只能用同一种解。这样输出的关节角保证了轨迹连续,不因多解而跳变。
2.3 生成神经网络训练数据集的关键步骤
有了逆解函数,数据生成本身很简单,但边界处理直接决定训练质量。我常用的数据集生成方式是:在关节空间均匀采样,而不是在笛卡尔空间均匀采样,然后用正运动学转换,再过滤掉不可达和奇异性较强的样本:
rng(42); N = 20000; l1 = 0.4; l2 = 0.3; q1_all = (20 + 120*rand(N,1)) * pi/180; q2_all = (30 + 120*rand(N,1)) * pi/180; % 关节限位: q1 in [20,140] deg, q2 in [30,150] deg X = []; Y = []; for i = 1:N q1 = q1_all(i); q2 = q2_all(i); [~, x, y] = fk_2dof(q1, q2, l1, l2); if sqrt(x^2+y^2) < abs(l1-l2) + 0.05 continue; end X = [X; x, y]; Y = [Y; q1, q2]; end两个要点在这段代码里体现得很清楚。第一,滤掉接近内部不可达边界的样本,也就是末端接近原点或距离小于两杆长度差的地方,这些区域逆解对输入扰动极度敏感,网络训练会把样本推到错误方向。第二,代码里用的是循环拼接,虽然比向量化慢,但清晰,数据量只有两万条时完全够快。这里没有对 x、y 做单位归一化,因为 MATLAB 的feedforwardnet训练时会在内部自动处理输入输出归一化,这项处理由mapminmax对象自动完成,推理时也会反向恢复,读者不必手工归一化输入。
3. 神经网络控制方案选型与BP网络结构设计
3.1 为什么前馈 BP 网络是两连杆逆解的最优选择
两自由度机械臂逆解是一个连续的非线性映射,输入是二维坐标,输出是二维角度。这类回归问题用全连接前馈 BP 网络就足够,用不到循环神经网络,也不需要图神经网络这类为序列或拓扑结构设计的网络结构。卷积网络的优势在局部特征提取,在这里反而浪费感受野。常见的选择是两层或三层隐藏层的全连接网络,激活函数用 tansig,输出层用线性激活,损失函数用均方误差。BP 训练本质上是最小化预测关节角与真实关节角之间的误差。在控制回路里,网络推理一次只做几次矩阵乘法,延时比在线优化小一个数量级,这是神经网络控制相对传统在线逆解的主要优势。
3.2 训练数据划分与 BP 网络的 MATLAB 实现
MATLAB 深度学习工具箱里有两个入口,一个fitnet,一个feedforwardnet。fitnet专为函数拟合设计,默认输出层是线性激活函数,适合回归任务;feedforwardnet更偏通用。做机械臂逆解我建议用前者,代码更少,默认配置也更不容易踩输出饱和的坑:
X = X'; Y = Y'; net = fitnet([12 12], 'trainscg'); net.trainParam.epochs = 2000; net.trainParam.goal = 1e-5; net.divideParam.trainRatio = 0.8; net.divideParam.valRatio = 0.15; net.divideParam.testRatio = 0.05; [net, tr] = train(net, X, Y);代码里的[12 12]是两个隐藏层各 12 个神经元,这个容量对两连杆逆解已经足够。神经元数量过多并不会显著提升精度,反而增加过拟合风险,尤其是在样本空间覆盖不均匀时。trainscg是无记忆的共轭梯度法,内存占用小,适合中小规模回归问题。训练结束后,把网络保存成net变量,推理调用y_pred = net(x_new)即可。需要注意fitnet自动完成数据划分,divideParam.valRatio取 0.15 是训练过程中用于早停的验证集比例,验证集不参与权重更新但从第 200 轮开始指导训练终止,这层逻辑和工程里做留出验证是同一个道理。
3.3 训练集覆盖范围与一个容易被忽略的数据盲区
训练集如果只在关节空间均匀采样,映射到笛卡尔空间后并非均匀分布,因为两连杆在接近极限位形时,末端点密度变化大。更麻烦的是,靠近工作空间边界的区域,即使关节角没有突变,逆解精度也受sqrt(1-cos_q2^2)中对 1 的数值精度影响。简单做法是在生成数据集时,按坐标网格再补一批边界数据:
gx = linspace(0.1, 0.7, 30); gy = linspace(-0.55, 0.55, 30); for xi = 1:30 for yi = 1:30 xq = gx(xi); yq = gy(yi); try [q1, q2] = ik_2dof(xq, yq, l1, l2, 'up'); X = [X; xq, yq]; Y = [Y; q1, q2]; catch end end end补这样一批网格点在训练集里的作用是强制网络在边界附近见到真实样本,而不是靠外插去猜。最终训练集里关节空间均匀采样和笛卡尔网格点混合,网络在边界坏区表现会好很多。训练完成后建议立刻做一次自检:把训练集输入网络输出预测角,再对预测角做正运动学反算末端坐标,算 RMS 误差,通常应在 1e-3 米量级。异常大的偏差说明网络容量不够或者训练集内混入了不同臂型。
4. 在 MATLAB 里跑通神经网络的轨迹跟踪控制
4.1 规划末端直线轨迹并用梯形速度曲线离散化
控制的输入是期望轨迹。以一条从 P0 到 P1 的直线轨迹为例,先规划末端位置随时间变化的序列。梯形速度曲线有三个阶段,加速、匀速、减速。给定总时长t_total和加速段时间ts,且ts必须小于t_total/2,可由位移反推最大速度。离散化后得到每个控制周期的末端目标点:
function [t, traj] = plan_line(P0, P1, t_total, ts, dt) L = norm(P1 - P0); if ts >= t_total/2 error('加速段时间必须小于总时长的一半'); end v_max = L / (t_total - ts); a = v_max / ts; t = 0:dt:t_total; n = length(t); traj = zeros(2, n); for i = 1:n ti = t(i); if ti <= ts s = 0.5 * a * ti^2; elseif ti <= t_total - ts s = v_max * (ti - ts/2); else s = L - 0.5 * a * (t_total - ti)^2; end traj(:, i) = P0 + (P1 - P0) * (s / L); end end这段代码的数学逻辑是位移对时间的三段积分。加速段位移0.5*a*t^2,匀速段是v_max*(t - ts/2),减速段从剩余距离反推。换算成末端坐标时,用归一化弧长s/L做线性插值,这样轨迹在起终点之间速度平滑。控制周期dt=0.01s对应 100Hz,这个频率足够两连杆机械臂的位置环使用。注意 P0 和 P1 必须都在工作空间内部,且避开奇异区域。
4.2 用训练好的网络逐点预测关节角并驱动关节
轨迹点生成后,每个控制周期调用一次训练好的网络做逆解。网络推理和解析逆解在这里是并列的一种实现方式,代码逻辑完全一样,区别只在于求解器:
[t, traj] = plan_line([0.55; 0], [0.35; 0.35], 2.0, 0.3, 0.01); n = length(t); q_cmd = zeros(2, n); for i = 1:n xd = traj(1, i); yd = traj(2, i); q_pred = net([xd; yd]); % 推理输出是 2x1 q_cmd(:, i) = q_pred; end预测出的q_cmd是关节角目标序列。直接发给关节位置环之前,必须做两个处理:一是限幅,把预测角限制在物理限位范围内;二是平滑,q_cmd的相邻采样点之间角度差不能过大,否则实际执行机构会因速度饱和而跟踪不上。限幅是一行命令:q_cmd = max(min(q_cmd, q_limit_max), q_limit_min)。平滑度可以通过计算diff(q_cmd, 1, 2)的绝对最大值快速检查,如果大于 0.05rad 每周期,说明网络特定点预测异常,最常见原因是验证轨迹穿过了训练集未覆盖的边界区域。
4.3 前馈加反馈的混合控制,把神经网络输出的残差交给PID
只用开环神经网络逆解承担全部精度是不够的,一方面网络对训练集之外的样本存在泛化误差,另一方面关节摩擦和负载扰动是确定性模型建不出来的。常见的做法是神经网络生成前馈角度,底层并联一个反馈控制器,把末端误差折算回关节修正量。控制框图不必展开成正式公式,但工程上就是把两种信号加起来:
e_prev = 0; q_act = [0.9; -0.5]; % 实际关节角初值 q_cmd = zeros(2, n); Kp = [30; 30]; Kd = [0.8; 0.8]; for i = 1:n q_ref = net([traj(1,i); traj(2,i)]); e = q_ref - q_act; u = Kp .* e + Kd .* (e - e_prev)/dt; q_act = q_act + u * dt; % 简化的位置环模型 q_cmd(:, i) = q_act; e_prev = e; end这里写的是每个关节独立 PD 控制加神经网络前馈角度。要注意u在这里直接把修正量加到关节角上,是一个简化仿真模型;真实机械臂需要用动力学方程或驱动器转矩环,但控制结构不变。混合控制的价值在于:神经网络负责提供初始值和大部分轨迹信息,反馈 PD 只补偿网络误差和扰动,回路带宽可以比纯反馈更低,从而在高刚度需求下不过度放大传感器噪声。
4.4 轨迹跟踪失效的排查清单
如果跟踪效果不好,先别急着调神经网络。多数问题的根源在数据或轨迹规划,而不是训练算法。以下表格是使用这些年总结的排查顺序:
| 现象 | 可能原因 | 排查手段 |
|---|---|---|
| 轨迹在中段拐弯 | 验证轨迹穿过了训练集覆盖盲区 | 绘制轨迹的关节角曲线,看是否突然跳变 |
| 起点和终点偏差大 | 梯形速度曲线 ts 过小,加速度突变 | 增大 ts 再试,并检查速度曲线是否连续 |
| 关节抖动 | 反馈增益 Kd 过大或轨迹离散周期太慢 | 减小 Kd,或把 dt 降到 0.005s |
| 特定区域反复错位 | 该区域位于工作空间边界附近 | 按 3.3 节补采样点后重新训练 |
| 角度预测超出限位 | 网络输出端饱和特性不理想 | 对网络输出直接加限幅饱和块 |
排查表格要配合诊断图使用。我习惯把期望轨迹、实际轨迹、预测关节角、误差序列四张图画在同一张图上,任何一段的错误都能从四者对应关系里看出是规划问题还是执行问题。训练网络过程本身不复杂,但轨迹跟踪调试七成时间都花在判断数据覆盖度够不够、限位合不合理、反馈增益配不配得上控制周期这三个问题上。
5. 神经网络机械臂控制的验证技巧:边界位形与泛化能力测试
验证一个逆解网络好不好,不能只靠训练集的损失值,也不该只用训练时的同分布测试集。常见做法是设计一条训练期间从未出现过的“8 字形轨迹”,逐点推理加正运动学反验,计算最大误差和 RMS 误差:
t_test = linspace(0, 2*pi, 600); traj_test = zeros(2, length(t_test)); for i = 1:length(t_test) cx = 0.4 + 0.12*cos(2*t_test(i)); cy = 0.12*sin(t_test(i)); % 8字前半 traj_test(:, i) = [cx; cy]; end err_all = zeros(1, length(t_test)); for i = 1:length(t_test) q_hat = net(traj_test(:, i)); [~, x_hat, y_hat] = fk_2dof(q_hat(1), q_hat(2), 0.4, 0.3); err_all(i) = norm([x_hat; y_hat] - traj_test(:, i)); end fprintf('Max err: %.4f m, RMS err: %.4f m\n', max(err_all), rms(err_all));这段验证代码有一个容易被忽略的陷阱:net(traj_test(:,i))的输入是二维列向量,输出也是二维列向量,不要混入批量维度。如果err_all的最大误差超过 2cm,重点检查轨迹是否进入了接近边界或奇异点区域。泛化验证的另一层含义是检查臂型一致性:将预测得到的q_hat用固定臂型选择重新走一遍逆解,两者的关节角差异应该小于一个很小的阈值,如果某点差异明显,说明神经网络在该区域学会了平均解或者跳变解,需要回去补数据。
最后说一个很实用的技巧,也是这个项目里最常忽略的一环:神经网络预测输出角度时,不要只拿角度误差做评价指标,fk_2dof反算后的末端误差才是真正对控制有意义的指标。因为关节角误差 0.02rad 在远离边界区域只对应几毫米末端偏差,但在靠近奇异点时会被放大数倍。直接对末端误差设置阈值,判断网络哪些点合格、哪些点不合格,比单看角度损失更直观。若发现不合格点集中分布在某个方向,建议在生成数据时对该区域的采样密度加倍后重新训练,这个迭代通常一到两轮就能把最大末端误差压进毫米级。
本文还有配套的精品资源,点击获取