Replies: 2 comments
补充调研:mjbatch —— CPU 路径上"缺失的那一层"已有可用的第三方实现(附方向决策)调研了 kevinzakka/mjbatch(CPU 上并行跑数千个 MuJoCo 仿真的库:C++ 线程池、释放 GIL、跨 batch 的数组直访、per-sim 模型参数)。核心结论:本文定位的"per-env 模型字段间接层",mjbatch 在 stock CPU MuJoCo 上已经完整实现了一套——不改上游、不依赖 warp。 这直接改变三个方案的取舍。 1. 机制对应:mjlab 的三个关键设计,mjbatch 各有等价物
内存特性与本文判断直接相关:model 副本是每线程一份、不是每 env 一份(只有首次 expand 才分配);每个 env 常驻的只有 2. 这意味着什么"终态"不必等上游。 本文判断 DR 的自然形态是 event term + 批量 per-env 字段写,但认为 CPU 路径要等上游 per-world 字段或 warp backend。以 mjbatch 为 backend,现在就能写: batch.expand("body_mass")[env_ids, body_id] *= u
batch.set_const(env_ids) # subtree_mass / dof_M0 等派生量逐 env 重算
capability 契约有了参考实现。 建议 3 要的"per-world 字段能力作为 对方案 2 有个意外的利好。 mjbatch 禁止 expand 的是 3. 代价与边界(如实说明)
4. 方向决策更新基于以上,三个候选方案重新定夺:
|
现状更新:per-env 模型间接层已在 MuJoCo 与 MJWarp 后端落地,legacy DR manager 已退役结论先更新一下:这个讨论定位的“backend 缺少 per-env model-field / model-identity indirection”已经不是提案状态。UniSim 的 MuJoCo CPU 与 MJWarp 两个 adapter 都已经落地,UniLab 也完成了消费侧收口。设计目标保持不变,并且现在可以明确写成两条硬约束:
1. MuJoCo CPU 后端
2. MJWarp 后端
3. UniLab 消费侧与架构收口
4. 仍然 fail closed 的边界这些是显式契约边界,不是回退到 legacy path 的理由:
关联索引UniLab
unilabsim/unisim
unilabsim/mjbatch_uni
因此,这个讨论的终态判断可以更新为:event manager + backend capability + curated per-env payload 已经在两个 production backends 兑现; |
Uh oh!
There was an error while loading. Please reload this page.
背景与问题
起因是一个直觉问题:为什么 UniLab 的 DR 要专门写一个
DomainRandomizationManager,而不是像 mjlab / Isaac Lab 那样直接用 event manager 管理?带着这个问题对比调研了 mjlab(MuJoCo Warp)和 Isaac Lab(PhysX)后,我们发现 DR manager 的存在和 #1058(SimToolReal 600-tool 吞吐瓶颈)其实是同一个根因的两个症状。把调研结果整理出来,供架构演进参考。
1. UniLab 现状:DR manager 与 event manager 是互斥的两代架构
DomainRandomizationManager(src/unilab/dr/manager.py)+DomainRandomizationProvider,目前仅 Sharpa 手任务家族在用,文档已标注为 legacy。它在 legacyNpEnv架构里不只是 DR——它就是 reset 本体:provider 产出ResetPlan(env_ids + qpos/qvel + randomization payload),manager 做 backend 能力协商(get_dr_capabilities())后调backend.set_state(...),再重建观测。plan 是 pickle-safe 的不可变 dataclass,要跨 spawn collector 进程。events:terms 做 DR,走EventManager(迁移自 mjlab v1.6.0 API),ManagerBasedRlEnv整个覆写reset(),两条路径互不调用。所以"为什么不用 event manager"对新任务已经成立——新任务确实在用。但新路径会撞到天花板:
RandomizeRigidBodyMass对recompute_inertia=True直接NotImplementedError,EventManager 明确拒绝直接 model-field 变更。不是 event manager 抽象不行,而是它底下的 backend contract 没有暴露对应能力。2. mjlab 为什么 event manager 就够:Sim 层吸收了模型字段间接寻址
mjlab 没有 DR manager,DR 全是挂在
EventTermCfg上的dr/函数。前提是 mujoco_warp 的单 model 多 world 架构:Sim.expand_model_fields()把 model 字段从 stride-0 共享广播 tile 成(nworld, N)真实 per-world 内存,零拷贝 torch 直写,CUDA graph 自动重捕获;@requires_model_fields("body_mass", recompute=set_const)声明字段与重算级别,EventManager 统一收集、统一扩容、apply()末尾按最强RecomputeLevel只重算一次;Sim层吃掉,manager 层无需特化。同一个机制支撑了 per-world mesh variants(
src/mjlab/entity/variants.py):所有 variant 的 mesh 合并进一个 canonicalMjModel只 compile 一次,per-world 差异通过(nworld, ngeom)的geom_dataid间接寻址 + 11 个派生字段 expand 实现;字段数值全部来自每个 variant 源 spec 的独立 compile 再 scatter(compiler-coherent),并有与独立 compile 字节级对照的等价性测试。3. 这与 #1058 的关系:mjlab 方案本质就是 issue 的「方案 2」
#1058 的三个候选方案对照 mjlab 实践:
mjModel的 ownership/连续 buffer 不适合部分共享,它用"合并进一个 model"回避。可以关闭,requires upstream MuJoCo。worldid索引(nworld,...)数组);stock CPU MuJoCo 的mjData直接解引用mjModel指针,没有 per-env-row 模型字段间接层,无法直接搬进 MuJoCoUni 的 CPU BatchEnvPool。CPU 路径要等价实现,需上游 MuJoCo 支持 per-world 字段,或引入 warp 类 backend。约束匹配度:600 工具只有 3 类 topology,mjlab 的 slot 机制允许 mesh geom 数量不同(padding +
dataid=-1),最坏退化为 3 个 template model(600→3 的数量级改善);assignment 固定 at init,与tool_id = env_id % 600完美匹配。4. Isaac Lab 的对照:异构性放在 USD 层,运行时无感知
MultiUsdFileCfg:600 个工具 USD 各建 prototype,Sdf.CopySpec复制 reference arc 到每个 env prim(index % 600)。USD 层源 layer 共享,但replicate_physics=False使 PhysX parser 放弃"解析 env_0 一次"的优化,12,288 个 prim 逐个 cooking(Isaac 冷启动 309s vs MuJoCoUni 20s 的主因)。instanceable被强制关闭、Isaac Lab 自己的 ray caster 要手写 mesh 去重、PhysX 官方称共享是调用方责任)——即可能存 12,288 份而非 600 份。mjlab 的 mesh 池方案在内存维度上实际优于 Isaac。set_masses/set_inertias/set_material_properties)本身就是批量 per-env 写接口,所以 DR 也只是 event term 的一次 tensor setter 调用。5. 核心判断:缺的是同一个层
MjModel指针,#1058 里甚至 600 个不同 modelDR manager 的存在和 600-model 瓶颈是同一根因:CPU MuJoCo backend 缺少 per-env/per-world 的模型间接寻址层。 缺它 → 工具只能编译成 600 份完整 model;缺它 → per-env 物理随机化无法写成一次批量 tensor 写,必须有专门组件做能力声明、payload 过滤、跨进程 plan。
6. 建议方向
SimBackend接口的 capability 声明引入(与 DR capabilities 同一条协商路径),backend 不支持则 fail closed,而不是在 env/manager 层探测。DomainRandomizationManager这个 legacy 协议层可随 Sharpa 家族迁移一起退役——一个问题(Task:评估公共模型复用与 model-affine 调度,解决 SimToolReal 600-tool 吞吐瓶颈 #1058)的解决同时消灭两个技术债。欢迎讨论:CPU 路径推动上游 per-world 字段支持 vs 引入 warp 类 backend 的取舍,以及方案 3 落地的优先级。
All reactions