From f01de962d08401e55928d8795db20a968cb9bd4d Mon Sep 17 00:00:00 2001 From: zilch Date: Tue, 8 Sep 2026 01:38:13 +0800 Subject: [PATCH] feat: add Microduck robot and integrate it into humanoid walk task (#17) Port the Pollen Robotics Microduck 14-DoF biped MJCF and meshes from microduck_rl (Apache-2.0 code, CC BY-SA-NC meshes) with STAND/SIT/FOLD key poses plus a trunk termination capsule, and register microduck-walk-flat/rough presets on the shared velocity-tracking env with scale-tuned gait rewards, task configs, posters, and env docs. Measure the shared foot-orientation penalty against the default-pose foot gravity so robots with non-world-aligned ankle frames (onshape- to-robot exports like Microduck) are not rewarded for tilting soles. --- .../microduck-walk-flat/motrix.fastsac.yaml | 19 + .../microduck-walk-rough/motrix.fastsac.yaml | 19 + docs/scripts/generate_robot_docs.py | 1 + .../images/poster/microduck-walk-flat.jpg | 3 + .../images/poster/microduck-walk-rough.jpg | 3 + .../_static/images/robots/microduck.png | 3 + docs/source/en/user_guide/envs/index.md | 2 + docs/source/en/user_guide/robots.md | 18 +- docs/source/zh_CN/user_guide/envs/index.md | 2 + docs/source/zh_CN/user_guide/robots.md | 18 +- .../locomotion/humanoid/__init__.py | 2 +- .../locomotion/humanoid/microduck.py | 123 +++++ .../locomotion/humanoid/walk_np.py | 14 +- motrix_envs/src/motrix_envs/robot/__init__.py | 3 + .../robot/assets/microduck/README.md | 14 + .../assets/microduck/assets/ankle_left.stl | 3 + .../assets/microduck/assets/ankle_right.stl | 3 + .../microduck/assets/banana_pcb_locker.stl | 3 + .../assets/microduck/assets/bearing_roll.stl | 3 + .../microduck/assets/bottom_head_shell.stl | 3 + .../assets/elec_rpi_robot_hat_pcb.stl | 3 + .../assets/microduck/assets/face_part.stl | 3 + .../assets/microduck/assets/foot_left.stl | 3 + .../assets/microduck/assets/foot_right.stl | 3 + .../robot/assets/microduck/assets/hip_l.stl | 3 + .../robot/assets/microduck/assets/jaw.stl | 3 + .../assets/microduck/assets/jaw_soft.stl | 3 + .../assets/microduck/assets/left_shell.stl | 3 + .../robot/assets/microduck/assets/leg.stl | 3 + .../robot/assets/microduck/assets/lens.stl | 3 + .../microduck/assets/m12_lens_holder.stl | 3 + .../assets/microduck/assets/motor_support.stl | 3 + .../robot/assets/microduck/assets/neck.stl | 3 + .../assets/microduck/assets/neck_pitch.stl | 3 + .../assets/microduck/assets/noenoeil.stl | 3 + .../robot/assets/microduck/assets/np_f970.stl | 3 + .../assets/pcb__raspberry_pi_zero_2_w.stl | 3 + .../assets/microduck/assets/power_support.stl | 3 + .../assets/microduck/assets/right_shell.stl | 3 + .../seeed_bearing__configuration__22x16x4.stl | 3 + .../seeed_bearing__configuration_default.stl | 3 + .../microduck/assets/soft_mouth_top.stl | 3 + .../assets/microduck/assets/sole_left.stl | 3 + .../assets/microduck/assets/sole_right.stl | 3 + .../robot/assets/microduck/assets/speaker.stl | 3 + .../microduck/assets/top_head_shell.stl | 3 + .../assets/microduck/assets/trunk_base.stl | 3 + .../microduck/assets/upper_leg_left.stl | 3 + .../microduck/assets/upper_leg_right.stl | 3 + .../assets/upper_leg_rigidity_plate.stl | 3 + .../robot/assets/microduck/assets/xl330.stl | 3 + .../assets/microduck/assets/yaw2roll.stl | 3 + .../microduck/assets/yaw_roll_motion.stl | 3 + .../robot/assets/microduck/microduck.xml | 430 ++++++++++++++++++ .../src/motrix_envs/robot/microduck.py | 96 ++++ motrix_envs/tests/test_humanoid_walk.py | 43 +- motrix_envs/tests/test_robot_cfg.py | 41 +- 57 files changed, 936 insertions(+), 32 deletions(-) create mode 100644 configs/task/microduck-walk-flat/motrix.fastsac.yaml create mode 100644 configs/task/microduck-walk-rough/motrix.fastsac.yaml create mode 100644 docs/source/_static/images/poster/microduck-walk-flat.jpg create mode 100644 docs/source/_static/images/poster/microduck-walk-rough.jpg create mode 100644 docs/source/_static/images/robots/microduck.png create mode 100644 motrix_envs/src/motrix_envs/locomotion/humanoid/microduck.py create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/README.md create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_left.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_right.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/banana_pcb_locker.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bearing_roll.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bottom_head_shell.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/elec_rpi_robot_hat_pcb.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/face_part.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_left.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_right.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/hip_l.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw_soft.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/left_shell.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/leg.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/lens.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/m12_lens_holder.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/motor_support.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck_pitch.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/noenoeil.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/np_f970.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/pcb__raspberry_pi_zero_2_w.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/power_support.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/right_shell.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration__22x16x4.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration_default.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/soft_mouth_top.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_left.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_right.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/speaker.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/top_head_shell.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/trunk_base.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_left.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_right.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_rigidity_plate.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/xl330.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw2roll.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw_roll_motion.stl create mode 100644 motrix_envs/src/motrix_envs/robot/assets/microduck/microduck.xml create mode 100644 motrix_envs/src/motrix_envs/robot/microduck.py diff --git a/configs/task/microduck-walk-flat/motrix.fastsac.yaml b/configs/task/microduck-walk-flat/motrix.fastsac.yaml new file mode 100644 index 0000000..f995fe0 --- /dev/null +++ b/configs/task/microduck-walk-flat/motrix.fastsac.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Task microduck-walk-flat: motrix.fastsac shared training recipe. +defaults: + - /algo_base@algo: motrix.fastsac + - _self_ +task: + env: microduck-walk-flat + rllib: motrix + algo: fastsac +num_envs: 2048 +play_num_envs: 16 +seed: 1 +checkpoint: + interval: 1000 +algo: + agent: + alpha_init: 0.01 + target_entropy_ratio: -0.1 + num_updates: 4 diff --git a/configs/task/microduck-walk-rough/motrix.fastsac.yaml b/configs/task/microduck-walk-rough/motrix.fastsac.yaml new file mode 100644 index 0000000..d1749ae --- /dev/null +++ b/configs/task/microduck-walk-rough/motrix.fastsac.yaml @@ -0,0 +1,19 @@ +# @package _global_ +# Task microduck-walk-rough: motrix.fastsac shared training recipe. +defaults: + - /algo_base@algo: motrix.fastsac + - _self_ +task: + env: microduck-walk-rough + rllib: motrix + algo: fastsac +num_envs: 2048 +play_num_envs: 16 +seed: 1 +checkpoint: + interval: 1000 +algo: + agent: + alpha_init: 0.01 + target_entropy_ratio: -0.1 + num_updates: 4 diff --git a/docs/scripts/generate_robot_docs.py b/docs/scripts/generate_robot_docs.py index b217240..4099305 100644 --- a/docs/scripts/generate_robot_docs.py +++ b/docs/scripts/generate_robot_docs.py @@ -38,6 +38,7 @@ "go1": {"kind": "quadruped", "lookat": (0.0, 0.0, 0.3), "distance": 1.4}, "go2": {"kind": "quadruped", "lookat": (0.0, 0.0, 0.3), "distance": 1.4}, "k1": {"kind": "humanoid", "lookat": (0.0, 0.0, 0.8), "distance": 2.1}, + "microduck": {"kind": "humanoid", "lookat": (0.0, 0.0, 0.15), "distance": 0.8}, } _DOC_CONFIGS = { diff --git a/docs/source/_static/images/poster/microduck-walk-flat.jpg b/docs/source/_static/images/poster/microduck-walk-flat.jpg new file mode 100644 index 0000000..bfae72b --- /dev/null +++ b/docs/source/_static/images/poster/microduck-walk-flat.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44f5cf7b759cb8ef8151818a882b780181a611e3a231bea001467adb65371ed9 +size 31796 diff --git a/docs/source/_static/images/poster/microduck-walk-rough.jpg b/docs/source/_static/images/poster/microduck-walk-rough.jpg new file mode 100644 index 0000000..b2c425a --- /dev/null +++ b/docs/source/_static/images/poster/microduck-walk-rough.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e7600d4467cacd92e1e32ab40e9334c38a83f0156e0e5032fcd41cfd59a84de +size 41509 diff --git a/docs/source/_static/images/robots/microduck.png b/docs/source/_static/images/robots/microduck.png new file mode 100644 index 0000000..41f39d3 --- /dev/null +++ b/docs/source/_static/images/robots/microduck.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d0d8814ec90cf39cbbe562231aee4bd0dfcb8426742b7724946ea7ce52ae471 +size 585301 diff --git a/docs/source/en/user_guide/envs/index.md b/docs/source/en/user_guide/envs/index.md index 208a088..29a510c 100644 --- a/docs/source/en/user_guide/envs/index.md +++ b/docs/source/en/user_guide/envs/index.md @@ -66,6 +66,8 @@ uv run scripts/view.py env= | k1-walk-flat | `k1-walk-flat` | Track walking commands with Booster K1 on flat ground. | `motrix.fastsac` | | k1-walk-rough | `k1-walk-rough` | Track walking commands with Booster K1 over uneven terrain. | `motrix.fastsac` | | k1-wbt-freekick | `k1-wbt-freekick` | Track a free-kick reference motion with Booster K1. | `motrix.fastsac` | +| microduck-walk-flat | `microduck-walk-flat` | Track walking commands with Microduck on flat ground. | `motrix.fastsac` | +| microduck-walk-rough | `microduck-walk-rough` | Track walking commands with Microduck over uneven terrain. | `motrix.fastsac` | | peg-insert | `peg-insert` | Control RM65 to grasp, align, and insert a peg into a socket. | `motrix.fastsac`, `skrl.ppo` | | pendulum | `pendulum` | Apply joint torque to swing up and balance a pendulum. | `rslrl.ppo`, `skrl.ppo` | | point_mass | `point_mass` | Move a two-dimensional point mass to a random target. | `skrl.ppo` | diff --git a/docs/source/en/user_guide/robots.md b/docs/source/en/user_guide/robots.md index 489bbcf..fe4f952 100644 --- a/docs/source/en/user_guide/robots.md +++ b/docs/source/en/user_guide/robots.md @@ -8,15 +8,15 @@ be composed into different scenes and tasks. The table below is generated direct - -| Screenshot | Registry name | Configuration class | Type | Model format | DoF | -| ---------------------------------------------------------------------------- | ------------- | ------------------- | --------- | ------------ | --- | -| anymal_c | `anymal_c` | `AnymalC` | Quadruped | MJCF | 12 | -| dex-evt | `dex-evt` | `DexEvt` | Humanoid | URDF | 23 | -| g1-29dof | `g1-29dof` | `UnitreeG129Dof` | Humanoid | MJCF | 29 | -| go1 | `go1` | `UnitreeGo1Robot` | Quadruped | MJCF | 12 | -| go2 | `go2` | `UnitreeGo2Robot` | Quadruped | MJCF | 12 | -| k1 | `k1` | `BoosterK1` | Humanoid | MJCF | 22 | +| Screenshot | Registry name | Configuration class | Type | Model format | DoF | +| --- | --- | --- | --- | --- | --- | +| anymal_c | `anymal_c` | `AnymalC` | Quadruped | MJCF | 12 | +| dex-evt | `dex-evt` | `DexEvt` | Humanoid | URDF | 23 | +| g1-29dof | `g1-29dof` | `UnitreeG129Dof` | Humanoid | MJCF | 29 | +| go1 | `go1` | `UnitreeGo1Robot` | Quadruped | MJCF | 12 | +| go2 | `go2` | `UnitreeGo2Robot` | Quadruped | MJCF | 12 | +| k1 | `k1` | `BoosterK1` | Humanoid | MJCF | 22 | +| microduck | `microduck` | `Microduck` | Humanoid | MJCF | 14 | diff --git a/docs/source/zh_CN/user_guide/envs/index.md b/docs/source/zh_CN/user_guide/envs/index.md index ea6cbda..efa1f82 100644 --- a/docs/source/zh_CN/user_guide/envs/index.md +++ b/docs/source/zh_CN/user_guide/envs/index.md @@ -66,6 +66,8 @@ uv run scripts/view.py env= | k1-walk-flat | `k1-walk-flat` | 控制 Booster K1 在平地上跟踪行走指令。 | `motrix.fastsac` | | k1-walk-rough | `k1-walk-rough` | 控制 Booster K1 在起伏地形上跟踪行走指令。 | `motrix.fastsac` | | k1-wbt-freekick | `k1-wbt-freekick` | 让 Booster K1 跟踪任意球射门参考动作。 | `motrix.fastsac` | +| microduck-walk-flat | `microduck-walk-flat` | 控制 Microduck 小型双足机器人在平地上跟踪行走指令。 | `motrix.fastsac` | +| microduck-walk-rough | `microduck-walk-rough` | 控制 Microduck 小型双足机器人在起伏地形上跟踪行走指令。 | `motrix.fastsac` | | peg-insert | `peg-insert` | 控制 RM65 抓取插销、对准插座并完成插入。 | `motrix.fastsac`, `skrl.ppo` | | pendulum | `pendulum` | 施加关节力矩使单摆摆起并保持直立。 | `rslrl.ppo`, `skrl.ppo` | | point_mass | `point_mass` | 控制二维质点移动到随机目标位置。 | `skrl.ppo` | diff --git a/docs/source/zh_CN/user_guide/robots.md b/docs/source/zh_CN/user_guide/robots.md index fe8dac1..01e0a46 100644 --- a/docs/source/zh_CN/user_guide/robots.md +++ b/docs/source/zh_CN/user_guide/robots.md @@ -8,15 +8,15 @@ scene 和 task 中。下表直接根据 registry 和 robot 配置生成。 - -| 截图 | Registry 名称 | 配置类 | 类型 | 模型格式 | 自由度 | -| ---------------------------------------------------------------------------- | ------------- | ----------------- | ---------- | -------- | ------ | -| anymal_c | `anymal_c` | `AnymalC` | 四足机器人 | MJCF | 12 | -| dex-evt | `dex-evt` | `DexEvt` | 人形机器人 | URDF | 23 | -| g1-29dof | `g1-29dof` | `UnitreeG129Dof` | 人形机器人 | MJCF | 29 | -| go1 | `go1` | `UnitreeGo1Robot` | 四足机器人 | MJCF | 12 | -| go2 | `go2` | `UnitreeGo2Robot` | 四足机器人 | MJCF | 12 | -| k1 | `k1` | `BoosterK1` | 人形机器人 | MJCF | 22 | +| 截图 | Registry 名称 | 配置类 | 类型 | 模型格式 | 自由度 | +| --- | --- | --- | --- | --- | --- | +| anymal_c | `anymal_c` | `AnymalC` | 四足机器人 | MJCF | 12 | +| dex-evt | `dex-evt` | `DexEvt` | 人形机器人 | URDF | 23 | +| g1-29dof | `g1-29dof` | `UnitreeG129Dof` | 人形机器人 | MJCF | 29 | +| go1 | `go1` | `UnitreeGo1Robot` | 四足机器人 | MJCF | 12 | +| go2 | `go2` | `UnitreeGo2Robot` | 四足机器人 | MJCF | 12 | +| k1 | `k1` | `BoosterK1` | 人形机器人 | MJCF | 22 | +| microduck | `microduck` | `Microduck` | 人形机器人 | MJCF | 14 | diff --git a/motrix_envs/src/motrix_envs/locomotion/humanoid/__init__.py b/motrix_envs/src/motrix_envs/locomotion/humanoid/__init__.py index f992068..7e2d89d 100644 --- a/motrix_envs/src/motrix_envs/locomotion/humanoid/__init__.py +++ b/motrix_envs/src/motrix_envs/locomotion/humanoid/__init__.py @@ -3,4 +3,4 @@ """Robot-agnostic humanoid locomotion environments and configuration schemas.""" -from . import dex_evt, g1, k1, walk_np # noqa: F401 register envs and expose base task +from . import dex_evt, g1, k1, microduck, walk_np # noqa: F401 register envs and expose base task diff --git a/motrix_envs/src/motrix_envs/locomotion/humanoid/microduck.py b/motrix_envs/src/motrix_envs/locomotion/humanoid/microduck.py new file mode 100644 index 0000000..b22a998 --- /dev/null +++ b/motrix_envs/src/motrix_envs/locomotion/humanoid/microduck.py @@ -0,0 +1,123 @@ +# Copyright Motphys Technology Co., Ltd. 2025, 2026 +# SPDX-License-Identifier: Apache-2.0 + +"""Pollen Robotics Microduck humanoid velocity-tracking presets and registration.""" + +from dataclasses import replace + +from motrix_env_core import registry +from motrix_env_core.base import SimCfg +from motrix_env_core.config.scene import HFieldTerrainCfg, SystemCameraCfg +from motrix_envs.config.scene import StandardSceneObjsCfg +from motrix_envs.locomotion.humanoid import cfg as humanoid_cfg +from motrix_envs.locomotion.humanoid.walk_np import HumanoidVelocityTrackingEnv +from motrix_envs.robot import Microduck + + +def _make_microduck_robot() -> Microduck: + return Microduck() + + +# Pinned Microduck termination collision inventory, owned by the Microduck walk task. +_MICRODUCK_TERMINATION_GEOMS = ("trunk_collision",) + + +@registry.envcfg("microduck-walk-flat") +def make_microduck_walk_flat_cfg() -> humanoid_cfg.HumanoidVelocityTrackingEnvCfg: + """Track walking commands with Microduck on flat ground. + + zh_CN: 控制 Microduck 小型双足机器人在平地上跟踪行走指令。 + """ + + return humanoid_cfg.HumanoidVelocityTrackingEnvCfg( + scene=humanoid_cfg.HumanoidWalkSceneCfg( + # Microduck is a ~25 cm robot; frame the lead env much closer than + # the full-size humanoid default camera (grid center at z=0.75). + system_camera=SystemCameraCfg( + lookat=(0.0, 0.0, 0.12), + distance=0.35, + elevation=-20.0, + azimuth=180.0, + ), + objs=StandardSceneObjsCfg(robot=_make_microduck_robot()), + ), + control_config=humanoid_cfg.ControlCfg(action_scale=0.5), + commands=humanoid_cfg.CommandsCfg( + vel_limit=[ + [-1.0, -1.0, -1.0], + [1.0, 1.0, 1.0], + ], + ), + gait=humanoid_cfg.GaitCfg( + period=0.5, + swing_height=0.04, + feet_phase_sigma=0.002, + ), + reward_config=humanoid_cfg.RewardCfg( + scales=humanoid_cfg.RewardScales( + tracking_lin_vel=10.0, + tracking_ang_vel=3.0, + penalty_action_rate=-0.5, + feet_phase=8.0, + ), + tracking_sigma=0.15, + close_feet_threshold=0.05, + pose_weights={ + "left_hip_yaw": 5.0, + "left_hip_roll": 1.0, + "left_hip_pitch": 0.01, + "left_knee": 0.01, + "left_ankle": 5.0, + "neck_pitch": 50.0, + "head_pitch": 50.0, + "head_yaw": 50.0, + "head_roll": 50.0, + "right_hip_yaw": 5.0, + "right_hip_roll": 1.0, + "right_hip_pitch": 0.01, + "right_knee": 0.01, + "right_ankle": 5.0, + }, + ), + asset=humanoid_cfg.AssetCfg( + foot_height_site_names=("left_foot", "right_foot"), + ground_geom_name="floor", + terminate_contact_geom_names=_MICRODUCK_TERMINATION_GEOMS, + ), + sim=SimCfg(dt=0.005, solver_iterations=6, solver_tolerance=1e-4), + spawn_xy_range=0.0, + ) + + +@registry.envcfg("microduck-walk-rough") +def make_microduck_walk_rough_cfg() -> humanoid_cfg.HumanoidVelocityTrackingEnvCfg: + """Track walking commands with Microduck over uneven terrain. + + zh_CN: 控制 Microduck 小型双足机器人在起伏地形上跟踪行走指令。 + """ + + return replace( + make_microduck_walk_flat_cfg(), + scene=humanoid_cfg.HumanoidWalkSceneCfg( + assets=humanoid_cfg.TerrainSceneAssetsCfg(), + system_camera=SystemCameraCfg( + lookat=(0.0, 0.0, 0.12), + distance=0.35, + elevation=-20.0, + azimuth=180.0, + ), + objs=StandardSceneObjsCfg( + floor=HFieldTerrainCfg( + hfield="terrain", + material="mat_ground", + ), + robot=_make_microduck_robot(), + ), + ), + spawn_xy_range=4.0, + render_spacing=0.0, + ) + + +registry.env("microduck-walk-flat")(HumanoidVelocityTrackingEnv) +registry.env("microduck-walk-rough")(HumanoidVelocityTrackingEnv) diff --git a/motrix_envs/src/motrix_envs/locomotion/humanoid/walk_np.py b/motrix_envs/src/motrix_envs/locomotion/humanoid/walk_np.py index ed70404..2f99775 100644 --- a/motrix_envs/src/motrix_envs/locomotion/humanoid/walk_np.py +++ b/motrix_envs/src/motrix_envs/locomotion/humanoid/walk_np.py @@ -171,6 +171,11 @@ def _init_buffers(self) -> None: self._phase_dt = 2.0 * np.pi * self._gait_freq * cfg.ctrl_dt self._termination_geoms = self._resolve_termination_geoms() self._num_termination_pairs = len(self._termination_geoms) + # Foot-link gravity direction at the default pose, captured on first + # reset. Foot orientation penalties are measured against this + # reference so robots whose ankle-link frames are not world-aligned + # (e.g. onshape-to-robot exports) are scored correctly. + self._default_foot_gravity: np.ndarray | None = None cur = cfg.curriculum self._penalty_terms = set(cur.penalty_terms) @@ -392,6 +397,9 @@ def reset(self, env_ids: np.ndarray) -> dict: self._reset_joint_velocity[env_ids] = 0.0 self._reset_program.execute(env_ids) self.sim_data.execute(row_ids) + if self._default_foot_gravity is None: + foot_quat = self.sim_data["foot_quat"][row_ids[0]] + self._default_foot_gravity = quaternion.rotate_inverse(foot_quat, self.gravity_vec) info = { "current_actions": np.zeros((num_reset, self._num_action), dtype=np.float32), @@ -462,8 +470,12 @@ def _r_close_feet(self, q: StateQuantities, threshold: float) -> np.ndarray: return (distance < threshold).astype(np.float32) def _r_feet_ori(self, q: StateQuantities) -> np.ndarray: + # Magnitude of the cross product between the current foot-frame gravity + # and its default-pose reference. With a world-aligned default + # (gravity = (0, 0, -1)) this reduces exactly to sqrt(gx^2 + gy^2). total = np.zeros((q.foot_quat.shape[0],), dtype=np.float32) for foot_index in range(2): foot_gravity = quaternion.rotate_inverse(q.foot_quat[:, foot_index], self.gravity_vec) - total = total + np.sqrt(np.sum(np.square(foot_gravity[:, :2]), axis=1)) + reference = self._default_foot_gravity[foot_index] + total = total + np.linalg.norm(np.cross(foot_gravity, reference), axis=1) return total diff --git a/motrix_envs/src/motrix_envs/robot/__init__.py b/motrix_envs/src/motrix_envs/robot/__init__.py index 8e447d2..6419b86 100644 --- a/motrix_envs/src/motrix_envs/robot/__init__.py +++ b/motrix_envs/src/motrix_envs/robot/__init__.py @@ -6,6 +6,7 @@ from motrix_envs.robot.booster import BoosterK1 from motrix_envs.robot.dex_evt import DexEvt from motrix_envs.robot.humanoid import HumanoidRobotCfg +from motrix_envs.robot.microduck import Microduck from motrix_envs.robot.quadruped import QuadrupedLegCfg, QuadrupedLegsCfg, QuadrupedRobotCfg from motrix_envs.robot.unitree import ( UnitreeG129Dof, @@ -19,12 +20,14 @@ registry.robotcfg("go1")(UnitreeGo1Robot) registry.robotcfg("go2")(UnitreeGo2Robot) registry.robotcfg("k1")(BoosterK1) +registry.robotcfg("microduck")(Microduck) __all__ = [ "AnymalC", "BoosterK1", "DexEvt", "HumanoidRobotCfg", + "Microduck", "QuadrupedLegCfg", "QuadrupedLegsCfg", "QuadrupedRobotCfg", diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/README.md b/motrix_envs/src/motrix_envs/robot/assets/microduck/README.md new file mode 100644 index 0000000..e42fd3b --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/README.md @@ -0,0 +1,14 @@ +# Microduck robot model + +Ported from https://github.com/pollen-robotics/microduck_rl +(`src/mjlab_microduck/robot/microduck/robot_walk.xml`, renamed to +`microduck.xml`). Relative to upstream, a named termination capsule +`trunk_collision` was added on `trunk_base` for the humanoid walk task's +fall-termination contact query; everything else is unmodified. + +- Upstream code and MJCF: Apache License 2.0 +- 3D mesh files (`assets/*.stl`): Creative Commons BY-SA-NC + (non-commercial), per the upstream README + +The Microduck is a ~800 g, ~25 cm bipedal robot by Pollen Robotics with 14 +actuated joints (5 per leg, 4 neck/head). diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_left.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_left.stl new file mode 100644 index 0000000..b00c6d5 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_left.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ec1bd89205b407efebaa9a8898ad9fea741389d87b80ecc5c96b21d1668119e +size 248984 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_right.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_right.stl new file mode 100644 index 0000000..857b787 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/ankle_right.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f83f388ae00787415e76b8ecaab769f08f25b8c4e90c1937ff0fc4d68d81722e +size 248884 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/banana_pcb_locker.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/banana_pcb_locker.stl new file mode 100644 index 0000000..bf99cee --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/banana_pcb_locker.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42ddde9d01bf5d91f88b99847017f14d97d2537ee27383c6d41dd8bf30a51134 +size 69484 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bearing_roll.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bearing_roll.stl new file mode 100644 index 0000000..14bcaa9 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bearing_roll.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5603a64b2d965ad75ea847767b5798130852e12a658fde7b5068fc457e2adcc +size 121084 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bottom_head_shell.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bottom_head_shell.stl new file mode 100644 index 0000000..ae58da7 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/bottom_head_shell.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1820f7cf96646b0fc0c431f07da87b91b13111bfa9857ab1bf2b1fc3388f8da2 +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/elec_rpi_robot_hat_pcb.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/elec_rpi_robot_hat_pcb.stl new file mode 100644 index 0000000..809cbab --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/elec_rpi_robot_hat_pcb.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a61582759d82b5b4df9b7d11099bf1bb3944fc77e6b74d3237adf24dd5d03d7 +size 942884 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/face_part.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/face_part.stl new file mode 100644 index 0000000..4b132af --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/face_part.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0e89a173e7bdc8038c4d413f458140d8f42f1a2f098f67a887c2388eff31718 +size 570684 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_left.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_left.stl new file mode 100644 index 0000000..6f42db4 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_left.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1f5d43456d0f522bd60870153795f1e8112c1adf9b8b4373b59608d9545c044 +size 996184 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_right.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_right.stl new file mode 100644 index 0000000..33e6155 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/foot_right.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54f97fa1f0e0e611d31329df9f3c3b17ffcca2d695c579d1d32cbe99e8a6c7ef +size 997684 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/hip_l.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/hip_l.stl new file mode 100644 index 0000000..a8d077b --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/hip_l.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e175896932d76dc7648cf939aa39cb7b1d188b783d91745adb9a4cec65e9052c +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw.stl new file mode 100644 index 0000000..13d7fad --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45cdbd319a4583095d8daa0db07dee726522a743d59acb82d27d82ded500a1cf +size 495984 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw_soft.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw_soft.stl new file mode 100644 index 0000000..15afa98 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/jaw_soft.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d2ade4e86df4b37ce313bb9b96023109dd04bab39dba08b154e76d332fbce2e +size 336384 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/left_shell.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/left_shell.stl new file mode 100644 index 0000000..eb7cdcc --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/left_shell.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2abde8e2b79c9b011cdb11957c67ab4287b45d7301affd4235ab81141118e331 +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/leg.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/leg.stl new file mode 100644 index 0000000..6f805ff --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/leg.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a91cc1ae31ecd9e0c1ba93f3bb2ad8a12e29bff24ec27d1ac66c0c3037d9fcf +size 738884 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/lens.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/lens.stl new file mode 100644 index 0000000..6fd0565 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/lens.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1c717432ff7666398de58adec6a1b7e03a7e45aa155dbfe27a6152ef19663e5 +size 93684 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/m12_lens_holder.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/m12_lens_holder.stl new file mode 100644 index 0000000..adb8919 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/m12_lens_holder.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:031d5ffaf6247328cf6d020f6b6695ffb3f8d3f22cf923b85811351d89ecfd7c +size 549684 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/motor_support.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/motor_support.stl new file mode 100644 index 0000000..5af06e1 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/motor_support.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce0aa5bef82c7474512d302568c341b9769b3005e0bdb91a19c2eaaba6dad480 +size 180884 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck.stl new file mode 100644 index 0000000..15c4264 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6e86b886153be8a8a776c1cb219c074ee9e89c8bd431117662a2d9e504d7cb5 +size 73484 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck_pitch.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck_pitch.stl new file mode 100644 index 0000000..d8f2ab0 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/neck_pitch.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a685ce65348ae4d0d49417c69a7a8871f8bbd54a6e3419362be9c334abd5d2ae +size 356084 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/noenoeil.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/noenoeil.stl new file mode 100644 index 0000000..de45b9a --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/noenoeil.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1167b3d5ad7fbbe6e63296bcd0c6b61556611e6bc1367b15ecfd27e3abb72f5 +size 49284 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/np_f970.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/np_f970.stl new file mode 100644 index 0000000..6d8c183 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/np_f970.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e55a48281331b3b7b1a363d2e93b6012f37c52045b6dc922a149ca7c1b38388 +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/pcb__raspberry_pi_zero_2_w.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/pcb__raspberry_pi_zero_2_w.stl new file mode 100644 index 0000000..af19201 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/pcb__raspberry_pi_zero_2_w.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:802cc66fc5447c67fc642d1339e55160df10f2ad04a65efd130e35fb7b2d3c90 +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/power_support.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/power_support.stl new file mode 100644 index 0000000..eae61fb --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/power_support.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee45e397d3f3422f34940a4b61156c3c11c7e8c2b240451ce5f9ddd1622a130e +size 641684 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/right_shell.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/right_shell.stl new file mode 100644 index 0000000..bc00058 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/right_shell.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3efba3e193cc6aa49d49a9c92cdded0504917397c9e2c5b8fe002a958e2f2d2b +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration__22x16x4.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration__22x16x4.stl new file mode 100644 index 0000000..08349f8 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration__22x16x4.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:295ee68ed9766c91fe06d3a2a49d39f7c2f56cc943395d293afdeb013123435e +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration_default.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration_default.stl new file mode 100644 index 0000000..399952a --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/seeed_bearing__configuration_default.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e815f3483a2e7b80844f3c0938a47e53db38b12faed9b2d364414c19e4322da4 +size 1048584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/soft_mouth_top.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/soft_mouth_top.stl new file mode 100644 index 0000000..f9dda84 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/soft_mouth_top.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c2b52b0b6e2ce944a1e1b5a0838e1216be1ad3a34c8cdf91be186e634aec57b +size 393584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_left.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_left.stl new file mode 100644 index 0000000..6c156b9 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_left.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e028c8b09fecd4cb354d042bf7fd95a1a1bc2d524d760d62137fa8d74093fc23 +size 789484 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_right.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_right.stl new file mode 100644 index 0000000..bafccb2 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/sole_right.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f5068872f3add10eb967327651445bdfca635f5a8a01befa0ef05e462b72b4c +size 795184 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/speaker.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/speaker.stl new file mode 100644 index 0000000..b1bc8ec --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/speaker.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75a35f9bcf94cd8e52f99b3fd6ae667b48f52503f9075ee2f699eebe8e7b3ddf +size 684 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/top_head_shell.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/top_head_shell.stl new file mode 100644 index 0000000..2a20b43 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/top_head_shell.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d01f396f514c9d2185cf6b8250b1d8382ac3fa0702eb525bcb284f706196291 +size 806084 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/trunk_base.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/trunk_base.stl new file mode 100644 index 0000000..1697798 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/trunk_base.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa4f853ae555a8b765e585ce4519c0bbff325ce298ec15a1437792c52a0beac1 +size 184984 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_left.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_left.stl new file mode 100644 index 0000000..6b9c2a7 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_left.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cb1c12dfa6d366a318bb1ec16f585427b9a185afeb7738d0a6f41f5db46a07d +size 612584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_right.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_right.stl new file mode 100644 index 0000000..43605ee --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_right.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5caf996cc3e151b01f393cdc6a6a6a940c1d86179d8b7c7f5f85cf1f1ac2fb63 +size 612584 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_rigidity_plate.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_rigidity_plate.stl new file mode 100644 index 0000000..f7c2ef3 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/upper_leg_rigidity_plate.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:231b3e5f1e24d146d735dc158cbcfec0c95a0d0ca8c500f61f04d15d0b665652 +size 180084 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/xl330.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/xl330.stl new file mode 100644 index 0000000..1dc4210 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/xl330.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96238ed0d020ce0009cf1a5aa4b6926176c800840318ee65e4375be2298d2744 +size 206384 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw2roll.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw2roll.stl new file mode 100644 index 0000000..7a607ed --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw2roll.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c8f193ec7cb40be0f48eeb5fba81bce2c87f12e1aab3f77f1bc4981386e1495 +size 620284 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw_roll_motion.stl b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw_roll_motion.stl new file mode 100644 index 0000000..14c2741 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/assets/yaw_roll_motion.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0c9902dee19f06cdb7fa930292e0e1c91e2dc59baa971211495b58ff76c3c60 +size 290784 diff --git a/motrix_envs/src/motrix_envs/robot/assets/microduck/microduck.xml b/motrix_envs/src/motrix_envs/robot/assets/microduck/microduck.xml new file mode 100644 index 0000000..35c9e22 --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/assets/microduck/microduck.xml @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/motrix_envs/src/motrix_envs/robot/microduck.py b/motrix_envs/src/motrix_envs/robot/microduck.py new file mode 100644 index 0000000..808d74c --- /dev/null +++ b/motrix_envs/src/motrix_envs/robot/microduck.py @@ -0,0 +1,96 @@ +# Copyright Motphys Technology Co., Ltd. 2025, 2026 +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +from motrix_env_core.config import configclass +from motrix_env_core.config.scene import KeyPoseCfg, MjcfFileCfg +from motrix_envs.robot.humanoid import HumanoidRobotCfg + +MICRODUCK_ASSET_DIR = Path(__file__).parent / "assets" / "microduck" +_MICRODUCK_MJCF = MICRODUCK_ASSET_DIR / "microduck.xml" + + +@configclass(kw_only=True) +class Microduck(HumanoidRobotCfg): + """Built-in Pollen Robotics Microduck 14-DoF biped robot configuration. + + Ported from https://github.com/pollen-robotics/microduck_rl (Apache-2.0 + code; mesh assets are CC BY-SA-NC, see the asset directory README). + """ + + model: MjcfFileCfg = MjcfFileCfg(file=_MICRODUCK_MJCF) + base_link_name: str = "trunk_base" + left_foot_link_name: str = "ankle_left" + right_foot_link_name: str = "ankle_right" + key_pose: KeyPoseCfg = KeyPoseCfg( + joint_names=[ + "left_hip_yaw", + "left_hip_roll", + "left_hip_pitch", + "left_knee", + "left_ankle", + "neck_pitch", + "head_pitch", + "head_yaw", + "head_roll", + "right_hip_yaw", + "right_hip_roll", + "right_hip_pitch", + "right_knee", + "right_ankle", + ], + poses={ + # Upstream keyframe "STAND" (trunk at z=0.12). + "default": [ + 0.0, + -0.08726646259971647, + -0.457924, + -0.004940, + 0.452984, + 0.3490658503988659, + 0.3490658503988659, + 0.0, + 0.0, + 0.0, + 0.08726646259971647, + 0.457924, + 0.004940, + -0.452984, + ], + # Upstream keyframe "SIT" (trunk at z=0.07). + "sit": [ + 0.0, + 0.0, + -0.5236, + 1.0472, + 0.0, + 0.5, + 1.6, + 0.0, + 0.0, + 0.0, + 0.0, + 0.5236, + -1.0472, + 0.0, + ], + # Upstream keyframe "FOLD" (trunk at z=0.07). + "fold": [ + 0.0, + 0.0, + 1.5708, + 1.5708, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.5708, + -1.5708, + 0.0, + ], + }, + ) diff --git a/motrix_envs/tests/test_humanoid_walk.py b/motrix_envs/tests/test_humanoid_walk.py index f97fd84..b630383 100644 --- a/motrix_envs/tests/test_humanoid_walk.py +++ b/motrix_envs/tests/test_humanoid_walk.py @@ -16,6 +16,10 @@ make_g129dof_walk_rough_cfg, ) from motrix_envs.locomotion.humanoid.k1 import make_k1_walk_flat_cfg, make_k1_walk_rough_cfg +from motrix_envs.locomotion.humanoid.microduck import ( + make_microduck_walk_flat_cfg, + make_microduck_walk_rough_cfg, +) from motrix_envs.locomotion.humanoid.walk_np import HumanoidVelocityTrackingEnv @@ -28,6 +32,8 @@ ("dex-evt-walk-rough", 23, 82, 85), ("k1-walk-flat", 22, 79, 82), ("k1-walk-rough", 22, 79, 82), + ("microduck-walk-flat", 14, 55, 58), + ("microduck-walk-rough", 14, 55, 58), ], ) def test_walk_presets_use_shared_humanoid_env(env_name, action_dim, policy_dim, value_dim): @@ -76,12 +82,27 @@ def test_k1_walk_uses_shared_reward_scales_and_named_contact_termination(): np.testing.assert_allclose(quantities.foot_clearance, 0.00716, atol=2e-3) +def test_microduck_walk_uses_named_contact_termination(): + walk_cfg = make_microduck_walk_flat_cfg() + env = HumanoidVelocityTrackingEnv(walk_cfg, num_envs=2) + env.init_state() + quantities = env._state_quantities(slice(None), np.arange(2, dtype=np.int64)) + + assert env._num_termination_pairs == 1 + np.testing.assert_allclose(quantities.foot_clearance, 0.0, atol=4e-3) + # Microduck ankle-link frames are ~90 degrees away from world-aligned, so + # the foot-orientation penalty must be measured against the default-pose + # reference: it is zero at the default stance for any link orientation. + np.testing.assert_allclose(env._r_feet_ori(quantities), 0.0, atol=1e-3) + + @pytest.mark.parametrize( ("make_flat_cfg", "make_rough_cfg"), [ (make_g129dof_walk_flat_cfg, make_g129dof_walk_rough_cfg), (make_dex_evt_walk_flat_cfg, make_dex_evt_walk_rough_cfg), (make_k1_walk_flat_cfg, make_k1_walk_rough_cfg), + (make_microduck_walk_flat_cfg, make_microduck_walk_rough_cfg), ], ) def test_walk_rough_only_overrides_scene_spawn_range_and_render_spacing(make_flat_cfg, make_rough_cfg): @@ -104,22 +125,24 @@ def test_walk_rough_only_overrides_scene_spawn_range_and_render_spacing(make_fla @pytest.mark.parametrize( - "make_cfg", + ("make_cfg", "camera_distance", "camera_lookat"), [ - make_g129dof_walk_flat_cfg, - make_g129dof_walk_rough_cfg, - make_dex_evt_walk_flat_cfg, - make_dex_evt_walk_rough_cfg, - make_k1_walk_flat_cfg, - make_k1_walk_rough_cfg, + (make_g129dof_walk_flat_cfg, 6.0, None), + (make_g129dof_walk_rough_cfg, 6.0, None), + (make_dex_evt_walk_flat_cfg, 6.0, None), + (make_dex_evt_walk_rough_cfg, 6.0, None), + (make_k1_walk_flat_cfg, 6.0, None), + (make_k1_walk_rough_cfg, 6.0, None), + (make_microduck_walk_flat_cfg, 0.35, (0.0, 0.0, 0.12)), + (make_microduck_walk_rough_cfg, 0.35, (0.0, 0.0, 0.12)), ], ) -def test_walk_presets_use_shared_system_camera(make_cfg): +def test_walk_presets_use_shared_system_camera(make_cfg, camera_distance, camera_lookat): scene = make_cfg().scene assert isinstance(scene, HumanoidWalkSceneCfg) - assert scene.system_camera.lookat is None - assert scene.system_camera.distance == pytest.approx(6.0) + assert scene.system_camera.lookat == camera_lookat + assert scene.system_camera.distance == pytest.approx(camera_distance) assert scene.system_camera.elevation == pytest.approx(-20.0) assert scene.system_camera.azimuth == pytest.approx(180.0) diff --git a/motrix_envs/tests/test_robot_cfg.py b/motrix_envs/tests/test_robot_cfg.py index 27b8a1a..64d7d99 100644 --- a/motrix_envs/tests/test_robot_cfg.py +++ b/motrix_envs/tests/test_robot_cfg.py @@ -27,6 +27,7 @@ BoosterK1, DexEvt, HumanoidRobotCfg, + Microduck, QuadrupedLegCfg, QuadrupedRobotCfg, UnitreeG129Dof, @@ -36,6 +37,7 @@ from motrix_envs.robot.anymal import ANYMAL_C_ASSET_DIR from motrix_envs.robot.booster import BOOSTER_K1_ASSET_DIR from motrix_envs.robot.dex_evt import DEX_EVT_ASSET_DIR +from motrix_envs.robot.microduck import MICRODUCK_ASSET_DIR from motrix_envs.robot.unitree import UNITREE_G1_ASSET_DIR, UNITREE_GO1_ASSET_DIR, UNITREE_GO2_ASSET_DIR @@ -69,6 +71,11 @@ class AnymalCSceneObjsCfg(SceneObjsCfg): robot: AnymalC = AnymalC() +@configclass +class MicroduckSceneObjsCfg(SceneObjsCfg): + robot: Microduck = Microduck() + + def test_builtin_robot_configs_are_registered(): expected_types = { "anymal_c": AnymalC, @@ -77,6 +84,7 @@ def test_builtin_robot_configs_are_registered(): "go1": UnitreeGo1Robot, "go2": UnitreeGo2Robot, "k1": BoosterK1, + "microduck": Microduck, } assert set(expected_types).issubset(registry.list_registered_robots()) @@ -91,11 +99,12 @@ def test_builtin_robot_configs_use_robot_only_files(): go1 = UnitreeGo1Robot() go2 = UnitreeGo2Robot() dex_evt = DexEvt() + microduck = Microduck() - assert all(isinstance(robot, RobotCfg) for robot in (anymal_c, k1, g1, go1, go2, dex_evt)) + assert all(isinstance(robot, RobotCfg) for robot in (anymal_c, k1, g1, go1, go2, dex_evt, microduck)) assert isinstance(anymal_c, QuadrupedRobotCfg) assert isinstance(anymal_c.model, MjcfFileCfg) - assert all(isinstance(robot, HumanoidRobotCfg) for robot in (k1, g1, dex_evt)) + assert all(isinstance(robot, HumanoidRobotCfg) for robot in (k1, g1, dex_evt, microduck)) assert isinstance(k1.model, MjcfFileCfg) assert isinstance(g1.model, MjcfFileCfg) assert isinstance(go1, QuadrupedRobotCfg) @@ -109,6 +118,9 @@ def test_builtin_robot_configs_use_robot_only_files(): assert Path(go1.model.file).parent == UNITREE_GO1_ASSET_DIR assert Path(go2.model.file).parent == UNITREE_GO2_ASSET_DIR assert Path(dex_evt.model.file).parent == DEX_EVT_ASSET_DIR + assert Path(microduck.model.file).parent == MICRODUCK_ASSET_DIR + assert Path(microduck.model.file).name == "microduck.xml" + assert not Path(microduck.model.file).name.startswith("scene_") assert Path(k1.model.file).name == "k1_22dof.xml" assert Path(g1.model.file).name == "g1_29dof.xml" assert Path(go1.model.file).name == "go1_position_actuator.xml" @@ -129,6 +141,7 @@ def test_builtin_robot_configs_use_robot_only_files(): (UnitreeG129Dof(), ("left_ankle_roll_link", "right_ankle_roll_link")), (DexEvt(), ("ankle_roll_l_link", "ankle_roll_r_link")), (BoosterK1(), ("left_foot_link", "right_foot_link")), + (Microduck(), ("ankle_left", "ankle_right")), ], ) def test_builtin_humanoids_expose_foot_link_semantics(robot, foot_link_names): @@ -339,6 +352,30 @@ def test_builtin_go2_asset_contains_all_referenced_meshes(): assert referenced_meshes == packaged_meshes +def test_builtin_microduck_robot_builds_model(): + model = build_scene_model(SceneCfg(objs=MicroduckSceneObjsCfg())) + + assert model.body_names == ["trunk_base"] + assert model.get_body("trunk_base").floatingbase is not None + assert model.num_actuators == 14 + assert "left_hip_yaw" in model.joint_names + assert "right_ankle" in model.joint_names + assert model.get_site("left_foot").parent_link.name == "ankle_left" + assert model.get_site("right_foot").parent_link.name == "ankle_right" + assert all(actuator.ctrl_range is not None for actuator in model.actuators) + assert all(actuator.force_range is not None for actuator in model.actuators) + assert [actuator.target_name for actuator in model.actuators] == Microduck().key_pose.joint_names + assert len(Microduck().key_pose.poses["default"]) == model.num_actuators + + +def test_builtin_microduck_asset_contains_only_referenced_meshes(): + root = ET.parse(MICRODUCK_ASSET_DIR / "microduck.xml").getroot() + referenced_meshes = {mesh.get("file") for mesh in root.findall("./asset/mesh")} + packaged_meshes = {path.name for path in (MICRODUCK_ASSET_DIR / "assets").iterdir() if path.is_file()} + + assert referenced_meshes == packaged_meshes + + def test_builtin_dex_evt_robot_builds_model(): model = build_scene_model(SceneCfg(objs=DexEvtSceneObjsCfg()))