所有项目

2025.08 - 2026.06 / 独立完成

无人机交通管理系统

从多智能体强化学习训练到 ONNX 推理服务与 3D 可视化,探索以安全空域约束指令管理无人机交通的工程化交付。

PythonPyTorchONNX RuntimeMQTTRedisFastAPIReactThree.jsMapLibre

这个项目面向城市低空场景中的无人机交通管理:使用多智能体强化学习计算安全空域约束,将模型导出为可独立运行的推理服务,并分别提供训练资料回放和地图上的运行时可视化。四个部分共用无人机位置、速度、目标点与安全空间等概念,但面向的资料来源和使用场景不同,因此分为离线与在线两条链路实现。

离线:训练与轨迹可视化

离线链路关注策略如何被训练与检查。训练过程会定期以当前模型执行一个评估 episode,记录逐步状态并输出轨迹 JSON;checkpoint 则用于导出 ONNX。这让回放资料能对应到明确的模型版本与场景条件。

flowchart LR
  subgraph Train[训练]
    direction TB
    TrainEnv[多机 3D 仿真环境]
    Mappo[MAPPO 训练]
    TrainEnv <-->|观测、动作与奖励| Mappo
  end
  Mappo --> Ckpt[模型 checkpoint]
  Ckpt --> Export[导出 ONNX actor]

  subgraph PeriodicEval[定期评估与回放]
    direction TB
    Episode[评估 episode]
    Episode --> Log[轨迹 JSON]
    Log --> Replay[3D 轨迹回放]
  end
  Mappo -. 在仿真环境中定期评估 .-> Episode

多机仿真与安全约束

训练环境包含多架无人机、建筑物与目标点。每架无人机根据自身速度、目标方向与尺寸,以及邻近无人机和建筑物的空间关系作出三个轴向的离散位移决策;MAPPO 使用共享 actor 产生动作,并在训练阶段以全局资讯估计价值。

策略提出候选位移后,系统会计算对应的候选安全空间。若该空间与其他无人机或建筑物相交,safety shield 会沿重叠较小的轴向收缩空间,再将原始位移裁切到可行范围。每一步都记录实际位移与候选位移的比例,让奖励同时考虑朝目标前进、到达目标,以及动作被安全约束修正的程度。

将安全性完全交给奖励函数,实现较简单,但训练早期容易出现大量碰撞,也无法保证每次输出都可执行;保留独立的 shield 则增加了策略输出与实际动作之间的差异,但能确保最终安全空间至少包含机体 bbox,且当前位置与建议航点都落在其中。

训练结果的 3D 可视化

训练指标只能反映整体趋势,难以说明某次避让为何成功或失败。因此我使用 Three.js 制作 Web 可视化工具,将训练过程与结果放入可交互的 3D 场景中:可同时查看无人机、目标点、人员、建筑物和 safety shield 产生的安全空间。

可视化让策略行为不再只是一组奖励数值。例如,当安全空间反覆被压缩、无人机在建筑物旁停滞,或多机接近时出现不合理的绕行,都能回到具体场景中判断是观测设计、奖励权重、动作范围还是 shield 约束造成,再针对性调整训练参数与算法设计。工具与训练程序分离后,也可直接载入保存的资料反覆检查同一段行为,而不需要重新训练。

在线:推理与地图可视化

在线链路将导出的 actor 接入持续更新的无人机资料。无人机先经 gRPC ↔ MQTT 桥接服务接入消息通道;资料处理服务接收遥测、保存各架无人机的最新状态并触发安全空间计算;推理服务返回安全空域约束指令后,资料处理服务再向无人机下发。另一个只读的 HTTP 接口把状态提供给地图可视化使用,避免查询操作干扰推理工作。

下图概括在线服务的部署拓扑。

flowchart TB
  UAV[UAV 无人机]
  Bridge[gRPC ↔ MQTT 桥接服务]
  MQTT[MQTT Broker]
  CEDS[资料处理服务]
  Redis[(Redis)]
  Compute[安全空域计算服务]
  API[UAV Status API]
  Map[地图可视化]

  UAV <-->|gRPC| Bridge
  Bridge <-->|MQTT| MQTT
  MQTT <--> CEDS
  CEDS <--> Redis
  MQTT <--> Compute
  API -->|只读| Redis
  Map -->|HTTP| API

模型推理与指令下发

训练使用 PyTorch Lightning,部署时只将 deterministic actor 导出为 ONNX。服务端以 ONNX Runtime 载入模型,并将 intra-op 和 inter-op thread 都限制为一个,避免推理 worker 与其他服务争抢 CPU 资源。这让部署端不需要携带完整的 PyTorch 训练栈,也将模型接口固定为「观测输入、离散动作输出」。

代价是训练与部署之间多了一条需要持续验证的边界:观测栏位的顺序、离散动作到位移的映射、机体尺寸、速度上限与座标缩放都必须保持一致。推理服务会将经纬度、高度和 NED 速度转为模型使用的局部 XYZ 座标并按比例缩放,完成推理后再转回地理座标,输出包含安全空间 bbox 与建议航点的安全空域约束指令。

在线资料以 MQTT 传递,Redis 保存最新状态而非长期轨迹。计算 worker 接到触发后读取所有无人机状态,计算指定无人机的安全空间;安全空域约束指令经资料处理服务保存并下发。指令带有产生时间与五秒有效期,让下游可以辨识过期结果。这种事件驱动方式避免让地图查询同步阻塞模型推理,但也需要处理遥测资料过期、请求重复和不同无人机状态不同步等时序问题。

下图呈现一次计算请求从遥测上报到安全空域约束指令下发的顺序。

sequenceDiagram
  participant U as UAV 无人机
  participant M as MQTT Broker
  participant C as 资料处理服务
  participant R as Redis
  participant S as 安全空间计算服务

  U->>M: 上报遥测资料
  M->>C: 传递遥测资料
  C->>R: 保存最新状态
  C->>M: 发布计算请求
  M->>S: 传递计算请求
  S->>R: 读取所有 UAV 状态
  R-->>S: 返回遥测资料
  S->>S: ONNX 推理与安全空间计算
  S->>M: 发布安全空域约束指令
  M->>C: 传递约束指令
  C->>R: 保存约束指令
  C->>M: 发布下行指令
  M->>U: 下发安全空域约束指令

训练和在线服务的 safety shield 有意采用不同的冲突处理方式。训练环境可对相交的两个候选空间做双边收缩;在线服务每次只处理一架无人机的请求,因此将其他已锁定的安全空间视为固定约束,只收缩当前请求者的候选空间。空间锁能避免后到的请求覆盖已分配区域,但也需要设计过期与重算策略,这是批量仿真和事件驱动服务之间的编排差异。

在地图上观察当前状态

地图可视化使用 MapLibre 显示底图与视角,将 OSM 建筑资料转为 GeoJSON 后以 fill-extrusion 生成白模:优先使用建筑高度,没有高度时根据楼层数估算,再回退至预设高度。Three.js 场景叠加在地图上,每秒从只读 HTTP 接口读取无人机资料,展示无人机模型、目标点、目标连线与半透明安全空间。

这一部分的关键在于座标转换。推理服务使用局部 XYZ,外部资料使用 WGS-84 纬度、经度与海拔;地图以固定原点将经纬度换算为相对米制 x/z 偏移,以海拔作 y 轴,才让无人机模型、安全空间与地图建筑出现在一致的视觉座标中。

OSM 建筑物目前用于运行时空间理解与结果检查,尚未作为推理服务的碰撞 bbox 输入。将真实地图建筑转换为与训练环境一致的几何约束,并处理资料精度与更新,会是后续需要完成的资料接入工作;因此本文不将白模展示描述为已完成的真实建筑避障。

回顾

这个项目将训练策略、模型部署和空间可视化分开实现,再以明确的数据结构将它们连接起来。离线链路用回放检查策略行为,在线链路则将模型输出转为带有有效期和空间约束的安全空域约束指令,并在地图上呈现当前状态。

后续的优先方向是将真实建筑几何接入推理约束、建立遥测过期与请求去重机制,以及以固定场景和可复现数据持续比较不同策略的安全性与计算成本。