简介:本资源面向本科、硕士阶段从事智能控制与路径规划方向的学习者,提供一套基于DQN(深度Q网络)算法实现机器人路径规划的完整MATLAB实践材料,适合课程设计、毕业设计及科研入门参考。压缩包共3个文件,包含1个m脚本文件与2张png结果图,整体约29KB,脚本承载DQN训练与路径规划主逻辑,图片用于展示规划效果与对比结果,结构精简便于快速上手。资源基于matlab2019a环境编写,若运行遇到问题可私信作者协助排查。目前已有2491人学习下载,说明该方案在同类教学资源中具有一定参考价值。读者可借此理解强化学习在路径规划中的建模思路,掌握状态、动作、奖励等关键要素的设置方法,并对照结果图验证算法收敛与路径生成效果,为后续改进网络结构或迁移至其他场景打下基础。
1. DQN 做机器人路径规划:为什么值得用、适合谁上手
机器人路径规划这件事,传统做法绕不开 A*、Dijkstra、RRT 这几类算法。它们在静态已知地图上表现稳定,但一旦环境里有动态障碍物、地图信息不完整,或者机器人运动学约束比较复杂,纯规划算法的调参就会变成一场拉锯战。DQN(Deep Q-Network)提供的是另一条路:把路径规划建模成马尔可夫决策过程,让机器人在与环境的反复交互中自己学出策略,而不是靠人把规则写死。
这个方向适合两类人。一类是做强化学习课程设计或毕业设计的学生,需要一个有明确评价指标、能可视化、能跑通完整训练闭环的项目;另一类是做移动机器人或 AGV 调度的一线工程师,想验证「学习型规划器」在特定场景下能不能比传统方法更省事。MATLAB 在这里的角色是快速原型工具——它的 Reinforcement Learning Toolbox 已经把 DQN 的训练循环、经验回放、目标网络这些组件封装好了,你不需要从零写神经网络反向传播,可以把精力放在环境建模和奖励设计上。
但要说清楚一点:DQN 不是拿来替代 A* 的。它解决的是「规则难以显式表达」的那类规划问题,比如障碍物运动模式不固定、奖励信号稀疏、需要在线适应。如果你的场景就是一张固定栅格地图,A* 几毫秒出结果,上 DQN 属于杀鸡用牛刀。判断标准很简单——当你的规划器需要「从经验中改进」而不是「每次重新搜索」时,DQN 才有意义。
2. 把路径规划写成 MDP:状态、动作、奖励怎么定
2.1 状态空间设计:栅格坐标还是传感器读数
DQN 的输入是状态,状态设计直接决定网络能不能学到东西。机器人路径规划里最常见的两种状态表示:
第一种是栅格坐标 + 局部障碍物信息。假设地图是 20×20 的栅格,机器人位置用 (row, col) 表示,状态向量可以设计成[robot_row, robot_col, goal_row, goal_col, 局部障碍物展平向量]。局部障碍物通常取机器人周围 5×5 或 7×7 的窗口,展平成 25 或 49 维。这种表示适合地图规模不大、障碍物分布相对固定的场景。
第二种是原始传感器读数。比如激光雷达的 8 方向或 16 方向距离值,加上目标点的相对角度和距离。这种表示更接近真实机器人,但状态维度高、训练慢,MATLAB 里用rlNumericSpec定义时要注意归一化。
我一般会先用第一种方案跑通闭环,确认奖励设计和超参数没问题,再换成传感器读数做迁移。因为栅格坐标的状态空间小,DQN 容易收敛,调试成本低。下面是一个状态定义的 MATLAB 代码片段:
% 地图参数 mapSize = 20; % 20x20 栅格 obsWindow = 5; % 局部障碍物窗口 5x5 stateDim = 4 + obsWindow^2; % [row, col, goalRow, goalCol, 局部障碍物] % 定义观测空间 obsInfo = rlNumericSpec([stateDim 1]); obsInfo.Name = 'robotState'; obsInfo.LowerLimit = 0; obsInfo.UpperLimit = 1; % 所有分量归一化到 [0,1] % 定义动作空间:上下左右 + 停留,共 5 个离散动作 actInfo = rlFiniteSetSpec([1 2 3 4 5]); actInfo.Name = 'action'; actInfo.Elements = {1,2,3,4,5};这里的关键参数是obsWindow。窗口太小,机器人看不到足够的环境信息,容易在局部最小值打转;窗口太大,状态维度膨胀,训练样本效率下降。5×5 是一个比较稳妥的起点,对应机器人前方两格、左右各两格的感知范围。UpperLimit和LowerLimit设成 0 和 1 是为了让输入网络前不需要额外做归一化,减少一层预处理。
2.2 动作空间与奖励函数:稀疏奖励为什么容易翻车
动作空间用离散集合是最简单的,上下左右加停留,5 个动作。如果机器人有差速运动学约束,可以改成 8 个方向或者连续转向角,但离散动作在 MATLAB 里用rlFiniteSetSpec定义最省事,训练也稳定。
奖励函数是 DQN 路径规划里最容易踩坑的地方。新手常犯的错误是只给终点奖励:到达目标 +100,其他时候 0。这种稀疏奖励在 20×20 的栅格上,随机探索撞到目标的概率极低,DQN 可能跑几万步都学不到有效策略。常见做法是加引导性奖励:
function reward = computeReward(robotPos, goalPos, prevDist, map) dist = norm(robotPos - goalPos); if isequal(robotPos, goalPos) reward = 100; % 到达目标 elseif map(robotPos(1), robotPos(2)) == 1 reward = -50; % 撞障碍物 else % 距离引导:靠近目标给正奖励,远离给负奖励 reward = (prevDist - dist) * 2; reward = reward - 0.1; % 每步小惩罚,鼓励最短路径 end endprevDist - dist这一项是距离差,乘以 2 是放大引导信号。每步减 0.1 是步数惩罚,防止机器人在原地绕圈。撞障碍物给 -50 而不是 -100,是因为太强的负奖励会让网络过度保守,学到「不动最安全」的退化解。这些数值不是固定的,地图越大,距离引导的系数可以适当调小,步数惩罚可以调大。
注意:奖励函数的量级要匹配。如果终点奖励是 100,步数惩罚是 0.1,那么机器人绕 1000 步的代价才等于一次到达目标。实际调参时建议先跑 500 个 episode 看平均回报曲线,如果曲线一直不涨,优先检查奖励设计而不是网络结构。
2.3 用 MATLAB 搭建 DQN 智能体:网络层数与经验回放参数
MATLAB 的 Reinforcement Learning Toolbox 里,DQN 智能体用rlDQNAgent创建。核心组件是 Q 网络和目标网络,网络结构不需要太深,路径规划这种状态维度几百以内的任务,两到三层全连接就够了。
% 定义 Q 网络:输入状态,输出每个动作的 Q 值 net = [ featureInputLayer(stateDim, 'Normalization', 'none') fullyConnectedLayer(128) reluLayer fullyConnectedLayer(128) reluLayer fullyConnectedLayer(5) % 5 个动作的 Q 值 ]; % 转成 dlnetwork 并创建 DQN 智能体 dlnet = dlnetwork(net); agentOpts = rlDQNAgentOptions(... 'SampleTime', 1, ... 'DiscountFactor', 0.95, ... 'ExperienceBufferLength', 1e5, ... 'MiniBatchSize', 64, ... 'TargetUpdateFrequency', 100, ... 'LearnFrequency', 1); agent = rlDQNAgent(dlnet, obsInfo, actInfo, agentOpts);DiscountFactor设 0.95 而不是 0.99,是因为路径规划是有限步任务,折扣因子太高会让网络过度关注远期回报,收敛变慢。ExperienceBufferLength设 1e5 是经验回放的容量,太小会导致样本相关性太强,太大则早期经验被稀释。TargetUpdateFrequency设 100 表示每 100 步把 Q 网络的权重复制给目标网络,这个值太小目标网络抖动大,太大则学习滞后。MiniBatchSize64 是常规起点,显存或内存不够可以降到 32。
训练循环用train函数,指定最大 episode 数和每 episode 最大步数:
trainOpts = rlTrainingOptions(... 'MaxEpisodes', 2000, ... 'MaxStepsPerEpisode', 200, ... 'ScoreAveragingWindowLength', 50, ... 'StopTrainingCriteria', 'AverageReward', ... 'StopTrainingValue', 80, ... 'Verbose', false, ... 'Plots', 'training-progress'); trainingStats = train(agent, env, trainOpts);MaxStepsPerEpisode设 200 是给机器人足够的探索步数,20×20 地图上最优路径通常不超过 40 步,200 步足够绕路。StopTrainingValue设 80 是平均回报阈值,到达目标奖励 100,减去步数惩罚和可能的绕路,80 左右说明策略已经比较稳定。
3. 训练环境与仿真循环:MATLAB 里怎么把机器人跑起来
3.1 自定义环境类:reset 和 step 函数的写法
MATLAB 里创建强化学习环境有两种方式:用rlFunctionEnv快速定义,或者继承rl.env.MATLABEnvironment写自定义类。路径规划涉及地图状态、碰撞检测、奖励计算,建议用自定义类,逻辑清晰也好调试。
classdef PathPlanningEnv < rl.env.MATLABEnvironment properties Map % 栅格地图,0 可通行,1 障碍 RobotPos % 当前机器人位置 [row, col] GoalPos % 目标位置 PrevDist % 上一步到目标的距离 MaxSteps % 每 episode 最大步数 StepCount % 当前步数 end methods function this = PathPlanningEnv(map, goal) obsInfo = rlNumericSpec([29 1]); % 4 + 5*5 = 29 actInfo = rlFiniteSetSpec([1 2 3 4 5]); this = this@rl.env.MATLABEnvironment(obsInfo, actInfo); this.Map = map; this.GoalPos = goal; this.MaxSteps = 200; end function [obs, reward, isDone, info] = step(this, action) this.StepCount = this.StepCount + 1; % 根据动作更新机器人位置 newPos = moveRobot(this.RobotPos, action, this.Map); % 计算奖励 dist = norm(newPos - this.GoalPos); reward = computeReward(newPos, this.GoalPos, this.PrevDist, this.Map); this.PrevDist = dist; this.RobotPos = newPos; % 判断终止 isDone = isequal(newPos, this.GoalPos) || ... this.Map(newPos(1), newPos(2)) == 1 || ... this.StepCount >= this.MaxSteps; obs = getObservation(this); info = struct('RobotPos', newPos); end function obs = reset(this) this.RobotPos = [1 1]; % 固定起点或随机起点 this.PrevDist = norm(this.RobotPos - this.GoalPos); this.StepCount = 0; obs = getObservation(this); end end endstep函数里moveRobot需要处理边界情况:机器人撞到地图边界时应该停在原地还是反弹。我一般让机器人停在原地,同时给一个小的负奖励,这样网络会学到「边界不可走」。isDone的三个条件分别对应到达目标、撞障碍物、超时,超时也算终止是为了防止机器人在一个 episode 里无限绕圈。
3.2 训练循环与 episode 管理:什么时候该停
训练循环本身不复杂,但有几个监控指标要盯着。MATLAB 的training-progress窗口会显示平均回报和 episode 步数,但那个窗口刷新有延迟,建议自己记录每 50 个 episode 的平均回报和成功率。
numEpisodes = 2000; rewardHistory = zeros(numEpisodes, 1); successHistory = zeros(numEpisodes, 1); for ep = 1:numEpisodes obs = reset(env); episodeReward = 0; isDone = false; stepCount = 0; while ~isDone && stepCount < 200 action = getAction(agent, obs); [nextObs, reward, isDone, info] = step(env, action); episodeReward = episodeReward + reward; obs = nextObs; stepCount = stepCount + 1; end rewardHistory(ep) = episodeReward; successHistory(ep) = double(isequal(info.RobotPos, env.GoalPos)); if mod(ep, 50) == 0 avgReward = mean(rewardHistory(max(1,ep-49):ep)); avgSuccess = mean(successHistory(max(1,ep-49):ep)); fprintf('Episode %d, AvgReward: %.2f, SuccessRate: %.2f\n', ... ep, avgReward, avgSuccess); end end判断训练是否该停,不要只看回报曲线。回报可能因为奖励设计的问题一直涨但成功率不涨。我一般同时看两个指标:平均回报连续 100 个 episode 不涨,且成功率超过 90%,就可以停了。如果成功率卡在 60% 到 70% 上不去,通常是探索不够或者状态表示有盲区,需要调EpsilonGreedyExploration的参数。
提示:MATLAB 里 DQN 默认用 epsilon-greedy 探索,
Epsilon从 1 降到 0.01 的速率由EpsilonDecay控制。路径规划任务建议EpsilonDecay设 0.001 到 0.005,让探索持续足够久。探索衰减太快,网络会过早收敛到次优策略。
3.3 仿真结果可视化:把路径画出来才算跑通
训练完不画图,等于没跑通。MATLAB 里用imagesc画栅格地图,用plot叠加机器人轨迹,几行代码就能看出策略好坏。
figure; imagesc(env.Map); colormap(flipud(gray)); hold on; plot(env.GoalPos(2), env.GoalPos(1), 'g*', 'MarkerSize', 12); plot(1, 1, 'bo', 'MarkerSize', 8); % 起点 % 用训练好的 agent 跑一个 episode obs = reset(env); path = [1 1]; isDone = false; while ~isDone action = getAction(agent, obs); [obs, ~, isDone, info] = step(env, action); path = [path; info.RobotPos]; end plot(path(:,2), path(:,1), 'r-', 'LineWidth', 2); title('DQN 路径规划结果');如果画出来的路径贴着障碍物走,说明碰撞惩罚不够;如果路径绕大圈,说明距离引导太弱或者步数惩罚太小。可视化不只是看结果,更是调参的依据。
4. 避坑与排查:DQN 路径规划里最常见的 5 个翻车现场
4.1 回报曲线震荡不收敛
现象:训练了几百个 episode,平均回报在正负之间来回跳,成功率没有明显上升。
原因:最常见的是学习率太大。DQN 的 Q 网络用 Adam 优化器时,默认学习率 0.001 在路径规划任务里可能偏高,导致每次更新步子太大,Q 值估计不稳定。另一个原因是经验回放缓冲区太小,样本相关性太强。
解决:把学习率降到 0.0005 或 0.0001,同时把ExperienceBufferLength从 1e4 提到 1e5。如果还震荡,检查奖励函数的量级是否一致——距离引导奖励和终点奖励差两个数量级时,网络会偏向学短期引导而忽略终点。
4.2 机器人学会原地打转
现象:训练后期,机器人每步都选「停留」动作,回报稳定在一个小负值,成功率接近零。
原因:步数惩罚太轻,或者撞障碍物的负奖励太重。机器人发现「不动」可以避免撞墙,而绕路找目标的回报不确定,于是学到保守策略。
解决:加大步数惩罚,从 0.1 提到 0.5 或 1.0;同时把撞障碍物的惩罚从 -50 降到 -20,减少对探索的抑制。另外检查DiscountFactor,如果设得太低(比如 0.8),机器人会过度短视,也容易原地打转。
4.3 训练到一半突然崩溃
现象:前 500 个 episode 回报稳步上升,然后突然掉到负值,再也回不去。
原因:目标网络更新频率太低,Q 值估计发散。或者经验回放里积累了太多失败样本,网络被带偏。
解决:把TargetUpdateFrequency从 100 降到 50 或 30,让目标网络跟得更紧。如果还不行,检查MiniBatchSize,太小(比如 16)会导致梯度估计方差大,提到 64 或 128。
4.4 路径贴墙走或者穿墙
现象:可视化结果显示路径紧贴障碍物边缘,甚至穿过障碍物。
原因:碰撞检测逻辑有漏洞。MATLAB 里栅格坐标是整数,但moveRobot函数如果没做边界检查,机器人可能移动到地图外,而Map索引越界时 MATLAB 会报错或者返回空值,导致奖励计算错误。
解决:在moveRobot里加边界判断,越界时返回原位置并给负奖励。碰撞检测用Map(newRow, newCol) == 1判断,确保索引在 1 到 mapSize 之间。
4.5 换一张地图就失效
现象:在训练地图上成功率 95%,换一张障碍物分布不同的地图,成功率掉到 30%。
原因:状态表示里没有包含足够的全局信息,网络过拟合到训练地图的特定障碍物布局。或者训练时只用了固定起点和终点,网络没学会泛化。
解决:在reset函数里随机化起点和终点,每次 episode 从可通行区域随机采样。状态向量里加入目标点的相对位置,而不是绝对坐标。如果地图规模变化大,考虑用卷积网络处理局部障碍物窗口,而不是全连接层展平。
5. 从仿真到落地:验证 DQN 规划器是否值得继续投入
训练曲线好看不代表方案能落地。我一般用三个指标做最终判断:成功率、路径长度比、推理耗时。成功率是在 100 张没见过的地图上跑,每张地图随机起点终点,统计到达目标的比例。路径长度比是 DQN 路径长度除以 A* 路径长度,如果超过 1.3,说明 DQN 绕路太多,实际场景里可能不可接受。推理耗时是在目标硬件上跑单步决策的时间,MATLAB 里可以用tic和toc测,如果单步超过 50ms,实时控制就悬了。
% 泛化测试:100 张随机地图 numTestMaps = 100; successCount = 0; pathRatioSum = 0; for i = 1:numTestMaps testMap = generateRandomMap(20, 0.2); % 20% 障碍物 testGoal = [20 20]; env = PathPlanningEnv(testMap, testGoal); obs = reset(env); path = [1 1]; isDone = false; tic; while ~isDone action = getAction(agent, obs); [obs, ~, isDone, info] = step(env, action); path = [path; info.RobotPos]; end inferTime = toc; if isequal(info.RobotPos, testGoal) successCount = successCount + 1; astarLen = computeAStarLength(testMap, [1 1], testGoal); pathRatioSum = pathRatioSum + size(path,1) / astarLen; end end fprintf('成功率: %.2f%%, 平均路径长度比: %.2f, 单步推理: %.1f ms\n', ... successCount/numTestMaps*100, pathRatioSum/successCount, inferTime/200*1000);如果成功率低于 80%,或者路径长度比超过 1.5,我一般不会继续在这个配置上投入,而是回头改状态表示或奖励函数。如果指标达标,下一步是把 MATLAB 训练好的网络导出成 ONNX 或 C 代码,部署到机器人控制器上。MATLAB 的exportONNXNetwork函数可以直接把 dlnetwork 导出,但要注意 DQN 的 Q 网络输出是离散动作的 Q 值,部署时还需要在外面包一层 argmax。
注意:MATLAB 训练时用的状态归一化和部署时的传感器预处理必须一致。我踩过一次坑,训练时状态归一化到 [0,1],部署时忘了做除法,机器人直接往反方向跑。这种问题在仿真里看不出来,只有上真机才暴露。
最后一个习惯:每次改完奖励函数或网络结构,先跑 200 个 episode 看趋势,不要一上来就 2000 个 episode。DQN 训练时间不短,早期快速验证比后期调参重要。希望帮到你。
本文还有配套的精品资源,点击获取