简介:本资源是一个面向人工智能与机器人方向研究者、高校师生及强化学习实践者的多无人机协同控制仿真项目,聚焦于利用多智能体深度强化学习解决动态环境下的目标围捕与协同决策问题。项目基于MADDPG算法,在自定义Gymnasium仿真环境中训练3架无人机智能体协同围捕5个移动目标,完整覆盖环境建模、网络架构(含Actor-Critic双网络及Target网络)、经验回放、分布式训练与评估全流程,适用于算法复现、课程实验或科研原型开发。压缩包共53个文件,含9个核心Python源码(如sim_env.py、maddpg.py、networks.py)、多组已训练模型参数(actor/critic及其target权重文件)、CSV训练日志、README说明文档及PNG环境示意图等,总大小3.79MB,结构清晰、模块解耦度高,便于理解MADDPG在多智能体任务中的实现细节与调试逻辑。目前已有149人学习下载,提供可直接运行的PyTorch代码框架、完整训练/评估脚本及配套技术说明,助读者快速掌握多无人机协同围捕的建模思路与工程落地关键点。
1. 项目概述与核心价值
最近在复现和优化一个多无人机协同围捕的项目,核心是基于MADDPG算法,在自定义的Gymnasium环境中训练多个无人机智能体,让它们学会协同决策,高效地围捕动态目标。这个项目听起来很酷,但真正动手做起来,从环境搭建、算法实现到训练调参,每一步都有不少门道。如果你也对多智能体强化学习(MARL)或者无人机集群控制感兴趣,想找一个有挑战性又能学到东西的练手项目,那这个仿真环境绝对值得深入研究。它不仅仅是一个算法Demo,更是一个完整的、可扩展的仿真系统,涵盖了从环境交互、智能体通信到分布式训练的全流程。
这个项目的核心价值在于,它把一个前沿的学术问题(多智能体协同决策)和一个具体的应用场景(无人机围捕)结合了起来。通过PyTorch实现MADDPG,你不仅能深入理解Actor-Critic框架在多智能体场景下的变体,还能亲手搭建一个符合OpenAI Gymnasium接口规范的仿真环境,这对于理解强化学习工程化落地非常有帮助。最终,你将看到一群最初只会乱飞的无人机,如何通过自主学习,演化出包围、驱赶、合围等复杂的协同策略,这个过程本身就充满了成就感。
2. 项目整体架构与设计思路拆解
2.1 为什么选择MADDPG与Gymnasium?
这个项目的技术选型非常经典且合理。MADDPG(Multi-Agent Deep Deterministic Policy Gradient)是多智能体深度确定性策略梯度算法,它是DDPG算法在多智能体场景下的直接扩展。其核心思想是“集中式训练,分布式执行”(CTDE)。在训练时,每个智能体的Critic网络可以获取全局状态信息(包括所有智能体的观测和动作),从而能更准确地评估联合动作的价值;但在执行时,每个智能体只依赖自身的局部观测来做出决策。这种设计巧妙地解决了多智能体环境中非平稳性和信用分配难的问题,非常适合我们无人机协同围捕的场景——训练时需要知道队友和目标的全盘信息来学习配合,实际飞行时每架无人机只能依靠自己的传感器。
选择Gymnasium(原OpenAI Gym的维护分支)作为环境接口标准,则是为了生态兼容性和可复现性。Gymnasium定义了一套清晰的环境接口(reset,step,render等),使得我们的训练环境可以无缝对接主流强化学习库(如Stable-Baselines3)。自定义环境让我们能完全控制状态空间、动作空间、奖励函数和动力学模型,这是实现特定围捕任务的关键。例如,我们可以将无人机的状态定义为位置、速度、朝向,动作定义为加速度指令,奖励函数则精心设计以鼓励接近目标、保持队形并避免碰撞。
2.2 仿真环境的核心组件设计
一个逼真且高效的多无人机围捕仿真环境,需要精心设计以下几个核心组件:
- 动力学模型:这是环境真实性的基础。我们通常采用简化的质点动力学或更复杂的四旋翼动力学模型。对于侧重算法验证的项目,使用二阶积分模型(由加速度控制位置和速度)在保证合理性的同时,能极大降低计算复杂度。关键参数如最大速度、最大加速度、转向速率需要根据真实无人机性能设定。
- 观测空间:每个无人机智能体的“眼睛”。通常包括:自身状态(位置、速度)、相对目标的状态(相对位置、相对速度)、以及部分队友信息(如最近几个队友的相对位置)。为了促进协同,观测中融入队友信息至关重要,但信息过多也会增加学习难度,需要权衡。
- 动作空间:智能体的“操控杆”。连续动作空间更为常见,例如三维空间中的加速度矢量
[a_x, a_y, a_z]。需要设定合理的数值范围,并考虑动作平滑性(如通过低通滤波)来模拟真实的飞行控制器响应。 - 奖励函数设计:这是引导智能体学习的“指挥棒”,也是项目成败的关键。一个有效的围捕奖励函数通常是多目标的加权和:
- 围捕奖励:当所有无人机与目标的平均距离小于某个阈值时,给予高额正奖励。也可以设计为距离的负指数函数
reward = -mean_distance。 - 协同奖励:鼓励无人机分散在目标周围,例如奖励无人机之间相对于目标的角度分布均匀性,避免所有无人机挤在一侧。
- 生存惩罚:每经过一个时间步给予一个小的负奖励,鼓励快速完成任务。
- 碰撞惩罚:无人机之间或与障碍物发生碰撞时,给予大的负奖励并提前结束本轮(episode)。
- 边界惩罚:飞出规定空域给予惩罚。 调整这些奖励项的权重(
w1, w2, ...)是调参的重点,直接决定了学习出的策略是激进还是保守,是注重个体还是强调整体。
- 围捕奖励:当所有无人机与目标的平均距离小于某个阈值时,给予高额正奖励。也可以设计为距离的负指数函数
注意:奖励函数的设计是一门艺术。初期可以简化,先让智能体学会基本靠近目标,再逐步增加协同、避障等要求。一次性设置过于复杂的奖励函数可能导致学习不稳定或收敛到局部最优。
3. MADDPG算法实现细节与PyTorch实战
3.1 MADDPG算法原理再回顾与网络结构
MADDPG为每个智能体i维护两套网络:Actor网络μ_i和 Critic网络Q_i。
- Actor (策略网络):输入是智能体
i自身的局部观测o_i,输出是它要执行的动作a_i。它的目标是最大化Critic网络评估的期望回报。 - Critic (价值网络):输入是所有智能体的观测联合
o = (o_1, ..., o_N)和所有智能体的动作联合a = (a_1, ..., a_N),输出是对当前状态下联合动作a的Q值估计Q_i(o, a)。注意,在训练时,Critic需要知道全局信息。
在PyTorch中,我们需要为N个智能体创建N对Actor-Critic网络。通常,所有智能体共享相同的网络结构,但参数独立。网络结构选择如下:
- Actor网络:通常是一个多层感知机(MLP)。例如:
输入层(obs_dim) -> 全连接层(256) -> ReLU -> 全连接层(256) -> ReLU -> 输出层(act_dim) -> Tanh。Tanh将输出限制在[-1, 1],再映射到实际动作范围。 - Critic网络:也是一个MLP,但输入维度更大。例如:
输入层(obs_dim * N + act_dim * N) -> 全连接层(256) -> ReLU -> 全连接层(256) -> ReLU -> 输出层(1)。
此外,和DDPG一样,每个当前网络都对应一个目标网络(μ'_i,Q'_i),用于稳定训练,通过软更新(θ' ← τθ + (1-τ)θ')缓慢跟踪当前网络参数。
3.2 经验回放与集中式Critic训练
多智能体环境的数据关联更复杂,我们使用一个共享的经验回放缓冲区(Replay Buffer)。每一条存储的经验元组是(o, a, r, o', done),其中o, a, r, o', done都是包含所有智能体信息的联合向量。采样时,一批这样的联合经验被取出,用于同时更新所有智能体。
训练Critic时,对于智能体i,其损失函数是TD误差的均方:L(θ_i) = E_{(o,a,r,o')}[(Q_i(o, a) - y)^2]其中目标y = r_i + γ * Q'_i(o', a')|_{a'_j = μ'_j(o'_j)}。这里a'是目标Actor网络根据下一个观测o'选择出的下一时刻联合动作。这就是“集中式”的体现:计算y时,Q'_i的输入需要所有智能体的目标动作a'。
训练Actor时,其目标是最大化Q_i(o, a_1, ..., a_N),其中a_i = μ_i(o_i),而其他智能体的动作a_j (j≠i)则从回放缓冲区中当前样本的动作部分取得(在计算图外,视为常数)。其梯度近似为:∇_{θ_i} J ≈ E_o[∇_{θ_i} μ_i(o_i) * ∇_{a_i} Q_i(o, a)|_{a_i=μ_i(o_i)}]PyTorch可以自动计算这个梯度。
3.3 PyTorch实现关键代码片段与技巧
以下是核心训练循环中的一个批处理更新步骤的简化代码,体现了上述原理:
import torch import torch.nn.functional as F def update_policy(self, agent_id, batch): # batch: dict of {'obs': [batch_size, n_agents, obs_dim], ...} obs = batch['obs'] actions = batch['actions'] rewards = batch['rewards'][:, agent_id].unsqueeze(1) # 智能体i的奖励 next_obs = batch['next_obs'] dones = batch['dones'][:, agent_id].unsqueeze(1) # 1. 更新Critic with torch.no_grad(): # 所有智能体根据目标网络选择下一动作 next_actions = [] for n in range(self.n_agents): next_actions.append(self.agents[n].target_actor(next_obs[:, n])) next_actions = torch.cat(next_actions, dim=1) # 计算目标Q值 target_q = self.agents[agent_id].target_critic(next_obs.reshape(batch_size, -1), next_actions) target_q = rewards + self.gamma * (1 - dones) * target_q current_q = self.agents[agent_id].critic(obs.reshape(batch_size, -1), actions.reshape(batch_size, -1)) critic_loss = F.mse_loss(current_q, target_q) self.critic_optimizer.zero_grad() critic_loss.backward() # 可添加梯度裁剪,防止爆炸 torch.nn.utils.clip_grad_norm_(self.agents[agent_id].critic.parameters(), 0.5) self.critic_optimizer.step() # 2. 更新Actor # 重新计算当前状态下的动作,其中智能体i的动作来自其当前Actor网络 new_actions = actions.clone() # 复制旧动作 new_action_i = self.agents[agent_id].actor(obs[:, agent_id]) new_actions[:, agent_id] = new_action_i # 替换为智能体i的新动作 actor_loss = -self.agents[agent_id].critic(obs.reshape(batch_size, -1), new_actions.reshape(batch_size, -1)).mean() self.actor_optimizer.zero_grad() actor_loss.backward() torch.nn.utils.clip_grad_norm_(self.agents[agent_id].actor.parameters(), 0.5) self.actor_optimizer.step() # 3. 软更新目标网络 self.soft_update(self.agents[agent_id].actor, self.agents[agent_id].target_actor, self.tau) self.soft_update(self.agents[agent_id].critic, self.agents[agent_id].target_critic, self.tau)实操心得:在计算Actor损失时,
new_actions中除了智能体i的动作外,其他智能体的动作new_actions[:, j]必须是从回放缓冲区取出的原始动作(actions[:, j]),并调用.detach()或通过克隆方式确保它们在计算图中被视为常量,不参与对智能体i的Actor参数求导。否则,梯度会通过其他智能体的动作错误地传播,这在理论上是错误的,实践中也会导致训练不稳定。
4. 自定义Gymnasium环境构建全流程
4.1 环境类骨架与核心方法
创建一个名为MultiUAVCaptureEnv的类,继承自gymnasium.Env。需要定义的关键属性和方法如下:
import gymnasium as gym import numpy as np class MultiUAVCaptureEnv(gym.Env): metadata = {'render_modes': ['human', 'rgb_array']} def __init__(self, num_uavs=3, arena_size=10.0, target_speed=0.5, ...): super().__init__() self.num_uavs = num_uavs self.arena_size = arena_size self.target_speed = target_speed # 定义动作空间和观测空间 # 动作:每个无人机3维加速度 (ax, ay, az),范围[-1, 1] self.action_space = gym.spaces.Box(low=-1, high=1, shape=(3,), dtype=np.float32) # 观测:每个无人机看到的信息,例如自身位置、速度、相对目标向量、最近两个队友的相对位置 obs_dim = 3 + 3 + 3 + (2 * 3) # 示例维度 self.observation_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(obs_dim,), dtype=np.float32) # 初始化状态 self.uav_positions = None self.uav_velocities = None self.target_position = None ... def reset(self, seed=None, options=None): # 重置环境到初始状态 super().reset(seed=seed) # 随机初始化无人机位置(在场地边缘附近)和目标位置(场地中心) self.uav_positions = ... # [num_uavs, 3] self.uav_velocities = np.zeros((self.num_uavs, 3)) self.target_position = ... # 计算每个无人机的初始观测 observations = self._get_obs() info = {} return observations, info def step(self, actions): # actions: List of arrays, 每个形状为(3,),对应每个无人机的加速度指令 # 1. 应用动力学模型更新状态 self._apply_dynamics(actions) # 2. 更新目标位置(例如,简单随机游走) self._update_target() # 3. 计算奖励和终止条件 rewards, terminated, truncated = self._compute_rewards_and_done() # 4. 获取新观测 observations = self._get_obs() info = {'episode': {'r': sum(rewards)}} return observations, rewards, terminated, truncated, info def render(self): # 可选:使用matplotlib或pygame进行可视化 ... def _get_obs(self): # 为每个无人机构建观测向量 obs_list = [] for i in range(self.num_uavs): # 自身状态 self_obs = np.concatenate([self.uav_positions[i], self.uav_velocities[i]]) # 相对目标 rel_target = self.target_position - self.uav_positions[i] # 最近队友的相对位置(按距离排序) distances = np.linalg.norm(self.uav_positions - self.uav_positions[i], axis=1) nearest_ids = np.argsort(distances)[1:3] # 排除自身,取最近两个 teammate_obs = (self.uav_positions[nearest_ids] - self.uav_positions[i]).flatten() # 拼接 full_obs = np.concatenate([self_obs, rel_target, teammate_obs]) obs_list.append(full_obs) return np.array(obs_list) # [num_uavs, obs_dim] def _apply_dynamics(self, actions): # 简单的二阶积分模型,考虑最大速度和加速度限制 dt = 0.1 # 时间步长 max_acc = 2.0 max_vel = 3.0 for i in range(self.num_uavs): # 限制加速度 acc = np.clip(actions[i], -1, 1) * max_acc # 更新速度 self.uav_velocities[i] += acc * dt # 限制速度 vel_norm = np.linalg.norm(self.uav_velocities[i]) if vel_norm > max_vel: self.uav_velocities[i] = self.uav_velocities[i] / vel_norm * max_vel # 更新位置 self.uav_positions[i] += self.uav_velocities[i] * dt # 边界弹性碰撞或吸收 self.uav_positions[i] = np.clip(self.uav_positions[i], -self.arena_size/2, self.arena_size/2)4.2 动力学模型与碰撞检测的实现细节
动力学模型_apply_dynamics是环境真实性的核心。上面的简化模型忽略了空气阻力、姿态动力学等。如果你需要更真实的四旋翼模型,可以考虑使用现成的动力学库(如gym-pybullet-drones中的模型),或者实现一个简化的牛顿-欧拉方程。但记住,模型越复杂,仿真步长时间越长,训练效率越低。对于算法验证,简单模型往往足够了。
碰撞检测_check_collision是必须的,它直接影响奖励和终止条件。对于球形无人机,检测很简单:
def _check_collision(self): collision_penalty = 0 terminated = False uav_radius = 0.3 # 无人机间碰撞 for i in range(self.num_uavs): for j in range(i+1, self.num_uavs): if np.linalg.norm(self.uav_positions[i] - self.uav_positions[j]) < 2 * uav_radius: collision_penalty += -10.0 # 大惩罚 terminated = True # 可设置为碰撞即结束本轮 # 无人机与边界碰撞(如果位置已用clip限制,则可能不需要) ... return collision_penalty, terminated注意事项:碰撞检测的计算复杂度是O(N^2),当无人机数量多时可能成为瓶颈。可以考虑使用空间划分数据结构(如四叉树/八叉树)来优化,或者在奖励函数中用连续的距离函数来“软化”碰撞惩罚(例如,当距离小于安全距离时,惩罚随距离减小而急剧增加),避免训练早期因频繁碰撞导致终止,学不到有效策略。
5. 训练流程、超参数调优与性能评估
5.1 完整的训练循环与日志记录
将环境、智能体、回放缓冲区组合起来,构成完整的训练流程。关键步骤包括:
- 初始化:环境、N个MADDPG智能体、共享经验回放缓冲区。
- 探索:在训练初期,为了鼓励探索,需要在Actor网络输出的动作上添加噪声(如OU噪声或高斯噪声)。随着训练进行,可以逐渐减小噪声幅度。
- 数据收集:每个时间步,每个智能体根据当前策略(带噪声)选择动作,环境执行联合动作,得到下一个状态和奖励,将整个经验元组存入缓冲区。
- 学习:当缓冲区数据足够后,每隔一定步数,随机采样一批数据,为每个智能体执行一次
update_policy。 - 评估:定期(如每100个训练轮次)运行一个评估阶段,关闭探索噪声,运行多个完整回合,记录平均回合奖励、成功率、平均步数等指标,以监控学习进度。
使用TensorBoard或Weights & Biases记录训练曲线至关重要。需要记录的指标包括:
train/episode_reward: 每个训练回合的总奖励。train/actor_loss: Actor网络的损失。train/critic_loss: Critic网络的损失。eval/success_rate: 评估阶段的围捕成功率(例如,所有无人机在目标一定距离内保持N步即算成功)。eval/mean_steps: 评估阶段完成任务的平均步数。
5.2 关键超参数设置与调优经验
MADDPG对超参数比较敏感。以下是一组常用的起点值,需要根据你的环境具体调整:
| 超参数 | 推荐值/范围 | 说明与调优经验 |
|---|---|---|
| 回放缓冲区大小 | 1e5 - 1e6 | 越大越好,但内存消耗也大。对于多智能体,需要存储联合经验,建议至少1e6。 |
| 批大小 (batch size) | 256 - 1024 | 通常256或512是好的起点。太小噪声大,太大计算慢且可能过拟合旧数据。 |
| Actor/Critic 学习率 | 1e-4 - 1e-3 | Actor的学习率通常略小于Critic(如Actor: 1e-4, Critic: 1e-3)。这是最需要调的参数之一。 |
| 折扣因子 γ | 0.95 - 0.99 | 0.99常见。如果任务步数短,可以低一些;任务步数长或需要长远规划,需要接近0.99。 |
| 目标网络更新率 τ | 0.005 - 0.01 | 软更新参数。越小目标网络更新越慢,训练越稳定但可能学习慢。0.005是常用值。 |
| 探索噪声 | OU噪声 θ=0.15, σ=0.2 | Ornstein-Uhlenbeck噪声适合惯性系统。初始标准差σ可设大些(如0.3),随训练衰减。 |
| 网络隐藏层 | [256, 256] 或 [128,128] | 两层或三层MLP通常足够。如果观测/动作维度很高,可以适当增加宽度。 |
| 激活函数 | ReLU | Actor输出层用Tanh,Critic输出层线性。中间层ReLU最常用。 |
| 优化器 | Adam | 默认参数β1=0.9, β2=0.999通常工作良好。 |
调优经验:
- 先调奖励函数,再调超参数:如果智能体根本学不到东西(奖励不上升),首先检查奖励函数设计是否合理,能否提供有效的学习信号。可以用一个简单的规则控制器(如直接飞向目标)在你的环境中测试,看它能获得多少奖励。
- 观察Critic Loss:Critic Loss应该随着训练逐渐下降并稳定在一个较低值。如果Critic Loss爆炸或变成NaN,通常是学习率太高、梯度爆炸或奖励值范围过大导致的。尝试降低学习率、添加梯度裁剪、或对奖励进行缩放/归一化。
- 探索与利用的平衡:初期探索噪声要大,让智能体充分探索状态空间。可以设置一个线性衰减计划,在训练到一定比例(如70%)后,将噪声减到很小。
- 智能体数量变化:训练好的策略对智能体数量可能有依赖性。一种更鲁棒的方法是使用参数共享,即所有智能体共享同一个Actor网络和Critic网络参数(但输入仍是各自的观测)。这有助于策略泛化到不同数量的智能体。
5.3 可视化评估与策略分析
训练完成后,可视化智能体的行为至关重要。除了在环境中render实时观看,还可以记录轨迹进行分析。
- 轨迹可视化:将每个时间步所有无人机和目标的位置记录下来,用动画(matplotlib.animation)或静态轨迹图展示。观察策略是否形成了有效的包围圈,是否有多余的机动。
- 策略分析工具:
- 价值热图:固定其他智能体和目标的位置,可视化某个智能体在空间不同位置的Critic Q值,可以理解它认为哪些位置“好”。
- 动作分布:在特定状态下,查看Actor网络输出的动作分布,检查是否合理(如是否总是输出最大加速度)。
- 注意力分析(如果网络有注意力机制):分析智能体的观测中,哪些部分(自身状态、目标信息、哪个队友的信息)对决策影响最大。
6. 常见问题排查与实战避坑指南
在多智能体强化学习项目中,你会遇到各种各样的问题。下面是我在复现和调试过程中遇到的一些典型问题及解决方法。
6.1 训练不稳定、奖励曲线震荡或无法提升
这是最常见的问题。
- 可能原因1:奖励函数设计不合理。奖励值过大或过小,或者存在相互冲突的奖励项。
- 排查:手动计算几个典型状态下的奖励值,看是否在合理范围(如-10到10之间)。检查正负奖励是否平衡。
- 解决:对奖励进行缩放(如除以一个常数)。简化奖励函数,先只保留核心奖励(如距离奖励),待学习稳定后再加入其他项。
- 可能原因2:学习率过高或优化器问题。
- 排查:观察Critic和Actor的损失曲线。如果损失值剧烈震荡或突然变成NaN,基本可以确定是学习率过高。
- 解决:将学习率降低一个数量级(如从1e-3降到1e-4)试试。确保使用了梯度裁剪。
- 可能原因3:探索不足。智能体早期被困在局部区域,无法发现更好的策略。
- 排查:查看早期回合的轨迹,看无人机是否只在很小范围内活动。
- 解决:增加初始探索噪声(OU噪声的σ)。或者尝试在训练初期使用完全随机策略收集一部分数据填充回放缓冲区。
- 可能原因4:非平稳性问题。这是多智能体固有的挑战,一个智能体策略的改变相当于环境在变,导致其他智能体之前学的Q函数失效。
- 排查:对比单智能体版本(只有一个无人机追目标)是否容易训练。如果单智能体很稳定,多智能体不稳定,很可能就是这个问题。
- 解决:MADDPG的集中式Critic本身就是为了缓解此问题。可以尝试更频繁地更新策略,或者使用策略集成(多个策略)来增加稳定性。
6.2 智能体学不会协同,表现为“自私”或“拥挤”
所有无人机都只追逐目标,挤在一起,不会分散包围。
- 可能原因:奖励函数缺乏明确的协同激励。如果奖励只基于个体到目标的距离,那么每个无人机的最优策略就是直线飞向目标,自然会导致拥挤。
- 解决:在奖励函数中显式地加入协同奖励项。例如:
- 角度分布奖励:计算所有无人机相对于目标的角度(在水平面上),奖励它们角度的均匀性(如最小夹角最大化)。
- 距离差异奖励:奖励无人机到目标距离的方差,鼓励它们分布在不同的距离上(内圈和外圈配合)。
- 基于 Voronoi 图的奖励:将空间根据无人机位置划分,奖励每个无人机负责的区域覆盖目标的面积。 引入这些项时,权重需要仔细调整,一开始权重小一些,避免干扰基本追逐行为的学习。
6.3 仿真速度慢,训练耗时过长
多智能体仿真和训练本身计算量就大。
- 可能原因1:Python循环过多。环境中的
for i in range(num_uavs)循环是主要瓶颈。 - 解决:向量化操作。利用NumPy的广播机制,一次性对所有无人机进行计算。例如,更新位置和速度:
# 向量化动力学更新示例 accelerations = np.clip(actions, -1, 1) * self.max_acceleration # [n_agents, 3] self.velocities += accelerations * self.dt # 限制速度范数 vel_norms = np.linalg.norm(self.velocities, axis=1, keepdims=True) exceed_mask = (vel_norms > self.max_velocity).squeeze() if np.any(exceed_mask): scale = self.max_velocity / vel_norms[exceed_mask] self.velocities[exceed_mask] *= scale self.positions += self.velocities * self.dt - 可能原因2:渲染开销大。如果使用matplotlib的
FuncAnimation实时渲染,会严重拖慢速度。 - 解决:训练时关闭渲染(
render_mode=None),或者大幅降低渲染频率(每100步渲染一次)。只在评估和演示时开启高质量渲染。 - 可能原因3:网络前向传播频繁。每一步都需要所有智能体的网络做前向传播。
- 解决:确保使用GPU进行训练。将观测、动作等数据在每一步都保持在GPU张量上,避免CPU-GPU之间的频繁数据传输。
6.4 策略在评估时表现远差于训练
训练曲线看起来很好,但关掉噪声后测试,无人机表现傻掉了。
- 可能原因:过度依赖探索噪声。智能体可能学会了利用噪声的特定模式来完成动作,而不是学习真正的状态-动作映射。
- 排查:在训练中期和后期,分别用带噪声和不带噪声的策略运行评估,对比性能。
- 解决:
- 在训练过程中,定期进行无噪声的评估,并以此作为保存最佳模型的依据。
- 使用延迟策略更新(Delayed Policy Updates):Critic更新多次后,才更新一次Actor。这能让Critic在更准确的Q值估计下指导Actor更新。
- 尝试在Actor的损失中加入策略熵正则化项(尽管MADDPG是确定性策略,但可以对其输出动作的分布加噪声后的熵进行正则化),鼓励探索的同时提高策略的鲁棒性。
这个项目从环境搭建到算法实现,再到调参优化,是一个完整的深度强化学习工程闭环。最大的收获往往不是最终那个漂亮的围捕动画,而是在解决上述一个个具体问题的过程中,对多智能体协同、强化学习算法以及仿真编程的深刻理解。当你看到无人机们从无头苍蝇演变成一支训练有素的团队时,那种感觉,绝对是敲代码的一大乐趣。
本文还有配套的精品资源,点击获取