# How Joint Friction Is Randomized in Microduck RL Training

> Discover how Microduck RL randomizes joint friction during training using a BAM actuator friction-scale hook and curriculum-driven ranges that adapt over time.

- Repository: [Pollen Robotics/microduck_rl](https://github.com/pollen-robotics/microduck_rl)
- Tags: deep-dive
- Published: 2026-09-02

---

**Microduck RL randomizes joint friction through a dedicated BAM actuator friction-scale hook invoked on every environment reset, using curriculum-driven ranges that evolve throughout training.**

The `pollen-robotics/microduck_rl` repository implements domain randomization for a 14-DOF quadruped robot where standard MuJoCo friction parameters are bypassed in favor of a custom actuator model. This article examines the exact mechanism, source files, and configuration patterns that enable per-episode joint friction variation.

## Why Standard MuJoCo Friction Randomization Doesn't Apply

Microduck uses **BAM (Bam actuator model)**—voltage-controlled XL330 servo units modeled in [`src/mjlab_microduck/actuator/friction_dr_bam.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/actuator/friction_dr_bam.py). Under this model, MuJoCo's native `dof_frictionloss` field is **zeroed** because the actuator supplies its own velocity-independent friction internally.

This design choice makes the conventional `dr.dof_frictionloss` domain randomizer ineffective. Instead, friction variability must be injected through the actuator's proprietary `friction_scale` parameter.

## The Core Randomization Function: `randomize_bam_friction`

All joint friction randomization flows through a single function defined in [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py):

```python
def randomize_bam_friction(
    env: ManagerBasedRlEnv,
    asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG
):
    """Randomize the BAM actuator friction scale per-environment."""
    asset = env.scene[asset_cfg.name]
    
    # Sample scale factors from curriculum-provided range

    scale = torch.empty(env.num_envs, device=env.device).uniform_(
        env.curriculum["joint_friction"].low,
        env.curriculum["joint_friction"].high
    )
    
    # Apply to all servo actuators (14 joints, excluding passive wheels)

    for actuator in asset.spec.actuators:
        if actuator.name.startswith("servo_"):
            actuator.friction_scale[:] = scale

```

This function operates in **reset mode**, meaning it executes fresh randomization every time environments are reset—typically once per episode.

## Event Registration Pattern

Every Microduck task configuration registers the friction randomization event following a consistent pattern. In [`src/mjlab_microduck/tasks/microduck_velocity_env_cfg.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/microduck_velocity_env_cfg.py):

```python
cfg.events["randomize_joint_friction"] = EventTermCfg(
    func=microduck_mdp.randomize_bam_friction,
    mode="reset",  # Apply on every environment reset

)

```

The same registration appears across task families, including [`microduck_roller_standup_env_cfg.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/microduck_roller_standup_env_cfg.py) and other specialized environments. This ensures **consistent friction randomization behavior** regardless of the specific control objective.

## Curriculum-Driven Randomization Ranges

Microduck employs **progressive curriculum** to control how friction ranges evolve during training. The curriculum is defined per-environment:

```python
cfg.curriculum["joint_friction"] = CurriculumTermCfg(
    func=microduck_mdp.joint_friction_curriculum,
    params=dict(
        event_name="randomize_joint_friction",
        ranges_stages=[
            {"stage": 0, "ranges": (0.6, 1.0)},  # Wide initial variation

            {"stage": 1, "ranges": (0.8, 1.0)},  # Tighter as policy improves

            # Subsequent stages further narrow the distribution

        ],
    ),
)

```

The curriculum mechanism:
1. **Starts broad**—exposing the policy to extreme friction variations early
2. **Progressively tightens**—focusing on realistic, deployment-relevant ranges
3. **Prevents catastrophic forgetting**—maintaining sufficient variability for robustness

## Architectural Flow: From Reset to Physics

The end-to-end friction randomization pipeline operates as follows:

1. **Environment reset triggered**—episode termination or timeout
2. **Event manager queries registered events**—finds `randomize_joint_friction` with `mode="reset"`
3. **`randomize_bam_friction` invoked**—samples from current curriculum stage
4. **`friction_scale` written to actuators**—all 14 servo joints updated in-place
5. **MuJoCo step executes**—scaled friction contributes to velocity-independent damping term

This non-accumulative design (preserved by mjlab 1.3.0's `dr.*` operations reading compile-time defaults each step) ensures **consistent statistical distributions** across training without drift.

## Sim-to-Real Motivation

Joint friction dominates sim-to-real transfer gaps for small legged robots. Physical bearings exhibit:

- Manufacturing tolerance variations (±20% typical)
- Temperature-dependent viscosity changes
- Wear-induced degradation over operational lifetime

By randomizing `friction_scale` across 0.6–1.0× nominal during early training, Microduck policies learn **invariant control strategies** that generalize to the physical robot's unmodeled damping characteristics without requiring system identification.

## Summary

- **BAM actuators bypass MuJoCo `dof_frictionloss`**, requiring custom friction injection
- **`randomize_bam_friction`** in [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py) implements the core randomization logic
- **Event registration** in task configs (e.g., [`microduck_velocity_env_cfg.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/microduck_velocity_env_cfg.py)) triggers randomization on every reset
- **Curriculum terms** progressively tighten randomization ranges from wide initial bounds to deployment-realistic values
- **14 servo joints** receive correlated scale factors, excluding passive wheel actuators

## Frequently Asked Questions

### What is a BAM actuator and why does it handle friction differently?

BAM (Bam actuator model) is Microduck's voltage-controlled XL330 servo implementation that internalizes friction physics. Unlike standard MuJoCo joints where `dof_frictionloss` applies external damping, BAM actuators compute friction internally based on motor characteristics. This requires randomization through the actuator's `friction_scale` parameter rather than native MuJoCo domain randomization.

### How does the curriculum control friction randomization over time?

The curriculum defines staged ranges passed to `joint_friction_curriculum`. Early training stages specify wider intervals (e.g., 0.6–1.0× nominal friction) to force robust policy learning. Later stages contract to tighter bands (e.g., 0.9–1.0×) focusing policy capacity on precise control near expected physical parameters. Stage transitions occur based on training progress metrics.

### Can different joints receive independent friction values?

The current implementation applies **correlated randomization**—all 14 servo joints receive identical scale factors per environment. This reflects the physical reality that friction variations derive from shared environmental factors (temperature, lubrication) rather than independent per-joint manufacturing defects. The code structure in `randomize_bam_friction` permits per-joint modification by indexing `actuator.friction_scale` individually.

### Where is the friction scale physically applied in simulation?

The `friction_scale` value multiplies the velocity-independent friction term within `FrictionDRBamActuator.compute_torque()`, defined in [`src/mjlab_microduck/actuator/friction_dr_bam.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/actuator/friction_dr_bam.py). This scaled friction contributes additively to actuator output torque during each MuJoCo physics step, affecting joint dynamics without modifying the underlying `dof_frictionloss` parameter.