diff --git a/.gitignore b/.gitignore index 91927a356..60e79a853 100644 --- a/.gitignore +++ b/.gitignore @@ -82,10 +82,6 @@ src/unilab/assets/motions/x2/*.csv src/unilab/assets/robots/x2/meshes/*.STL src/unilab/assets/robots/microduck/assets/* !src/unilab/assets/robots/microduck/assets/.gitkeep -src/unilab/assets/robots/t800/assets/* -!src/unilab/assets/robots/t800/assets/.gitkeep -src/unilab/assets/robots/t800/textures/* -!src/unilab/assets/robots/t800/textures/.gitkeep src/unilab/assets/robots/g1/assets/ src/unilab/assets/robots/g1/textures/ src/unilab/assets/robots/go2/assets/ diff --git a/docs/sphinx/source/en/4-developer_guide/7-motion_assets.md b/docs/sphinx/source/en/4-developer_guide/7-motion_assets.md index 4a8ea9ff5..7c36b71e1 100644 --- a/docs/sphinx/source/en/4-developer_guide/7-motion_assets.md +++ b/docs/sphinx/source/en/4-developer_guide/7-motion_assets.md @@ -88,7 +88,7 @@ Robot binary meshes and textures (for example `.STL`, `.obj`, and `.png`) are externalized the same way, on the Hugging Face dataset repo [unilabsim/unilab-robots](https://huggingface.co/datasets/unilabsim/unilab-robots). The registered robots are a2, allegro_hand, g1, go2, go2_arm, microduck, -sharpa_wave, t800, and x2 (`ROBOT_ASSET_SPECS` in `src/unilab/assets/hub.py`). +sharpa_wave, and x2 (`ROBOT_ASSET_SPECS` in `src/unilab/assets/hub.py`). Their mesh/texture directories download lazily on first use and land under their original paths (for example `src/unilab/assets/robots/g1/assets/` and `robots/g1/textures/` for G1), so the original relative XML paths remain @@ -107,30 +107,29 @@ To add a new robot's binary assets: 1. Upload each directory to the HF repo while keeping the directory layout identical. A robot with multiple asset directories requires one upload per - directory. For example, T800 uses: + directory. For example, G1 uses: ```bash uv run hf upload unilabsim/unilab-robots \ - src/unilab/assets/robots/t800/assets robots/t800/assets \ + src/unilab/assets/robots/g1/assets robots/g1/assets \ --repo-type dataset uv run hf upload unilabsim/unilab-robots \ - src/unilab/assets/robots/t800/textures robots/t800/textures \ + src/unilab/assets/robots/g1/textures robots/g1/textures \ --repo-type dataset ``` 2. Ignore the downloaded directory contents in `.gitignore` (robots whose entire directory is HF-hosted ignore the whole directory; the older - microduck/t800 entries keep a `.gitkeep`), exclude the directory in + microduck entry keeps a `.gitkeep`), exclude the directory in `tool.uv.build-backend.source-exclude`, and register it in `ROBOT_ASSET_SPECS`. 3. Scenes built through `create_backend` are then covered automatically: `ensure_robot_assets_for_paths` resolves the registered directories on a cold path before any backend parses the XML. Entry points that bypass - `create_backend` resolve explicitly, e.g. a T800 task factory calls: + `create_backend` resolve explicitly, e.g. the MicroDuck task factory calls: ```python - resolve_robot_asset_dir("robots/t800/assets", marker="LINK_BASE.obj") - resolve_robot_asset_dir("robots/t800/textures", marker="LINK_BASE.png") + resolve_robot_asset_dir("robots/microduck/assets", marker="trunk_base.stl") ``` ## Architecture Notes @@ -149,6 +148,6 @@ To add a new robot's binary assets: original local path exactly. - Robot binary assets use the same directory resolver (`resolve_robot_asset_dir`). The - thin X2, MicroDuck, and T800 task factories resolve their directories once + thin X2 and MicroDuck task factories resolve their directories once before delegating to the shared manager environment factory. The resolver is also exposed through the `unilab-pull-assets` CLI. 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 d3d9014a0..9283e39fe 100644 --- a/docs/sphinx/source/en/5-reference/5-support_matrix.md +++ b/docs/sphinx/source/en/5-reference/5-support_matrix.md @@ -13,9 +13,9 @@ in English. Do not infer support beyond the evidence grade shown below. headless physics in an external Python 3.11 worker; its owner scope is currently limited to the configured G1 walk-flat PPO/SAC paths. - `--sim mjwarp` has completed training validation only on the `g1_walk_flat` - host adapter, where PPO (torch) and SAC (torch) are Tested; the SAC - `t800_walk_flat` mjwarp owner is Configured only, other entrypoints follow - the matrix below, and using it requires installing the `mjwarp` extra. + host adapter, where PPO (torch) and SAC (torch) are Tested; other + entrypoints follow the matrix below, and using it requires installing the + `mjwarp` extra. - `--algo`, `--task`, and `--sim` jointly select the owner YAML. - Do not treat `training.sim_backend` as a standalone backend switch. @@ -53,9 +53,7 @@ owner may only cover training smoke and an explicitly enabled DR subset. `mjwarp` has completed training validation only on the `g1_walk_flat` host adapter: the PPO (torch) and SAC (torch) owners have completed training validation and have backend, contract, and playback automated coverage, so -they are marked `Tested`. The SAC `t800_walk_flat` mjwarp owner only has an -owner YAML and compose coverage, so it is marked `Configured`, which does not -imply training validation. mjwarp playback +they are marked `Tested`. mjwarp playback supports explicit, finite-step `record` and reuses the MuJoCo offline renderer; `uv run eval --sim mjwarp --render-mode interactive` routes to the MuJoCo interactive viewer (mjwarp physics, MuJoCo rendering of env[0], forced @@ -110,7 +108,6 @@ rendering/playback paths remain unsupported. | PPO (torch) | `go2w_joystick_flat` (go2w joystick flat) | Tested | - | Tested | - | - | | PPO (torch) | `go2w_joystick_rough` (go2w joystick rough) | Tested | - | Tested | - | - | | PPO (torch) | `stewart_balance` (stewart balance) | Tested | - | Tested | - | - | -| PPO (torch) | `t800_walk_flat` (t800 walk flat) | Tested | Registered | - | - | - | | APPO (torch) | `go1_joystick_flat` (Go1 joystick) | Tested | - | Tested | - | - | | APPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Tested | - | - | | APPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Registered | Registered | Registered | @@ -137,7 +134,6 @@ rendering/playback paths remain unsupported. | SAC (torch) | `g1_23dof_wall_flip_tracking` (g1 23dof wall flip tracking) | Tested | - | Registered | - | - | | SAC (torch) | `g1_23dof_wbt_obs` (g1 23dof wbt obs) | Tested | - | Registered | - | - | | SAC (torch) | `g1_wbt_obs` (g1 wbt obs) | Tested | - | Registered | - | - | -| SAC (torch) | `t800_walk_flat` (t800 walk flat) | Tested | Configured | - | - | - | | TD3 (torch) | `go1_joystick_flat` (Go1 joystick) | Registered | - | Tested | - | - | | TD3 (torch) | `go2_joystick_flat` (Go2 joystick) | Registered | - | Tested | - | - | | TD3 (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Registered | Registered | Registered | diff --git a/docs/sphinx/source/zh_CN/4-developer_guide/7-motion_assets.md b/docs/sphinx/source/zh_CN/4-developer_guide/7-motion_assets.md index d4b957ef6..f69c5d374 100644 --- a/docs/sphinx/source/zh_CN/4-developer_guide/7-motion_assets.md +++ b/docs/sphinx/source/zh_CN/4-developer_guide/7-motion_assets.md @@ -81,7 +81,7 @@ env: 托管在 Hugging Face 数据集仓库 [unilabsim/unilab-robots](https://huggingface.co/datasets/unilabsim/unilab-robots)。 已注册的机器人为 a2、allegro_hand、g1、go2、go2_arm、microduck、sharpa_wave、 -t800、x2(见 `src/unilab/assets/hub.py` 的 `ROBOT_ASSET_SPECS`)。它们的 +x2(见 `src/unilab/assets/hub.py` 的 `ROBOT_ASSET_SPECS`)。它们的 mesh/纹理目录在首次使用时按需下载,落盘到原始路径(例如 G1 的 `src/unilab/assets/robots/g1/assets/` 与 `robots/g1/textures/`),因此 XML 中的 原始相对路径保持有效。这些目录通过 `pyproject.toml` 的 @@ -97,28 +97,27 @@ uv run unilab-pull-assets --robot all # 所有已注册机器人 新增某个机器人的二进制资产: 1. 按目录上传到 HF 仓库,保持目录结构一致。一个机器人有多个资产目录时, - 每个目录分别上传。例如 T800: + 每个目录分别上传。例如 G1: ```bash uv run hf upload unilabsim/unilab-robots \ - src/unilab/assets/robots/t800/assets robots/t800/assets \ + src/unilab/assets/robots/g1/assets robots/g1/assets \ --repo-type dataset uv run hf upload unilabsim/unilab-robots \ - src/unilab/assets/robots/t800/textures robots/t800/textures \ + src/unilab/assets/robots/g1/textures robots/g1/textures \ --repo-type dataset ``` 2. 在 `.gitignore` 中忽略下载目录(整个目录托管到 HF 的机器人直接忽略整个目录; - 较早的 microduck/t800 条目保留 `.gitkeep`),在 + 较早的 microduck 条目保留 `.gitkeep`),在 `tool.uv.build-backend.source-exclude` 中排除该目录,并在 `ROBOT_ASSET_SPECS` 中注册。 3. 经由 `create_backend` 构建的 scene 会被自动覆盖: `ensure_robot_assets_for_paths` 在 backend 解析 XML 之前的冷路径解析已注册目录。 - 绕过 `create_backend` 的入口需要显式解析,例如 T800 task factory 调用两次: + 绕过 `create_backend` 的入口需要显式解析,例如 MicroDuck task factory: ```python - resolve_robot_asset_dir("robots/t800/assets", marker="LINK_BASE.obj") - resolve_robot_asset_dir("robots/t800/textures", marker="LINK_BASE.png") + resolve_robot_asset_dir("robots/microduck/assets", marker="trunk_base.stl") ``` ## 架构说明 @@ -131,6 +130,6 @@ uv run unilab-pull-assets --robot all # 所有已注册机器人 `ensure_robot_assets_for_paths`,然后再分发给具体 backend。 - 热路径(`step` / `reset`)**不会**触发任何文件下载或解析。 - `ASSETS_ROOT_PATH` 定义不变,下载落盘位置与原始本地路径完全一致。 -- 机器人二进制资产使用同一目录 resolver(`resolve_robot_asset_dir`)。X2、 - MicroDuck 和 T800 的薄 task factory 都会先在冷路径解析一次,再委托给共享 +- 机器人二进制资产使用同一目录 resolver(`resolve_robot_asset_dir`)。X2 和 + MicroDuck 的薄 task factory 都会先在冷路径解析一次,再委托给共享 manager env factory;同一 resolver 也通过 `unilab-pull-assets` CLI 暴露。 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 f4b929f5c..cc1d4c142 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 @@ -46,7 +46,7 @@ uv run scripts/generate_support_matrix.py --write `Tested` 只描述仓库中已有自动化覆盖或显式 maintainer 训练验证,不代表该组合具备同名 MuJoCo owner 的全部 backend capability;例如 phase-1 Motrix owner 可能只覆盖训练 smoke 和明确启用的 DR 子集。 -`mjwarp` 完成训练验证的只有 `g1_walk_flat` host adapter:PPO (torch) 与 SAC (torch) owner 已完成训练验证,并有 backend、contract 与 playback 自动化覆盖,因此标记为 `Tested`。SAC `t800_walk_flat` 的 mjwarp owner 只有 owner YAML 与 compose 覆盖,标记为 `Configured`,不代表训练验证。mjwarp playback 默认仅支持显式、有限步数的 `record` 并复用 MuJoCo 离线 renderer;`uv run eval --sim mjwarp --render-mode interactive` 路由到 MuJoCo 交互 viewer(mjwarp 跑物理、MuJoCo 渲染 env[0],强制单 env);不支持 `auto` 或 native playback。其他 entrypoint 中出现的 `Registered` 只表示 env/backend registry identity,不代表对应算法、terrain、完整 DR 或 production training 支持。 +`mjwarp` 完成训练验证的只有 `g1_walk_flat` host adapter:PPO (torch) 与 SAC (torch) owner 已完成训练验证,并有 backend、contract 与 playback 自动化覆盖,因此标记为 `Tested`。mjwarp playback 默认仅支持显式、有限步数的 `record` 并复用 MuJoCo 离线 renderer;`uv run eval --sim mjwarp --render-mode interactive` 路由到 MuJoCo 交互 viewer(mjwarp 跑物理、MuJoCo 渲染 env[0],强制单 env);不支持 `auto` 或 native playback。其他 entrypoint 中出现的 `Registered` 只表示 env/backend registry identity,不代表对应算法、terrain、完整 DR 或 production training 支持。 `isaacgym` 是 Python 3.8 子进程后端,当前只接入 `g1_walk_flat`。SAC (torch) owner 已在真机(external Python 3.8 worker runtime,不在仓库 CI 覆盖)完成训练与 record playback 验证,标记为 `Tested`;其余 isaacgym cell 最高只到 `Configured`(registry + owner YAML + compose/contract 覆盖),不代表任何训练或 play 验证。playback 走 IsaacGym 原生渲染(viewer + camera sensor 离屏录制),有显示器时 `play_render_mode=auto` 打开交互 viewer,无显示器时自动降级为离屏录制。 @@ -95,7 +95,6 @@ uv run scripts/generate_support_matrix.py --write | PPO (torch) | `microduck_velocity_bam_flat` (microduck velocity bam flat) | Tested | - | - | - | - | - | | PPO (torch) | `microduck_velocity_flat` (microduck velocity flat) | Tested | Configured | - | - | - | - | | PPO (torch) | `stewart_balance` (stewart balance) | Tested | - | Tested | - | - | - | -| PPO (torch) | `t800_walk_flat` (t800 walk flat) | Tested | Registered | - | - | - | - | | APPO (torch) | `go1_joystick_flat` (Go1 joystick) | Tested | - | Tested | - | - | - | | APPO (torch) | `go2_joystick_flat` (Go2 joystick) | Tested | - | Tested | - | - | - | | APPO (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Registered | Registered | Registered | Registered | @@ -123,7 +122,6 @@ uv run scripts/generate_support_matrix.py --write | SAC (torch) | `g1_23dof_wbt_obs` (g1 23dof wbt obs) | Tested | - | Registered | - | - | - | | SAC (torch) | `g1_wbt_obs` (g1 wbt obs) | Tested | - | Registered | - | - | - | | SAC (torch) | `microduck_velocity_flat` (microduck velocity flat) | Tested | Configured | - | - | - | - | -| SAC (torch) | `t800_walk_flat` (t800 walk flat) | Tested | Configured | - | - | - | - | | TD3 (torch) | `go1_joystick_flat` (Go1 joystick) | Registered | - | Tested | - | - | - | | TD3 (torch) | `go2_joystick_flat` (Go2 joystick) | Registered | - | Tested | - | - | - | | TD3 (torch) | `g1_walk_flat` (G1 walk flat) | Tested | Registered | Registered | Registered | Registered | Registered | diff --git a/pyproject.toml b/pyproject.toml index 993016312..cccd79d1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,6 @@ source-exclude = [ "/src/unilab/assets/robots/go2_arm/assets", "/src/unilab/assets/robots/microduck/assets", "/src/unilab/assets/robots/sharpa_wave/meshes", - "/src/unilab/assets/robots/t800/assets", - "/src/unilab/assets/robots/t800/textures", "/src/unilab/assets/robots/x2/meshes", ] diff --git a/scripts/tools/support_matrix.py b/scripts/tools/support_matrix.py index ccf61f35c..f06858b4f 100644 --- a/scripts/tools/support_matrix.py +++ b/scripts/tools/support_matrix.py @@ -338,8 +338,6 @@ def render_support_matrix(root: Path | None = None) -> str: "", "`mjwarp` 完成训练验证的只有 `g1_walk_flat` host adapter:PPO (torch) 与 SAC (torch) owner " "已完成训练验证,并有 backend、contract 与 playback 自动化覆盖,因此标记为 `Tested`。" - "SAC `t800_walk_flat` 的 mjwarp owner 只有 owner YAML 与 compose 覆盖,标记为 `Configured`," - "不代表训练验证。" "mjwarp playback 默认仅支持显式、有限步数的 `record` 并复用 MuJoCo 离线 renderer;" "`uv run eval --sim mjwarp --render-mode interactive` 路由到 MuJoCo 交互 viewer" "(mjwarp 跑物理、MuJoCo 渲染 env[0],强制单 env);不支持 `auto` 或 native playback。" diff --git a/src/unilab/assets/hub.py b/src/unilab/assets/hub.py index a6c1af77b..52d8ed1f2 100644 --- a/src/unilab/assets/hub.py +++ b/src/unilab/assets/hub.py @@ -54,10 +54,6 @@ "go2w": (("robots/go2/assets", "base_0.obj", "**/*", "asset"),), "microduck": (("robots/microduck/assets", "trunk_base.stl", "*.stl", "STL"),), "sharpa_wave": (("robots/sharpa_wave/meshes", "DP_HB1_4F.STL", "*.STL", "STL"),), - "t800": ( - ("robots/t800/assets", "LINK_BASE.obj", "*.obj", "OBJ"), - ("robots/t800/textures", "LINK_BASE.png", "*.png", "PNG"), - ), "x2": (("robots/x2/meshes", "pelvis.STL", "*.STL", "STL"),), } diff --git a/src/unilab/assets/robots/t800/LICENSE.engineai.txt b/src/unilab/assets/robots/t800/LICENSE.engineai.txt deleted file mode 100644 index bda48c827..000000000 --- a/src/unilab/assets/robots/t800/LICENSE.engineai.txt +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2025- Shenzhen Zhongqing Robot Technology Co., Ltd. ("EngineAI") -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/unilab/assets/robots/t800/assets/.gitkeep b/src/unilab/assets/robots/t800/assets/.gitkeep deleted file mode 100644 index 8b1378917..000000000 --- a/src/unilab/assets/robots/t800/assets/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/unilab/assets/robots/t800/scene_flat.xml b/src/unilab/assets/robots/t800/scene_flat.xml deleted file mode 100644 index a9b65a910..000000000 --- a/src/unilab/assets/robots/t800/scene_flat.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/unilab/assets/robots/t800/t800.xml b/src/unilab/assets/robots/t800/t800.xml deleted file mode 100644 index a01164189..000000000 --- a/src/unilab/assets/robots/t800/t800.xml +++ /dev/null @@ -1,398 +0,0 @@ - - - diff --git a/src/unilab/assets/robots/t800/textures/.gitkeep b/src/unilab/assets/robots/t800/textures/.gitkeep deleted file mode 100644 index 8b1378917..000000000 --- a/src/unilab/assets/robots/t800/textures/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/unilab/conf/ppo/task/t800_walk_flat/base.yaml b/src/unilab/conf/ppo/task/t800_walk_flat/base.yaml deleted file mode 100644 index f6a8bff38..000000000 --- a/src/unilab/conf/ppo/task/t800_walk_flat/base.yaml +++ /dev/null @@ -1,290 +0,0 @@ -# @package _global_ -# Canonical EngineAI T800 25-DoF walk-flat Manager-Based task declaration. -env: - scene: - model_file: src/unilab/assets/robots/t800/scene_flat.xml - default_keyframe_name: stand - entities: - robot: - root_body_name: LINK_BASE - joint_names: - - J00_HIP_PITCH_L - - J01_HIP_ROLL_L - - J02_HIP_YAW_L - - J03_KNEE_PITCH_L - - J04_ANKLE_PITCH_L - - J05_ANKLE_ROLL_L - - J06_HIP_PITCH_R - - J07_HIP_ROLL_R - - J08_HIP_YAW_R - - J09_KNEE_PITCH_R - - J10_ANKLE_PITCH_R - - J11_ANKLE_ROLL_R - - J12_TORSO_YAW - - J13_SHOULDER_PITCH_L - - J14_SHOULDER_ROLL_L - - J15_SHOULDER_YAW_L - - J16_ELBOW_PITCH_L - - J17_ELBOW_YAW_L - - J18_SHOULDER_PITCH_R - - J19_SHOULDER_ROLL_R - - J20_SHOULDER_YAW_R - - J21_ELBOW_PITCH_R - - J22_ELBOW_YAW_R - - J23_HEAD_PITCH - - J24_HEAD_YAW - actuator_names: - - J00_HIP_PITCH_L - - J01_HIP_ROLL_L - - J02_HIP_YAW_L - - J03_KNEE_PITCH_L - - J04_ANKLE_PITCH_L - - J05_ANKLE_ROLL_L - - J06_HIP_PITCH_R - - J07_HIP_ROLL_R - - J08_HIP_YAW_R - - J09_KNEE_PITCH_R - - J10_ANKLE_PITCH_R - - J11_ANKLE_ROLL_R - - J12_TORSO_YAW - - J13_SHOULDER_PITCH_L - - J14_SHOULDER_ROLL_L - - J15_SHOULDER_YAW_L - - J16_ELBOW_PITCH_L - - J17_ELBOW_YAW_L - - J18_SHOULDER_PITCH_R - - J19_SHOULDER_ROLL_R - - J20_SHOULDER_YAW_R - - J21_ELBOW_PITCH_R - - J22_ELBOW_YAW_R - - J23_HEAD_PITCH - - J24_HEAD_YAW - body_names: [LINK_BASE] - sim_dt: 0.002 - ctrl_dt: 0.01 - max_episode_seconds: 20.0 - observations: - policy: - enable_corruption: true - terms: - base_ang_vel: - func: unilab.envs.mdp.builtin_sensor - params: {sensor_name: torso_gyro} - noise: - _target_: unilab.managers._noise.UniformNoiseCfg - n_min: -0.2 - n_max: 0.2 - operation: add - projected_gravity: - func: unilab.envs.mdp.projected_gravity_from_sensor - params: {sensor_name: torso_upvector} - noise: - _target_: unilab.managers._noise.UniformNoiseCfg - n_min: -0.05 - n_max: 0.05 - operation: add - joint_pos: - func: unilab.envs.mdp.joint_pos_rel - params: - asset_cfg: - _target_: unilab.managers.SceneEntityCfg - name: robot - joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - noise: - _target_: unilab.managers._noise.UniformNoiseCfg - n_min: -0.01 - n_max: 0.01 - operation: add - joint_vel: - func: unilab.envs.mdp.joint_vel_rel - params: - asset_cfg: - _target_: unilab.managers.SceneEntityCfg - name: robot - joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - noise: - _target_: unilab.managers._noise.UniformNoiseCfg - n_min: -1.5 - n_max: 1.5 - operation: add - actions: - func: unilab.envs.mdp.last_action - params: {action_name: joint_pos} - command: - func: unilab.envs.mdp.generated_commands - params: {command_name: twist} - gait_phase: - func: unilab.tasks.locomotion.g1.manager_terms.G1GaitPhase - params: {frequency: 1.5, init_mode: offset_phase} - critic: - terms: - base_ang_vel: - func: unilab.envs.mdp.builtin_sensor - params: {sensor_name: torso_gyro} - projected_gravity: - func: unilab.envs.mdp.projected_gravity_from_sensor - params: {sensor_name: torso_upvector} - joint_pos: - func: unilab.envs.mdp.joint_pos_rel - params: - asset_cfg: - _target_: unilab.managers.SceneEntityCfg - name: robot - joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - joint_vel: - func: unilab.envs.mdp.joint_vel_rel - params: - asset_cfg: - _target_: unilab.managers.SceneEntityCfg - name: robot - joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - actions: - func: unilab.envs.mdp.last_action - params: {action_name: joint_pos} - command: - func: unilab.envs.mdp.generated_commands - params: {command_name: twist} - gait_phase: - func: unilab.tasks.locomotion.g1.manager_terms.G1GaitPhase - params: {frequency: 1.5, init_mode: offset_phase} - base_lin_vel: - func: unilab.envs.mdp.builtin_sensor - params: {sensor_name: pelvis_local_linvel} - policy_observation_group: policy - critic_observation_group: critic - actions: - joint_pos: - _target_: unilab.tasks.locomotion.t800.manager_terms.T800JointPositionActionCfg - entity_name: robot - actuator_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - held_actuator_names: [J12_TORSO_YAW, J23_HEAD_PITCH, J24_HEAD_YAW] - scale: - J00_HIP_PITCH_L: 0.5 - J01_HIP_ROLL_L: 0.2 - J02_HIP_YAW_L: 0.2 - J03_KNEE_PITCH_L: 0.5 - J04_ANKLE_PITCH_L: 0.5 - J05_ANKLE_ROLL_L: 0.2 - J06_HIP_PITCH_R: 0.5 - J07_HIP_ROLL_R: 0.2 - J08_HIP_YAW_R: 0.2 - J09_KNEE_PITCH_R: 0.5 - J10_ANKLE_PITCH_R: 0.5 - J11_ANKLE_ROLL_R: 0.2 - J13_SHOULDER_PITCH_L: 0.2 - J14_SHOULDER_ROLL_L: 0.2 - J15_SHOULDER_YAW_L: 0.05 - J16_ELBOW_PITCH_L: 0.2 - J17_ELBOW_YAW_L: 0.05 - J18_SHOULDER_PITCH_R: 0.2 - J19_SHOULDER_ROLL_R: 0.2 - J20_SHOULDER_YAW_R: 0.05 - J21_ELBOW_PITCH_R: 0.2 - J22_ELBOW_YAW_R: 0.05 - use_default_offset: true - commands: - twist: - _target_: unilab.tasks.locomotion.g1.manager_terms.G1VelocityCommandCfg - entity_name: robot - resampling_time_range: [20.0, 20.0] - heading_command: false - heading_control_stiffness: 0.5 - rel_standing_envs: 0.0 - rel_heading_envs: 0.0 - rel_world_envs: 0.0 - rel_forward_envs: 0.0 - init_velocity_prob: 0.0 - planar_dead_zone: 0.2 - ranges: - lin_vel_x: [-0.6, 1.0] - lin_vel_y: [-0.4, 0.4] - ang_vel_z: [-0.8, 0.8] - events: - reset_scene_to_default: - func: unilab.envs.mdp.reset_scene_to_default - mode: reset - reset_root_state_uniform: - func: unilab.envs.mdp.reset_root_state_uniform - mode: reset - params: - pose_range: - x: [-0.5, 0.5] - y: [-0.5, 0.5] - z: [0.0, 0.0] - roll: [0.0, 0.0] - pitch: [0.0, 0.0] - yaw: [-3.141592653589793, 3.141592653589793] - velocity_range: - x: [-0.5, 0.5] - y: [-0.5, 0.5] - z: [-0.5, 0.5] - roll: [-0.5, 0.5] - pitch: [-0.5, 0.5] - yaw: [-0.5, 0.5] - pd_gains: - func: unilab.envs.mdp.pd_gains - mode: reset - params: - kp_range: [0.9, 1.1] - kd_range: [0.9, 1.1] - operation: scale - terminations: - time_out: - func: unilab.envs.mdp.time_out - time_out: true - tilt: - func: unilab.tasks.locomotion.g1.manager_terms.g1_tilt_exceeded - params: {max_tilt_deg: 25.0} - base_height: - func: unilab.envs.mdp.root_height_below_minimum - params: {minimum_height: 0.7165} - -reward: - tracking_lin_vel: - func: unilab.tasks.locomotion.common.sensor_reward_terms.track_lin_vel - weight: 2.0 - params: {tracking_sigma: 0.25, command_name: twist, sensor_name: pelvis_local_linvel} - tracking_ang_vel: - func: unilab.tasks.locomotion.common.sensor_reward_terms.track_ang_vel - weight: 0.2 - params: {tracking_sigma: 0.25, command_name: twist, sensor_name: torso_gyro} - feet_phase: - func: unilab.tasks.locomotion.g1.manager_terms.feet_phase - weight: 1.5 - params: - frequency: 1.5 - swing_height: 0.13 - tracking_sigma: 0.014 - min_forward_speed: 0.0 - command_name: twist - lin_vel_z: - func: unilab.tasks.locomotion.common.sensor_reward_terms.lin_vel_z - weight: -1.0 - params: {sensor_name: pelvis_local_linvel} - ang_vel_xy: - func: unilab.tasks.locomotion.common.sensor_reward_terms.ang_vel_xy - weight: -0.25 - params: {sensor_name: torso_gyro} - base_height: - func: unilab.tasks.locomotion.common.manager_terms.base_height_l2 - weight: -500.0 - params: {target_height: 1.0165} - orientation: - func: unilab.tasks.locomotion.common.sensor_reward_terms.orientation - weight: -5.0 - params: {sensor_name: torso_upvector} - penalty_feet_ori: - func: unilab.tasks.locomotion.g1.manager_terms.penalty_feet_ori - weight: -10.0 - action_rate: - func: unilab.envs.mdp.action_rate_l2 - weight: -0.01 - pose: - func: unilab.tasks.locomotion.g1.manager_terms.weighted_pose - weight: -0.1 - params: - asset_cfg: - _target_: unilab.managers.SceneEntityCfg - name: robot - joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - pose_weights: [0.01, 1.0, 5.0, 0.01, 5.0, 5.0, 0.01, 1.0, 5.0, 0.01, 5.0, 5.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0] diff --git a/src/unilab/conf/ppo/task/t800_walk_flat/mujoco.yaml b/src/unilab/conf/ppo/task/t800_walk_flat/mujoco.yaml deleted file mode 100644 index b58966d5b..000000000 --- a/src/unilab/conf/ppo/task/t800_walk_flat/mujoco.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# @package _global_ -# MuJoCo owner for the standalone T800 Manager-Based PPO task. -defaults: - - /task/t800_walk_flat/base - - _self_ - -training: - task_name: T800WalkFlat - sim_backend: mujoco - play_steps: 2000 - -algo: - num_envs: 2048 - max_iterations: 5000 - empirical_normalization: false - obs_groups: - actor: - - policy - critic: - - critic - policy: - actor_hidden_dims: [512, 256, 128] - critic_hidden_dims: [512, 256, 128] - -play_profile: - enabled: true - env: - render_spacing: 2.0 diff --git a/src/unilab/conf/sac/task/t800_walk_flat/base.yaml b/src/unilab/conf/sac/task/t800_walk_flat/base.yaml deleted file mode 100644 index 1b3cb6a35..000000000 --- a/src/unilab/conf/sac/task/t800_walk_flat/base.yaml +++ /dev/null @@ -1,111 +0,0 @@ -# @package _global_ -# Standalone EngineAI T800 25-DoF Manager-Based SAC walk-flat owner. -env: - scene: - model_file: src/unilab/assets/robots/t800/scene_flat.xml - default_keyframe_name: stand - entities: - robot: - root_body_name: LINK_BASE - body_names: [LINK_BASE] - joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J12_TORSO_YAW, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R, J23_HEAD_PITCH, J24_HEAD_YAW] - actuator_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J12_TORSO_YAW, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R, J23_HEAD_PITCH, J24_HEAD_YAW] - sim_dt: 0.002 - ctrl_dt: 0.01 - max_episode_seconds: 20.0 - observations: - policy: - enable_corruption: true - terms: - base_ang_vel: {func: unilab.envs.mdp.builtin_sensor, params: {sensor_name: torso_gyro}, scale: 0.25} - projected_gravity: {func: unilab.envs.mdp.projected_gravity_from_sensor, params: {sensor_name: torso_upvector}} - joint_pos: - func: unilab.envs.mdp.joint_pos_rel - params: - asset_cfg: {_target_: unilab.managers.SceneEntityCfg, name: robot, joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R]} - noise: {_target_: unilab.managers._noise.UniformNoiseCfg, n_min: -0.01, n_max: 0.01, operation: add} - joint_vel: - func: unilab.envs.mdp.joint_vel_rel - scale: 0.05 - params: - asset_cfg: {_target_: unilab.managers.SceneEntityCfg, name: robot, joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R]} - noise: {_target_: unilab.managers._noise.UniformNoiseCfg, n_min: -0.1, n_max: 0.1, operation: add} - actions: {func: unilab.envs.mdp.last_action, params: {action_name: joint_pos}} - command: {func: unilab.envs.mdp.generated_commands, params: {command_name: twist}} - gait_phase: {func: unilab.tasks.locomotion.g1.manager_terms.G1GaitPhase, params: {frequency: 1.5, init_mode: offset_phase}} - critic: - enable_corruption: false - terms: - base_ang_vel: {func: unilab.envs.mdp.builtin_sensor, params: {sensor_name: torso_gyro}, scale: 0.25} - projected_gravity: {func: unilab.envs.mdp.projected_gravity_from_sensor, params: {sensor_name: torso_upvector}} - joint_pos: - func: unilab.envs.mdp.joint_pos_rel - params: - asset_cfg: {_target_: unilab.managers.SceneEntityCfg, name: robot, joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R]} - joint_vel: - func: unilab.envs.mdp.joint_vel_rel - scale: 0.05 - params: - asset_cfg: {_target_: unilab.managers.SceneEntityCfg, name: robot, joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R]} - actions: {func: unilab.envs.mdp.last_action, params: {action_name: joint_pos}} - command: {func: unilab.envs.mdp.generated_commands, params: {command_name: twist}} - gait_phase: {func: unilab.tasks.locomotion.g1.manager_terms.G1GaitPhase, params: {frequency: 1.5, init_mode: offset_phase}} - base_lin_vel: {func: unilab.envs.mdp.builtin_sensor, params: {sensor_name: pelvis_local_linvel}, scale: 2.0} - policy_observation_group: policy - critic_observation_group: critic - actions: - joint_pos: - _target_: unilab.tasks.locomotion.t800.manager_terms.T800JointPositionActionCfg - entity_name: robot - actuator_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R] - held_actuator_names: [J12_TORSO_YAW, J23_HEAD_PITCH, J24_HEAD_YAW] - scale: {J00_HIP_PITCH_L: 1.0, J01_HIP_ROLL_L: 1.0, J02_HIP_YAW_L: 1.0, J03_KNEE_PITCH_L: 1.0, J04_ANKLE_PITCH_L: 1.0, J05_ANKLE_ROLL_L: 1.0, J06_HIP_PITCH_R: 1.0, J07_HIP_ROLL_R: 1.0, J08_HIP_YAW_R: 1.0, J09_KNEE_PITCH_R: 1.0, J10_ANKLE_PITCH_R: 1.0, J11_ANKLE_ROLL_R: 1.0, J13_SHOULDER_PITCH_L: 0.2, J14_SHOULDER_ROLL_L: 0.2, J15_SHOULDER_YAW_L: 0.05, J16_ELBOW_PITCH_L: 0.2, J17_ELBOW_YAW_L: 0.05, J18_SHOULDER_PITCH_R: 0.2, J19_SHOULDER_ROLL_R: 0.2, J20_SHOULDER_YAW_R: 0.05, J21_ELBOW_PITCH_R: 0.2, J22_ELBOW_YAW_R: 0.05} - use_default_offset: true - commands: - twist: - _target_: unilab.tasks.locomotion.g1.manager_terms.G1VelocityCommandCfg - entity_name: robot - resampling_time_range: [20.0, 20.0] - heading_command: false - heading_control_stiffness: 0.5 - rel_standing_envs: 0.0 - rel_heading_envs: 0.0 - rel_world_envs: 0.0 - rel_forward_envs: 0.0 - init_velocity_prob: 0.0 - planar_dead_zone: 0.2 - ranges: {lin_vel_x: [-0.6, 1.0], lin_vel_y: [-0.4, 0.4], ang_vel_z: [-0.8, 0.8]} - events: - reset_scene_to_default: {func: unilab.envs.mdp.reset_scene_to_default, mode: reset} - reset_root_state_uniform: - func: unilab.envs.mdp.reset_root_state_uniform - mode: reset - params: - pose_range: {x: [-0.5, 0.5], y: [-0.5, 0.5], z: [0.0, 0.0], roll: [0.0, 0.0], pitch: [0.0, 0.0], yaw: [-3.141592653589793, 3.141592653589793]} - velocity_range: {x: [-0.5, 0.5], y: [-0.5, 0.5], z: [-0.5, 0.5], roll: [-0.5, 0.5], pitch: [-0.5, 0.5], yaw: [-0.5, 0.5]} - pd_gains: {func: unilab.envs.mdp.pd_gains, mode: reset, params: {kp_range: [0.9, 1.1], kd_range: [0.9, 1.1], operation: scale}} - terminations: - time_out: {func: unilab.envs.mdp.time_out, time_out: true} - tilt: {func: unilab.tasks.locomotion.g1.manager_terms.g1_tilt_exceeded, params: {max_tilt_deg: 65.0}} - base_height: {func: unilab.envs.mdp.root_height_below_minimum, params: {minimum_height: 0.3}} - curriculum: - penalty_scaling: - func: unilab.tasks.locomotion.g1.manager_terms.G1PenaltyCurriculum - params: {initial_scale: 0.125, min_scale: 0.125, max_scale: 0.25, level_down_threshold: 150.0, level_up_threshold: 750.0, degree: 0.001} - -reward: - tracking_lin_vel: {func: unilab.tasks.locomotion.common.sensor_reward_terms.track_lin_vel, weight: 2.0, params: {tracking_sigma: 0.25, command_name: twist, sensor_name: pelvis_local_linvel}} - tracking_ang_vel: {func: unilab.tasks.locomotion.common.sensor_reward_terms.track_ang_vel, weight: 1.5, params: {tracking_sigma: 0.25, command_name: twist, sensor_name: torso_gyro}} - penalty_ang_vel_xy: {func: unilab.tasks.locomotion.common.sensor_reward_terms.ang_vel_xy, weight: -1.0, params: {sensor_name: torso_gyro}} - penalty_orientation: {func: unilab.tasks.locomotion.common.sensor_reward_terms.orientation, weight: -10.0, params: {sensor_name: torso_upvector}} - penalty_action_rate: {func: unilab.envs.mdp.action_rate_l2, weight: -4.0} - pose: - func: unilab.tasks.locomotion.g1.manager_terms.weighted_pose - weight: -0.5 - params: - asset_cfg: {_target_: unilab.managers.SceneEntityCfg, name: robot, joint_names: [J00_HIP_PITCH_L, J01_HIP_ROLL_L, J02_HIP_YAW_L, J03_KNEE_PITCH_L, J04_ANKLE_PITCH_L, J05_ANKLE_ROLL_L, J06_HIP_PITCH_R, J07_HIP_ROLL_R, J08_HIP_YAW_R, J09_KNEE_PITCH_R, J10_ANKLE_PITCH_R, J11_ANKLE_ROLL_R, J13_SHOULDER_PITCH_L, J14_SHOULDER_ROLL_L, J15_SHOULDER_YAW_L, J16_ELBOW_PITCH_L, J17_ELBOW_YAW_L, J18_SHOULDER_PITCH_R, J19_SHOULDER_ROLL_R, J20_SHOULDER_YAW_R, J21_ELBOW_PITCH_R, J22_ELBOW_YAW_R]} - pose_weights: [0.01, 2.0, 5.0, 0.01, 5.0, 5.0, 0.01, 2.0, 5.0, 0.01, 5.0, 5.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0] - penalty_feet_ori: {func: unilab.tasks.locomotion.g1.manager_terms.penalty_feet_ori, weight: -20.0} - penalty_close_feet_lateral: {func: unilab.tasks.locomotion.t800.manager_terms.penalty_close_feet_lateral, weight: -5.0, params: {min_width: 0.18, sigma: 0.04}} - feet_phase: {func: unilab.tasks.locomotion.g1.manager_terms.feet_phase, weight: 5.0, params: {frequency: 1.5, swing_height: 0.11, tracking_sigma: 0.014, min_forward_speed: 0.0, command_name: twist}} - alive: {func: unilab.tasks.locomotion.common.manager_terms.alive, weight: 10.0} diff --git a/src/unilab/conf/sac/task/t800_walk_flat/mjwarp.yaml b/src/unilab/conf/sac/task/t800_walk_flat/mjwarp.yaml deleted file mode 100644 index b30906415..000000000 --- a/src/unilab/conf/sac/task/t800_walk_flat/mjwarp.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# @package _global_ -# Configured-only SAC mjwarp owner for the T800 Manager-Based task. Keeps -# DENYLIST parity with the MuJoCo owner plus the mjwarp capacity knobs; kp/kd -# randomization is disabled because the mjwarp backend does not advertise gain -# DR support. Offline record reuses MuJoCo rendering; interactive eval routes -# through the MuJoCo viewer; native playback and device-resident runtime are -# intentionally absent. -defaults: - - /task/t800_walk_flat/base - - _self_ - -training: - task_name: T800WalkFlat - sim_backend: mjwarp - play_render_mode: record -algo: - num_envs: 2048 - max_iterations: 10000 - save_interval: 1000 - learning_starts: 10 - updates_per_step: 8 - gamma: 0.98488578 - algo_params: - alpha_init: 0.001 - target_entropy_ratio: 0.0 -env: - mjwarp_nconmax: 128 - mjwarp_njmax: 256 - render_spacing: 2.0 - events: - # mjwarp does not advertise kp/kd gain DR support. - pd_gains: null diff --git a/src/unilab/conf/sac/task/t800_walk_flat/mujoco.yaml b/src/unilab/conf/sac/task/t800_walk_flat/mujoco.yaml deleted file mode 100644 index 84a26f846..000000000 --- a/src/unilab/conf/sac/task/t800_walk_flat/mujoco.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# @package _global_ -# MuJoCo owner for the standalone T800 Manager-Based SAC task. -defaults: - - /task/t800_walk_flat/base - - _self_ - -training: - task_name: T800WalkFlat - sim_backend: mujoco - -algo: - num_envs: 2048 - max_iterations: 100000 - save_interval: 1000 - learning_starts: 10 - updates_per_step: 8 - gamma: 0.98488578 - algo_params: - alpha_init: 0.001 - target_entropy_ratio: 0.0 diff --git a/src/unilab/tasks/__init__.py b/src/unilab/tasks/__init__.py index 5d1f94783..f6bc3beb1 100644 --- a/src/unilab/tasks/__init__.py +++ b/src/unilab/tasks/__init__.py @@ -13,7 +13,6 @@ "unilab.tasks.locomotion.go2_arm", "unilab.tasks.locomotion.a2", "unilab.tasks.locomotion.microduck", - "unilab.tasks.locomotion.t800", "unilab.tasks.manipulation.allegro_inhand", "unilab.tasks.manipulation.sharpa_inhand", "unilab.tasks.manipulation.stewart", diff --git a/src/unilab/tasks/locomotion/t800/__init__.py b/src/unilab/tasks/locomotion/t800/__init__.py deleted file mode 100644 index 5262d9a2e..000000000 --- a/src/unilab/tasks/locomotion/t800/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""EngineAI T800 walk-flat task on the shared Manager-Based runtime.""" - -from unilab.assets.hub import resolve_robot_asset_dir -from unilab.base import registry -from unilab.envs import ManagerBasedRlEnv, ManagerBasedRlEnvCfg, make_manager_based_rl_env - - -def make_t800_walk_env( - cfg: ManagerBasedRlEnvCfg, - num_envs: int = 1, - backend_type: str = "mujoco", -) -> ManagerBasedRlEnv: - """Resolve T800 binary assets before materializing the generic runtime.""" - resolve_robot_asset_dir("robots/t800/assets", marker="LINK_BASE.obj") - resolve_robot_asset_dir("robots/t800/textures", marker="LINK_BASE.png") - return make_manager_based_rl_env(cfg, num_envs=num_envs, backend_type=backend_type) - - -registry.register_env_config("T800WalkFlat", ManagerBasedRlEnvCfg) -registry.register_env("T800WalkFlat", make_t800_walk_env, sim_backend="mujoco") -registry.register_env("T800WalkFlat", make_t800_walk_env, sim_backend="mjwarp") - - -__all__ = ["make_t800_walk_env"] diff --git a/src/unilab/tasks/locomotion/t800/manager_terms.py b/src/unilab/tasks/locomotion/t800/manager_terms.py deleted file mode 100644 index 8092abcb6..000000000 --- a/src/unilab/tasks/locomotion/t800/manager_terms.py +++ /dev/null @@ -1,169 +0,0 @@ -"""T800-specific terms for the Manager-Based locomotion environment.""" - -from __future__ import annotations - -import math -from dataclasses import dataclass -from numbers import Real -from typing import TYPE_CHECKING, Any - -import numpy as np - -from unilab.envs.mdp import JointPositionAction, JointPositionActionCfg -from unilab.managers.manager_base import ManagerTermBaseCfg -from unilab.tasks.locomotion.common.manager_terms import SensorTermBase -from unilab.utils.rotation import np_quat_apply_inverse, np_yaw_quat - -if TYPE_CHECKING: - from collections.abc import Sequence - - from unilab.managers._types import ManagerBasedRlEnv - - -_LATERAL_FOOT_SENSORS = ( - "left_foot_pos", - "right_foot_pos", - "base_link_quaternion", -) - - -def _finite_real(value: Any, *, label: str, strict_positive: bool = False) -> float: - if isinstance(value, (bool, np.bool_)) or not isinstance(value, Real): - raise TypeError(f"{label} must be a real number") - result = float(value) - if not math.isfinite(result): - raise ValueError(f"{label} must be finite") - if result < 0.0 or (strict_positive and result == 0.0): - relation = "greater than zero" if strict_positive else "non-negative" - raise ValueError(f"{label} must be {relation}") - return result - - -def compute_lateral_feet_penalty( - left_foot: np.ndarray, - right_foot: np.ndarray, - base_quat: np.ndarray, - min_width: float = 0.20, - sigma: float = 0.04, -) -> np.ndarray: - """Penalize signed foot separation below a minimum heading-frame width.""" - separation_w = left_foot - right_foot - separation_heading = np_quat_apply_inverse(np_yaw_quat(base_quat), separation_w) - signed_width = separation_heading[:, 1] - deficit = np.maximum(min_width - signed_width, 0.0) - return 1.0 - np.exp(-np.square(deficit / sigma)) - - -def _validate_unique_selectors(label: str, selectors: Sequence[str]) -> None: - if len(selectors) != len(set(selectors)): - raise ValueError(f"T800JointPositionAction {label} selectors contain duplicates") - - -@dataclass(kw_only=True) -class T800JointPositionActionCfg(JointPositionActionCfg): - """Control selected joints while holding the remaining T800 joints.""" - - held_actuator_names: tuple[str, ...] | list[str] - - def build(self, env: ManagerBasedRlEnv) -> "T800JointPositionAction": - return T800JointPositionAction(self, env) - - -class T800JointPositionAction(JointPositionAction): - """Joint-position action with cold-path-resolved non-policy hold targets.""" - - def __init__(self, cfg: T800JointPositionActionCfg, env: ManagerBasedRlEnv): - _validate_unique_selectors("active", cfg.actuator_names) - _validate_unique_selectors("held", cfg.held_actuator_names) - - super().__init__(cfg, env) - active_ids, _ = self._entity.find_joints_by_actuator_names(cfg.actuator_names) - held_ids, _ = self._entity.find_joints_by_actuator_names(cfg.held_actuator_names) - if len(set(active_ids)) != len(active_ids) or len(set(held_ids)) != len(held_ids): - raise ValueError("T800JointPositionAction resolved joint IDs must be unique") - if set(active_ids) & set(held_ids): - raise ValueError("T800JointPositionAction active and held joints overlap") - if set(active_ids) | set(held_ids) != set(range(self._entity.num_joints)): - raise ValueError( - "T800JointPositionAction active and held joints must form a complete partition" - ) - - self._held_ids = np.asarray(held_ids, dtype=np.intp) - self._held_ids.setflags(write=False) - held_shape = (self.num_envs, self._held_ids.size) - self._held_default_targets = np.empty( - held_shape, dtype=self._entity.data.default_joint_pos.dtype - ) - self._held_encoder_bias = np.empty(held_shape, dtype=self._entity.data.encoder_bias.dtype) - - def apply_actions(self) -> None: - super().apply_actions() - np.take( - self._entity.data.default_joint_pos, - self._held_ids, - axis=1, - out=self._held_default_targets, - ) - np.take( - self._entity.data.encoder_bias, - self._held_ids, - axis=1, - out=self._held_encoder_bias, - ) - np.subtract( - self._held_default_targets, - self._held_encoder_bias, - out=self._held_default_targets, - ) - self._entity.set_joint_position_target( - self._held_default_targets, - joint_ids=self._held_ids, - ) - - -class penalty_close_feet_lateral(SensorTermBase): - """Penalty for insufficient signed lateral foot separation.""" - - _allowed_params = frozenset({"min_width", "sigma"}) - - def __init__(self, cfg: ManagerTermBaseCfg, env: ManagerBasedRlEnv): - super().__init__(cfg, env) - self._min_width = _finite_real( - cfg.params.get("min_width", 0.20), label=f"{self.name} min_width" - ) - self._sigma = _finite_real( - cfg.params.get("sigma", 0.04), - label=f"{self.name} sigma", - strict_positive=True, - ) - self._sensor_view = self._bind(_LATERAL_FOOT_SENSORS) - if self._sensor_view.dimensions != (3, 3, 4): - raise ValueError( - f"{self.name} sensor dimensions must be (3, 3, 4); received " - f"{self._sensor_view.dimensions} on backend " - f"'{self._sensor_view.backend_type}'" - ) - - def __call__(self, env: ManagerBasedRlEnv, **params: Any) -> np.ndarray: - del params - values = self._read(self._sensor_view, self.name) - expected_shape = (env.num_envs, 10) - if values.shape != expected_shape: - raise ValueError( - f"{self.name} sensor view must have shape {expected_shape}, got {values.shape}" - ) - return compute_lateral_feet_penalty( - values[:, 0:3], - values[:, 3:6], - values[:, 6:10], - min_width=self._min_width, - sigma=self._sigma, - ) - - -__all__ = [ - "T800JointPositionAction", - "T800JointPositionActionCfg", - "compute_lateral_feet_penalty", - "penalty_close_feet_lateral", -] diff --git a/src/unilab/tasks/migration_matrix.py b/src/unilab/tasks/migration_matrix.py index 2c8ae4303..8c61bc0fe 100644 --- a/src/unilab/tasks/migration_matrix.py +++ b/src/unilab/tasks/migration_matrix.py @@ -58,7 +58,6 @@ class TaskMigrationRecord: { "MicroduckVelocityBamFlat", "MicroduckVelocityFlat", - "T800WalkFlat", } ) @@ -172,7 +171,6 @@ def migration_record(task_name: str) -> TaskMigrationRecord: "manager terms and owner configuration.", ) if task_name in _EXTERNAL_ASSET_LOCOMOTION_TASKS: - family = "t800_locomotion" if task_name == "T800WalkFlat" else "microduck_locomotion" scope = ( "Hydra owner YAML materializes the canonical NumPy Manager-Based runtime." if task_name == "MicroduckVelocityFlat" @@ -187,7 +185,7 @@ def migration_record(task_name: str) -> TaskMigrationRecord: ) return TaskMigrationRecord( task_name, - family, + "microduck_locomotion", "Compatible", "complete", scope, diff --git a/tests/assets/test_hub.py b/tests/assets/test_hub.py index 7a0754c6c..6f86d9d1e 100644 --- a/tests/assets/test_hub.py +++ b/tests/assets/test_hub.py @@ -353,7 +353,6 @@ def test_robot_asset_specs_cover_hf_hosted_robots(): "go2w", "microduck", "sharpa_wave", - "t800", "x2", } assert set(ROBOT_ASSET_SPECS) == expected diff --git a/tests/envs/locomotion/t800/test_t800_asset_contract.py b/tests/envs/locomotion/t800/test_t800_asset_contract.py deleted file mode 100644 index a66155d6a..000000000 --- a/tests/envs/locomotion/t800/test_t800_asset_contract.py +++ /dev/null @@ -1,374 +0,0 @@ -"""Asset-level contracts for the EngineAI T800 walk-flat model.""" - -from __future__ import annotations - -import hashlib -import xml.etree.ElementTree as ET -from pathlib import Path - -import numpy as np -import pytest -from unisim.backend.mujoco.xml import create_discardvisual_xml - -from unilab.assets import ASSETS_ROOT_PATH -from unilab.assets.hub import resolve_robot_asset_dir - -ROBOT_XML = ASSETS_ROOT_PATH / "robots" / "t800" / "t800.xml" -SCENE_XML = ASSETS_ROOT_PATH / "robots" / "t800" / "scene_flat.xml" -G1_ROBOT_XML = ASSETS_ROOT_PATH / "robots" / "g1" / "g1.xml" -G1_SCENE_XML = ASSETS_ROOT_PATH / "robots" / "g1" / "scene_flat.xml" -TEXTURES_DIR = ROBOT_XML.parent / "textures" -ENGINEAI_LICENSE = ROBOT_XML.parent / "LICENSE.engineai.txt" - -EXPECTED_TEXTURES = { - "LINK_BASE": ( - "LINK_BASE.png", - "a298400671d0868d8a404801e280d2f9bd01b33ec76660354192baba7a9b12e0", - ), - "LINK_HIP_PITCH": ( - "LINK_HIP_PITCH_L.png", - "192c9753ad475eb1cd631c2de474e3f644dd2f1189a40ed25759299321265b08", - ), - "LINK_HIP_ROLL": ( - "LINK_HIP_ROLL_L.png", - "f123c3a3fed080c94c0296618e8a6712f7da7234a9a8611be3e08a7972fb58ab", - ), - "LINK_HIP_YAW": ( - "LINK_HIP_YAW_L.png", - "97b5f5c52a05f8315ed07c531f5a8834e92fbae0f89cd21853428ae858eacc9a", - ), - "LINK_KNEE_PITCH": ( - "LINK_KNEE_PITCH_L.png", - "ce66f28fc5dac742c73e9b4dfe735e20d2b89641afb4d6e033292a7e41a2de85", - ), - "LINK_ANKLE_ROLL": ( - "LINK_ANKLE_ROLL_L.png", - "c69f48f543389e72954d8ede6d93a9d4e96441576f3e35639454138d70b476b2", - ), - "LINK_TORSO_YAW": ( - "LINK_TORSO_YAW.png", - "b7f88ec8e9276ac9c62e19d52037ee30cbc4eefee44e36ca46974711cd8430f4", - ), - "LINK_SHOULDER_PITCH": ( - "LINK_SHOULDER_PITCH_L.png", - "6bb410836ee6431783ee0f69429f9853c14e884f069b6466b9d4b8bda0a95fea", - ), - "LINK_SHOULDER_ROLL": ( - "LINK_SHOULDER_ROLL_L.png", - "fb76b91c8155478cf6d92088d89914a6eb1b907f78a1dcf18128fffee8618311", - ), - "LINK_SHOULDER_YAW": ( - "LINK_SHOULDER_YAW_L.png", - "e7778698452bedb342bbaf198e5c782e17f1d2a9e10f94e2721b8a01fb815737", - ), - "LINK_ELBOW_PITCH_L": ( - "LINK_ELBOW_PITCH_L.png", - "cb9c31e56644890ba5e3c356bcc1ad266a3cf459104a82505aedd62efb66f260", - ), - "LINK_ELBOW_PITCH_R": ( - "LINK_ELBOW_PITCH_R.png", - "8aa0572b3ef8e819f69937d4be0536e0854dc6ef39bd821668dea9b73b0f270c", - ), - "LINK_ELBOW_YAW_L": ( - "LINK_ELBOW_YAW_L.png", - "672b2be4bcdce1863424685a1f681f0d7b23d143fd910311d833268e1246c05c", - ), - "LINK_ELBOW_YAW_R": ( - "LINK_ELBOW_YAW_R.png", - "6283738a1be141cdb676ad3b93cfc2145bcf1f4c7ae39ce8f886fcdcaa3beee6", - ), - "LINK_HEAD_YAW": ( - "LINK_HEAD_YAW.png", - "65fc0f09e4bd0bfc5c910e2e7267ff598a1cf13f85845b56d51055150d53765f", - ), -} - -JOINT_NAMES = ( - "J00_HIP_PITCH_L", - "J01_HIP_ROLL_L", - "J02_HIP_YAW_L", - "J03_KNEE_PITCH_L", - "J04_ANKLE_PITCH_L", - "J05_ANKLE_ROLL_L", - "J06_HIP_PITCH_R", - "J07_HIP_ROLL_R", - "J08_HIP_YAW_R", - "J09_KNEE_PITCH_R", - "J10_ANKLE_PITCH_R", - "J11_ANKLE_ROLL_R", - "J12_TORSO_YAW", - "J13_SHOULDER_PITCH_L", - "J14_SHOULDER_ROLL_L", - "J15_SHOULDER_YAW_L", - "J16_ELBOW_PITCH_L", - "J17_ELBOW_YAW_L", - "J18_SHOULDER_PITCH_R", - "J19_SHOULDER_ROLL_R", - "J20_SHOULDER_YAW_R", - "J21_ELBOW_PITCH_R", - "J22_ELBOW_YAW_R", - "J23_HEAD_PITCH", - "J24_HEAD_YAW", -) -EXPECTED_KP = np.asarray( - [ - 180, - 100, - 100, - 180, - 40, - 40, - 180, - 100, - 100, - 180, - 40, - 40, - 100, - 60, - 50, - 50, - 60, - 50, - 60, - 50, - 50, - 60, - 50, - 100, - 100, - ], - dtype=np.float64, -) -EXPECTED_KD = np.asarray( - [ - 5, - 3, - 3, - 5, - 0.3, - 0.3, - 5, - 3, - 3, - 5, - 0.3, - 0.3, - 5, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 1, - 1, - ], - dtype=np.float64, -) -EXPECTED_FORCE_LIMITS = np.asarray( - [ - 415, - 370, - 222, - 415, - 160, - 160, - 415, - 370, - 222, - 415, - 160, - 160, - 222, - 160, - 160, - 160, - 160, - 52, - 160, - 160, - 160, - 160, - 52, - 52, - 52, - ], - dtype=np.float64, -) - - -@pytest.fixture(scope="module", autouse=True) -def _resolve_t800_binary_assets() -> None: - resolve_robot_asset_dir("robots/t800/assets", marker="LINK_BASE.obj") - resolve_robot_asset_dir("robots/t800/textures", marker="LINK_BASE.png") - - -def _sha256(path: Path) -> str: - return hashlib.sha256(path.read_bytes()).hexdigest() - - -def _required_attrib(root: ET.Element, xpath: str) -> dict[str, str]: - element = root.find(xpath) - assert element is not None - return element.attrib - - -def test_t800_robot_materials_bind_original_textures(): - mujoco = pytest.importorskip("mujoco") - root = ET.parse(ROBOT_XML).getroot() - expected_files = { - material_name: f"textures/{file_name}" - for material_name, (file_name, _) in EXPECTED_TEXTURES.items() - } - material_textures = { - element.get("name"): element.get("texture") - for element in root.findall("./asset/material") - if element.get("name") in EXPECTED_TEXTURES - } - texture_files = { - element.get("name"): element.get("file") - for element in root.findall("./asset/texture") - if element.get("name") in EXPECTED_TEXTURES - } - - assert material_textures == {name: name for name in EXPECTED_TEXTURES} - assert texture_files == expected_files - assert {path.name for path in TEXTURES_DIR.glob("*.png")} == { - file_name for file_name, _ in EXPECTED_TEXTURES.values() - } - for file_name, expected_digest in EXPECTED_TEXTURES.values(): - assert _sha256(TEXTURES_DIR / file_name) == expected_digest - - model = mujoco.MjModel.from_xml_path(str(ROBOT_XML)) - rgb_role = int(mujoco.mjtTextureRole.mjTEXROLE_RGB) - for material_name in EXPECTED_TEXTURES: - material_id = mujoco.mj_name2id( - model, - mujoco.mjtObj.mjOBJ_MATERIAL, - material_name, - ) - assert material_id >= 0 - assert model.mat_texid[material_id, rgb_role] >= 0 - - -def test_t800_vendored_assets_include_engineai_license(): - assert ENGINEAI_LICENSE.is_file() - assert _sha256(ENGINEAI_LICENSE) == ( - "a3e5f08bf7ae0983cc7a9f602e9a555f5956a8dade26cf4cf9cc95c441fe0b6c" - ) - - -def test_t800_flat_scene_matches_g1_ground_visual_contract(): - t800_scene = ET.parse(SCENE_XML).getroot() - g1_scene = ET.parse(G1_SCENE_XML).getroot() - - for xpath in ( - "./visual/headlight", - "./visual/rgba", - "./visual/global", - "./asset/texture[@type='skybox']", - "./asset/texture[@name='groundplane']", - "./asset/material[@name='groundplane']", - "./worldbody/geom[@name='floor']", - ): - assert _required_attrib(t800_scene, xpath) == _required_attrib(g1_scene, xpath) - - g1_robot = ET.parse(G1_ROBOT_XML).getroot() - expected_light = _required_attrib(g1_robot, "./worldbody/light") - t800_lights = t800_scene.findall("./worldbody/light") - assert [light.attrib for light in t800_lights] == [expected_light] - assert ET.parse(ROBOT_XML).getroot().find("./worldbody/light") is None - - -def test_t800_robot_xml_is_robot_only_and_uses_25_position_actuators(): - mujoco = pytest.importorskip("mujoco") - root = ET.parse(ROBOT_XML).getroot() - assert root.find(".//keyframe") is None - - model = mujoco.MjModel.from_xml_path(str(ROBOT_XML)) - assert (model.nq, model.nv, model.nu) == (32, 31, 25) - names = tuple( - mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_ACTUATOR, actuator_id) - for actuator_id in range(model.nu) - ) - assert names == JOINT_NAMES - - affine = int(mujoco.mjtBias.mjBIAS_AFFINE) - assert np.all(model.actuator_biastype == affine) - np.testing.assert_allclose(model.actuator_gainprm[:, 0], EXPECTED_KP) - np.testing.assert_allclose(model.actuator_biasprm[:, 2], -EXPECTED_KD) - np.testing.assert_allclose(model.actuator_forcerange[:, 0], -EXPECTED_FORCE_LIMITS) - np.testing.assert_allclose(model.actuator_forcerange[:, 1], EXPECTED_FORCE_LIMITS) - np.testing.assert_allclose(model.actuator_ctrlrange, model.jnt_range[1:]) - - -def test_t800_flat_scene_has_walk_sensors_and_stand_keyframe(): - mujoco = pytest.importorskip("mujoco") - model = mujoco.MjModel.from_xml_path(str(SCENE_XML)) - - sensor_names = { - mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_SENSOR, sensor_id) - for sensor_id in range(model.nsensor) - } - required_sensors = { - "pelvis_local_linvel", - "torso_gyro", - "torso_upvector", - "left_foot_pos", - "left_foot_quat", - "right_foot_pos", - "right_foot_quat", - *(f"left_foot_contact_{index}" for index in range(4)), - *(f"right_foot_contact_{index}" for index in range(4)), - } - assert required_sensors <= sensor_names - - key_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_KEY, "stand") - assert key_id >= 0 - assert model.key_qpos[key_id].shape == (32,) - assert model.key_ctrl[key_id].shape == (25,) - - -def test_t800_training_compile_discards_visual_meshes(): - mujoco = pytest.importorskip("mujoco") - training_xml = Path(create_discardvisual_xml(str(SCENE_XML))) - try: - model = mujoco.MjModel.from_xml_path(str(training_xml)) - finally: - training_xml.unlink(missing_ok=True) - - assert model.nmesh == 0 - assert model.ntex == 0 - assert np.all(model.geom_type != int(mujoco.mjtGeom.mjGEOM_MESH)) - - -def test_t800_stand_foot_sites_track_the_collision_sole_height(): - mujoco = pytest.importorskip("mujoco") - model = mujoco.MjModel.from_xml_path(str(SCENE_XML)) - data = mujoco.MjData(model) - key_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_KEY, "stand") - mujoco.mj_resetDataKeyframe(model, data, key_id) - mujoco.mj_forward(model, data) - - for side in ("left", "right"): - site_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_SITE, f"{side}_foot") - sole_heights = [] - for index in range(4): - geom_id = mujoco.mj_name2id( - model, - mujoco.mjtObj.mjOBJ_GEOM, - f"{side}_foot_contact_{index}_geom", - ) - rotation = data.geom_xmat[geom_id].reshape(3, 3) - vertical_extent = np.abs(rotation[2]) @ model.geom_size[geom_id] - sole_heights.append(data.geom_xpos[geom_id, 2] - vertical_extent) - - site_height = data.site_xpos[site_id, 2] - lowest_collision = min(sole_heights) - assert site_height >= lowest_collision - 1.0e-3 - assert site_height - lowest_collision < 5.0e-3 diff --git a/tests/envs/locomotion/t800/test_t800_manager_terms.py b/tests/envs/locomotion/t800/test_t800_manager_terms.py deleted file mode 100644 index d147766bb..000000000 --- a/tests/envs/locomotion/t800/test_t800_manager_terms.py +++ /dev/null @@ -1,305 +0,0 @@ -"""Unit contracts for T800-specific Manager-Based terms.""" - -from __future__ import annotations - -from types import SimpleNamespace -from typing import Any, cast - -import numpy as np -import pytest -from unisim.backend.base import SimBackend - -from unilab.base.entity import EntityCfg, EntityScene -from unilab.managers import RewardTermCfg -from unilab.managers._types import ManagerBasedRlEnv -from unilab.tasks.locomotion.t800.manager_terms import ( - T800JointPositionActionCfg, - compute_lateral_feet_penalty, - penalty_close_feet_lateral, -) - - -class _ActionBackend: - backend_type = "fake" - num_envs = 2 - num_actuators = 3 - - def __init__(self) -> None: - self.actuator_names = ("knee_motor", "hip_motor", "head_motor") - self.target_joint_names = ("knee", "hip", "head") - self.joint_index = {"hip": 0, "knee": 1, "head": 2} - self.dof_pos = np.zeros((self.num_envs, 3), dtype=np.float32) - - def get_actuator_names(self) -> tuple[str, ...]: - return self.actuator_names - - def get_actuator_joint_names(self) -> tuple[str, ...]: - return self.target_joint_names - - def get_actuator_ctrl_range(self) -> np.ndarray: - return np.tile(np.asarray([[-10.0, 10.0]], dtype=np.float32), (3, 1)) - - def get_joint_dof_pos_indices(self, names: list[str]) -> np.ndarray: - return np.asarray([self.joint_index[name] for name in names], dtype=np.int32) - - def get_joint_dof_vel_indices(self, names: list[str]) -> np.ndarray: - return self.get_joint_dof_pos_indices(names) - - def get_dof_pos(self) -> np.ndarray: - return self.dof_pos - - def get_dof_vel(self) -> np.ndarray: - return np.zeros_like(self.dof_pos) - - def get_default_dof_pos(self) -> np.ndarray: - return np.asarray([0.1, 0.2, 0.3], dtype=np.float32) - - def get_joint_range(self) -> np.ndarray: - return np.tile(np.asarray([[-1.0, 1.0]], dtype=np.float32), (3, 1)) - - -def _action( - *, - active: tuple[str, ...] = ("hip", "knee"), - held: tuple[str, ...] = ("head",), - clip: dict[str, tuple[float, float]] | None = None, -) -> tuple[Any, np.ndarray, EntityScene]: - backend = _ActionBackend() - control = np.zeros((backend.num_envs, backend.num_actuators), dtype=np.float32) - scene = EntityScene( - { - "robot": EntityCfg( - joint_names=("hip", "knee", "head"), - actuator_names=backend.actuator_names, - ) - }, - cast(SimBackend, backend), - control, - ) - env = cast( - ManagerBasedRlEnv, - SimpleNamespace(num_envs=backend.num_envs, scene=scene), - ) - scale = {name: {"hip": 2.0, "knee": 3.0}.get(name, 1.0) for name in active} - cfg = T800JointPositionActionCfg( - entity_name="robot", - actuator_names=active, - held_actuator_names=held, - scale=scale, - use_default_offset=True, - clip=clip, - ) - return cfg.build(env), control, scene - - -def test_t800_action_writes_active_targets_and_holds_non_policy_defaults() -> None: - action, control, scene = _action() - scene["robot"].data.encoder_bias[:] = np.asarray([[0.01, 0.02, 0.03]]) - raw = np.asarray([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32) - - action.process_actions(raw) - action.apply_actions() - - assert action.action_dim == 2 - assert action.target_names == ["hip", "knee"] - np.testing.assert_allclose(action.processed_action, raw * [2.0, 3.0] + [0.1, 0.2]) - np.testing.assert_allclose(control[:, 1], action.processed_action[:, 0] - 0.01) - np.testing.assert_allclose(control[:, 0], action.processed_action[:, 1] - 0.02) - np.testing.assert_allclose(control[:, 2], 0.3 - 0.03) - - -def test_t800_action_clips_active_targets_before_mapping_and_keeps_held_target() -> None: - action, control, scene = _action(clip={"hip": (-1.0, 1.0), "knee": (-2.0, 2.0)}) - scene["robot"].data.encoder_bias[:] = np.asarray( - [[0.01, 0.02, 0.03], [0.04, 0.05, 0.06]], dtype=np.float32 - ) - raw = np.asarray([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32) - - action.process_actions(raw) - action.apply_actions() - - np.testing.assert_allclose(action.processed_action, [[1.0, 2.0], [1.0, 2.0]]) - np.testing.assert_allclose(control[:, 1], [1.0 - 0.01, 1.0 - 0.04]) - np.testing.assert_allclose(control[:, 0], [2.0 - 0.02, 2.0 - 0.05]) - np.testing.assert_allclose(control[:, 2], [0.3 - 0.03, 0.3 - 0.06]) - - -@pytest.mark.parametrize( - ("active", "held", "message"), - [ - (("hip", "head"), ("head",), "overlap"), - (("hip",), ("head",), "partition"), - (("hip", "hip"), ("knee", "head"), "duplicate"), - (("hip", "knee"), ("head", "head"), "duplicate"), - (("hip", "knee"), ("missing",), "match"), - ], -) -def test_t800_action_partition_fails_closed( - active: tuple[str, ...], - held: tuple[str, ...], - message: str, -) -> None: - with pytest.raises(ValueError, match=message): - _action(active=active, held=held) - - -class _LateralScene: - def __init__(self, sensor_data: dict[str, np.ndarray]) -> None: - self.sensor_data = sensor_data - self.bound_names: tuple[str, ...] | None = None - - def bind_sensor_data(self, names: tuple[str, ...]): - self.bound_names = tuple(names) - arrays = [self.sensor_data[name] for name in names] - return SimpleNamespace( - dimensions=tuple(array.shape[1] for array in arrays), - backend_type="fake", - read=lambda: np.concatenate(arrays, axis=1), - ) - - -def _lateral_env( - *, - left: np.ndarray | None = None, - right: np.ndarray | None = None, - base_quat: np.ndarray | None = None, -) -> tuple[ManagerBasedRlEnv, _LateralScene]: - left = ( - np.asarray([[0.0, 0.05, 0.0], [0.0, -0.10, 0.0]], dtype=np.float32) - if left is None - else left - ) - right = ( - np.asarray([[0.0, -0.05, 0.0], [0.0, 0.10, 0.0]], dtype=np.float32) - if right is None - else right - ) - base_quat = ( - np.asarray([[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]], dtype=np.float32) - if base_quat is None - else base_quat - ) - scene = _LateralScene( - { - "left_foot_pos": left, - "right_foot_pos": right, - "base_link_quaternion": base_quat, - } - ) - env = cast(ManagerBasedRlEnv, SimpleNamespace(num_envs=left.shape[0], scene=scene)) - return env, scene - - -def _lateral_term(env: ManagerBasedRlEnv, **params: Any) -> penalty_close_feet_lateral: - return penalty_close_feet_lateral( - RewardTermCfg( - func=penalty_close_feet_lateral, - weight=-1.0, - params={"min_width": 0.20, "sigma": 0.04, **params}, - ), - env, - ) - - -def test_lateral_penalty_is_invariant_to_fore_aft_foot_offsets() -> None: - left = np.asarray([[0.0, 0.05, 0.0], [3.0, 0.05, 1.0]], dtype=np.float32) - right = np.asarray([[0.0, -0.05, 0.0], [-4.0, -0.05, -2.0]], dtype=np.float32) - quat = np.asarray([[1.0, 0.0, 0.0, 0.0]] * 2, dtype=np.float32) - - penalty = compute_lateral_feet_penalty(left, right, quat, min_width=0.20, sigma=0.04) - - assert penalty[0] == pytest.approx(penalty[1]) - - -def test_lateral_penalty_is_invariant_to_world_yaw() -> None: - local_left = np.asarray([[1.0, 0.05, 0.0]], dtype=np.float32) - local_right = np.asarray([[-2.0, -0.05, 0.0]], dtype=np.float32) - yaw_90_left = np.asarray([[-0.05, 1.0, 0.0]], dtype=np.float32) - yaw_90_right = np.asarray([[0.05, -2.0, 0.0]], dtype=np.float32) - identity = np.asarray([[1.0, 0.0, 0.0, 0.0]], dtype=np.float32) - yaw_90 = np.asarray([[np.sqrt(0.5), 0.0, 0.0, np.sqrt(0.5)]], dtype=np.float32) - - local_penalty = compute_lateral_feet_penalty( - local_left, local_right, identity, min_width=0.20, sigma=0.04 - ) - yawed_penalty = compute_lateral_feet_penalty( - yaw_90_left, yaw_90_right, yaw_90, min_width=0.20, sigma=0.04 - ) - - np.testing.assert_allclose(yawed_penalty, local_penalty, atol=1.0e-6) - - -def test_lateral_penalty_is_zero_at_or_above_minimum_width() -> None: - left = np.asarray([[0.0, 0.10, 0.0], [0.0, 0.20, 0.0]], dtype=np.float32) - right = np.asarray([[0.0, -0.10, 0.0], [0.0, -0.10, 0.0]], dtype=np.float32) - quat = np.asarray([[1.0, 0.0, 0.0, 0.0]] * 2, dtype=np.float32) - - penalty = compute_lateral_feet_penalty(left, right, quat, min_width=0.20, sigma=0.04) - - np.testing.assert_array_equal(penalty, 0.0) - - -def test_lateral_penalty_is_near_one_for_crossed_feet() -> None: - left = np.asarray([[0.0, -0.10, 0.0]], dtype=np.float32) - right = np.asarray([[0.0, 0.10, 0.0]], dtype=np.float32) - quat = np.asarray([[1.0, 0.0, 0.0, 0.0]], dtype=np.float32) - - penalty = compute_lateral_feet_penalty(left, right, quat, min_width=0.20, sigma=0.04) - - assert penalty[0] > 0.999 - - -def test_lateral_term_binds_named_sensors_and_matches_helper() -> None: - env, scene = _lateral_env() - term = _lateral_term(env) - - result = term(env) - - assert scene.bound_names == ( - "left_foot_pos", - "right_foot_pos", - "base_link_quaternion", - ) - expected = compute_lateral_feet_penalty( - scene.sensor_data["left_foot_pos"], - scene.sensor_data["right_foot_pos"], - scene.sensor_data["base_link_quaternion"], - min_width=0.20, - sigma=0.04, - ) - np.testing.assert_allclose(result, expected) - - -def test_lateral_term_fails_closed_when_a_named_sensor_is_missing() -> None: - env, scene = _lateral_env() - del scene.sensor_data["right_foot_pos"] - - with pytest.raises(KeyError, match="right_foot_pos"): - _lateral_term(env) - - -def test_lateral_term_rejects_wrong_sensor_dimensions_at_construction() -> None: - env, _ = _lateral_env(base_quat=np.zeros((2, 3), dtype=np.float32)) - - with pytest.raises(ValueError, match=r"dimensions.*\(3, 3, 3\)"): - _lateral_term(env) - - -@pytest.mark.parametrize( - ("params", "error", "message"), - [ - ({"min_width": -0.01}, ValueError, "min_width"), - ({"sigma": 0.0}, ValueError, "sigma"), - ({"sigma": -0.01}, ValueError, "sigma"), - ({"min_width": float("nan")}, ValueError, "finite"), - ({"sigma": float("inf")}, ValueError, "finite"), - ({"min_width": True}, TypeError, "real number"), - ], -) -def test_lateral_term_rejects_invalid_parameters_at_construction( - params: dict[str, Any], error: type[Exception], message: str -) -> None: - env, _ = _lateral_env() - - with pytest.raises(error, match=message): - _lateral_term(env, **params) diff --git a/tests/envs/locomotion/t800/test_t800_owner_contract.py b/tests/envs/locomotion/t800/test_t800_owner_contract.py deleted file mode 100644 index 953131beb..000000000 --- a/tests/envs/locomotion/t800/test_t800_owner_contract.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Registry and cold-path owner contracts for the T800 walk-flat task.""" - -from __future__ import annotations - -import importlib -import importlib.util - -from unilab.base import registry -from unilab.envs import ManagerBasedRlEnvCfg -from unilab.tasks import __unilab_registry_modules__ - - -def test_t800_registry_metadata_is_manager_based_and_bootstrapped_explicitly() -> None: - """The production bootstrap exposes the Manager-Based MuJoCo and mjwarp owners.""" - registry.ensure_registries() - - assert "unilab.tasks.locomotion.t800" in __unilab_registry_modules__ - metadata = registry.list_registered_envs() - assert metadata["T800WalkFlat"] == { - "config_factory": "ManagerBasedRlEnvCfg", - "available_backends": ["mujoco", "mjwarp"], - } - - t800 = importlib.import_module("unilab.tasks.locomotion.t800") - for backend in ("mujoco", "mjwarp"): - factory = registry._envs["T800WalkFlat"].env_factory_dict[backend] - assert factory is t800.make_t800_walk_env - assert callable(factory) - assert factory.__name__ == "make_t800_walk_env" - assert factory.__module__ == "unilab.tasks.locomotion.t800" - assert registry._envs["T800WalkFlat"].env_cfg_factory is ManagerBasedRlEnvCfg - - try: - legacy_spec = importlib.util.find_spec("unilab.envs.locomotion.t800") - except ModuleNotFoundError: - legacy_spec = None - assert legacy_spec is None - - -def test_t800_factory_resolves_assets_before_generic_runtime(monkeypatch) -> None: - """Asset markers resolve before the exact generic runtime invocation.""" - t800 = importlib.import_module("unilab.tasks.locomotion.t800") - calls: list[tuple[str, object, object]] = [] - sentinel = object() - - def fake_resolver(directory: str, *, marker: str): - calls.append(("resolve", directory, marker)) - return sentinel - - def fake_builder(cfg, *, num_envs: int, backend_type: str): - calls.append(("build", num_envs, backend_type)) - assert cfg is config - return sentinel - - config = ManagerBasedRlEnvCfg() - monkeypatch.setattr(t800, "resolve_robot_asset_dir", fake_resolver) - monkeypatch.setattr(t800, "make_manager_based_rl_env", fake_builder) - - result = t800.make_t800_walk_env(config, num_envs=4, backend_type="mujoco") - - assert result is sentinel - assert calls == [ - ("resolve", "robots/t800/assets", "LINK_BASE.obj"), - ("resolve", "robots/t800/textures", "LINK_BASE.png"), - ("build", 4, "mujoco"), - ] diff --git a/tests/envs/locomotion/t800/test_t800_ppo_owner.py b/tests/envs/locomotion/t800/test_t800_ppo_owner.py deleted file mode 100644 index 74ed9b8fb..000000000 --- a/tests/envs/locomotion/t800/test_t800_ppo_owner.py +++ /dev/null @@ -1,234 +0,0 @@ -"""Hydra and cold-path contracts for the T800 Manager-Based PPO owner.""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, cast - -import numpy as np -import pytest -from hydra import compose, initialize_config_dir -from hydra.core.global_hydra import GlobalHydra - -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 ManagerBasedRlEnvCfg -from unilab.tasks.locomotion.t800.manager_terms import T800JointPositionActionCfg - -ROOT_DIR = Path(__file__).parents[4] -CONF_DIR = ROOT_DIR / "src" / "unilab" / "conf" / "ppo" - -_JOINT_NAMES = ( - "J00_HIP_PITCH_L", - "J01_HIP_ROLL_L", - "J02_HIP_YAW_L", - "J03_KNEE_PITCH_L", - "J04_ANKLE_PITCH_L", - "J05_ANKLE_ROLL_L", - "J06_HIP_PITCH_R", - "J07_HIP_ROLL_R", - "J08_HIP_YAW_R", - "J09_KNEE_PITCH_R", - "J10_ANKLE_PITCH_R", - "J11_ANKLE_ROLL_R", - "J12_TORSO_YAW", - "J13_SHOULDER_PITCH_L", - "J14_SHOULDER_ROLL_L", - "J15_SHOULDER_YAW_L", - "J16_ELBOW_PITCH_L", - "J17_ELBOW_YAW_L", - "J18_SHOULDER_PITCH_R", - "J19_SHOULDER_ROLL_R", - "J20_SHOULDER_YAW_R", - "J21_ELBOW_PITCH_R", - "J22_ELBOW_YAW_R", - "J23_HEAD_PITCH", - "J24_HEAD_YAW", -) -_ACTIVE_JOINT_NAMES = _JOINT_NAMES[:12] + _JOINT_NAMES[13:23] -_HELD_JOINT_NAMES = (_JOINT_NAMES[12], _JOINT_NAMES[23], _JOINT_NAMES[24]) -_ACTION_SCALES = ( - 0.5, - 0.2, - 0.2, - 0.5, - 0.5, - 0.2, - 0.5, - 0.2, - 0.2, - 0.5, - 0.5, - 0.2, - 0.2, - 0.2, - 0.05, - 0.2, - 0.05, - 0.2, - 0.2, - 0.05, - 0.2, - 0.05, -) -_POSE_WEIGHTS = ( - 0.01, - 1.0, - 5.0, - 0.01, - 5.0, - 5.0, - 0.01, - 1.0, - 5.0, - 0.01, - 5.0, - 5.0, - 50.0, - 50.0, - 50.0, - 50.0, - 50.0, - 50.0, - 50.0, - 50.0, - 50.0, - 50.0, -) - - -def _compose_owner(): - GlobalHydra.instance().clear() - with initialize_config_dir(config_dir=str(CONF_DIR), version_base="1.3"): - return compose("config", overrides=["task=t800_walk_flat/mujoco"]) - - -def _materialize_owner() -> tuple[Any, ManagerBasedRlEnvCfg]: - cfg = _compose_owner() - registry.ensure_registries() - env_override = BackendAdapter( - cfg, root_dir=ROOT_DIR, algo_name="ppo" - ).build_task_env_cfg_override() - env_cfg = registry.materialize_env_config("T800WalkFlat") - assert isinstance(env_cfg, ManagerBasedRlEnvCfg) - apply_cfg_overrides(env_cfg, env_override) - env_cfg.validate() - return cfg, env_cfg - - -def test_t800_ppo_owner_materializes_the_complete_manager_contract() -> None: - cfg, env_cfg = _materialize_owner() - env_cfg = cast(Any, env_cfg) - - assert cfg.training.task_name == "T800WalkFlat" - assert cfg.training.sim_backend == "mujoco" - assert cfg.training.play_steps == 2000 - assert env_cfg.sim_dt == pytest.approx(0.002) - assert env_cfg.ctrl_dt == pytest.approx(0.01) - assert env_cfg.max_episode_seconds == pytest.approx(20.0) - assert env_cfg.policy_observation_group == "policy" - assert env_cfg.critic_observation_group == "critic" - - assert env_cfg.scene is not None - assert env_cfg.scene.model_file.endswith("robots/t800/scene_flat.xml") - assert env_cfg.scene.default_keyframe_name == "stand" - robot = env_cfg.scene.entities["robot"] - assert robot.root_body_name == "LINK_BASE" - assert robot.body_names == ["LINK_BASE"] - assert tuple(robot.joint_names or ()) == _JOINT_NAMES - assert tuple(robot.actuator_names or ()) == _JOINT_NAMES - - action = env_cfg.actions["joint_pos"] - assert isinstance(action, T800JointPositionActionCfg) - assert tuple(action.actuator_names) == _ACTIVE_JOINT_NAMES - assert tuple(action.held_actuator_names) == _HELD_JOINT_NAMES - action_scale = cast(Any, action.scale) - assert list(action_scale) == list(_ACTIVE_JOINT_NAMES) - assert tuple(action_scale.values()) == pytest.approx(_ACTION_SCALES) - assert action.use_default_offset is True - - policy_terms = env_cfg.observations["policy"].terms - critic_terms = env_cfg.observations["critic"].terms - expected_terms = ( - "base_ang_vel", - "projected_gravity", - "joint_pos", - "joint_vel", - "actions", - "command", - "gait_phase", - ) - assert tuple(policy_terms) == expected_terms - assert tuple(critic_terms) == (*expected_terms, "base_lin_vel") - assert sum((3, 3, 22, 22, 22, 3, 2)) == 77 - assert sum((3, 3, 22, 22, 22, 3, 2, 3)) == 80 - assert policy_terms["base_ang_vel"].params["sensor_name"] == "torso_gyro" - assert policy_terms["projected_gravity"].params["sensor_name"] == "torso_upvector" - assert critic_terms["base_lin_vel"].params["sensor_name"] == "pelvis_local_linvel" - - assert tuple(env_cfg.rewards) == ( - "tracking_lin_vel", - "tracking_ang_vel", - "feet_phase", - "lin_vel_z", - "ang_vel_xy", - "base_height", - "orientation", - "penalty_feet_ori", - "action_rate", - "pose", - ) - assert env_cfg.rewards["feet_phase"].weight == pytest.approx(1.5) - assert env_cfg.rewards["feet_phase"].params["swing_height"] == pytest.approx(0.13) - assert env_cfg.rewards["feet_phase"].params["tracking_sigma"] == pytest.approx(0.014) - assert env_cfg.rewards["base_height"].params["target_height"] == pytest.approx(1.0165) - assert env_cfg.terminations["tilt"].params["max_tilt_deg"] == pytest.approx(25.0) - assert env_cfg.terminations["base_height"].params["minimum_height"] == pytest.approx(0.7165) - assert tuple(env_cfg.rewards["pose"].params["pose_weights"]) == pytest.approx(_POSE_WEIGHTS) - - for name, bound in { - "base_ang_vel": 0.2, - "projected_gravity": 0.05, - "joint_pos": 0.01, - "joint_vel": 1.5, - }.items(): - noise = policy_terms[name].noise - assert noise is not None - assert noise.n_min == pytest.approx(-bound) - assert noise.n_max == pytest.approx(bound) - assert all(term.noise is None for term in critic_terms.values()) - - -@pytest.mark.slow -def test_t800_ppo_owner_builds_and_steps_real_mujoco_env() -> None: - pytest.importorskip("mujoco") - cfg, _ = _materialize_owner() - env = cast( - Any, - registry.make( - "T800WalkFlat", - sim_backend=str(cfg.training.sim_backend), - num_envs=1, - env_cfg_override=BackendAdapter( - cfg, root_dir=ROOT_DIR, algo_name="ppo" - ).build_task_env_cfg_override(), - ), - ) - try: - reset_obs, reset_info = env.reset() - assert isinstance(reset_info, dict) - assert env.action_space.shape == (22,) - assert reset_obs["obs"].shape == (1, 77) - assert reset_obs["critic"].shape == (1, 80) - - action = np.zeros((1, 22), dtype=np.float32) - state = env.step(action) - assert np.isfinite(state.obs["obs"]).all() - assert np.isfinite(state.obs["critic"]).all() - assert np.isfinite(state.reward).all() - controls = env._control - assert controls.shape == (1, 25) - assert np.isfinite(controls).all() - finally: - env.close() diff --git a/tests/envs/locomotion/t800/test_t800_sac_owner.py b/tests/envs/locomotion/t800/test_t800_sac_owner.py deleted file mode 100644 index a05e47def..000000000 --- a/tests/envs/locomotion/t800/test_t800_sac_owner.py +++ /dev/null @@ -1,216 +0,0 @@ -"""Hydra and cold-path contracts for the T800 Manager-Based SAC owner.""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, cast - -import numpy as np -import pytest -from hydra import compose, initialize_config_dir -from hydra.core.global_hydra import GlobalHydra - -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 ManagerBasedRlEnvCfg -from unilab.tasks.locomotion.t800.manager_terms import ( - T800JointPositionActionCfg, - penalty_close_feet_lateral, -) - -ROOT_DIR = Path(__file__).parents[4] -CONF_DIR = ROOT_DIR / "src" / "unilab" / "conf" / "sac" - -_JOINT_NAMES = ( - "J00_HIP_PITCH_L", - "J01_HIP_ROLL_L", - "J02_HIP_YAW_L", - "J03_KNEE_PITCH_L", - "J04_ANKLE_PITCH_L", - "J05_ANKLE_ROLL_L", - "J06_HIP_PITCH_R", - "J07_HIP_ROLL_R", - "J08_HIP_YAW_R", - "J09_KNEE_PITCH_R", - "J10_ANKLE_PITCH_R", - "J11_ANKLE_ROLL_R", - "J12_TORSO_YAW", - "J13_SHOULDER_PITCH_L", - "J14_SHOULDER_ROLL_L", - "J15_SHOULDER_YAW_L", - "J16_ELBOW_PITCH_L", - "J17_ELBOW_YAW_L", - "J18_SHOULDER_PITCH_R", - "J19_SHOULDER_ROLL_R", - "J20_SHOULDER_YAW_R", - "J21_ELBOW_PITCH_R", - "J22_ELBOW_YAW_R", - "J23_HEAD_PITCH", - "J24_HEAD_YAW", -) -_ACTIVE = _JOINT_NAMES[:12] + _JOINT_NAMES[13:23] -_HELD = (_JOINT_NAMES[12], _JOINT_NAMES[23], _JOINT_NAMES[24]) -_SCALES = (1.0,) * 12 + (0.2, 0.2, 0.05, 0.2, 0.05, 0.2, 0.2, 0.05, 0.2, 0.05) -_POSE = (0.01, 2.0, 5.0, 0.01, 5.0, 5.0, 0.01, 2.0, 5.0, 0.01, 5.0, 5.0) + (50.0,) * 10 - - -def _compose_owner(): - GlobalHydra.instance().clear() - with initialize_config_dir(config_dir=str(CONF_DIR), version_base="1.3"): - return compose("config", overrides=["task=t800_walk_flat/mujoco"]) - - -def _materialize_owner() -> tuple[Any, ManagerBasedRlEnvCfg]: - cfg = _compose_owner() - registry.ensure_registries() - env_override = BackendAdapter( - cfg, root_dir=ROOT_DIR, algo_name="sac" - ).build_task_env_cfg_override() - env_cfg = registry.materialize_env_config("T800WalkFlat") - assert isinstance(env_cfg, ManagerBasedRlEnvCfg) - apply_cfg_overrides(env_cfg, env_override) - env_cfg.validate() - return cfg, env_cfg - - -def test_t800_sac_owner_materializes_complete_contract() -> None: - cfg, env_cfg = _materialize_owner() - env_cfg = cast(Any, env_cfg) - - assert CONF_DIR.name == "sac" - assert cfg.training.task_name == "T800WalkFlat" - assert cfg.training.sim_backend == "mujoco" - assert cfg.algo.gamma == pytest.approx(0.98488578) - assert cfg.algo.learning_starts == 10 - assert cfg.algo.updates_per_step == 8 - assert cfg.algo.algo_params.alpha_init == pytest.approx(0.001) - assert cfg.algo.algo_params.target_entropy_ratio == pytest.approx(0.0) - assert cfg.algo.max_iterations == 100000 - assert cfg.algo.save_interval == 1000 - assert env_cfg.sim_dt == pytest.approx(0.002) - assert env_cfg.ctrl_dt == pytest.approx(0.01) - assert env_cfg.max_episode_seconds == pytest.approx(20.0) - - robot = env_cfg.scene.entities["robot"] - assert env_cfg.scene.model_file.endswith("robots/t800/scene_flat.xml") - assert env_cfg.scene.default_keyframe_name == "stand" - assert robot.root_body_name == "LINK_BASE" - assert robot.body_names == ["LINK_BASE"] - assert tuple(robot.joint_names) == _JOINT_NAMES - assert tuple(robot.actuator_names) == _JOINT_NAMES - - action = env_cfg.actions["joint_pos"] - assert isinstance(action, T800JointPositionActionCfg) - assert tuple(action.actuator_names) == _ACTIVE - assert tuple(action.held_actuator_names) == _HELD - assert list(cast(Any, action.scale)) == list(_ACTIVE) - assert tuple(cast(Any, action.scale).values()) == pytest.approx(_SCALES) - assert action.use_default_offset is True - - policy = env_cfg.observations["policy"].terms - critic = env_cfg.observations["critic"].terms - assert tuple(policy) == ( - "base_ang_vel", - "projected_gravity", - "joint_pos", - "joint_vel", - "actions", - "command", - "gait_phase", - ) - assert tuple(critic) == (*tuple(policy), "base_lin_vel") - assert env_cfg.observations["policy"].enable_corruption is True - assert env_cfg.observations["critic"].enable_corruption is False - assert policy["base_ang_vel"].params["sensor_name"] == "torso_gyro" - assert policy["projected_gravity"].params["sensor_name"] == "torso_upvector" - assert policy["base_ang_vel"].scale == pytest.approx(0.25) - assert policy["joint_vel"].scale == pytest.approx(0.05) - assert critic["projected_gravity"].params["sensor_name"] == "torso_upvector" - assert critic["base_lin_vel"].params["sensor_name"] == "pelvis_local_linvel" - assert critic["base_lin_vel"].scale == pytest.approx(2.0) - assert policy["joint_pos"].params["asset_cfg"].joint_names == list(_ACTIVE) - assert policy["joint_vel"].params["asset_cfg"].joint_names == list(_ACTIVE) - assert critic["joint_pos"].params["asset_cfg"].joint_names == list(_ACTIVE) - assert critic["joint_vel"].params["asset_cfg"].joint_names == list(_ACTIVE) - assert policy["joint_pos"].noise.n_max == pytest.approx(0.01) - assert policy["joint_vel"].noise.n_max == pytest.approx(0.1) - assert all(term.noise is None for term in critic.values()) - - assert sum((3, 3, 22, 22, 22, 3, 2)) == 77 - assert sum((3, 3, 22, 22, 22, 3, 2, 3)) == 80 - assert tuple(env_cfg.rewards) == ( - "tracking_lin_vel", - "tracking_ang_vel", - "penalty_ang_vel_xy", - "penalty_orientation", - "penalty_action_rate", - "pose", - "penalty_feet_ori", - "penalty_close_feet_lateral", - "feet_phase", - "alive", - ) - expected_weights = { - "tracking_lin_vel": 2.0, - "tracking_ang_vel": 1.5, - "penalty_ang_vel_xy": -1.0, - "penalty_orientation": -10.0, - "penalty_action_rate": -4.0, - "pose": -0.5, - "penalty_feet_ori": -20.0, - "penalty_close_feet_lateral": -5.0, - "feet_phase": 5.0, - "alive": 10.0, - } - for name, weight in expected_weights.items(): - assert env_cfg.rewards[name].weight == pytest.approx(weight) - assert tuple(env_cfg.rewards["pose"].params["pose_weights"]) == pytest.approx(_POSE) - lateral = env_cfg.rewards["penalty_close_feet_lateral"] - assert lateral.func is penalty_close_feet_lateral - assert lateral.params["min_width"] == pytest.approx(0.18) - assert lateral.params["sigma"] == pytest.approx(0.04) - phase = env_cfg.rewards["feet_phase"] - assert phase.params["swing_height"] == pytest.approx(0.11) - assert phase.params["tracking_sigma"] == pytest.approx(0.014) - - curriculum = env_cfg.curriculum["penalty_scaling"] - assert curriculum.params == { - "initial_scale": 0.125, - "min_scale": 0.125, - "max_scale": 0.25, - "level_down_threshold": 150.0, - "level_up_threshold": 750.0, - "degree": 0.001, - } - - -@pytest.mark.slow -def test_t800_sac_owner_builds_and_steps_real_mujoco_env() -> None: - pytest.importorskip("mujoco") - cfg, _ = _materialize_owner() - env = cast( - Any, - registry.make( - "T800WalkFlat", - sim_backend="mujoco", - num_envs=1, - env_cfg_override=BackendAdapter( - cfg, root_dir=ROOT_DIR, algo_name="sac" - ).build_task_env_cfg_override(), - ), - ) - try: - obs, info = env.reset() - assert isinstance(info, dict) - assert env.action_space.shape == (22,) - assert obs["obs"].shape == (1, 77) - assert obs["critic"].shape == (1, 80) - state = env.step(np.zeros((1, 22), dtype=np.float32)) - assert np.isfinite(state.obs["obs"]).all() - assert np.isfinite(state.obs["critic"]).all() - assert np.isfinite(state.reward).all() - assert env._control.shape == (1, 25) - assert np.isfinite(env._control).all() - finally: - env.close() diff --git a/tests/scripts/test_support_matrix.py b/tests/scripts/test_support_matrix.py index ca0dd03a7..6e38150cb 100644 --- a/tests/scripts/test_support_matrix.py +++ b/tests/scripts/test_support_matrix.py @@ -21,19 +21,6 @@ def test_support_matrix_marks_go2_ppo_backends_as_tested(): assert row.cells["motrix"].level == EvidenceLevel.TESTED -def test_support_matrix_marks_t800_mujoco_tested_and_sac_mjwarp_configured_only(): - """T800 evidence stays on MuJoCo; the SAC mjwarp owner is configured but unvalidated.""" - ppo_row = _row("PPO (torch)", "t800_walk_flat") - sac_row = _row("SAC (torch)", "t800_walk_flat") - - for row in (ppo_row, sac_row): - assert row.cells["mujoco"].level == EvidenceLevel.TESTED - assert row.cells["motrix"].level == EvidenceLevel.MISSING - - assert ppo_row.cells["mjwarp"].level == EvidenceLevel.REGISTERED - assert sac_row.cells["mjwarp"].level == EvidenceLevel.CONFIGURED - - def test_support_matrix_marks_validated_g1_mjwarp_entrypoints_as_tested(): torch_row = _row("PPO (torch)", "g1_walk_flat") sac_row = _row("SAC (torch)", "g1_walk_flat") diff --git a/tests/tasks/test_migration_matrix.py b/tests/tasks/test_migration_matrix.py index 11a0d11b0..ea2c85947 100644 --- a/tests/tasks/test_migration_matrix.py +++ b/tests/tasks/test_migration_matrix.py @@ -31,7 +31,6 @@ def test_registered_tasks_have_explicit_migration_records() -> None: ("MicroduckVelocityFlat", "microduck_locomotion", "complete", "Compatible"), ("MicroduckGroundPickFlat", "microduck_locomotion", "mba", "Adapted"), ("MicroduckSitStandFlat", "microduck_locomotion", "mba", "Adapted"), - ("T800WalkFlat", "t800_locomotion", "complete", "Compatible"), ("Go2JoystickRough", "quadruped_rough", "complete", "Compatible"), ], ) diff --git a/tests/tasks/test_package_boundary.py b/tests/tasks/test_package_boundary.py index e324d56d5..4f3476cca 100644 --- a/tests/tasks/test_package_boundary.py +++ b/tests/tasks/test_package_boundary.py @@ -20,7 +20,6 @@ "unilab.tasks.locomotion.go2_arm", "unilab.tasks.locomotion.a2", "unilab.tasks.locomotion.microduck", - "unilab.tasks.locomotion.t800", "unilab.tasks.manipulation.allegro_inhand", "unilab.tasks.manipulation.sharpa_inhand", "unilab.tasks.manipulation.stewart", diff --git a/tests/tasks/test_production_registry_closeout.py b/tests/tasks/test_production_registry_closeout.py index e9e12b078..de1771d88 100644 --- a/tests/tasks/test_production_registry_closeout.py +++ b/tests/tasks/test_production_registry_closeout.py @@ -40,9 +40,6 @@ # Approved wrapper: cold-path MicroDuck STL resolution before delegating # to the generic factory. ("unilab.tasks.locomotion.microduck", "make_microduck_velocity_env"), - # Approved wrapper: cold-path T800 OBJ/texture resolution before - # delegating to the generic factory. - ("unilab.tasks.locomotion.t800", "make_t800_walk_env"), ) _SNAPSHOT_CODE = textwrap.dedent( diff --git a/tests/test_pull_assets.py b/tests/test_pull_assets.py index 82336fea3..a96f61744 100644 --- a/tests/test_pull_assets.py +++ b/tests/test_pull_assets.py @@ -16,37 +16,6 @@ def _populate(directory: Path, *, suffix: str, count: int) -> Path: return directory -def test_pull_assets_t800_resolves_both_asset_directories( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], -): - targets = { - "robots/t800/assets": _populate(tmp_path / "assets", suffix=".obj", count=26), - "robots/t800/textures": _populate(tmp_path / "textures", suffix=".png", count=15), - } - calls: list[tuple[str, str, bool]] = [] - - def fake_resolver(directory: str, *, marker: str, show_progress: bool) -> Path: - calls.append((directory, marker, show_progress)) - return targets[directory] - - monkeypatch.setattr(pull_assets, "resolve_robot_asset_dir", fake_resolver) - - assert pull_assets.main(["--robot", "t800"]) == 0 - assert calls == [ - ("robots/t800/assets", "LINK_BASE.obj", False), - ("robots/t800/textures", "LINK_BASE.png", False), - ] - output = capsys.readouterr().out - assert "26 OBJ files" in output - assert "15 PNG files" in output - lines = output.strip().splitlines() - assert len(lines) == 2 - assert lines[0] == "Downloading t800 assets ..." - assert lines[-1].startswith("Robot assets ready: 1 robots, 2 directories, 41 files") - - def test_pull_assets_microduck_keeps_single_stl_directory( monkeypatch: pytest.MonkeyPatch, tmp_path: Path,