Domain Randomization Techniques in Microduck RL: A Complete Technical Guide
Microduck RL implements 10+ domain randomization techniques across physics parameters, sensor noise, and external disturbances to enable robust sim-to-real transfer for bipedal locomotion.
Domain randomization (DR) is the core strategy that allows policies trained in simulation to deploy successfully on Pollen Robotics' physical Microduck robot. According to the pollen-robotics/microduck_rl source code, these techniques are organized as event terms in environment configuration files, with granular toggles for each randomization type. This guide breaks down every major DR technique, its implementation, and how to configure it.
Physics-Based Domain Randomization Techniques
Center-of-Mass (CoM) Randomization
The most critical DR technique for balance control involves perturbing the robot's mass distribution. Microduck RL applies two levels of CoM randomization:
-
Trunk CoM randomization: Adds random offsets to the main body center of mass
- Configuration:
cfg.events["randomize_com"]inmicroduck_velocity_env_cfg.py - Range: ±0.003 m initially, ramped to ±0.015 m during curriculum
- Toggle:
ENABLE_COM_RANDOMIZATION
- Configuration:
-
Head-assembly CoM randomization: Applies separate offsets to each head body (
neck,head_pitch, etc.)- Configuration:
cfg.events["randomize_head_com"] - Range: ±0.003 m ramped to ±0.01 m
- Toggle:
ENABLE_HEAD_COM_RANDOMIZATION
- Configuration:
This captures manufacturing variations in the torso and head assemblies that significantly affect balance dynamics.
Mass and Inertia Randomization
The dr.pseudo_inertia helper scales both mass and inertia tensors simultaneously for selected bodies:
# From microduck_velocity_env_cfg.py
cfg.events["randomize_mass_inertia"] = EventTermCfg(
func=dr.pseudo_inertia,
mode="reset",
params={
"asset_cfg": SceneEntityCfg("robot", body_names=("trunk",)),
"operation": "scale",
"ranges": (0.95, 1.05), # ±5%
},
)
This preserves realistic mass-inertia relationships while introducing ±5% variation in trunk dynamics.
Joint Dynamics Randomization
Three complementary techniques randomize the actuator and transmission models:
| Technique | Implementation | Target Parameter | Range |
|---|---|---|---|
| Joint friction randomization | microduck_mdp.randomize_bam_friction |
BAM actuator friction budget | 0.9–1.1 |
| Joint damping randomization | microduck_mdp.randomize_dof_field_scaled |
dof_damping per joint |
0.9–1.1 |
| Armature randomization | dr.joint_armature |
Reflected rotor inertia | 0.9–1.1 |
The BAM friction randomizer (src/mjlab_microduck/actuator/friction_dr_bam.py) specifically handles the tendon-driven actuator model used in Microduck's legs, scaling the friction budget that accounts for cable routing losses.
Sensor and Observation Domain Randomization
IMU Orientation Randomization
Unlike typical approaches that add noise to IMU readings, Microduck RL applies constant random rotations to simulate IMU mounting errors:
- Implementation: Actor-side observation functions
base_ang_vel_imu_misalignedandprojected_gravity_imu_misaligned - Toggle:
ENABLE_IMU_ORIENTATION_RANDOMIZATIONinmicroduck_velocity_env_cfg.py - Range: Up to 6° misalignment in any direction
This is more realistic than additive noise because physical IMU mounting errors are fixed per-robot, not time-varying.
Encoder Bias Randomization
Each joint's position encoder receives a permanent offset for the entire episode:
# Event configuration
cfg.events["encoder_bias"] = EventTermCfg(
func=mdp.randomize_encoder_bias,
mode="reset",
params={
"asset_cfg": SceneEntityCfg("robot"),
"bias_range": ENCODER_BIAS_RANGE, # default ±0.015 rad
},
)
This forces policies to be robust to calibration errors without explicit state estimation.
Episode Initialization and External Disturbances
Base Orientation Randomization
Every episode starts with randomized initial tilt to improve recovery robustness:
- Configuration:
cfg.events["randomize_base_orientation"] - Range: ±10° pitch, ±5° roll
- Effect: Forces the policy to learn rapid stabilization from non-upright poses
Velocity Push Randomization
To simulate external disturbances and train recovery behaviors:
if ENABLE_VELOCITY_PUSHES:
cfg.events["push_robot"] = EventTermCfg(
func=mdp.push_by_setting_velocity,
mode="interval", # Triggers between pushes
interval_range_s=(3.0, 6.0), # Random 3-6 second intervals
params={
"velocity_range": {"x": (-0.3, 0.3), "y": (-0.3, 0.3)},
"asset_cfg": SceneEntityCfg("robot"),
},
)
The interval mode is crucial—unlike reset-time randomization, this injects disturbances mid-episode, forcing continuous disturbance rejection rather than just initial pose recovery.
Wheel Friction Randomization
For roller-equipped variants of Microduck, passive wheel friction is randomized:
- Configuration:
cfg.events["randomize_wheel_friction"]inmicroduck_velocity_rollers_env_cfg.py - Target: Backlash and roller contact friction in wheeled locomotion variants
- See also: Used in
microduck_spin_env_cfg.pyfor spinning maneuvers
Configuring Domain Randomization in Your Environment
Enabling and Disabling Techniques
Each DR component has a boolean toggle defined at the top of environment configuration files:
# In any microduck_*_env_cfg.py
ENABLE_COM_RANDOMIZATION = True # Trunk CoM drift
ENABLE_HEAD_COM_RANDOMIZATION = True # Head assembly mass distribution
ENABLE_MASS_INERTIA_RANDOMIZATION = True # Mass/inertia scaling
ENABLE_JOINT_FRICTION_RANDOMIZATION = True # BAM friction budget
ENABLE_JOINT_DAMPING_RANDOMIZATION = True # Joint viscous damping
ENABLE_ARMATURE_RANDOMIZATION = True # Rotor inertia reflection
ENABLE_IMU_ORIENTATION_RANDOMIZATION = True # IMU mounting error (~6°)
ENABLE_ENCODER_BIAS_RANDOMIZATION = True # Encoder zero-point offset
ENABLE_BASE_ORIENTATION_RANDOMIZATION = True # Initial tilt at reset
ENABLE_VELOCITY_PUSHES = True # External disturbance injection
ENABLE_WHEEL_FRICTION_RANDOMIZATION = False # Only for roller variants
Adding Custom Randomization Events
The mjlab.envs.mdp.dr module provides reusable randomizers. Example for body position offset:
from mjlab.envs.mdp import dr
from mjlab.envs.manager_based_rl import EventTermCfg, SceneEntityCfg
cfg.events["randomize_custom"] = EventTermCfg(
func=dr.body_ipos, # Position offset randomizer
mode="reset", # Apply at episode start
params={
"asset_cfg": SceneEntityCfg("robot", body_names=("custom_body",)),
"operation": "add", # Additive offset (vs. "scale")
"ranges": (-0.005, 0.005), # ±5 mm
},
)
Available dr.* helpers include: body_ipos, body_mass, body_inertia, joint_armature, joint_stiffness, joint_damping, joint_limits, joint_default_pos, and pseudo_inertia.
Key Implementation Files
| File | Purpose |
|---|---|
src/mjlab_microduck/tasks/microduck_velocity_env_cfg.py |
Primary velocity control environment; defines all DR toggles and default event configurations |
src/mjlab_microduck/tasks/microduck_velocity_rollers_env_cfg.py |
Roller variant with wheel friction randomization |
src/mjlab_microduck/tasks/microduck_standup_env_cfg.py |
Stand-up task reusing core DR components |
src/mjlab_microduck/tasks/microduck_spin_env_cfg.py |
Spin task with wheel-specific DR |
src/mjlab_microduck/tasks/mdp.py |
Custom randomization functions: randomize_bam_friction, randomize_dof_field_scaled, randomize_delayed_actuator_gains |
src/mjlab_microduck/actuator/friction_dr_bam.py |
BAM actuator friction model implementation |
Summary
- Center-of-mass randomization (trunk and head) handles manufacturing variations in mass distribution
- Joint dynamics randomization covers friction, damping, and reflected inertia across the transmission
- Sensor randomization uses constant offsets for IMU mounting and encoder bias rather than additive noise
- External disturbances via velocity pushes train active recovery behaviors at random intervals
- Modular toggles in environment configs allow precise control of which DR techniques are active
- Extensible design via
dr.*helpers andEventTermCfgenables rapid experimentation with new randomization strategies
Frequently Asked Questions
How does Microduck RL's domain randomization differ from Isaac Sim's built-in DR?
Microduck RL extends Isaac Sim's foundation with BAM-specific actuation models and sensor-level randomization that targets real hardware failure modes. The BAM friction randomizer in friction_dr_bam.py models tendon cable losses, while IMU orientation and encoder bias randomization simulate calibration errors rather than measurement noise—both critical for the physical Microduck robot.
Can I use different domain randomization ranges for training vs. evaluation?
Yes. The mode parameter in EventTermCfg controls when randomization applies: "reset" for episode starts, "interval" for mid-episode disturbances, or fixed curriculum ranges that expand during training. For evaluation, set ENABLE_*_RANDOMIZATION toggles to False or override ranges via params["ranges"] to test specific conditions.
What is the most important domain randomization technique for sim-to-real transfer?
According to the configuration defaults in microduck_velocity_env_cfg.py, center-of-mass randomization has the largest magnitude range (±15 mm trunk, ±10 mm head) and is enabled in all locomotion variants. Mass distribution errors most severely affect balance, making CoM randomization the highest-impact technique for bipedal walking transfer.
How do velocity pushes improve policy robustness compared to other disturbance methods?
Velocity pushes use mode="interval" rather than mode="reset", meaning they occur during running episodes rather than only at initialization. This forces continuous disturbance rejection rather than pose recovery, and the random 3–6 second interval prevents adaptation to predictable disturbance timing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →