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

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, 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).


# 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, 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, 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:

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:


# 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 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 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 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 (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, while the model‑level zeroing that disables native MuJoCo friction occurs in src/mjlab_microduck/tasks/mdp.py (lines 3220–3221) within the requires_model_fields decorator logic.

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 →