You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
调研报告:Holosoma 如何统一 MuJoCo / IsaacGym / IsaacSim 物理后端
摘要
Holosoma 的“统一物理后端”不是把几个引擎包装成同名的
step(),而是形成了一条完整闭环:其中最重要的不是继承关系,而是三件事:
它也并非完全消除了后端分支:初始化时序、domain randomization 和绘制等路径仍有
hasattr/ simulator type switch,宽基类中也仍有NotImplementedError。这些是 UniLab 不宜照搬的部分。本文是源码审计,不是跨引擎数值精度或性能 benchmark。审计基线:
amazon-far/holosoma@6761e2bunilabsim/UniLab@21dbe461. 配置负责选择后端,任务负责统一编排
Holosoma 用
SIMULATOR_REGISTRY把逻辑名称映射到 concrete config:isaacgym -> IsaacGymisaacsim -> IsaacSimmujoco -> MuJoCo + classicmjwarp -> MuJoCo + warp这里
mjwarp不是第四套任务接口,而是复用 MuJoCo facade、只替换其内部计算后端。注册与默认配置见config_values/simulator.py#L10-L111。BaseTask根据_target_动态构造 simulator,再统一执行:任务层因此掌握生命周期顺序,但不知道 backend constructor 的细节:
base_task.py#L89-L190。这解决的是“从配置得到哪个实现”和“各实现按什么生命周期启动”两个问题;它本身还没有解决状态语义是否真的一致。
2.
BaseSimulator是任务访问物理世界的统一前门BaseSimulator汇集了场景和资产创建、仿真 step/refresh、控制、actor/object state、camera/sensor、viewer/bridge 等入口:base_simulator.py#L31-L116、base_simulator.py#L249-L467。具体的 IsaacGym、IsaacSim、MuJoCo 类实现这个前门。MuJoCo 内部还有第二层 facade:顶层
MuJoCo对任务隐藏 classic 与 warp 的差异,IMujocoBackend再约束ClassicBackend/WarpBackend。adapter 可以按数据驻留位置选择 zero-copy fast path 或 CPU slow path,任务代码不需要感知:mujoco/backends/base.py、mujoco.py#L292-L343。所以它实际有两层变化轴:
3. 真正的 contract 是状态语义,而不是方法名
Holosoma 把 actor root state 固定为:
并进一步约束:
xyzw;write_state_updates()显式 flush。这些约束直接写在
BaseSimulatoractor state API 中:base_simulator.py#L842-L1028、base_simulator.py#L1033-L1170。UnifiedRootStatesView又为旧式 tensor 使用方式提供相同的逻辑视图:root_states_view.py#L1-L114。adapter 的价值体现在“原生表示不一致”时:
wxyz与 contractxyzw间转换。实现证据分别见
MuJoCo get/set actor state、IsaacGym get/set actor state、IsaacSim get/set actor state。一个值得保留的判断是:同样叫
get_actor_state()并不代表统一;只有 frame、quaternion、shape、ordering、写入可见性都一致,才是可替换的 contract。4.
ObjectRegistry把 backend 物理索引变成语义地址不同引擎对 actor/object 的创建顺序和绝对索引不一致。Holosoma 不让任务持有这些物理索引,而是引入
ObjectRegistry:ROBOT、静态SCENE、可自由运动的INDIVIDUAL分类;[env0_objects][env1_objects]...;因此绝对物理索引可以因 backend 而不同,任务依赖的名字和虚拟地址不变。数据结构与索引规则见
object_registry.py#L39-L150、object_registry.py#L280-L455,共享注册模板见base_simulator.py#L320-L377。这相当于在 simulator API 上建立了一个稳定的“虚拟地址空间”,是多 actor 任务能跨后端复用的重要前提。
5. 配置只统一真正同义的字段
Holosoma 没有把所有引擎参数拍平成一组看似通用的标量:
PhysicsConfig的核心只放 mass/density;physx块;isaacgym/isaacsim/mujoco原生配置块;配置结构见
config_types/scene.py#L17-L182。资产侧支持 USD / URDF / XML 多格式候选,由 shared selector 根据 backend 支持列表选择;无支持格式时直接报错,而不是静默 fallback:
asset_format.py#L1-L70。这体现了一个重要边界:统一的是选择协议和失败语义,不是强行让每个引擎接受同一种原生资产。6. hooks/plugins 统一 side effects,而不复制 step loop
Holosoma 定义了以下 lifecycle phase:
hook manager 校验 callback arity,保持 deterministic registration order,支持 frequency cadence,并在 close 时逆序清理:
hooks.py#L11-L205。BaseTask与直接使用 simulator 的路径发出同一组 phase:base_task.py#L441-L470。camera、录制、遥操作、bridge 等横切能力因此可以注册到生命周期,而不是各自复制或侵入训练 step loop。例如 camera producer 先注册
FRAME_END,消费者插件后注册,就能通过注册顺序保证读到本帧 fresh buffer。相关设计演进可见 hooks PR #158、plugins PR #160、scene/object abstraction PR #147、cross-backend DR PR #162、camera PR #170。
7. 跨后端测试验证“发生了相同的物理行为”
Holosoma 把同一组行为场景复用于各 backend wrapper,覆盖:
入口分别见
test_behavior_mujoco.py#L1-L121、test_behavior_isaacgym.py#L1-L94、test_behavior_isaacsim.py#L1-L71。不支持的 backend × behavior 组合显式skip并记录原因,而不是从矩阵中静默消失。这类测试比“setter 后 getter 返回同一个值”更有价值,因为后者只能证明存储/API 自洽,不能证明 frame conversion、接触参数或 step semantics 正确。
8. 统一层仍有泄漏与债务
Holosoma 的实现应视为有价值的工程样本,而不是可以原样复制的最终形态:
BaseTask仍通过hasattr(self.simulator, "gym")区分 IsaacGym 的初始化时序:base_task.py#L132-L162。这说明 lifecycle contract 还不完整。get_simulator_type()后直接调用 backend native API:randomization/terms/objects.py#L180-L312。这破坏了 dependency inversion。BaseSimulator较宽,许多可选能力靠NotImplementedError或测试 skip 表达,而不是显式 capability object。所以它做到了“主体任务可跨引擎复用”,但还没有做到“所有上层代码只依赖纯 contract”。
9. 与 UniLab 当前设计的对照
main@21dbe46BaseSimulator+ concrete simulatorsSimBackend(abc.ABC)_target_create_backendxyzwwxyzObjectRegistry+ virtual indexNotImplementedError/ skip 表达hasattr或 simulator type switchUniLab 已有的基础包括:
SimBackend统一 step/set_state、base/body state、sensor、DR、playback 等 contract,并明确 base position/velocity 的 world frame 与 quaternionwxyz:base/backend/base.py#L58-L317、base/backend/base.py#L435-L478;BackendPlayCapabilities和 DR capabilities 已显式暴露支持面,而不是让 env 猜 backend class;NpEnv.step()只通过_backend.step()推进物理:np_env.py#L171-L245;create_backend()集中处理 lazy import 和 constructor routing:base/backend/__init__.py#L92-L187;registry.py#L39-L124;SceneCfg当前只声明 model、fragments、terrain 与 visual override,是较薄的冷路径配置:scene.py#L8-L29。换句话说,UniLab 不缺一个新的顶层
BaseSimulator;更值得补的是 contract 的覆盖范围、生命周期扩展点和跨后端行为证据。10. 对 UniLab 的建议
以下是基于本次审计的设计建议,不代表已经批准的实现 roadmap。
A. 保持现有 owner config 与
SimBackend边界继续由
task=<task>/<backend>owner config 选择完整可运行组合;training.sim_backend只作为身份/一致性字段。env 热路径不根据 backend string、class name 或hasattr切换实现。B. 先补语义 contract,再补方法
每个新增 contract 都应同时写清:
UniLab 当前
wxyzcontract 没有必要为了模仿 Holosoma 改成xyzw;关键是所有 backend adapter 在边界转换、同一测试矩阵验证。C. capability-first,避免继续扩大宽基类
对资产格式、actor state write、sensor、renderer、video、DR 等可选能力,优先扩展 typed capability/plan,而不是让上层捕获
NotImplementedError,也不要从backend_type推断支持情况。配置解析时就应 fail loudly 或给出明确 warning。D. 多 actor/object registry 按实际需求增量建设
如果路线图出现物体操作、动态障碍或多 actor 场景,可以借鉴
ObjectRegistry的语义 name + virtual index:backend 在冷路径报告实体,共享层生成稳定映射,热路径只用缓存 ID。若任务始终是单机器人 locomotion,则不应为了架构对称先复制all_root_statesproxy。E. 引入小而明确的 lifecycle phase
优先考虑
PRE_STEP / POST_STEP / EPISODE_START / EPISODE_END / CLOSE等最小集合,并定义:注册顺序、异常策略、调用频率、资源逆序释放。录制、观测采集、遥操作、profiling 等 side effect 通过 phase 扩展,避免 runner/env/backend 各自复制 step loop。F. 建立 scenario × backend 行为矩阵
为同一个 task/scenario 在 MuJoCo、Mjwarp、Motrix、Drake 等被声明支持的 backend 上复用行为断言,至少覆盖:
不支持的格子必须记录明确原因。容差应按物理量和 backend 定义,不能要求不同 solver trajectory bitwise equal。
G. 坚守冷热路径边界
资产解析、name -> id、model metadata、shape/capability 检查在构造/materialize 阶段完成并缓存;
step()、observation、reward、reset 热路径只使用已解析的数组、ID 和 callable。这样既保持 backend isolation,也避免统一层引入额外性能税。结论
Holosoma 的可迁移价值不在于一个“大而全”的基类,而在于它把统一后端拆成了 选择、语义、寻址、生命周期、适配、验证 六个相互闭合的 contract。UniLab 已经有更严格的
SimBackend、owner config 和 capability 基础,下一步若要增强跨后端一致性,应优先补 lifecycle 与 behavior matrix,并在真实多 actor 需求出现时再引入语义对象注册表;不要把 Holosoma 残留的 simulator type switch、hasattr分支或宽接口一并复制过来。All reactions