简介:面向水下机器人、人工智能、自动化等相关专业学生的毕业设计级项目,基于深度强化学习实现水下障碍物规避,提供从仿真到真机部署测试的完整源码与说明文档,简单部署即可运行。压缩包共30个文件,核心由18个Python脚本组成,涵盖网络模型定义、训练测试流程以及Gazebo仿真控制逻辑;同时包含2个ROS.launch启动文件、1个Gazebo world仿真场景、水下图像样例与README说明文档,体积仅301KB,便于按需查阅。项目内置DDDQN避障决策模块、FCRN单目深度估计模型,并针对BlueRobotics、Husarion等常见水下/地面机器人平台提供实机部署与测试脚本,配合训练/测试数据索引,可支撑课题演示、论文复现及二次开发。目前已有59人学习下载,适合作为毕业设计、课程设计或初期项目立项的快速起点。
1. 深度强化学习水下机器人避障:毕设选题前先想清楚的一件事
如果你正在为“基于深度强化学习的水下机器人避障”这个毕业设计题目发愁,大概率是被三件事卡住:环境怎么搭、避障策略怎么训、以及训练出来的模型到底能不能用。这个题目之所以在毕设和课程设计里越来越常见,是因为它把深度学习、强化学习、机器人运动控制三个方向串成了一条线,既有理论深度,又能出可视化效果,而且模型训练好了以后,确实可以做到“给个目标点就能自己躲开障碍物”。
不过先说句实话:这个题目的下限很低,上限很高。网上流传的“功能完善、操作简单、简单部署即可运行”的项目包,多数只是把训练环境和一套 PPO/DQN 代码打成一个 zip,真正决定你毕设分数的,是你能不能讲清楚状态空间、奖励函数和仿真环境里的坑。这篇文章就按“原理 — 部署 — 训练 — 验证 — 踩坑”的顺序,把一条能落地的路径完整讲给你。
2. 从观测到动作:把水下避障拆成马尔可夫决策过程
2.1 深度强化学习模型到底在学习什么
水下机器人避障不是图像识别那种“输入图片、输出类别”的监督学习,而是让 agent 在环境里试错,通过最大化累计奖励来学会策略。这个策略就是“当前状态下该往哪个方向、以多大速度运动”的映射。避障任务的特殊性在于:水下没有 GPS 那样可靠的全局定位,声呐噪声大,水流扰动强,机器人的运动模型又是非线性的,所以不能简单套用地面小车的“激光雷达 + DWA 局部规划”那套思路。
把问题形式化为马尔可夫决策过程之后,你会发现避障本质上是两个问题叠加:一是“我在哪、障碍物在哪个方向”,也就是状态估计;二是“下一步怎么动”,也就是动作选择。深度强化学习的意义在于,这两个问题可以端到端解决——深度网络直接消化声呐点云或声呐特征,输出连续速度指令,不需要手工写规则。
常见做法是采用 Actor-Critic 结构:Actor 网络负责输出动作,Critic 网络负责评估这个动作在当前状态下的价值。训练过程中,Critic 教会 Actor 如何调整动作分布。水下机器人的避障环境动作空间是连续的(前进速度、偏航角速度),所以 Q-learning 系算法很难直接用,实际项目里主流选型集中在 PPO、DDPG、SAC 之间。下一节的状态空间和动作空间定义,会直接影响算法收敛速度,也是毕设答辩时最容易被抓着问的地方。
2.2 状态空间怎么定:声呐、相机、里程计融合的常见做法
水下机器人避障的状态空间设计通常有四个层次:
- 原始传感器层:单波束声呐的距离读数、前视声呐(FLS)的一维或二维强度图、水下相机的灰度帧。
- 特征提取层:对声呐读数做归一化,把距离值映射到 0~1;对图像做降采样,或先送入卷积网络提特征。
- 机器人自身状态层:当前速度、角速度、朝向(IMU 姿态)、与水下目标的相对位置。
- 目标信息层:目标点相对机器人的方向角和距离。
毕设项目里最稳妥的做法是“声呐测距 + 自身速度 + 目标方向”组合成低维向量,而不要一上来就上原始相机图像。原因有两个:第一,高维图像端到端训练需要海量交互数据,仿真里跑几万步根本不够;第二,答辩时你很难解释图像特征和避障行为的因果关系。使用低维向量状态时,网络结构可以做得非常浅——两层全连接加一层 LSTM 就足够应付大多数仿真场景,训练速度快,也更稳定。
下面给出一段状态空间构造的示例代码,这是把声呐读数、机器人当前前进速度、目标方向角拼成一条状态向量的典型写法:
def get_observation(sensor, vehicle): """ 组装避障状态向量 sensor: 声呐模块,提供 n 个方向上的距离读数 vehicle: 机器人本体,提供前进速度 u、侧向速度 v 和航向角 psi """ # 1. 声呐距离:截断到 5 米内,然后归一化到 0~1,越近值越接近 0 sonar_ranges = np.clip(sensor.ranges, 0.0, 5.0) / 5.0 # 2. 目标方向:把目标点相对机器人的方位角转换到 [-pi, pi],再做 sin/cos 编码 angle_to_target = normalize_angle(target_polar_angle) target_encoding = [ np.sin(angle_to_target), np.cos(angle_to_target) ] # 3. 机器人当前速度:前进速度与偏航角速度 velocity_obs = [ vehicle.x_dot / 1.0, # 最大前进速度按 1.0 m/s 归一化 vehicle.yaw_dot / 1.0 # 最大角速度按 1.0 rad/s 归一化 ] # 合并成状态向量 obs = np.concatenate([sonar_ranges, target_encoding, velocity_obs]).astype(np.float32) return obs逻辑说明:声呐读数归一化非常关键,原始读数从小数厘米到几十米,量级差异会导致网络输入不稳定;目标方向用 sin/cos 编码而不是直接用角度值,是为了避免角度跨越 ±π 时出现的跳变;速度项告诉 agent“我现在动得多快”,没有这一项,策略会分不清“该减速避障”和“已经撞上之后才反应”的区别。
参数说明:声呐方向数一般取 8~16 个,小于 8 个会遗漏侧向障碍信息,大于 16 个在低维 MLP 里边际收益很低。截断距离 5 米不是随便定的,要大于单步避障的安全距离,又要小于声呐有效量程的 70%,这样在训练早期 agent 才能感知到“远处有东西”,而不是只有撞上才看见。
2.3 动作空间与奖励函数:稀疏奖励为什么是避障训练的头号难题
动作空间在连续避障任务里通常是二维或三维的:前进速度、偏航角速度,加上可选的俯仰角速度。航行的机器人一般不做横移,所以 SAUV/ROV 类的动作可以简化成以下形式:
# 连续动作:前进速度 (surge) 和偏航角速度 (yaw rate) action = np.zeros(2) action[0] = 0.5 * np.tanh(actor_output[0]) # 速度范围映射到 [-0.5, 0.5] m/s action[1] = 0.8 * np.tanh(actor_output[1]) # 角速度范围映射到 [-0.8, 0.8] rad/s奖励函数是强化学习里最玄学的部分。新手容易犯的错误是把奖励设计成稀疏的“到达目标 +1、碰撞 -1”,这样 agent 在前期几乎拿不到任何梯度信号,十几万步还在原地打转。常见做法是用稠密奖励 + 分阶段塑形:
- 距离惩罚:每一步对“到目标点的距离变化”进行奖励,距离变近给正奖励,变远给负奖励。
- 碰撞惩罚:碰撞发生时给一个较大的负奖励,而不是简单结束回合。
- 航向奖励:目标方向角和当前航向的夹角越小越好,引导机器人“面朝目标”。
- 时间惩罚:每一步给一个小的负值,避免策略学成在水里原地转圈耗时间。
下面是我在毕设项目里常用的稠密奖励函数实现:
def compute_reward(state, action, new_state, done): """ 稠密奖励:距离变化 + 航向对齐 + 碰撞惩罚 + 时间惩罚 """ # 1. 距离变化奖励:这次比上次离目标近了就加分 delta_dist = state.distance_to_target - new_state.distance_to_target reward_dist = 2.0 * delta_dist # 2. 航向奖励:目标方位角与机器人当前航向的差,越小越好 heading_error = abs(normalize_angle(new_state.heading_to_target - new_state.heading)) reward_heading = -0.1 * heading_error # 3. 对高速行为的软惩罚,防止它一味横冲直撞 reward_slow = -0.05 * abs(new_state.surge_velocity) # 4. 碰撞与大奖励 reward_collision = -10.0 if new_state.collision else 0.0 # 5. 时间惩罚,让策略学会尽量快 reward_time = -0.01 reward = reward_dist + reward_heading + reward_slow + reward_collision + reward_time return float(reward)参数说明:距离变化奖励系数 2.0 是经验值,系数过大 agent 会只朝目标猛冲、忽视避障;航向奖励系数 0.1 太小,策略容易忽略朝向。碰撞惩罚 10.0 要和单步距离奖励的量级匹配——如果你设成 100,而距离奖励每步最多 2,agent 会干脆停在原地不动。训练时建议先关掉航向奖励跑 5000 步,观察 agent 是否至少学会接近目标,再逐步打开,否则多个奖励互相撕扯,很难定位问题。
2.4 各种深度强化学习算法列表对比与选型依据
我在项目里见过不少同学纠结选哪种深度强化学习算法,这里给出结论式的对比表,按水下避障这个特定场景排优先级。
| 算法 | 动作空间 | 样本效率 | 训练稳定性 | 水下避障适用度 | 主要原因 |
|---|---|---|---|---|---|
| PPO | 连续/离散 | 中 | 高 | ★★★★★ | clip 机制防止策略更新过猛,调参难度最低,仿真环境里最常用 |
| SAC | 连续 | 高 | 中 | ★★★★☆ | 熵项鼓励探索,样本利用率好,但温度系数敏感,容易不稳 |
| DDPG | 连续 | 低 | 低 | ★★★☆☆ | 确定性策略噪声处理麻烦,对超参和随机种子敏感 |
| DQN 及其变体 | 离散 | 中 | 高 | ★★☆☆☆ | 只能输出离散动作,适合做离散方向决策,不适合连续速度控制 |
| TD3 | 连续 | 中 | 中 | ★★★★☆ | DDPG 的改进版,双 Q 网络延迟更新,比 DDPG 稳,但实现复杂 |
如果你的毕设目标是“能跑起来、能出论文图、能讲清楚原理”,我建议无脑选择 PPO 或者 SAC。网上各种深度强化学习算法列表对比贴子很多,但真正适合水下机器人避障的很少,因为水下环境的状态噪声和奖励延迟比地面小车大得多。PPO 对奖励缩放不那么敏感,而且是 on-policy 算法,每一步采样都用来更新,出图比较快;SAC 虽然样本效率高,但熵系数的自动调参在声呐噪声环境下容易震荡。
算法选定后,网络结构也值得在论文里写一笔:Actor 和 Critic 可以共享底层特征提取层,再各自输出动作分布和状态价值。输入维度上,如果是 16 维声呐 + 4 维自身状态 + 2 维目标信息,那第一层用 128 个神经元就够了;水下环境不需要超大网络,深度加深反而会让策略收敛变慢。
3. 简单部署即可运行:从环境依赖到跑通第一个训练闭环
3.1 训练环境搭建:Python、框架与仿真器的最简组合
拿到任何“简单部署即可运行”的项目包,第一步都不是双击运行,而是先确认三件事:Python 版本、深度学习框架、仿真环境接口。常见组合有两种:
第一种是纯 Python 仿真环境,比如 gym 风格的水下环境,状态和动作都是数组,渲染可选。优点是部署最简单,pip 装上稀疏依赖就能跑,适合做算法对比和调参;缺点是物理真实性一般,水动力、声呐噪声都是人为模拟的。
第二种是 ROS + Gazebo + UUV Simulator,这是毕设里最容易出效果、也最容易翻车的组合。Gazebo 里能导入水下机器人模型,UUV Simulator 提供了推进器、水动力模型,你还可以挂上声呐插件模拟近距离障碍物检测。但 ROS 版本和 Gazebo 版本的匹配非常折磨人,Ubuntu 20.04 上比较稳的是 ROS Noetic + Gazebo 11,Ubuntu 22.04 则要 ROS 2 的另一个分支。
毕设如果不想在环境上耗两周,我建议先走纯 Python 仿真,把 PPO 训练闭环跑通,再花时间把训练好的模型接到 Gazebo 里做可视化验证。下面是从零开始装一个最小 python 环境的过程:
# 创建虚拟环境,避免和系统 Python 打架 python3 -m venv abyss_env source abyss_env/bin/activate # 安装深度学习框架,CPU 版本足够跑仿真 pip install torch==2.0.1 --index-url https://download.pytorch.org/whl/cpu # 安装强化学习基础库和任务接口 pip install gym==0.26.2 numpy # 如果项目自带源码包,进入源码目录后用 setup.py 自动装剩余依赖 cd underwater_avoid_pp_bishe pip install -e .逻辑说明:torch 安装指定了 CPU 版本是因为避障环境本身计算量不大,CPU 上跑几千步也能完成训练,装 GPU 版反而会引入 CUDA 版本匹配问题,对毕设部署没有意义。gym 版本对齐很重要,0.26 之后 reset 接口返回格式变化,很多老代码会报错。
参数说明:Python 不要用 3.11 以上,部分强化学习库对 3.11 的支持仍旧够呛;如果你拿到的是老项目 zip,里面大概率写着 tensorflow 1.x 的代码,那千万别装 tensorflow 2.x 硬跑,要么找现成的迁移脚本,要么干脆换成 torch 重写 agent 部分。
3.2 最小可运行的 PPO 避障训练主循环
一个能跑出避障行为的 PPO 训练脚本,核心只需要三块:环境交互、经验收集、策略更新。下面这份代码省去了 TensorBoard 等边角,聚焦在“这个 agent 到底怎么学”:
from stable_baselines3 import PPO def make_env(): """构造水下避障环境,要求环境类实现 reset/step/reward""" import underwater_env # 从项目包导入的环境类 return underwater_env.UWEnv() # 1. 初始化 PPO 策略 # policy = "MlpPolicy":使用多层感知机处理低维状态向量 # n_steps = 2048:每收集 2048 步经验后做一次更新,控制更新频率 # batch_size = 64:从经验里随机采的小批量大小 # learning_rate = 3e-4:PPO 常用初始学习率 # gamma = 0.99:折扣因子,0.99 表示更看重长期回报 # clip_range = 0.2:新旧策略比值裁剪范围,防止单次更新过大 model = PPO( "MlpPolicy", make_env(), n_steps=2048, batch_size=64, learning_rate=3e-4, gamma=0.99, clip_range=0.2, verbose=1, seed=42 ) # 2. 训练 # total_timesteps = 200_000:总共采样的步数,不是训练轮数 # 每一步等于 agent 执行一个动作并在环境里推进一个仿真周期 model.learn(total_timesteps=200_000) # 3. 保存模型,防止训练到一半程序崩溃后从头再来 model.save("ppo_underwater_avoid.zip") print("训练完成,模型已保存。")逻辑说明:stable_baselines3 是常用的 PPO 实现库,内部帮我们处理了 GAE(广义优势估计)和 clip 裁剪逻辑。上面的脚本里看不到策略更新的数学细节,但你要能解释清楚clip_range的作用——当新策略和旧策略在某个状态下输出动作的概率比超过 0.2 或者低于 0.8 时,梯度被裁剪掉,避免一步更新太猛导致策略崩溃。
参数说明:n_steps=2048对应 on-policy 算法的样本收集量,太小则每次更新方差大,太大则训练变慢;batch_size=64是 PPO 内部做 mini-batch 梯度下降时的采样大小,一般取 32 到 256 之间;gamma=0.99在水下避障里比较合适,因为任务周期短,不需要太远期的回报。seed一定要固定,否则同一个项目包在你电脑上复现的结果和作者跑出来的完全不同,答辩时没法讲故事。
3.3 参数怎么设:一个能稳定收敛的调参顺序
调参是毕设项目里最耗时的环节,也是最容易放弃的环节。我总结了一套从项目包里拿到代码后必须检查的参数清单,按重要性排序:
| 参数 | 推荐范围 | 不合适的后果 |
|---|---|---|
| 奖励缩放系数 | 1.0 - 5.0 | 过大导致策略激进,过小导致不想动 |
| 声呐距离截断 | 3 - 8 米 | 过小感知不到障碍,过大奖励梯度稀疏 |
| PPO n_steps | 1024 - 4096 | 过小更新频繁训练不稳,过大训练慢 |
| PPO clip_range | 0.1 - 0.3 | 过大导致策略震荡,过小收敛慢 |
| 熵系数 | 0.0 - 0.01 | 太大策略乱走,太小过早陷入局部最优 |
| 训练总步数 | 10万 - 50万 | 少于 5 万很难看出明显避障行为 |
训练刚开始时,不要盯着奖励曲线看,要看两个更直观的行为指标:平均到达目标率、碰撞率。如果碰撞率居高不下,优先猜测是奖励惩罚不够,把碰撞惩罚从 10 调到 20 看看;如果 agent 一直原地转圈,则多半是熵系数太高或航向奖励太弱。这类“现象—原因—解决”的排查思路,比漫无目的地网格搜索参数有效得多。
4. 让策略在水下仿真里进化:训练监控与可视化验证
4.1 仿真环境选型:Gazebo 还是轻量级 python 环境
毕设项目源码包里通常已经写好了一个环境,但你要判断它属于哪一类。轻量级 python 环境适合算法对比,可视化能力弱,只能画出二维轨迹和声呐扇形图。Gazebo + UUV Simulator 则能提供三维的、带水动力效应的场景,机器人的俯仰、翻滚、推进器响应都会体现在状态里,但训练成本高一个量级。
我的习惯是:先在轻量环境里把 PPO 训练到“能绕开静态障碍物”,再把训练好的模型权重导出,接到 Gazebo 里做固定场景验证。改环境而不是重训模型,因为 Gazebo 仿真的速度可能只有实时速度的十分之一,50 万步的训练会让人怀疑人生。
如果你确实要用 Gazebo 训练,也有一个折中方案:用 Gazebo 并行启动多个机器人实例,也就是 vectorized environments,同一批 GPU/CPU 上同时跑 8 个环境,训练速度能快 5 倍以上。Prro 库里的SubprocVecEnv可以并行采集经验。但要注意,并行环境里的随机种子要错开,否则八个环境完全一样,相当于只用一个环境跑。
4.2 连续空间动作噪声:声呐噪声和动作平滑的博弈
水下场景和地面小车最大的不同,是声呐测距经常出现“丢帧”和“突变”。前一帧读数是 4.5 米,下一帧直接变成 1.2 米,这一跳对策略的决策影响很大。处理这个问题的常见做法是给状态输入加一个低通滤波:
class LowPassFilter: """一阶低通滤波器,平滑声呐输入""" def __init__(self, alpha=0.2): self.alpha = alpha self.value = None def update(self, new_value): if self.value is None: self.value = new_value else: self.value = self.alpha * new_value + (1 - self.alpha) * self.value return self.value逻辑说明:alpha=0.2表示新读数只占 20%,大部分权重留在历史值上。这样能滤掉声呐突发噪声,但也会引入相位延迟——障碍物已经出现在很近的范围时,滤波后的值还偏大。所以毕业设计的验证环节,滤波后的碰撞率才是有效指标,你不要只看滤波前的不稳定读数。
动作这边同样需要平滑。PPO 输出的是动作分布的均值,直接用它作为速度指令容易让机器人动作发抖。更稳的做法是让动作输出经过一个速率限制器,限制每步速度变化不能超过 0.1 m/s。这相当于机器人本身的动力学约束,也避免训练出的策略因为在仿真里可以瞬间变速而在真实平台上无法应用。
4.3 训练监控与模型保存:TensorBoard、checkpoint 和评估回放
训练过程不能只粘贴一个奖励曲线图。毕设答辩时,评审老师更想看到“这个策略是逐步学出来的”,而不是“最后跑通了一次”。所以训练代码里至少要保留三类日志:
- 标量指标:每 1000 步打印一次平均奖励、平均碰撞数、平均到达目标数。
- 模型快照:每 10000 步存一个 checkpoint,这样如果 20 万步后策略崩了,你还能回到 15 万步的模型接着调。
- 轨迹回放:每 5000 步让当前模型在固定环境里跑 10 局,记录每一局是否避开障碍。
下面是加 checkpoint 和评估流程的最小写法:
# 模型保存间隔 save_interval = 10000 eval_interval = 20000 for step in range(total_steps): model.learn(total_timesteps=eval_interval, reset_num_timesteps=False) # 保存一个带步数编号的检查点 model.save(f"checkpoints/ppo_avoid_{step//save_interval:03d}.zip") # 固定 5 个随机种子,评估当前策略的避障成功率 success_count = 0 for seed in range(5): env = make_env(seed=seed) success_count += eval_once(model, env) print(f"Step {step}: 避障成功率 {success_count/5:.2f}")逻辑说明:这种“训一段、评一次”的方式比一次性训完再评估更可靠,因为强化学习训练过程中策略会经历“学到一点—剧烈波动—再学到一点”的过程,避开某个糟糕中间态的模型反而是最优的。checkpoint 文件一定要带步数和评估结果的后缀,否则你事后根本分不清哪个模型是哪一次存出来的。
参数说明:reset_num_timesteps=False是 stable_baselines3 里常见的坑,如果不加这个参数,每次调用learn都会把总步数清零,导致学习率调度器重新开始衰减;加了False才能让三个learn调用连贯起来。评估用的随机种子和训练种子错开,避免只会在一条固定航线上避障的“背题策略”。
4.4 把模型从轻量环境迁移到可视化场景
如果项目包里提供了 ROV 水下机器人模型和三维场景,那么迁移验证就是一个加分项。迁移时最常遇到两个问题:一是状态维度不匹配,轻量环境的声呐可能是 16 个方向,Gazebo 里声呐插件不一定输出同样数量的读数,你需要写一个适配层,把不同角度的声呐读数插值到统一维度。二是动作范围不一致,Gazebo 的推进器响应有延迟和饱和,需要把轻量环境训练时的动作限制条件再套一遍。
一个实用技巧是迁移时不重新训练,而是冻结策略网络,只对最终速度输出做一次线性缩放。让机器人先以 50% 的模型输出速度运行,观察避障行为是否成立,再逐步提高比例。这比重新训练快得多,也能在答辩时展示“仿真部署时的工程化思考”。
5. 毕设避坑:水下机器人避障项目最常见的 5 个翻车现场
5.1 从 zip 源码包解压开始报错,项目没法运行
现象:按教程安装完依赖,运行python main.py立刻报ModuleNotFoundError或者ImportError: cannot import name from stable_baselines3。
原因:多数毕设项目源码是在作者自己电脑上写的,依赖版本被隐式绑定。常见情况是作者用了stable_baselines3>=2.0,但网上教程安装的还是 1.x;或者作者用了自定义 gym 环境名,包没有正确安装到虚拟环境里。
解决:不要硬解缺什么装什么。第一步pip list看核心库版本,第二步去项目setup.py或requirements.txt里找锁定版本,第三步建新虚拟环境后按 requirements 一次性安装。如果项目没有 requirements,就用 pip freeze 从作者环境里导出依赖的在线教程(这段我没见过项目包,只能给通用做法)。
5.2 训练了 3 个小时,奖励曲线一直在负区域徘徊
现象:TensorBoard 里的平均奖励没有上升趋势,偶尔跳到 -50 又落回 -30;机器人始终往一个方向撞墙,或者原地打转完全不接近目标。
原因:奖励函数里距离变化奖励占主导,但状态空间没有包含目标方向,agent 猜不到目标在哪里;或者是碰撞惩罚设置太大,agent 学会了“不动就是最安全”。
解决:先检查状态向量里是否包含目标相对角度。如果不包含,加目标编码是第一步。然后做“单目标贴近测试”——把障碍物全部移除,只用最近目标点训练 5000 步;如果这都学不会接近目标,那就是奖励和状态不匹配,先解决这个基础问题再引入障碍物。
5.3 训练中途程序崩溃,模型参数全部丢失
现象:跑了 30 万步,因为电脑休眠、内存不足或者 CUDA 报错,程序退出,所有模型状态只在内存里,没保存过。
原因:训练脚本只在结束后保存,没有做周期性 checkpoint;很多毕设项目也用 Jupyter Notebook 跑训练,中断后 kernel 重启就全没了。
解决:训练循环里必须加model.save("checkpoints/milestone_xxx.zip"),间隔不超过 20000 步。另外,推荐用命令行脚本而不是 notebook 跑长训练,nohup python train.py > train.log 2>&1 &挂在后台,崩溃后能从日志定位到最后一次保存点。模型保存是血泪教训,不是什么高级技巧,但吃过亏的人都知道它值多少分。
5.4 改成自己的场景后,声呐数据全是 5.0 米
现象:替换了项目自带的障碍物模型后,机器人就算撞上障碍物,声呐读数也恒定为最大量程,碰撞检测却正常。
原因:声呐插件的 mesh 碰撞体和视觉 mesh 不一致。Gazebo 里超声波传感器默认只检测有collision属性的物体,如果障碍物模型只有视觉 mesh 而没设置碰撞体,射线直接穿过障碍物,读到的就是最大距离。
解决:检查障碍物模型的.sdf或urdf文件,确认<collision>标签存在且几何尺寸和视觉一致。如果没有碰撞体,可以在 Gazebo 编辑器里给模型补一个<geometry><box>的碰撞体,再启动训练。这类问题在纯 python 环境不存在,但凡是接入 Gazebo 的毕设几乎都会撞上,提前写进论文的“仿真环境搭建”章节反而能加分。
5.5 换一个随机种子,避障成功率从 80% 掉到 10%
现象:同一个模型文件,在种子 A 下成功避开障碍,在种子 B 下却直接撞上;评审问你是不是过拟合,你答不上来。
原因:PPO 在固定场景下训练容易记忆障碍物位置而不是学习避障策略。如果你验证时用的随机种子刚好和训练种子重叠,那成功率虚高;换个新种子就暴露了策略没有泛化能力。
解决:训练过程中就要不断更换障碍物布局和 spawn 位置,而不是固定一个场景。常见做法是每 5000 步重新随机生成障碍物位置,但保持障碍物数量一致。验证时使用 10 个以上新种子,报告“平均成功率 ± 方差”。如果方差过大,优先减小策略网络的容量,再增大训练的步数,因为小网络被迫提取更通用的特征,大网络更容易背题。
5.6 避障避得好好的,但机器人永远到不了目标点
现象:agent 学会了躲开所有障碍物,但导航轨迹像贪吃蛇一样绕来绕去,走了很远的路才到达目标,步数严重超标。
原因:奖励函数里避障惩罚和到达目标奖励没有取平衡。agent 发现“绕远路虽然拿不到目标奖励,但避障的负奖励少”,于是路径贪婪地偏离直线。
解决:加入“单位时间达到目标”的奖励,比如每局结束时按剩余时间和路径长度给一个额外奖励,或者直接把距离奖励的系数调大,让“离目标更近”比“避开障碍”更有吸引力。调整后重新观察轨迹,理想行为是接近目标时开始转向避障,躲过后立刻恢复朝目标方向前进。
6. 把毕设做出工作量的最后一招:安全性与消融对比验证
验证深度强化学习避障效果,光说“模型跑通了”是不够的。我建议给项目加两个模块,能显著提高答辩说服力:一个是避障安全性曲线,一个是消融实验。
安全性曲线就是统计模型在不同噪声水平下的碰撞概率。你可以把声呐噪声标准差从 0 逐步加到 0.5,画一条“碰撞率—噪声强度”曲线。这条曲线能说明策略对传感器噪声的鲁棒性,也是水下场景和地面仿真最大的区别。如果碰撞率从噪声 0.1 开始飙升,那就是状态滤波做得不够,回去加低通滤波然后重训。
消融实验是为了证明“深度强化学习”真的起作用了,而不是“不管是什么控制器,在这个环境里都能避障”。至少做三组对比:完整 PPO 模型、去掉航向奖励的 PPO、随机动作策略。每组在相同的 10 个随机种子下评估,记录避障成功率和平均到达时间。如果完整模型和去掉航向奖励的版本差距不大,说明你的航向奖励设计不关键,论文里就把这条从核心创新点里删掉。
最后一条教训:不要把时间花在反复调一个永远不收敛的超参数上。我第一做这个题目时,连续两个星期把学习率从 1e-3 调到 1e-6,奖励曲线纹丝不动,后来才发现是状态向量里的声呐读数忘了归一化,量级差了 100 倍。遇到这种时候,优先检查数据输入和奖励信号的数值范围,再碰算法参数。希望今天的这份路径和踩坑记录能帮到你,少走一个月弯路。
本文还有配套的精品资源,点击获取