Skip to content

Commit 6135579

Browse files
authored
Merge pull request #1545 from Motphys/release/superdex-1.1.1
release: consolidate SuperDex integration and bump UniLab
2 parents 45e4dff + 6b68f4a commit 6135579

51 files changed

Lines changed: 1912 additions & 116 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/sphinx/source/adr/ADR-0000-index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ orphan: true
2222
| [ADR-0006 Community Manager API On NumPy Runtime](ADR-0006-community-manager-api-on-numpy-runtime.md) | Manager API / NumPy runtime | Accepted |
2323
| [ADR-0007 UniSim Extraction Boundary](ADR-0007-unisim-extraction-boundary.md) | Physics package extraction | Accepted |
2424
| [ADR-0008 Debug Overlay Primitive Contract And Playback Session](ADR-0008-debug-overlay-primitive-contract-and-playback-session.md) | Debug overlay / playback session | Accepted |
25+
| [ADR-0009 SuperDex Native C++ Scene Batch Executor](ADR-0009-superdex-persistent-cpu-workers.md) | Backend CPU scene execution | Accepted |
2526

2627
## ADR Governance
2728

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
orphan: true
3+
---
4+
5+
# ADR-0009 SuperDex Native C++ Scene Batch Executor
6+
7+
- Status: Accepted
8+
- Date: 2026-09-07
9+
- Owners: SuperDex fork / UniSim backend / UniLab task-config maintainers
10+
- Supersedes: None
11+
- Superseded by: None
12+
13+
## Context
14+
15+
The initial SuperDex adapter creates one independent native scene for every
16+
environment but advances them serially from Python. `Scene.step()` releases the
17+
GIL, yet a Python thread pool would still leave generalized-force writes and
18+
articulated state reads as separate Python-to-C++ calls. A subprocess/shared
19+
memory design was considered and rejected before implementation because it
20+
would introduce a new runtime protocol beyond the requested integration scope.
21+
22+
The maintained fork [unilabsim/project_superdex](https://github.com/unilabsim/project_superdex)
23+
can build an extension against the exact Physics and Robotics sources. This
24+
removes the wheel/header ABI mismatch that prevents a downstream native shim.
25+
The roadmap is [UniLab#1533](https://github.com/Motphys/UniLab/issues/1533).
26+
27+
## Decision
28+
29+
1. Add `superdex.physics.SceneBatchExecutor` to the fork's `mochi_physics`
30+
pybind extension. It owns persistent C++ worker threads, but does not own
31+
scenes or actors.
32+
2. One executor invocation receives contiguous generalized-force, articulated
33+
pose/velocity, link-state, contact and solver-status arrays. It writes each
34+
actor's forces, advances each distinct scene, and refreshes every runtime
35+
cache before its completion barrier opens.
36+
3. UniSim owns the executor and creates it after cold-path materialization. It
37+
keeps reset, asset conversion, cache-frame conversion and `SimBackend`
38+
ownership in the adapter. `close()` joins the executor
39+
before destroying bots, scenes, and the process-global runtime.
40+
4. `superdex_num_workers=0` selects `min(available physical CPU cores,
41+
num_envs)`. Explicit worker counts are capped at `num_envs`; the SDK stays
42+
single-threaded so the executor is the only physics parallelism layer.
43+
44+
## Consequences
45+
46+
The local integration requires Physics and Robotics bindings built from this
47+
fork at the same commit. Older qpos/qvel-only executor builds are rejected. It
48+
neither changes package versions nor publishes a wheel. The executor provides
49+
CPU scene parallelism; it does not claim GPU physics, native rendering, or
50+
dynamics equivalence with MuJoCo.
51+
52+
Validation must compare serial and parallel trajectories, selected reset, and
53+
complete backend throughput using the same scene, actions, batch, and substep
54+
count. Training evidence must report the actual outer worker count and SDK
55+
thread count separately.
56+
57+
## Alternatives Considered
58+
59+
- Python `ThreadPoolExecutor`: useful for a narrow step probe, but does not
60+
fuse force/state binding calls or give the adapter a durable native barrier.
61+
- Subprocess IPC and shared memory: rejected because SuperDex does not require
62+
process isolation and the protocol is outside this roadmap's approved scope.
63+
- Linking a downstream extension to the released wheel: rejected because the
64+
wheel does not publish a stable extension ABI or matching headers.
65+
66+
## Evidence In Repo
67+
68+
- `src/unilab/conf/ppo/task/go2_joystick_flat/superdex.yaml` selects automatic
69+
native workers for the SuperDex owner.
70+
- `src/unilab/scripts/train_offpolicy.py` and
71+
`src/unilab/scripts/train_rsl_rl.py` obtain rank-owned CPU ids from UniRL
72+
before environment construction.
73+
- `tests/algos/test_offpolicy_double_buffer_runner.py` verifies that each rank
74+
receives complete physical-core groups, including SMT siblings.
75+
- `tests/ipc/test_dp_launcher.py` verifies the corresponding resolver contract
76+
through the UniRL dependency.
77+
78+
## Related Documents
79+
80+
- {doc}`SuperDex Backend </en/2-user_guide/3-backends/8-superdex>`
81+
- {doc}`UniSim Extraction Boundary </adr/ADR-0007-unisim-extraction-boundary>`
82+
- [UniLab#1533](https://github.com/Motphys/UniLab/issues/1533)

‎docs/sphinx/source/adr/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ orphan: true
2222
| [ADR-0006 Community Manager API On NumPy Runtime](ADR-0006-community-manager-api-on-numpy-runtime.md) | Manager API / NumPy runtime | Accepted |
2323
| [ADR-0007 UniSim Extraction Boundary](ADR-0007-unisim-extraction-boundary.md) | Physics package extraction | Accepted |
2424
| [ADR-0008 Debug Overlay Primitive Contract And Playback Session](ADR-0008-debug-overlay-primitive-contract-and-playback-session.md) | Debug overlay / playback session | Accepted |
25+
| [ADR-0009 SuperDex Native C++ Scene Batch Executor](ADR-0009-superdex-persistent-cpu-workers.md) | Backend CPU scene execution | Accepted |
2526

2627
## ADR Governance
2728

‎docs/sphinx/source/conf.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@
277277
_LANGUAGE_PATH_FORWARD: dict[str, str] = {
278278
"en/why_unilab": "zh_CN/why_unilab",
279279
"en/2-user_guide/3-backends/6-drake": "zh_CN/2-user_guide/3-backends/6-drake",
280+
"en/2-user_guide/3-backends/8-superdex": "zh_CN/2-user_guide/3-backends/8-superdex",
280281
"en/1-getting_started/5-faq": "zh_CN/1-getting_started/5-faq",
281282
"en/4-developer_guide/1-architecture/6-manager_based_api": (
282283
"zh_CN/4-developer_guide/1-architecture/6-manager_based_api"

‎docs/sphinx/source/en/2-user_guide/3-backends/0-index.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Simulation Backends
22

33
UniLab exposes backend names through registry/config paths, including `mujoco`,
4-
`motrix`, `mjwarp`, `drake`, `isaacgym`, `genesis`, `isaacsim`, and `newton`
4+
`motrix`, `mjwarp`, `drake`, `isaacgym`, `genesis`, `isaacsim`, `newton`, and `superdex`
55
where an owner is registered.
66
User commands select them with `--sim`, which routes to the matching task owner
77
YAML; do not switch a run by overriding `training.sim_backend` alone.
@@ -146,4 +146,5 @@ separately authorized issue.
146146
5-genesis
147147
6-drake
148148
7-newton
149+
8-superdex
149150
```
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# SuperDex Backend
2+
3+
SuperDex is an optional CPU physics adapter owned by `unisim.backend.superdex`.
4+
The initial UniLab owner is the fixed-base `FR3JointTarget` task:
5+
`src/unilab/conf/ppo/task/fr3_joint_target/superdex.yaml`. It uses seven torque
6+
actions, 21 observation values, joint-state resets and the standard NumPy
7+
manager runtime. Its support level is **Configured**; bounded rollout or short
8+
training checks do not establish full-training performance or platform support.
9+
The implementation is tracked in [#1534](https://github.com/Motphys/UniLab/issues/1534)
10+
under [roadmap #1533](https://github.com/Motphys/UniLab/issues/1533).
11+
12+
## Local Development Setup
13+
14+
This development profile uses locally linked UniSim and UniLab checkouts; it
15+
does not require a new published version. SuperDex Physics/Robotics 1.0.0 requires
16+
Python 3.12. CPU physics does not require CUDA. Native rendering and video are
17+
not part of the FR3 owner; training defaults to `no_play=true`.
18+
19+
In the UniLab checkout, use an existing Python 3.12 virtual environment or create
20+
one, then install the local packages:
21+
22+
For the temporary local SuperDex source build used by this roadmap, first run:
23+
24+
```bash
25+
bash scripts/tools/setup_superdex_env.sh
26+
source ~/.cache/unisim/superdex/env.sh
27+
```
28+
29+
With no arguments the script clones the modified
30+
`unilabsim/project_superdex` integration branch, installs the public SuperDex
31+
Python facade, builds `mochi_physics_pybind` and `superdex_robotics_pybind` in
32+
Release mode, and installs the local UniSim/UniRL/UniLab checkouts editable. It
33+
does not publish or install a SuperDex wheel from PyPI. The generated
34+
`env.sh` exports the assets and native-extension paths. Re-run the script after
35+
changing the SuperDex checkout; CMake reuses the existing build directory.
36+
37+
```bash
38+
uv venv --python 3.12
39+
export UNILAB_LOCAL_UNISIM=/absolute/path/to/unisim
40+
uv pip install -e "${UNILAB_LOCAL_UNISIM}[superdex,mujoco]" -e . --group pyproject.toml:dev
41+
export UV_NO_SYNC=1
42+
export SUPERDEX_ASSETS_PATH=/absolute/path/to/project_superdex/assets
43+
```
44+
45+
`UNILAB_LOCAL_UNISIM` enables the repository's local dependency validation:
46+
tests require an editable installation and check that its metadata and imported
47+
module point to exactly that checkout. Without this variable, the normal
48+
indexed-release requirement remains in force. `UV_NO_SYNC=1` preserves the local
49+
links when running existing Make targets; `uv sync` would re-resolve the locked
50+
release profile.
51+
52+
Native FR3 assets remain in the upstream SuperDex checkout. The asset hub
53+
registers `bots/arms/fr3_v2/fr3_v2.superdex_bot`, checking its collision SDF,
54+
render files, `LICENSE` and `NOTICE` before constructing physics. No native
55+
robot binaries are bundled or downloaded by UniLab. Set
56+
`env.superdex_assets_root=/absolute/path/to/project_superdex/assets` to override
57+
`SUPERDEX_ASSETS_PATH` for a specific owner invocation.
58+
59+
## Run the FR3 Task
60+
61+
```bash
62+
uv run --no-sync train --algo ppo --task fr3_joint_target --sim superdex \
63+
algo.max_iterations=2 algo.num_steps_per_env=16 \
64+
algo.algorithm.num_learning_epochs=1
65+
```
66+
67+
The target joint positions, rewards, reset ranges and action scales live in the
68+
task's `base.yaml`. The torque bounds `[20,20,20,20,5,5,5]` Nm are an explicit
69+
research profile, not rated hardware limits. `superdex_effort_limits` declares
70+
the same bounds at the native backend boundary. The SDK remains single-threaded;
71+
the native scene executor below owns all supported CPU parallelism.
72+
73+
## Default CPU Environment Parallelism
74+
75+
The backend uses SuperDex's source-built `SceneBatchExecutor`, a persistent C++
76+
thread pool that owns the barrier across independent scenes. Each substep writes
77+
batched generalized forces, advances scenes, and returns articulated/link state,
78+
contact sensors and solver status without per-environment Python binding calls.
79+
Asset materialization, reset and cache-frame transforms remain owned by the
80+
UniSim adapter. This is CPU thread parallelism, not GPU physics, and it does
81+
not change the PPO/APPO collector, learner or policy contracts. The decision is
82+
recorded in {doc}`/adr/ADR-0009-superdex-persistent-cpu-workers` and
83+
[unisim#41](https://github.com/unilabsim/unisim/issues/41).
84+
85+
Both task owners select automatic workers by default:
86+
87+
| Owner option | Meaning |
88+
| --- | --- |
89+
| `env.superdex_num_workers=0` | Automatic: `min(available physical CPU cores, num_envs)` |
90+
| `env.superdex_num_workers=1` | One native C++ scene worker |
91+
| `env.superdex_num_workers=K` | Explicit C++ worker count, capped at `num_envs` |
92+
93+
A native scene's `DebugDraw` state is thread-affine, so the batched mode is
94+
incompatible with an attached native SuperDex debugger: the adapter fails
95+
closed with an actionable error when a debugger client connects. To debug with
96+
the native debugger, run `env.superdex_execution_mode=serial`, which steps
97+
every scene on the environment thread and never builds the native worker pool
98+
([unisim#55](https://github.com/unilabsim/unisim/issues/55)). Serial mode is a
99+
debugging profile, not a performance configuration.
100+
101+
`eval --sim superdex --render-mode interactive` renders through the native
102+
SuperDex (Polyscope) viewer instead of the MuJoCo viewer path. The owner layer
103+
automatically switches the env to `superdex_execution_mode=serial`, and the CLI
104+
forces `training.play_env_num=1` because the viewer draws exactly one scene;
105+
an explicit `training.play_env_num=...` override is preserved.
106+
107+
With 1024 environments on a 16-core/32-thread host, automatic selection yields
108+
16 workers. Concurrent multi-rank collectors are assigned whole physical-core
109+
groups, including their logical siblings, so ranks do not split an SMT core.
110+
`training.dp_collector_cpu_ids` may instead provide one explicit CPU-id list
111+
per rank. The selected block is applied before SuperDex materializes its native
112+
worker pool.
113+
114+
For every physics substep, the host runs the pre-step control callback, enters
115+
the native batch barrier, then publishes the refreshed batch before the next
116+
callback. Selected reset preserves caller row order and leaves unselected
117+
environments unchanged. A native worker failure closes the executor and reports
118+
the failure; it does not silently return stale state or switch to serial.
119+
Closing an environment joins its C++ workers before scenes are destroyed.
120+
121+
Worker count alone is not evidence of speedup. Throughput comparisons must use
122+
the same model, control sequence, batch size and substeps, and report complete
123+
backend/env time, startup, RSS, CPU use and actual worker count. Count environment
124+
control steps, not physics substeps. Existing contact approximations are unchanged.
125+
126+
The fixed root still has a named entity and readable body state. Reset terms
127+
write joint state; they do not request a floating-root layout. The task does
128+
not require contact sensors, cameras, site Jacobians or runtime material DR.
129+
SuperDex has no native renderer. Its default record playback uses the offline
130+
MuJoCo renderer with the authored MJCF visual model while SuperDex remains the
131+
physics backend. `.superdex_bot` scenes must provide `visual_model_file` for
132+
this path. Selecting playback mode `none` skips playback entirely; it is not
133+
evidence that a checkpoint has executed a rollout.
134+
135+
`superdex_allow_contact_approximation` defaults to `false`. It is reserved for
136+
explicitly audited MJCF conversion profiles: enabling it accepts a warning about
137+
contact/material approximation, including missing torsional/rolling friction
138+
equivalence. It does not establish arbitrary MJCF task compatibility.
139+
Contact queries report the last completed solver step. Reset clears this state;
140+
it does not provide a fresh geometric overlap test until a positive physics step
141+
has completed. Kinematic body/joint getters are refreshed immediately at reset.
142+
143+
## Validation and Ownership
144+
145+
The `go2_joystick_flat/superdex` PPO owner is a research sim2sim profile. It
146+
inherits the MuJoCo owner, preserving 49 actor observations, 52 critic
147+
observations, 12 position-target actions, normalization, network dimensions and
148+
control timing. It disables runtime PD gain randomization and explicitly opts
149+
into contact approximation. The adapter's cold-path MJCF conversion is restricted;
150+
this owner does not imply arbitrary scene support or equivalent walking behavior.
151+
152+
Create a small source checkpoint with the MuJoCo owner, then pass its path to the
153+
optional checkpoint test:
154+
155+
```bash
156+
uv run --no-sync train --algo ppo --task go2_joystick_flat --sim mujoco \
157+
algo.num_envs=2 algo.max_iterations=2 algo.num_steps_per_env=16 \
158+
algo.algorithm.num_mini_batches=1 algo.algorithm.num_learning_epochs=1 \
159+
training.device=cpu training.no_play=true
160+
export UNILAB_SUPERDEX_GO2_CHECKPOINT=/absolute/path/to/source/run/model_1.pt
161+
uv run --no-sync pytest tests/envs/test_go2_superdex.py -q
162+
```
163+
164+
This validates the source `run_config.json` before environment construction,
165+
checks rejection of changed policy action semantics, loads the actual policy
166+
through the production playback session and executes 64 SuperDex control steps
167+
without a renderer. It checks finite values and interface compatibility; a
168+
two-iteration checkpoint is not expected to walk reliably.
169+
170+
```bash
171+
uv run --no-sync pytest tests/assets/test_superdex_assets.py \
172+
tests/envs/test_fr3_superdex.py tests/test_cli_runtime_requirements.py -q
173+
```
174+
175+
The optional native tests require the SDK and `SUPERDEX_ASSETS_PATH`; they cover
176+
finite rollout data, selected reset isolation, immediate observation refresh
177+
and a spawned `EnvFactory`. Missing runtime/assets produce an explicit skip;
178+
such a run is not native validation. The base asset/config tests need no native
179+
asset checkout.
180+
181+
Engine conversion and physics live in UniSim; asset registration, Hydra and task
182+
terms remain in UniLab. See
183+
{doc}`/adr/ADR-0007-unisim-extraction-boundary`,
184+
{doc}`/adr/ADR-0006-community-manager-api-on-numpy-runtime` and
185+
{doc}`/adr/ADR-0002-backend-capability-boundary-for-play-and-snapshot`.

‎docs/sphinx/source/zh_CN/2-user_guide/3-backends/0-index.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
UniLab 通过 registry/config 路径暴露后端名称,包括在对应 owner 注册后可用的
44
`mujoco`、`motrix`、`mjwarp`、`drake`、`isaacgym`、`genesis`、`isaacsim` 和
5-
`newton`。用户命令通过
5+
`newton`、`superdex`。用户命令通过
66
`--sim` 选择后端,该选项会路由到对应的 task owner YAML;不要仅靠 override
77
`training.sim_backend` 来切换一次运行。
88

@@ -137,4 +137,5 @@ benchmark v1 目前只保留 `BenchmarkCase`、`BenchmarkResult` 和 provenance
137137
5-genesis
138138
6-drake
139139
7-newton
140+
8-superdex
140141
```

0 commit comments

Comments
 (0)