Domain Randomization Contract¶
Domain randomization is an env-owner provider contract plus backend capability application. User configuration examples live in Domain Randomization.
Lifecycle Classes¶
Init lifecycle: changes model identity or geometry. These changes run during env/backend initialization, materialization, or cache construction.
Reset lifecycle: changes state or parameters within the same model identity. Providers dispatch a reset randomization payload through
ResetPlan.Interval lifecycle: applies perturbations between steps, such as push or body force plans.
Hot paths must not parse XML/assets or probe backend private methods with
getattr or hasattr.
Provider Minimum¶
A task that uses DR should define:
A task-owned domain-randomization config dataclass.
A
DomainRandomizationProvider.Reset behavior returning
ResetPlanstate and randomization payloads.Interval behavior through
IntervalRandomizationPlanwhen needed.Env construction that calls
self._init_domain_randomization(...).
Shared types live in unisim.dr.types (interval term descriptors in
unisim.dr.interval); both are re-exported from src/unilab/dr/__init__.py.
Manager behavior lives in src/unilab/dr/manager.py.
Backend Capability Boundary¶
Backend support is explicit. A reset or interval item only counts as a unified DR item when three pieces exist together:
ResetRandomizationPayloadhas an explicit field, orIntervalRandomizationPlan.opscarries anIntervalTermOpfor the term.The backend declares and implements the capability.
The task config/provider samples and dispatches that field or op.
MuJoCo and Motrix differences stay in backend capability declarations, backend implementations, and owner YAMLs.
Interval Term Descriptors¶
Interval plans are term-descriptor based: IntervalRandomizationPlan.ops
carries a tuple of IntervalTermOp entries (term name, NumPy payload,
optional body_ids) from unisim.dr.interval, re-exported through
unilab.dr.
Builtin term names are the
INTERVAL_TERM_*constants; their payload contracts are pinned byINTERVAL_TERM_SPECS(push: payload shape(3,), nobody_ids; the four body terms: payload shape(num_envs, len(body_ids), 3)with requiredbody_ids).IntervalTermOp.validate()enforces these contracts for builtin terms; unknown custom terms pass validation through untouched.Capability ownership stays with the backend:
DomainRandomizationCapabilities.supported_interval_termsis the authoritative declaration, queried viasupports_interval_term/get_unsupported_interval_terms.DomainRandomizationManager.apply_interval_randomization_if_dueis generic: it contains no term names and no per-term branches, so a backend-owned custom term needs no manager change. Terms missing from the capability set fail closed withNotImplementedErrornaming the backend type and the terms; on the backend side,SimBackend.apply_interval_randomizationroutes each op through its handler table and fails closed with the backend class and term name when no handler exists.Ops and plans must stay pickle-safe (protocol 4) across spawn-based collector processes: stdlib + NumPy frozen dataclasses only.
The legacy plan fields (
push_perturbation_limit,body_ids,body_linear_velocity_delta,body_angular_velocity_delta,body_force,body_torque) and the legacysupports_interval_*capability bools are deprecated:IntervalRandomizationPlan.iter_ops()still adapts set legacy fields into ops 1:1, and the bools remain as capability fallbacks. New providers should populateops; the legacy fields will be removed in the next unisim-core major release.
MuJoCo BatchEnvPool Snapshot¶
Current MuJoCo reset randomization uses BatchEnvPool.reset(..., randomization=...) with a fixed field whitelist. Indexed reads and writes are
available through get_field_indexed(...) and set_field_indexed(...). This
interface lives in the mujoco-uni-runtime package (mujoco_uni.batch_env), not in this
repository; the reset-term constants that map onto it are in
unisim.dr.types.
The supported reset fields and their per-env block shapes are below. The leading
dimension is always len(env_ids); the trailing block size is the field’s full
flat width in a single mjModel.
Field |
Per-env block shape |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Refresh behavior is fixed by the backend: body_mass, body_ipos,
body_iquat, body_inertia, and dof_armature trigger an mj_setConst
refresh after the write, while gravity, geom_friction, kp, and kd do
not.
Two caveats:
geom_sizeis not inSUPPORTED_FIELDS. Geometry size is expressed through init-lifecycle model materialization (seeGeomSizeOverride/ModelVariantSpecinunisim.dr.types), not reset randomization.gravityreset randomization requires amujoco-uni-runtimebuild that ships it. This repository depends on the officialmujocopackage (>=3.5, with the default version pinned byuv.lock) plusmujoco-uni-runtime, whoseSUPPORTED_FIELDSincludesgravity; older batch-env packages such asmujoco-uni==3.6.0.post6do not.
Motor Control Extension¶
Motor-actuator tasks that do not map policy output directly to backend position
actuators should keep conversion in the env owner layer. Register a pre-step
callback through SimBackend.set_pre_step_control(...); the backend calls it
before physics substeps and refreshes sensors after stepping.
Go2W is the current all-motor actuator example: its env owner combines leg position targets and wheel torque, while kp/kd randomization stays in the env owner cache rather than leaking MuJoCo position-actuator mechanics into shared payloads.
Evidence In Repo¶
DR types:
unisim.dr.typesandunisim.dr.interval, re-exported bysrc/unilab/dr/__init__.pyDR manager:
src/unilab/dr/manager.pyBackend interface:
unisim.backend.baseProvider interface:
src/unilab/dr/provider.py