Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ MotrixLab 是构建在 MotrixSim 仿真后端之上的强化学习框架,提

项目使用 UV workspace,包含以下 package:

- `motrix_env_core`:backend 无关的环境框架、配置与机器人 registry,不依赖具体 simulator
- `motrix_env_core`:backend 无关的环境框架、配置与机器人 registry
- `motrix_env_motrixsim`:MotrixSim 仿真后端(SimBackend、scene compiler、renderer、torch frontend)
- `motrix_env_mujoco`:仅用于编译的 MuJoCo scene 后端
- `motrix_robots`:可复用机器人配置、默认姿态与模型资产;仅依赖 core。框架代码从定义模块导入模型类,例如 `motrix_robots.unitree`
Expand All @@ -23,7 +23,7 @@ MotrixLab 是构建在 MotrixSim 仿真后端之上的强化学习框架,提
- `motrix_deploy`:框架无关的 artifact、运行时契约与部署 CLI
- `motrix_deploy_mujoco`:SceneCfg 支撑的 MuJoCo 后端插件
- `motrix_deploy_motrixsim`:SceneCfg 支撑的原生 MotrixSim 部署后端插件
- `motrix_deploy_unitree`:Unitree SDK2 DDS 硬件后端插件
- `motrix_deploy_unitree`:Unitree 硬件配置、sensor/actuation 接线与 SDK2 DDS 硬件后端插件
- `motrix_deploy_tasks`:具体部署任务实现

`scripts/` 提供训练、可视化、评估入口。内置环境由环境 package 注册并通过项目 registry 暴露;simulator backend
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ _Microduck locomotion policies trained with MotrixLab, rendered in MotrixRender
- **Unified Interface**: Provides a concise and unified reinforcement learning training and evaluation interface
- **Multi-framework Support**: Supports SKRL (JAX/PyTorch), RSLRL (PyTorch), and the built-in FastSAC implementation
- **Rich Environments**: Includes various robot simulation environments such as basic control, locomotion, and manipulation tasks
- **Sim-to-Real Deployment**: The same policy code deploys via the deploy CLI — Sim2Sim to MuJoCo or native MotrixSim, Sim2Real to real hardware
- **Sim-to-Real Deployment**: The same policy code deploys via the deploy CLI — simulation deployment to MuJoCo or native MotrixSim, hardware deployment to Unitree Go2
- **High-precision, High-performance Simulation**: Built on [MotrixSim](https://motrixsim.readthedocs.io/), a high-precision, high-performance physics engine
- **Visual Training**: Supports real-time rendering and training process visualization

Expand Down Expand Up @@ -181,7 +181,7 @@ MotrixLab is a [uv](https://docs.astral.sh/uv/) workspace with the following pac
| **motrix_deploy** | `motrix-deploy` | Artifacts, control sessions, runtime contracts, and deployment CLI |
| **motrix_deploy_mujoco** | `motrix-deploy-mujoco` | MuJoCo deployment backend plugin |
| **motrix_deploy_motrixsim** | `motrix-deploy-motrixsim` | Native MotrixSim deployment backend plugin |
| **motrix_deploy_unitree** | `motrix-deploy-unitree` | Unitree SDK2 DDS hardware backend plugin |
| **motrix_deploy_unitree** | `motrix-deploy-unitree` | Asset-free Unitree hardware configuration, sensor/actuation wiring, and SDK2 DDS backend plugin |
| **motrix_deploy_tasks** | `motrix-deploy-tasks` | Walking tasks, deployment scenes, and installed Hydra recipes |
| **motrix_env_core** | `motrix-env-core` | Backend-agnostic environment framework, configuration, registry, and rendering |
| **motrix_env_motrixsim** | `motrix-env-motrixsim` | Live MotrixSim backend, renderer, and Torch frontend |
Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ _使用 MotrixLab 训练的 microduck 行走策略,由 MotrixRender 实时渲
- **统一接口**: 提供简洁统一的强化学习训练和评估接口
- **多框架支持**: 支持 SKRL(JAX/PyTorch)、RSLRL(PyTorch)和内置 FastSAC 实现
- **丰富环境**: 包含基础控制、运动、操作等多种机器人仿真环境
- **Sim-to-Real 部署**: 同一套策略代码,通过部署 CLI Sim2Sim 到 MuJoCo 或原生 MotrixSim 仿真、Sim2Real 到真实硬件
- **Sim-to-Real 部署**: 同一套策略代码,通过部署 CLI 部署到 MuJoCo 或原生 MotrixSim 仿真,以及 Unitree Go2 真机
- **高精度高性能仿真**: 基于 [MotrixSim](https://motrixsim.readthedocs.io/) 高精度、高性能物理仿真引擎
- **可视化训练**: 支持实时渲染和训练过程可视化

Expand Down Expand Up @@ -181,7 +181,7 @@ MotrixLab 是一个 [uv](https://docs.astral.sh/uv/) workspace,包含以下 pa
| **motrix_deploy** | `motrix-deploy` | artifact、控制会话、运行时契约与部署 CLI |
| **motrix_deploy_mujoco** | `motrix-deploy-mujoco` | MuJoCo 部署 backend plugin |
| **motrix_deploy_motrixsim** | `motrix-deploy-motrixsim` | 原生 MotrixSim 部署 backend plugin |
| **motrix_deploy_unitree** | `motrix-deploy-unitree` | Unitree SDK2 DDS 硬件 backend plugin |
| **motrix_deploy_unitree** | `motrix-deploy-unitree` | 独立于资产的 Unitree 硬件配置、sensor/actuation 接线与 SDK2 DDS backend plugin |
| **motrix_deploy_tasks** | `motrix-deploy-tasks` | 行走任务、部署场景与安装的 Hydra 配方 |
| **motrix_env_core** | `motrix-env-core` | backend 无关的环境框架、配置、registry 与渲染 |
| **motrix_env_motrixsim** | `motrix-env-motrixsim` | MotrixSim 实时仿真 backend、renderer 与 Torch frontend |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ task.validate_command(context.command)
observation = task.build_observation(state, context)
```

Deployment's robot I/O boundary `RobotInterface` and optional `KeyboardDeviceProvider` / `GamePadDeviceProvider`
capability protocols are defined in `motrix_deploy.robot.interface`; import them from that module. The providers expose
input devices whose lifecycle they own, rather than discovering runtime plugins. Runtime discovery remains in
`motrix_deploy.runtime.factory` through `motrix_deploy.backends`, and recipes still select plugins with `runtime.backend`.
The prepared `runtime.robot` is available before opening. The application constructs
`ControlSession(robot=runtime.robot, command_binding=binding, ...)` explicitly,
binds it with `runtime.bind_control_session(control)`, and runs it inside `with runtime`.
The factory does not construct bindings or controllers through callbacks.

For example, MuJoCo deployment obtains a `MujocoKeyboardDevice` from the runtime's `KeyboardDeviceProvider` and constructs a
`KeyboardPlanarVelocityBinding`. Training can use a random binding for the same `PlanarVelocityCommand`. Both paths end at the
same typed task contract.
Expand Down
225 changes: 225 additions & 0 deletions docs/source/en/user_guide/tutorial/advanced/motrix_deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,228 @@ motrix-deploy-unitree joint-control enp5s0 FL_thigh_joint 0.9 \

This helper uses the artifact's robot, gain, timing, and limit settings. It waits for Start and A, moves the joint, holds,
returns to the default pose, and closes with damping.

## Advanced usage

Artifact inspection resolves the versioned task class through `motrix_deploy.tasks` and validates its Pydantic
`spec_type`, without constructing a task or importing training environments or simulator backends.
The JSON wire format remains `{name, config}`. Python uses direct spec attributes such as `spec.kp` and
`spec.action_scale`; action scale and bounds are joint vectors in canonical order. Policy action bounds apply
before scaling and adding the default pose; robot position limits apply to the resulting targets.

### Load a task spec from YAML

For a programmatic application, keep the complete Go2 task fields in `task.yaml` (the contents of
`task.config`, not the manifest envelope). OmegaConf loads and merges configuration and resolves interpolations;
Pydantic then validates the plain dictionary:

```python
from omegaconf import OmegaConf
from motrix_deploy_tasks.tasks.go2_walk import Go2WalkTaskSpec

cfg = OmegaConf.load("task.yaml")
spec = Go2WalkTaskSpec.model_validate(OmegaConf.to_container(cfg, resolve=True))
```

`Go2WalkTaskSpec` uses field constraints and a model validator for joint-vector lengths and ordered bounds.
No separate `validate()` call is needed. Use the artifact reader for a full deployment manifest.

### Startup and array validation

`RobotInterface.enable()` takes no initial command. Unitree owns the transition to
`RobotSpec.default_joint_position`, using zero target velocity/feedforward torque
and the resolved startup gains. `runtime.kp` / `runtime.kd` override artifact gains;
`null` retains the artifact gains. Startup does not call task `process_action()`
with a zero action. Initial-state safety and task-termination checks still run before
enable, and fresh-state checks run afterward; Start/A gating remains unchanged.

`DeployTask` declares neither `observation_size` nor `action_size`. The policy
input boundary validates actual observations, and the task action-conversion
boundary validates actual policy outputs. CLI assembly does not precheck task
dimensions. Manifest tensor specs remain model metadata; `DeploymentProfile`
dimensions remain export-only checks against the compiled environment.

### Hardware sensor and motor wiring

`motrix_deploy_unitree.hardware.UnitreeGo2HardwareCfg` owns asset-free hardware poses and wiring.
`HardwareSensorBinding` maps SDK IMU fields, including `wxyz` quaternion conversion to canonical `xyzw`;
`UnitreeMotorBinding` maps canonical joint names to SDK motor indices. These definitions live in the
hardware plugin's `sensor` and `actuation` modules, not in simulation robot models.
The artifact still owns the policy joint order, default pose, and limits. Hardware configuration imports
neither model assets nor a simulator or SDK eagerly, and does not fabricate base position or linear velocity.

### Select a deployment world

Select a world with `runtime.deploy_env_id` and a robot with the existing registry ID `runtime.robot_id`.
The CLI calls `motrix_deploy.env.assemble_deploy_scene(deploy_env_id, robot_id)`: it creates the robot-free world,
fills `scene.objs.robot` using `motrix_env_core.registry.make_robot_config(robot_id)`.
The core registry lazily discovers installed `motrix_env_core.robots` entry points, shared by training and deployment.
The deployment worlds `flat` and `rough` live in independent `motrix_deploy_tasks.envs` modules. They use core
floor/height-field configuration without importing a training task, robot model, or training contact setup.

To keep the same Go2 recipe and artifact but switch its world:

```bash
motrix-deploy task=go2-walk-flat/sim artifact=artifacts/go2-walk-flat.deploy \
runtime.deploy_env_id=rough
```

A world-only override leaves robot attachment placement unchanged. Adjust `runtime.robot_translation` if the terrain
needs different clearance; the complete `task=go2-walk-rough/sim` recipe supplies its own attachment defaults.

The installed flat recipe selects the world and robot by ID:

```yaml
runtime:
kind: simulation
backend: mujoco
deploy_env_id: flat
robot_id: go2
robot_translation: [0.0, 0.0, -0.114]
robot_rotation: [0.0, 0.0, 0.0, 1.0]
```

`runtime.physics` and `runtime.sensor_bindings` remain separate settings. MuJoCo accepts only the completed `SceneCfg`;
it neither selects an environment/robot pair nor assembles the scene or queries a training registry.
Programmatic `SimulationRuntimeConfig.scene` receives the same complete scene with the robot declared only in `scene.objs.robot`.

Placement has one source: `RobotCfg.translation` (Vec3) and `rotation` (xyzw) are attachment transforms that compose once
with the model's intrinsic base pose, not absolute world root poses. Go2's intrinsic base height is 0.445 m; the flat
recipe's -0.114 m offset gives 0.331 m, and the rough recipe's -0.025 m offset gives 0.42 m with identity rotation.
The runtime preserves these transforms on its copied scene. `mj_resetData` restores the compiled `model.qpos0`, including
the composed root placement; initialization overrides only articulation joints with the bound `RobotSpec` key pose.

For an external world plugin, declare a zero-argument `SceneCfg` factory in the `motrix_deploy.envs` entry-point group.
`motrix_deploy.env.available_deploy_envs()` lists installed IDs without loading factories;
`create_deploy_env(id)` loads only the selected factory and requires `scene.objs.robot` to be empty.
The core assembly helper inserts the robot selected by the existing robot registry; no generic scene-factory registry
or Hydra scene instantiation is involved. The CLI consumes `deploy_env_id`, `robot_id`, and optional
`robot_translation` / `robot_rotation`, applying attachment overrides to `scene.objs.robot.translation` / `.rotation`
before passing the ready scene to the simulation plugin. These application options are not runtime backend fields.

`SimulationRuntimeConfig` owns scene, rendering, realtime pacing, and state-source settings.
Its `physics` field uses the shared `motrix_env_core.config.sim.SimCfg` for `dt`,
`solver_iterations`, `solver_tolerance`, and `gravity`, defaulting to
`SimCfg(dt=0.002, solver_iterations=100)`. Both backend compilers consume this field directly:
optional solver/gravity fields set to `None` preserve model-source values; explicit values override them.
The YAML layout remains `runtime.physics`, independent of scene and state sensors.
Set physics values before runtime construction: the runtime reads the shared `SimCfg` used for
compilation; changing `dt` after compilation is not a supported runtime retuning workflow.

Programmatic applications use the same helper and pass its result to `SimulationRuntimeConfig`:

```python
from motrix_deploy.env import assemble_deploy_scene
from motrix_deploy.runtime.config import SensorBindings, SimulationRuntimeConfig
from motrix_env_core.config.sim import SimCfg

scene = assemble_deploy_scene("flat", "go2")
scene.objs.robot.translation = (0.0, 0.0, -0.114)
scene.objs.robot.rotation = (0.0, 0.0, 0.0, 1.0)
config = SimulationRuntimeConfig(
scene=scene,
physics=SimCfg(dt=0.002, solver_iterations=100),
sensor_bindings=SensorBindings(
base_angular_velocity="gyro",
base_linear_acceleration="accelerometer",
base_linear_velocity="global_linvel",
),
)
```

`SimulationRuntimeConfig.sensor_bindings` uses the frozen `SensorBindings` dataclass,
not a Python dictionary. It supports only `base_angular_velocity`,
`base_linear_acceleration`, and `base_linear_velocity`, each `str | None` with a
`None` default. Names must be nonempty and contain a nonwhitespace character;
unknown roles and non-string values are rejected. YAML `runtime.sensor_bindings`
keeps the same mapping keys and is converted with `SensorBindings(**mapping)`
at the configuration boundary. An absent field or explicit `None` means no binding;
MuJoCo rejects either for its three required roles when opening the robot.
The backend still checks sensor existence, type, dimension, and frame; it does not
synthesize state for other roles or bind every `RobotState` field to a sensor.

### Assemble the runtime and controller explicitly

The runtime factory selects the backend and prepares `runtime.robot`; its bound
`RobotSpec` is available before `open()`. The application creates the task, policy,
and command binding, then constructs and binds the control session. Using the
`config` above and your application-owned `task`, `policy`, and `command_binding`:

```python
from motrix_deploy.runtime.control import ControlSession
from motrix_deploy.runtime.factory import create_simulation_runtime

runtime = create_simulation_runtime("mujoco", config)
robot = runtime.robot
control = ControlSession(
robot=robot,
task=task,
policy=policy,
command_binding=command_binding,
period_s=0.02,
)
runtime.bind_control_session(control)
with runtime:
result = runtime.run(steps=50)
```

Construct a robot-dependent task from `robot.spec` after runtime creation. Simulation
runtimes derive `RobotSpec` only from the scene; they do not accept an external spec.
For artifact deployment, the CLI validates the artifact's robot contract against
`runtime.robot.spec` before assembling control.
`ControlSession.period_s` is the single control-period setting; `SimulationRuntimeConfig`
does not duplicate it. The simulation runtime validates it against the physics timestep
when binding or resolving the session. Runtime creation does not accept controller-building callbacks. The runtime context owns
world resources and simulation advancement; `ControlSession` owns shared control
execution, and `RobotInterface` owns robot I/O without advancing or closing the world.
Hardware applications use `create_hardware_runtime(name, config, context)` followed
by the same explicit construction and binding. Direct callers can also supply a
session to `runtime.run(control, steps=...)`.

### Select native MotrixSim deployment

The installed simulation recipes default to MuJoCo. With `motrix-deploy-motrixsim`
installed, choose the native backend using the same recipe and artifact:

```bash
motrix-deploy task=go2-walk-flat/sim artifact=artifacts/go2-walk-flat.deploy \
runtime.backend=motrixsim runtime.viewer=false runtime.realtime=false duration_s=1.0
```

The same override works for `task=go2-walk-rough/sim`; no duplicated task recipe is
needed. Programmatic applications call `create_simulation_runtime("motrixsim", config)`
and follow the explicit session-binding sequence above. The Go2 state mappings remain
`gyro`, `accelerometer`, and `global_linvel`: native sensors validate local angular
velocity/acceleration and world-frame linear velocity. The runtime owns native MSD
compilation, model/data, reset, physics, and optional SDK `RenderApp` rendering,
not a MuJoCo runtime or training environment. Removing `runtime.viewer=false` enables the
native viewer and its keyboard provider in a working graphical environment.

For the programmatic ONNX example, run
`python examples/deploy_to_sim.py --backend motrixsim --headless --steps 50`
with a matching ONNX policy; see the example's policy-file options. This path loads raw ONNX, not a
`.deploy` manifest: task preprocessing, tensor specs, and control settings are explicit
in the Python example and must match the model. Its scene and controller are shared
with the default MuJoCo path. Headless uses a zero-velocity command; windowed mode uses
the viewer keyboard provider when available.

### Use an external Hydra config

The native commands above work with the installed task package, without a checkout config directory. The canonical
built-in runtime recipes live in `motrix_deploy_tasks/config/task/{task}/{sim,hardware}.yaml`; prefer `task=...` to select them.
For a custom application's own configuration, Hydra's standard `--config-path` and `--config-name` remain available.
This illustrative command assumes you have authored `/absolute/path/to/my_app/config/deploy.yaml`; it does not select
or copy a built-in recipe:

```bash
motrix-deploy \
--config-path /absolute/path/to/my_app/config \
--config-name deploy \
artifact=artifacts/go2-walk-flat.deploy
```

Use an absolute config path: Hydra resolves relative paths against the CLI module, not the shell's current directory.
External configs own their defaults and complete runtime/backend configuration, and need not use the installed `task`
group. For simulation, select `runtime=sim` in defaults and provide `runtime.backend`, `runtime.deploy_env_id`, and `runtime.robot_id`, plus all
settings required by the selected backend (including MuJoCo state-sensor mappings). The artifact does not supply these
runtime choices. Use Hydra's normal options; there is no additional `--config` argument.
Loading
Loading