Skip to content

fix: mjwarp 后端补齐 body kinematics 只读能力,恢复 G1 walk MBA 训练 #1282

Description

@TATP-233

现象

uv run train --algo sac --task g1_walk_flat --sim mjwarp 在建 env 阶段直接失败:

NotImplementedError: Entity 'robot' capability 'body position state' is unavailable on backend 'mjwarp':
mjwarp host_numpy profile does not expose world-frame body positions;
the G1 host adapter supports only base, dof, and configured sensor cache reads.

flashsac/td3/ppo 的 mjwarp owner 走同一 factory(make_g1_walk_env),同样受影响。

现状(本地实证,非推测)

逐 commit 复跑同一命令的结果:

commit 说明 结果
05c6d29 #1232 G1 迁入 Manager-Based runtime 已失败,同一报错
6047408 #1259 优化波之前 同样失败
HEAD (abef0da) #1254/#1264/#1265/#1267/#1270/#1274/#1277/#1280 同样失败

结论:mjwarp G1 训练#1232 MBA 迁移起即不可用,不是 #1259 优化波引入的回归。#1259 系列改动(#1254 固定 get_body_*_vel_b 解析语义、#1274 update-phase state-read cache 等)是本次修复必须适配的最新契约,而非根因。

根因链:

  1. G1 walk flat base owner(conf/sac/task/g1_walk_flat/base.yaml)声明 root_body_name: pelvis + body_names: [pelvis]
  2. Entity.__init__src/unilab/base/entity.py:640 _validate_body_state)在 init 时 eager 校验 4 个 world-frame body getter + root 的 2 个 body-frame velocity getter。
  3. mjwarp host_numpy profile(src/unilab/base/backend/mjwarp/backend.py:774-810)对全部 get_body_*NotImplementedError,只支持 base/dof/sensor cache 读取。
  4. create_backendsrc/unilab/base/backend/__init__.py:158)的 mjwarp 分支完全忽略 body_state_required;mujoco/motrix 分支会将其翻译为 add_body_sensors=True

已有拦截测试但被 lane 过滤:tests/base/test_mjwarp_backend.py::test_g1_walk_flat_owner_one_step[sac] 在 HEAD 实际失败(-m slow 显式运行确认),默认 lane deselect slow 标记,所以一直没有拦住。

运行时真实需求很小:termination root_height_below_minimum 每步读 root_link_pos_w(pelvis 世界坐标,热路径);其余 obs/reward terms 均走 named sensor cache(torso_gyrotorso_upvectorpelvis_local_linvel、脚部 pos/quat/contact sensors),与 mjwarp 现有 host_numpy profile 兼容。

修复方案(本 issue 只做这些)

  1. create_backend mjwarp 分支接收 body_state_required=True 并向 MjwarpBackend 传递 body-sensor 注入开关(对齐 mujoco/motrix 的 add_body_sensors 语义)。
  2. mjwarp 冷路径 materialization 注入 world-frame tracking frame sensors(framexpos/framexquat/framelinvel/frameangvel),复用 unilab.base.backend.mujoco.xml.inject_mujoco_tracking_sensors(mjwarp materialization 已有引用 mujoco xml helper 的先例)。
  3. MjwarpBackend 实现 get_body_{pos,quat,lin_vel,ang_vel}_w:从每步本已下载的 _sensor_cache 读取,热路径零新增 device→host 同步;get_body_{lin,ang}_vel_bfix: pin get_body_*_vel_b to analytical body-frame semantics across backends #1254 语义用 quat inverse rotate 解析计算(复用 unilab/utils/rotation.py 的批量实现),与 SimBackend 已声明接口对齐。
  4. 测试:恢复 test_g1_walk_flat_owner_one_step(ppo+sac 两个 param)通过;新增 mjwarp vs mujoco 的 body-state 数值 parity 测试(stand keyframe + 若干 step 后 pos/quat/vel 对比,slow+CUDA lane)。

明确不做

  • 不放宽 Entity 的 eager body-state 校验,也不给 mjwarp 加 contract 特判(contract first)。
  • 不改任何 obs/reward term、obs 维度或 owner YAML 的 DENYLIST 字段(sim2sim 契约保持不变)。
  • 不在热路径新增 asset 解析或 backend 私有能力探测。
  • 不适配 perf: add CUDA graph capture to mjwarp backend #1273 的 CUDA graph capture(不在当前分支;合入时需 rebase 验证 sensor cache 下载与 graph 的交互)。
  • 不验证 g1_walk_flat 以外 task 的 mjwarp owner(如后续需要另建 issue)。

规模与永久维护成本

预计 4–6 个文件、300–500 行净改动:backend/__init__.pymjwarp/backend.pymjwarp/materialization.pytests/base/test_mjwarp_backend.py,可能加一个 parity 测试文件。永久维护成本:mjwarp backend 新增约百行 getter 与冷路径 sensor 注入,语义与 mujoco backend 对齐,由 parity 测试防止两后端漂移;无新增配置字段、无新增 execution path。

验证

  • uv run pytest tests/base/test_mjwarp_backend.py -m slow 全绿(含恢复的两个 owner 测试与新增 parity 测试)。
  • make test-all 通过。
  • 手动 uv run train --algo sac --task g1_walk_flat --sim mjwarp 能进入正常训练迭代。

相关:#1042#1232#1254#1259#1274

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions