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.
总结论
当前 UniLab 的 Manager-Based API 是对 mjlab v1.6.0 “Manager 组合模型”的 NumPy/多后端迁移,不是 mjlab runtime 的 drop-in replacement。
可以概括为:
func + params、function/class term、局部 resetManagerBasedRlEnv.step()返回值NpEnvStateSimBackend多后端因此,“兼容”的准确含义是:熟悉 mjlab Manager API 的开发者可以沿用同样的 term/config/lifecycle 心智模型;但一个依赖 Torch、mjlab Scene/Entity、Warp model fields、IK、camera/raycast 或 viewer 的 task,不能只改 import 就迁移到 UniLab。
1. 对比基线
本次比较锁定:
dev/issue-1042-manager-based-api,e6f136d4ef9fb560b71aa7f738c80fc0b377d79fmain,0fb8a681136be94ffc636a3dd423cabb97d91f10UniLab 的 ADR-0006 正好将这个 mjlab commit 固定为迁移基线,所以这不是泛泛比较,而是可以逐文件追溯的 source-aligned migration。
需要区分三层:
后两层不能由第一层自动推出。
2. 总体架构
mjlab 是纵向集成的 MuJoCo runtime:Scene、Entity、Sensor、Simulation、Viewer、训练栈彼此紧密配合。
UniLab 是横向分层的 infrastructure:Manager 不接触 backend 私有对象,场景资产由 task/XML 持有,物理能力必须经
SimBackendcontract 暴露,训练框架在NpEnv边界之外适配。3. 公共 API 兼容度
两边 canonical public surface 基本逐名一致:
ManagerBase、ManagerTermBase、ManagerTermBaseCfgActionManager、ActionTerm、ActionTermCfgObservationManager、ObservationGroupCfg、ObservationTermCfgRewardManager、RewardTermCfgTerminationManager、TerminationTermCfgEventManager、EventMode、EventTermCfgCommandManager、CommandTerm、CommandTermCfgCurriculumManager、CurriculumTermCfgMetricsManager、MetricsTermCfgRecorderManager、RecorderTerm、RecorderTermCfgSceneEntityCfg可在两边的 exports 直接核验:
UniLab 的扩展:
NoiseCfg、NoiseModelCfg、NoiseModelWithAdditiveBiasCfg、ConstantNoiseCfg、UniformNoiseCfg、GaussianNoiseCfg。ManagerBasedRLEnv/ManagerBasedRLEnvCfg大写RLalias;它们和ManagerBasedRlEnv是同一对象,没有第二套实现。make_manager_based_rl_env()factory。关键不兼容点:
ManagerBase、ManagerTermBase、env 都有devicecontract。device,term 输入输出使用np.ndarray,manager core 不允许依赖 Torch、learner 或 runner。mjlab.*改为unilab.*。.to()、.cpu()、Torch indexing 或 Warp API,必须改写。4. Env 配置设计
sim.mujoco.timestepsim_dttimestep * decimationctrl_dtdecimationsim_substeps = ctrl_dt / sim_dtscene.num_envsnum_envsepisode_length_s,默认0.0max_episode_seconds,必须有限且大于零SceneCfgcomposerSimulationCfgSimBackendViewerConfigreset_scene_to_defaultevents={},owner YAML 必须显式声明"obs"/"critic"UniLab 新增两个重要字段:
policy_observation_groupcritic_observation_group映射到 runner-facing:
被映射的 group 必须:
concatenate_terms=Trueobs_groups_specmjlab 则可以直接返回任意 group 名,也支持 non-concatenated nested group。
UniLab production 配置路径是:
materializer 支持
_target_、dotted callable、typed overlay、None禁用;未知字段、错误 target、错误 term 类型和缺少必填字段均 fail-closed。代表配置可见:后端切换必须选择对应
task=<task>/<backend>owner YAML;training.sim_backend只是身份字段,不能单独 override 来切换后端。5. Env 返回与终止契约
mjlab
所有主要数据是 Torch tensor,group 名原样保留。
UniLab
核心差异如下:
reset()(torch obs dict, extras)(numpy obs dict, info)step()NpEnvStatestate.final_observation、info["final_observation"]info["_final_observation"]auto_reset=Falsetruncatedterminated=Truemjlab 的
is_finite_horizon主要影响 RSL-RL wrapper 是否提供 bootstrap 使用的extras["time_outs"];raw env 仍返回 timeout 为truncated。UniLab 在 env 层完成语义重映射:
truncatedterminatedUniLab 的 final-observation contract 对 off-policy replay、正确 bootstrap 和异步采集尤其重要。NpEnvState 与 auto-reset 实现 可见其先复制 terminal observation,再执行局部 reset 和 obs scatter。
6. 初始化、step、reset 时序
初始化
mjlab:
Scene,组合MjSpec。Simulation并初始化 Scene/Entity/Sensor。UniLab:
num_envs与 backend 一致性。EntityScenefacade 和ResetStateTransaction。SimBackend.materialize()。单步时序
mjlab:
mjlab 源码明确说明:MuJoCo
mj_step的 placement 导致 termination、reward、event 所读的部分 derived quantity 落后最后一个 physics substep;随后统一sim.forward(),保证 observation 刷新。UniLab:
UniLab 不在 env 内直接循环 backend 私有 physics API;子步生命周期由
SimBackend.step()所有。reset
mjlab reset 核心顺序:
UniLab reset:
这种 transaction 设计是为了避免多个 reset/event term 各自直接写 backend,并确保 backend mutation 仍由 owner contract 控制。
7. 各 Manager 功能矩阵
func + params、function/class term、插入顺序、局部 resetdevice;加强 cfg、shape、dtype、finite 校验requires_substep_state_feedback;无反馈时只 dispatch 一次nan_policy="error";runner boundary 只映射 policy/criticreward/<term>runner logtime_out=Truetimeout 分离startup/reset/interval/step、global/per-env timer、reset 最小间隔model_fields;mutation 走 backend capabilitypost_compute()last/max/mean/sum、per_substepsim_substeps>1时不支持per_substep,构造阶段直接失败pre_reset/post_reset/post_step/closehooksObservation 细节
两边 term pipeline 相同:
共同支持:term/group history、flatten 或保留 history 维、per-env 或共享 delay、hold probability、update period、per-env phase、partial reset history backfill、concatenated 或 term dict 输出。
差异:
nan_policy="disabled"。"error",并更严格检查数组类型、batch shape、dtype、range 和有限值。Reward
mjlab 在 reward term 计算后执行
torch.nan_to_num(value, nan=0.0, posinf=0.0, neginf=0.0),即异常 reward 会静默归零。UniLab 在 Manager term 边界直接抛错,非法输出正常情况下不会到达
NpEnv的通用最终 sanitize。这更符合 fail-closed,但意味着某些原来“带病训练”的 mjlab task 会在 UniLab 立即停止。Command 的源兼容陷阱
mjlab 使用
_update_metrics(self),UniLab 使用_update_metrics(self, env_ids: np.ndarray | None = None)。UniLab 这样做是为了 reset-row scoped 更新。因此直接复制一个覆盖零参数
_update_metrics()的 mjlab command term,需要改签名。UniLab 还增加post_compute(),用于 command transaction commit 后刷新派生状态。mjlab 额外提供 DebugVisualizer、Viser GUI controls、viewer pause callback、GUI reset override、per-term debug checkbox。UniLab manager core 不拥有 viewer glue;
debug_vis=True会显式NotImplementedError。8. Reusable MDP term 覆盖
这里比较的是仓库共享
envs/mdpnamespace,不代表 task-specific term 的总数。Action
mjlab 内置 joint position、relative joint position、joint velocity、joint effort、tendon length/velocity/effort、site effort、Differential IK。
UniLab 共享 action namespace 目前只有
JointPositionAction。部分 production task 有自有 action term,例如 Allegro、Stewart 等,但它们不是上述通用 action API 的等价补齐。Observation
共同项主要包括 base linear/angular velocity、projected gravity、relative joint position/velocity、last action、generated commands、built-in named sensor、sensor-derived projected gravity。
mjlab 共享层另外有
height_scan,并依赖完整 raycast sensor。UniLab named sensor term 在冷路径调用 backendbind_sensor_data(names);热路径只读 immutable view,不重新解析 sensor 名称或 XML。Reward
共同项包括 alive/terminated、joint velocity L2、action rate/acceleration、flat orientation。
mjlab 共享层额外包括 joint torque/acceleration、joint limits、posture、electrical power。UniLab 共享层额外包括 linear/angular velocity tracking、body angular velocity penalty。
大量 locomotion、motion tracking 和 manipulation reward 位于各自 task-owned manager term 模块,不能只看共享文件推断 production 覆盖。
Termination
共同项包括 timeout、bad orientation、root height below threshold。mjlab 额外有通用
nan_detection;UniLab 主要通过 manager/NpEnv 边界校验处理非法数值。Event 与 Domain Randomization
mjlab 的完整 model-field DR namespace 覆盖 actuator、body、camera、geom、joint、light、material、contact pair、site、tendon。
其核心设计包括
requires_model_fields、RecomputeLevel、MuJoCo-Warp per-world model-field expansion,以及修改后按 recompute level 更新 derived model data。UniLab 发现携带
model_fieldsmetadata 的 event 会直接报NotImplementedError,不会绕过 backend contract。UniLab 当前共享 reusable event/DR 包括 reset scene/default/root state、geom friction、joint armature、PD gains、rigid-body mass、rigid-body COM、scene gravity、velocity push。
限制包括:
SimBackend。recompute_inertia=True当前不支持。mjlab 共享 events 还覆盖 terrain patch reset、joint offset reset、external force/torque 和 body impulse 等。
9. Scene、Entity、Sensor 与 keyframe
spec_fnmodel_file、fragments、terrain slot、entity partitionsEntityCfg.spec_fn()生成/编辑 MjSpecdefault_keyframe_name引用 task-level XMLmjlab 的
EntityData提供很宽的状态面,包括 root/body/COM/geom/site pose 与 velocity、joint position/velocity/acceleration/force、tendon state/target、site effort target、external wrench、actuator state、camera/light/material indexing。UniLab 当前正式 facade 主要覆盖 root/body pose 与 velocity、joint position/velocity/default/soft limits、joint-position target、reset/DR 所需正式写入口、named sensor view,以及 projected gravity 等可由正式状态派生的值。
SceneEntityCfg的 selector 表面仍与 mjlab 基本一致,包括 joint/body/geom/site/actuator/tendon/camera/light/material/texture/pair,且保留 regex、preserve_order、names/IDs 一致性检查和全选压缩。但若 UniLab Entity/backend 没有正式暴露 tendon、camera、light 等能力,resolve 阶段会 fail-closed;selector 字段存在不等于 capability 已实现。
keyframe 所有权也完全不同:
scene.fragment_files引入。相关实现:
10. Backend 与 Viewer
mjlab runtime 固定为 MuJoCo + MuJoCo-Warp,device 可配置,但 Manager、Entity、Sensor 与物理实现都围绕这套技术栈设计。
UniLab factory 可识别
mujoco、motrix、mjwarp、drake。这表示存在 backend 路由,不表示每个 task 或 manager capability 都在四个 backend 上支持。当前 Registry 路由统计:
UniLab 的 viewer/playback 由 backend 和
visualization/层拥有,不属于 Manager API。因而不能将“UniLab 能播放或渲染”理解为“具备 mjlab Manager DebugVisualizer/Viser GUI API”。11. RNG 与可复现性
mjlab:
seed()重置 Torch、NumPy、Python、Warp 等进程级/global RNG。cfg.seed=None的 docstring 声称会生成 seed 并写回,但当前 env 源码只在 seed 非空时调用seed();这是源码与文档不一致。UniLab:
seed=None时用secrets.randbits(63)生成实际 seed,并写回 cfg。np.random.Generator。seed()只替换该 generator 的状态,不重置整个进程的随机状态。所以两边即使使用相同整数 seed,也不能期待 Torch 与 NumPy 随机序列逐样本一致。
12. Registry、训练和 Sim2Sim
mjlab
Task registry 一次保存 training env cfg、play env cfg、RSL-RL cfg 和 optional runner class,加载时 deep copy。训练入口主要围绕 RSL-RL PPO,raw Torch observation 可以直接包装为 TensorDict。CLI 通过 Tyro overlay Python dataclass。
参见 mjlab task registry。
UniLab
Registry 分离 env config 类型/factory、每个 backend 的 env factory、Hydra task/backend owner 和 algorithm owner 配置。
NumPy env 在 wrapper 边界转换为 Torch/TensorDict。仓库提供 PPO、APPO、SAC、TD3、FlashSAC 等入口,但这不表示每个 task 都支持所有算法,实际支持必须由对应 YAML、注册和测试证明。
Sim2Sim
UniLab 独有跨 backend contract snapshot 和 checkpoint dimension guard。
当前 DENYLIST 为:
这些字段不一致时默认抛
CrossBackendIncompatibleError。其中env.*structural 字段只在一侧出现也会 fail-closed。WARNING_LIST 包括 reward tuning、action latency、
ctrl_dt;ALLOWLIST 包括 backend identity、scene、play steps、domain randomization、noise 等。其他行为:
run_config.json.contract_snapshot。training.sim2sim_strict=false可将 DENYLIST 差异降为 warning。实现见 sim2sim.py。mjlab 当前没有对应的 repository-level 跨物理后端契约,因为其 runtime 本身不是多 backend 抽象。
13. Production task 覆盖
mjlab:12 个静态注册 task
UniLab:39 个 production task
迁移矩阵划分为:
A2JoystickFlat、AllegroInhandRotation、AllegroInhandRotationGrasp、Go1JoystickFlat、Go2FootStand、Go2JoystickFlat、Go2WJoystickFlat、StewartBalanceGo1JoystickRough、Go2JoystickRough、Go2WJoystickRoughG1WalkFlat、G1WalkRough、G1Walk23DofFlat、G1Walk23DofRoughGo2ArmManipLoco、SharpaInhandRotation、SharpaInhandRotationGrasp总计:
这里的
Compatible表示使用 canonical NumPy Manager-Based runtime,不表示与旧 env 或 mjlab task 数值行为完全相同。例如 rough migration matrix 明确记录以下 legacy reward 尚未 manager port:
feet_gaitfeet_air_time及 variancefeet_contact_without_cmdfeet_height_bodyfeet_slidecontact_forcesundesired_contactsjoint_mirrorjoint_powerjoint_torques_l2joint_acc_l2及 wheel variant完整事实边界见 migration_matrix.py。
14. 实际迁移成本
device、.to()、Torch indexingrequires_model_fieldsDR最容易迁移的例子是纯数值 term:
如果迁移需要在 term 中加入 backend 分支、访问 model/data 私有对象,说明缺的是 owner-layer contract,不能在 task term 内绕过。
15. 选型判断
如果目标是:
那么 mjlab 的完整 runtime 更直接,内置功能也更宽。
如果目标是:
NpEnvState、async/off-policy/training contract;那么应使用 UniLab Manager-Based runtime,但把它视为“mjlab 风格组合 API + UniLab runtime”,而不是 mjlab runtime 的替身。
若要继续提高两者功能覆盖,优先级应是:
SimBackend增加正式 post-substep hook,解除 metrics 限制。这些都属于新公共 contract 或 backend production 能力,按当前仓库治理规则应拆成独立 issue,不适合顺手补齐。
16. 已知文档漂移与验证
发现两处值得单独记录的源码/文档差异:
seed=Nonedocstring 与实际初始化行为不一致。RecorderTerm.record_pre_resetdocstring 仍沿用 mjlab 语义,声称看不到 post-action terminal observation;但当前NpEnv已先计算并保存 terminal observation,再进入 reset hook。这更像文档尚未同步,不宜直接判定为实现缺陷。验证结果:
最终状态:
All reactions