动力学
LAV2 面向陆空两栖与飞行平台提供统一的动力学参数与接口框架。当前实现将动力学组织为一个共享参数对象,以及若干与执行形式相关的模型实现,便于本地 MuJoCo runner 与 GPU / 任务后端围绕同一套物理常数与接口假设保持一致。这里的模块拆分主要服务于实现组织与数值计算,并不意味着旋翼与接触动力学在物理上已经被完全解耦;例如升力对接触力的影响等耦合关系,当前实现尚未进一步建模。
机体参数
VehicleParams 是平台最核心的参数接口。它将机体描述划分为:
- 环境参数
- 机身参数
- 螺旋桨参数
- 履带参数
- 与 PX4 对齐的参数
其中一部分值来自直接配置,另一部分则会在 __post_init__ 中派生出来,例如推力与反扭矩系数、默认初始 RPM,以及悬停油门相关量。因此在实际使用中,VehicleParams 不仅定义平台的原始常数,也定义了下游动力学与控制器真正消费的量。
当你需要质量、惯量、仿真步长、旋翼系数或履带几何参数时,应优先从 VehicleParams 出发,而不是把这些字面量散落在各个模块中。
在模块结构层面,具体动力学实现统一组织在 DynamicsBase 这一公共接口之下,因此旋翼、履带、以及并行后端版本都遵循相同的生命周期与参数归属模式。
时间尺度:sim_dt 与 step_dt
一旦同一套动力学被放到本地单速率模拟器之外使用,这两个时间参数就会变得非常重要。sim_dt 表示执行器与物理积分步长,而 step_dt 表示控制器更新步长。
在带 decimation 的框架中,例如 Isaac Lab,这两者往往并不相同,因为物理引擎的步进频率通常高于控制策略或命令更新频率。当后续引入高层与低层策略异步运行时,这种区分也同样重要。把这两个字段统一保存在 VehicleParams 中,可以避免将频率假设隐含在单个模块内部。
旋翼动力学
RotorDynamics 将每个旋翼的 RPM 命令转换为一个 8 维输出向量:
- 前 4 个量:推力
- 后 4 个量:反扭矩
它的更新规则遵循标准的旋翼模型结构。命令转速不会被瞬时施加,而是先经过滤波与速率限制;随后根据旋翼速度与电机角加速度计算推力与反扭矩:
其中,\(\omega_i\) 表示旋翼转速,\(f_i\) 表示推力,\(\tau_i\) 表示反扭矩,\(c_T\) 与\(c_M\) 分别是推力与扭矩系数,\(J_m\) 是电机转动惯量,\(d_i \in \{+1, -1\}\) 用于编码旋翼旋向。实际滤波更新中,还会使用独立的上升/下降时间常数,并对 RPM 变化率做显式裁剪。
要让这个模型真正有意义,它必须和真实平台参数保持一致,尤其包括:
- 初始旋翼转速
- 电机惯量与时间常数
- 推力与扭矩系数
- 最大 RPM
- 旋翼旋向
履带动力学
TrackDynamics 对左右履带命令建模,并返回一个 (2, n_w, 2) 形状的数组:
- 第 0 维:左、右履带两侧
- 第 1 维:每一侧的轮/接触点
- 第 2 维:平面力分量
(Fx, Fy)
在当前阶段,对用户来说最关键的理解是:这个模块会根据左右履带命令与机体平面运动,计算各个接触点上的驱动力分布,而本地仿真就是通过这些力来施加履带侧推进效果。
状态与命令约定
本地 runner 使用与控制器接口一致的紧凑状态切片:
- 旋翼模式动力学消费形状为
(4,)的电机 RPM 命令 - 履带模式动力学消费形状为
(2,)的左右履带命令 - 履带滑移计算会读取机体系
[u, v, r]
在控制器侧,飞行状态 / 目标向量使用 12 个通道,顺序为:
[x, y, z, vx, vy, vz, roll, pitch, yaw, wx, wy, wz]
履带状态 / 目标向量使用 6 个通道,顺序为:
[x, y, yaw, u, v, r]
理解这些布局之后,再去阅读本地模拟器和各类任务封装会容易很多。
并行后端一致性
lav2/dynamics/torch/ 下的 Torch 实现镜像了 NumPy 版 API,从而让大规模并行训练可以复用同一套动力学逻辑。未来如果引入其它计算框架,例如 JAX 或 Warp,也应遵循相同规则。
当某个动力学模块在 NumPy 路径下新增实现时,并行后端应在各自框架目录下添加同名版本,并在除环境维度以外的行为上保持一致。换句话说,批量实现可以增加额外的 env 轴,但底层的状态、命令与输出语义应保持不变。
API 交叉引用
- 共享参数:VehicleParams
- 旋翼模型:RotorDynamics
- 履带模型:TrackDynamics
- Torch 旋翼模型:lav2.dynamics.torch.rotor
- Torch 履带模型:lav2.dynamics.torch.track