Update "mjModel changes" section in simulation.rst

PiperOrigin-RevId: 838768328
Change-Id: I5c09186e52115df37e9412495ecc7dceddb2ed8a
This commit is contained in:
Yuval Tassa
2025-12-01 07:38:46 -08:00
committed by Copybara-Service
parent 19e2d0ae2f
commit 4896e698f6
+117 -33
View File
@@ -553,47 +553,131 @@ or termination of the iterative solver.
.. _siChange:
Model changes
~~~~~~~~~~~~~
mjModel changes
~~~~~~~~~~~~~~~
.. admonition:: Model editing framework
.. admonition:: Procedural Model editing with :ref:`mjSpec`
:class: tip
The discussion below regarding mjModel changes at runtime was written before the 3.2.0 introduction of the
:doc:`Model Editing<modeledit>` framework. It is still valid, but the new framework is the safe and recommended way
to modify models.
The discussion below regarding :ref:`mjModel` modifications was written before the introduction of procedural
:doc:`Model Editing<modeledit>`. It is still valid, but the new framework is the safe and recommended way to modify
models. The main reason to modify an :ref:`mjModel` at runtime rather than modifying the :ref:`mjSpec` and compiling
again is *speed*. However it can be unsafe to make some changes, either in the sense that segfaults are possible, or
that the physics will change unexpectedly.
The MuJoCo model contained in mjModel is supposed to represent constant physical properties of the system, and in
theory should not change after compilation. Of course in practice things are not that simple. It is often desirable to
change the physics options in ``mjModel.opt``, so as to experiment with different aspects of the physics or to create
custom computations. Indeed these options are designed in such a way that the user can make arbitrary changes to them
between time steps.
The general rule is that real-valued parameters are safe to change, while structural integer parameters are not because
that may result in incorrect sizes or indexing. This rule does not hold universally, and below we describe the
exceptions.
The general rule is that real-valued parameters are safe to change, while structural integer parameters are not
because that may result in incorrect sizes or indexing. This rule does not hold universally though. Some real-valued
parameters such as inertias are expected to obey certain properties. On the other hand, some structural parameters
such as object types may be possible to change, but that depends on whether any sizes or indexes depend on them.
Arrays of type mjtByte can be changed safely, since they are binary indicators that enable and disable certain
features. The only exception here is ``mjModel.tex_data`` which is texture data represented as mjtByte.
Exceptions to the general rule that **integer** types are **not safe to change**:
When changing mjModel fields that corresponds to resources uploaded to the GPU, the user must also call the
corresponding upload function: ``mjr_uploadTexture``, ``mjr_uploadMesh``, ``mjr_uploadHField``. Otherwise the data used
for simulation and for rendering will no longer be consistent.
.. list-table::
:widths: 1 1 4
:header-rows: 1
:class: schema-small
A related consideration has to do with changing real-valued fields of mjModel that have been used by the compiler to
compute other real-valued fields: if we make a change, we want it to propagate. That is what the function
:ref:`mj_setConst` does: it updates all derived fields of mjModel. These are fields whose names end with "0",
corresponding to precomputed quantities when the model is in the reference configuration ``mjModel.qpos0``.
* - Field
- Modifiability
- Notes
* - ``XXX_limited`` |br| ``XXX_group`` |br| ``XXX_matid`` |br| ``XXX_texid``
- Safe
-
* - ``XXX_sameframe``
- Unsafe
- This flag tells the engine to skip a parent/child frame transformation. It is safe to change from nonzero to
zero, but not vice versa.
* - ``geom_contype`` |br| ``geom_conaffinity``
- Unsafe
- This is a possible to do safely if ``body_contype`` and ``body_conaffinity`` of the parent body are updated to be
the bitwise OR over all child geoms.
* - ``geom_condim`` |br| ``geom_priority``
- Safe
-
* - ``cam_resolution``
- Safe
-
* - ``light_castshadow`` |br| ``light_active``
- Safe
-
* - ``flex_contype`` |br| ``flex_conaffinity`` |br| ``flex_condim`` |br| ``flex_priority``
- Safe
-
* - ``tex_data``
- Safe
- Must call :ref:`mjr_uploadTexture` to update the values in GPU memory.
Finally, if changes are made to mjModel at runtime, it may be desirable to save them back to the XML. The function
:ref:`mj_saveLastXML` does that in a limited sense: it copies all real-valued parameters from mjModel back to the
internal :ref:`mjSpec`, and then saves it as XML. This does not cover all possible changes that the user could have
made. The only way to guarantee that all changes are saved is to save the model as a binary MJB file with the function
:ref:`mj_saveModel`, or even better, make the changes directly in the XML. Unfortunately there are situations where
changes need to be made programmatically, as in system identification for example, and this can only be done with the
compiled model. So in summary, we have reasonable but not perfect mechanisms for saving model changes. The reason for
this lack of perfection is that we are working with a compiled model, so this is like changing a binary executable and
asking a "decompiler" to make corresponding changes to the C code -- it is just not possible in general.
When considering exceptions to the rule that real-valued parameters are safe to change, we need to note the function
:ref:`mj_setConst`, which constitutes the last step of the compilation process. This function propagates changes from
some fields to other fields, allowing changes that would otherwise be unsafe.
Exceptions to the general rule that **real-valued** types **are safe to change**:
.. list-table::
:widths: 1 1 4
:header-rows: 1
:class: schema-small
* - Field
- Modifiability
- Notes
* - ``qpos0`` |br| ``qpos_spring``
- Safe with :ref:`mj_setConst`.
-
* - ``body_mass`` |br| ``body_inertia`` |br| ``body_ipos`` |br| ``body_iquat``
- Safe with :ref:`mj_setConst`.
- Note that mass and inertia are usually scaled together, since inertia is :math:`\sum m r^2`. Scaling them
separately is legitimate, but implies a changing of the spatial mass distribution. Also note that diagonal
inertias must obey the triangle inequality.
* - ``body_pos`` |br| ``body_quat``
- Safe with :ref:`mj_setConst`.
- Unsafe for static bodies, invalidates the midphase collision structures (BVH).
* - ``body_gravcomp``
- Safe.
- If the number of bodies with gravity compensation is changed from zero to non-zero,
:ref:`mj_setConst` must be called.
* - ``dof_armature``
- Safe with :ref:`mj_setConst`.
-
* - ``geom_pos`` |br| ``geom_quat`` |br| ``geom_size`` |br| ``geom_rbound`` |br| ``geom_aabb``
- Unsafe.
-
* - ``{site,cam,light}_`` |br| ``{pos,quat}``
- Mostly safe.
- For cameras and lights with tracking or targeting, :ref:`mj_setConst` is required.
* - ``tendon_stiffness`` |br| ``tendon_damping``
- Mostly safe.
- Affects whether kinematic trees are allowed to sleep. If changing from/to zero, :ref:`mj_setConst` is required.
* - ``actuator_gainprm`` |br| ``actuator_biasprm``
- Mostly safe.
- For position-like actuators using :ref:`dampratio<actuator-position-dampratio>`, :ref:`mj_setConst` is required.
* - ``eq_data``
- Safe with :ref:`mj_setConst`.
- For connect and weld constraints, offsets are computed if not provided.
* - ``hfield_size``
- Safe with :ref:`mj_setConst`.
-
* - ``hfield_data``
- Safe.
- Data range must be in [0, 1].
|br| :ref:`mjr_uploadHField` is required to update the values in GPU memory.
* - ``mesh_scale`` |br| ``mesh_pos`` |br| ``mesh_quat``
- Not unsafe, but has no effect.
- ``mesh_pos`` and ``mesh_quat`` affect SDF sensors at runtime.
* - ``mesh_vert`` |br| ``mesh_normal`` |br| ``mesh_face`` |br| ``mesh_polynormal``
- Unsafe for colliding meshes.
- Safe for visual meshes, but requires :ref:`mjr_uploadMesh` to update the values in GPU memory.
* - ``bvh_aabb`` |br| ``oct_aabb`` |br| ``oct_coeff``
- Unsafe
-
Finally, if changes are made to mjModel at runtime, it may be desirable to save them back to the XML. The functions
:ref:`mj_saveLastXML` and :ref:`mj_copyBack` do that in a limited sense: they copy all real-valued parameters from
:ref:`mjModel` back to the :ref:`mjSpec` (the global internal spec in the former case, the user's copy in the latter).
This does not cover all possible changes that the user could have made. The only way to guarantee that all changes are
saved is to save the model as a binary MJB file with the function :ref:`mj_saveModel`, or even better, make the changes
directly in XML or :ref:`mjSpec`. So in summary, we have reasonable but not perfect mechanisms for saving model changes.
The reason for this lack of perfection is that we are working with a compiled model, so this is like changing a binary
executable and asking a "decompiler" to make corresponding changes to the C code -- it is just not possible in general.
.. _siLayout: