域随机化
本页介绍 LAV2 仿真栈内部的域随机化。这里关注的是载具内在参数的随机化,也就是在同一个任务定义下,让不同环境实例持有不同但仍然物理合理的载具参数、执行器响应和控制器配置。
在 LAV2 中,这类随机化更贴近平台模型本身。它讨论的核心不是任务目标如何变化,而是同一类载具在仿真中可以呈现出哪些合理的参数差异。如果你想看这套做法在迁移链路中的作用,可以继续阅读 Real2Sim;本页则专门说明它在仿真内部是如何表达和执行的。
flowchart TD
A[标称平台定义] --> B[VehicleParams]
B --> C[控制器侧参数]
B --> D[执行器与动力学侧参数]
C --> E[Torch 控制器]
D --> F[Mixer 与 RotorDynamics]
E --> G[任务后端随机化入口]
F --> G
G --> H[按环境实例采样后的运行时状态]
H --> I[并行训练环境]
设计边界
LAV2 将平台的标称描述集中保留在 VehicleParams 中。域随机化建立在这份标称模型之上,运行时只对其中被允许的参数子集施加扰动。这样一来,仓库始终保留一个统一的质量、惯量、执行器常数、时序假设和 PX4 对齐参数来源,而训练后端只负责在执行阶段生成不同的参数样本。
当前实现遵循一套比较一致的模式。运行时组件先保留自己所拥有参数的一份标称副本,然后在需要随机化时按环境实例采样,把采样结果写回实际参与并行计算的张量状态,并刷新所有依赖这些参数的缓存量。正因为如此,这套设计才能和大规模并行后端兼容;随机化在这里体现为对运行时状态的受控改写,而不是一套脱离主模型定义的平行配置。
因此,LAV2 把域随机化视为仿真组件自身的性质,而不是任务层单独施加的一层“外部噪声”。真正关键的问题不是某个任务能否注入随机性,而是被扰动的控制器或动力学模块在参数变化之后,是否仍然能够保持清晰、稳定并可复用的接口。这也是为什么当前实现主要围绕控制器、mixer 和旋翼动力学展开,而没有完全隐藏在任务代码内部。
参数作用面
第一类参数作用面在控制器侧。在 lav2.controller.torch.pid 中,批量 FlightController 通过 FlightController.randomize 支持在重置时重采样每个环境实例上的 PID 增益和控制器限幅。从建模意义上说,这和动力学参数随机化并不相同。它改变的是同一组目标与状态信号如何被解释成控制指令,因此更接近“内环控制配置存在偏差”的不确定性,而不仅仅是“平台物理参数存在偏差”。
第二类参数作用面在执行器分配与推力映射路径上。lav2.controller.torch.mixer 中的 Mixer 提供了 Mixer.randomize,可以扰动控制分配和归一化节流解释所依赖的参数。由于 mixer 会在采样后重建分配矩阵和与推力相关的缓存量,这种变化并不是简单地在输出端乘一个系数,而是直接改变了期望 wrench 被映射为旋翼命令的方式。
第三类参数作用面在旋翼动力学本体。lav2.dynamics.torch.rotor 中的 RotorDynamics 通过 RotorDynamics.randomize 支持扰动推力与扭矩系数、速率限制、电机转动惯量以及升速和降速时间常数。这一点与某些只对最终推力做乘性扰动的简化方法有本质区别。在 LAV2 中,被随机化的不只是“最后输出多大力”,还包括执行器本身如何响应输入命令,而这通常更贴近真实推进链路在仿真到实物迁移中暴露出的误差来源。
这三类参数面共同构成了 LAV2 当前已经落地的载具内在参数随机化范围。与此同时,像质量、惯量、质心偏移这样的机体参数,目前仍主要体现为明确的扩展方向,还没有在所有后端中统一接入为同一层运行时机制。
运行时拓扑
LAV2 的运行时拓扑是有意分层的。标称车辆定义仍然集中保留,但真正参与执行的采样参数则存放在各个并行运行时对象内部。正是这种分工,让仓库既可以维持统一的平台词汇,又能让不同环境实例在任务周期级别拥有彼此不同的参数样本。
flowchart LR
A[VehicleParams] --> B[FlightController]
A --> C[Mixer]
A --> D[RotorDynamics]
B --> E[动作项]
C --> E
D --> E
E --> F[事件项或动作项]
F --> G[控制器随机化]
F --> H[Mixer 随机化]
F --> I[旋翼随机化]
G --> J[按环境实例存储的并行张量]
H --> J
I --> J
这里最重要的一点是,编排层并不会重新发明一套参数语言。后端只负责决定“什么时候随机化”和“对哪些环境实例随机化”。至于采样结果如何表示、哪些缓存需要刷新、后续计算如何继承这些新值,仍然由控制器和动力学模块自己负责。这种分离让后端代码保持较薄,同时避免把任务语义与组件内部的参数管理耦合在一起。
当前已经接入的主要入口是重置阶段的随机化,这适合表达“同一轮任务中平台参数固定、不同轮次之间参数变化”的问题设定。与此同时,更复杂的任务同样可能需要时变随机化,例如电机故障下的容错控制、模型摄动下的自适应控制,或者其它在任务执行过程中逐步改变平台条件的场景。在这种情况下,随机化入口就不应局限于重置事件,而可以进一步接入动作项或其它更高频的运行时更新路径。
后端接入
LAV2 中面向训练的后端都遵循这套总体逻辑,而主要入口首先是事件项。在 Isaac Lab 路径中,lav2.tasks.isaaclab.LAV2_base.mdp.events 提供了显式的事件辅助函数,它们会找到对应的动作项,再取出挂在其上的运行时组件,最后调用组件自己的随机化接口。默认接线则在 lav2.tasks.isaaclab.LAV2_base.LAV2_env_cfg 中完成,其中重置事件给出了控制器、mixer 和 rotor 扰动的实际参数范围。
mjlab 路径与之高度相似。lav2.tasks.mjlab.LAV2_base.mdp.events 提供了同类事件层封装,而 lav2.tasks.mjlab.LAV2_base.LAV2_env_cfg 则把这些事件组装进 manager-based 环境配置。这样的结构相似性很有价值,因为即使底层环境框架不同,开发者仍然可以用同一套心智模型去理解域随机化的注入路径。
Genesis Forge 目前采用的路线略有不同。它并没有完全复用与 Isaac Lab、mjlab 对应的事件模块结构,而是在动作侧环境装配中直接触发随机化。lav2.tasks.genesis_forge.LAV2_base.mdp.actions 在这一层直接调用控制器、mixer 和 rotor 的随机化方法。这也说明在需要更频繁更新的场景里,随机化同样可以从动作项接入,而不必局限在事件项内部。
把这几条路径放在一起看,设计意图其实是清晰的。LAV2 不希望把域随机化做成某个后端特有的技巧,而是希望由后端决定注入位置、由共享控制与动力学组件定义随机化语义。正因为如此,这套实现才更容易在 Isaac Lab、mjlab 和未来后端之间迁移。
建模含义
当前实现已经比最简单的“在推力输出后乘一个噪声系数”更接近真实系统建模。一旦执行器时间常数、推力系数、限幅和控制器增益都可以在不同环境实例之间独立变化,策略面对的就不再是一套被冻结的内环与执行器,而是一族在物理和控制意义上都彼此相近、但又存在差异的闭环系统。
不过,也需要明确它仍然不是一套完全通用的域随机化引擎。仓库目前还没有为所有参数类别统一抽象出 scale、add、abs 这样的操作模型,也没有提供一套能够跨机体、执行器和控制器对象统一选取目标的参数语言。这些都是自然的后续开发方向,可以逐步把当前已经可用的随机化层推进成更完整的通用框架。
同时,控制器侧不确定性和动力学侧不确定性在概念上必须保持区分。控制器增益随机化主要对应内环配置漂移,mixer 与 rotor 参数随机化主要对应执行器和被控对象失配。二者可以同时使用,但它们回答的是不同类型的失效模式,因此无论在代码还是文档中,都不应被混成同一个概念。
后续扩展
最自然的扩展方向,是把机体侧随机化真正补齐。质量、惯量结构、质心偏移,以及履带侧执行器参数,都适合沿用 rotor 与控制器目前已经建立的运行时模式继续推进。只有当这些量也被纳入同一类运行时抽象时,飞行构型与地面构型的载具内在参数随机化才会更加对称。
另一个重要方向是增强采样参数的可观测性。目前各个运行时模块会直接改写内部并行张量,但整个系统还没有提供统一的内部参数缓冲区或随机化参数记录结构,用来做日志、回放或有条件地暴露给策略。这类显式记录能力会让训练失败区间分析、训练与评估分布对比,以及参数组合导致的策略退化诊断都更直接。
这些方向建立在现有结构之上继续展开。现阶段的设计已经建立了比较清晰的所有权关系:标称参数保持集中,运行时组件拥有采样后的执行状态,任务后端通过事件项或动作项触发随机化。
相关页面
API 交叉引用
- 车辆参数: VehicleParams
- Torch PID 模块: lav2.controller.torch.pid
- 飞行控制器: FlightController
- 飞行控制器随机化: FlightController.randomize
- Torch 混控器模块: lav2.controller.torch.mixer
- 混控器: Mixer
- 混控器随机化: Mixer.randomize
- Torch 旋翼动力学模块: lav2.dynamics.torch.rotor
- 旋翼动力学: RotorDynamics
- 旋翼随机化: RotorDynamics.randomize
- Isaac Lab 基础环境配置: lav2.tasks.isaaclab.LAV2_base.LAV2_env_cfg
- Isaac Lab 事件模块: lav2.tasks.isaaclab.LAV2_base.mdp.events
- mjlab 基础环境配置: lav2.tasks.mjlab.LAV2_base.LAV2_env_cfg
- mjlab 事件模块: lav2.tasks.mjlab.LAV2_base.mdp.events
- Genesis Forge 动作模块: lav2.tasks.genesis_forge.LAV2_base.mdp.actions