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"] in microduck_velocity_env_cfg.py
    • Range: ±0.003 m initially, ramped to ±0.015 m during curriculum
    • Toggle: ENABLE_COM_RANDOMIZATION
  • 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

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_misaligned and projected_gravity_imu_misaligned
  • Toggle: ENABLE_IMU_ORIENTATION_RANDOMIZATION in microduck_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:


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 and EventTermCfg enables 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →