简介:针对MAPPO多智能体强化学习在航天器编队控制中的应用,压缩包提供一套可直接运行的完整工程,包含1个Python主程序、2张轨迹与训练结果图、1份功能检查清单和1份任务说明文档,整体仅2.87MB。系统实现8个航天器在20个空间碎片环境中从圆形编队到对角线编队的精确变换,采用集中训练-分散执行(CTDE)架构,融合控制屏障函数安全约束与PD控制器混合策略,达到编队误差≤14m、位置误差≤10m的性能指标。除完整代码外,资源还详细解析MAPPO算法原理、混合控制策略设计及安全约束机制,并附有功能检查清单便于逐项验证,轨迹图可直观对比编队演化过程。已有88人学习下载,适合航天器编队控制、强化学习应用方向的工程师和研究生参考。
1. 多航天器编队变换为什么要用 MAPPO:从奖励稀疏和耦合控制说起
把一个三星星座从沿轨串飞构型改成空间绕飞构型,看似只差几次目标位置修正,真正上手做时就会发现,比单星轨道机动难的不是一点半点。每个航天器都在推自己的位置误差,控制量又会互相扰动,传统控制器在这里很容易出现来回震荡。MAPPO 的价值在于,它把编队看作一组共享环境、各自决策的智能体,用集中训练、分布执行的 Actor-Critic 结构,让每颗星学会在队友也会动的前提下做动作,而不是各自死磕目标点。这套系统的完整代码就是围绕这个思路组织的,环境、算法、训练入口都齐备,适合做星群任务规划、编队重构和相对导航控制的工程师与研究生,拿来改改奖励函数就能接自己的任务。
2. 拿到的系统长什么样:代码结构与环境搭建的完整骨架
这套系统能直接跑起来,靠的不是算法封装,而是环境建模时没有回避关键物理量。多航天器编队变换通常以领航星为参考点,跟随星处在几百米到几公里的相对距离上,远小于轨道半径,所以相对运动可以近似为线性系统。最常见的是 C-W 方程(Hill 方程),在旋转坐标系下,状态量为 6 维——相对位置(x, y, z)和相对速度(vx, vy, vz),控制量为推力加速度(ax, ay, az)。方程里的加速度由三部分叠加:径向方向的重力梯度项 3 n² x、轨道面内的科氏力项 2 n [vy, -vx, 0]、以及轨道法向的重力恢复项 -n² z。
这里的 n 是领航星的平均运动角速度,低轨场景下大约在 0.001 rad/s 量级。很多入门实现喜欢直接把绝对位置塞进去,忽略了领航星的轨道运动,结果编队背景速度远大于相对控制量,训练出来的策略只会一路跟着背景速度走。正确做法是先把绝对轨道运动剥掉,只对相对状态做控制。
import numpy as np def cw_step(state, thrust, n, dt): """基于 C-W 方程的相对运动单步递推。 state: (x, y, z, vx, vy, vz),单位 m, m/s thrust: 推力加速度 (3,),单位 m/s^2 n: 轨道角速度 rad/s dt: 仿真步长 s """ x, y, z, vx, vy, vz = state ax, ay, az = thrust ax_cw = 2.0 * n * vy + 3.0 * n * n * x ay_cw = -2.0 * n * vx az_cw = -n * n * z vx_new = vx + (ax_cw + ax) * dt vy_new = vy + (ay_cw + ay) * dt vz_new = vz + (az_cw + az) * dt x_new = x + vx_new * dt y_new = y + vy_new * dt z_new = z + vz_new * dt return np.array([x_new, y_new, z_new, vx_new, vy_new, vz_new])逻辑上这段代码先算 C-W 方程给出的自然加速度,再叠加上推力加速度,然后做半隐式欧拉积分。半隐式意味着先更新速度、再用新速度更新位置,比完全显式欧拉稳定,又不需要构造矩阵求逆,很适合放在训练循环里每步调用。要改成轨道转移大机动时,这个模型就不够用了,得切回二体问题做数值积分,但编队变换属于相对距离几百米的小偏差问题,线性化精度足够。
参数 n 和 dt 的匹配非常影响收敛。dt 是控制周期,也是强化学习环境的决策周期,我一般取 0.5 到 1 秒。如果 dt 太大会让离散误差超过控制量精度,训练前期就出现发散;如果 dt 太小,一个编队变换 episode 要几千步,训练速度会慢到让人想放弃。环境写完后先做一件事:不给推力让卫星自由积分 1000 秒,如果相对位置在闭合椭圆上转而不是直线飞走,说明动力学部分基本正确。
编队构型描述上,这套系统用领航者-跟随者模型。初始构型和目标构型都放在配置里,常见的有三类:沿轨串飞、水平圆绕飞、垂直平面椭圆绕飞。编队变换就是让每个跟随星的相对位置从初始偏置移动到目标偏置,同时把相对速度压下来,控制量尽量小。有了这个定义,后面状态空间和奖励函数才能跟着落地。
2.1 代码包目录与运行前的环境准备
强化学习项目最常见的返工原因是环境、算法、超参数混在一起。这套系统按典型的分层方式组织:环境层负责状态、动作、奖励;算法层只做策略更新,完全不接触动力学;配置层集中放超参数和编队构型;入口脚本把前两层串起来。好处是换编队构型时不用动算法文件,换算法时也不用重写环境。
formation_mappo/ ├── envs/ │ ├── formation_env.py # 编队变换仿真环境,C-W动力学+观测+奖励 │ └── __init__.py ├── algo/ │ ├── networks.py # Actor 与 Critic 网络定义 │ ├── mappo.py # MAPPO 策略更新核心 │ └── buffer.py # rollout 存储与 GAE 计算 ├── config/ │ └── hyperparams.yaml # 动力学与训练参数,含编队构型定义 ├── train.py # 训练入口 ├── test.py # 加载权重做构型变换回放 └── requirements.txt # 依赖清单formation_env.py 是核心环境文件,里面包含构型初始化、C-W 单步递推、观测拼接、奖励和碰撞检查。networks.py 只放网络的类定义,不写训练逻辑。mappo.py 是 PPO 更新部分,接收 buffer 里的数据做裁剪和梯度更新。buffer.py 里做 GAE 计算,这段代码单独拎出来是对的,因为多智能体场景下每个智能体的 value 估计都要单独算,混在训练循环里很难查 bug。
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt运行前先把 Python 环境隔离起来,不要直接往系统环境里安装。强化学习训练依赖链很脆弱,torch 和 numpy 版本不一致,轻则警告,重则训练到一半突然爆出一个算不出来梯度的错。requirements.txt 我习惯写成下限版本而不是锁死版本,方便在不同机器上安装。如果你要复现论文级的结果,就把精确版本记录进去,比如 torch==2.1.2、numpy==1.26.4,否则换台机器环境不同,曲线完全对不上。
numpy>=1.24 torch>=2.0 matplotlib>=3.8 pyyaml>=6.0装完之后,第一次建议直接跑默认配置。跑之前确认没有多个 Python 环境同时生效,很多新手在虚拟环境里装好依赖,命令行却还指向全局 Python,导致 import torch 失败或版本不对。如果机器没有 GPU,不需要改代码,PyTorch 会自动落到 CPU 上跑,只是训练时间会明显拉长。
训练命令就是一句:
python train.py --config config/hyperparams.yaml训练过程会把网络权重保存到 runs/exp1/ 目录,测试入口只读权重文件做回放。把训练和测试分开是刻意为之,测试时不希望策略还带着探索噪声,而是要使用确定性的均值动作来评估构型变换效果。
3. MAPPO 算法在编队变换里的落地:状态、动作、奖励与网络架构
MAPPO 之所以适合多航天器编队变换,核心在于集中训练分布执行。编队里每颗航天器决策时只能拿到自己的局部观测,但训练时可以让 Critic 看到所有航天器的完整状态。这样缓解了多智能体环境中的非平稳性问题,又不牺牲部署时的独立性。单智能体 PPO 直接套上去不是不能用,但每个智能体各自训练 Critic 时,其他智能体的策略变化会把它学到的价值函数搞废,收敛极不稳定。
3.1 状态空间与动作空间怎么定义
状态空间的设计要遵循一个原则:部署时能拿到什么,训练观测里就只能放什么。多航天器之间如果假设没有全连通通信,那么每颗星只能看到自己的相对位置速度、目标构型偏移,以及可以通过星间链路测到的邻居相对位置。目标构型偏移必须放进观测,因为编队变换任务本质上是从当前态转移到指定构型,不告诉智能体目标位置,它只能盲目探索。
import numpy as np class FormationEnv: def __init__(self, num_agents=3, safe_dist=30.0): self.num_agents = num_agents self.safe_dist = safe_dist self.n = 0.001 self.dt = 0.5 def _encode_obs(self, agent_id): # 自身相对位置速度:6 维,做归一化 ego = self.rel_state[agent_id].copy() / np.array([500.0, 500.0, 500.0, 5.0, 5.0, 5.0]) # 目标构型偏移:3 维 target = self.target_offsets[agent_id].copy() / 500.0 neighbor = [] for j in range(self.num_agents): if j != agent_id: d = self.rel_state[j][:3] - self.rel_state[agent_id][:3] neighbor.append(d / 500.0) return np.concatenate([ego, target] + neighbor)这段代码的关键是归一化。航天器相对位置动辄几百米,速度在每秒米量级,如果不归一化直接喂给神经网络,梯度会被大数值特征带偏,训练很容易出现 NaN。尺度因子不是拍脑袋定的,我从这些物理量的典型值出发:位置 500 米、速度 5 m/s。如果你的场景是几公里的编队,把 500 改成 3000;如果是近距离操作,改成 50。
动作空间定义为连续推力加速度,维度是 3,取值范围正负 0.01 m/s²。这个范围对应小推力器做编队维持的典型量级。如果取值过大,智能体会用蛮力快速冲过去,碰撞惩罚压不住;如果过小,智能体永远追不上目标构型。Actor 网络输出经过 tanh 限制到 [-1, 1],再线性映射到实际加速度范围。
action_low = -0.01 * np.ones(3) action_high = 0.01 * np.ones(3) def _map_action(raw_action): return 0.5 * (raw_action + 1.0) * (action_high - action_low) + action_low这里有个容易踩的细节:网络输出的分布是一个高斯分布,采样出来的值通过 tanh 变换后,log_prob 也要跟着修正。很多直接拿单智能体 PPO 改多智能体的代码忘了修正这项,导致策略梯度方向错误,训练曲线看起来在涨,实际上策略根本没有被正确优化。
3.2 奖励函数设计:主奖励、构型惩罚与避碰项
奖励是这套系统里最需要花时间的部分。多航天器编队变换要同时满足到达目标构型、相对速度衰减、避免碰撞、节省燃料四个目标,把这四个目标叠成一个标量奖励,需要仔细平衡权重。
我常用的奖励结构是一个步进式惩罚加稀疏到达奖励:每个控制周期计算相对位置误差,误差越小奖励越高;同时惩罚相对速度、推力大小和碰撞风险。初始构型和目标构型相差几百米时,稀疏奖励完全没有引导意义,前期探索会浪费大量时间。
def _compute_reward(self, agent_id, thrust): pos_err = np.linalg.norm( self.rel_state[agent_id][:3] - self.target_offsets[agent_id]) vel_err = np.linalg.norm( self.rel_state[agent_id][3:]) thrust_penalty = np.linalg.norm(thrust) r = (-0.5 * pos_err - 0.1 * vel_err - 1.0 * thrust_penalty) r += self._collision_penalty(agent_id) return r def _collision_penalty(self, agent_id): penalty = 0.0 for j in range(self.num_agents): if j == agent_id: continue d = np.linalg.norm( self.rel_state[j][:3] - self.rel_state[agent_id][:3]) if d < self.safe_dist: penalty -= (self.safe_dist - d) ** 2 return penalty位置误差项的权重是 0.5,速度误差是 0.1,推力惩罚是 1.0。这个梯度的含义是:优先减小位置误差,但不要为了快速到达而用大力矩。推力惩罚权重放到 1.0,鼓励智能体走平滑轨迹。如果训练后发现轨迹来回震荡,就把推力惩罚调高;如果到达目标后还在原地飘,就把速度误差权重调高。
碰撞惩罚做了平方惩罚,距离越近惩罚增长越快。safe_dist 设为 30 米,这个值要根据编队尺度调整。如果编队间隔是 200 米,30 米安全距离合理;如果是 50 米的小编队,安全距离要降到 10 米左右,否则奖励里避碰项会压过主任务项,智能体宁可站在原地也不动。
关于额外的到达奖励,我会在构型误差小于阈值时加一个正项。这个项不需要很大,但能显著告诉智能体“这样做是对的”。没有这个项时,策略会把构型误差降到很小,但不会精确到位;加了这个项后,末端收敛精度明显提升。
3.3 Actor-Critic 网络与 CTDE 实现
Actor 网络负责从局部观测到动作分布。为了让多颗星共享经验,系统采用了共享参数 Actor,每颗星都使用同一个网络,观测中包含自己的目标构型偏移,所以网络能区分不同任务。这种方式在多智能体配合类任务里训练效率更高,不需要每增加一颗星就重新训练一套参数。
import torch import torch.nn as nn class Actor(nn.Module): def __init__(self, obs_dim, act_dim, action_scale=0.01): super().__init__() self.mlp = nn.Sequential( nn.Linear(obs_dim, 128), nn.Tanh(), nn.Linear(128, 128), nn.Tanh(), nn.Linear(128, 128), nn.Tanh(), ) self.mean_head = nn.Linear(128, act_dim) self.log_std = nn.Parameter(torch.zeros(act_dim)) self.action_scale = action_scale def forward(self, obs): h = self.mlp(obs) mean = torch.tanh(self.mean_head(h)) * self.action_scale std = torch.exp(self.log_std.clamp(-5.0, 0.5)) return mean, std def sample(self, obs): mean, std = self.forward(obs) dist = torch.distributions.Normal(mean, std) raw = dist.sample() action = torch.tanh(raw) * self.action_scale # 对 tanh 变换后的分布做 log_prob 修正 log_prob = dist.log_prob(raw).sum(-1) log_prob = log_prob - torch.log((1 - action / self.action_scale ** 2) + 1e-6).sum(-1) return action, log_probActor 输出均值后经过 tanh 压缩,再乘 action_scale。log_std 是学习参数,初始化为 0,也就是方差为 1。训练时 clamp 到 [-5, 0.5],防止标准差学到极端值。这段网络的隐藏层用了三层 Tanh,而不是 ReLU,因为连续控制任务里 Tanh 的平滑特性通常更好,ReLU 在这类小网络上容易出现死神经元。
Critic 网络的结构更简单,输入是全局状态,输出是价值估计。全局状态是所有航天器观测拼接而成,包含位置速度目标偏移和邻居信息。集中训练时 Critic 拥有全局信息,它评估的是联合状态的价值,而不是单颗星自己的价值。
class Critic(nn.Module): def __init__(self, global_state_dim): super().__init__() self.mlp = nn.Sequential( nn.Linear(global_state_dim, 256), nn.ReLU(), nn.Linear(256, 256), nn.ReLU(), nn.Linear(256, 1), ) def forward(self, global_state): return self.mlp(global_state).squeeze(-1)部署时只需要 Actor,Critic 只在训练阶段存在。这就是 MAPPO 相对独立 PPO 的核心优势:训练时可以用全局信息降低方差,执行时每颗星又完全独立。如果你在真实星上部署,加载 Actor 权重即可,Critic 网络保存下来主要是为了后续继续训练。
4. 训练主循环与关键参数调优:让系统跑起来并收敛
环境、奖励、网络都就位后,真正让系统收敛的是训练循环和超参数。多航天器场景的 rollout 数据量比单智能体大得多,因为每个环境 step 会同时产生所有航天器的经验。这里最忌讳的是把数据一股脑塞进 PPO 更新,却不做 GAE 和优势标准化。
4.1 训练入口与 rollout 采集
训练主循环的结构可以用一小段伪代码表示:
def train(): for update in range(num_updates): rollout = [] obs_list = env.reset() for step in range(steps_per_update): with torch.no_grad(): actions = actor.sample(torch.FloatTensor(obs_list)).numpy() next_obs, rewards, done = env.step(actions) rollout.append((obs_list.copy(), actions.copy(), rewards.copy(), done.copy())) obs_list = next_obs buffer.from_rollout(rollout) buffer.compute_gae(critic, gamma, lam) for epoch in range(update_epochs): batch = buffer.sample(mini_batch_size) update_actor_critic(actor, critic, batch, clip_eps, entropy_coef)实际代码里 rollout 还需要存每个 step 的 value 估计和 actor 输出的 log_prob,这里画的是训练主干的形状,目的是讲清楚数据流。rollout 采集阶段关闭梯度计算,因为此时只是环境交互,不需要回传梯度。actor.sample 返回的 action 是带探索噪声的,测试时不走这个流程,而是直接取分布均值。
buffer.from_rollout 负责把原始序列转成训练需要的张量。compute_gae 计算广义优势估计,这一步需要使用 Critic 的价值函数做时序差分。多智能体场景下,每个智能体的 reward 是独立存储的,因此 GAE 也要按智能体维度分别计算。最容易犯的错是把所有航天器的 reward 求平均再算优势,这样会抹掉个体差异,个别星学不动。
4.2 PPO Clip、GAE、熵系数这些参数怎么设
MAPPO 的超参数大部分继承自 PPO,但在多航天器场景里有自己的偏好。这张表是我不太改动训练结构时的常用起点:
| 参数 | 常用值 | 作用与调整策略 |
|---|---|---|
| lr | 3e-4 | Actor 和 Critic 共用优化器,训练不稳时降到 1e-4 |
| gamma | 0.99 | 折扣因子,编队变换时间尺度短,0.99 足够 |
| gae_lambda | 0.95 | 大一点方差高,小一点偏差高 |
| clip_eps | 0.2 | 裁剪幅度,任务复杂时降到 0.1 |
| entropy_coef | 0.01 | 探索度,越大策略越随机 |
| update_epochs | 10 | 每个 rollout 更新 10 轮 |
| mini_batch_size | 1024 | 显存不足时减小到 512 |
调参顺序不要太跳跃。我一般先固定其他参数,单独把学习率从 3e-4 降到 1e-4,如果训练曲线明显变稳,再考虑调 entropy_coef。entropy_coef 是一个需要反复试的参数,太小会让策略过早固化,编队变换这一类的多峰最优问题里特别容易陷进局部解;太大会让策略一直随机抖动,永远学不到精细控制。
mini_batch_size 决定了每次更新的数据量,它和 update_epochs 搭配。如果 mini_batch_size 太小,数据重复使用次数不变的情况下,每次梯度估计噪声很大;如果太大,更新次数少,经验利用率低。多航天器环境单次 rollout 产生的数据是环境步数乘以智能体数量,通常一个 mini_batch 可以覆盖 1 到 2 个完整 episode。
4.3 从训练曲线判断收敛与构型变换效果
判断训练结果不能只看 episode reward。多智能体系统的平均奖励容易被个别表现好的智能体拉高,掩盖其他智能体没学好的事实。我习惯同时看三个指标:episode reward 滑动平均、所有航天器到目标构型的平均距离、以及碰撞惩罚项。
python train.py --config config/hyperparams.yaml --run_dir runs/exp1训练脚本会实时输出这些指标。如果 reward 在涨,但平均距离下降很慢,说明奖励函数里位置误差权重太低;如果距离已经收敛,reward 还在缓慢下降,通常是推力惩罚在起作用,策略在微调轨迹平滑度。
借助训练日志可以画出曲线。这里不需要 TensorBoard,matplotlib 画个滑动平均图就行。看曲线时注意区分:训练初期 reward 快速上升是正常的,说明智能体从随机探索中找到了基本控制策略;中期进入平台期是正常的,策略在优化细节;后期如果出现大幅回退,说明超参数或奖励权重有问题,直接停掉改配置,不要硬跑下去。
5. 多航天器编队训练的常见问题与避坑排查
多航天器强化学习的坑,绝大多数不在算法本身,而在环境、奖励和数据分布上。下面几条都是我在类似任务里反复踩过的,每一条都是先看到现象、再追根因、最后改配置解决。
5.1 现象一:episode reward 前期上涨,训练到一半突然变成 NaN
现象:训练曲线前几百个 episode 都正常,然后 reward 突然变成 NaN,训练被迫中断。重启后换了个随机种子又能跑一段,但还是会在某个地方崩掉。
原因:最常见的是状态或奖励里出现了极大数值,经过损失函数回传后梯度爆炸。C-W 方程里的相对位置如果因为动力学发散涨到几千公里,归一化因子就不再适用,网络输入直接爆掉。另一个常见源是 Critic 的 value 估计过大,导致 GAE 出现异常值。
解决:先检查状态归一化,确认相对位置的尺度不会被突破。然后把 PPO 更新里对 advantage 做标准化这一步骤打开,也就是减去均值除以标准差。给 log_std 加 clamp 也很有用。如果还崩,把学习率降到 1e-4 再试一次。我之前在这上面花了两天时间,最后发现问题出在某颗星初始位置被随机到了极其接近领航星,相对速度又很大,一步积分就把位置推到几十公里外,归一化彻底失效。解法很简单:初始化时限制相对速度和位置的取值范围。
5.2 现象二:有一两颗星始终不去目标构型,沿轨道方向往复飞行
现象:训练完成后,其中一颗星的位置误差已经收敛到几十米以内,另一颗星却还在目标构型附近来回飘,轨迹像正弦波一样沿飞行方向振荡。整体 reward 看着还行,因为那颗学得好的星把平均值拉上去了。
原因:共享奖励下的信度分配问题。MAPPO 虽然每个智能体有自己的 Actor,但如果 reward 用的是全体智能体奖励的均值,个体无法准确判断自己的动作对总奖励的贡献。学得好的星会掩盖学得差的星,后者在梯度中收到的信号被稀释了。另一个原因是观测里没有包含自身的目标构型偏移,导致所有星学习到的其实是同一套控制策略,无法区分布置。
解决:把奖励改成个体主奖励加小量全局辅助奖励。每颗星的距离误差项用自己的目标偏移计算,碰撞惩罚保留全局项。观测里已经包含了目标偏移,可以再增加一个 one-hot 的 agent_id 向量,让共享网络有机会区分不同星。这类问题排查时不要只看 total reward,要分别打印每颗星自己的平均距离误差,一眼就能看出是哪颗星在摆烂。
5.3 现象三:训练曲线很好,测试时却变成一团散沙
现象:训练结束时的评估曲线看着很漂亮,每颗星距离误差都降到了 30 米以内。把保存的权重加载进测试脚本后,构型完全保持不住,几颗星四散飞开,有的甚至撞在一起。
原因:训练过程中 Actor 输出带探索噪声,且每个 episode 的初始状态是在固定范围内随机采样的。测试时如果继续使用采样动作,策略会在轨迹上抖动,而这类由随机策略学出来的策略,均值动作往往才是稳定解。另一个原因是训练时初始构型扰动范围太小,测试时给了一个不同的初始相对速度,策略没见过这类状态,自然就崩了。
解决:测试时使用 actor 网络输出的均值,而不是从分布中采样。固定测试脚本的随机种子,让每次验证落在同一组初始条件上。如果真实使用场景的初始构型散布比训练时大,就在训练环境的 reset 函数里增大初始状态扰动范围。我之前正是靠增加初始速度扰动解决这个问题,原来训练时初始速度默认为零,测试时给了 0.5 m/s 的初始观测量,策略直接失稳。增加扰动范围重新训练后,泛化性好了很多。
5.4 现象四:换台机器训练完全跑不动,CPU 和 GPU 的结果对不上
现象:同一份代码,在开发机上用 CPU 训练能收敛,拿到 GPU 服务器上训练后 reward 曲线完全不同,甚至 NaN;或者在装有不同版本 numpy 的机器上,连环境初始化都会报形状错误。
原因:一是全局随机种子没有在环境创建前固定,不同机器上多线程运算的顺序不同,导致经验轨迹不一致。二是 PyTorch 在 CPU 和 GPU 上的浮点累加顺序不同,小数值差异会被 PPO 更新放大。三是 numpy 和 torch 的版本组合不一致,导致某些操作返回了不同 dtype。
解决:在训练入口最开始固定所有随机源,包括 Python 的 random、numpy 和 torch;设置 torch.manual_seed 之后再创建环境。对于需要严格复现的场景,把 torch.use_deterministic_algorithms(True) 打开,并设置 torch.set_num_threads(1)。依赖环境写成 requirements-lock.txt,精确到次版本号。不要迷信 GPU 一定比 CPU 好,多航天器环境规模不大时,CPU 单线程加确定性算法的训练结果更容易复现。
6. 验证与进阶:从仿真曲线到一次完整的编队变换演示
验证这套系统最简单的方式是直接用 test.py 加载训练好的权重,跑一次从串飞构型到水平圆绕飞构型的完整变换。
python test.py --checkpoint runs/exp1/model.pt \ --scenario transform_A_to_B \ --seed 42脚本会把每个航天器的相对轨迹画出来,同时输出终端时刻位置误差和碰撞次数。验收标准我习惯这样定:位置误差小于 15 米、相对速度误差小于 0.05 m/s、全程无碰撞。满足这三个条件,训练才算真正成功,而不是只看 reward 曲线。
进阶方向有几个值得尝试。第一个是把奖励函数里的推力惩罚拆成平滑项和燃料项,平滑项惩罚相邻两个时刻控制量的差值,可以进一步抑制震荡。第二个是把安全距离从固定值改成动态值,构型变换初期用大安全距离,后期逐渐收紧,这样既能避免训练前期碰撞,又不会让策略因为避碰要求而不敢靠拢。
我现在已经养成一个习惯:训练结束后不急着调参,而是把测试阶段每个智能体的到达误差单独打印出来,看是谁拖了后腿。多智能体系统最容易出现的情况就是平均奖励好看、个别智能体摆烂。你拿到这套系统后,建议也先用同样的方式验证,确认每颗星都真的到达目标构型,再决定要不要调奖励权重。希望帮到你。
本文还有配套的精品资源,点击获取