
简介这份资源面向无人艇USV控制、深度强化学习入门与课程设计人群聚焦基于DQN的避障控制问题。包内提供无人艇与巡逻艇的运动学建模脚本、DQN训练主流程、经验回放与目标Q值计算、奖励函数设计、ε-greedy策略及模糊集合等模块可用于复现避障仿真实验、理解强化学习在欠驱动水面航行器上的落地方式。资源共12个文件以11个m脚本和1个mat数据文件为主m文件承担建模、训练与仿真逻辑mat文件保存训练过程或环境数据压缩包约24.47MB结构紧凑便于按模块查阅。目前已有6280人学习下载适合希望从零搭建DQN避障仿真、对照调试奖励与收敛问题的读者参考也可作为相关课题的代码基线。1. 从一条翻船视频说起DQN 到底能不能管住无人艇刷到过一条无人艇湖试视频船在开阔水面跑得好好的一进桥墩附近的回流区就开始画龙左满舵右满舵来回摆最后擦着桥墩蹭过去。评论区一堆人说PID 没调好但做过水面无人艇USV的人心里清楚这大概率不是 PID 的问题——是环境扰动的时变性和控制策略的固定性对不上。风、浪、流、浅水效应、岸壁效应这些在传统控制里要么被当成有界扰动压住要么被前馈补偿掉可一旦工况切换参数就得重调。这正是深度强化学习 DQN 切入无人艇避障控制的现实动机让策略自己从交互里学而不是靠人把每种工况都标定一遍。这篇笔记讲的是基于深度强化学习 DQN 的无人艇避障控制落地平台是 MATLAB。适合两类人一类是手里有无人艇模型或想搭一个、准备用强化学习做避障的在校生和工程师另一类是已经跑过 PID、A*、人工势场想看看 DQN 到底能不能接上、值不值得投入的人。我会把状态设计、动作离散化、奖励函数、训练循环、MATLAB 里的实现细节和踩过的坑都摊开讲能照着复现也能看清边界。2. 为什么是 DQN 而不是 PID 或势场法选型理由与问题建模2.1 避障控制的三条技术路线对比在动手写代码之前先把选型这件事说清楚否则后面调参全是玄学。无人艇避障控制常见三条路线传统控制PID 制导律、几何/势场法人工势场、A*、RRT、学习类方法强化学习、模仿学习。它们不是替代关系而是适用边界不同。路线核心假设优势明显短板PID 制导扰动有界、模型近似线性实现快、可解释、实时性好工况切换要重调参动态障碍难处理人工势场 / A*环境已知或可感知路径清晰、易调试局部极小值、动态障碍抖动、势场参数敏感DQN 类强化学习可交互采样、奖励可设计能学非线性策略、适应动态障碍样本效率低、训练不稳、奖励难设计DQN 的价值在于它把避障从设计规则变成设计奖励 让策略自己找路。代价是训练成本高、收敛看运气。所以我的判断是静态已知环境用 A* 就够了别上 DQN只有动态障碍多、工况变化频繁、又拿不到精确模型时DQN 才值得投入。2.2 把避障问题写成 MDP状态、动作、奖励三件套DQN 求解的是马尔可夫决策过程落地第一步是把无人艇避障写成 MDP 四元组S, A, P, R。这一步设计错了后面训练再久也白搭。状态 S 的设计要包含三类信息自身运动状态、障碍相对位置、任务目标方向。我一般用这样的状态向量% 状态向量构造state [v, omega, d_obs, theta_obs, d_goal, theta_goal] % v 当前航速 (m/s) % omega 当前艏摇角速度 (rad/s) % d_obs 最近障碍距离 (m)归一化到 [0,1] % theta_obs 障碍相对方位角 (rad)归一化到 [-1,1] % d_goal 目标点距离 (m)归一化 % theta_goal 目标相对方位角 (rad)归一化 function s buildState(usv, obs, goal) s zeros(1,6); s(1) usv.v / usv.v_max; s(2) usv.omega / usv.omega_max; [d_o, th_o] nearestObstacle(usv, obs); s(3) min(d_o / usv.sensor_range, 1); s(4) th_o / pi; d_g norm(goal - usv.pos); th_g wrapToPi(atan2(goal(2)-usv.pos(2), goal(1)-usv.pos(1)) - usv.psi); s(5) min(d_g / usv.map_diag, 1); s(6) th_g / pi; end逻辑说明状态里同时放障碍和目标两组极坐标信息是为了让网络能同时权衡躲和去。参数上所有量都做了归一化因为 DQN 的输入层对量纲敏感不归一化会导致某些维度梯度主导训练直接跑偏。sensor_range是感知半径map_diag是地图对角线长度这两个参数决定了归一化尺度换地图要重设。动作 A 用离散化这是 DQN 的硬约束——它只能处理离散动作空间。常见做法是把连续舵角离散成 5 档或 7 档% 动作空间5 档舵角 固定推力 action_set [-30, -15, 0, 15, 30] * pi/180; % 舵角 (rad) % 索引 1~5 对应上述舵角推力保持恒定或按航速闭环奖励 R 是最容易翻车的地方。稀疏奖励只有到达目标才给正奖励会导致训练初期几乎学不到东西奖励太密又会让策略钻空子。我一般用分段塑形奖励function r computeReward(usv, obs, goal, prev_dist) d_goal norm(goal - usv.pos); [d_obs, ~] nearestObstacle(usv, obs); r 0; % 1) 靠近目标给正奖励势能塑形 r r 1.0 * (prev_dist - d_goal); % 2) 碰撞重罚 if d_obs usv.safe_radius r r - 100; end % 3) 离障碍太近给连续惩罚鼓励保持安全距离 if d_obs usv.warn_radius r r - 0.5 * (usv.warn_radius - d_obs); end % 4) 到达目标 if d_goal usv.goal_radius r r 200; end % 5) 时间惩罚逼它别磨蹭 r r - 0.01; end参数说明safe_radius是碰撞判定半径warn_radius是预警半径一般取 safe_radius 的 2~3 倍goal_radius是到达判定半径。势能塑形项(prev_dist - d_goal)是关键它把稀疏的到达信号变成每步都有反馈的稠密信号是 DQN 能训起来的前提。但系数不能太大否则策略会为了靠近目标而贴着障碍走碰撞惩罚压不住。2.3 网络结构与超参数的初始设定MATLAB 里可以用 Deep Learning Toolbox 或 Reinforcement Learning Toolbox 搭 DQN。网络不用太深两层全连接足够% DQN 网络输入 6 维状态输出 5 个动作的 Q 值 layers [ featureInputLayer(6, Normalization, none) fullyConnectedLayer(64) reluLayer fullyConnectedLayer(64) reluLayer fullyConnectedLayer(5) % 输出维度 动作数 ];超参数初始值我一般这样设经验回放池 10000批大小 64折扣因子 0.95学习率 1e-3目标网络更新间隔 200 步ε 从 1.0 线性衰减到 0.05。折扣因子取 0.95 而不是 0.99是因为避障任务里远期奖励不确定性大折扣太接近 1 会让 Q 值发散。这些值不是最优是能跑起来的起点后面按训练曲线调。3. 在 MATLAB 里把 DQN 避障跑起来环境、训练与验证3.1 搭一个最小可用的无人艇运动学环境训练之前得有个环境。MATLAB 里最省事的做法是写一个继承自rl.env.MATLABEnvironment的类或者干脆用函数式循环。我倾向后者调试直观。无人艇用三自由度运动学模型简化版忽略横摇纵摇function [next_state, reward, done] stepEnv(state, action_idx, obs, goal, usv) % 动作映射 delta usv.action_set(action_idx); % 三自由度运动学更新简化 usv.psi usv.psi usv.v * tan(delta) / usv.L * usv.dt; usv.pos usv.pos usv.v * [cos(usv.psi), sin(usv.psi)] * usv.dt; % 边界约束 usv.pos max(min(usv.pos, usv.map_max), usv.map_min); % 计算奖励与终止 prev_dist norm(goal - state(1:2)); % 注意这里用上一状态位置 [d_obs, ~] nearestObstacle(usv, obs); done false; if d_obs usv.safe_radius done true; % 碰撞终止 end if norm(goal - usv.pos) usv.goal_radius done true; % 到达终止 end next_state buildState(usv, obs, goal); reward computeReward(usv, obs, goal, prev_dist); end逻辑说明usv.dt是仿真步长一般取 0.1~0.2 s太小训练慢太大运动学失真。usv.L是艇长用于舵角到艏摇的转换。这里用的是简化运动学真实艇还要考虑惯性、阻尼、推力响应但作为 DQN 训练环境简化模型足够验证策略逻辑等策略收敛再上高保真模型做迁移。3.2 训练主循环经验回放与目标网络怎么接DQN 的两个核心机制是经验回放和目标网络。MATLAB 里手写训练循环比用工具箱更可控% 初始化 replay_buffer struct(s, {}, a, {}, r, {}, s_next, {}, done, {}); buffer_cap 10000; target_net dqn_net; % 目标网络初始与主网络相同 gamma 0.95; epsilon 1.0; eps_decay 0.995; eps_min 0.05; for episode 1:2000 state resetEnv(); total_r 0; for t 1:500 % ε-贪婪选动作 if rand epsilon a randi(5); else q predict(dqn_net, state); [~, a] max(q); end [next_state, r, done] stepEnv(state, a, obs, goal, usv); % 存入回放池 replay_buffer(end1) struct(s,state,a,a,r,r, ... s_next,next_state,done,done); if numel(replay_buffer) buffer_cap replay_buffer(1) []; end % 采样训练 if numel(replay_buffer) 64 batch replay_buffer(randperm(numel(replay_buffer), 64)); % 构造目标 Q 值 for i 1:64 if batch(i).done y(i) batch(i).r; else q_next predict(target_net, batch(i).s_next); y(i) batch(i).r gamma * max(q_next); end end % 反向传播更新主网络此处用 trainNetwork 或自定义梯度 % ... end state next_state; total_r total_r r; if done, break; end end % ε 衰减 epsilon max(epsilon * eps_decay, eps_min); % 目标网络软更新或硬更新 if mod(episode, 20) 0 target_net dqn_net; % 硬更新 end fprintf(Episode %d, Reward %.2f, Eps %.3f\n, episode, total_r, epsilon); end逻辑说明y(i)是 DQN 的目标值done为真时没有后续状态目标就是即时奖励这是终止状态处理的关键漏了这一步 Q 值会系统性偏高。目标网络每 20 个 episode 硬更新一次也可以用软更新target tau*main (1-tau)*target软更新更稳但慢。epsilon衰减到 0.05 后保持保证训练后期还有探索。3.3 训练收敛的判据与验证方法怎么知道训练好了别只看 reward 曲线。我一般看三个指标平均回报的滑动平均是否稳定上升、碰撞率是否降到 5% 以下、到达率是否稳定在 90% 以上。验证时固定随机种子跑 100 个 episode统计这三个数。如果 reward 涨但碰撞率不降说明奖励设计有问题策略在钻空子。% 验证固定策略跑 100 个 episode collisions 0; arrivals 0; for ep 1:100 state resetEnv(); for t 1:500 q predict(dqn_net, state); [~, a] max(q); % 验证时不用 ε-贪婪 [state, ~, done] stepEnv(state, a, obs, goal, usv); if done if norm(goal - usv.pos) usv.goal_radius arrivals arrivals 1; else collisions collisions 1; end break; end end end fprintf(到达率 %.1f%%, 碰撞率 %.1f%%\n, arrivals, collisions);4. 避坑与排查DQN 避障训练里最常见的 5 个翻车现场4.1 现象reward 一直涨但艇在原地打转原因奖励里的时间惩罚太小或者势能塑形项系数过大策略发现原地小幅摆动也能持续获得靠近目标的微小正奖励于是学会了磨洋工。这是典型的奖励钻空子。解决加大时间惩罚从 -0.01 提到 -0.05同时给势能塑形项加一个只有实际位移超过阈值才给奖励的门控。我一般加if norm(usv.pos - prev_pos) 0.01才计入塑形奖励。4.2 现象训练前期 Q 值爆炸输出 NaN原因学习率太大或者奖励量级没归一化。碰撞惩罚 -100 和势能奖励 ±1 差两个数量级梯度被大奖励主导Q 值发散。解决把奖励整体缩放到 [-1, 1] 区间碰撞惩罚改成 -1到达 1势能塑形按比例缩到 ±0.1。学习率从 1e-3 降到 1e-4 试。血泪经验奖励量级不统一是 DQN 训练不稳的头号原因。4.3 现象训练几百 episode 后策略突然退化碰撞率反弹原因经验回放池里早期的高奖励样本被反复采样网络过拟合到旧策略加上目标网络更新太频繁Q 值震荡。解决目标网络更新间隔从 20 拉到 50或者改用软更新 tau0.01。回放池容量加大到 50000让样本分布更均匀。另外检查 ε 是不是衰减太快探索不足会导致策略早熟。4.4 现象仿真里跑得好换一张地图就撞原因状态归一化用了固定尺度比如 map_diag换地图后归一化尺度不匹配网络看到的输入分布变了。这是过拟合到训练地图的典型表现。解决归一化尺度改成动态计算或者用相对量障碍距离除以感知半径而不是除以地图尺寸。更彻底的做法是训练时随机化地图尺寸和障碍布局做域随机化。4.5 现象训练慢到怀疑人生一个 episode 要几十秒原因MATLAB 里用predict逐样本前向传播没有批处理或者环境 step 里做了大量冗余计算比如每步都重算所有障碍距离。解决把状态批量构造用predict的批处理接口障碍距离用 KD-tree 或预计算的空间索引别每步遍历。实测把逐样本改成批处理后训练速度能快 3~5 倍。另外dt别设太小0.1 s 够用。5. 让 DQN 避障真正可用的几个进阶技巧训练能收敛只是及格线要让这套东西在真实无人艇上有意义还得处理几个工程问题。第一个是动作空间的设计。纯离散舵角在低速时控制粒度太粗我一般用离散舵角 连续推力的混合动作空间或者用 DQN 输出舵角档位、再用一个 PID 内环跟踪推力。这样既保留 DQN 的离散优势又不牺牲低速操控性。第二个是奖励的课程学习。一上来就在密集障碍里训练策略几乎不可能探索到目标。我的做法是分三阶段第一阶段空旷水域只学朝目标走第二阶段加少量静态障碍学绕开第三阶段加动态障碍和随机流场学适应。每阶段训练到到达率稳定再进下一阶段。这个技巧能把训练成功率从三成提到八成以上。第三个是仿真到实艇的迁移。仿真里的运动学模型和真实艇差得远直接迁移大概率翻车。常见做法是在仿真里加域随机化随机化艇的阻尼系数、推力响应时间、传感器噪声让策略见过足够多的变体再上真艇时用少量在线微调。MATLAB 里可以把这些随机参数做成环境 reset 时采样的变量。进阶技巧解决的问题关键参数混合动作空间低速控制粒度粗舵角档位数、推力 PID 增益课程学习密集障碍下探索失败阶段数、每阶段收敛阈值域随机化仿真到实艇迁移差阻尼/推力/噪声的随机范围最后一个技巧是验证别只看成功率。我习惯在验证时记录最小障碍距离的分布而不只是碰撞与否。因为一个策略可能到达率很高但每次都是擦着障碍过去这种策略上真艇就是定时炸弹。最小障碍距离的中位数应该稳定在安全半径的 1.5 倍以上才算真正可用。说个我自己的教训早期做这套东西时我盯着 reward 曲线调了两周越调越乱后来才发现问题根本不在超参数而在状态里漏了障碍相对方位角这一维网络根本分不清障碍在左还是在右。从那以后我养成的习惯是训练不收敛先回去检查状态和奖励的设计别急着调学习率。状态和奖励对了DQN 自己会找到路设计错了调参就是给自己找安慰。希望帮到你。本文还有配套的精品资源点击获取