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
The model is the description, the data is the state
mjModelholds everything the compiler produced (bodies, joints, masses, limits, options) and is meant to stay constant while you simulate.mjDataholds the state and every intermediate result, and belongs to the model it was made from. One model can drive manymjData, which is how MuJoCo runs simulations side by side.mj_loadXMLreturns 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]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]
One step is forward dynamics, then integration
mj_stepcomputes the accelerations for the current state, then advances time by one timestep (F5.05).mj_forwarddoes 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]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_qposadrandjnt_dofadr, and find the joint itself by name withmj_name2id, so that adding a joint elsewhere does not shift your indices.[5][6]Energy is off by default, and with Euler one step behind
d->energystays 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 aftermj_stepdescribes 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, callmj_forwardfor 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]Armature is inertia the joint carries and the bodies do not show
A rotor spinning times faster than the joint it drives adds 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 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 grows quickly: behind a 100:1 gearbox, the rotor counts ten thousand times over.[4][10]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.
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.
Symbols and checks
Symbol Meaning Unit mass of body b kg gravity vector (default 0 0 −9.81) m/s² centre of mass of body b m - ✓ 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
MuJoCo kinetic energy
Symbols and checks
Symbol Meaning Unit generalised velocity rad/s or m/s mass matrix kg·m² or kg - ✓ MuJoCo KE vs 1/2 I thetadot^2 (worst, J)expected 0 · got 5.55112×10⁻¹⁷ · absolute error 5.6×10⁻¹⁷
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.
Symbols and checks
Symbol Meaning Unit gear ratio (motor turns per joint turn) dimensionless rotor inertia kg·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⁻¹⁶
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.
Symbols
Symbol Meaning Unit solref time constant s timestep s 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);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
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 holdSymbols 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
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 6Symbols 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
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 -->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
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 -->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
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 identicalSymbols 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
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)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
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 */ }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.
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>Look at what was stored
mj_printFormattedModelwrites 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 Field Meant Stored jnt_range, upper limitrad (90°) 0.0274 rad (1.57°) body_mass, the link200 g 24.4 g 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>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
kModelPathconstant 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 }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 Model No compiler warning Limit check Mass check Test As written passes fails fails fails Fixed passes passes passes passes Fixed, plus a warning fails passes passes fails
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
Declare the angle unit at the top of every model.
<compiler angle="radian"/>orangle="degree", even when it is the default, so nobody has to remember which applies. URDF files are always read in radians.[4]Give every body that matters an explicit mass or inertial element.
A mass derived from the default density assumes a solid geom with the density of water. Write down the mass you measured, and let the compiler derive only what you have no better number for.[4]
Check the pointer the loader returns, and the message buffer too.
A failed load returns null with the reason in the buffer; a load with a compiler warning returns the model with the warning in the same buffer (F5.12). Owning the model from that line, as in module 03, leaves these two checks as the only things to remember.[3][8]
Look up joints and bodies by name.
mj_name2idreturns the index, or −1 if the name does not exist, which a test can catch. Hard-coded indices break silently when the model changes.[5]Call
mj_forwardafter editing the state by hand.Positions of bodies, sensor readings and energy are computed from
qposandqvel; until forward dynamics runs again they describe the old state.[2]Keep the timestep and soft-constraint time constants in step.
A soft constraint's time constant should be at least twice the timestep (F5.04). Unless the integrator is
discrete, MuJoCo raises a smaller time constant in the standard (time constant, damping ratio) format to that floor, unless therefsafeflag is off, so the value you wrote may not be the value used. Halving the timestep shows whether a result depends on it.[13][14]
Questions
- Why is
qposlonger thanqvel? - 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
nqequal tonv, 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->energyis 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_rangein Python ism->jnt_rangein C, shaped as two columns. The check behind this page reads the same joint limit both ways. Because Python fields are views,mj_stepchanges 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
- listed
Modern Robotics, Chapter 8.1.3: Understanding the Mass Matrix (opens in a new tab)[17]
Kevin M. Lynch · Northwestern University, Modern Robotics video series (YouTube)
What the mass matrix is and why it changes with the arm's configuration, from the course this project follows.
- listed
Modern Robotics, Chapter 8.9: Actuation, Gearing, and Friction (opens in a new tab)[18]
Kevin M. Lynch · Northwestern University, Modern Robotics video series (YouTube)
Gearing and the apparent inertia of a motor seen through it: the physics behind armature.
Read
- read
MuJoCo Overview (3.17.0) (opens in a new tab)[1]
Google DeepMind
The overview: model and data, the compiler, and the clarifications section, which answers several of this page's questions in advance.
- read
MuJoCo Programming chapter, Simulation (3.17.0) (opens in a new tab)[2]
Google DeepMind
The simulation loop, the state and how to save and restore it.
- read
MuJoCo XML Reference (3.17.0) (opens in a new tab)[4]
Google DeepMind
Every attribute and its default. Look up an element here before trusting what you think its default is.
- read
MuJoCo tutorial notebook (Python, runs in Colab) (opens in a new tab)[19]
Google DeepMind, GitHub
The official notebook, and the fastest way to see a model, a step and a rendered frame. Opened in Colab, it runs the latest MuJoCo release rather than 3.17.0.
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]read
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.
- [2]read
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.
- [3]read
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.
- [4]read
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).
- [5]read
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.
- [6]read
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.
- [7]read
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.
- [8]derived
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.
- [9]read
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.
- [10]listed
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.
- [11]read
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.
- [12]read
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.
- [13]read
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.
- [14]read
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.
- [15]read
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.
- [16]read
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.
- [17]listed
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.
- [18]listed
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.
- [19]read
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.