跳转至

轨迹

LAV2 中的轨迹用于定义时间参数化的参考信号。它位于高层任务意图与实际驱动车辆的控制或策略路径之间,回答的是一个非常基础但又非常关键的问题:在给定时刻 t,系统当前应当跟踪什么目标?

在这个仓库里,轨迹并不被视为控制器逻辑。它不负责决定跟踪误差如何调节,也不承担奖励计算或后端环境结构的定义。它的职责更单一,也更清晰:生成语义稳定的参考状态,使同一类目标信号能够在本地仿真、并行训练后端和评估工作流之间复用。

角色与边界

轨迹的主要作用,是生成与控制栈使用同一套状态语义的参考量。对飞行路径而言,这意味着输出一个 12 维目标,顺序为 [x, y, z, vx, vy, vz, roll, pitch, yaw, wx, wy, wz]。因此,轨迹层位于控制级联之上,也位于选择参考信号族的任务或脚本之下。

这个边界之所以重要,是因为它把职责清楚地分开了。轨迹描述的是“随时间变化的期望运动”。控制器负责把这个参考量翻译成力、力矩或执行器级命令。任务后端则负责决定何时采样参考、如何把它暴露给策略,以及跟踪效果如何进入奖励或终止条件。只有把这几层保持分离,轨迹实现才能保持可复用,而不会逐渐演化成控制器逻辑或任务逻辑的隐式副本。

flowchart LR
  T[时间参数] --> Traj[轨迹]
  Traj --> Ref[12维参考目标]
  Ref --> Ctrl[控制器或策略路径]
  Ctrl --> Act[执行器与动力学]
  Act --> State[观测状态]

模块组织

LAV2 在 lav2.trajectories 下提供轨迹实现,并在 lav2.trajectories.torch 下提供批量版本。NumPy 路径主要面向本地或单实例使用,Torch 路径则主要面向需要同时对大量环境采样参考信号的并行后端。

在基础层面,BaseTrajectory 定义了 NumPy 轨迹的统一接口,SpatialTrajectory 则进一步引入了世界坐标系锚点这一概念。Torch 侧通过 lav2.trajectories.torch.base 保持相同的高层语义,只是在接口上增加了前置的批量维度。

这种 NumPy 与 Torch 成对出现的组织方式,是仓库里一个很重要的设计选择。它使得同一类轨迹既可以用于本地调试,也可以用于大规模训练。批量实现可以增加环境维度,但不应悄悄改变参考目标本身的含义。

参考量语义

轨迹目标的组装逻辑主要集中在 BaseTrajectory.build_target 及其 Torch 对应实现中。一个具体轨迹通常先计算位置、速度和加速度,再把这些基础量拼装成完整目标向量。偏航角既可以根据平面速度自动生成,也可以固定为常数;横滚、俯仰和机体系角速度则由轨迹配置直接给出,而不是从闭环跟踪结果中反推出来。

这意味着轨迹层已经携带了一部分姿态参考语义,但它依然不是控制器。它只是以控制栈能够理解的形式提供期望的姿态相关量,真正如何去调节这些量,仍然属于控制器或策略的职责。

另一个很有用的设计细节是,航向生成逻辑是共享的,而不是由每一种轨迹形状分别实现。当 yaw_from_velocity 打开时,航向会跟随平面速度方向,偏航角速度参考则由速度和加速度共同计算;当它关闭时,轨迹则可以保持固定航向。这让螺旋、矩形、双纽线等不同轨迹族在航向语义上保持一致。

典型轨迹类型

最简单的一类内置参考是 HoverStepTrajectory。它生成分段常值的位置与偏航目标,因此特别适合观察瞬态行为。超调、稳定时间、饱和以及模式切换等问题,在阶跃类目标下往往最容易暴露,因为期望状态本身就是有意不连续的。

RectangleTrajectory 更适合用于观察长直线匀速段与尖角转向之间的切换行为。它能够比较直接地暴露方向变化、控制裕度以及航向更新过程中的问题,因此在对比连续平滑曲线与具有明显几何拐点的路径时非常有用。

HelixTrajectory 引入了水平与垂向耦合运动。当你希望观察控制器或策略在平移、航向对齐与高度变化同时存在时是否仍然稳定,螺旋轨迹通常是很好的参考。可选的垂向振荡也使它比单纯平面圆轨迹更有信息量。

LemniscateTrajectoryLissajousTrajectory 更适合覆盖更广的工作空间。双纽线会反复穿过中心区域,因此有助于观察系统在曲率变化和自交运动附近的表现。Lissajous 轨迹则更一般,可以用连续、平滑且周期性的方式覆盖更大的三维范围,适合做更密集的参考激励,而不是单一机动动作验证。

把这些轨迹放在一起看,它们服务的是不同的工程目的。阶跃类目标更适合看瞬态,几何闭环轨迹更适合看持续跟踪,复杂周期曲线更适合暴露多轴耦合效应。相比某条轨迹的具体数学形式,这种“它更适合拿来验证什么”的区分往往更重要。

轨迹如何进入系统

在本地路径中,轨迹会随着仿真时间被采样,随后把生成出的目标传给 MuJoCo runner 所期望的控制器接口。在训练后端中,类似的参考量也可能由命令管理器或后端专用任务项持有,但概念角色是一致的:它仍然是在生成与控制栈对齐的目标信号。

flowchart TD
  A[轨迹类] --> B[在时刻 t 采样]
  B --> C[参考目标]
  C --> D[本地 runner 或任务后端]
  D --> E[控制器或策略]
  E --> F[执行与动力学]

这也是轨迹与任务边界应当保持清晰的地方。任务定义的是参考信号如何调度、如何随机化、如何进入奖励以及如何暴露给学习代码;轨迹定义的是参考信号本身属于哪一类运动原语。只有把这两部分分开,同一个运动原语才更容易在本地验证、控制器评估和强化学习环境中复用,而不需要重复实现。

扩展约定

新增轨迹时,最重要的要求是保持输出语义稳定。新的轨迹类仍应输出与现有轨迹族相同含义的 12 维目标,而不应在采样函数里混入控制器启发式、后端专用奖励假设或本地可视化逻辑。

第二个要求,是保持 NumPy 与 Torch 路径的一致性。如果某条轨迹先在本地使用,随后又在并行环境中变得有价值,那么 Torch 版本应尽量保持相同的运动定义和目标布局,而不是演化出一个“看起来相似、但语义已经变化”的变体。对这里来说,参数命名和输出含义的一致性,比代码表面上的逐行对称更重要。

第三个要求,是继续把时间作为显式的组织变量。这个包里的轨迹是时间参数化的参考生成器,而不是会在内部悄悄改变任务逻辑的状态机。只有这样,它们在更大的控制与训练工作流里才能维持稳定角色,也更容易被单独理解与复用。

API 交叉引用