Problems with Control In MuJoCo?

December 05 2023

The Problem

I was experiencing a strange issue where my applied control inputs to a small object in MuJoCo resulted in zero motion. If you are experiencing issues then, you might like to hear about how I solved this problem.

The Setting

To better understand how I produced this error, then I will share some of the implementation details of the MuJoCo simulation I was running.

Python Script

        
# Get data for new model and save image of it
data = mujoco.MjData(test_model)
renderer = mujoco.Renderer(
    test_model, width=640, height=480,
)

mujoco.mj_forward(test_model, data)
renderer.update_scene(data)

media.show_image(renderer.render(), title="initial-condition")

# Simulate Forward With Control

frames = []
images = []
fig, ax = plt.subplots()
while data.time < duration:
    # Compute Controls
    # print("model.actuator('swing') = ", test_model.actuator('swing'))
    # print(dir(test_model.actuator('swing')))
    # print(test_model.actuator('swing'))
    # print(test_model.set_u([1.5,0.0]))
    #
    # print(data.act)
    # print(data.ctrl)

    data.ctrl[0] = 1.5


    # Step forward
    mujoco.mj_step(test_model, data)

    if len(frames) < data.time * framerate:
        renderer.update_scene(data)
        pixels = renderer.render()
        frames.append(pixels)
        
    

Code (MuJoCo File test.mjcf)

        
<mujoco>
    <option integrator="RK4"/>

    <compiler angle="degree" />

    <!-- Define external files/assets we would like to use in world -->
    <asset>
        <!-- Include STL File -->
        <mesh name="nut-mesh" file="./m5-nut-105.stl" />

        <!-- Other STL Files Go Here... -->

        <!-- Textures -->
        <texture
                name="grid" type="2d" builtin="checker" rgb1=".2 .4 .6"
                rgb2=".4 .6 .8" width="300" height="300"
        />

        <!-- Define materials which can be useful for visually distinguishing things -->
        <material name="nut-material" texrepeat="1 1" rgba="0 0 0.8 1" />
        <material name="grid" texture="grid" texrepeat="8 8" reflectance=".2"/>
    </asset>
    <worldbody>
        <geom name="ground_plane" size="200.0 200.0 .01" type="plane" material="grid"/>
        <!-- Bathe both sides with light -->
        <light pos="-20 0 50.0" dir="+1 0 -1 " />
        <light pos="+20 0 50.0" dir="-1 0 -1 " />

        <!-- Other Bodies Defined Here... -->

        <body name="nut" pos="0.0 0 10.0">
            <freejoint />
            <geom name="nut-geom" type="mesh" mesh="nut-mesh" material="nut-material"/>
        </body>
    </worldbody>

    <actuator>
        <motor gear="1" joint="swing" name="swing" forcerange="-100 100" />
        <motor gear="1" joint="motion-toward" name="toward" forcerange="-100 100" />
    </actuator>
</mujoco>
        
    

Some other notes

I tried multiple values of data.ctrl[0], between -100 and 100 with no visible impact on my object's trajectories. I also visited the MuJoCo documentation many times to observe whether I was missing a key field of data that needed to be set in order to enable control. There seemed to be nothing else that I needed to set.

The Root Cause

In fact, the issue was that the body's mass was so large that the small forces that I was choosing were miniscule in comparison.

In my fledgling understanding of MuJoCo, I had assumed that the mass of the object would somehow be something appropriate for the object that I had in mind (an m5 bolt). However, this is not necessarily the case.

My understanding now is that MuJoCo estimates the mass of the object based on the volume of the object (as defined in the STL file). This is a reasonable assumption, but it is not always correct. In my case, the m5 bolt was actually quite large (not sure if this is because the units of its dimensions were changed upon import into MuJoCo, or because I downloaded a model from the internet and the original designer did not choose realistic units). In any case, the mass of the object was estimated to be over 200,000 kg.

It's no wonder that nothing moved. Ha!

Solutions

The direct solution to this problem for this script is to either:

  • Reduce the mass of the object
  • Increase the control limits of the actuator

I chose to do the former, because I wanted to make the mass of this bolt more realistic. I set the mass to 1kg in the python script, as follows:

        
model.body("nut").mass = 1.0
model.body("nut").inertia = np.array([1.0, 1.0, 1.0])
        
    

This was done shortly after loading the model and resulted in proper actuation of the object.

In the future, I'll always remember to check the mass of the object before trying to control it. And, I may define all of the control boundaries in my python controllers to include the mass explicitly.