Scope level
roadmap
Work type
feature
Area
mujoco
面向 maintainer 的摘要
目标: 解决 discussion #1541 指出的共同根因——缺少 per-env 模型间接层,同时不削弱 UniLab 的 manager/config 边界或 UniSim 的 backend contract。终态是:mjbatch 拥有 CPU MuJoCo 执行机制,UniSim 的 MuJoCo 与 MJWarp adapters 分别拥有各自 engine realization,UniLab 拥有 task 选择、事件生命周期与随机化语义;Manager-Based API 成为唯一 DR 生命周期,legacy provider/manager 协议退出。
推荐路径: 以现有 mjbatch per-env model-field / variant 工作作为 CPU executor primitive;在 UniSim 增加窄接口,覆盖 reset-time model fields 与 construction-time same-layout variants;MuJoCo 与 MJWarp 两个 adapters 都实现该接口;由 UniLab EventManager / Entity / reset transaction 消费;在 SimToolReal 600-tool 工作负载上验证 CPU 路径,并用独立 compile oracle 验证 MJWarp 路径;最后完成 legacy DR 迁移和删除。
交付边界: per-env model fields、same-layout mesh variants、uniform-contract topology routing、capability 声明、task-owned tool assignment、EventManager/reset transaction 集成、MJWarp 兼容、legacy DR 协议移除、正确性测试与可复现 benchmark。任意 raw engine-model 访问、第二套长期 DR manager、异构 public state/action shape 均不在范围内。
预计 review 规模: 一个已开放的 mjbatch PR/release、三个 UniSim contract/adapter 切片、三个 UniLab manager/config/task 切片,以及一个 legacy 迁移/删除切片。跨 runtime public contract 与生命周期决策需要先补 ADR。
长期责任: mjbatch maintainers 负责 executor 正确性与 MuJoCo ABI/release 联动;UniSim maintainers 负责 SimBackend capabilities 与 adapters;UniLab maintainers 负责 Hydra/task owner config、EventManager、Entity/reset transaction、task rollout,以及最终退役 legacy DR manager。
需要决策的问题: 确认窄 public contract、construction-time variant plan 的落点、immutable assignment 语义、topology-group 边界、per-env playback 语义、MuJoCo/MJWarp 共用 variant preparation 的实现归属,以及 legacy provider 的弃用与删除版本策略。
本 roadmap 目前是提案,等待 maintainer 批准。边界获批前,不创建 integration branch 或 child issues。
问题与仓库证据
Discussion #1541 与 #1058 指出两个症状的共同根因:
UniLab legacy DomainRandomizationManager 之所以演化成 reset/DR 协议,是因为 CPU backend 缺少批量 per-env model write。
SimToolReal 的 600-tool 工作负载需要编译数百个完整 MjModel,因为 backend 缺少 model-variant 间接层。
当前三个仓库已经体现出预期的分层:
UniLab 现状
src/unilab/managers/event_manager.py 刻意拒绝 direct model_fields mutation,因为还没有公开 backend contract。
src/unilab/base/reset_state.py 已经能将多个 reset terms 组合成一次 SimBackend.set_state(...) 调用,并使用 dense selected-row randomization payload。
src/unilab/envs/mdp/events.py 仍拒绝 RandomizeRigidBodyMass(..., recompute_inertia=True),并在冷路径通过重新 compile MuJoCo scene XML 获取惯性默认值。
src/unilab/dr/manager.py 仍是 legacy provider/reset 协议;当前仓库内没有任务调用 _init_domain_randomization(),但文档明确该路径保留给外部 custom tasks。
src/unilab/base/backend_factory.py 是 UniLab 到 UniSim 的转译边界。
UniSim 现状
src/unisim/backend/base.py 拥有 SimBackend、set_state、materialization 与 DR capability negotiation。
src/unisim/dr/types.py 拥有 DomainRandomizationCapabilities、ResetRandomizationPayload,以及目前较窄的 InitRandomizationPlan / ModelVariantSpec。
src/unisim/backend/mujoco/backend.py 已将 curated reset payload 映射到 mjbatch expand(...) 与一次 set_const(...)。
src/unisim/backend/mjwarp/randomization.py 已体现 backend-owned per-world field expansion 与分级 derived-constant 重算的设计。
src/unisim/backend/mjwarp/backend.py 已支持 reset-time per-world mass/inertia、geom friction/size/solver 参数、damping/armature 和 kp/kd 写入,但 construction-time mesh variant 与 per-env playback 仍缺失。
当前 MuJoCo playback 假设所有 env 共用一个 model,因此 variant playback 语义需要更新 contract。
mjbatch-uni 现状
PR #12 提供缺失的 executor primitives:per-sim model fields、compiler-coherent same-layout mesh variants、显式 topology-affine routing。
该 PR 已对 renamed mjbatch-uni main 完成冲突更新,并且当前 head CI 通过;但它还不是 UniSim 消费的已发布 mjbatch-uni 版本线。
Discussion #1541 对 mjlab 的架构结论不是“把 EventManager 搬进 backend”,恰恰相反:simulation 层拥有 field expansion、派生量重算与 variant realization;event manager 只声明和调度 task 语义。
补充审计 1:MJWarp / mjlab 兼容性
结论
可行,且必须成为本 roadmap 的完成条件,而不是事后 follow-up。
理由:
UniSim MJWarp backend 已经具备 reset-time per-world model-field MBA-DR:
constructor 阶段调用 expand_model_fields(...);
字段从 (1, tail...) tile 成 (nworld, tail...);
所有 later DR write 都是固定地址 in-place upload,保持 CUDA graph pointer 稳定;
mass/inertial family 通过 set_const / set_const_0 分级重算。
本地 mjlab 参考实现已经证明 construction-time mesh variants 可行:
VARIANT_DEPENDENT_FIELDS 覆盖 geom_size、geom_rbound、geom_aabb、geom_pos、geom_quat、body_mass、body_subtreemass、body_inertia、body_invweight0、body_ipos、body_iquat;
build_variant_model(...) 为每个 world scatter geom_dataid 与 geom_matid;
assignment 固定在 simulation init;
per-world defaults 保留每个 variant 的 baseline,而不是把单一 model 默认值广播到所有 worlds;
viewer/playback 通过 expanded fields 同步 per-world 状态。
因此 UniSim MJWarp adapter 不需要发明新物理语义;需要把 mjlab 已验证的 variant realization 移植到自己的 SceneCfg / SimBackend 生命周期,并保持 graph capture 前完成所有 array replacement。
现有缺口
当前 UniSim MJWarp backend 与目标之间有 6 个具体缺口:
输入模型仍是单一 SceneCfg.model_file
materialize_mjwarp_scene(...) 只 materialize 一个 scene;
没有 variant source catalog、slot alignment、assignment 或 canonical mesh pool。
geom_dataid / geom_matid 未进入 per-world variant realization
current reset DR expansion 只覆盖 payload-writable fields 与 derived fields;
mesh selection / material selection 不是 reset randomization,必须在 construction path 初始化。
geom_pos / geom_quat 未在当前 reset expansion list 中
它们不是当前 reset payload 字段;
但属于 mjlab VARIANT_DEPENDENT_FIELDS;
variant construction 必须把它们按 variant scatter 成 per-world arrays。
host DR mirrors 的默认值不能继续广播单一 CPU model
current _bind_dr_host_mirrors() 把 self._cpu_model 的默认值 broadcast 到 (num_envs, ...);
对 variants 来说,每个 world 的 baseline 来自各自 independently compiled source;
如果继续单一 broadcast,第一次 reset randomization 会覆盖/破坏 variant baseline。
需要引入 per-world defaults,并向 UniLab Entity binding 暴露“default 是 (N, ...) 而不是 (tail...)”的语义。
reset-time primitive geom bounds 逻辑不适用于 mesh variants
PrimitiveGeomBounds 只支持 sphere/capsule/ellipsoid/cylinder/box 的运行时 size 派生;
mesh variant 的 rbound/aabb/pos/quat/inertia 必须来自 reference compile oracle;
不允许用户分别提交不一致 derived fields。
playback 仍是单一 visual model
get_playback_model(env_index) 验证后仍返回同一个 scene_visual_model_file;
mesh/material variant 需要 canonical visual model + per-env geom_dataid/geom_matid snapshot,或 per-env visual model materialization。
需要的实现切片
MJWarp 支持应拆成独立 child,而不是藏在 MuJoCo adapter 里:
neutral variant plan 校验和 uniform layout 检查;
MuJoCo-family source preparation:独立 compile 每个 source variant、pool/dedup mesh/material、stable slot 对齐;
在 put_model(...) 后、CUDA graph capture 前写入:
per-world geom_dataid
per-world geom_matid
per-world 11 个 variant-dependent fields;
用 variant rows 初始化 host DR mirrors 与 per-world defaults;
playback snapshot/materialization;
与 CPU MuJoCo 路径共用的 cross-backend oracle tests。
兼容边界
兼容范围: same-layout mesh variants + uniform public layout topology。
不承诺: per-env ragged nq/nv/nu/sensor layout。
graph 安全: 所有 array replacement 只能发生在 cold path / graph capture 前;reset path 只写已经 per-world 化的固定地址 arrays。
gravity parity: current MJWarp backend 明确 fail closed,不声明 reset gravity。pinned mujoco_warp.Model.opt.gravity 的 leading dimension 是 *,技术上可以扩展,但需要单独 effect test 与 kernel 语义验证;是否纳入本 roadmap 由 maintainer 决策。若不纳入,任务配置在 MJWarp 上请求 gravity 必须 fail closed,而不是静默跳过。
补充审计 2:结束 MBA 与 legacy DR 共存
结论
可行,但必须作为 roadmap 的 release-blocking child 处理;能否直接删除取决于外部 custom task 的迁移状态。
当前证据显示:
仓库内已经没有 production provider 用户
全仓搜索显示,_init_domain_randomization() 只有 NpEnv 定义,没有任何 in-repo task 调用;
DomainRandomizationProvider 没有在 src/unilab 中有 concrete production subclass;
ManagerBasedRlEnv 测试明确断言 _dr_manager is None。
文档仍把 provider 路径作为外部 custom task 的公开扩展点
英文/中文 DR 文档均说明当前仓库没有任务使用,但外部任务可通过 DomainRandomizationProvider + DomainRandomizationManager 接入;
因此立即删除是 public API/support 决策,而不是纯 dead-code cleanup。
Manager-Based 事件面已经覆盖多数 legacy reset DR 语义
src/unilab/envs/mdp/events.py 已有:
geom_friction
joint_armature
pd_gains
randomize_rigid_body_mass
randomize_body_mass_inertia
randomize_rigid_body_com
randomize_physics_scene_gravity
push_by_setting_velocity
apply_body_impulse
randomize_encoder_bias
缺失的不是又一套 manager,而是 backend capability 与 per-world defaults
mass/inertia、mesh variant baseline、geom field defaults、uniform capability;
这些应由 UniSim contract + adapters 提供,再由现有 Entity/reset transaction 绑定。
结束共存的目标状态
Roadmap 完成后:
Manager-Based API 是唯一 DR 生命周期:
reset terms -> ResetStateTransaction -> one SimBackend.set_state(randomization=...);
interval terms -> IntervalRandomizationPlan;
fixed model identity -> construction-time variant plan。
不再存在 DomainRandomizationProvider / DomainRandomizationManager protocol。
NpEnv 删除 _dr_manager 状态、_init_domain_randomization()、legacy reset override 和 interval dispatch hook。
unilab.dr 不再拥有第二套 manager/provider;如 Manager API 仍需 re-export UniSim plan/capability 类型,只能保留薄 re-export 或迁移到更合适的 owner module。
DR 文档、provider guide、sim2sim allowlist 中的 env.domain_rand 语义和 legacy examples 全部更新或删除。
legacy manager tests 删除或改写为 Manager-Based equivalence tests。
必要迁移步骤
外部消费者清单
联系 Sharpa/外部 custom task owner,确认 provider subclass、env.domain_rand 字段、reset observation/info side-effects、interval ops;
本仓库无法用源码证明外部不存在,因此删除前必须有维护者确认或 deprecation window。
语义等价迁移
legacy build_common_reset_randomization() 的 base mass/COM、gravity、ground friction、armature、kp/kd 映射到对应 event terms;
reset observation rebuild 迁移到 Manager-Based reset observation pipeline;
interval legacy fields 已在 UniSim plan 中 deprecated,应改为 IntervalTermOp 或现有 interval event terms。
弃用窗口
若按 semver 支持外部插件,先在一个 release 中发出 explicit deprecation;
provider path 不再获得新能力,特别是 per-env variants;
文档迁移指南完整;
下一 planned breaking release 删除。
删除与守卫
删除 src/unilab/dr/manager.py 与 provider.py;
清理 NpEnv hooks;
删除 legacy tests/docs;
新增 guard test:Manager-Based env 不携带 _dr_manager,import graph 中不存在 legacy manager。
如果外部消费者无法在 roadmap 期限内迁移,本 roadmap 不能标记完成;不得为了结束共存而静默破坏外部任务,也不得继续给 legacy protocol 添加 per-env variant 能力。
提案架构
1. reset-time model mutation 保持在现有 payload contract 之后
UniLab event terms 继续在 ResetStateTransaction 中组合写入,并提交一个 curated payload:
UniLab EventManager terms
-> ResetStateTransaction
-> SimBackend.set_state(env_ids, qpos, qvel, randomization=...)
-> backend-specific per-env field write + one strongest recompute
UniSim 应扩展 DomainRandomizationCapabilities 与 ResetRandomizationPayload,而不是暴露 raw mjbatch views。Event terms 不得调用 expand、set_const、geom_dataid 或 backend-private pool。
2. 固定 model identity 使用 construction-time variant plan
SimToolReal 的 tool_id = env_id % 600 是 task identity,不是每次 reset 的 randomization。UniLab 应 materialize 一个最终 immutable assignment,并将 backend-neutral variant plan 传给 UniSim。
UniSim plan 必须仅依赖 NumPy/stdlib,并保持 pickle-safe:
使用已 materialize 的 model source descriptors,不使用 live MjSpec 对象;
assignment 是最终 integer array,形状 (num_envs,);
backend construction 后 assignment 不可变;
显式声明 same-layout 或 uniform-contract topology 支持;
不把 executor representation strategy 暴露为 task config。
现有 ModelVariantSpec(geom_size_overrides=...) 过窄且偏引擎实现,不应扩展成通用 mesh/topology catalog。
3. engine realization 留在 backend executor/adapter
mjbatch CPU executor 应拥有:
per-worker mutable mjModel 应用;
per-env expanded model-field rows;
set_const / derived-field 重算;
independent reference compile;
mesh pooling 与 deduplication;
same-layout VariantPack;
topology-affine routing。
UniSim MuJoCo adapter 可以调用这些 primitives,但它们不是 UniLab public API。
MJWarp path 的 realization 归 UniSim MJWarp adapter:
在 graph capture 前替换/初始化 per-world Warp arrays;
写入 per-world geom_dataid / geom_matid;
scatter variant-dependent fields;
保持 reset writes graph-safe;
拥有 MJWarp playback realization。
MuJoCo CPU 与 MJWarp 可以共用 backend-neutral variant plan 与 MuJoCo-family source preparation helper,但不得把 live MjSpec、mjbatch view 或 Warp array 暴露给 UniLab。
4. 不提前暴露异构 state/action shapes
当前 SimBackend 假设一个统一 public layout:nq、nv、actuator 数、control shape、sensor layout 与 state arrays。
只有当所有 variants 都能投影到这个统一 contract 时,才允许 topology-affine execution。仅改变 tool geometry/mesh、而 robot joints、actuators、sensors 与 observation layout 保持对齐的 variants 在范围内。需要 per-env nq/nv/nu 或 ragged sensors 的 variants 依赖另一个 heterogeneous contract 决策,在本 roadmap 中 fail closed。
5. playback 成为显式 backend capability
Per-env visual playback 不得由 UniLab 用 private MuJoCo 对象拼装。UniSim 应定义 backend 可提供:
一个 canonical visual model;
per-env visual model snapshot;
向 visual twin 同步 per-env fields 的例程。
MuJoCo adapter 可以选择高效实现,但 capability 与语义必须公开且可测试。
Proposed child issues
Child issues:UniLab #1564 /#1565 /#1566 /#1567 ;UniSim #66 /#67 /#68 ;mjbatch #3 / PR #12 。建议 review 顺序保持纵向:先确认 contract 与 adapter 语义,再扩大 task rollout。
Child
仓库
可 review 结果
主要验收门槛
L1 / #1564
UniLab
记录跨仓边界,并定义 task-owned tool catalog 与最终 assignment materialization
Maintainer-approved ADR;Hydra config tests
M1 / mjbatch #3 + PR #12
mjbatch-uni
合并/发布 PR #12 版本线,包含 model fields、same-layout variants 与 topology routing
Executor oracle tests;wheel release;ABI pin evidence
U1 / #66
unisim
扩展 DR capability/payload 并新增 construction-time variant plan,但不暴露 mjbatch types
Fake/conformance tests;import boundary;API review
U2 / #67
unisim
将 neutral plan 映射到 mjbatch;验证统一 public layout;实现 per-env playback 语义
Independent-compile equivalence;reset/step/playback tests
U3 / #68
unisim
将 neutral plan 映射到 MJWarp per-world arrays,保留 variant baseline,并实现 graph-safe playback
mjlab/mjwarp oracle equivalence;CUDA graph-safe reset tests
L2 / #1565
UniLab
消费新 capability/payload;补充需要的 Entity bindings;移除 UniLab 侧 MuJoCo recompile defaults
Manager/reset transaction tests;无 mjbatch/MuJoCo private access
L3 / #1566
UniLab
在 task owner 中 materialize 600-tool catalog 与 immutable assignment,并接入 rollout
代表性 workload 的 correctness/performance/memory report
L4 / #1567
UniLab
迁移外部 provider 用户,并删除 legacy DR manager/provider 协议
External consumer inventory;Manager-Based equivalence;legacy code/docs/tests removed
L1 — UniLab ADR 与 task config schema
范围:
新增跨仓 ownership boundary ADR;
task-owned variant source catalog;
最终 assignment 表示与 materialization;
所选 backend 缺少 variant support 时 fail closed;
文档明确 assignment 在 construction 后固定。
不在范围:
在 UniLab compile MjSpec;
暴露 topology-affine executor API;
task-specific rollout tuning。
M1 — mjbatch-uni executor release
基于已 review 的 PR #12 :
land 并发布 per-env model-field / variant API;
保持与独立 compile reference models 的 exact equivalence;
发布兼容 wheel 版本线;
记录 MuJoCo ABI pin 与 UniSim 所需 version floor。
U1 — UniSim contract 与 conformance
只扩展窄 public surface:
model-field capability metadata;
支持的 reset payload fields 与 recompute obligation;
construction-time same-layout variant plan;
immutable assignment 校验;
topology support 作为 uniform-public-layout 声明;
fake backend 与 conformance 覆盖。
拒绝:
raw engine model handles;
arbitrary field-name writes;
将 model_update() context 直接暴露给 UniLab;
多套互不连通的 capability 系统。
U2 — UniSim MuJoCo adapter 与 playback
将 neutral contract 转译到 mjbatch:
在 adapter 中 compile materialized variant sources;
内部选择 canonical same-layout 或 topology-affine representation;
验证唯一统一 SimBackend layout;
将 reset payload fields 映射到 expand(...),并执行一次 strongest set_const;
按 U1 实现 per-env 或 canonical playback;
更新 packaging/version requirements。
U3 — UniSim MJWarp adapter 与 mjlab 对齐
将同一个 neutral variant plan 转译到 MJWarp:
复用或移植 mjlab 已验证的 same-layout mesh variant realization;
独立 compile source variants,取得 compiler-coherent baseline;
stable slot 对齐并 pool/dedup mesh/material assets;
在 put_model(...) 后、CUDA graph capture 前初始化 per-world:
geom_dataid
geom_matid
geom_size
geom_rbound
geom_aabb
geom_pos
geom_quat
body_mass
body_subtreemass
body_inertia
body_invweight0
body_ipos
body_iquat
用 variant rows 初始化 host DR mirrors 与 per-world defaults;
reset writes 只做固定地址 in-place upload;
实现 MJWarp per-env/canonical playback;
与 CPU MuJoCo adapter 共用 cross-backend task fixture,比较 state/sensor/contact rollout。
L2 — UniLab EventManager、Entity 与 reset transaction
在不分支 backend 的前提下消费 contract:
通过 ResetRandomizationPayload 启用 curated model-field terms;
保持一次 transaction / 一次 backend commit;
只通过已声明 UniSim capabilities 增加 Entity bindings 与 immutable defaults;
移除 _scene_inertial_defaults() 在 UniLab 侧的 MuJoCo XML recompile;
保留 fail-closed capability errors;
manager code 不 import mjbatch 或 MuJoCo。
L3 — UniLab SimToolReal 集成
Task owner 工作:
声明 600-tool catalog;
materialize tool_id = env_id % 600;
选择 backend-specific owner YAML;
接入在 tool variants 间保持统一的 observations/actions;
收集端到端 memory、cold-start、throughput 与 SAPG rollout 证据。
L4 — 结束 legacy DR 与 Manager-Based API 共存
不为新的 per-env model capabilities 扩展 DomainRandomizationManager。Manager-Based 能力闭环后,本 child 必须:
建立外部 provider 消费者清单;
将 legacy provider fields 映射为 event terms;
证明 reset observation、info、rollout 与 interval behavior 等价;
提供 deprecation notice 与迁移指南;
删除 DomainRandomizationProvider、DomainRandomizationManager、NpEnv legacy hooks 和 legacy tests/docs;
保持 Manager-Based API 为唯一 DR lifecycle。
删除是本 roadmap 的完成条件。若外部消费者不能迁移,则 roadmap 保持 open,并明确记录阻塞的消费者与迁出计划。
Execution status (2026-09-13)
mjbatch-uni: PR #12 merged to main at f694b933f9e2ed35a7fa0c74b057bd624c6c45c4. Tag v0.2.0 was published and PyPI release 0.2.0 completed with 13 artifacts.
UniSim: PR #70 merged to main at 4270aa81d868744980db90dac6dd959d3f542f50. Tag v1.3.0 was published and PyPI release 1.3.0 completed. Obsolete issue fix: restore G1 SAC locomotion training performance #67 /style: ruff format joystick_sac.py #68 remote branches were deleted after verifying their content was contained in the squash-merged tree.
UniLab: integration head c414cbf8d5ce9f3877af76712985a4bdea692c0b; PR #1568 is open to main, intentionally keeps the package version at 1.2.0, and consumes published unisim-core>=1.3.0 plus mjbatch-uni~=0.2.0.
UniLab main remains untouched. No integration-only direct dependency remains in the UniLab default or ROCm lock profiles.
Legacy-provider audit: the current UniLab integration head contains no production provider call and physically removes the unilab.dr namespace; importing it now fails closed. UniSim also removes the old init-time InitRandomizationPlan / ModelVariantSpec adapter path. Public GitHub code search still finds historical UniLab forks/downstreams carrying old symbols (11 non-Motphys/UniLab repos in the first 100 code hits, including two unilabsim downstream tasks). Those snapshots do not block this source branch, but maintainer approval of the breaking unreleased transition is still needed before closure.
Child issues UniLab Define task-owned fixed variant config and cross-repository ADR #1564 -Retire legacy DomainRandomization provider protocol #1567 , UniSim fix: restore G1 SAC locomotion training performance #66 -style: ruff format joystick_sac.py #68 , and mjbatch fix go2 reward #3 are closed.
Final local gates:
mjbatch 0.2.0: pytest -q (69 passed, 1 expected skip); ruff format/check, pyright, sdist build, cp313 x86_64 wheel build, and twine checks passed. A fresh isolated PyPI cp313 wheel install and model-field smoke passed.
UniSim 1.3.0: uv sync --locked --extra mujoco --extra mjwarp --extra motrix; pytest -q (279 passed, 33 skipped); ruff check ., uv lock --check, sdist build, twine check, and isolated sdist smoke passed. Fresh PyPI base and [mujoco] installs passed import/fixed-variant smoke tests.
UniLab: uv sync --locked --extra mujoco --extra mjwarp --extra motrix; pytest -q (1592 passed, 26 skipped after API/test ablation); make test-all passed; the representative one-iteration PPO slow test passed.
Required remote checks:
mjbatch-uni merged-main CI and v0.2.0 release workflow passed, including all wheel builds, sdist, provenance, and PyPI publication — main CI 34764223336 , release 34764639495 .
UniSim merged-main CI passed for 4270aa8, and the v1.3.0 release workflow passed sdist verification plus PyPI trusted publishing — main CI 34765573334 , release 34766092577 .
UniLab PR feat: complete per-env fixed model variant roadmap #1568 final head: ruff format/lint, mypy, pyright, benchmark smoke, Ubuntu coverage tests, and Sphinx all passed — CI 34766807223 , Docs 34766807198 .
Representative 64-env / 64-variant generated same-layout mesh workload:
Post-ablation CPU MuJoCo (three construction runs): median env construction 0.181 s, median construction RSS delta about 48.7 MB, all finite, all 64 assignments active; throughput varied 368k–473k env-steps/s on this small workload.
Post-ablation MJWarp / RTX 4090: env construction 0.970 s, first reset 0.00200 s, construction RSS delta 420.6 MB, 68,815 env-steps/s, finite. The added shared-field checks and derived-constant recompute prioritize correctness over a few tens of milliseconds on this CI-scale fixture.
A/B path ablation outcome
mjbatch removed the speculative four-level recompute taxonomy, unused derived-field metadata, retained per-Batch variant state, and redundant group convenience methods. It retained VariantPack and explicit topology-affine groups, and added shared model-parameter/options fail-closed validation.
UniSim removed the second direct fixed-variant lifecycle, mock FakeBackend path, speculative source-format negotiation, test-only capability helper, production-unused reset metadata, and retained per-variant MJWarp oracle models. MuJoCo/MJWarp no longer compile a canonical scene merely to discard it. MJWarp now refreshes compiler-derived constants before graph capture.
UniLab collapsed the task API to two config types plus optional explicit names; Manager factory directly builds the one final UniSim plan. The representative fixture exposes only materialized model files, reuses the production RSL-RL adapter and shared memory profiler, and no longer duplicates backend conformance assertions.
Closing cleanup removed the final compatibility surfaces: unilab.dr is absent and the old init-time model-variant API is no longer exported or implemented. Manager-Based construction/reset/interval paths are the only DR lifecycle, and unsupported capabilities fail closed.
Net code delta from the immediately prior heads: UniSim -272 lines, UniLab -296 lines. mjbatch removed 61 lines of redundant API while adding 113 lines of correctness/shared-field validation and tests.
Roadmap relationship and target branch
Roadmap status: implementation, A/B ablation, and the mjbatch 0.2.0 / unisim-core 1.3.0 releases are complete; UniLab review and merge remain open. Per maintainer direction, UniLab stays at 1.2.0 and is not tagged, released, or published in this roadmap step。
Declared base: main。
Planned UniLab integration branch: dev/issue-1563-per-env-model-contract。
Cross-repository policy: unisim 与 mjbatch child issues 遵循各自仓库治理;本 roadmap 记录它们的目标与验收门槛。roadmap 规划期间不修改任何仓库的 main。
PR policy: UniLab child PRs 指向 integration branch。跨仓 children 按依赖顺序 merge/release,并显式 pin 版本。
Definition of done
Dependencies and blockers
Completed: mjbatch PR 测了一下linux host numpy 做 mlp, 真是失败的尝试. 得出结论,在linux上这样搞太差了, 不要再幻想了 #12 merged and v0.2.0 was published; UniSim and UniLab now consume the registry dependency.
Completed: UniSim PR refactor: share logger lifecycle without changing wandb semantics #70 merged, stale child branches were deleted, and v1.3.0 was published; UniLab now consumes the registry dependency.
UniSim U1/U2/U3 必须先于 UniLab L2/L3 production integration。
MJWarp U3 依赖 neutral variant plan(U1)和 mjlab reference equivalence fixture;不依赖 CPU mjbatch executor 的 Python VariantPack。
MJWarp reset gravity parity 需要单独验证 Model.opt.gravity per-world kernel 语义;未验证前保持 fail closed。
无法呈现统一 public SimBackend layout 的 topology variant,阻塞在单独 heterogeneous contract 决策上。
Playback 需要在 canonical model、per-env snapshot、visual-twin field sync 之间由 maintainer 选择。
legacy 删除依赖外部 provider 消费者清单;未完成迁移不能关闭 roadmap。
Proposed owner
UniLab Env/Config maintainers、UniSim Backend maintainers 与 mjbatch maintainers 共同负责。另需一名跨仓 coordinator 负责 roadmap branch 与 release pin sequence。
Validation plan
mjbatch: 与独立 compile models 的确定性 exact comparison、多线程隔离、memory 与 throughput benchmarks。
UniSim: contract/conformance tests、import-boundary tests、MuJoCo/MJWarp reset/step/playback adapter tests、package compatibility。
UniLab: Hydra materialization、EventManager/reset transaction、Entity binding、fail-closed backend capability、SimToolReal rollout 与 benchmark evidence。
MJWarp: 以 mjlab variant implementation 为 reference oracle,覆盖 graph capture 前 initialization、固定地址 reset upload、per-world defaults、mesh/material playback 和 cross-backend rollout comparison。
跨仓: pin 住 UniLab -> UniSim -> mjbatch/MJWarp 的 integration smoke,证明 private engine object 不跨越 UniLab 边界。
Scope level
roadmap
Work type
feature
Area
mujoco
面向 maintainer 的摘要
SimBackendcapabilities 与 adapters;UniLab maintainers 负责 Hydra/task owner config、EventManager、Entity/reset transaction、task rollout,以及最终退役 legacy DR manager。本 roadmap 目前是提案,等待 maintainer 批准。边界获批前,不创建 integration branch 或 child issues。
问题与仓库证据
Discussion #1541 与 #1058 指出两个症状的共同根因:
DomainRandomizationManager之所以演化成 reset/DR 协议,是因为 CPU backend 缺少批量 per-env model write。MjModel,因为 backend 缺少 model-variant 间接层。当前三个仓库已经体现出预期的分层:
UniLab 现状
src/unilab/managers/event_manager.py刻意拒绝 directmodel_fieldsmutation,因为还没有公开 backend contract。src/unilab/base/reset_state.py已经能将多个 reset terms 组合成一次SimBackend.set_state(...)调用,并使用 dense selected-row randomization payload。src/unilab/envs/mdp/events.py仍拒绝RandomizeRigidBodyMass(..., recompute_inertia=True),并在冷路径通过重新 compile MuJoCo scene XML 获取惯性默认值。src/unilab/dr/manager.py仍是 legacy provider/reset 协议;当前仓库内没有任务调用_init_domain_randomization(),但文档明确该路径保留给外部 custom tasks。src/unilab/base/backend_factory.py是 UniLab 到 UniSim 的转译边界。UniSim 现状
src/unisim/backend/base.py拥有SimBackend、set_state、materialization 与 DR capability negotiation。src/unisim/dr/types.py拥有DomainRandomizationCapabilities、ResetRandomizationPayload,以及目前较窄的InitRandomizationPlan/ModelVariantSpec。src/unisim/backend/mujoco/backend.py已将 curated reset payload 映射到 mjbatchexpand(...)与一次set_const(...)。src/unisim/backend/mjwarp/randomization.py已体现 backend-owned per-world field expansion 与分级 derived-constant 重算的设计。src/unisim/backend/mjwarp/backend.py已支持 reset-time per-world mass/inertia、geom friction/size/solver 参数、damping/armature 和 kp/kd 写入,但 construction-time mesh variant 与 per-env playback 仍缺失。mjbatch-uni 现状
mjbatch-unimain 完成冲突更新,并且当前 head CI 通过;但它还不是 UniSim 消费的已发布mjbatch-uni版本线。Discussion #1541 对 mjlab 的架构结论不是“把 EventManager 搬进 backend”,恰恰相反:simulation 层拥有 field expansion、派生量重算与 variant realization;event manager 只声明和调度 task 语义。
补充审计 1:MJWarp / mjlab 兼容性
结论
可行,且必须成为本 roadmap 的完成条件,而不是事后 follow-up。
理由:
expand_model_fields(...);(1, tail...)tile 成(nworld, tail...);set_const/set_const_0分级重算。VARIANT_DEPENDENT_FIELDS覆盖geom_size、geom_rbound、geom_aabb、geom_pos、geom_quat、body_mass、body_subtreemass、body_inertia、body_invweight0、body_ipos、body_iquat;build_variant_model(...)为每个 world scattergeom_dataid与geom_matid;SceneCfg/SimBackend生命周期,并保持 graph capture 前完成所有 array replacement。现有缺口
当前 UniSim MJWarp backend 与目标之间有 6 个具体缺口:
输入模型仍是单一
SceneCfg.model_filematerialize_mjwarp_scene(...)只 materialize 一个 scene;geom_dataid/geom_matid未进入 per-world variant realizationgeom_pos/geom_quat未在当前 reset expansion list 中VARIANT_DEPENDENT_FIELDS;host DR mirrors 的默认值不能继续广播单一 CPU model
_bind_dr_host_mirrors()把self._cpu_model的默认值 broadcast 到(num_envs, ...);(N, ...)而不是(tail...)”的语义。reset-time primitive geom bounds 逻辑不适用于 mesh variants
PrimitiveGeomBounds只支持 sphere/capsule/ellipsoid/cylinder/box 的运行时 size 派生;rbound/aabb/pos/quat/inertia必须来自 reference compile oracle;playback 仍是单一 visual model
get_playback_model(env_index)验证后仍返回同一个scene_visual_model_file;geom_dataid/geom_matidsnapshot,或 per-env visual model materialization。需要的实现切片
MJWarp 支持应拆成独立 child,而不是藏在 MuJoCo adapter 里:
put_model(...)后、CUDA graph capture 前写入:geom_dataidgeom_matid兼容边界
nq/nv/nu/sensorlayout。mujoco_warp.Model.opt.gravity的 leading dimension 是*,技术上可以扩展,但需要单独 effect test 与 kernel 语义验证;是否纳入本 roadmap 由 maintainer 决策。若不纳入,任务配置在 MJWarp 上请求 gravity 必须 fail closed,而不是静默跳过。补充审计 2:结束 MBA 与 legacy DR 共存
结论
可行,但必须作为 roadmap 的 release-blocking child 处理;能否直接删除取决于外部 custom task 的迁移状态。
当前证据显示:
_init_domain_randomization()只有NpEnv定义,没有任何 in-repo task 调用;DomainRandomizationProvider没有在src/unilab中有 concrete production subclass;ManagerBasedRlEnv测试明确断言_dr_manager is None。DomainRandomizationProvider + DomainRandomizationManager接入;src/unilab/envs/mdp/events.py已有:geom_frictionjoint_armaturepd_gainsrandomize_rigid_body_massrandomize_body_mass_inertiarandomize_rigid_body_comrandomize_physics_scene_gravitypush_by_setting_velocityapply_body_impulserandomize_encoder_bias结束共存的目标状态
Roadmap 完成后:
ResetStateTransaction-> oneSimBackend.set_state(randomization=...);IntervalRandomizationPlan;DomainRandomizationProvider/DomainRandomizationManagerprotocol。NpEnv删除_dr_manager状态、_init_domain_randomization()、legacy reset override 和 interval dispatch hook。unilab.dr不再拥有第二套 manager/provider;如 Manager API 仍需 re-export UniSim plan/capability 类型,只能保留薄 re-export 或迁移到更合适的 owner module。env.domain_rand语义和 legacy examples 全部更新或删除。必要迁移步骤
外部消费者清单
env.domain_rand字段、reset observation/info side-effects、interval ops;语义等价迁移
build_common_reset_randomization()的 base mass/COM、gravity、ground friction、armature、kp/kd 映射到对应 event terms;IntervalTermOp或现有 interval event terms。弃用窗口
删除与守卫
src/unilab/dr/manager.py与provider.py;NpEnvhooks;_dr_manager,import graph 中不存在 legacy manager。如果外部消费者无法在 roadmap 期限内迁移,本 roadmap 不能标记完成;不得为了结束共存而静默破坏外部任务,也不得继续给 legacy protocol 添加 per-env variant 能力。
提案架构
1. reset-time model mutation 保持在现有 payload contract 之后
UniLab event terms 继续在
ResetStateTransaction中组合写入,并提交一个 curated payload:UniSim 应扩展
DomainRandomizationCapabilities与ResetRandomizationPayload,而不是暴露 raw mjbatch views。Event terms 不得调用expand、set_const、geom_dataid或 backend-private pool。2. 固定 model identity 使用 construction-time variant plan
SimToolReal 的
tool_id = env_id % 600是 task identity,不是每次 reset 的 randomization。UniLab 应 materialize 一个最终 immutable assignment,并将 backend-neutral variant plan 传给 UniSim。UniSim plan 必须仅依赖 NumPy/stdlib,并保持 pickle-safe:
MjSpec对象;(num_envs,);现有
ModelVariantSpec(geom_size_overrides=...)过窄且偏引擎实现,不应扩展成通用 mesh/topology catalog。3. engine realization 留在 backend executor/adapter
mjbatch CPU executor 应拥有:
mjModel应用;set_const/ derived-field 重算;VariantPack;UniSim MuJoCo adapter 可以调用这些 primitives,但它们不是 UniLab public API。
MJWarp path 的 realization 归 UniSim MJWarp adapter:
geom_dataid/geom_matid;MuJoCo CPU 与 MJWarp 可以共用 backend-neutral variant plan 与 MuJoCo-family source preparation helper,但不得把 live
MjSpec、mjbatch view 或 Warp array 暴露给 UniLab。4. 不提前暴露异构 state/action shapes
当前
SimBackend假设一个统一 public layout:nq、nv、actuator 数、control shape、sensor layout 与 state arrays。只有当所有 variants 都能投影到这个统一 contract 时,才允许 topology-affine execution。仅改变 tool geometry/mesh、而 robot joints、actuators、sensors 与 observation layout 保持对齐的 variants 在范围内。需要 per-env
nq/nv/nu或 ragged sensors 的 variants 依赖另一个 heterogeneous contract 决策,在本 roadmap 中 fail closed。5. playback 成为显式 backend capability
Per-env visual playback 不得由 UniLab 用 private MuJoCo 对象拼装。UniSim 应定义 backend 可提供:
MuJoCo adapter 可以选择高效实现,但 capability 与语义必须公开且可测试。
Proposed child issues
Child issues:UniLab #1564/#1565/#1566/#1567;UniSim #66/#67/#68;mjbatch #3 / PR #12。建议 review 顺序保持纵向:先确认 contract 与 adapter 语义,再扩大 task rollout。
L1 — UniLab ADR 与 task config schema
范围:
不在范围:
MjSpec;M1 — mjbatch-uni executor release
基于已 review 的 PR #12:
U1 — UniSim contract 与 conformance
只扩展窄 public surface:
拒绝:
model_update()context 直接暴露给 UniLab;U2 — UniSim MuJoCo adapter 与 playback
将 neutral contract 转译到 mjbatch:
SimBackendlayout;expand(...),并执行一次 strongestset_const;U3 — UniSim MJWarp adapter 与 mjlab 对齐
将同一个 neutral variant plan 转译到 MJWarp:
put_model(...)后、CUDA graph capture 前初始化 per-world:geom_dataidgeom_matidgeom_sizegeom_rboundgeom_aabbgeom_posgeom_quatbody_massbody_subtreemassbody_inertiabody_invweight0body_iposbody_iquatL2 — UniLab EventManager、Entity 与 reset transaction
在不分支 backend 的前提下消费 contract:
ResetRandomizationPayload启用 curated model-field terms;_scene_inertial_defaults()在 UniLab 侧的 MuJoCo XML recompile;L3 — UniLab SimToolReal 集成
Task owner 工作:
tool_id = env_id % 600;L4 — 结束 legacy DR 与 Manager-Based API 共存
不为新的 per-env model capabilities 扩展
DomainRandomizationManager。Manager-Based 能力闭环后,本 child 必须:DomainRandomizationProvider、DomainRandomizationManager、NpEnvlegacy hooks 和 legacy tests/docs;删除是本 roadmap 的完成条件。若外部消费者不能迁移,则 roadmap 保持 open,并明确记录阻塞的消费者与迁出计划。
Execution status (2026-09-13)
mainatf694b933f9e2ed35a7fa0c74b057bd624c6c45c4. Tagv0.2.0was published and PyPI release 0.2.0 completed with 13 artifacts.mainat4270aa81d868744980db90dac6dd959d3f542f50. Tagv1.3.0was published and PyPI release 1.3.0 completed. Obsolete issue fix: restore G1 SAC locomotion training performance #67/style: ruff format joystick_sac.py #68 remote branches were deleted after verifying their content was contained in the squash-merged tree.c414cbf8d5ce9f3877af76712985a4bdea692c0b; PR #1568 is open tomain, intentionally keeps the package version at 1.2.0, and consumes publishedunisim-core>=1.3.0plusmjbatch-uni~=0.2.0.mainremains untouched. No integration-only direct dependency remains in the UniLab default or ROCm lock profiles.unilab.drnamespace; importing it now fails closed. UniSim also removes the old init-timeInitRandomizationPlan/ModelVariantSpecadapter path. Public GitHub code search still finds historical UniLab forks/downstreams carrying old symbols (11 non-Motphys/UniLabrepos in the first 100 code hits, including twounilabsimdownstream tasks). Those snapshots do not block this source branch, but maintainer approval of the breaking unreleased transition is still needed before closure.pytest -q(69 passed, 1 expected skip); ruff format/check, pyright, sdist build, cp313 x86_64 wheel build, and twine checks passed. A fresh isolated PyPI cp313 wheel install and model-field smoke passed.uv sync --locked --extra mujoco --extra mjwarp --extra motrix;pytest -q(279 passed, 33 skipped);ruff check .,uv lock --check, sdist build, twine check, and isolated sdist smoke passed. Fresh PyPI base and[mujoco]installs passed import/fixed-variant smoke tests.uv sync --locked --extra mujoco --extra mjwarp --extra motrix;pytest -q(1592 passed, 26 skipped after API/test ablation);make test-allpassed; the representative one-iteration PPO slow test passed.4270aa8, and the v1.3.0 release workflow passed sdist verification plus PyPI trusted publishing — main CI 34765573334, release 34766092577.A/B path ablation outcome
VariantPackand explicit topology-affine groups, and added shared model-parameter/options fail-closed validation.unilab.dris absent and the old init-time model-variant API is no longer exported or implemented. Manager-Based construction/reset/interval paths are the only DR lifecycle, and unsupported capabilities fail closed.-272lines, UniLab-296lines. mjbatch removed 61 lines of redundant API while adding 113 lines of correctness/shared-field validation and tests.Roadmap relationship and target branch
main。dev/issue-1563-per-env-model-contract。main。Definition of done
DomainRandomizationManager/ provider protocol、NpEnvlegacy hooks、legacy tests/docs 已删除或进入已批准删除窗口;Manager-Based API 是唯一 DR lifecycle。Dependencies and blockers
VariantPack。Model.opt.gravityper-world kernel 语义;未验证前保持 fail closed。SimBackendlayout 的 topology variant,阻塞在单独 heterogeneous contract 决策上。Proposed owner
UniLab Env/Config maintainers、UniSim Backend maintainers 与 mjbatch maintainers 共同负责。另需一名跨仓 coordinator 负责 roadmap branch 与 release pin sequence。
Validation plan