From 34e0db4e7f2a575f94563bb4ec6dd48ed589c32b Mon Sep 17 00:00:00 2001 From: TATP-233 Date: Tue, 6 Oct 2026 20:11:16 +0800 Subject: [PATCH] feat(drake): scope Go2 owner into tensor runtime --- Makefile | 4 ++ .../en/1-getting_started/2-installation.md | 16 +++--- .../en/2-user_guide/3-backends/0-index.md | 21 ++++--- .../en/2-user_guide/3-backends/6-drake.md | 11 ++-- .../source/en/5-reference/5-support_matrix.md | 57 ++++++++++--------- .../zh_CN/1-getting_started/2-installation.md | 9 ++- .../zh_CN/2-user_guide/3-backends/0-index.md | 12 +++- .../zh_CN/2-user_guide/3-backends/6-drake.md | 9 ++- .../zh_CN/5-reference/5-support_matrix.md | 53 +++++++++-------- scripts/tools/setup_drake_env.sh | 12 ++-- scripts/tools/support_matrix.py | 6 +- src/unilab/base/registry.py | 8 ++- src/unilab/cli.py | 22 ++++++- .../ppo/task/go2_joystick_flat/drake.yaml | 44 ++++++++++++++ src/unilab/tasks/locomotion/go2/__init__.py | 1 + tests/base/test_registry.py | 11 +++- .../locomotion/go2/test_manager_based_cfg.py | 40 +++++++++++-- tests/envs/test_stewart.py | 14 ++--- tests/scripts/test_ci_scope.py | 2 +- tests/scripts/test_drake_training_smoke.py | 7 +-- tests/scripts/test_support_matrix.py | 18 +++++- tests/tasks/test_go2_tensor_runtime.py | 1 + tests/test_cli_runtime_requirements.py | 27 ++++++++- 23 files changed, 291 insertions(+), 114 deletions(-) create mode 100644 src/unilab/conf/ppo/task/go2_joystick_flat/drake.yaml diff --git a/Makefile b/Makefile index 28a9c78c2..7ac591156 100644 --- a/Makefile +++ b/Makefile @@ -89,3 +89,7 @@ clean: find src/unilab/assets/caches -type f ! -name '.gitkeep' -delete 2>/dev/null || true find src/unilab/assets/checkpoints -type f ! -name '.gitkeep' -delete 2>/dev/null || true find src/unilab/assets/scenes -type f ! -name '.gitkeep' -delete 2>/dev/null || true + +.PHONY: setup-drake +setup-drake: + uv run --no-sync bash scripts/tools/setup_drake_env.sh --download-drake diff --git a/docs/sphinx/source/en/1-getting_started/2-installation.md b/docs/sphinx/source/en/1-getting_started/2-installation.md index 0d9bcf482..2e68f617b 100644 --- a/docs/sphinx/source/en/1-getting_started/2-installation.md +++ b/docs/sphinx/source/en/1-getting_started/2-installation.md @@ -120,9 +120,9 @@ is unreachable. ## Backend Extras -The tensor-only Manager runtime currently exposes five production backends: -`mujoco`, `mjwarp`, `genesis`, `newton`, and `motrix`. Their simulator -dependencies are optional. +The tensor-only Manager runtime currently exposes five production backends +(`mujoco`, `mjwarp`, `genesis`, `newton`, and `motrix`) plus the scoped Drake +owner. Their simulator dependencies are optional. ```bash # All scoped backends together. @@ -136,11 +136,12 @@ uv sync --extra mujoco --extra mjwarp --extra genesis --extra newton --extra mot | Genesis | `uv sync --extra genesis` | The validated path uses Linux x86_64, an NVIDIA GPU, and the pinned torch/Genesis versions | | Newton | `uv sync --extra newton` | Linux CUDA with the pinned newton / MuJoCo-Warp / Warp versions | | Motrix | `uv sync --extra motrix` | CPU-authoritative MotrixSim with packed Torch HOST_BRIDGE transfers | +| Drake | `make setup-drake` | Local Drake C++ prefix plus the DrakeUni native batch extension; currently scoped to PPO `go2_joystick_flat` | -The `drake`, `isaacgym`, `isaacsim`, and `superdex` adapters remain temporarily -shelved by `unisim-core` during issue #1811. Their extras and historical backend -pages are not production support claims and the UniLab train/eval CLI rejects -them until new capability, parity, and support-matrix evidence is provided. +The `isaacgym`, `isaacsim`, and `superdex` adapters remain temporarily shelved +by `unisim-core` during issue #1811. Their extras and historical backend pages +are not production support claims and the UniLab train/eval CLI rejects them +until new capability, parity, and support-matrix evidence is provided. Read the scoped backend pages for runtime variables, renderer requirements, and verification commands: @@ -150,6 +151,7 @@ verification commands: - {doc}`Genesis <../2-user_guide/3-backends/5-genesis>` - {doc}`Newton <../2-user_guide/3-backends/7-newton>` - {doc}`Motrix <../2-user_guide/3-backends/2-motrix>` +- {doc}`Drake <../2-user_guide/3-backends/6-drake>` ## Algorithm Extras diff --git a/docs/sphinx/source/en/2-user_guide/3-backends/0-index.md b/docs/sphinx/source/en/2-user_guide/3-backends/0-index.md index 39c044c6e..a599f5bda 100644 --- a/docs/sphinx/source/en/2-user_guide/3-backends/0-index.md +++ b/docs/sphinx/source/en/2-user_guide/3-backends/0-index.md @@ -1,15 +1,15 @@ # Simulation Backends The tensor-only Manager runtime currently exposes `mujoco`, `mjwarp`, `genesis`, -`newton`, and `motrix`. User commands select one with `--sim`, which routes to -the matching task owner YAML; do not switch a run by overriding -`training.sim_backend` alone. +`newton`, `motrix`, and the scoped Drake owner described below. User commands +select one with `--sim`, which routes to the matching task owner YAML; do not +switch a run by overriding `training.sim_backend` alone. -The `drake`, `isaacgym`, `isaacsim`, and `superdex` adapters remain temporarily -shelved by `unisim-core` during issue #1811. Their historical pages are retained -for adapter context only and are not production support claims. The train/eval -CLI rejects these names until new capability, parity, and support-matrix -evidence is provided. +The `isaacgym`, `isaacsim`, and `superdex` adapters remain temporarily shelved +by `unisim-core` during issue #1811. Their historical pages are retained for +adapter context only and are not production support claims. The train/eval CLI +rejects these names until new capability, parity, and support-matrix evidence is +provided. ## Runtime Prerequisites @@ -24,6 +24,10 @@ evidence is provided. - Motrix requires the `motrix` extra and provides a CPU-authoritative packed HOST_BRIDGE; the current canonical support scope is SAC `g1_walk_flat` and FlashSAC `g1_motion_tracking`. +- Drake requires the locally built DrakeUni batch extension. Its scoped PPO + `go2_joystick_flat` owner uses CPU physics and the packed HOST_BRIDGE + lifecycle; floating-root and reset-randomization events remain disabled until + the backend contract supports them. ## OS and GPU Support @@ -34,6 +38,7 @@ evidence is provided. | Genesis | Linux x86_64 | Required: NVIDIA GPU and driver; only the `gs.gpu` channel is validated | | Newton | Linux | Required: NVIDIA CUDA; the selected-reset lane is device-resident | | Motrix | Linux / macOS / Windows | CPU-authoritative physics; Torch CUDA buffers are optional | +| Drake | Linux x86_64 / Apple Silicon macOS | CPU-authoritative physics; Torch CUDA buffers are optional | Backend device requirements are independent of the learner device: MuJoCo can still train with its learner on CUDA, ROCm, MPS, or XPU. See the platform diff --git a/docs/sphinx/source/en/2-user_guide/3-backends/6-drake.md b/docs/sphinx/source/en/2-user_guide/3-backends/6-drake.md index 11eed4a6c..6430630cc 100644 --- a/docs/sphinx/source/en/2-user_guide/3-backends/6-drake.md +++ b/docs/sphinx/source/en/2-user_guide/3-backends/6-drake.md @@ -1,6 +1,8 @@ # Drake Backend -> **Drake is temporarily shelved.** This adapter is outside the tensor-only Manager runtime during #1811 and is not a production support claim. The train/eval CLI rejects it until new capability, parity, and support-matrix evidence is provided. +> **Scoped runtime support.** Drake currently has one Manager-runtime owner: +> PPO `go2_joystick_flat`. The page is not a broader production support claim. +> Task support remains governed by the generated support matrix. Drake is an experimental CPU batch backend. UniLab still owns the task, @@ -95,9 +97,10 @@ uv run train --algo ppo --task go2_joystick_flat --sim drake ``` This uses the Drake owner configuration (`1024` environments, `151` iterations, -and CPU training because Drake exposes float64 NumPy buffers). The Drake owner -also uses the scene keyframe reset; floating-root randomization is not exposed -by the current backend contract. +and CPU training because Drake exposes a CPU-authoritative packed host bridge). +The Drake owner uses the scene default/keyframe reset. Floating-root +randomization and PD reset randomization are not exposed by the current backend +contract. On Apple Silicon macOS, this command completed all 151 iterations locally (Drake 1.56.0, Python 3.13, 1024 environments) in about 254 seconds. diff --git a/docs/sphinx/source/en/5-reference/5-support_matrix.md b/docs/sphinx/source/en/5-reference/5-support_matrix.md index 10f0b11c6..3f54e755d 100644 --- a/docs/sphinx/source/en/5-reference/5-support_matrix.md +++ b/docs/sphinx/source/en/5-reference/5-support_matrix.md @@ -8,7 +8,7 @@ support beyond the evidence grade shown below. ## Backend Selection Rules - The tensor-only Manager runtime currently supports `mujoco`, `mjwarp`, - `genesis`, `newton`, and `motrix`. + `genesis`, `newton`, `motrix`, and the scoped Drake owner below. - The default backend is `mujoco`. - `--sim mjwarp` requires the `mjwarp` extra. Validated combinations are shown in the generated matrix below; all other entrypoints retain their matrix @@ -19,12 +19,14 @@ support beyond the evidence grade shown below. are shown in the generated matrix. - `--sim motrix` requires the `motrix` extra; the CPU-authoritative packed HOST_BRIDGE profile is shown in the generated matrix. +- `--sim drake` requires the local Drake extra and DrakeUni batch extension. + Its current scoped support claim is PPO `go2_joystick_flat` only. - `--algo`, `--task`, and `--sim` jointly select the owner YAML. - Do not treat `training.sim_backend` as a standalone backend switch. -- `drake`, `isaacgym`, `isaacsim`, and `superdex` are temporarily outside this - runtime scope. Their adapters remain in UniSim, but they are not UniLab - production support claims; re-enabling requires capability, parity, and - support-matrix evidence (#1811). +- `isaacgym`, `isaacsim`, and `superdex` are temporarily outside this runtime + scope. Their adapters remain in UniSim, but they are not UniLab production + support claims; re-enabling requires capability, parity, and support-matrix + evidence (#1811). ## Playback Differences @@ -74,34 +76,35 @@ This table is derived from UniSim's SDK-free public static inventory. It describ |---|---|---|---|---|---|---|---|---|---|---|---| | `mujoco` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge only; no ROCm CUDA-only fallback | In-process; no external Python worker | unknown | unknown | Unsupported | Exact | | `motrix` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge only; no ROCm CUDA-only fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact | +| `drake` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge only; no ROCm CUDA-only fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Exact | | `mjwarp` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Unsupported | | `newton` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Unsupported | | `genesis` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | Unsupported | Unsupported | Unsupported | Unsupported | ### Entrypoint x Task Owner -| Entrypoint | Task owner | MuJoCo | Motrix | mjwarp | Newton | Genesis | -|------------|------------|---|---|---|---|---| -| PPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | - | - | - | -| PPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Tested | Registered | Configured | -| PPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | -| PPO (torch) | `x2_wall_flip_tracking` (X2 wall flip tracking) | Tested | - | - | - | - | -| PPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | -| PPO (torch) | `allegro_inhand_grasp` (allegro inhand grasp) | Tested | - | - | - | - | -| PPO (torch) | `g1_box_tracking` (g1 box tracking) | Tested | - | - | - | - | -| PPO (torch) | `stewart_balance` (stewart balance) | Tested | - | - | - | - | -| APPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | - | - | - | -| APPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Registered | Registered | Registered | -| APPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | -| APPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | -| SAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Tested | Tested | Tested | Tested | -| SAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | Configured | Registered | Configured | -| SAC (torch) | `g1_wbt_obs` (g1 wbt obs) | Tested | - | - | - | - | -| FlashSAC (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | - | - | - | -| FlashSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Configured | Registered | Registered | -| FlashSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Tested | Configured | Configured | Configured | -| WarpSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Tested | Registered | Registered | -| WarpSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | Tested | Registered | Registered | +| Entrypoint | Task owner | MuJoCo | Motrix | Drake | mjwarp | Newton | Genesis | +|------------|------------|---|---|---|---|---|---| +| PPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Configured | - | - | - | +| PPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Tested | Registered | Configured | +| PPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | - | +| PPO (torch) | `x2_wall_flip_tracking` (X2 wall flip tracking) | Tested | - | - | - | - | - | +| PPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | - | +| PPO (torch) | `allegro_inhand_grasp` (allegro inhand grasp) | Tested | - | - | - | - | - | +| PPO (torch) | `g1_box_tracking` (g1 box tracking) | Tested | - | - | - | - | - | +| PPO (torch) | `stewart_balance` (stewart balance) | Tested | - | - | - | - | - | +| APPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Registered | - | - | - | +| APPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Registered | Registered | Registered | +| APPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | - | +| APPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | - | +| SAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Tested | - | Tested | Tested | Tested | +| SAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | - | Configured | Registered | Configured | +| SAC (torch) | `g1_wbt_obs` (g1 wbt obs) | Tested | - | - | - | - | - | +| FlashSAC (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Registered | - | - | - | +| FlashSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Configured | Registered | Registered | +| FlashSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Tested | - | Configured | Configured | Configured | +| WarpSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Tested | Registered | Registered | +| WarpSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | - | Tested | Registered | Registered | ### Source Index diff --git a/docs/sphinx/source/zh_CN/1-getting_started/2-installation.md b/docs/sphinx/source/zh_CN/1-getting_started/2-installation.md index 32bdcf84a..8757a808a 100644 --- a/docs/sphinx/source/zh_CN/1-getting_started/2-installation.md +++ b/docs/sphinx/source/zh_CN/1-getting_started/2-installation.md @@ -108,8 +108,9 @@ Hugging Face endpoint 无法访问时,可设置 `HF_ENDPOINT=https://hf-mirror ## 后端 Extras -当前 tensor-only Manager runtime 暴露五个生产后端:`mujoco`、`mjwarp`、 -`genesis`、`newton`、`motrix`。各自的仿真依赖均为可选。 +当前 tensor-only Manager runtime 暴露五个生产后端(`mujoco`、`mjwarp`、 +`genesis`、`newton`、`motrix`),另有一个 scoped Drake owner。各自的仿真依赖 +均为可选。 ```bash # 一次安装全部 scoped 后端。 @@ -123,8 +124,9 @@ uv sync --extra mujoco --extra mjwarp --extra genesis --extra newton --extra mot | Genesis | `uv sync --extra genesis` | 已验证路径使用 Linux x86_64、NVIDIA GPU 及固定版本 torch/Genesis | | Newton | `uv sync --extra newton` | Linux CUDA,并钉定 newton / MuJoCo-Warp / Warp 版本 | | Motrix | `uv sync --extra motrix` | CPU-authoritative MotrixSim 与 packed Torch HOST_BRIDGE 传输 | +| Drake | `make setup-drake` | 本地 Drake C++ 前缀与 DrakeUni native batch extension;当前 scoped 到 PPO `go2_joystick_flat` | -`drake`、`isaacgym`、`isaacsim` 与 `superdex` 适配器在 issue #1811 期间由 +`isaacgym`、`isaacsim` 与 `superdex` 适配器在 issue #1811 期间由 `unisim-core` 暂时搁置。它们的 extras 和历史后端页面不构成生产支持声明; 在提供新的 capability、parity 与支持矩阵证据之前,UniLab train/eval CLI 会直接拒绝这些后端。 @@ -136,6 +138,7 @@ runtime 变量、渲染器要求和验证命令见 scoped 后端页面: - {doc}`Genesis <../2-user_guide/3-backends/5-genesis>` - {doc}`Newton <../2-user_guide/3-backends/7-newton>` - {doc}`Motrix <../2-user_guide/3-backends/2-motrix>` +- {doc}`Drake <../2-user_guide/3-backends/6-drake>` ## 算法 Extras diff --git a/docs/sphinx/source/zh_CN/2-user_guide/3-backends/0-index.md b/docs/sphinx/source/zh_CN/2-user_guide/3-backends/0-index.md index 52fa24284..c01361040 100644 --- a/docs/sphinx/source/zh_CN/2-user_guide/3-backends/0-index.md +++ b/docs/sphinx/source/zh_CN/2-user_guide/3-backends/0-index.md @@ -1,10 +1,11 @@ # 仿真后端 当前 tensor-only Manager runtime 暴露 `mujoco`、`mjwarp`、`genesis`、 -`newton` 与 `motrix`。用户命令通过 `--sim` 选择后端,并组合对应的 task owner -YAML;不要单独覆盖 `training.sim_backend`。 +`newton`、`motrix`,以及下文描述的 scoped Drake owner。用户命令通过 `--sim` +选择后端,并组合对应的 task owner YAML;不要单独覆盖 +`training.sim_backend`。 -`drake`、`isaacgym`、`isaacsim` 与 `superdex` 适配器在 issue #1811 期间由 +`isaacgym`、`isaacsim` 与 `superdex` 适配器在 issue #1811 期间由 `unisim-core` 暂时搁置。历史页面仅用于 adapter 背景,不构成生产支持声明。 在提供新的 capability、parity 与支持矩阵证据之前,train/eval CLI 会直接 拒绝这些后端。 @@ -20,6 +21,10 @@ YAML;不要单独覆盖 `training.sim_backend`。 为 SAC `g1_walk_flat` 和 FlashSAC `g1_motion_tracking`。 - Motrix 需要 `motrix` extra,提供 CPU-authoritative packed HOST_BRIDGE;当前 canonical 支持范围为 SAC `g1_walk_flat` 和 FlashSAC `g1_motion_tracking`。 +- Drake 需要本地编译的 DrakeUni batch extension。其 scoped PPO + `go2_joystick_flat` owner 使用 CPU physics 与 packed HOST_BRIDGE + lifecycle;在 backend contract 补齐前,浮动根重置与 reset randomization + 事件保持禁用。 ## OS 与 GPU 支持 @@ -30,6 +35,7 @@ YAML;不要单独覆盖 `training.sim_backend`。 | Genesis | Linux x86_64 | 要求:NVIDIA GPU 与 driver;仅验证 `gs.gpu` channel | | Newton | Linux | 要求:NVIDIA CUDA;selected-reset 通道为 device-resident | | Motrix | Linux / macOS / Windows | CPU-authoritative physics;Torch CUDA buffer 可选 | +| Drake | Linux x86_64 / Apple Silicon macOS | CPU-authoritative physics;Torch CUDA buffer 可选 | 后端设备要求与 learner 设备独立:MuJoCo 仍可以让 learner 使用 CUDA、ROCm、MPS 或 XPU。平台配置见 {doc}`../../1-getting_started/2-installation`。 diff --git a/docs/sphinx/source/zh_CN/2-user_guide/3-backends/6-drake.md b/docs/sphinx/source/zh_CN/2-user_guide/3-backends/6-drake.md index d10bbffca..f1ddda201 100644 --- a/docs/sphinx/source/zh_CN/2-user_guide/3-backends/6-drake.md +++ b/docs/sphinx/source/zh_CN/2-user_guide/3-backends/6-drake.md @@ -1,6 +1,8 @@ # Drake 后端 -> **Drake 暂时搁置。** 该适配器在 #1811 期间不属于 tensor-only Manager runtime,也不构成生产支持声明。在提供新的 capability、parity 与支持矩阵证据之前,train/eval CLI 会直接拒绝该后端。 +> **Scoped runtime 支持。** Drake 当前只有一个 Manager-runtime owner:PPO +> `go2_joystick_flat`。本页不构成更广泛的生产支持声明;任务支持范围以生成的 +> 支持矩阵为准。 Drake 是实验性的 CPU 批量物理后端;task、reward、observation 和训练循环仍由 @@ -89,8 +91,9 @@ uv run train --algo ppo --task go2_joystick_flat --sim drake ``` 该命令使用 Drake owner 配置中的 1024 个环境、151 次迭代,并固定使用 CPU 训练 -(Drake 暴露的是 float64 NumPy buffer)。Drake owner 使用场景 keyframe 重置;当前 -backend contract 尚未提供浮动根状态随机化。只想验证安装时,可临时追加 +(Drake 暴露的是 CPU-authoritative packed host bridge)。Drake owner 使用场景默认 +/keyframe 重置;当前 backend contract 尚未提供浮动根状态与 PD reset +randomization。只想验证安装时,可临时追加 `algo.max_iterations=1 algo.num_envs=4 algo.num_steps_per_env=4 training.no_play=true env.drake_nthread=1`;这些不是生产参数。 diff --git a/docs/sphinx/source/zh_CN/5-reference/5-support_matrix.md b/docs/sphinx/source/zh_CN/5-reference/5-support_matrix.md index f677157a0..c0a14212a 100644 --- a/docs/sphinx/source/zh_CN/5-reference/5-support_matrix.md +++ b/docs/sphinx/source/zh_CN/5-reference/5-support_matrix.md @@ -11,16 +11,20 @@ ## Backend 选择规则 - 当前 tensor-only Manager runtime 只支持 `mujoco`、`mjwarp`、`genesis`、 - `newton`、`motrix`。 + `newton`、`motrix`,以及下方 scoped Drake owner。 - 默认后端是 `mujoco` - `--sim mjwarp` 使用前需安装 `mjwarp` extra;已完成验证的组合以下方生成矩阵为准,其他入口也按矩阵查证 - `--sim genesis` 使用前需安装 `genesis` extra;真实 CUDA 依赖和平台约束见生成矩阵 - `--sim newton` 使用前需安装 `newton` extra;真实 CUDA 依赖和平台约束见生成矩阵 - `--sim motrix` 使用前需安装 `motrix` extra;CPU-authoritative packed HOST_BRIDGE profile 见生成矩阵 +- `--sim drake` 需要本地 Drake extra 与 DrakeUni batch extension;当前 scoped + 支持范围仅为 PPO `go2_joystick_flat` - `--algo`、`--task`、`--sim` 共同选择 owner YAML - 不要把 `training.sim_backend` 当独立 backend switch -- `drake`、`isaacgym`、`isaacsim`、`superdex` 暂时不在本 runtime 支持范围内。adapter 保留在 UniSim,但不构成 UniLab 生产支持声明;重新启用需要 capability、parity 和支持矩阵证据(#1811) +- `isaacgym`、`isaacsim`、`superdex` 暂时不在本 runtime 支持范围内。adapter + 保留在 UniSim,但不构成 UniLab 生产支持声明;重新启用需要 capability、parity + 和支持矩阵证据(#1811) ## Playback Differences @@ -63,34 +67,35 @@ uv run scripts/generate_support_matrix.py --write |---|---|---|---|---|---|---|---|---|---|---|---| | `mujoco` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge only; no ROCm CUDA-only fallback | In-process; no external Python worker | unknown | unknown | 不支持 | 支持 | | `motrix` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge only; no ROCm CUDA-only fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 | +| `drake` | Host bridge / in-process / host bridge | CPU / CUDA | Required only when the learner requests CUDA state/control buffers | Supported: CPU-authoritative physics with optional CUDA Torch buffers | CPU-authoritative host bridge only; no CUDA physics claim | CPU-authoritative host bridge only; no ROCm CUDA-only fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 支持 | | `mjwarp` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 不支持 | | `newton` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 不支持 | | `genesis` | Device-resident / in-process / direct | CUDA | Required for the entire tensor lifecycle | Supported: Linux CUDA only | Unsupported; no CPU, MPS, or ROCm fallback | Unsupported; no CPU, MPS, or ROCm fallback | In-process; no external Python worker | 不支持 | 不支持 | 不支持 | 不支持 | ### Entrypoint x Task Owner -| Entrypoint | Task owner | MuJoCo | Motrix | mjwarp | Newton | Genesis | -|------------|------------|---|---|---|---|---| -| PPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | - | - | - | -| PPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Tested | Registered | Configured | -| PPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | -| PPO (torch) | `x2_wall_flip_tracking` (X2 wall flip tracking) | Tested | - | - | - | - | -| PPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | -| PPO (torch) | `allegro_inhand_grasp` (allegro inhand grasp) | Tested | - | - | - | - | -| PPO (torch) | `g1_box_tracking` (g1 box tracking) | Tested | - | - | - | - | -| PPO (torch) | `stewart_balance` (stewart balance) | Tested | - | - | - | - | -| APPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | - | - | - | -| APPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Registered | Registered | Registered | -| APPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | -| APPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | -| SAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Tested | Tested | Tested | Tested | -| SAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | Configured | Registered | Configured | -| SAC (torch) | `g1_wbt_obs` (g1 wbt obs) | Tested | - | - | - | - | -| FlashSAC (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | - | - | - | -| FlashSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Configured | Registered | Registered | -| FlashSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Tested | Configured | Configured | Configured | -| WarpSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Tested | Registered | Registered | -| WarpSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | Tested | Registered | Registered | +| Entrypoint | Task owner | MuJoCo | Motrix | Drake | mjwarp | Newton | Genesis | +|------------|------------|---|---|---|---|---|---| +| PPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Configured | - | - | - | +| PPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Tested | Registered | Configured | +| PPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | - | +| PPO (torch) | `x2_wall_flip_tracking` (X2 wall flip tracking) | Tested | - | - | - | - | - | +| PPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | - | +| PPO (torch) | `allegro_inhand_grasp` (allegro inhand grasp) | Tested | - | - | - | - | - | +| PPO (torch) | `g1_box_tracking` (g1 box tracking) | Tested | - | - | - | - | - | +| PPO (torch) | `stewart_balance` (stewart balance) | Tested | - | - | - | - | - | +| APPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Registered | - | - | - | +| APPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Registered | Registered | Registered | +| APPO (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | - | - | - | - | - | +| APPO (torch) | `allegro_inhand` (Allegro in-hand) | Tested | - | - | - | - | - | +| SAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Tested | - | Tested | Tested | Tested | +| SAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | - | Configured | Registered | Configured | +| SAC (torch) | `g1_wbt_obs` (g1 wbt obs) | Tested | - | - | - | - | - | +| FlashSAC (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Registered | - | - | - | +| FlashSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Configured | Registered | Registered | +| FlashSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Tested | - | Configured | Configured | Configured | +| WarpSAC (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | - | Tested | Registered | Registered | +| WarpSAC (torch) | `g1_motion_tracking` (G1 motion tracking) | Tested | Registered | - | Tested | Registered | Registered | ### Source Index diff --git a/scripts/tools/setup_drake_env.sh b/scripts/tools/setup_drake_env.sh index f93d1d24a..26e7128f7 100755 --- a/scripts/tools/setup_drake_env.sh +++ b/scripts/tools/setup_drake_env.sh @@ -1,8 +1,6 @@ -# HISTORICAL SHELVED-BACKEND TOOL (#1811) -# Retained only as adapter migration context. This tool does not install a -# UniLab production runtime backend and must not be treated as a support -# claim. The scoped tensor Manager runtime currently exposes mujoco, mjwarp, -# and genesis. +# Local setup for the scoped Drake tensor Manager owner (#1811). Drake C++ and +# the DrakeUni native extension remain optional local dependencies; installing +# them does not publish a package or claim support for any additional task. #!/usr/bin/env bash # Set up the UniLab Drake batch runtime on Linux or Apple Silicon macOS. @@ -333,6 +331,6 @@ export UNILAB_DRAKE_UNI_SOURCE="$DRAKE_UNI_SOURCE" export $LIBRARY_PATH_VAR="$RUNTIME_LIBRARY_PATH" 然后在 UniLab 根目录运行: -uv run --no-sync pytest tests/base/backend/test_drake_batch_pool.py \ - tests/scripts/test_drake_training_smoke.py -q +uv run --no-sync pytest tests/base/backend/test_drake_batch_pool.py -q +uv run --no-sync pytest -m slow tests/scripts/test_drake_training_smoke.py -q EOF diff --git a/scripts/tools/support_matrix.py b/scripts/tools/support_matrix.py index b469f443e..fe108b91f 100644 --- a/scripts/tools/support_matrix.py +++ b/scripts/tools/support_matrix.py @@ -23,7 +23,7 @@ BACKENDS: tuple[str, ...] = tuple( backend for backend in _ALL_BACKENDS - if backend in {"mujoco", "mjwarp", "genesis", "newton", "motrix"} + if backend in {"mujoco", "mjwarp", "genesis", "newton", "motrix", "drake"} ) SHELVED_BACKENDS = frozenset(_ALL_BACKENDS) - frozenset(BACKENDS) @@ -290,6 +290,10 @@ def _is_tested(spec: EntrypointSpec, task_slug: str, backend: str, root: Path) - spec.entrypoint_id, task_slug, ) in _MAINTAINER_VALIDATED_NEWTON_ENTRYPOINT_TASKS + if backend == "drake": + # This scoped owner has config/contract/lifecycle coverage, but full + # cross-platform training parity evidence has not been checked in. + return False return spec.generic_tested diff --git a/src/unilab/base/registry.py b/src/unilab/base/registry.py index 7444147e0..4136577f2 100644 --- a/src/unilab/base/registry.py +++ b/src/unilab/base/registry.py @@ -47,15 +47,19 @@ def __call__( "genesis", "newton", "motrix", + "drake", ) _SHELVED_SIM_BACKENDS = ( - "drake", "isaacgym", "isaacsim", "superdex", ) _SUPPORTED_SIM_BACKENDS = _TENSOR_MANAGER_SUPPORTED_SIM_BACKENDS + _SHELVED_SIM_BACKENDS -_DEFAULT_SIM_BACKEND_ORDER: tuple[str, ...] = _TENSOR_MANAGER_SUPPORTED_SIM_BACKENDS +# Drake is an explicit owner-selected CPU host bridge during its scoped +# reintroduction; never use it as an implicit fallback backend. +_DEFAULT_SIM_BACKEND_ORDER: tuple[str, ...] = tuple( + backend for backend in _TENSOR_MANAGER_SUPPORTED_SIM_BACKENDS if backend != "drake" +) _TENSOR_MANAGER_BACKEND_SCOPE_ERROR = ( "Simulation backend '{backend}' is temporarily out of the tensor-only " "Manager runtime scope. Active backends: " diff --git a/src/unilab/cli.py b/src/unilab/cli.py index 0a99e750a..872bb4ff5 100644 --- a/src/unilab/cli.py +++ b/src/unilab/cli.py @@ -22,11 +22,11 @@ "genesis", "newton", "motrix", + "drake", ) -# Adapters retained by UniSim but temporarily outside the tensor-only Manager -# runtime during issue #1811. They must remain unroutable through public CLI. +# Adapters retained by UniSim but outside the scoped tensor Manager runtime +# during issue #1811. They must remain unroutable through public CLI. _SHELVED_SIMS = ( - "drake", "isaacgym", "isaacsim", "superdex", @@ -159,6 +159,22 @@ def _check_runtime_requirements(algo: str, sim: str) -> None: "`pip install unilab[motrix]` (or `uv sync --extra motrix` in a source " "checkout)." ) + if sim == "drake": + if find_spec("drake_uni") is None: + raise SystemExit( + "sim=drake requires the Drake extra and a built DrakeUni batch extension. " + "Run scripts/tools/setup_drake_env.sh --download-drake in a source checkout." + ) + from unisim.backend.drake.backend import ensure_drake_batch_available + + available, error = ensure_drake_batch_available() + if not available: + detail = str(error) if error is not None else "unknown import error" + raise SystemExit( + "sim=drake could not load the DrakeUni batch extension. Set DRAKE_HOME and " + "the platform library path (LD_LIBRARY_PATH on Linux, DYLD_LIBRARY_PATH on " + f"macOS); details: {detail}" + ) def _override_bool(overrides: Sequence[str], key: str) -> bool | None: diff --git a/src/unilab/conf/ppo/task/go2_joystick_flat/drake.yaml b/src/unilab/conf/ppo/task/go2_joystick_flat/drake.yaml new file mode 100644 index 000000000..eec73972c --- /dev/null +++ b/src/unilab/conf/ppo/task/go2_joystick_flat/drake.yaml @@ -0,0 +1,44 @@ +# @package _global_ +defaults: + - /task/go2_joystick_flat/base + - _self_ + +training: + task_name: Go2JoystickFlat + sim_backend: drake + # Drake is an in-process CPU-authoritative host bridge. Keep this scoped + # owner on the CPU learner path while its packed reset profile is validated. + device: cpu + +algo: + num_envs: 1024 + max_iterations: 151 + obs_groups: + actor: + - actor + critic: + - critic + actor: + obs_normalization: true + distribution_cfg: + init_std: 0.5 + critic: + obs_normalization: true + algorithm: + learning_rate: 3.0e-4 + entropy_coef: 1.0e-3 + +env: + drake_backend_mode: batch + drake_nthread: 0 + events: + # Drake's current public profile has no floating-root reset layout or reset + # randomization; retain the keyframe/default reset and fail closed rather + # than silently approximating these terms. + reset_root_state_uniform: null + pd_gains: null + +reward: + # DrakeUni reports per-body net contact force in world coordinates, while the + # Go2 gait term expects MuJoCo's contact-frame force (normal first). + contact: null diff --git a/src/unilab/tasks/locomotion/go2/__init__.py b/src/unilab/tasks/locomotion/go2/__init__.py index b95b3a251..8a21a5c6d 100644 --- a/src/unilab/tasks/locomotion/go2/__init__.py +++ b/src/unilab/tasks/locomotion/go2/__init__.py @@ -5,5 +5,6 @@ registry.register_env_config("Go2JoystickFlat", ManagerBasedRlEnvCfg) registry.register_env("Go2JoystickFlat", make_manager_based_rl_env, sim_backend="mujoco") +registry.register_env("Go2JoystickFlat", make_manager_based_rl_env, sim_backend="drake") __all__: list[str] = [] diff --git a/tests/base/test_registry.py b/tests/base/test_registry.py index d8adf18ff..7ef4093ad 100644 --- a/tests/base/test_registry.py +++ b/tests/base/test_registry.py @@ -258,7 +258,7 @@ def test_register_env_invalid_backend_raises(): registry_mod.register_env(_name, _TestEnvA, "not_a_backend") -@pytest.mark.parametrize("backend", ["drake", "isaacgym", "isaacsim", "superdex"]) +@pytest.mark.parametrize("backend", ["isaacgym", "isaacsim", "superdex"]) def test_register_env_rejects_shelved_backend(backend): """Shelved adapters fail closed instead of registering a runtime path.""" _name = "_TestShelvedBackendEnv" @@ -281,6 +281,13 @@ def test_default_backend_order_is_tensor_manager_scope(): ) +def test_drake_is_registered_without_changing_default_backend_order(): + """Drake is explicit-select only; absent owners still reject it.""" + assert "drake" in registry_mod._TENSOR_MANAGER_SUPPORTED_SIM_BACKENDS + assert "drake" not in registry_mod._DEFAULT_SIM_BACKEND_ORDER + assert "drake" not in registry_mod._envs[_TEST_ENV_A].env_factory_dict + + def test_register_env_without_config_raises(): """register_env() when config is not yet registered must raise ValueError.""" with pytest.raises(ValueError, match="not registered"): @@ -379,7 +386,7 @@ def test_make_unsupported_backend_raises(): def test_make_rejects_shelved_backend_registration(): with pytest.raises(ValueError, match="temporarily out of the tensor-only"): - registry_mod.register_env(_TEST_ENV_A, lambda *args, **kwargs: None, "drake") + registry_mod.register_env(_TEST_ENV_A, lambda *args, **kwargs: None, "isaacgym") def test_make_no_env_factory_raises(): diff --git a/tests/envs/locomotion/go2/test_manager_based_cfg.py b/tests/envs/locomotion/go2/test_manager_based_cfg.py index fc46a4a83..62b2c342b 100644 --- a/tests/envs/locomotion/go2/test_manager_based_cfg.py +++ b/tests/envs/locomotion/go2/test_manager_based_cfg.py @@ -87,6 +87,11 @@ id="flashsac-mujoco", ), ) +_DRAKE_OWNER_CASE = ( + "ppo", + ("task=go2_joystick_flat/drake",), + "drake", +) def _compose(config_group: str, overrides: Sequence[str]) -> DictConfig: @@ -223,10 +228,33 @@ def test_go2_flat_registry_is_manager_only() -> None: assert bare_cfg.rewards == {} assert registry.list_registered_envs()["Go2JoystickFlat"] == { "config_factory": "ManagerBasedRlEnvCfg", - "available_backends": ["mujoco"], + "available_backends": ["mujoco", "drake"], } +def test_go2_flat_drake_owner_keeps_policy_io_and_disables_unsupported_dr() -> None: + registry.ensure_registries() + _, env_cfg, _ = _materialize(*_DRAKE_OWNER_CASE[:2]) + + assert env_cfg.events["reset_root_state_uniform"] is None + assert env_cfg.events["pd_gains"] is None + assert env_cfg.rewards["contact"] is None + assert env_cfg.actions["joint_pos"].scale == pytest.approx(0.25) + assert list(env_cfg.observations["policy"].terms) == [ + "base_ang_vel", + "projected_gravity", + "joint_pos", + "joint_vel", + "actions", + "command", + "gait_phase", + ] + assert list(env_cfg.observations["critic"].terms) == [ + *(list(env_cfg.observations["policy"].terms)), + "base_lin_vel", + ] + + @pytest.mark.parametrize( ("backend", "owner"), (("mujoco", "task=go2_joystick_flat/mujoco"),), @@ -319,13 +347,17 @@ def test_go2_flat_flashsac_uses_canonical_manager_events_and_numpy_noise() -> No assert env_cfg.rewards["contact"].weight == pytest.approx(1.5) -def test_go2_flat_drake_is_out_of_tensor_manager_scope() -> None: +def test_go2_flat_drake_factory_is_registered_before_optional_runtime_load() -> None: registry.ensure_registries() - hydra_cfg, _, env_override = _materialize("ppo", ("task=go2_joystick_flat/mujoco",)) - with pytest.raises(ValueError, match="does not support simulation backend 'drake'"): + hydra_cfg, _, env_override = _materialize("ppo", ("task=go2_joystick_flat/drake",)) + assert "drake" in registry.list_registered_envs()["Go2JoystickFlat"]["available_backends"] + try: registry.make( str(hydra_cfg.training.task_name), sim_backend="drake", env_cfg_override=env_override, num_envs=1, ) + except (ImportError, NotImplementedError, RuntimeError, TypeError, ValueError) as exc: + # Registration must not bypass the optional native runtime boundary. + assert "drakeuni batch runtime" in str(exc).lower() diff --git a/tests/envs/test_stewart.py b/tests/envs/test_stewart.py index 40d850846..e38fef6d2 100644 --- a/tests/envs/test_stewart.py +++ b/tests/envs/test_stewart.py @@ -17,7 +17,7 @@ from unilab.base import registry from unilab.base.config_adapter import BackendAdapter from unilab.base.config_materialization import apply_cfg_overrides -from unilab.envs import ManagerBasedRlEnv, ManagerBasedRlEnvCfg, make_manager_based_rl_env +from unilab.envs import ManagerBasedRlEnv, ManagerBasedRlEnvCfg from unilab.managers import ObservationTermCfg, RewardTermCfg, TerminationTermCfg from unilab.tasks.manipulation.stewart.balance import ( StewartBalanceState, @@ -354,13 +354,11 @@ def test_stewart_state_machine_and_fall_reward_are_exact() -> None: env.close() -def test_stewart_drake_materializes_or_fails_at_optional_runtime_boundary() -> None: - with pytest.raises(ValueError, match="temporarily out of the tensor-only Manager runtime"): - make_manager_based_rl_env( - _materialize("ppo", ("task=stewart_balance/mujoco",))[1], - num_envs=1, - backend_type="drake", - ) +def test_stewart_drake_remains_owner_scoped() -> None: + """Only tasks with a Drake owner registration may select the backend.""" + registry.ensure_registries() + backends = registry.list_registered_envs()["StewartBalance"]["available_backends"] + assert "drake" not in backends @pytest.mark.slow diff --git a/tests/scripts/test_ci_scope.py b/tests/scripts/test_ci_scope.py index b293cafac..83841577b 100644 --- a/tests/scripts/test_ci_scope.py +++ b/tests/scripts/test_ci_scope.py @@ -5,7 +5,7 @@ ROOT = Path(__file__).parents[2] WORKFLOWS = ROOT / ".github" / "workflows" -SHELVED_BACKENDS = ("isaacgym", "isaacsim", "superdex", "drake") +SHELVED_BACKENDS = ("isaacgym", "isaacsim", "superdex") # Some optional packages and identifiers contain these words without routing a # UniLab backend (for example CUDA nccl/cusparselt). The production gate is the # explicit ``--extra `` install pattern. diff --git a/tests/scripts/test_drake_training_smoke.py b/tests/scripts/test_drake_training_smoke.py index 93a8cac26..4325ae703 100644 --- a/tests/scripts/test_drake_training_smoke.py +++ b/tests/scripts/test_drake_training_smoke.py @@ -26,14 +26,13 @@ def _drake_batch_available() -> bool: not _drake_batch_available(), reason="optional DrakeUni batch extension has not been built", ) -@pytest.mark.parametrize("task", ["go2_joystick_flat/drake", "go2_joystick_flat/drake"]) -def test_drake_ppo_one_iteration_training_smoke(task: str, tmp_path: Path) -> None: +def test_drake_ppo_one_iteration_training_smoke(tmp_path: Path) -> None: """Drake task configs can run the real RSL-RL training entry point.""" result = subprocess.run( [ sys.executable, "src/unilab/scripts/train_rsl_rl.py", - f"task={task}", + "task=go2_joystick_flat/drake", "training.no_play=true", f"training.log_root={tmp_path / 'logs'}", "algo.num_envs=4", @@ -49,6 +48,6 @@ def test_drake_ppo_one_iteration_training_smoke(task: str, tmp_path: Path) -> No ) assert result.returncode == 0, ( - f"Drake PPO smoke failed for {task}:\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}" + f"Drake PPO smoke failed:\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}" ) assert "Learning iteration 0/1" in result.stdout diff --git a/tests/scripts/test_support_matrix.py b/tests/scripts/test_support_matrix.py index 3ee30cb15..7c9812cd2 100644 --- a/tests/scripts/test_support_matrix.py +++ b/tests/scripts/test_support_matrix.py @@ -23,7 +23,7 @@ def test_shelved_adapters_have_no_support_cells() -> None: """During #1811, shelved adapters are not production support claims.""" - assert SHELVED_BACKENDS == frozenset({"drake", "isaacgym", "isaacsim", "superdex"}) + assert SHELVED_BACKENDS == frozenset({"isaacgym", "isaacsim", "superdex"}) for row in build_support_rows(ROOT): assert set(row.cells) == set(BACKENDS) assert set(row.cells).isdisjoint(SHELVED_BACKENDS) @@ -325,4 +325,18 @@ def test_support_matrix_marks_allegro_appo_backends_as_tested(): def test_generated_support_matrix_exposes_only_tensor_manager_backends() -> None: from scripts.tools import support_matrix - assert support_matrix.BACKENDS == ("mujoco", "motrix", "mjwarp", "newton", "genesis") + assert support_matrix.BACKENDS == ("mujoco", "motrix", "drake", "mjwarp", "newton", "genesis") + + +def test_drake_scoped_owner_stays_configured_until_training_validation() -> None: + rows = build_support_rows(Path(__file__).resolve().parents[2]) + + tested = { + (row.entrypoint_label, row.task_slug) + for row in rows + if row.cells["drake"].level >= EvidenceLevel.TESTED + } + assert tested == set() + assert _row("PPO (torch)", "go2_joystick_flat").cells["drake"].level is ( + EvidenceLevel.CONFIGURED + ) diff --git a/tests/tasks/test_go2_tensor_runtime.py b/tests/tasks/test_go2_tensor_runtime.py index 361e903e0..71b7306be 100644 --- a/tests/tasks/test_go2_tensor_runtime.py +++ b/tests/tasks/test_go2_tensor_runtime.py @@ -352,6 +352,7 @@ def test_go2_runtime_does_not_add_backend_registrations() -> None: registry.ensure_registries() assert registry.list_registered_envs()["Go2JoystickFlat"]["available_backends"] == [ "mujoco", + "drake", ] diff --git a/tests/test_cli_runtime_requirements.py b/tests/test_cli_runtime_requirements.py index e4e2fd190..dcd56f83e 100644 --- a/tests/test_cli_runtime_requirements.py +++ b/tests/test_cli_runtime_requirements.py @@ -47,5 +47,30 @@ def test_check_runtime_requirements_requires_motrix_extra( cli._check_runtime_requirements("sac", "motrix") +def test_check_runtime_requirements_requires_drake_extra( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(cli, "find_spec", lambda name: None if name == "drake_uni" else object()) + + with pytest.raises(SystemExit, match="sim=drake requires the Drake extra"): + cli._check_runtime_requirements("ppo", "drake") + + +def test_check_runtime_requirements_rejects_unusable_drake_batch( + monkeypatch: pytest.MonkeyPatch, +) -> None: + def fake_find_spec(name: str): + return object() if name == "drake_uni" else None + + def unavailable() -> tuple[bool, ImportError | None]: + return False, ImportError("native extension unavailable") + + monkeypatch.setattr(cli, "find_spec", fake_find_spec) + monkeypatch.setattr("unisim.backend.drake.backend.ensure_drake_batch_available", unavailable) + + with pytest.raises(SystemExit, match="could not load the DrakeUni batch extension"): + cli._check_runtime_requirements("ppo", "drake") + + def test_cli_backend_choices_are_tensor_manager_scope() -> None: - assert cli.SUPPORTED_SIMS == ("mujoco", "mjwarp", "genesis", "newton", "motrix") + assert cli.SUPPORTED_SIMS == ("mujoco", "mjwarp", "genesis", "newton", "motrix", "drake")