From 2819a5ca5e7b1ff2b30ba79c43569f5e20121f20 Mon Sep 17 00:00:00 2001 From: ZheyangXu Date: Wed, 28 Oct 2026 16:32:33 +0800 Subject: [PATCH] feat: add cyberdog2 flat locomotion --- .../cyberdog2-walk-flat/motrix.fastsac.yaml | 23 ++ .../task/cyberdog2-walk-flat/rslrl.ppo.yaml | 36 +++ .../cyberdog2-walk-flat/skrl.ppo.jax.yaml | 15 ++ .../cyberdog2-walk-flat/skrl.ppo.torch.yaml | 13 + .../task/cyberdog2-walk-flat/skrl.ppo.yaml | 40 +++ .../config/task/cyberdog2-walk-flat/sim.yaml | 25 ++ .../locomotion/quadruped/__init__.py | 2 +- .../motrix_envs/locomotion/quadruped/cfg.py | 5 +- .../locomotion/quadruped/cyberdog2.py | 93 +++++++ motrix_robots/src/motrix_robots/__init__.py | 2 + .../assets/cyberdog2/assets/abad.obj | 3 + .../assets/cyberdog2/assets/body.obj | 3 + .../assets/cyberdog2/assets/hip.obj | 3 + .../assets/cyberdog2/assets/hip_mirror.obj | 3 + .../assets/cyberdog2/assets/knee.obj | 3 + .../assets/cyberdog2/cyberdog2.xml | 241 ++++++++++++++++++ motrix_robots/src/motrix_robots/cyberdog2.py | 44 ++++ .../src/motrix_robots/registration.py | 2 + 18 files changed, 552 insertions(+), 4 deletions(-) create mode 100644 configs/task/cyberdog2-walk-flat/motrix.fastsac.yaml create mode 100644 configs/task/cyberdog2-walk-flat/rslrl.ppo.yaml create mode 100644 configs/task/cyberdog2-walk-flat/skrl.ppo.jax.yaml create mode 100644 configs/task/cyberdog2-walk-flat/skrl.ppo.torch.yaml create mode 100644 configs/task/cyberdog2-walk-flat/skrl.ppo.yaml create mode 100644 motrix_deploy_tasks/src/motrix_deploy_tasks/config/task/cyberdog2-walk-flat/sim.yaml create mode 100644 motrix_envs/src/motrix_envs/locomotion/quadruped/cyberdog2.py create mode 100644 motrix_robots/src/motrix_robots/assets/cyberdog2/assets/abad.obj create mode 100644 motrix_robots/src/motrix_robots/assets/cyberdog2/assets/body.obj create mode 100644 motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip.obj create mode 100644 motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip_mirror.obj create mode 100644 motrix_robots/src/motrix_robots/assets/cyberdog2/assets/knee.obj create mode 100644 motrix_robots/src/motrix_robots/assets/cyberdog2/cyberdog2.xml create mode 100644 motrix_robots/src/motrix_robots/cyberdog2.py diff --git a/configs/task/cyberdog2-walk-flat/motrix.fastsac.yaml b/configs/task/cyberdog2-walk-flat/motrix.fastsac.yaml new file mode 100644 index 00000000..70bb7340 --- /dev/null +++ b/configs/task/cyberdog2-walk-flat/motrix.fastsac.yaml @@ -0,0 +1,23 @@ +# @package _global_ +# Task cyberdog2-walk-flat: motrix.fastsac shared training recipe. +defaults: + - /algo_base@algo: motrix.fastsac + - _self_ +task: + env: cyberdog2-walk-flat + rllib: motrix + algo: fastsac +num_envs: 1024 +play_num_envs: 16 +seed: 1 +checkpoint: + interval: 1000 +algo: + agent: + actor_hidden_dim: 512 + critic_hidden_dim: 512 + num_atoms: 101 + v_min: -30.0 + v_max: 30.0 + num_updates: 2 + buffer_size: 512 diff --git a/configs/task/cyberdog2-walk-flat/rslrl.ppo.yaml b/configs/task/cyberdog2-walk-flat/rslrl.ppo.yaml new file mode 100644 index 00000000..fe0f57fc --- /dev/null +++ b/configs/task/cyberdog2-walk-flat/rslrl.ppo.yaml @@ -0,0 +1,36 @@ +# @package _global_ +# Task cyberdog2-walk-flat: rslrl.ppo shared training recipe. +defaults: + - /algo_base@algo: rslrl.ppo + - _self_ +task: + env: cyberdog2-walk-flat + rllib: rslrl + algo: ppo + train_backend: null +num_envs: 49152 +play_num_envs: 16 +seed: 1 +checkpoint: + interval: 100 +algo: + max_iterations: 3000 + obs_groups: + actor: + - policy + critic: + - value + actor: + hidden_dims: + - 512 + - 256 + - 128 + init_noise_std: 0.5 + critic: + hidden_dims: + - 512 + - 256 + - 128 + algorithm: + desired_kl: 0.01 + entropy_coef: 0.001 diff --git a/configs/task/cyberdog2-walk-flat/skrl.ppo.jax.yaml b/configs/task/cyberdog2-walk-flat/skrl.ppo.jax.yaml new file mode 100644 index 00000000..e4aff9cf --- /dev/null +++ b/configs/task/cyberdog2-walk-flat/skrl.ppo.jax.yaml @@ -0,0 +1,15 @@ +# @package _global_ +# Task cyberdog2-walk-flat: skrl.ppo, train_backend=jax delta. +defaults: + - /task/cyberdog2-walk-flat/skrl.ppo@_global_ + - _self_ +task: + train_backend: jax +algo: + agent: + rollouts: 12 + learning_rate: 0.0008 + learning_rate_scheduler_kwargs: + kl_threshold: 0.03 + grad_norm_clip: 0.75 + kl_threshold: 0.07 diff --git a/configs/task/cyberdog2-walk-flat/skrl.ppo.torch.yaml b/configs/task/cyberdog2-walk-flat/skrl.ppo.torch.yaml new file mode 100644 index 00000000..de2e4d91 --- /dev/null +++ b/configs/task/cyberdog2-walk-flat/skrl.ppo.torch.yaml @@ -0,0 +1,13 @@ +# @package _global_ +# Task cyberdog2-walk-flat: skrl.ppo, train_backend=torch delta. +defaults: + - /task/cyberdog2-walk-flat/skrl.ppo@_global_ + - _self_ +task: + train_backend: torch +algo: + agent: + rollouts: 24 + learning_rate: 0.0003 + learning_rate_scheduler_kwargs: + kl_threshold: 0.01 diff --git a/configs/task/cyberdog2-walk-flat/skrl.ppo.yaml b/configs/task/cyberdog2-walk-flat/skrl.ppo.yaml new file mode 100644 index 00000000..d80f11ae --- /dev/null +++ b/configs/task/cyberdog2-walk-flat/skrl.ppo.yaml @@ -0,0 +1,40 @@ +# @package _global_ +# Task cyberdog2-walk-flat: skrl.ppo shared training recipe. +defaults: + - /algo_base@algo: skrl.ppo + - _self_ +task: + env: cyberdog2-walk-flat + rllib: skrl + algo: ppo + train_backend: null +num_envs: 1024 +play_num_envs: 16 +seed: 1 +checkpoint: + interval: 100 +algo: + models: + value: + hiddens: + - 512 + - 256 + - 128 + policy: + initial_log_std: -0.6931471805599453 + hiddens: + - 512 + - 256 + - 128 + separate: true + agent: + rollouts: 24 + mini_batches: 4 + learning_epochs: 5 + learning_rate: 0.0003 + learning_rate_scheduler_kwargs: + kl_threshold: 0.01 + entropy_loss_scale: 0.001 + value_loss_scale: 1.0 + trainer: + timesteps: 12000 diff --git a/motrix_deploy_tasks/src/motrix_deploy_tasks/config/task/cyberdog2-walk-flat/sim.yaml b/motrix_deploy_tasks/src/motrix_deploy_tasks/config/task/cyberdog2-walk-flat/sim.yaml new file mode 100644 index 00000000..aa320ba0 --- /dev/null +++ b/motrix_deploy_tasks/src/motrix_deploy_tasks/config/task/cyberdog2-walk-flat/sim.yaml @@ -0,0 +1,25 @@ +# @package _global_ +# cyberdog2 flat sim spawns crouched, which enables the stand-up preparation lifecycle +# (R then P in the viewer), and requires the clipped crouch translation below. +defaults: + - /runtime: sim + - _self_ + +artifact: artifacts/cyberdog2-walk-flat.deploy + +runtime: + backend: mujoco + sensor_bindings: + base_angular_velocity: gyro + base_linear_acceleration: accelerometer + base_linear_velocity: global_linvel + deploy_env_id: flat + robot_id: cyberdog2 + robot_init_key_pose: defaults + # Imported root starts at 0.445 m; place clipped crouch close to floor + # without an initial penetration in the flat MuJoCo scene. + robot_translation: [0.0, 0.0, -0.32] + robot_rotation: [0.0, 0.0, 0.0, 1.0] + physics: + dt: 0.002 + solver_iterations: 100 diff --git a/motrix_envs/src/motrix_envs/locomotion/quadruped/__init__.py b/motrix_envs/src/motrix_envs/locomotion/quadruped/__init__.py index 2809acc2..1b84a66c 100644 --- a/motrix_envs/src/motrix_envs/locomotion/quadruped/__init__.py +++ b/motrix_envs/src/motrix_envs/locomotion/quadruped/__init__.py @@ -1,4 +1,4 @@ # Copyright Motphys Technology Co., Ltd. 2025, 2026 # SPDX-License-Identifier: Apache-2.0 -from . import anymalc, go1, go2, walk_np # noqa: F401 register envs and expose base task +from . import anymalc, cyberdog2, go1, go2, walk_np # noqa: F401 register envs and expose base task diff --git a/motrix_envs/src/motrix_envs/locomotion/quadruped/cfg.py b/motrix_envs/src/motrix_envs/locomotion/quadruped/cfg.py index ec2e825e..97a54b8f 100644 --- a/motrix_envs/src/motrix_envs/locomotion/quadruped/cfg.py +++ b/motrix_envs/src/motrix_envs/locomotion/quadruped/cfg.py @@ -157,8 +157,8 @@ def validate(self) -> None: @configclass class VelocityCommandCfg: - lower: NDArray[np.float32] = np.array([0.5, 0.0, 0.0], dtype=np.float32) - upper: NDArray[np.float32] = np.array([0.5, 0.0, 0.0], dtype=np.float32) + lower: NDArray[np.float32] = np.array([1, 0.0, 0.0], dtype=np.float32) + upper: NDArray[np.float32] = np.array([1, 0.0, 0.0], dtype=np.float32) standing_probability: float = 0.0 standing_threshold: float = 0.05 resampling_seconds_range: tuple[float, float] | None = None @@ -172,7 +172,6 @@ def validate(self) -> None: f"got {self.resampling_seconds_range}" ) - @configclass class Commands: velocity: VelocityCommandCfg = VelocityCommandCfg() diff --git a/motrix_envs/src/motrix_envs/locomotion/quadruped/cyberdog2.py b/motrix_envs/src/motrix_envs/locomotion/quadruped/cyberdog2.py new file mode 100644 index 00000000..79759f23 --- /dev/null +++ b/motrix_envs/src/motrix_envs/locomotion/quadruped/cyberdog2.py @@ -0,0 +1,93 @@ +# Copyright Motphys Technology Co., Ltd. 2025, 2026 +# SPDX-License-Identifier: Apache-2.0 + +"""Cyberdog2 flat- and rough-terrain walk configuration and environment registration.""" + +from dataclasses import replace + +import numpy as np +from motrix_robots.cyberdog2 import Cyberdog2Robot + +from motrix_env_core import registry +from motrix_env_core.config import configclass +from motrix_env_core.config.scene import FlatTerrainCfg +from motrix_envs.config.scene import StandardSceneObjsCfg +from motrix_envs.locomotion.quadruped.cfg import ( + Commands, + QuadrupedSceneCfg, + QuadrupedWalkEnvCfg, + QuadrupedWalkRandomizationCfg, + RewardConfig, + RewardScales, + Sensor, + VelocityCommandCfg, +) +from motrix_envs.locomotion.quadruped.walk_np import QuadrupedWalkTask + + +@registry.envcfg("cyberdog2-walk-flat") +@configclass +class Cyberdog2WalkCfg(QuadrupedWalkEnvCfg): + """Track walking commands with Cyberdog2 on flat ground. + + zh_CN: 控制 Cyberdog2 在平地上跟踪行走指令。 + """ + + render_spacing: float = 0.0 + spawn_xy_range: float = 4.0 + commands: Commands = Commands( + velocity=VelocityCommandCfg( + lower=np.array([1.0, -0, -0], dtype=np.float32), + upper=np.array([1.0, 0, 0], dtype=np.float32), + standing_probability=0.1, + resampling_seconds_range=(5.0, 10.0), + ) + ) + + # CyberDog2 keeps its own IMU sensor names; remap them to the task's + # source-independent sensor roles instead of renaming the model sensors. + sensor: Sensor = Sensor( + gyro="imu_ang_vel", + local_linvel="imu_lin_vel", + ) + + randomization: QuadrupedWalkRandomizationCfg = QuadrupedWalkRandomizationCfg( + enabled=True, + joint_pos_noise=0.03, + joint_vel_noise=0.1, + base_lin_vel_noise=(0.1, 0.1, 0.0), + base_ang_vel_noise=(0.1, 0.1, 0.1), + action_delay_steps=(0, 1), + kp_scale_range=(0.9, 1.1), + damping_scale_range=(0.9, 1.1), + sliding_friction_range=(0.4, 0.9), + base_mass_scale_range=(0.9, 1.1), + base_com_offset_noise=(0.01, 0.01, 0.005), + ) + + scene: QuadrupedSceneCfg = QuadrupedSceneCfg( + objs=StandardSceneObjsCfg( + floor=FlatTerrainCfg( + material="mat_ground", + friction=(0.6, 0.005, 0.0001), + ), + robot=Cyberdog2Robot(), + ), + ) + reward_config: RewardConfig = RewardConfig( + scales=RewardScales( + similar_to_default=-1, + ), + tracking_ang_vel_sigma=0.05, + target_foot_height=0.05, + base_height_target=0.35, + ) + + def for_play(self) -> "Cyberdog2WalkCfg": + """Return a deterministic Cyberdog2 evaluation config.""" + + cfg = super().for_play() + return replace(cfg, noise_config=replace(cfg.noise_config, level=0.0)) + + +registry.env("cyberdog2-walk-flat")(QuadrupedWalkTask) diff --git a/motrix_robots/src/motrix_robots/__init__.py b/motrix_robots/src/motrix_robots/__init__.py index 42eb5143..43537f86 100644 --- a/motrix_robots/src/motrix_robots/__init__.py +++ b/motrix_robots/src/motrix_robots/__init__.py @@ -10,6 +10,7 @@ from motrix_robots.anymal import AnymalC from motrix_robots.booster import BoosterK1 +from motrix_robots.cyberdog2 import Cyberdog2Robot from motrix_robots.dex_evt import DexEvt from motrix_robots.humanoid import HumanoidRobotCfg from motrix_robots.microduck import Microduck @@ -28,4 +29,5 @@ "UnitreeG129Dof", "UnitreeGo1Robot", "UnitreeGo2Robot", + "Cyberdog2Robot", ] diff --git a/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/abad.obj b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/abad.obj new file mode 100644 index 00000000..da97e8a6 --- /dev/null +++ b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/abad.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb3cf38f810a3fd66e31c287b250da9a9fdc8d2af13da8bb979aa376a247a812 +size 601614 diff --git a/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/body.obj b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/body.obj new file mode 100644 index 00000000..d1e7eecb --- /dev/null +++ b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/body.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:973921ffa383e6656116fbcb4c364927a3f7a412c0a26e63bd0ab62b0528d4a7 +size 7809635 diff --git a/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip.obj b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip.obj new file mode 100644 index 00000000..ca014fea --- /dev/null +++ b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d49154b3cb3878042ff0bf3720067ad073a14d74c7897bf7829cdfdd1f96a49f +size 3546335 diff --git a/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip_mirror.obj b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip_mirror.obj new file mode 100644 index 00000000..39e819f8 --- /dev/null +++ b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/hip_mirror.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1685df4baf50768a24d578a2398e8bbbffde0a6e1f41f516113154331c854d1a +size 3486194 diff --git a/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/knee.obj b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/knee.obj new file mode 100644 index 00000000..e3f7adb2 --- /dev/null +++ b/motrix_robots/src/motrix_robots/assets/cyberdog2/assets/knee.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d6a9953d407c652dba6688d71ce7cb008a813503860fd882a9bc762d43e0d32 +size 201207 diff --git a/motrix_robots/src/motrix_robots/assets/cyberdog2/cyberdog2.xml b/motrix_robots/src/motrix_robots/assets/cyberdog2/cyberdog2.xml new file mode 100644 index 00000000..14f1c39e --- /dev/null +++ b/motrix_robots/src/motrix_robots/assets/cyberdog2/cyberdog2.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/motrix_robots/src/motrix_robots/cyberdog2.py b/motrix_robots/src/motrix_robots/cyberdog2.py new file mode 100644 index 00000000..35c7ef39 --- /dev/null +++ b/motrix_robots/src/motrix_robots/cyberdog2.py @@ -0,0 +1,44 @@ +# 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_robots.quadruped import QuadrupedLegCfg, QuadrupedLegsCfg, QuadrupedRobotCfg + +CYBERDOG2_ASSET_DIR = Path(__file__).parent / "assets" / "cyberdog2" +CYBERDOG2_MJCF = CYBERDOG2_ASSET_DIR / "cyberdog2.xml" + + +@configclass(kw_only=True) +class Cyberdog2Robot(QuadrupedRobotCfg): + """Cyberdog2 quadruped robot configuration""" + + model: MjcfFileCfg = MjcfFileCfg(file=CYBERDOG2_MJCF) + base_link_name: str = "body" + # Contact geoms are the *_foot_collision boxes defined in cyberdog2.xml; + # unlike Go2, CyberDog2 does not name its foot collision geom "FL_foot". + legs: QuadrupedLegsCfg = QuadrupedLegsCfg( + front_left=QuadrupedLegCfg(contact_geom_name="FL_foot_collision"), + front_right=QuadrupedLegCfg(contact_geom_name="FR_foot_collision"), + rear_left=QuadrupedLegCfg(contact_geom_name="RL_foot_collision"), + rear_right=QuadrupedLegCfg(contact_geom_name="RR_foot_collision"), + ) + key_pose: KeyPoseCfg = KeyPoseCfg( + joint_names=[ + "FR_abad_joint", + "FL_abad_joint", + "RR_abad_joint", + "RL_abad_joint", + "FR_hip_joint", + "FL_hip_joint", + "RR_hip_joint", + "RL_hip_joint", + "FR_knee_joint", + "FL_knee_joint", + "RR_knee_joint", + "RL_knee_joint", + ], + poses={"default": [-0.10, 0.10, -0.10, 0.10, 0.80, 0.80, 1.00, 1.00, -1.50, -1.50, -1.50, -1.50]}, + ) diff --git a/motrix_robots/src/motrix_robots/registration.py b/motrix_robots/src/motrix_robots/registration.py index 06895d8a..45ebdd33 100644 --- a/motrix_robots/src/motrix_robots/registration.py +++ b/motrix_robots/src/motrix_robots/registration.py @@ -10,6 +10,7 @@ from motrix_env_core import registry from motrix_robots.anymal import AnymalC from motrix_robots.booster import BoosterK1 +from motrix_robots.cyberdog2 import Cyberdog2Robot from motrix_robots.dex_evt import DexEvt from motrix_robots.microduck import Microduck from motrix_robots.unitree import ( @@ -28,3 +29,4 @@ def register() -> None: registry.register_robot_config("go2", UnitreeGo2Robot) registry.register_robot_config("k1", BoosterK1) registry.register_robot_config("microduck", Microduck) + registry.register_robot_config("cyberdog2", Cyberdog2Robot)