跳转至

Sim2Real

本页介绍将 LAV2 从“仅仿真验证”推进到软件在环与贴近实机部署工作流时,需要使用的中间件与部署侧软件栈。在当前仓库中,这条链路的入口位于 sim2real/ 之下。

范围

本页关注如何组装并运行部署侧软件栈,包括:

  • 模拟器
  • 飞控
  • ROS 工作区
  • 地面站
  • LAV2 中间件与策略节点

sim2real/ 下的 ROS 工作区与主 lav2/ 包是彼此独立的。这种分离是有意的:lav2/ 包承载核心仿真、控制与训练侧逻辑,而 ROS 工作区则承载中间件与面向部署的集成层。

flowchart LR
  Sim[模拟器]
  PX4[PX4 SITL]
  MAVROS[MAVROS 与 ROS 工作区]
  Node[LAV2 部署节点]
  Policy[策略或控制器]
  Mixer[Mixer 与映射]
  GCS[地面站]

  Sim <--> PX4
  PX4 <--> MAVROS
  MAVROS --> Node
  Node --> Policy
  Policy --> Mixer
  Mixer --> Node
  Node --> PX4
  GCS <--> PX4

SITL 栈

概览

当 sim-to-real 验证进入 SITL 阶段时,整套软件通常会扩展为数个独立演化的层。对当前工作流来说,完整栈通常包括:

  1. 模拟器:MuJoCo、Isaac Sim、Gazebo 或其它物理后端
  2. 飞控:PX4 以及 LAV2 一侧使用的控制器或策略
  3. ROS:中间件、MAVROS 以及其它机器人软件包
  4. 地面站:通常是 QGroundControl

ROS 往往还会继续拉入定位、视觉、激光雷达等附加包,因此明确编排这些层之间的关系很重要。

推荐安装路径

当前推荐路径是 Linux 环境下的 ROS 2、PX4 与基于 Pegasus Simulator 的 Isaac Sim 后端。结合现有 SITL 记录,实际的目标组合是:

  • Ubuntu 22.04 + ROS 2 Humble,或 Ubuntu 24.04 + ROS 2 Jazzy
  • 已安装并可运行 SITL 的 PX4
  • 本地可用的 Isaac Sim
  • 安装在其上的 Pegasus Simulator

就当前仓库状态而言,Pegasus Simulator 是 PX4 SITL 工作流的主要后端。原因不是它是唯一选择,而是当前 LAV2 的仿真到部署链路就是围绕它组织起来的。

组件安装

可以把安装工作视作四组相关组件:

  • 模拟器安装与配置
  • PX4 安装与 SITL 就绪
  • ROS 2 安装与 Python 环境准备
  • 地面站可用性

按这种分组理解是有帮助的,因为很多 SITL 问题其实来自这些层之间的版本漂移,而不是某个单独包的错误。

模拟器与飞控之间的桥接是整个栈里变动最大的部分之一。当前最相关的参考资料包括 Pegasus SimulatorPX4-Isaac-Sim

对 LAV2 而言,当前仍然更推荐手工搭建环境,而不是优先采用容器化方案。容器确实可以减少环境漂移,但在当前这种模拟器端需要较多自定义调整的工作流中,手工环境仍然更容易迭代。如果你需要参考容器方案,可以查看 nvidia_isaac-sim_ros2_docker 以及 Pegasus Simulator Docker PR for Isaac Sim 4.5.0

Pegasus Simulator 安装说明

Pegasus Simulator 应当按照其官方安装指南进行安装,但当前 LAV2 工作流还有一些额外约束。基础流程仍应遵循 Pegasus Simulator installation guide

如果你的 Isaac Sim 是通过 Python 或 uv / pip 风格的方式安装,而不是通过上游 Pegasus 文档默认的预构建目录结构安装,就需要额外调整 Pegasus 配置脚本,使其中的 ISAACSIM_PATHISAACSIM_PYTHON 等路径指向本地真实的 Isaac Sim 安装位置。

为了与当前 LAV2 平台配置保持一致,现有工作记录也默认使用 LAV2 维护的 Pegasus fork,而不是直接使用上游默认版本。

之后还需要调整诸如 PX4 路径、仿真步长以及 LAV2 模型路径等配置,例如:

这些设置正是模拟器后端与 LAV2 平台描述交汇的边界。

如何启动模拟器侧

对于 Pegasus Simulator,主要有两种运行方式。

Pegasus 可以通过 Isaac Sim 图形界面运行,这种方式便于手动检查。对应说明见 extension mode tutorial

Pegasus 也可以通过独立 Python 运行。这是 LAV2 SITL 当前更推荐的路径,因为更容易脚本化、复现与开发期修改。对应说明见 standalone application tutorial,当前面向 LAV2 的示例位于维护分支中的 examples/100_sitl_lav2.py

本页最重要的结论是:模拟器必须以一种足够稳定的形式运行,使得 PX4、ROS 与 LAV2 中间件都能可靠接入。当前最符合这一要求的是 standalone Python 路径。

ROS 工作区

构建环境

当模拟器侧可用之后,下一步就是构建并运行 sim2real/ros2_ws 下的 ROS 工作区。

这里有一个关键环境约束:ROS 工作区中的 MAVROS 集成依赖系统级 ROS Python 环境。这意味着用于该工作区的虚拟环境应基于 system Python 创建,而不是基于一个完全隔离、看不到 ROS 包的 Python 环境。

现有工作流记录推荐使用基于 system Python 的虚拟环境,例如:

uv venv --no-managed-python --system-site-packages

随后在 sim2real/ros2_ws 内,使用该环境对应的 Python 解释器构建工作区,例如通过 python -m colcon build

如果生成的 install tree 仍然指向了错误的解释器,一个实际可行的办法是手工修改 install/ 目录下生成的 Python 入口脚本 shebang,使其指向目标虚拟环境中的 Python 可执行文件。

之所以需要如此谨慎,是因为 ROS 2 在很多工作流中仍不能完美支持任意形式的 Python 虚拟环境布局,因此工作区的构建与运行环境必须显式处理。

ROS 工作区环境细节

如果 ROS 工作区能够构建,但安装后的入口脚本仍然使用了错误的解释器,就修改 sim2real/ros2_ws/install/ 下生成脚本的 shebang,让它们指向你期望的虚拟环境 Python。

LAV2 MAVROS 入口

ROS 工作区完成构建后,LAV2 一侧主要的 launch 入口是:

  • ros2 launch lav2_mavros base_play.launch.py --show-args

这个 launch 文件是当前部署路径的实际控制中枢:策略模型、中间件 topic、PX4 连接和运行模式选择,都是在这里被装配成一个统一 ROS launch 入口的。

对应的 base_play_node.py 实现则负责把以下几部分串起来:

  • MAVROS odometry 与状态输入
  • 目标生成或外部目标订阅
  • 策略推理
  • 控制器侧命令映射
  • 面向 PX4 的 setpoint 发布

其中最重要的结构点,不在于 launch 参数细节,而在于节点围绕飞行模式与解锁状态实现了一套小型有限状态机。正是这套逻辑,让部署节点能够区分“等待”、“保持”和“激活 Offboard”等阶段,而不是始终盲目发布命令。

stateDiagram-v2
  state "等待状态" as WaitingForState
  state "保持" as Hold
  state "Offboard 激活" as ActiveOffboard
  [*] --> WaitingForState
  WaitingForState --> Hold: 里程计可用
  Hold --> ActiveOffboard: 已解锁且 Offboard 已就绪
  ActiveOffboard --> Hold: 模式切换或解除解锁
  Hold --> WaitingForState: 状态丢失
  WaitingForState --> [*]

运行时流程

sequenceDiagram
  autonumber
  participant Sim as 模拟器
  participant PX4 as PX4 SITL
  participant MAVROS as MAVROS
  participant Node as LAV2 部署节点
  participant Policy as 策略或控制器
  participant Mixer as Mixer 与映射
  participant GCS as 地面站

  Sim->>PX4: 推进 SITL 物理与传感器桥接
  PX4-->>MAVROS: 发布飞行器状态
  MAVROS-->>Node: 转发里程计与模式状态
  Node->>Policy: 构造观测并执行推理
  Policy-->>Node: 返回策略空间中的命令
  Node->>Mixer: 转换并归一化命令
  Mixer-->>Node: 返回面向 PX4 的设定值
  Node-->>PX4: 发布 Offboard 设定值
  GCS->>PX4: 发送解锁或模式切换
  PX4-->>GCS: 回报状态与日志流

启动顺序

从高层看,SITL 运行顺序通常是:

  1. 启动模拟器后端
  2. 启动 PX4 SITL,并确认模拟器与飞控连接正常
  3. 通过 base_play.launch.py 启动 MAVROS 与 LAV2 ROS 节点
  4. 连接地面站
  5. 在其余栈健康的前提下切入 Offboard
  6. 执行策略控制飞行
  7. 测试结束后切回非 Offboard 模式并检查日志

这个顺序之所以重要,是因为只有当模拟器、PX4、ROS 与地面站对当前飞行状态和命令流的理解一致时,部署侧软件栈才真正可用。

地面站与验证

由于 sim-to-real 验证应尽量贴近真实部署条件,因此工作流也默认将地面站(例如 QGroundControl)纳入闭环。

在当前工作流中,当 MAVROS 与模拟器启动后,应:

  • 将地面站连接到 PX4
  • 只在其余软件栈健康时解锁并切入 Offboard
  • 通过 ROS 节点运行 LAV2 策略
  • 飞行后切回非 Offboard 模式
  • 导出飞控日志用于后续分析

这些日志可以在本地检查,也可以上传到诸如 PX4 Flight Review 这类分析工具中进行事后验证。

PX4 控制模式对齐

为什么对齐重要

SITL 可以验证通信链路与编排是否正确,但要实现可用部署,还必须保证整条控制路径上的命令语义一致。策略、控制器、Mixer、ROS 中间件与 PX4 至少要在三个问题上达成一致:

  • 当前命令的控制量究竟是什么
  • 该控制量处于哪个坐标系中
  • 到达 PX4 之前,数值范围是如何被归一化的

任何一个假设发生漂移,都可能让一个在本地回放中看似稳定的策略,在 Offboard 控制下变得不可用。

面向 PX4 的命令族

对于当前 LAV2 控制栈,最有用的 PX4 命令族包括:

  • cmd_motor_thrusts
  • cmd_ctbm
  • cmd_ctbr
  • cmd_ctatt
  • cmd_acc
  • cmd_vel
  • cmd_pos

较低层的模式与 LAV2 控制栈更直接对应,而较高层模式则允许 PX4 在内部闭合更多控制环。

cmd_motor_thrusts 最接近执行器侧控制。它要求在发布前先做归一化,而物理上有意义的推力范围必须被压缩进 PX4 所期望的归一化区间中。

cmd_ctbmcmd_ctbrcmd_ctatt 则更适合那些已经在“总推力 + 旋转控制”层面工作的 LAV2 策略或控制器。在这些模式下,总推力仍需要映射到 PX4 所期待的归一化范围,而姿态相关命令也必须遵守 PX4 的坐标系约定。

cmd_acccmd_velcmd_pos 则工作在更高层抽象上。当外环仍放在 PX4 之外,但又希望与 PX4 所采用的 NED 风格加速度、速度、位置目标语义保持一致时,这些模式会更有用。

归一化与映射

最关键的归一化问题是总推力。在 LAV2 中,Mixer 不仅负责把横滚、俯仰、偏航与总推力需求分配到各旋翼,还负责计算与 PX4 风格命令接口保持一致所需的归一化推力量。

因此,部署路径应当尽量保持清晰的职责分层:

  • 控制器或策略先在 LAV2 内部表示下计算期望命令
  • mixer 与 mapping 层将其转换为面向 PX4 的归一化表示
  • ROS 节点最终发布 PX4 可接受的 setpoint

这样可以让命令语义保持清晰,而不是把归一化逻辑散落在多个层中。

对于 cmd_ctatt 这类姿态相关接口,姿态本身也必须先转换成 PX4 所期望的表示形式,桥接层在发布前还需要仔细处理坐标系与通道顺序。

对齐应当放在哪一层

PX4 控制模式对齐通常有两个实际落点。

第一种做法是在策略动作空间中直接对齐。此时,学习得到的策略本身就会输出 PX4 所期望的、在归一化与坐标系上已经一致的命令。优点是 ROS 节点中的翻译工作更少,整条控制链更容易端到端检查;代价是策略会更强地耦合到 PX4 接口。

第二种做法是让策略继续输出训练时最方便使用的表示,然后在 ROS bridge 节点中完成对齐。这样训练侧与部署侧的解耦会更好,但中间件层就必须承担更严格、更稳定的命令翻译责任。

在当前 LAV2 结构中,更实际的分工通常是:

  • 保持策略输出与训练环境兼容
  • 复用 lav2.controller.mappingMixer 进行命令解释与归一化
  • 在 ROS 节点中完成最终面向 PX4 控制模式的发布

这种切分方式既能让部署节点保持足够薄,也能在系统边界上完成 PX4 特定的对齐工作。

Offboard FSM 与发布策略

base_play_node.py 中,真正重要的实现细节不是 launch 参数列表,而是围绕 Offboard 激活所设计的有限状态机。

节点不应该表现为一个无状态的命令转发器。它需要显式跟踪 odometry 是否可用、飞行器是否已经进入适合发布命令的阶段,以及何时才可以安全地进入主动 Offboard 控制。实践中,这通常会将运行时拆成如下阶段:

  1. 等待有效状态反馈
  2. Offboard 接管前的保持 / 待命
  3. 飞行器已解锁且 ready 后的主动命令发布

也正是在这个状态机边界上,命令对齐才真正从“配置假设”变成“运行时行为”。在平台进入主动阶段之前,节点不应把策略输出当作有效飞行命令;而一旦 Offboard 激活,该节点就必须持续地以当前选定的 PX4 控制模式发布 setpoint。

实际验证重点

在 SITL 或实机中验证本节内容时,最值得优先检查的是:

  • 当前选用的 PX4 控制模式,是否与策略或控制器输出的命令表示一致
  • 总推力与旋转相关项是否只被归一化了一次
  • 从状态估计到最终 setpoint 发布的坐标系是否始终一致
  • Offboard FSM 是否能够干净地进入与退出激活阶段

如果这四点保持一致,剩余问题通常更偏向控制器调参与参数对齐,而不是接口语义失配。

API 交叉引用