☰
ROS+深度强化学习导航避障源码:DQN/DDPG/PPO/SAC实战
2026/10/10 4:52:51 网站建设 项目流程

简介:这是一份基于ROS与深度强化学习的移动机器人导航避障Python项目源码,面向机器人方向学生、导航算法研究者及ROS开发者。资源整合多种深度强化学习算法,可在仿真环境中对比不同策略在障碍物场景下的避障与路径规划表现,并附有详细使用说明,便于快速搭建环境并复现实验。压缩包共2000个文件,主要包含Python源码与脚本、ROS catkin构建配置、launch启动文件、机器人模型与仿真世界文件、C++辅助节点、TF坐标转换相关组件等,整体约6.04MB,结构清晰紧凑。已有956人学习下载,适合希望对比DQN、DDQN、Dueling DQN等强化学习算法在实际导航任务中效果的开发者,可基于现有代码二次开发或开展算法改进实验。

1. 深度强化学习做导航避障:这套 ROS + Python 源码直接省掉三轮踩坑

做移动机器人导航的人,多半在 ROS 的 move_base 参数里泡过一阵子:膨胀半径、代价地图阈值、全局局部规划器换了一轮,遇到动态障碍物还是像蒙着眼撞上去。这套基于 ROS 和深度强化学习不同算法的移动机器人导航避障 Python 源码,走的是另一条路——把避障决策直接交给策略网络,DQN、DDPG、PPO、SAC 几类主流算法都打包在 agents 目录里,Gazebo 里训完切推理模式就能在真机上跑。适合已经会 ROS 基础、想在导航里接强化学习的人,也适合课程设计想快速产出算法对比结果的同学。拿到包之后,先别急着灌数据,按第 3 章的流程把环境捋顺,后面出问题的概率会小很多。

2. 算法选型与源码结构:DQN、DDPG、PPO、SAC 谁负责哪一段

2.1 四种算法在导航避障里的定位

先说结论:这套源码里同时出现四个算法,不是把热门模型堆在一起凑数,而是它们分别覆盖了导航避障里两种不同类型的决策问题。新手拿到包最容易犯的错,是四个算法挨个跑一遍然后选个 reward 最高的,根本不看动作空间性质,结果换到真机全废。

DQN 处理离散动作集。在 ROS 导航场景里,动作集可以定义成"左转 30°、直行、右转 30°、减速"这类有限组合。DQN 的优势是训练直观、收敛过程好画图,适合做课程设计里的基线对照。代价也明显——离散动作会让轨迹出现折线感,机器人转弯像开关一样一格格跳,所以源码里 DQN 更多承担"让新手看懂强化学习怎么接 ROS"的角色。

DDPG 是连续动作空间的老牌算法,Actor-Critic 架构直接输出连续的线速度 v 和角速度 ω,比 DQN 更贴近底盘控制习惯。它的缺点同样出名:对超参数极其敏感,Q 值容易过估计,训练时 reward 震荡幅度大。在这套源码里,DDPG 跑简单地形——直线冲目标加一个转弯避障,效果可以接受,但一旦地图复杂到要连续绕三个障碍,训练曲线就开始抽风。

PPO 是 on-policy 算法,用 clipped surrogate objective 限制策略更新步长,训练稳定性明显好于 DDPG。代价是样本效率低,一个回合的数据用完就扔,训练时间肉眼可见地变长。导航场景里 PPO 适合长时间挂机训练,比如晚上挂机第二天来看结果,它不太容易出现"策略突然崩掉"之类的问题。

SAC 在这四个里是默认推荐。它用熵正则项保证探索,温度系数自动调节,在连续动作空间里典型表现是前期探索充分、后期收敛稳定。对动态障碍物这种需要持续探索边界情况的任务,SAC 的样本效率比 PPO 高一截,实际效果在迷宫和随机障碍场景里基本排最前。选型逻辑一句话:想快速出对比结果用 DQN,要连续动作且环境干净用 DDPG,要稳定用 PPO,综合效果好、少调参直接上 SAC。

2.2 源码目录与模块划分

解压 zip 以后第一件事不是急着跑训练,而是先看目录结构。这套包划分得很常规,和大多数强化学习工程项目的布局一致,下面这张表可以当索引用:

目录/文件职责
envs/ROS 环境封装,订阅激光话题、发布 cmd_vel、组装状态
agents/dqn.py、ddpg.py、ppo.py、sac.py 四个策略文件
training/训练主循环、replay buffer、reward 计算
configs/各算法的超参数 yaml 配置
launch/Gazebo 仿真与训练节点的 launch 文件
models/训练产出的模型权重目录
使用详细说明.pdf环境搭建、训练、部署的完整文档

agents/ 里每个算法文件都实现了同一套接口:choose_action、store_transition、learn、save_model。这样设计的好处是训练主循环不需要关心背后是哪个算法,切换算法只改 launch 里的一个参数。我一般会在第一次跑通后把四个文件的网络结构分别打印出来对比,能明显看出它们的输出假设差异——PPO 的 actor 输出的是高斯分布的均值和方差,DDPG 直接输出确定性动作,SAC 则是 tanh 压缩后的连续动作加熵项。这个差异直接决定了后续调参侧重点,比如 DDPG 要盯着 critic 的 Q 值是否过估计,PPO 要看策略熵有没有塌陷。

2.3 环境接口:状态空间、动作空间与奖励函数

导航避障强化学习有个核心问题:720 个激光点加上目标位置不能直接塞给网络,否则输入维度爆炸,收敛慢到没法等。源码的做法是把激光雷达数据降采样到 10~20 维,再拼接目标相对角度和距离,组成一个 12~22 维的状态向量。这个降采样不是简单隔几个点取一个,而是按角度区间取最小值,因为避障最关心的是最近障碍方向,不是平均距离。

动作空间上,SAC 和 DDPG 输出二维连续动作:线速度限制在 0~0.5 m/s,角速度限制在 -0.8~0.8 rad/s。DQN 走离散路线,动作集是 8 个方向组合。奖励函数是最值得细看的部分,它直接决定训练出来的行为是急功近利还是稳健保守,源码默认实现如下:

  • 到达目标点:+50,并结束本回合
  • 碰撞:-20,并结束本回合
  • 每存活一步:-0.05,促使机器人别磨蹭
  • 距离变化:靠近目标 +0.1,远离目标 -0.1

这套设计的核心是"稀疏大奖励 + 密度小惩罚"组合。很多新手只设到达和碰撞两个稀疏奖励,训练几万步 reward 还在原地踏步,缺的就是距离变化这个引导项。没有引导项,前几百回合全是碰撞结束,策略完全不知道该往哪走。这个问题在源码里已经处理过,但你要是自己动手改奖励函数,最容易踩的就是把距离权重调得过大——机器人会为了蹭近目标而贴着障碍物走,看起来 reward 很高,实际行为极其危险。距离项权重控制在 0.1~0.5 之间比较安全。

3. 环境搭建与训练流程:从 Gazebo 仿真到 rospy 交互

3.1 依赖安装:先把环境一次装齐

安装这块没什么玄学,就是按版本匹配来。首先是 ROS 本体,Ubuntu 20.04 对应 ROS Noetic,Ubuntu 22.04 对应 ROS Humble。如果你只是想把环境快速跑起来,用鱼香ROS 的一键安装脚本是目前省事的做法,换源、依赖、环境变量它都会处理。装完用roscore验证一下能正常启动再继续。

Python 依赖主要看 torch 版本。agents/ 目录用到了torch.nn和torch.distributions,训练跑在 Gazebo 里时瓶颈在仿真速度,不在 GPU,装 CPU 版 torch 就够。验证命令如下:

# 验证 ROS 环境 source /opt/ros/noetic/setup.bash roscore & rosversion # 安装 Python 强化学习依赖 pip install torch numpy pyyaml rospkg # 验证 torch 可导入 python3 -c "import torch; print(torch.__version__)" # 创建 ROS 工作空间并编译 mkdir -p ~/drl_nav_ws/src mv ~/下载/drl_nav_pkg ~/drl_nav_ws/src/ cd ~/drl_nav_ws catkin_make source devel/setup.bash

说明最后两步:catkin_make会编译所有自定义消息和服务,如果你后续要加自己的 msg 类型,必须先编译再跑训练脚本,否则 rospy 会一直报找不到消息定义的错。版本对齐这件事我踩过一次大的——torch 1.x 和 2.x 的torch.distributions.Normal接口行为略有差异,用 2.x 跑 1.x 时代写的离散动作代码,log_prob 计算时容易出现 NaN,训练 reward 直接空白。装完依赖先用一条空网络前向传播测试,确认没有 NaN 再进下一步。

3.2 启动 Gazebo 仿真与训练节点

整个训练流程建议拆成三个终端,职责清清楚楚:一个跑 roscore,一个跑 Gazebo 仿真,一个跑训练主节点。源码的 launch/ 目录已经整合好了启动文件:

# 终端 1:启动仿真环境(Gazebo + 机器人模型 + 激光雷达) roslaunch drl_nav gazebo_world.launch # 终端 2:启动训练节点 roslaunch drl_nav train.launch algorithm:=sac

gazebo_world.launch加载的是一张小型室内地图,里面放了几块静态障碍物和一堵墙,机器人起始位置在地图左下角,目标点通过 launch 参数传入。train.launch里的algorithm参数用于切换 DQN、DDPG、PPO、SAC,它会透传给 training 主循环去加载对应的 agent。不想用 roslaunch 的话也可以手动开节点:

# 终端 3:手动方式运行训练 source devel/setup.bash rosrun drl_nav train.py --algorithm sac --episodes 5000

--episodes是训练回合数,5000 个回合在 Gazebo 里大约要跑 6~10 小时,取决于仿真实时因子。这里有个频率问题值得注意:Gazebo 的 physics 更新频率默认 1000Hz,机器人控制频率是 10Hz,两者差两个数量级,训练时如果多个节点同时往cmd_vel上发指令,控制指令容易被挤掉,表现为小车动作一顿一顿。这个问题的具体解法放在第 4 章的避坑清单里说。

3.3 训练参数怎么调

训练参数集中在 configs/ 的 yaml 文件里,每个算法一个文件。以下是 SAC 默认配置的核心字段:

参数默认值说明
learning_rate3e-4actor 和 critic 共用,偏低更稳
gamma0.99折扣因子,越大越看长远
tau0.005目标网络软更新系数
buffer_size1e6经验池容量,小了容易过拟合
batch_size256采样批量,和 reward 方差相关
update_interval4每 4 步更新一次策略
entropy_coef0.2SAC 温度系数初始值

调参原则是每次只动一个变量。我常用的套路是:先固定 batch_size 和 gamma,把 learning_rate 从 3e-4 往 1e-4 调,观察 reward 曲线是否出现高频震荡。震荡频繁说明学习率偏高,策略更新步子迈得太大;曲线平滑但收敛慢,说明学习率偏低。ppo.yaml 里重点是 clip_range,默认 0.2,导航任务里我通常压到 0.15,因为机器人动作范围小,clip_range 太大会让策略在障碍边缘反复横跳。dqn.yaml 里的 epsilon_decay 从 1.0 衰减到 0.05,衰减太快会导致探索不足,模型老在起始点附近打转。

3.4 模型保存与 TensorBoard 监控

训练过程中源码每个回合结束会把当前权重存到 models/ 目录,命名格式类似sac_episode_1000.pth。同时把 reward、loss、碰撞次数写进 TensorBoard 日志目录:

tensorboard --logdir=logs/ # 浏览器打开 http://localhost:6006

看监控曲线有个技巧:别只盯 reward 均值,要把"每回合碰撞次数"和"到达终点次数"单独拉出来看。reward 曲线被平滑后看不出问题,但碰撞次数如果到 3000 回合还在高位不降,基本可以断定是奖励函数设计问题而不是算法问题——这个区分能帮你少走半天弯路。另外,保存模型时不要只看最后的 epoch,训练后期常有轻微过拟合,最后几十回合的权重未必是最好的。折中的做法是每 500 回合另存一个验证快照,用固定种子跑 20 个回合统计成功率,取最高的那个用于部署。

4. 训练翻车与避坑清单:四个高频问题的现象、原因与解决

这一章是整套源码实战里最值钱的部分。以下每条都是我在复现和调参过程中实际撞过的坑,按"现象 → 原因 → 解决"的格式写,方便你对照排查。

4.1 reward 长时间在负值区间徘徊,到不了正数

现象:训练跑了两千多回合,TensorBoard 里的 reward 曲线一直压在 -5 到 0 之间震荡,偶尔冒出一个正数马上又掉回去。机器人基本一碰壁就结束回合,没有形成任何有效路径。

原因:典型的奖励函数稀疏问题。源码默认奖励虽然包含到达 +50 和碰撞 -20,但如果机器人起始位置和目标之间隔着一堵墙,探索初期几乎所有回合都以碰撞收场,正样本极少,策略根本没机会学到"接近目标"是什么感觉。

解决:把奖励函数里的距离变化项权重调高。将envs/reward.py里的distance_reward系数从 0.1 提到 0.5,同时在训练前 500 回合把目标点改到机器人正前方 2 米处,让策略先学会直线接近目标,再逐步增加地图难度。这个"课程学习"的思路在避障任务里非常管用,比调网络结构见效快得多。

提示:改奖励函数之后,之前跑过的对比实验要全部重跑,别拿旧曲线和新曲线硬比,那样结论站不住。

4.2 机器人在原地转圈,reward 却还在缓慢上涨

现象:训练中期,机器人到障碍物附近开始原地打转,角速度持续输出最大值,线速度接近 0,看起来像被卡住了。诡异的是 reward 曲线没有崩,反而微涨,让你误以为策略在进步。

原因:连续动作空间的边界约束没做好。SAC 和 DDPG 的网络输出经过 tanh 压缩到 [-1, 1],环境层再映射到实际速度范围。如果角速度映射到 ±0.8 rad/s,而 tanh 输出长时间贴在 1.0 附近,说明策略已经饱和,机器人就靠高速旋转找出路。稀疏奖励让它觉得"只要在动就有希望",于是转圈反而成了局部最优。

解决:在动作映射函数里加软约束,把角速度范围收窄到 ±0.5 rad/s,同时对"高角速度 + 低线速度"的组合加一个额外惩罚,比如-0.02 * abs(w) / (v + 0.1)。训练时留意 TensorBoard 的 action 分布曲线,看到大量采样点堆在动作边界上,优先处理这个,而不是急着加网络层数。

4.3 Gazebo 里跑得好好的,拿到真机直接撞墙

现象:仿真里 90% 以上的回合都能到达目标,换到真机小车后前几秒就开始偏移,遇到白色墙面直直撞上去。激光数据在 rviz 里看完全正常,但策略就是没反应。

原因:sim-to-real gap,也就是仿真和真机的传感器分布差异。Gazebo 激光雷达是理想模型,真机激光有噪声、有最大量程截断,里程计还有漂移。源码训练时没有给激光数据加噪声,策略对"干净的 12 维状态"过度拟合了,一遇到带噪声的输入就输出异常动作。

解决:训练时在状态空间里注入高斯噪声,均值 0、方差 0.02,同时把激光最大有效距离从 10 米截到 3.5 米,让策略不能依赖远处信息躲障碍。另一个有效手段是训练时随机改变机器人的初始朝向和目标点位置,避免策略背下固定起点到终点的路径——这种"背答案"行为在换地图时立刻现出原形。

4.4 多个节点争抢 cmd_vel,小车动作一顿一顿

现象:gazebo_world.launch 和 train.launch 一起跑,开始时正常,几分钟后小车在 Gazebo 里动作明显卡顿,rostopic hz 看到速度话题发布频率从 10Hz 掉到 2Hz。

原因:这是 ROS 话题多发布者冲突。Gazebo 自带的底盘控制节点和训练节点的策略循环都在往cmd_vel发速度指令,订阅者会交替收到来源不同的指令,底盘执行器被反复覆写。源码在 launch 层面没做节点管理,多进程同时发布就会出现这个现象——就像多个节点同时发布移动指令话题时,底盘节点不知道该听谁的。

解决:做话题优先级仲裁。最简单的方式是在训练节点的发布循环里加一个互斥判断,读取cmd_vel上其他发布者的时间戳,如果最近 0.1 秒内有非本节点发布的指令就跳过当前帧。更规范的做法是写一个独立的 velocity gate 节点,统一接收策略输出并控制实际下发权限,这也方便后续真机部署时接急停。排查这类问题有个通用思路:ROS 里速度指令争夺是最隐蔽的翻车点之一,因为rostopic echo看每条指令都是正常的,合在一起就卡顿。

5. 训练完的模型怎么上真机:推理节点改造与安全兜底

5.1 从训练模式切到推理模式

训练节点和推理节点本质上是同一个环境封装,区别只在于推理模式不调用learn(),也不再往经验池写数据。源码里train.py提供了--mode inference参数,切换后主循环只做"订阅激光 → 组装状态 → 输出动作 → 发布 cmd_vel"这几件事:

# 加载指定回合的模型 rosrun drl_nav train.py --mode inference --algorithm sac --load_epoch 4500

load_epoch对应模型文件名的回合号,建议先用验证集对比再定,不要默认加载最后一个保存点。训练后期过拟合很常见,最后几十回合的权重不一定最好。我一般每 500 回合保存一次,在固定种子环境下跑 20 个回合记录成功率,取成功率最高的那个权重做部署。这一步看着繁琐,实际能省掉真机上反复试错的成本。

5.2 控制频率与 cmd_vel 发布:参数固化

训练时控制频率是 10Hz,真机上保持一样的 10Hz,这个频率不要随手改。调太高,激光数据更新跟不上,策略等于闭着眼开车;调太低,紧急转向来不及,撞墙概率飙升。推理循环的核心逻辑如下:

# 推理循环核心逻辑 rate = rospy.Rate(10) # 控制频率 10Hz,与训练时保持一致 while not rospy.is_shutdown(): obs = build_state(latest_scan, target_pos) # 组装状态向量 action = agent.choose_action(obs, deterministic=True) # 确定性动作 cmd = map_action(action, configs.velocity_range) # 映射到实际速度范围 cmd_pub.publish(cmd) # 发布到 /cmd_vel rate.sleep()

两个细节必须强调。第一,choose_action推理时一定传deterministic=True,否则 SAC 会按高斯分布采样动作,真机上的表现就是车辆抖动,转弯半径忽大忽小。第二,build_state里要复用训练时的降采样参数:激光点数、状态维度、归一化系数都必须和训练一致,差一个数模型输出就是乱的。所谓"差一个数"指的是归一化时用错了最大值,我见过有人把激光距离从 0~10 米归一化改成 0~3.5 米,整个策略行为完全变形。

5.3 安全兜底:碰撞检测与急停

真机部署最怕的不是策略弱,而是策略弱的时候没有兜底机制。源码附带了一套安全防护思路,但不是开箱即用的完整节点,需要自己接硬件。我的做法是加一个独立的安全节点,频率 50Hz,订阅激光话题实时计算最近障碍物距离:

# 安全节点伪逻辑:最近障碍物 < 0.3m 时强制停车 rosrun drl_nav safety_node.py --min_dist 0.3

安全节点的优先级要高于策略推理节点。实现上直接往cmd_vel发 0 速度,同时通过一个服务调用关掉推理节点的发布。这个熔断机制在真机调试时相当于后悔药——没有它,你不敢放开手让策略自由跑。加完还要配一个手动急停按键映射,我习惯把键盘的空格键绑到急停话题上,出问题第一反应按空格,而不是去终端找 Ctrl-C。这套安全兜底属于工程层面必须补的部分,源码包停留在研究层面,接线和按键映射要自己完成。首次上真机建议把线速度上限压到 0.2 m/s 先跑通全流程,再逐步提速,不要上来就按训练参数跑。硬件这东西没有后悔药,撞一次底盘可能就是几百块。

6. 复现之后还能怎么玩:给 SAC 加一个 TD3 基线做横向对比

源码包里没有 TD3,但拿它做横向对比是成本最低的进阶玩法。TD3 是 DDPG 的升级版,双 critic 取最小 Q 值加目标策略平滑正则,专门解决 DDPG 的 Q 值过估计问题。在导航避障这种传感器噪声较大的场景里,TD3 通常比 DDPG 稳定不少。实现上只需要在 agents/ 目录复制 ddpg.py 改成 td3.py,网络结构不变,把 critic 复制成两个,更新时取min(q1, q2)作为目标:

# TD3 核心差异:双 critic 取小值,抑制 Q 值过估计 target_q = reward + gamma * torch.min(target_q1, target_q2)

加入之后把四个算法的 reward 曲线和碰撞次数放到同一张图里对比,你会发现 SAC 收敛最快、TD3 最稳、PPO 曲线最平、DQN 早期波动最大。这套对比数据可以直接用在课程设计报告和论文的实验章节里,属于复现这套源码的额外产出,比你重新搭一套环境省事得多。

说到这里想起一个教训:做横向对比时,所有算法的训练回合数、batch_size、地图配置必须完全一致,换任何一个变量,对比结论都站不住。我当初把 PPO 的训练回合数多给了 2000,结果答辩时被问了一句"这差异是算法带来的还是训练量带来的",只能当场坦白。从那以后我每次做算法对比都强制把 configs/ 里的 yaml 统一成同一套参数,只有算法文件不同,跑完再动任何变量。这套基于 ROS 和深度强化学习的导航避障源码包,你下载后按使用说明把环境搭好,从第 2 章的目录对照开始走一遍,很快就能把四个算法全部复现出来,希望帮到你。

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

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

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

立即咨询