简介本资源是一个面向人工智能与机器人方向研究者、高校研究生及强化学习实践者的多无人机协同围捕仿真项目聚焦于多智能体深度强化学习在动态目标围捕任务中的落地实现。项目基于MADDPG算法在自定义Gymnasium仿真环境中训练3架无人机协同围捕5个移动目标完整覆盖环境建模、智能体网络设计含Actor/Critic及其Target网络、经验回放缓冲区、分布式训练与评估全流程适用于灾害救援、边境巡检等高价值协同控制场景。压缩包共53个文件包含9个核心Python源码如sim_env.py、maddpg.py、networks.py、25个预训练模型权重文件按智能体编号与网络类型分类、CSV训练日志、PNG可视化图、README说明及附赠文档整体大小为3.79MB。目前已有149人下载学习提供可直接运行的PyTorch工程结构、模块化代码组织与清晰的训练/评估分离逻辑便于复现实验、调试算法或拓展至更多智能体与复杂任务。1. 项目缘起从单打独斗到群体智慧几年前当我第一次尝试用强化学习训练一个无人机去追踪一个移动目标时过程堪称“灾难”。那个单智能体模型要么像个无头苍蝇一样乱撞要么就卡在某个角落“思考人生”。后来项目需求变成了多架无人机协同围捕一个高机动性目标问题立刻复杂了几个数量级。每架无人机都是一个独立的决策体它们不仅要感知环境、追踪目标还要实时理解其他同伴的意图避免碰撞并协同形成有效的包围圈。这不再是简单的“个体优化”而是典型的“多智能体协同决策”问题。正是在这个背景下我决定动手搭建一个“多无人机协同围捕仿真环境”。这个项目的核心就是利用多智能体深度强化学习特别是MADDPG算法来训练一群无人机智能体让它们学会在复杂的自定义环境中像一支训练有素的队伍一样高效地围捕目标。整个项目基于PyTorch框架构建并依托Gymnasium这个强化学习环境接口标准确保了代码的模块化和可复现性。今天我就把这个从环境设计、算法实现到训练调参的完整过程以及其中踩过的无数个坑毫无保留地分享出来。无论你是刚接触多智能体强化学习的新手还是想寻找一个可落地的协同控制案例的同行相信这篇长文都能给你带来实实在在的参考。2. 核心问题拆解多无人机围捕到底难在哪在开始敲代码之前我们必须先想清楚我们要解决的“多无人机协同围捕”问题其技术难点究竟在哪里。这直接决定了我们环境的设计和算法的选型。2.1 环境动态性与部分可观测性在真实世界中无人机通过自身的传感器如摄像头、激光雷达感知环境。这意味着每架无人机只能获得一个局部观测它看不到整个地图的全貌也看不到其他无人机“脑子里”在想什么。这种部分可观测性是多智能体问题的核心挑战之一。在我们的仿真环境中需要为每个智能体构建一个合理的观测空间例如包含自身位置、速度、朝向、与目标的相对位置和距离、与最近友方/敌方单位的相对信息等。目标通常被设定为具有某种逃逸策略如随机移动、或向包围圈薄弱点移动的动态实体这进一步增加了环境的复杂性和不确定性。2.2 智能体间的信用分配与探索-利用权衡当围捕成功时功劳属于谁是那个正面拦截的还是侧面包抄的在强化学习中这被称为信用分配问题。如果所有智能体共享一个全局奖励例如“目标被捕获”那么每个智能体很难知道自己的具体行动对最终结果贡献了多少导致学习效率低下。此外多智能体环境中的探索更加困难。一个智能体的探索性随机行为可能会破坏其他智能体已经学到的协作策略导致整体性能崩溃。2.3 策略的非平稳性这是多智能体强化学习最本质的挑战。在单智能体设定中环境是稳定的马尔可夫性。但在多智能体设定中从任何一个智能体的视角看环境都在随着其他智能体策略的更新而不断变化。也就是说其他智能体本身就是环境动态的一部分。这直接破坏了传统强化学习算法如DQN、DDPG所依赖的环境平稳性假设导致训练极其不稳定。3. 技术选型为什么是MADDPG Gymnasium PyTorch面对上述挑战经过一番调研和对比我选择了MADDPG作为核心算法Gymnasium作为环境接口PyTorch作为实现框架。这个技术栈的组合是经过深思熟虑的。3.1 MADDPG专为竞争与合作场景而生MADDPG是DeepMind在2017年提出的算法全称是Multi-Agent Deep Deterministic Policy Gradient。它可以说是为解决我们上面提到的核心痛点而设计的。核心思想集中式训练分布式执行。这是MADDPG的精华所在。集中式训练在训练阶段每个智能体的评论家网络可以获取全局信息包括所有智能体的观测和动作。这完美解决了信用分配和环境非平稳性问题。评论家可以基于全局状态评估某个智能体动作的好坏从而给出更准确的指导。分布式执行在执行阶段每个智能体只依赖自己的演员网络根据自身的局部观测做出决策。这符合实际应用场景无人机只需要自己的传感器数据就能行动。MADDPG还引入了策略集成的思想即每个智能体的评论家在训练时会将其他智能体的策略也作为输入的一部分。这迫使智能体去学习如何应对其他智能体策略的变化从而学到更鲁棒、更具协作性的策略。对于我们的围捕任务这意味着无人机不仅能学会追捕目标还能学会预测同伴的动向主动补位形成默契的配合。3.2 Gymnasium强化学习环境的“标准插座”Gymnasium是OpenAI Gym的分支和维护版本它定义了一套强化学习环境的标准接口。使用它的好处显而易见标准化reset(),step(action),render()等核心方法让环境的使用和切换变得统一。生态丰富有大量现成的环境、封装工具和训练框架如Stable-Baselines3与之兼容方便我们未来扩展或对比。自定义灵活我们可以轻松地继承gym.Env类实现自己的__init__,reset,step,render方法快速构建出符合我们物理规则和任务逻辑的无人机围捕世界。3.3 PyTorch动态图带来的灵活与直观选择PyTorch而非TensorFlow主要是出于开发效率和调试便利性的考虑。动态计算图在构建像MADDPG这样结构相对复杂的多网络模型时动态图允许我们像写普通Python代码一样构建网络调试异常直观。你可以随时打印张量的形状和值快速定位问题。Pythonic的APIPyTorch的API设计非常贴近Python和NumPy的使用习惯学习曲线平缓代码可读性高。强大的社区与文档无论是自定义层、复杂的损失函数还是分布式训练PyTorch都有丰富的教程和社区解答能极大降低开发阻力。注意在项目初期我也尝试过用TensorFlow 2.x的Keras API但在处理需要自定义梯度、多个优化器交替更新这种复杂训练逻辑时PyTorch的动态图特性让代码流程清晰得多尤其是在调试多智能体策略网络相互影响时。4. 仿真环境构建设计一个“合理”的无人机世界环境是智能体学习的“考场”设计得好坏直接决定算法能否学到有效策略。我们的自定义Gymnasium环境需要精心设计以下几个部分。4.1 状态空间与观测空间设计这是连接物理仿真与强化学习算法的桥梁。全局状态用于训练时的集中式评论家。通常包含所有无人机和目标的位置、速度、朝向等。这是一个ndarray。局部观测用于每个智能体的分布式演员。对于第i架无人机其观测o_i可能包括自身状态位置(x_i, y_i, z_i)速度向量(vx_i, vy_i, vz_i)朝向角。与目标的关系相对位置向量(dx_target, dy_target, dz_target)距离d_target相对速度。与邻居的关系为了避免碰撞和促进协同可以包含与最近k架友方无人机的相对位置和距离。这里k是一个超参数太大增加维度太小可能信息不足。边界信息与仿真区域边界的距离防止无人机飞出界外。# 观测空间示例 (假设2D平面k2个最近邻居) self.observation_space spaces.Box( low-np.inf, highnp.inf, shape(2 2 2*2 2,), # [自身位置(2), 自身速度(2), 邻居相对位置(2*2), 目标相对位置(2)] dtypenp.float32 )4.2 动作空间设计我们假设无人机由底层控制器控制强化学习智能体输出的是高层指令。一个常见且合理的设定是连续动作空间输出每个轴向上的加速度指令(ax, ay, az)或者速度增量(Δvx, Δvy, Δvz)。这样更符合物理规律也便于MADDPG这类适用于连续动作空间的算法处理。归一化将动作值域限制在[-1, 1]然后在环境中乘以一个缩放系数转换为实际的控制量。这有助于提升训练的稳定性。self.action_space spaces.Box(low-1.0, high1.0, shape(2,), dtypenp.float32) # 2D平面下的x,y方向加速度4.3 奖励函数设计引导智能体学会“围捕”奖励函数是强化学习的“指挥棒”设计它是门艺术。一个糟糕的奖励函数会让智能体学到奇怪的行为比如原地转圈也能获得奖励。我们的奖励需要同时鼓励追踪、包围和协作。我设计的奖励函数包含以下几个部分距离奖励鼓励无人机接近目标。例如r_distance -α * d其中d是无人机与目标的距离α是系数。负奖励意味着距离越远惩罚越大。围捕奖励这是关键。不仅要接近还要形成包围。可以计算所有无人机到目标的距离之和当这个和减小时给予正奖励。或者更精细地计算目标与无人机群形成的凸包中心之间的距离距离越小奖励越高。成功捕获奖励当任何一架无人机与目标的距离小于某个阈值捕获半径时给予所有智能体一个大的正奖励并结束本轮回合。生存惩罚/时间惩罚每走一步给予一个小的负奖励鼓励智能体尽快完成任务。碰撞惩罚如果无人机之间或与障碍物发生碰撞给予大的负奖励并可能结束回合。出界惩罚飞出规定区域给予惩罚。一个示例性的奖励计算对于单个智能体i在一时间步def _get_reward(self, agent_id): agent_pos self.agents[agent_id].position target_pos self.target.position # 1. 基础距离奖励 dist_to_target np.linalg.norm(agent_pos - target_pos) reward_distance -0.1 * dist_to_target # 2. 围捕奖励全局所有智能体共享 # 计算所有无人机到目标距离的方差方差小说明包围圈均匀 all_distances [np.linalg.norm(a.position - target_pos) for a in self.agents] distance_variance np.var(all_distances) reward_surround -0.05 * distance_variance # 方差越小惩罚越小相当于奖励 # 3. 捕获奖励在step函数中判断并全局给予 # if min(all_distances) self.capture_radius: reward_capture 50 # 4. 时间惩罚 reward_time -0.01 # 5. 碰撞惩罚在step函数中判断 # if collision: reward_collision -10 individual_reward reward_distance reward_time global_reward reward_surround # 围捕奖励是全局的 return individual_reward, global_reward在实际训练中需要反复调整这些奖励的系数观察智能体的行为变化这是一个漫长的调参过程。4.4 动态模型与回合终止条件我们需要一个简单的物理模型来更新无人机和目标的状态。通常使用离散时间积分新速度 旧速度 加速度 * 时间步长Δt新位置 旧位置 新速度 * Δt同时需要限制最大速度和加速度使其符合物理实际。回合终止条件成功任何无人机与目标距离小于捕获半径。失败发生碰撞或无人机出界。超时步数超过最大步数限制防止智能体无所事事。5. MADDPG算法实现详解与PyTorch代码剖析理解了环境和算法思想后我们进入最核心的代码实现部分。我将结合PyTorch代码拆解MADDPG的每一个组件。5.1 网络结构设计演员与评论家每个智能体都有自己独立的演员网络和评论家网络。但在MADDPG中评论家网络的输入与其他智能体相关。演员网络输入是当前智能体的局部观测o_i输出是该智能体的动作a_i连续值。import torch import torch.nn as nn import torch.nn.functional as F class Actor(nn.Module): def __init__(self, obs_dim, act_dim, hidden_dim256): super(Actor, self).__init__() self.fc1 nn.Linear(obs_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.fc3 nn.Linear(hidden_dim, act_dim) # 输出层使用tanh激活将动作值约束在[-1,1] self.tanh nn.Tanh() def forward(self, obs): x F.relu(self.fc1(obs)) x F.relu(self.fc2(x)) action self.tanh(self.fc3(x)) # 输出在[-1, 1]之间 return action评论家网络输入是所有智能体的观测拼接和所有智能体的动作拼接输出是一个标量Q值代表在全局状态o_all下执行联合动作a_all的好坏。class Critic(nn.Module): def __init__(self, total_obs_dim, total_act_dim, hidden_dim256): super(Critic, self).__init__() # 输入维度 所有观测的维度 所有动作的维度 input_dim total_obs_dim total_act_dim self.fc1 nn.Linear(input_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.fc3 nn.Linear(hidden_dim, 1) # 输出一个Q值 def forward(self, obs_all, acts_all): # obs_all: [batch_size, total_obs_dim] # acts_all: [batch_size, total_act_dim] x torch.cat([obs_all, acts_all], dim1) x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) q_value self.fc3(x) return q_value这里的关键是在训练时我们可以轻松地获取obs_all和acts_all从经验回放池中但在执行时每个智能体的演员网络只需要自己的obs_i。5.2 经验回放池这是打破数据相关性和提高样本效率的关键。我们使用一个共享的经验回放池存储所有智能体的联合经验(obs_all, acts_all, rewards, next_obs_all, dones)。import numpy as np from collections import deque import random class ReplayBuffer: def __init__(self, capacity): self.buffer deque(maxlencapacity) def push(self, transition): transition: (obs_all, acts_all, rewards, next_obs_all, dones) self.buffer.append(transition) def sample(self, batch_size): batch random.sample(self.buffer, batch_size) # 解压并转换为PyTorch张量 obs_all_b, acts_all_b, rewards_b, next_obs_all_b, dones_b zip(*batch) return (torch.FloatTensor(np.array(obs_all_b)), torch.FloatTensor(np.array(acts_all_b)), torch.FloatTensor(np.array(rewards_b)).unsqueeze(1), # 保持维度一致 torch.FloatTensor(np.array(next_obs_all_b)), torch.FloatTensor(np.array(dones_b)).unsqueeze(1))5.3 核心训练逻辑集中式批评与策略集成MADDPG的训练循环遵循DDPG的模式但为每个智能体单独进行。以下是单次更新的核心步骤对于智能体i采样从经验池中采样一个批量的联合经验。计算目标Q值使用目标演员网络和目标评论家网络。用目标演员网络根据next_obs_all生成下一时刻的联合目标动作target_acts_all。将next_obs_all和target_acts_all输入目标评论家网络得到目标Q值target_q。根据贝尔曼方程计算目标y r_i γ * target_q * (1 - done)。注意这里r_i是智能体i的个人奖励但target_q是基于全局信息计算的。更新评论家计算当前评论家网络的预测Q值并用均方误差损失MSE(Q(obs_all, acts_all), y)来更新评论家网络。关键点acts_all是实际执行的动作obs_all是全局观测。更新演员演员网络的目的是最大化评论家网络给出的Q值。我们通过策略梯度来更新演员用当前演员网络根据当前obs_all重新计算动作acts_all_new注意这里需要obs_all来生成所有动作以计算梯度。将obs_all和acts_all_new输入评论家网络得到Q值。演员的损失就是-Q的平均值因为我们要最大化Q。软更新目标网络使用软更新方式缓慢更新目标网络参数θ_target τ * θ (1-τ) * θ_target其中τ是一个很小的数如0.01这有助于稳定训练。策略集成在计算目标动作target_acts_all时我们使用的是所有智能体的目标演员网络。这意味着在训练智能体i的评论家时我们假设其他智能体j的策略是它们的目标策略即稍旧版本的策略。这模拟了智能体需要应对其他智能体策略变化的情况从而学到更鲁棒的策略。5.4 探索策略OU噪声与衰减对于连续动作空间探索通常通过在演员网络输出的动作上添加噪声来实现。MADDPG原文使用了Ornstein-Uhlenbeck过程噪声这种噪声具有惯性适合物理控制问题。class OUNoise: def __init__(self, action_dim, mu0, theta0.15, sigma0.2): self.action_dim action_dim self.mu mu self.theta theta self.sigma sigma self.state np.ones(self.action_dim) * self.mu self.reset() def reset(self): self.state np.ones(self.action_dim) * self.mu def sample(self): dx self.theta * (self.mu - self.state) dx self.sigma * np.random.randn(self.action_dim) self.state dx return self.state在训练初期我们需要较大的探索因此噪声幅度较大。随着训练进行应逐渐衰减噪声让智能体更多地利用学到的策略。可以线性或指数衰减噪声的sigma参数。6. 训练过程实战调参、可视化与Debug心法把代码跑起来只是第一步让模型真正收敛并学到有效策略才是真正的挑战。这部分分享我实际训练中的经验和踩过的坑。6.1 超参数设置一个可行的起点超参数对强化学习训练结果影响巨大。以下是我经过多次实验后一个相对稳定的参数组合可以作为你项目的起点超参数值说明学习率 (Actor)1e-4演员网络学习率通常比评论家小学习率 (Critic)1e-3评论家网络学习率折扣因子 γ0.95未来奖励的折扣0.95-0.99常见软更新系数 τ0.01目标网络更新速度越小越稳定回放池容量1e6经验回放池大小批次大小1024每次更新从回放池采样的样本数OU噪声参数 θ0.15OU过程均值回归速度OU噪声参数 σ0.2OU过程波动率初始探索强度σ衰减率0.9995每回合后噪声衰减系数最小σ0.01噪声下限保持一点探索最大训练回合数5000每回合最大步数200环境步数上限为什么评论家学习率比演员高因为评论家需要更快速地学习到一个相对准确的Q函数才能为演员提供可靠的梯度方向。如果评论家学得慢演员就像在黑暗中摸索。6.2 训练曲线解读如何判断模型在“学习”仅仅看最终的成功率是不够的训练过程中的指标更能反映问题。回合奖励这是最直观的指标。理想情况下它应该随着训练回合数增加而呈现上升趋势并最终稳定在一个较高的值。如果奖励曲线剧烈震荡或持续下降说明训练不稳定。评论家损失评论家网络的损失值应该逐渐下降并趋于平稳。如果损失值爆炸变成NaN或极大值很可能是学习率太高、梯度爆炸或者奖励函数设计不合理值域过大。演员损失演员损失即负的Q值应该逐渐减小因为我们在最大化Q。但其绝对值意义不大更应关注其趋势。平均回合步数对于围捕任务如果智能体学会了协作平均完成任务的步数应该减少。如果步数一直很高可能是奖励函数中“时间惩罚”不够或者智能体陷入了局部最优比如只追不围。我常用的可视化工具是TensorBoard或Weights Biases。将上述指标实时记录并可视化能帮助你快速判断训练状态。6.3 常见问题与Debug技巧奖励不增长智能体“摆烂”检查奖励函数是不是生存惩罚或时间惩罚太大导致智能体觉得“不动”的损失最小尝试调整奖励系数增加成功捕获的奖励。检查探索初始噪声σ是否太小智能体可能根本没有进行有效探索。尝试增大σ并观察智能体在环境中的随机动作是否覆盖了合理的范围。简化环境先从最简单的场景开始比如1个无人机追1个静止目标。确保算法在这个简单任务上能学通再增加难度。训练不稳定奖励曲线“过山车”降低学习率这是第一选择。尤其是评论家学习率尝试降到5e-4或1e-4。增大批次大小更大的批次能提供更稳定的梯度估计。检查目标网络更新τ值是否太小太小的τ会导致目标网络更新过慢学习滞后。可以尝试稍微增大τ到0.05。梯度裁剪在PyTorch中可以在优化器更新前对评论家网络的梯度进行裁剪防止梯度爆炸。torch.nn.utils.clip_grad_norm_(critic.parameters(), max_norm1.0)智能体学会“作弊”或出现怪异行为这是奖励函数设计有漏洞的典型表现。例如如果只奖励距离减小智能体可能会学会高速掠过目标而不是停下来围捕。你需要仔细审视奖励函数的每一个项思考它可能诱导出的非预期行为并增加相应的惩罚项如速度惩罚、动作变化惩罚来约束。PyTorch特定DebugNaN值使用torch.isnan(tensor).any()检查网络输出、损失值中是否出现NaN。这通常是由于计算溢出如softmax输入过大或除零错误引起的。设备问题确保模型、数据都在同一个设备上CPU或GPU。一个常见的错误是将GPU上的模型与CPU上的数据做运算。维度不匹配在拼接观测和动作输入评论家时务必仔细检查张量的维度。使用tensor.shape打印维度是Debug的必备技能。7. 从仿真到进阶策略迁移与性能优化当你的智能体在基础仿真环境中表现良好后可以考虑以下几个进阶方向这能让你的项目和理解更上一层楼。7.1 策略迁移从简单到复杂不要一开始就在复杂环境中训练。我建议采用课程学习的思想阶段一训练1个无人机追1个静止目标。验证基础算法流程。阶段二训练2个无人机协同追1个缓慢移动的目标。观察是否出现简单的配合。阶段三训练3个或更多无人机围捕1个具有简单逃逸策略如向最近无人机反方向移动的目标。阶段四增加环境复杂性如加入障碍物或让目标具有更复杂的策略如S形移动。在每一个阶段训练好的模型可以作为下一阶段模型的预训练权重。这能显著加速训练并提高最终性能。7.2 通信机制的引入在标准的MADDPG中智能体之间没有显式通信协作完全通过奖励函数隐式学习。我们可以尝试引入简单的通信机制例如共享局部观测每个智能体可以广播自己观测到的部分信息如自身位置、目标相对位置。学习通信协议像CommNet或TarMAC这类算法可以让智能体通过学习来生成和解析通信消息。 在我们的环境中可以尝试让无人机智能体额外输出一个“通信向量”并作为其他无人机观测的一部分。这需要修改演员和评论家网络的输入维度。7.3 工程优化加速训练与部署当环境模型和智能体数量增加时仿真速度可能成为瓶颈。向量化环境使用gym.vector.SyncVectorEnv或第三方库如ray并行运行多个环境实例同时收集数据能极大提高数据采集效率。JIT编译对于环境中的核心计算如距离计算、物理更新可以使用PyTorch的torch.jit.script或NumPy的向量化操作进行加速避免低效的Python循环。模型量化与剪枝如果未来考虑部署到算力有限的边缘设备如无人机机载电脑可以对训练好的PyTorch模型进行动态量化或剪枝在几乎不损失精度的情况下减小模型体积、提升推理速度。这个项目从零开始到最终看到多架无人机在仿真中默契配合成功围捕高速移动的目标整个过程充满了挑战和乐趣。它不仅仅是一个算法实现更是一次对多智能体系统、深度强化学习以及问题拆解能力的综合锻炼。我最深的体会是奖励函数的设计和超参数的调优其重要性不亚于算法本身。很多时候算法是骨架而这些“软”设计才是赋予智能体灵魂的关键。希望我的这些经验能帮你少走一些弯路更快地享受到多智能体协同带来的美妙。如果你在复现过程中遇到任何问题欢迎随时交流讨论。本文还有配套的精品资源点击获取
