MuJoCo essentials

MuJoCo turns an XML description into numbers, then moves those numbers forward in time. Most surprises come from the first half: the compiler fills in whatever you leave out, and it never complains about a model that is valid but not what you meant. This module covers what the compiler decides, what one step computes, and the handful of conventions that catch everyone once.

4 formulas, 8 code rules · 38/38 checks pass · 19 sources · fact-checked and reviewed

Key ideas

  1. The model is the description, the data is the state

    mjModel holds everything the compiler produced (bodies, joints, masses, limits, options) and is meant to stay constant while you simulate. mjData holds the state and every intermediate result, and belongs to the model it was made from. One model can drive many mjData, which is how MuJoCo runs simulations side by side. mj_loadXML returns null if the model fails to load, with the reason in the buffer you pass it. The documentation says a warning also returns null, but in 3.17.0 a compiler warning returns the model and leaves the warning in that buffer, so check both (F5.12).[1][2][3]

  2. The compiler fills in what you leave out

    Angles in MJCF are degrees unless the model says otherwise, and mjModel always holds radians (F5.08). A body with no mass given gets one from its geoms' volume at a density of 1000 kg/m³ (F5.09): the running example's bob, given only its size, would weigh 24.4 g, as if made of water, instead of 200 g. Neither is an error, so read back what was stored (F5.11).[1][4][5]

  3. One step is forward dynamics, then integration

    mj_step computes the accelerations for the current state, then advances time by one timestep (F5.05). mj_forward does the first half alone: it brings every derived quantity up to date without moving time on. Call it after you set positions or velocities by hand and before you read anything computed from them.[2]

  4. Positions and velocities can differ in size

    A hinge has one position and one velocity, but a ball joint stores its orientation as a unit quaternion (four numbers) and moves with three angular velocities; a free joint has seven and six (F5.07). Find a joint's entries with jnt_qposadr and jnt_dofadr, and find the joint itself by name with mj_name2id, so that adding a joint elsewhere does not shift your indices.[5][6]

  5. Energy is off by default, and with Euler one step behind

    d->energy stays zero unless the energy flag is on or the model has an energy sensor. With the default Euler integrator, and the other single-step ones, its value after mj_step describes the state at the start of that step, because it is computed inside forward dynamics, before the integrator moves on. With RK4 it comes from the last of the four intermediate evaluations, close to the new state; the documentation says RK4 computes energy only once per step, but in 3.17.0 it is recomputed at every stage (F5.06). Either way, call mj_forward for the state you hold. Potential energy is zero when the centre of mass lies on the horizontal plane through the world origin, not at the bottom of the swing (F5.01), so it can differ from a hand calculation by a constant, as module 06 shows.[2][4][7][8][9]

  6. Armature is inertia the joint carries and the bodies do not show

    A rotor spinning NN times faster than the joint it drives adds N2IrotorN^2 I_{\text{rotor}} to that joint's inertia (F5.03). MuJoCo adds it straight onto the mass matrix. For a joint driven by one actuator, there are two places to put it: the joint's armature, which takes the reflected value, or the actuator's, which takes the rotor's own and multiplies by the actuator's gear squared, so only when that gear is the mechanical ratio. Tendons have an armature of their own. In the check behind this page, a joint armature of 10−410^{-4} kg·m² raises the pendulum's mass matrix by that amount, to within rounding. Use one place, not both, or the rotor is counted twice. The factor N2N^2 grows quickly: behind a 100:1 gearbox, the rotor counts ten thousand times over.[4][10]

  7. Same state, same step, same result

    MuJoCo is deterministic: save the full integration state, restore it, and the next step is bit-for-bit identical (F5.10). The guarantee holds for a single MuJoCo version on the same architecture. A Mac and a Linux CI machine can drift apart, and a small difference can grow as the simulation runs, quickly in models with contact.[2][11]

Formulas and rules

The same entries as the formula sheet. Open an entry's details for its symbols and the checks behind it.

  1. MuJoCo potential energy (gravity part)

    Zero is where the centre of mass lies on the horizontal plane through the world origin, not the bottom of the swing. Passive springs (joint, tendon, flex) add their stored energy; constraint energy is not included.

    VMuJoCo=−∑bmb g⋅xbV_{\text{MuJoCo}} = -\sum_b m_b\, \mathbf{g}\cdot\mathbf{x}_b
    F5.01✓ 1/1 checked[4][9]
    Symbols and checks
    SymbolMeaningUnit
    mbm_{b}mass of body bkg
    gggravity vector (default 0 0 −9.81)m/s²
    xbx_{b}centre of mass of body bm
    • ✓ MuJoCo PE + m g d vs hand PE (worst, J)expected 0 · got 7.63278×10⁻¹⁷ · absolute error 7.6×10⁻¹⁷MuJoCo's zero height is the pivot; the constant offset is m g d
  2. MuJoCo kinetic energy

    T=12 vTM(q) vT = \tfrac12\,\mathbf{v}^{\mathsf T} M(\mathbf{q})\,\mathbf{v}
    F5.02✓ 1/1 checked[4][10][12]
    Symbols and checks
    SymbolMeaningUnit
    vvgeneralised velocityrad/s or m/s
    MMmass matrixkg·m² or kg
    • ✓ MuJoCo KE vs 1/2 I thetadot^2 (worst, J)expected 0 · got 5.55112×10⁻¹⁷ · absolute error 5.6×10⁻¹⁷
  3. Reflected (armature) inertia through a gear

    Direct drive is N = 1, so the rotor's own inertia adds to the joint. Enter it either as the joint's armature (the reflected value) or as the actuator's armature (the rotor's own, which MuJoCo scales by the actuator's gear squared, so only when gear is the mechanical ratio), not both, or it is counted twice.

    Ireflected=N2 IrotorI_{\text{reflected}} = N^2\, I_{\text{rotor}}
    F5.03✓ 3/3 checked[4][10]
    Symbols and checks
    SymbolMeaningUnit
    NNgear ratio (motor turns per joint turn)dimensionless
    IrotorI_{\text{rotor}}rotor inertiakg·m²
    • ✓ joint armature 1e-4: mass matrix = I + 1e-4 (kg m^2)expected 0.0059208 · got 0.0059208 · relative error 1.5×10⁻¹⁶
    • ✓ actuator armature 1e-6 at gear 10: mass matrix = I + 10^2 x 1e-6expected 0.0059208 · got 0.0059208 · relative error 1.5×10⁻¹⁶the rotor's own inertia, reflected by the actuator's gear
    • ✓ both set: the two add (counted twice if they describe one rotor)expected 0.0060208 · got 0.0060208 · relative error 1.4×10⁻¹⁶
  4. Constraint time constant floor

    The docs say timeconst should be at least 2h. In the standard (timeconst, dampratio) format, MuJoCo raises a smaller value to 2h unless the refsafe flag is disabled; the discrete integrator applies a different safeguard instead.

    timeconst≥2h\text{timeconst} \ge 2h
    Symbols
    SymbolMeaningUnit
    timeconsttimeconstsolref time constants
    hhtimesteps
  5. The simulation loop

    mj_forward computes everything for the current state without advancing time. Splitting a step into mj_step1 and mj_step2 reproduces mj_step only with the single-step integrators (Euler, implicit, implicitfast, discrete). With RK4, mj_step2 quietly integrates with Euler; set controls in a callback instead.

    while (d->time < t_end) {
      mj_step(m, d);     // forward dynamics at d->time, then integrate one step
    }
    // controls that need this step's intermediate results (single-step integrators only):
    mj_step1(m, d);  /* set d->ctrl */  mj_step2(m, d);
    F5.05✓ 4/4 checked[2]
    Symbols and checks
    • ✓ mj_step vs mj_step1 + mj_step2, 500 steps, Euler, implicit, implicitfast, discrete (largest gap, rad)expected 0 · got 0 · absolute error 0
    • ✓ RK4: mj_step1 + mj_step2 differs from mj_stepobserved as statedmj_step2 integrates with Euler when the model uses RK4
    • ✓ mj_forward leaves the time unchanged (s)expected 0 · got 0 · absolute error 0
    • ✓ the sheet's C++ statements compile (-Wconversion -Wsign-conversion -Werror)expected 0 · got 0 · absolute error 0F5.05, F5.06, F5.10, F5.11, F5.12; F5.07 lists fields rather than statements
  6. Energy is off by default, and with Euler one step behind

    With the single-step integrators (Euler, implicit, implicitfast, discrete), energy is computed inside forward dynamics, before the integrator moves the state on. With RK4 it comes from the last intermediate stage, close to the new state but not exactly it. An energy sensor in the model also turns the computation on. Potential energy covers gravity and passive springs; constraint energy is not included.

    // in the model: <option><flag energy="enable"/></option>
    mj_step(m, d);     // single-step integrators: d->energy describes the state at the start of this step
    mj_forward(m, d);  // recompute: d->energy now describes the state you hold
    F5.06✓ 6/6 checked[2][4][7]
    Symbols and checks
    • ✓ energy flag off (default): energy stays zero (J)expected 0 · got 0 · absolute error 0
    • ✓ after mj_step, energy equals the pre-step state's: Euler, implicit, implicitfast, discrete (largest gap, J)expected 0 · got 0 · absolute error 0
    • ✓ and differs from the post-step state'sobserved as statedcall mj_forward after mj_step to get the energy of the new state
    • ✓ RK4: energy after mj_step equals the energy at its last intermediate state (J)expected 0 · got 0 · absolute error 0gap to the old state 5.7e-04 J, to the new state 6.2e-09 J; the docs say energy is computed once per RK4 step
    • ✓ flag off, but an energy sensor in the model: energy is computedobserved as stated
    • ✓ the sheet's C++ statements compile (-Wconversion -Wsign-conversion -Werror)expected 0 · got 0 · absolute error 0F5.05, F5.06, F5.10, F5.11, F5.12; F5.07 lists fields rather than statements
  7. Positions and velocities can differ in size

    Rotations are stored as unit quaternions (four numbers) but move with three angular velocities, so nq ≥ nv. Index qpos with jnt_qposadr and qvel with jnt_dofadr, never with the same index.

    m->nq  // dim(qpos): hinge 1, slide 1, ball 4, free 7 (position + unit quaternion)
    m->nv  // dim(qvel): hinge 1, slide 1, ball 3, free 6
    F5.07✓ 3/3 checked[4][6]
    Symbols and checks
    • ✓ hinge: nq = nv = 1observed as stated
    • ✓ free joint: nq = 7, nv = 6observed as statedposition plus unit quaternion; three linear plus three angular velocities
    • ✓ ball joint: nq = 4, nv = 3observed as stated
  8. MJCF angles are degrees unless you say otherwise

    The default for MJCF is degree (URDF is always radian). The compiler converts to radians, and mjModel and mjData always hold radians. A range written in radians under the default is read as degrees, silently.

    <compiler angle="radian"/>  <!-- or write every angle attribute in degrees -->
    F5.08✓ 5/5 checked[4]
    Symbols and checks
    • ✓ range="-90 90" with the default compiler: stored upper limit (rad)expected 1.5708 · got 1.5708 · relative error 0
    • ✓ the same with angle="radian": stored upper limit (rad)expected 90 · got 90 · absolute error 0read as 90 radians: no error, a wrong model
    • ✓ as written: 1.5708 read as degrees, stored upper limit (rad)expected 0.0274156 · got 0.0274156 · relative error 0the joint is limited (autolimits) to about 1.57 degrees
    • ✓ as written: the joint is limitedobserved as stated
    • ✓ fixed (angle="radian"): stored upper limit (rad)expected 1.5708 · got 1.5708 · relative error 0
  9. Mass and inertia come from the geoms unless you give them

    With inertiafromgeom="auto" (the default), a body without an inertial element gets its mass and inertia from its geoms, each geom from its mass or, failing that, its volume times density (default 1000). Units are whatever you use consistently; the defaults assume metres, kilograms and seconds.

    <geom type="sphere" size="0.018" mass="0.2"/>   <!-- explicit: use this -->
    <geom type="sphere" size="0.018"/>              <!-- density 1000: water -->
    F5.09✓ 3/3 checked[1][4]
    Symbols and checks
    • ✓ sphere with no mass: body mass = 1000 x volume (kg)expected 0.024429 · got 0.024429 · relative error 1.4×10⁻¹⁶the running example's 18 mm bob would weigh this, not 200 g
    • ✓ as written: link mass from density 1000 (kg)expected 0.024429 · got 0.024429 · relative error 1.4×10⁻¹⁶
    • ✓ fixed (mass on the geom): link mass (kg)expected 0.2 · got 0.2 · relative error 0
  10. Same state, same step, same result

    The integration state is everything the next step reads. It includes time, positions, velocities, actuator activations, history buffers and plugin state; the user inputs (controls, applied forces, equality-constraint toggles, mocap poses, userdata); and the solver warm start. Restore all of it and the result is bit-for-bit identical within one MuJoCo version on the same architecture; restore less, and small differences can grow during integration, quickly in models with contact.

    std::vector<mjtNum> s(static_cast<std::size_t>(mj_stateSize(m, mjSTATE_INTEGRATION)));
    mj_getState(m, d, s.data(), mjSTATE_INTEGRATION);  // save everything the next step reads
    // ... later ...
    mj_setState(m, d, s.data(), mjSTATE_INTEGRATION);  // restore: the next mj_step is identical
    F5.10✓ 3/3 checked[2][11]
    Symbols and checks
    • ✓ two runs of 2000 steps: bit-identical stateobserved as stated
    • ✓ save the integration state, run, restore, rerun: bit-identicalobserved as stated
    • ✓ the sheet's C++ statements compile (-Wconversion -Wsign-conversion -Werror)expected 0 · got 0 · absolute error 0F5.05, F5.06, F5.10, F5.11, F5.12; F5.07 lists fields rather than statements
  11. Read back what the compiler stored

    The text lists each array under its mjModel name (jnt_range, body_mass, body_inertia, ...), so search for the field you meant to set. mj_printModel does the same with a fixed format of two significant figures ("% -9.2g"); mj_printData prints mjData. Slide-joint limits are lengths, not angles.

    mj_printFormattedModel(m, "model.txt", "%.6g");  // every field of mjModel, after compilation
    // or read one field directly: m->jnt_range[2 * j + 1] is joint j's upper limit (radians for a hinge)
    F5.11✓ 9/9 checked[1][5][6][15]
    Symbols and checks
    • ✓ the page's geom is the running example's bob (radius, depth)observed as stated
    • ✓ as written: 1.5708 read as degrees, stored upper limit (rad)expected 0.0274156 · got 0.0274156 · relative error 0the joint is limited (autolimits) to about 1.57 degrees
    • ✓ as written: link mass from density 1000 (kg)expected 0.024429 · got 0.024429 · relative error 1.4×10⁻¹⁶
    • ✓ mj_printFormattedModel shows jnt_range and body_mass as storedobserved as statedprinted with %.6g, so agreeing to six figures
    • ✓ the page's test compiles against libmujoco 3.17.0 (-Wall -Wextra -Wconversion -Werror)expected 0 · got 0 · absolute error 0
    • ✓ the sheet's C++ statements compile (-Wconversion -Wsign-conversion -Werror)expected 0 · got 0 · absolute error 0F5.05, F5.06, F5.10, F5.11, F5.12; F5.07 lists fields rather than statements
    • ✓ as written: the test fails, on the limit and on the massobserved as stated
    • ✓ C++ reads the same stored limit as Python (rad)expected 0.0274156 · got 0.0274156 · relative error 0gtest prints the value it found
    • ✓ fixed: the test passes (exit code)expected 0 · got 0 · absolute error 0
  12. What the loader's return value means

    Observed with MuJoCo 3.17.0. The Programming chapter says the loaders return null on an error or a warning; for a compiler warning, mj_loadXML in fact returns the model and leaves the warning in the buffer. Check the buffer as well as the pointer. ModelPtr is module 03's owning pointer.

    std::array<char, 1000> msg{};
    ModelPtr m(mj_loadXML(path, nullptr, msg.data(), static_cast<int>(msg.size())), &mj_deleteModel);
    if (!m) { /* the model failed to load; msg says why */ }
    else if (msg[0] != '\0') { /* it loaded, with a compiler warning in msg */ }
    F5.12✓ 6/6 checked[2][3]
    Symbols and checks
    • ✓ the sheet's C++ statements compile (-Wconversion -Wsign-conversion -Werror)expected 0 · got 0 · absolute error 0F5.05, F5.06, F5.10, F5.11, F5.12; F5.07 lists fields rather than statements
    • ✓ clean model: pointer returned, message emptyobserved as stated
    • ✓ compiler warning: pointer still returned, warning in the messageobserved as statedthe Programming chapter says null on an error or a warning; for this warning, it is not null
    • ✓ error: null pointer, reason in the messageobserved as stated
    • ✓ as written: it loads with an empty message, so no compiler warning eitherobserved as statedthe test's EXPECT_STREQ on the message buffer passes
    • ✓ fixed model plus a compiler warning: the test fails, on the message aloneobserved as statedso the empty-message check can fail

Worked example: a model that loads without complaint, and is wrong

One link with the running example's bob on it, written by someone who meant a joint limit of ±90° and a 200 g bob. Both XML models and the test below were compiled and run by tools/robotics/verify.py exactly as printed here, against MuJoCo 3.17.0.

  1. The model as written

    The limit is written in radians, the bob is given a size and no mass. Nothing here is invalid.

    <mujoco>
      <worldbody>
        <body name="link">
          <joint name="hinge" type="hinge" axis="0 1 0" range="-1.5708 1.5708"/>
          <geom type="sphere" size="0.018" pos="0 0 -0.168"/>
        </body>
      </worldbody>
    </mujoco>
  2. It loads

    No error, and an empty message buffer, so no compiler warning either; the test in step 5 checks both (F5.12). The compiler read the range in its default unit, degrees, and worked the bob's mass out from its volume (F5.08, F5.09).[3][4][8]

  3. Look at what was stored

    mj_printFormattedModel writes every field of the compiled model to a text file, at the precision you ask for (F5.11). Search it for the fields you meant to set.[8]

    Meant versus stored, read back from the compiled model
    FieldMeantStored
    jnt_range, upper limitπ/2\pi/2 rad (90°)0.0274 rad (1.57°)
    body_mass, the link200 g24.4 g
  4. Say what you mean

    Two changes: declare the unit the file is written in, and give the bob its mass. With a mass on the geom, density is no longer used.

    <mujoco>
      <compiler angle="radian"/>
      <worldbody>
        <body name="link">
          <joint name="hinge" type="hinge" axis="0 1 0" range="-1.5708 1.5708"/>
          <geom type="sphere" size="0.018" pos="0 0 -0.168" mass="0.2"/>
        </body>
      </worldbody>
    </mujoco>
  5. Make it a test

    Compare the compiled numbers with values from outside the file, so the test can fail (F4.07). An empty message buffer means no compiler warning (F5.12). Joints and bodies are found by name, and the model is owned from the line that loads it, as in module 03. The tolerance on the limit matches the five figures written in the file. (The includes and the kModelPath constant are left out.)

    TEST(PendulumModel, CompilesToTheNumbersWeMeant) {
      std::array<char, 1000> error{};
      std::unique_ptr<mjModel, decltype(&mj_deleteModel)> m(
          mj_loadXML(kModelPath, nullptr, error.data(), static_cast<int>(error.size())),
          &mj_deleteModel);
      ASSERT_NE(m, nullptr) << error.data();
      EXPECT_STREQ(error.data(), "") << "loaded with a warning";
      const int joint = mj_name2id(m.get(), mjOBJ_JOINT, "hinge");
      const int body = mj_name2id(m.get(), mjOBJ_BODY, "link");
      ASSERT_GE(joint, 0);
      ASSERT_GE(body, 0);
      EXPECT_NEAR(m->jnt_range[2 * joint + 1], std::numbers::pi / 2, 1e-4);  // +90 degrees
      EXPECT_NEAR(m->body_mass[body], 0.2, 1e-12);                           // kg
    }
  6. What the test said

    Run against each model. Both load without a compiler warning, and a third run, on the fixed model with a deprecated attribute added, shows that the message check fails when one appears. On the model as written, both comparisons fail, each with the value it found; on the fixed model, the test passes.[8]

    The same test, run against both models
    ModelNo compiler warningLimit checkMass checkTest
    As writtenpassesfailsfailsfails
    Fixedpassespassespassespasses
    Fixed, plus a warningfailspassespassesfails

A model that loads is only a model that parses. Check the numbers it compiled to against numbers you worked out elsewhere, once by eye and then in a test that stays.

Best practice

Questions

Why is qpos longer than qvel?
Because of rotations. A quaternion uses four numbers for an orientation that has three degrees of freedom, so ball and free joints have one more position than velocity (F5.07). A model of hinges only has nq equal to nv, which is why the mismatch tends to surface late.[6]
My joint limit barely lets the joint move. What happened?
Almost certainly a range written in radians in a file the compiler reads in degrees: ±1.5708 becomes ±1.5708°. The reverse mistake, degrees under angle="radian", gives a limit of 90 radians, more than fourteen turns away (F5.08).[4][8]
d->energy is always zero. Is the model broken?
No; energy is computed only when the model asks for it, with <flag energy="enable"/> inside <option> or with an energy sensor (F5.06).[4][8]
Will the same simulation give the same numbers on my Mac and in CI?
Not necessarily. Exact reproducibility is promised only within one MuJoCo version on one architecture, and an Apple-silicon Mac and an x86 Linux runner are different architectures. Compare across machines with a tolerance chosen for the purpose, and keep bit-for-bit comparisons to runs on the same machine.[11]
Python or C++?
The same library either way. The Python bindings follow the C API, with functions of the same names and fields that read MuJoCo's own memory: m.jnt_range in Python is m->jnt_range in C, shaped as two columns. The check behind this page reads the same joint limit both ways. Because Python fields are views, mj_step changes them in place; copy a value you want to keep. Python is quicker for exploring and plotting; the control loop in this project is C++.[8][16]
How much armature should I give a joint?
The rotor inertia from the motor's data sheet times the gear ratio squared, on the joint; or the rotor inertia alone on the actuator, which applies the gear for you, provided the actuator's gear is the mechanical ratio and not also a torque constant (F5.03). With no motor chosen yet, note that the model is incomplete rather than invent a number. MuJoCo's documentation adds that a small positive armature can noticeably improve stability; if you add one for that reason, say so in the model.[4][10]

Watch

Read

Sources

Numbered in order of first use on this page. read: checked against the claim. listed: exists and covers the topic, wording not checked. derived: worked out here and checked numerically.

  1. [1]

    MuJoCo Overview (3.17.0) (opens in a new tab)

    Google DeepMind

    Separation of model and data; Examples (the text version of a compiled model); Clarifications: Units are unspecified

    mjModel holds the model description and is expected to remain constant; mjData holds all dynamic variables and intermediate results and is constructed given an mjModel, so one model can run many states. Saved as text, a compiled model shows all of mjModel, revealing what the compiler did. MuJoCo does not fix physical units; any consistent system works, but the defaults (gravity 9.81, density 1000) assume MKS. Read from the 3.17.0 source of the docs.

    read
  2. [2]

    MuJoCo Programming chapter, Simulation (3.17.0) (opens in a new tab)

    Google DeepMind

    Initialization; Simulation loop (mj_step, mj_step1, mj_step2); State and control (integration state)

    mjModel and mjData are allocated by API functions; the load functions return NULL on an error or a warning, and the XML loader writes the reason into the buffer passed to it; mjData is specific to its mjModel. mj_step computes forward dynamics at the current time, then integrates; splitting it into mj_step1 and mj_step2 works only with the single-step integrators. The integration state is everything forward dynamics reads. Read from the 3.17.0 source of the docs.

    read
  3. [3]

    mj_loadXML, src/xml/xml_api.cc (MuJoCo 3.17.0) (opens in a new tab)

    Google DeepMind, GitHub

    On a compile warning, mj_loadXML copies the warning text into the caller’s error buffer and still returns the model; on success with no warning it clears the buffer.

    read
  4. [4]

    MuJoCo XML Reference (3.17.0) (opens in a new tab)

    Google DeepMind

    option-timestep (default 0.002 s; RK4 costs about four times per step); option-gravity (default 0 0 −9.81); option-integrator; option-flag-energy; option-flag-eulerdamp (implicit joint damping in Euler, on by default); body-joint-armature (reflected inertia, gear ratio squared; positive armature improves stability); actuator-general-armature (rotor inertia, scaled by gear squared); body-joint-damping (Euler treats joint damping implicitly); body-joint-frictionloss; compiler-inertiafromgeom; body-geom-density (default 1000 kg/m³, used only when mass is not given)

    MuJoCo defaults, energy definitions, armature, damping and friction-loss behaviour. Read from the 3.17.0 source of the docs (doc/XMLreference.rst).

    read
  5. [5]

    MuJoCo API Reference, Functions (3.17.0) (opens in a new tab)

    Google DeepMind

    mj_printModel, mj_printFormattedModel, mj_printData; mj_name2id

    mj_printModel prints the model to a text file, and mj_printFormattedModel does so with a printf-style float format; mj_name2id returns the id of the named object of a given type, or −1 if there is none. Read from the 3.17.0 source of the docs and include/mujoco/mujoco.h.

    read
  6. [6]

    include/mujoco/mjmodel.h and mjdata.h (MuJoCo 3.17.0) (opens in a new tab)

    Google DeepMind, GitHub

    nq: number of generalized coordinates = dim(qpos); nv: degrees of freedom = dim(qvel); jnt_qposadr, jnt_dofadr

    The sizes of qpos and qvel and the per-joint start addresses into each, as declared in the public headers.

    read
  7. [7]

    mj_RungeKutta and mj_forwardSkip, src/engine/engine_forward.c (MuJoCo 3.17.0) (opens in a new tab)

    Google DeepMind, GitHub

    RK4 evaluates forward dynamics at each intermediate stage with skipsensor set and a comment saying energy is not recomputed, but mj_forwardSkip recomputes energy whenever its own flags are clear, so energy is recomputed at every stage; the last evaluation is at the final stage. The Computation chapter says the optional computations run only once.

    read
  8. [8]

    Compiled and run checks for module 05 (tools/robotics/verify.py)

    Module 05’s checks in tools/robotics/verify.py: MuJoCo 3.17.0 behaviour (the step loop and its split under every integrator, energy timing including RK4’s stages and energy sensors, joint sizes, angle units, default density, joint and actuator armature, determinism); the worked example’s two XML models compiled and read back field by field and with mj_printFormattedModel; its GoogleTest, extracted from the page, compiled against the C library from the same release and run against both models and a warning case; mj_loadXML probed with a clean model, one with a compiler warning and one that fails; and five of the sheet’s six C++ entries (F5.05, F5.06, F5.10–F5.12) compiled with -Wconversion -Wsign-conversion -Werror.

    derived
  9. [9]

    mj_energyPos, src/engine/engine_sensor.c (MuJoCo 3.17.0) (opens in a new tab)

    Google DeepMind, GitHub

    Potential energy = −Σ m_b g·x_b (body centres of mass), so zero height is the world origin.

    read
  10. [10]

    Modern Robotics — Mechanics, Planning, and Control (opens in a new tab)

    Kevin M. Lynch, Frank C. Park · Cambridge University Press, 2017. Free preprint for personal use at the book site.

    §8.1.2 General Formulation (p. 275), §8.1.3 Understanding the Mass Matrix (p. 277), §8.9.2 Apparent Inertia (p. 311), §8.9.4 Friction (p. 315)

    Manipulator dynamics M(θ)θ̈ + c(θ,θ̇) + g(θ) = τ, kinetic energy ½θ̇ᵀMθ̇, apparent (reflected) inertia through gearing, friction models. Section titles and pages confirmed from the preprint's table of contents.

    listed
  11. [11]

    MuJoCo Computation chapter, Numerical integration (3.17.0) (opens in a new tab)

    Google DeepMind

    explicit, semi-implicit and implicit-in-velocity Euler; D matrix; implicitfast drops Coriolis and centripetal terms; discrete integrator; §Friction loss (dry friction modelled as a constraint whose effect is to drive the joint velocity to zero); §Reproducibility; §Numerical integration, RK4 and the tip “Choosing timestep and integrator”

    The exact update equations of every MuJoCo integrator, and how friction loss is modelled. The docs recommend implicitfast for most models (similar cost to Euler, more stable), keep Euler for compatibility, and call RK4 best for energy-conserving or nearly conserving systems; they report RK4 qualitatively better than single-step methods on an energy-conserving example even at a quarter of the timestep, and single-step methods with implicit velocity-dependent forces significantly more stable than RK4 when those forces are large.

    read
  12. [12]

    University Physics Volume 1, §10.4 Moment of Inertia and Rotational Kinetic Energy (opens in a new tab)

    William Moebs, Samuel J. Ling, Jeff Sanny · OpenStax (Rice University), 2016. CC BY 4.0

    Eq. 10.18 (K = ½Iω²); Fig. 10.20 (table of common shapes, an image)

    Rotational kinetic energy. The shape table is an image, so the sphere and cylinder values are also cited from other sources on this page.

    read
  13. [13]

    MuJoCo Modeling chapter, solver parameters and friction (3.17.0) (opens in a new tab)

    Google DeepMind

    Solver parameters: solref default "0.02 1" (time constant, damping ratio 1: critically damped); timeconst at least twice the timestep (enforced unless refsafe is off); Restitution (zero damping in the direct format); friction loss is first-order, purely damped, no exact stick (slow slippage)

    Constraints are soft mass-spring-dampers, critically damped by default; restitution needs the direct format with zero damping, and even then energy only fluctuates around its initial value. Why simulated friction loss creeps instead of sticking.

    read
  14. [14]

    src/engine/engine_core_constraint.c (MuJoCo 3.17.0) (opens in a new tab)

    Google DeepMind, GitHub

    solref safety: solref[0] raised to 2·timestep unless refsafe is disabled (standard format, non-discrete integrators)

    With refsafe enabled and solref in the standard format, a time constant below twice the timestep is raised to it, except under the discrete integrator, which uses a different safeguard.

    read
  15. [15]

    src/engine/engine_print.c (MuJoCo 3.17.0) (opens in a new tab)

    Google DeepMind, GitHub

    mj_printModel uses the float format "% -9.2g", two significant figures; mj_printFormattedModel takes the format as an argument.

    read
  16. [16]

    MuJoCo Python bindings (3.17.0) (opens in a new tab)

    Google DeepMind

    introduction; Structs; Functions

    The Python API is consistent with the underlying C API; struct fields give access to MuJoCo’s raw memory without copying, so mj_step changes them in place and values to be kept must be copied; MuJoCo functions keep their C names. Read from the 3.17.0 source of the docs.

    read
  17. [17]

    Modern Robotics, Chapter 8.1.3: Understanding the Mass Matrix (opens in a new tab)

    Kevin M. Lynch · Northwestern University, Modern Robotics video series (YouTube)

    The book’s own short video on the mass matrix M(θ). Linked from the book site’s chapter 8 video list.

    listed
  18. [18]

    Modern Robotics, Chapter 8.9: Actuation, Gearing, and Friction (opens in a new tab)

    Kevin M. Lynch · Northwestern University, Modern Robotics video series (YouTube)

    The book’s own video for §8.9: motors, gearing, apparent (reflected) inertia and friction models. Linked from the book site’s chapter 8 video list.

    listed
  19. [19]

    MuJoCo tutorial notebook (Python, runs in Colab) (opens in a new tab)

    Google DeepMind, GitHub

    The official hands-on introduction: MJCF, mjModel and mjData, named access, simulation and rendering, from Python. Its “Open in Colab” link runs the latest release, not necessarily 3.17.0.

    read