简介:一份面向水下机器人研究人员的深度强化学习AUV动态避障系统设计与优化资料,聚焦动态环境中AUV的避障问题,涵盖IMM-EKF障碍物状态预测、DQN多行为网络调用、DDPG-PID水平面避障控制及SumTree-DDPG三维避障算法。内容兼顾理论框架与可运行代码,包含环境配置、模型训练与仿真对比,便于复现和二次开发,适合具备编程基础、对机器人学与自动化控制感兴趣的科研人员和工程师。压缩包内为1个docx文档,大小51KB,以图文代码形式组织,目前已有105人学习。资料中详细解释了各算法原理,并给出传统方法与DRL方法在避障成功率、路径最优性和能耗效率方面的对比结果,可支撑梳状搜索巡检、自主回收等场景下的避障策略开发与安全性提升。
1. 水下自主避障,卡在动态环境里的不是算力,是决策范式
湖试现场,AUV按预设航线向目标点推进,前视声呐在 12 米处扫到一艘缓慢横移的作业船。传统避障模块给出的反应是:向左偏航绕行。可船也在向左移动,等 AUV 绕到左侧,船身刚好挡住去路,最终触发急停上浮——任务中断。这个场景在真实水下机器人项目里反复出现,问题不在传感器没探测到障碍物,而在“探测到之后怎么决策”。基于深度强化学习的 AUV 动态避障系统,把避障从“当前帧路径规划”变成“长期交互决策”,让水下机器人学会预测障碍物运动趋势并提前调整三维轨迹。这篇笔记会围绕多模型感知增强、三维避障奖励设计和仿真训练闭环展开,面向正在做水下机器人设计代码验证、或准备把动态避障方案落地的工程师,给出可直接复现的代码骨架和参数边界。
2. 多模型感知增强:让AUV在水下噪声里“看见”障碍物
2.1 单传感器为什么不够:声呐、视觉、惯性数据的互补边界
水下环境是典型的多传感器互补场景。前视声呐能提供几十米内的扇形障碍物距离信息,但帧率低、角分辨率粗糙,对细长缆绳或小型水下目标经常漏检;光学摄像头在近距、清水环境下能给出丰富纹理特征,可在浑浊水体或低照度下几乎失效;DVL(多普勒测速仪)和 IMU 提供载体速度与姿态,却在悬停或低速时存在漂移。单一传感器做避障感知,要么“看得远但看不清”,要么“看得清但看不久”,这是多模型感知增强存在的根本原因。
所谓“多模型”,这里指的是两条线并行:一条是目标级感知模型,用轻量目标检测网络从前视声呐图像或水下图像中提取障碍物类别与位置;另一条是环境级感知模型,用测距声呐和 DVL 的连续数据流构建局部障碍物栅格。两者输出经过融合后,形成强化学习决策需要的观测向量。相比直接把原始声呐图像喂给策略网络,这种“先感知后决策”的架构让训练更稳定,也更容易在仿真到实机迁移时做域随机化。
2.2 数据对齐与融合:从声呐目标检测到局部栅格置信度
感知增强的核心不在“多”,而在“对齐”。我一般按三个时间步处理:先对各传感器时间戳做最近邻同步,再把声呐、摄像头坐标系下的目标点统一转换到 AUV 载体坐标系,最后对同一障碍物的多源检测做置信度加权。前视声呐的目标检测可以采用轻量 YOLO 变体,输入 640×640 声呐图像,输出障碍物类别、边界框和置信度;同时把测距声呐的回波按角度和距离投影到极坐标栅格,形成一张以 AUV 为中心、半径 20 米、角度分辨率 2 度的局部占用栅格。
融合时的关键参数是栅格单元衰减系数。距离越远,声呐回波越弱,直接取最大值会频繁误报。我用每个栅格单元存储“障碍物存在概率 p”,按帧更新:检测到回波则 p = min(1.0, p + 0.3),未检测到则 p = max(0.05, p - 0.1)。这样既保留历史帧的信息,又不会让瞬时噪声直接触发避障动作。目标检测输出则通过一个跟踪器关联连续帧,目标编号稳定地传入强化学习状态空间,避免同一障碍物在连续帧之间被当成新目标。
2.3 观测向量构造:把感知结果压缩成决策输入
以下代码示意如何把目标检测和栅格融合结果拼装成强化学习的观测向量。实际工程里,这段逻辑放在环境封装类的_get_obs()方法中。
import numpy as np def build_observation(targets, grid_map, auv_state, max_targets=5): """ 将多模型感知结果拼装为固定维度的观测向量。 targets: list of dict, 每项含 x, y, z(载体坐标系), vx, vy, vz, confidence grid_map: (90,) 数组, 前方180度范围内按2度分格的障碍物概率 auv_state: dict, 含 depth, pitch, roll, surge_velocity """ feat_dim = max_targets * 6 + 90 + 6 obs = np.zeros(feat_dim, dtype=np.float32) # 最近邻关联后的目标按置信度排序,取前 max_targets 个 targets = sorted(targets, key=lambda t: t["confidence"], reverse=True)[:max_targets] for i, t in enumerate(targets): idx = i * 6 obs[idx:idx + 6] = [ t["x"], t["y"], t["z"], np.clip(t["vx"], -2, 2), np.clip(t["vy"], -2, 2), np.clip(t["vz"], -2, 2) ] obs[max_targets * 6: max_targets * 6 + 90] = grid_map obs[max_targets * 6 + 90:] = [ auv_state["depth"], auv_state["pitch"], auv_state["roll"], auv_state["surge_velocity"], 0.0, # 目标点相对方位 0.0 # 目标点相对距离 ] return obs参数说明:max_targets=5设定了最多跟踪 5 个障碍物,超出部分丢弃,保证观测维度固定,这是强化学习策略网络全连接层能处理的前提;速度直接 clip 在 ±2 m/s,防止奇异值拉偏训练分布;栅格图和载体状态拼接时保持顺序固定,后续网络输入层维度必须与之一致。auv_state里最后两个 0.0 是目标点相对方位和距离,由全局规划器在每一步填充,这里先留空位。
3. 深度强化学习决策:从状态空间设计到三维避障奖励函数
3.1 算法选型:PPO还是SAC——连续控制与训练稳定性的取舍
水面动态避障小车路径规划常用 DQN 这类离散动作算法,但 AUV 在三维空间里的控制量是连续量:推进器推力、艏摇角速度、垂向推力。离散化动作会带来控制不平滑,在近底或狭水道场景容易诱发振荡。所以深度强化学习算法的选择范围收敛到 PPO 和 SAC 两个主流连续控制算法。
| 对比维度 | PPO | SAC |
|---|---|---|
| 训练稳定性 | 高,clip 机制天然抗策略崩塌 | 中,依赖温度系数自动调节 |
| 样本效率 | 较低,需要更多采样 | 较高,off-policy 重用历史数据 |
| 超参数敏感度 | 低,默认参数通常可用 | 高,学习率、熵系数都要细致调 |
| 部署难度 | 低,单策略网络 | 中,Actor-Critic 加双 Q 网络 |
| 实机迁移 | 策略平滑,便于做安全检查 | 动作探索更激进,初期风险更高 |
我的做法是:仿真阶段先用 PPO 跑通全流程,验证奖励函数和感知融合逻辑是否正确;如果后续需要降低训练时间,再切到 SAC 做加速实验。对多数 AUV 项目,PPO 是更稳妥的起点,这也是基于深度强化学习的移动机器人室内自主导航方法里常用的组合。
3.2 状态空间与动作空间:把“多模型感知”映射成三维机动能力
动作空间设计直接反映三维避障算法和传统平面避障的差异。平面避障小车只需要“线速度 + 角速度”两个控制量,AUV 至少需要三个:纵向推力(前进速度)、垂向推力(下潜/上浮速度)、偏航力矩(转向角速度)。我做的项目中,动作定义如下:
action = np.array([ surge_speed, # 目标纵向速度,范围 [0, 1.5] m/s heave_speed, # 目标垂向速度,范围 [-0.5, 0.5] m/s yaw_rate # 目标偏航角速度,范围 [-0.4, 0.4] rad/s ])纵向速度下限设 0 而不是负值,避免 AUV 在避障时频繁倒车扰乱 DVL 流速测量;垂向速度限制在 ±0.5 m/s,防止快速下潜导致深度超调触发安全保护。状态空间里除了第 2 章的观测向量,还得多加一组“上一帧动作”,让策略感知自身控制惯性,这也是水下机器人实机部署时很关键的一环——推进器响应有延迟,如果状态里没有历史动作信息,策略会输出来回震荡的控制量。
3.3 奖励塑形:导航进度、碰撞惩罚与动态障碍物预测的折中
三维避障的奖励函数是整套系统里最需要反复调的部分。我把它拆成三个分量:导航进度奖励、避障惩罚、能耗惩罚。导航进度奖励不能只看“距离目标点的欧氏距离减小”,因为 AUV 在水下还要做三维绕行,欧氏距离在绕行阶段几乎不变,策略会因为没有及时拿到正奖励而放弃探索。更实用的做法是用“投影距离进展”:沿 AUV 到目标点连线方向,计算当前时刻位置在连线上的投影增量。
def compute_reward(state_before, state_after, action, goal, obstacles): """ 三维避障奖励函数核心逻辑。 state_before / state_after: AUV位置(载体坐标系下的全局系位置) action: 上一步动作 obstacles: 当前帧障碍物列表, 含 position, velocity """ proj_before = np.dot(state_before, goal) / np.linalg.norm(goal) proj_after = np.dot(state_after, goal) / np.linalg.norm(goal) reward = 2.0 * (proj_after - proj_before) # 安全距离:计算AUV与每个障碍物的最近相遇距离 min_dist = 999.0 for obs in obstacles: rel_pos = obs["position"] - state_after rel_vel = obs["velocity"] - action_provided_velocity # 最近相遇距离近似 ttc = ... dist = np.linalg.norm(rel_pos) min_dist = min(min_dist, dist) if min_dist < 5.0: reward -= 10.0 * (5.0 - min_dist) / 5.0 # 稠密避障惩罚 # 碰撞终止 if min_dist < 1.5: reward -= 50.0 terminated = True # 能耗惩罚:动作幅度越大扣越多 reward -= 0.02 * (action[0] ** 2 + action[1] ** 2 + action[2] ** 2) return reward参数说明:投影距离进展的系数 2.0 是根据仿真测试定的,太小则导航动力不足,太大则策略会忽视避障;避障惩罚采用线性分段函数,距离小于 5 米开始惩罚,小于 1.5 米判定碰撞终止并给 -50 的终止奖励。这里action_provided_velocity是当前 AUV 实际速度而非上一帧动作指令,因为推进器响应延迟会导致指令速度和实际速度不一致,用实际速度算最近相遇距离更准确。
4. 仿真训练闭环:用Gazebo与UUV Simulator把策略训到可部署
4.1 仿真环境与任务场景:动态障碍物怎么注入
我没有从零写水动力模型,直接基于 UUV Simulator(Gazebo 的水下机器人仿真插件)搭环境。任务场景定义为:AUV 从起点出发,穿越一片 40m × 30m × 10m 的水域,区域内随机生成 3~5 个静态障碍物和 1~2 个动态障碍物。动态障碍物的运动模式包括直线横穿、正弦摆动和突然变向三种,用来模拟真实水下环境中作业船、潜水员或水下航行器的运动特征。
障碍物在仿真里的注入方式不需要复杂:定义障碍物为 Gazebo 中的 cylinder 模型,动态障碍物通过一个 Python 脚本控制其位置按预设轨迹更新,每 0.2 秒发布/obstacle_pose话题。感知模块从话题中计算障碍物相对 AUV 的距离和速度,灌入第 2 章的观测向量。这样做的优势是:感知、决策、控制三部分完全解耦,后续换真实声呐数据时,只需要把/obstacle_pose话题替换成感知融合模块的输出。
4.2 训练主循环与PPO超参数:从随机探索到稳定避障
训练主循环我直接用 stable-baselines3 的 PPO 接口,但环境封装必须自己写。核心是继承gym.Env,把 AUV 的里程计、姿态、感知融合结果整合成观测,把策略网络输出的动作映射成推进器指令,最后返回 reward 和 terminated。
import gym from stable_baselines3 import PPO class AUVEnv(gym.Env): def __init__(self, config): super().__init__() self.obs_dim = 5 * 6 + 90 + 6 self.action_space = gym.spaces.Box( low=np.array([0.0, -0.5, -0.4]), high=np.array([1.5, 0.5, 0.4]), dtype=np.float32 ) self.observation_space = gym.spaces.Box( low=-np.inf, high=np.inf, shape=(self.obs_dim,), dtype=np.float32 ) # 连接仿真或真实底层驱动 def step(self, action): # 将动作转换推进器控制指令 # 推进器模型:输入目标速度,输出带延迟的实际速度 actual_vel = self.propulsion_model(action) # 更新AUV位姿、感知信息 # 计算reward return obs, reward, terminated, {} def reset(self): # 随机生成新场景,障碍物位置和运动模式重新采样 return obs train_cfg = { "learning_rate": 3e-4, "n_steps": 2048, "batch_size": 128, "gamma": 0.99, "gae_lambda": 0.95, "clip_range": 0.2, "ent_coef": 0.01, "max_grad_norm": 0.5, "n_epochs": 10 } model = PPO("MlpPolicy", AUVEnv(train_cfg), **train_cfg, verbose=1) model.learn(total_timesteps=2_000_000)参数说明:n_steps=2048是 PPO 每次更新前收集的步数,水下任务每步推进频率 5 Hz,2048 步对应约 6.8 分钟的连续交互,足够覆盖一次从起点到目标点的完整航行;gamma=0.99是对未来奖励的折扣,因为避障决策影响的是未来几秒的轨迹,折扣太低会让策略只看眼前;ent_coef=0.01是探索系数,太大会导致策略一直随机动作,太小则过早收敛到局部最优。初始化时我会先跑 200 个随机种子下的快速实验(每种子 50 万步),挑出成功率最高的种子组合继续训练,这是深度强化学习算法项目里节省时间最有效的技巧之一。
4.3 检查点策略与部署前评估:不能只看训练曲线收敛
训练过程中每 10 万步保存一次模型检查点。很多新手只看 loss 下降就开始部署,但 PPO 的训练曲线平滑不代表策略真能避开动态障碍物。我的评估方法是固定 50 个测试场景(障碍物位置、运动模式在 reset 时定了就不再变),每个检查点跑 10 次,统计到达目标点成功率、平均碰撞时间、绕行路径长度三个指标。成功率能上 90% 的检查点才有资格进入实机部署候选列表。
部署前还要做一步“动作平滑化”:把策略输出的动作做一阶低通滤波,再送入推进器控制器。
def smooth_action(raw_action, prev_action, alpha=0.3): """ 对策略输出做低通滤波,抑制高频抖动。 alpha=0.3 表示新动作只占 30% 权重。 """ return alpha * raw_action + (1 - alpha) * prev_actionalpha=0.3是经验值:AUV 推进器响应时间常数约 0.5 秒,控制周期 0.2 秒,alpha 太小会导致指令滞后明显,太大则滤波效果不足。这个环节单独拿出来说,是因为仿真里推进器是理想线性模型,实机推进器有饱和、死区、延迟,不做平滑直接部署,策略输出的高频抖振会烧推进器驱动。
5. 避坑:从训练到实机部署的5个常见翻车点
5.1 现象:训练曲线奖励一直在涨,但 AUV 在原地打转
训练到 60 万步时,累计奖励稳步上升,可视化 AUV 轨迹却发现它一直在目标点附近绕圈,始终没有接近终点。原因是奖励函数里“投影距离进展”在某段区域内接近零,AUV 只要能保持不碰撞,绕圈也能拿到微小的导航奖励;而且避障惩罚在距离障碍物远时为零,策略发现“绕圈不减分”就钻了空子。
解决方法是加一条“任务时限奖励”:每次 episode 如果超过 300 秒未到达目标,终止并给 -10 的惩罚,同时把投影距离奖励改成“若 10 秒无正进展,额外给予 -0.5”。策略发现绕圈拿不到正收益,自然转向直线前进。
5.2 现象:仿真里策略成功避障,实机却直直撞上去
仿真中模型能避开静态障碍物,实机测试在同样位置却发生碰撞。逐帧回放数据发现:实机声呐探测到障碍物的时间比仿真晚了约 0.8 秒,且测距噪声标准差达到 0.6 米。仿真里感知模块默认每步都有准确的距离观测,实机声呐扫描周期长、漏检率高,策略训练时没见过这种“感知缺失”的状态,一旦观测突然缺少某个障碍物,策略会按无障碍物模式直行。
解决方法是训练时对观测向量做随机遮蔽:以 0.1 的概率把某个目标的速度和位置置零,同时模拟声呐目标随时间延迟出现。这个“感知域随机化”操作,是弥合仿真到实机差距性价比最高的手段。
5.3 现象:动态障碍物绕行失败,明明检测到了还是撞上
动态障碍物从侧向 30 度快速接近时,策略总是选择减速等它过去,结果距离越来越近。分析奖励曲线发现,碰撞惩罚里“最近相遇距离”用的是当前帧瞬时距离,没有预测未来相遇位置。当障碍物有横向速度时,当前帧距离远不等于未来距离远。
在奖励计算里加入“基于相对速度的预测相遇距离”:用rel_pos和rel_vel估算最接近点的距离closest_approach,然后基于这个距离施加避障惩罚。我把这个项加入后,动态障碍物避障成功率从 62% 提升到 84%。
5.4 现象:多传感器融合在仿真中反而导致性能下降
加入摄像头目标检测分支后,策略的成功率不升反降。排查发现是融合逻辑里摄像头检测的置信度阈值设得低,在浑浊水体中频繁把噪声当成障碍物,观测向量里出现大量位置抖动的“幽灵目标”,策略被误导做出不必要的绕行动作。
解决方法是:融合权重不按固定比例,而是按置信度动态调整——声呐置信度高于 0.7 时以声呐目标为主,摄像头仅在置信度超过 0.9 时才参与融合;低于 0.5 的检测结果直接丢弃。多模型感知增强的前提是每个模型都能提供“干净”的信息,带强噪声的输入不如不融合。
5.5 现象:训练过程崩溃,策略突然输出全零动作
训练到 150 万步左右,奖励曲线暴跌,策略输出全零动作。查看日志发现是heave_speed动作长期处于负值(持续上浮),AUV 深度值超出仿真环境的硬边界,环境返回的观测全变成 NaN,策略网络参数的梯度也因此污染。
在环境step()里做了两层防护:观测向量里所有值做 NaN 检查和值域裁剪;深度超出边界时提前终止 episode 并给 -20 的惩罚,而不是让仿真继续跑出非法状态。区块里已加入np.nan_to_num(obs, nan=0.0, posinf=0.0, neginf=0.0)兜底。
6. 从“能避障”到“避得漂亮”:三维路径平滑与安全底线验证
6.1 用碰撞时间指标验证策略质量,而不只看奖励值
训练完成后,我习惯用“碰撞时间”(TTC, Time to Collision)作为最终验证指标:TTC 定义为 AUV 与障碍物按当前相对速度继续运动时的预计碰撞时间。优秀的避障策略应该在 TTC 低于 4 秒时就做出明显的三维绕行动作,而不是等到距离很近了才急转。统计测试场景里的 TTC 分布,重点看 TTC < 2 秒的样本占比,这个比例低于 5% 才允许进入下一阶段。
6.2 一个实用的下潜避障技巧:垂向优先于水平转向
遇到顶部漂浮物或浅水区动态障碍时,AUV 如果只想水平偏航绕行,很容易进入窄水道被“逼停”。我测试出一个简单但有效的策略:当感知模块报告的障碍物 TTC < 3 秒且障碍物高度范围允许向下通过时,优先输出heave_speed负向动作(下潜),同时降低纵向速度到 0.5 m/s。这个规则不需要改变 PPO 策略网络的结构,只需在策略输出之后加一个安全规则层,把动作映射到更安全的控制量上。它牺牲了一点点最优性,但换来了实机部署时的安全底线。
三维避障算法和路径平滑性的关系也值得留意——训练收敛后的策略轨迹经常有棱角,如果让水动力控制器直接跟踪,会产生持续的艏摇震荡。我对轨迹做了一次 Savitzky-Golay 滤波,窗口长度 11,多项式阶数 3,能有效去掉高频抖动而不引入相位滞后。
AUV 深度强化学习避障这套方案,从感知到决策再到部署,每一步都有比想象中更多的工程细节。完成一版能稳定避开动态障碍物的策略之后,反过来看最深的教训是:强化学习算法本身只占三成工作量,感知对齐和奖励设计才真正决定最终边界。希望这些踩出来的坑,能帮你少走一段弯路。
本文还有配套的精品资源,点击获取