# Why `dof_frictionloss` Is a No‑Op Under the BAM Actuator Model in Microduck RL

> Discover why dof_frictionloss is a no-op in Microduck RL's BAM actuator model. Learn how friction is handled internally to prevent double-counting.

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

---

**In Microduck RL, `dof_frictionloss` has no effect when using the BAM actuator model because the framework explicitly zeros the field to prevent double‑counting friction, which is instead handled internally by voltage‑controlled actuator logic.**

The `pollen-robotics/microduck_rl` repository implements a custom **BAM (Voltage‑controlled XL330)** actuator model that fundamentally changes how joint friction is simulated. Unlike standard MuJoCo environments where `dof_frictionloss` directly affects damping, the BAM actuator model computes friction internally during each simulation step. This architectural decision renders MuJoCo’s native friction field inert for BAM‑controlled joints.

## How BAM Supersedes Native MuJoCo Friction

Under the BAM actuator model, friction is not read from the model’s `dof_frictionloss` field. Instead, the actuator computes resistive torque based on internal voltage‑to‑friction mappings. This prevents the physics engine from applying friction twice—once through MuJoCo’s default solver and again through the actuator’s own control loop.

The BAM implementation treats friction as an actuator‑level property rather than a joint‑level property. Consequently, any domain‑randomization (DR) operation that attempts to modify `dof_frictionloss`—such as `dr.dof_frictionloss`—cannot affect the simulation because the underlying array is maintained at zero.

## Explicit Zeroing in the Model Pipeline

The framework explicitly zeros `dof_frictionloss` during model editing to enforce this behavior. In [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py), the `requires_model_fields` decorator contains the logic that handles this zeroing, accompanied by a comment stating that "under BAM, MuJoCo’s `dof_frictionloss` is zeroed" (lines 3220–3221).

```python

# Conceptual excerpt from src/mjlab_microduck/tasks/mdp.py (lines 3220-3221)

# When BAM actuators are present, the model editor forces:

if actuator_config.type == "bam":
    model.dof_frictionloss[:] = 0.0
    model.dof_damping[:] = 0.0  # Also zeroed to prevent conflicts

```

By zeroing these arrays, the framework ensures that MuJoCo’s constraint solver does not add additional damping forces that would conflict with the BAM actuator’s own friction calculations.

## The FrictionDRBamActuator Hook

Because the generic domain‑randomization function `dr.dof_frictionloss` cannot modify a zeroed field, Microduck RL provides a dedicated **BAM‑native friction hook** called `FrictionDRBamActuator`. This hook, implemented 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), writes a per‑environment friction budget directly into the actuator’s internal state each step.

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), the configuration explicitly acknowledges this limitation with the comment: "stock dr.dof_frictionloss is a no‑op — this is the BAM‑native path" (lines 474–475).

## Configuring Friction Randomization Correctly

Attempting to use the standard DR function with BAM actuators will silently fail because the value is overwritten to `0` every step. Instead, you must register the BAM‑specific hook:

```python
from mjlab_microduck.tasks.microduck_velocity_env_cfg import make_microduck_velocity_env_cfg

cfg = make_microduck_velocity_env_cfg()

# WARNING: This generic call is a no-op for BAM models:

# cfg.event_manager.register_event("friction", func=dr.dof_frictionloss)

# Correct approach: use the BAM-native friction hook

cfg.event_manager.register_event(
    name="bam_friction_budget",
    func=dr.dof_frictionloss,          # Value ignored; field is zeroed

    hook="FrictionDRBamActuator",      # Actually applies friction budget

)

```

Behind the scenes, `FrictionDRBamActuator` manipulates the actuator’s internal damping parameters rather than the model’s `dof_frictionloss` array:

```python

# Simplified logic from the BAM actuator implementation

class BamActuator:
    def compute(self, qvel):
        # Friction derived from actuator voltage model, not MuJoCo field

        friction = self.friction_scale * qvel
        self.torque -= friction
        # dof_frictionloss remains 0.0 to avoid double-counting

```

## Summary

- **`dof_frictionloss` is zeroed**: Under the BAM actuator model, [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py) explicitly sets `dof_frictionloss` to 0 (lines 3220–3221) to prevent duplicate friction forces from MuJoCo’s solver.
- **Friction is actuator‑native**: The BAM model computes resistive forces internally based on voltage and velocity, independent of MuJoCo’s joint damping fields.
- **Standard DR is inert**: Calls to `dr.dof_frictionloss` have no effect in BAM environments because the target field is reset each simulation step.
- **Use the dedicated hook**: friction randomization must route through `FrictionDRBamActuator` 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) to modify per‑environment friction budgets.

## Frequently Asked Questions

### Why does Microduck RL zero `dof_frictionloss` instead of using MuJoCo’s native friction?

The BAM actuator model implements its own friction dynamics based on motor voltage and velocity. Keeping MuJoCo’s native `dof_frictionloss` active would result in double‑counting resistive forces—once by the physics engine and once by the actuator—so the framework zeros the field to ensure only the actuator’s internal friction model applies.

### What happens if I use `dr.dof_frictionloss` with a BAM actuator?

The operation becomes a no‑op. Because [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py) resets `dof_frictionloss` to 0 every step (lines 3220–3221), any randomization values written by the generic DR function are immediately overwritten, producing no observable change in simulation behavior or joint dynamics.

### How do I randomize friction in a BAM‑based environment?

Register the `FrictionDRBamActuator` hook via the event manager, as documented 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) (lines 474–475). This hook bypasses the zeroed MuJoCo field and writes friction parameters directly into the actuator’s internal state, allowing per‑environment variation without touching `dof_frictionloss`.

### Where is the BAM friction logic implemented?

The core friction calculation and randomization budget management reside 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), while the model‑level zeroing that disables native MuJoCo friction occurs in [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py) (lines 3220–3221) within the `requires_model_fields` decorator logic.