Skip to content

3.17.0

Latest

Choose a tag to compare

@mmossg mmossg released this 22 Oct 21:45

Version 3.17.0 (October 22, 2026)

Significant new features

  1. c00ed8153 Added a batch API in mujoco/mjbatch.h (in Python as
    mujoco.batch): many simulations of one model stepped on a thread pool of its own, each held as its
    integration state and loaded into an mjData for each call, so memory scales with threads and results are
    bit-identical to a plain loop at any thread count. It supports per-simulation model and option fields or models of
    the caller's, observations of the state after a step, recorded trajectories, persistent simulations (required for
    sleep), and arbitrary per-simulation functions. Follows the design of
    mjbatch by @kevinzakka.

Engine

  1. Flex improvements:

    • 94483e166 Added bending elasticity for 1D flexes, using a twist-free isotropic
      discrete elastic rod model. The cross-section is selected with the new section
      attribute: a circle of the flex radius, or a box with half-sizes sectionsize
      that uses the stiffness of its weak axis. It is enabled by a positive young modulus,
      requires a straight reference configuration (curved initial poses can be set with qpos or keyframes), is
      compatible with edge equality constraints, and is integrated implicitly by the discrete
      integrator. Previously young was ignored for 1D flexes: existing models that set it now bend, and fail to
      compile if the cross-section is degenerate (zero radius with the default circle section), if a vertex joins a
      zero-length edge, or if the rod is not straight.

    • 36d0df9a5, 897e3fcb1 Interpolated flexes (trilinear or quadratic
      dofs) now support self-collision, excluding elements in
      the same or neighboring cells.

    • 39dd10f2d The default arena memory for flex models is now much smaller, sized from
      the flex mesh topology instead of nv^2. If the Newton solver runs out of arena memory it falls back to CG with
      a warning.

  2. mj_setConst now computes only quantities derived from other fields of mjModel, while the compiler alone
    resolves spec-level conventions:

    • 55c1b585a, d6d41f324 The damping of position actuators from
      dampratio, the spring length of tendons from (-1, -1), and model statistics
      are now resolved only at compile time, not by mj_setConst. Set the model fields directly when editing a
      compiled model (mj_copyBack now copies tendon spring lengths back to the spec).

    • c8362756d Constraint solvers now scale their termination criteria by the trace of the current inertia
      matrix (of the model or island) instead of mjModel.stat.meaninertia. Models solved with PGS, noslip or islands
      disabled can stop at slightly different iterations. The meaninertia attribute is
      deprecated and now emits a compile warning; remove it to migrate.

    • abddec5b9 For body-based weld constraints without a specified
      relpose, the relative pose in qpos0 is now recomputed by mj_setConst when
      the model is modified after compilation (matching the second body's anchor in
      body-based connect constraints). The compiler marks such welds with a nonzero eq_data[11], and
      mjNEQDATA increased from 11 to 12.

Warning

Breaking ABI changes

body-based welds that had no relpose at compile time. Zero eq_data[11] before calling mj_setConst
to set the relative pose of such a weld directly.

  • 865316f19 Added MJSETCONST_ARRAYS and MJSETCONST_SCALARS in mjxmacro.h, listing the
    mjModel arrays and scalars read and written by each phase of mj_setConst.
  1. e15ecd842 Added single-shot multicontact for geoms with positive contact
    margins.

  2. 7394a8672 mju_boxQP now updates its Cholesky factor incrementally with mju_cholUpdate, making
    cold-started solves 1.4-1.6x faster and warm-started solves 1.9-2.9x faster.

  3. aa0f88e77 Trees marked sleep="init" which cannot be put to sleep now cause a warning
    rather than an error, and start awake. See Sleeping for details.

Compiler

Warning

Breaking API changes

  1. aad2e2982 mjs_fuseStatic and mjs_discardVisual now return the number of bodies fused or
    elements discarded, and -1 on error. Previously they returned 0 on success, so a caller could not tell whether the
    spec changed. In Python, MjSpec.fuse_static and MjSpec.discard_visual return the same number instead of
    None.

    Migration: Test for an error with < 0 rather than != 0.

  2. 084828896 mjs_addTexture now takes a default class, like mjs_addMesh and
    mjs_addMaterial.

    Migration: mjs_addTexture(spec) → mjs_addTexture(spec, NULL). MjSpec.add_texture is unchanged.

  1. 3576818f5, b0f206d04 Textures can be left by reference instead of being
    decoded into mjModel. With compiler/passthrough set to "textures", or
    texture/embed set to "false", a single-file texture is not read at compile time; the
    model holds its path instead of its pixels, so a scene that attaches many textured sub-models loads in a fraction of
    the memory and time. In addition, a compiled mjSpec no longer keeps the decoded pixels of its textures between
    compilations.

    • 3576818f5 What the model holds: tex_adr is -1, tex_width and tex_height are 0, ntexdata
      excludes the texture, the resolved path is at tex_pathadr, and any authored
      content_type is at the new tex_contenttypeadr. Flipped, composite, six-file,
      procedural, buffer and in-memory textures stay embedded, as does any texture with embed "true".

    • 1be762d0c Getting the pixels: mj_loadTexture decodes a texture of either kind, and
      mj_embedAssets returns a copy of the model with every by-reference texture loaded (which followed by
      mj_saveModel gives a hermetic MJB). Both are in Python as mujoco.mj_loadTexture and
      mujoco.mj_embedAssets.

    • fd92ba1db Rendering: the built-in and Filament renderers load by-reference textures on demand, and
      Filament uploads each file once for all textures that share it. A file that fails to resolve produces a warning and
      an untextured material.

  2. 084828896 Textures can have default classes: mjsDefault has a texture
    member and texture takes a class.

  3. 4e9a2de2d Added mjs_dedupAssets (MjSpec.dedup_assets), which merges the textures and meshes of
    an mjSpec that would compile to the same asset, as those of a sub-model attached several times do: the first
    of each group is kept and every reference to the others is rewritten to it. Like mjs_fuseStatic, it returns
    the number of assets merged.

  4. 027cb3dd8 The asset paths in mjModel (paths at mesh_pathadr, hfield_pathadr,
    skin_pathadr and tex_pathadr) now store the resolved path or URI from which the compiler read the asset
    (combining the model file's directory and meshdir or texturedir with file, unless file is an
    absolute path or URI), so mju_openResource can open them directly even after attaching
    models with different asset directories. Code that prefixed these paths with its own directory should pass them to
    mju_openResource as is.

  5. 9f035384d Mesh polygon normals (mjModel.mesh_polynormal) are now computed from the whole polygon
    outline using Newell's method rather than the first three vertices (#3691).

Python bindings

  1. 08330629b Added a mujoco.batch module over the batch API: Batch(model, nsim) with live
    (nsim, ...) arrays from bind() and named views such as joint(name), per-simulation model fields from
    expand(), step, forward, reset, set_const and rollout with the GIL released, and jac and
    ray queries.

  2. 1313ef78e Added support for building the Python bindings with Bazel (initially
    covers the core bindings, without rendering or the viewer).

  3. 39b7193e8 Fixed several bugs in named access (data.actuator('a'),
    model.joint('j'), data.bind(...)), and the corresponding generated accessors in the JavaScript bindings:

    • Multi-control or multi-output actuators (such as orientation) now follow
      actuator_ctrladr and actuator_outadr with leading dimension ctrlnum or outnum, instead of reading
      a single row at the actuator index.

    • data.actuator('a').moment is now a read-only dense copy of the actuator's moment arm of shape (nv,) or
      (outnum, nv), instead of reading the sparse actuator_moment array as if it were dense.

    • Views of multi-column per-dof fields (cdof, cdof_dot, dof_dampingpoly) on joints after the first now
      index the right rows.

  4. 7cdc8857e Unallocated arena arrays of MjData (such as efm_* when the discrete integrator is
    not used, or island arrays when no islands are formed) are now filled with zeros, or -1 for index fields, instead of
    returning uninitialized memory.

Bug fixes

  1. Fixed several bugs in interpolated flexcomp elasticity:

    • 4bbc96957 Volume (solid) interpolated flexes whose first cell is empty (e.g. a trilinear
      flexcomp from a mesh that does not fill the corner of its bounding box) now retain their
      elasticity; previously elasticity was gated on the first cell's stiffness.

    • b0b20166d Editing a flex to remove a strain equality (e.g. from
      body.make_flex) and recompiling now recomputes stiffness instead of keeping the strain constraint eigenmodes.

    • 08bf94b18 Shell-mode interpolated flexes with elastic2d set to
      "stretch" or "both" now apply membrane stretch forces and implicit stiffness, and "bend" shells
      without strain constraints no longer allocate flex_stiffness.

  2. Fixed several bugs in the Filament renderer:

    • 5c7c1e3ae Textures now allocate mipmap levels (including sRGB textures), fixing minification aliasing.

    • e81c8aed4 Skins that share vertices across faces no longer fail with an index count mismatch
      (#3719).

    • 001aa3203 Material colors now apply only when an object has the default color, instead of overwriting
      custom object colors.

    • 6cfc76e32 Textures with an unsupported number of channels are now skipped with a warning, matching the
      classic renderer, instead of calling mju_error.

  3. Fixed several bugs on error paths which called mju_error while C++ objects or locks were held (an error
    handler that does not return, as in the Python bindings and the model compiler, skips C++ destructors):

    • 4cc38db84, 8c1e45559, 526583e5b Invalid configurations of the touch grid, cable and
      SDF plugins leaked more than 10 MB per failed compilation (they now fail plugin initialization with a warning);
      mju_getXMLDependencies and unknown material layer roles also leaked memory on
      invalid XML, and unsupported equality types in the XML writer are now reported as write errors instead of calling
      mju_error.

    • 4cc38db84 An error while compiling meshes or textures on the compiler's worker threads crashed the
      process, and a failed library load in mj_loadAllPluginLibraries left its mutex locked.

    • ae15ab978 mj_recompile freed the given mjData and called mju_error if a plugin
      failed to initialize, and did not delete the model and data on some other failures, contrary to its documentation.

Models

  1. 94483e166 The
    elastic mechanism example models
    now use 1D flex rods: the cantilever comparison of quadratic flexes adds a rod, and the elastic slider-crank and the
    tendon-loaded dipper arm are built from rods.