
简介基于Python与STK11的多智能体强化学习卫星调度实验资源面向人工智能、通信工程、自动化、电子信息等专业的高校学生与科研工作者适用于毕业设计、课程作业或项目初期演示。内容涵盖完整源码、详细设计文档、实验数据与报告可帮助理解多智能体强化学习在卫星调度场景中的应用与实现。资源包共203个文件主要包括Python脚本、PNG图表、CSV实验数据、STK场景快照sn/sa/sc等、模型权重pth文件、Word文档等压缩包大小79.62MB目录规整便于检索。已有150人学习下载。资源提供经过功能测试的完整工程除核心算法外还附带多组CSV训练数据与增强数据集、飞行场景文件及配置说明便于直接运行复现实验也支持二次开发拓展其他调度场景适合入门到进阶学习。1. 从“单星单任务”到“多星协同”的调度实验如果只是单颗卫星按预定时间窗口拍地面目标传统回溯算法基本够用可当卫星数上到 4 颗、目标数上到几百再考虑侧摆、存储和下行时间这就不再是匹配问题而是一个带时间窗约束的多机调度问题复杂度直接落在 NP-Hard。这个实验正是围绕这个场景展开用 Python 连接 STK11 生成访问窗口再将窗口数据喂给多个智能体让每颗星自己学“什么时候拍、拍完是否下传、要不要把余量留给后续目标”。项目包里同时给了完整源码、设计文档和多种 CSV 数据适合做毕设或课程设计也适合刚接触强化学习的人从数据读到策略输出。下面我会按数据生成、环境建模、训练调参、验证扩展这条主线拆开关键参数和坑直接用命令和代码说明。2. 用 STK11 生成访问窗口数据是调度策略的地基2.1 STK11 在实验里的角色几何计算交给 STK决策留给 Python多星调度最耗时也最不适合手写编码的部分是“可见时间窗”。做这个实验时我们拿到的通常是轨道根数和目标经纬度能不能在某段时间内看到某个目标涉及地球掩星、地形遮挡、侧摆角度等一系列几何计算。如果全部在 Python 里自己算一个中等规模场景的校验时间就可能超过策略训练的时间。stk11是 STK 的 Python 封装它把 STK 当作计算引擎Python 只负责发起场景构建、查询访问、导出结果。这样状态空间不是抽象编号而是跟仿真秒时间对齐的真实窗口强化学习智能体学出来的策略才更接近工程可用。很多人一上来就调强化学习算法忽略这一步结果训练曲线很漂亮换一批卫星轨道数据后策略却彻底失灵问题多半出在窗口数据本身。动手前先确认 STK 许可可用并检查stk11能不能正常连上。我一般用 3.8 以上版本的 Python在命令行里做一次最简单的连通性测试python -c from stk11 import STK11; print(STK11())能打印出对象说明通信链路正常。若导入失败先看是否有 STK 安装目录和许可文件而不是急着换库。项目里的 CSV 都是预先从 STK 场景导出的所以训练流程并不依赖 GUI但复现新实验时这一步绕不开。2.2 用 stk11 建立场景并批量导出访问区间搭建实验时我不会在 STK GUI 里手动点选而是写一个统一脚本。下面这段代码对应项目里数据准备模块的核心逻辑重点是把访问开始时间和结束时间批量写入 DataFrame。注意这只是演示思路完整 API 以你本机 stk11 版本为准。from stk11 import STK11 import pandas as pd stk STK11() stk.new_scenario(sat_sched_demo) sc stk.get_scenario() sat sc.create_satellite(SAT01) sensor sat.create_sensor(SENSOR01) for idx, (lat, lon) in enumerate([(31.2, 121.5), (40.4, 116.6), (22.5, 113.9)]): sc.create_target(fT{idx}, latlat, lonlon) for tgt in sc.get_children(typeTarget): access stk.calculate_access(sat, tgt) intervals access.compute_interval() for i in intervals: print(tgt.name, i.start, i.end)这里的思路是先建场景再创建卫星和传感器最后对每个目标计算访问区间。intervals中每个元素都带有开始和结束时间但单位可能是 UTCG 日期字符串直接写进 CSV 后处理会很麻烦。常见做法是转成相对场景起始时间的秒数epoch sc.get_epoch() rows [] for i in intervals: rows.append({ target_id: tgt.name, start_sec: (i.start - epoch).total_seconds(), end_sec: (i.end - epoch).total_seconds(), }) df pd.DataFrame(rows) df.to_csv(1_access_200_500.csv, indexFalse)epoch是场景起始时间把绝对时间统一转成秒后强化学习环境可以用连续时间轴处理窗口不用在每个 step 里解析字符串时间训练速度会有可感知的提升。项目里的1_access_200_500.csv、lab1.csv、lab4.csv等文件基本都遵循类似结构只是字段顺序可能不同解析时以start_sec、end_sec这两个字段为主线。2.3 从文件名看数据规模和用途源码包里的 CSV 命名不是随机的。数字部分通常暗示任务规模和窗口数量后缀则说明是否做过数据增强。我按项目文件的实际用途整理了一张速查表文件可读出的信息用途1_access_200_500.csv200 个目标、500 个时间窗附近小规模训练用快速验证环境是否跑通lab1.csv/lab2_4.csv/lab3_400.csvlab 代表不同实验分组不同约束或目标数量用于消融对比MRL_data_400_1000_augmented.csv400 个目标、1000 个窗口增强数据做过时间抖动等扩增用于最终效果评估lab1_augment.csvlab1 的扩增版本测试数据增强对策略泛化能力的影响在读数据时建议先用pandas检查空行和缺失时间窗。同一个目标可能在多个不连续窗口出现这意味着智能体必须决定用哪个窗口而不是看到窗口就抢。这样一个 CSV 文件实际上同时包含了调度任务和可见性图省去再跑一轮 STK 的重复计算。把这一层理解为“预计算出来的环境数据”后面写环境类时的数据量就清楚多了。STK 导出的数据只是几何可见性还不等于调度约束。还需要从项目附带的文档里手工整理出任务收益、截止时间、单星存储上限等字段。这是最费精力但最值得做的一步因为后续所有奖励函数和动作掩码都要从这里引用。3. 把 CSV 转成多智能体强化学习环境状态、动作与奖励的落地3.1 调度问题先写成马尔可夫博弈卫星调度如果从单星视角看是序贯决策问题从多颗卫星同时决策看就是马尔可夫博弈。这个实验把每颗卫星当成一个智能体在决策时刻选择一个目标或一个空闲动作。我通常这样定义状态、动作和奖励组成内容说明观测当前时间、剩余存储、可访问目标编号列表每个智能体各一份动作观测某个目标、空闲离散动作空间受窗口掩码限制奖励目标优先级收益 冲突罚 错过窗口罚量纲控制在单步 [-1, 1] 附近把这些写下来不是为了规范漂亮而是为了确定环境接口。项目内设计文档里有一张 UML 类图把环境主体设计成SatelliteSchedEnv。初学者不需要重写整个设计只需要看懂数据流CSV 文件 → 窗口列表 → 每个智能体的动作掩码。3.2 用 PettingZoo 风格的 AEC 环境封装数据在多智能体场景里独立训练多个 DQN 时最省事的接口是 AECAgent Environment Cycle。下面代码演示如何把 CSV 窗口读成每个智能体的观测项import pandas as pd import numpy as np class SatelliteSchedEnv: def __init__(self, csv_path, num_agents4, slot_interval10): df pd.read_csv(csv_path) df[start_slot] (df[start_sec] // slot_interval).astype(int) df[end_slot] (df[end_sec] // slot_interval).astype(int) self.df df self.num_agents num_agents self.time_horizon df[end_slot].max() 1 self.agent_states {i: {storage: 0, current_time: 0} for i in range(num_agents)} def reset(self): self.agent_states {i: {storage: 0, current_time: 0} for i in range(self.num_agents)} return self._get_obs() def _get_obs(self): obs {} for agent_id in range(self.num_agents): usable self.df[ (self.df[sat_id] agent_id) (self.df[start_slot] self.agent_states[agent_id][current_time]) ] obs[agent_id] usable[[target_id, start_slot, end_slot]].values[:20] return obs这里把秒时间切成slot_interval的离散时隙是为了让 DQN 的 Q 网络拿到固定长度观测。如果直接塞变长窗口列表需要额外分组 mask代码复杂度会高很多。obs长度固定取最近 20 个可选窗口超过 20 的窗口在训练时被截断所以紧急度越高的目标应该手动放到列表前面否则容易被网络忽略。字段sat_id不一定在 CSV 里存在。如果原始数据没有可以通过窗口归属反推通常会有一条规则某个访问窗口属于哪颗卫星是由 STK 中的 access 对象决定的。在训练代码里这个字段的主要意义是生成动作掩码避免某颗星去抢不属于自己可视窗口的目标。这一步是环境调试中最容易出错的地方。3.3 奖励函数收益先放大时间罚重奖励设计对多智能体卫星调度的收敛速度影响很大。很多失败的训练问题不是算法不行而是奖励太稀疏或量纲不一致。一个简单可运行的版本如下def compute_reward(self, agent_id, action_target, current_time, conflictFalse): reward 0.0 if action_target is None: reward - 0.01 else: priority self.targets.loc[ self.targets[target_id] action_target, priority ].values[0] w self.df[(self.df[agent_id] agent_id) (self.df[target_id] action_target)].iloc[0] if current_time w[end_sec]: reward - 10.0 elif conflict: reward - 5.0 else: reward priority * 10.0 return reward奖励函数里空闲小惩罚是为了避免智能体一直发呆错过窗口惩罚到 -10是为了压低“瞎试”行为priority * 10是为了让高价值目标在初始策略中占优。实际训练时要观察 reward 分布如果平均每步奖励长期小于 0说明环境难度过高需要先调短 episode 或减少智能体数量。3.4 算法选型独立 DQN 起步再看 CTDE 框架多智能体强化学习没有银弹这个项目我建议先跑独立 DQNI-DQN因为每颗卫星的动作空间相对独立窗口约束天然过滤掉大量无效动作I-DQN 已经能打败构造式贪心启发式。训练后期如果出现多颗星抢同一个目标造成冲突再切到 MAPPO 或 QMIX 这类集中训练、分布执行CTDE算法。算法训练方式优势代价I-DQN每个智能体独立训练实现简单稳定忽略环境非平稳性MAPPO集中 critic分布式 actor更好利用全局信息内存与实现复杂度更大QMIX集中训练并分解 Q 值适合大规模动作空间需要单调性假设项目源码里保留了两条训练入口一条是train_idqn.py一条是train_mappo.py。我的建议是初学者先完整跑通 IDQN确认数据链路和奖励尺度没问题再切换 MAPPO。直接上 MAPPO 的人往往最后分不清是环境 bug 还是算法 bug。4. 训练时序、超参数与多智能体一起卡死的排查4.1 一回合到底有多长时间步不是越多越好在这个实验里一回合对应一段仿真周期比如 24 小时。如果按 10 秒一个时隙切分24 小时就是 8640 步。这个步数对强化学习来说太长完整探索一遍非常慢而且稀疏奖励会让智能体在前几百回合根本收集不到有效样本。我不建议直接走完整时长。常见做法是把场景切成 30 分钟或 60 分钟的子场景先让策略在短场景收敛再逐步拉长时间窗。项目源码里的环境类带了一个truncate_horizon参数默认是 300 个时隙对应大约 50 分钟仿真时间。如果开发前不检查这个参数看到训练曲线每回合都是负分很容易误判为 bug。4.2 能直接落地的超参数组合在lab1.csv这类 200 目标数据集上下面这组参数能稳定收敛同时不会把显存撑爆参数数值备注learning_rate1e-4多智能体环境下太大会震荡gamma0.99调度奖励延迟较长不宜降到 0.9batch_size128每次梯度更新使用样本数buffer_size100000经验池容量epsilon1.0 → 0.05前 100k 步线性退火target_update500DQN 目标网络更新步数num_agents4基准卫星数量这些参数可以原样传入 PyTorch 训练循环也可以对应stable-baselines3里的字典参数。如果使用ray[rllib]多智能体环境的 wrapper 开销较大数据流不透明所以我更倾向于用原生 PyTorch 写一个小训练循环。项目里的train_idqn.py就是这条路径。import torch import numpy as np class ReplayBuffer: def __init__(self, capacity100000): self.buffer [] self.capacity capacity def push(self, transition): self.buffer.append(transition) if len(self.buffer) self.capacity: self.buffer.pop(0) def sample(self, batch_size): idx np.random.choice(len(self.buffer), batch_size, replaceFalse) return [self.buffer[i] for i in idx] buffer ReplayBuffer() for epoch in range(200): obs env.reset() done False while not done: actions {} for agent_id in range(env.num_agents): if np.random.rand() eps: actions[agent_id] np.random.choice(env.get_action_space(agent_id)) else: with torch.no_grad(): q_vals agent_nets[agent_id](torch.FloatTensor(obs[agent_id])) actions[agent_id] int(q_vals.argmax()) next_obs, rewards, dones, _ env.step(actions) for agent_id in range(env.num_agents): buffer.push((obs[agent_id], actions[agent_id], rewards[agent_id], next_obs[agent_id], dones[agent_id])) obs next_obs # 训练更新省略但必须包含 target_net 冻结、定期同步这里所有智能体共用一个经验池可以提升样本利用率前提是各智能体动作空间维度一致。如果卫星配置差异很大建议每个智能体单独开 buffer否则采样时向量长度对不齐训练会直接报错。4.3 “所有智能体都不动”的检查顺序多智能体强化学习最磨人的现象是训练曲线一直平甚至回合回报等于负数。遇到这种情况我按下面的顺序排查检查env.reset()后的第一个观测是否为全零。如果 CSV 第一行就是空窗口说明时间槽没有对齐。检查动作掩码。如果某颗星在绝大多数时间步里没有可选窗口它的策略会倾向永远 idle因为探索不到有效奖励。解决办法是提高空闲惩罚或者初始化时让智能体固定从高优先级目标开始。检查奖励是否被存储超限惩罚抵消。增强文件MRL_data_400_1000_augmented.csv窗口密度高多星很容易同时选中同一目标冲突罚项会压制收益让策略趋于保守。打印一个完整 episode 的剪影例如T100, agent0 观测到第 3 个目标窗口执行动作 2reward0.7。加一行if step % 50 0: logger.info(...)就能看清策略是否在按预期推进。4.4 在 Windows 和 Linux 下跑这套实验的坑很多同学第一次跑是在 Windows 上STK 的许可和 Python 库的通信绕不开一个坑COM 注册。我的经验是启动训练前先做一次依赖检查python -c from stk11 import STK11; print(STK11()) python -c import pandas, numpy, torch; print(deps ok)第二行确认 Python 侧依赖完整。如果缺失stk11可以执行pip install stk11但要注意库本身不会代替 STK 安装它只负责连接。只训练不生成新数据时不需要启动 STK GUI 或持有许可要复现全新场景时STK 环境和许可就必须配置好。项目提供的运行方式也是分离的训练入口不依赖仿真器数据导出才依赖 STK。5. 训练完成之后用随机/贪婪基线和扩增数据验证策略5.1 不看训练曲线先看调度收益训练收敛不代表调度结果可用。我会把策略输出的动作序列还原成一张调度甘特图检查每个目标是否在窗口内完成。项目里给出的评测脚本通常生成类似下表的对比结果方法完成任务数平均优先级收益冲突次数随机选择86120.537贪心按优先级132214.312训练后的 I-DQN158268.73多智能体强化学习在这个场景里的主要优势不是单纯提高完成任务数量而是冲突次数明显下降。多颗卫星在共享目标竞争信息后学会了在窗口重叠时主动退出把机会让给时间余量更大的星。这种协同行为在贪心策略里很难手工写出来也是 MARL 区别于单智能体调度的核心价值。5.2 验证时请带上增强样本项目里有lab1_augment.csv和MRL_data_400_1000_augmented.csv这类文件。增强数据不是拿来替代训练集的它更像一种隐式正则。我的验证命令如下python evaluate.py --policy_dir ./checkpoints/exp_03 \ --csv_path lab1_augment.csv \ --episodes 20 \ --output metrics.json这里--policy_dir是训练产生的 checkpoint 目录--csv_path指定评估数据测试时不要把训练集和测试集混在一起--episodes我一般设 20 以上太少时一次随机种子变化就让指标波动 10% 以上--output只是汇总指标的位置。如果增强数据上表现下降明显说明策略过拟合到原始窗口分布需要回主训练集里加入 5% 以内的时间抖动再训。5.3 把实验接进更大场景前的三个调整如果想把实验扩展成 10 星以上不要着急堆算法先处理以下三点。第一观测里不要把窗口编号直接裸用改成目标经纬度、剩余窗口时长、目标收益三个特征否则智能体对不同数量的目标泛化能力很差。第二把训练中的存储约束从硬限制改成软罚硬限制会让某些星策略极端保守。第三在环境层加入冲突保护同一时间槽内只允许一颗星对一个目标发起动作这样神经网络结构完全不用改冲突率直接下降。5.4 最后一招把时间槽从 10 秒改成 30 秒如果训练一直慢一个最有效的办法是把环境里的slot_interval从 10 秒调到 30 秒。这样时间步数从 8640 降到 2880动作空间不变训练速度提升接近三倍。代价是窗口边界精度下降但只要目标成像时间普遍小于 30 秒结果几乎不受影响。这个参数在源码里属于隐藏参数很多人只盯着学习率调忘了时间粒度本身就是最关键的一层抽象。本文还有配套的精品资源点击获取