☰
基于强化学习的六轴机械臂避障路径规划Matlab实现
2026/10/28 17:14:34 网站建设 项目流程

简介:基于强化学习的六轴机械臂自主避障路径规划Matlab代码,面向机器人、自动化及计算机等专业学生,适用于课程设计、期末大作业或毕业设计中的路径规划算法仿真。代码采用参数化编程,可在Matlab 2014、2019a、2024a等版本直接运行,并附带案例数据以便快速复现。资源包共13个文件,其中7个m脚本负责核心算法与仿真流程,2个mat文件提供障碍环境数据,3个png图片展示规划结果与流程图,另含1个md说明文件,整体仅454KB,目录结构简洁清晰。已有99人学习下载。借助代码可掌握强化学习Q-learning或策略迭代在六轴臂避障中的应用,理解逆运动学与最短距离计算如何支持动态重规划,通过可视化脚本观察机械臂运动轨迹与避障效果,适合希望快速搭建仿真环境、深入理解算法细节并完成课程设计的同学。

1. 基于强化学习的六轴臂自主避障路径规划:这套 Matlab 代码在解决什么问题

一个六轴机械臂在焊接、装配现场最容易翻车的动作,不是搬运重物,而是从 A 点挪到 B 点时躲开横在半路的工件和夹具。传统规划用 RRT 或人工势场,碰上高维关节空间经常算得慢、路径抖、卡进局部极小。把强化学习当成前置策略生成器,先让机械臂在仿真里自己撞出避障规律,再拿它做路径初值或直接输出动作序列,是近几年落地最多也最容易上手的方向。这套基于强化学习的六轴臂自主避障路径规划 Matlab 代码,做的就是这件事:在 Matlab 的机器人系统工具箱和强化学习工具箱里搭好六轴臂模型,用深度强化学习算法训练一个能根据当前关节角与障碍物距离输出下一步动作的策略,最后还原成可执行的末端轨迹。适合做机械臂避障课题的研究生,也想给现有运动规划加智能选路能力的机器人工程师。

2. 六轴臂避障路径规划为什么用强化学习:从状态空间到奖励函数

2.1 六轴臂的状态描述:关节角、末端位姿与障碍物距离

机械臂路径规划问题的状态空间,和轮式小车的动态避障完全不一样。小车是二维平面,状态大多是位置、航向和速度;六轴机械臂在关节空间里至少六维,末端还有三维位置和三维姿态,如果全塞进状态向量,维数直接上到十几维,学习难度会陡增。

我一般会做状态压缩:把观察向量 obs 拆成四段,第一段是当前关节角(1×6),第二段是正运动学算出来的末端位置(1×3),第三段是末端与目标点的相对位置(1×3),第四段是末端离最近障碍物的距离,以及这个最近点的方向向量(1×4)。合计 16 维。末端姿态我通常不直接进网络,因为六轴臂本身有多余自由度,姿态直接丢进去会让收敛慢半拍。

在 Matlab 里,这段状态对应的观察空间定义长这样:

% 观察空间:16 维连续向量 obsInfo = rlNumericSpec([16 1]); obsInfo.Name = "JointAndObstacleState"; obsInfo.Description = "q(6) + eePos(3) + targetRel(3) + obsDist(1) + obsVec(3)";

obsDist 加 obsVec 实际占了 4 维,其中 obsVec 的三个分量只有方向有意义,模长和 obsDist 是重复信息。obsInfo 的维度必须和状态函数实际输出的向量长度对齐,否则 rlFunctionEnv 在校验维度时会直接报错,这一步坑过不少人。

状态函数里计算这些量的典型写法是:

function obs = getObs(robot, q, eeName, targetPos, obstaclePose) T = getTransform(robot, q, eeName); % 末端位姿矩阵 eePos = T(1:3, 4)'; % 末端位置 targetRel = targetPos - eePos; % 目标相对位移 [dist, vec] = distanceToObstacle(eePos, obstaclePose); obs = [q(:)', eePos, targetRel, dist, vec]; end

getTransform 返回 4×4 齐次矩阵,取第 4 列前 3 行就是末端位置。distanceToObstacle 是自己封装的函数,返回末端到障碍物包围盒表面的距离和指向障碍物的单位向量。距离符号方向容易写反,我之前就在这里翻过车,向量方向一旦反了,奖励函数会引导机械臂往障碍物上撞。

2.2 强化学习算法选型:DQN、PPO 还是 DDPG

状态和动作定了之后,算法选择是个现实问题。Matlab 强化学习工具箱里常用的三个 agent 适用边界很清晰,可以参考这张表:

算法动作空间Matlab 构造函数在六轴避障里的定位
DQN离散rlDQNAgent动作离散化为关节角增量组合,适合教学验证
DDPG连续rlDDPGAgent直接输出连续关节角增量,规划最常用
PPO连续/离散rlPPOAgent训练稳定性好,适合复杂奖励和障碍物随机化

对于六轴臂这种连续关节空间,DQN 的动作离散化会带来维度爆炸:每个关节分 3 档增量,6 个关节就是 729 个动作,训练效率很低。所以我主推 DDPG 或 PPO。区别在于,DDPG 是 off-policy,样本利用率高,同样的回合数能学到的信息更多,但超参数敏感;PPO 是 on-policy,每轮更新都要重新采数据,慢一些,但收敛更稳,奖励函数里加了很多障碍物距离项时,PPO 不太容易因为异常奖励值崩掉。

我的选择习惯是:障碍物固定、目标固定,用 DDPG,跑得快;每回合随机挪障碍物位置,用 PPO,泛化能力更好。这是目前深度强化学习算法在六轴机械臂避障里比较主流的分工方式。

2.3 奖励函数设计:稀疏与密集奖励的取舍

奖励函数是这套代码的灵魂,也是最玄学的部分。稀疏奖励最简单:到位给 +100,碰撞给 -100,其它时候是 0。问题是六轴臂关节空间维度高,从初始位置到目标全靠随机探索,几乎永远摸不到正奖励,训练曲线一条平线。我第一版就是这么写的,跑了 300 回合完全没进步,后来全部换成密集奖励。

密集奖励的做法是叠加多个项:

% 距离奖励:末端越靠近目标越大 r_dist = -0.5 * norm(targetRel); % 碰撞惩罚:按最近距离指数惩罚 r_obs = -2.0 * exp(-3 * dist); % 到位与碰撞的硬约束 if dist < 0.05 r = r - 50; % 贴近障碍物即视为危险 end if norm(targetRel) < 0.03 r = r + 80; % 到达目标 done = true; end

距离奖励保证机械臂有往目标走的梯度,碰撞惩罚保证它学会绕着走,硬约束负责在临界点给明确停止信号。三个项的系数比例需要反复调。我的经验是碰撞惩罚的最大值要略大于距离奖励的每步收益,这样机械臂宁可绕远路也不贴障碍物。

3. 在 Matlab 里搭训练环境:机械臂模型、碰撞检测与观测接口

3.1 加载六轴臂模型:URDF 导入与 loadrobot 的取舍

Matlab 里最省事的机械臂模型加载方式是 loadrobot,但工具箱内置模型大多是七轴或双臂平台,经典六轴臂模型没有内置。真做六轴避障项目,我会优先用 URDF 文件导入自己手头那台臂的模型。

% 导入自建六轴臂 URDF,DataFormat 统一用 row robot = importrobot('my_six_axis.urdf'); robot.DataFormat = "row"; eeName = 'tool0'; % 替换成 URDF 里真实的末端框架名 % 查看关节顺序和名称 showdetails(robot);

showdetails 输出的是关节树,务必把关节名按顺序抄下来,后面写的 q 向量顺序必须和它完全一致。URDF 里如果有多余的固定关节,会算进总关节数里,碰撞检测的输入维度也要跟着变。也就是说,URDF 里每多一个固定关节,观察空间和动作空间维度就要重新核对一次。

如果只是快速验证算法,也可以用 loadrobot 加载七轴模型并固定一个关节当六轴用:

robot = loadrobot("frankaEmikaPanda", "DataFormat", "row"); % 把第 7 个关节固定成 0,控制维度变成 6 eeName = "panda_link8"; T_init = getTransform(robot, homeConfiguration(robot), eeName);

loadrobot 返回的 rigidBodyTree 的 DataFormat 建议统一改成 "row",后面所有关节向量都是 1×n 行向量,踩坑最少。homeConfiguration 拿到的默认关节角度也受 DataFormat 影响,别混用。

3.2 碰撞检测与障碍物最近距离的计算

六轴臂避障绕不开碰撞检测。Matlab 自带 checkCollision 函数能同时做自碰撞和环境碰撞判定:

% 建立一个长方体障碍物并放在指定位置 obsBox = collisionBox(0.2, 0.2, 0.4); T_obs = eye(4); T_obs(1:3, 4) = [0.5, 0, 0.3]; % 障碍物中心位置 obsBox.Pose = T_obs; % 判断当前关节配置是否碰撞 isCollide = checkCollision(robot, q, obsBox, 'IgnoreSelfCollision', true);

checkCollision 返回逻辑值,适合在 step 函数里判终止条件。但如果奖励函数需要“离障碍物多近”这种连续量,就得自己算末端到障碍物的距离。这里有个实用近似:用障碍物包围盒表面到末端点的最短距离。

function [dist, vec] = distanceToObstacle(eePos, box) % 将末端位置转换到障碍物局部坐标系 T = box.Pose; local = T \ [eePos(:); 1]; % 计算到长方体表面的距离近似 half = box.Dimensions / 2; dx = max(abs(local(1)) - half(1), 0); dy = max(abs(local(2)) - half(2), 0); dz = max(abs(local(3)) - half(3), 0); dist = norm([dx, dy, dz]); % 方向向量(局部坐标,调用处转回全局) vec = sign(local(1:3)) .* [dx, dy, dz]; end

实际工程里我通常再做一层 min 合并,把多个障碍物的距离取最小统一送进奖励函数。checkCollision 速度不快,每步调用一次可能成为训练瓶颈,后面避坑章节细说。

3.3 用 rlFunctionEnv 定义训练环境:reset 与 step 的职责边界

Matlab 强化学习工具箱支持 Simulink 环境,但我更推荐纯 MATLAB 的 rlFunctionEnv。它把环境拆成 reset 和 step 两个回调,调试起来比 Simulink 直观得多。

% 环境句柄:reset 生成初始状态,step 执行动作并返回奖励 resetFcn = @() resetEnv(robot, eeName, qInit, targetPos, obsBox); stepFcn = @(action, obs) stepEnv(robot, eeName, targetPos, obsBox, action, obs); env = rlFunctionEnv(obsInfo, actInfo, stepFcn, resetFcn);

reset 函数返回初始观察值 obs0:

function obs0 = resetEnv(robot, eeName, qInit, targetPos, obsBox) % 随机化初始关节角,给探索留一点扰动 q = qInit + 0.1 * randn(1, 6); obs = getObs(robot, q, eeName, targetPos, obsBox); obs0 = obs; end

step 函数的签名是 [obs, reward, done, loggedSignals] = stepFcn(action, obs)。一次 step 要做三件事:把 action 叠加到当前关节角、调用 checkCollision 判断是否出界或碰撞、计算奖励并更新观察。

3.4 rlFunctionEnv 两个最容易忽视的细节

rlFunctionEnv 要求 action 和 obs 都是列向量,你内部算的时候要注意维度转置,否则 agent 和环境之间数据维度对不上,训练启动那一刻就报错,连第一回合都跑不了。这是纯 MATLAB 环境最常见的翻车点。

再一个是 step 函数千万不要做成可重入的。训练时如果 UseParallel 打开,多个 worker 会同时调用 stepFcn,函数里如果有全局变量或持久变量,会出现状态串台。我习惯把所有环境状态都放进 cell 或 struct 传参,不在函数外部存共享变量。

4. DDPG/PPO 训练脚本骨架与四个必调参数

4.1 网络结构与 DDPG agent 初始化的最小可跑代码

有了环境,剩下就是建网络、建 agent、开训练。DDPG 需要 actor 和 critic 两个网络,actor 输入 obs 输出连续动作,critic 输入 obs+action 输出 Q 值。以 16 维观察、6 维动作为例:

% actor:把观察映射到 [-1,1] 的连续动作 actorNetwork = [ featureInputLayer(16, 'Name', 'obs') fullyConnectedLayer(128, 'Name', 'fc1') reluLayer('Name', 'relu1') fullyConnectedLayer(64, 'Name', 'fc2') reluLayer('Name', 'relu2') fullyConnectedLayer(6, 'Name', 'action') tanhLayer('Name', 'tanh')]; actor = rlContinuousDeterministicActor(actorNetwork, obsInfo, actInfo); % critic:输入 obs+action,输出标量 Q criticNetwork = [ featureInputLayer(16, 'Name', 'obs') fullyConnectedLayer(128, 'Name', 'fc1') reluLayer('Name', 'relu1') fullyConnectedLayer(128, 'Name', 'fc2') reluLayer('Name', 'relu2') fullyConnectedLayer(1, 'Name', 'q')]; critic = rlQValueFunction(criticNetwork, obsInfo, actInfo);

tanhLayer 把输出压到 [-1, 1],真正施加到机械臂时再乘一个动作范围系数,比如关节角增量限幅 ±0.25 rad。这个缩放不要写进网络里,留在 step 函数里做,否则训练初期探索噪声会被缩放系数放大,动作抖得厉害。

然后组装 agent:

agentOpts = rlDDPGAgentOptions(... 'SampleTime', 0.1, ... 'DiscountFactor', 0.99, ... 'MiniBatchSize', 128, ... 'TargetSmoothFactor', 1e-3, ... 'ExperienceBufferLength', 1e5, ... 'ExplorationNoise', 0.2); agent = rlDDPGAgent(actor, critic, agentOpts);

4.2 训练选项里最值得调的四个参数

训练效果好坏,很大程度由四个参数决定,比网络层数更关键。

第一是 MiniBatchSize。我习惯设 128,太小训练震荡厉害,太大每轮更新变慢,尤其碰撞检测耗时高的时候感受明显。64 也能跑,但后面要监控 reward 方差。

第二是 TargetSmoothFactor。它控制目标网络软更新速度,一般取 1e-3 到 1e-2 之间。这个值调太大会让 critic 训练发散,表现在训练中段 reward 突然掉到很低的水平且回不来;太小则目标网络更新太慢,学习效率低。我通常从 1e-3 起步,不稳定就降到 5e-4。

第三是 ExplorationNoise。DDPG 的探索靠给动作加噪声。噪声太大机械臂会满空间乱甩,容易频繁撞上障碍物,但这样才有多样性经验;噪声太小策略会过早锁定一条过不去的路径。我一般设 0.2 起步,等平均 reward 曲线抬头后再降到 0.05,相当于做一次探索退火。

第四是 ExperienceBufferLength。经验池长度决定了 agent 能回忆多远的旧经验。我建议至少 1e5,内存够就 1e6。经验池太小,训练后期会出现“忘了以前怎么避障”的情况,reward 曲线来回震荡。

4.3 训练循环与收敛指标:别只盯着 Loss

训练脚本主体就是一次 train 调用:

trainOpts = rlTrainingOptions(... 'MaxEpisodes', 500, ... 'MaxStepsPerEpisode', 128, ... 'ScoreAveragingWindowLength', 20, ... 'StopTrainingCriteria', 'AverageReward', ... 'StopTrainingValue', 350, ... 'Plots', 'training-progress', ... 'Verbose', true); trainingStats = train(agent, env, trainOpts);

这里 StopTrainingCriteria 用的是平均奖励而不是 Loss。实操里 actor/critic 的 Loss 参考意义不大,因为 DDPG 的 critic loss 即使训练正常也会持续波动。真正能说明问题的是:平均回合奖励是否在抬升、每回合结束原因是否从“碰撞终止”逐步变为“到达目标终止”。

注意:rlTrainingOptions 的 StopTrainingCriteria 只监控平均奖励,别把它当成成功率。成功率要在训练循环外用固定回合统计。

想追踪成功率,可以在 step 函数返回的 loggedSignals 里加一个 done 原因标志,回合结束后统一统计。常见做法是给奖励函数加一个 offset,比如 r = r + 1,用来区分未到位的回合和到位的回合;或者用固定初始状态反复调用 getAction 统计成功率。

5. 避坑:六轴臂强化学习训练中 5 个典型坑

5.1 训练曲线一直水平,agent 完全没有学习迹象

现象:跑了一百多回合,AverageReward 一直平着走,偶尔有尖峰又立刻掉回来。

原因:最常见不是网络问题,而是奖励量级不对。距离奖励系数太小,每步收益差不到 0.1,探索噪声带来的随机波动就把学习信号淹没了。其次是初始状态随机范围太大,agent 连“往哪个方向走有梯度”都分不清。

解决:把距离奖励系数往上提到 -1 到 -5 的量级,同时把目标点放在机械臂工作空间的中间区域。如果还不动,把碰撞惩罚先关掉做无障碍训练,确认能学会到达目标再加障碍物,这也是课程学习。

5.2 step 函数里 checkCollision 频繁报错或卡死

现象:训练到某一个回合,控制台突然抛错,提示输入维度不匹配,或者 MATLAB 直接假死。

原因:action 叠加后关节角超出 URDF 定义限位,checkCollision 对超限角度计算得到 NaN,几何判定异常。另一个原因是 step 函数没对 action 做 clip,训练后期连续层偶尔会输出边界值。

解决:在 step 函数第一行强制 clip 动作和角度:

% 动作限幅再叠加,避免超出关节限位 dq = max(min(action', 0.25), -0.25); qNew = q + dq; qNew = min(max(qNew, qMin + 0.05), qMax - 0.05); % 安全边界

同时给 checkCollision 包一层 try-catch,出错就按碰撞处置返回很大的负奖励,不要让训练中断。

5.3 训练速度奇慢,一天跑不完 200 回合

现象:单回合步数不多,但每步耗时很高,500 回合跑了一下午进度还没过半。

原因:getTransform 和 checkCollision 都是完整几何计算,如果每步调用多次,开销非常大。我见过有人为了算触碰距离对每个 link 都调一次 getTransform,那就是 7 次正运动学计算,训练速度慢一个数量级。

解决:把每个 step 里的 getTransform 调用次数压到最少,末端位置只在状态更新处算一次;碰撞检测用简化几何,URDF 里精细 mesh 在训练阶段用碰撞盒替代。另外可以开 'UseParallel', true 多核并行采集经验,但要注意统一随机种子,否则每回合初始状态不可复现,调试困难。

5.4 训练完的路径贴着障碍物走,安全余量不足

现象:成功率上去了,机械臂确实能到目标,但回放轨迹发现末端离障碍物只有一两厘米,稍微有模型误差就会撞上。

原因:碰撞惩罚只在很近距离内触发,agent 学会了“只要不触碰就能得分”,于是干脆贴着安全边界走。这是强化学习避障里很经典的安全性问题。

解决:把碰撞惩罚改成连续函数,不要用阶跃式触发。用指数惩罚:r_obs = -C × exp(-lambda × dist),dist 越近惩罚越大。lambda 在 3 到 10 之间调,C 设 2 到 20。这样 agent 会在“离远一点”和“绕路代价”之间学安全折中。

5.5 训练好的策略换个障碍物位置就失灵

现象:训练时障碍物固定在 [0.5, 0, 0.3],测试同一位置成功率 90%,挪到 [0.4, 0.2, 0.3] 直接掉到 20%。

原因:这就是过拟合。强化学习在固定环境下学到的是记忆路线,不是避障策略,障碍物一换就露馅。这个问题在真实工程里出现频率极高。

解决:训练一开始就做随机化,每个回合在 reset 里给障碍物位置加随机偏移,幅度从 ±0.05m 起步,稳定后扩到 ±0.2m。这正是动态避障里常见的 domain randomization 思想。障碍物随机化之后前期奖励曲线会变难看,但后期泛化能力是固定障碍物怎么都练不出来的。

6. 验证与落地技巧:把训练好的策略接进路径规划

训练收敛只是第一步,真正能用还要解决“策略输出怎样变成平滑轨迹”的问题。DDPG 或 PPO 输出的是一串离散动作序列,直接发给关节会抖。我会先把 agent 在目标场景下 rollout 一次拿到关节轨迹点,再用 pchip 插值把轨迹加密平滑:

% 用训练好的 agent roll out 一条轨迹 obs = resetFcn(); qTraj = []; for i = 1:128 action = getAction(agent, {obs}); [obs, ~, done, ~] = stepFcn(action, obs); qTraj = [qTraj; obs(1:6)]; % 取出关节角 if done, break; end end tt = linspace(0, 1, size(qTraj, 1)); % pchip 插值加密,避免过冲 qSmooth = interp1(tt, qTraj, linspace(0, 1, 500), 'pchip');

pchip 比普通三次样条更适合关节空间,不会在目标点附近产生 overshoot。平滑后的轨迹在发给控制器前,要做一次 checkCollision 复核,确认每个插值点都没有碰撞。

评估环节我习惯固定 20 组随机初始状态和障碍物位置,统计成功率、平均路径长度、平均规划时间三张指标表。成功率低于 90% 回头调奖励权重,路径太长加大距离奖励权重,规划时间太长压缩动作步长或改用 PPO 减小策略波动。

最后说个人习惯:强化学习调参本质是玄学,但别在调参上一条路走到黑。先把完整流程跑通,奖励函数每个分量单独打印出来看数值范围,确认没有 NaN 和量级失衡再谈优化。训练中断不要慌,用 SaveAgentCriteria 定期存 agent,恢复训练比重新训练省一半时间。环境随机化要早开,经验池要大,碰撞惩罚要连续,把这几条养成肌肉记忆,成功率自然就上来了,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询