From 8734cab3661b80b076f8dc5664e1892811bd2b4f Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Fri, 7 Nov 2025 05:11:31 -0800 Subject: [PATCH] Add sleeping documentation https://youtu.be/vct493lGQ8Q PiperOrigin-RevId: 829390353 Change-Id: I92f85a03be0bb585d2d8e29de1ed1a2e848f247f --- doc/XMLreference.rst | 39 ++++- doc/changelog.rst | 9 +- doc/computation/index.rst | 79 ++++++++-- doc/overview.rst | 69 +++++++-- doc/programming/simulation.rst | 172 ++++++++++++++++++++- model/sleep/100_humanoids.xml | 50 ++++++ model/sleep/dominos.xml | 92 +++++++++++ model/sleep/humanoid.xml | 273 +++++++++++++++++++++++++++++++++ 8 files changed, 754 insertions(+), 29 deletions(-) create mode 100644 model/sleep/100_humanoids.xml create mode 100644 model/sleep/dominos.xml create mode 100644 model/sleep/humanoid.xml diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index aa80f6f5..e56446fb 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -463,7 +463,7 @@ adjust it properly through the XML. .. _option-sleep_tolerance: :at:`sleep_tolerance`: :at-val:`real, "1e-4"` - Velocity tolerance below which sleeping is possible. Feature under development, documentation pending. + Velocity tolerance below which :ref:`sleeping` is allowed. .. _option-sdf_iterations: @@ -674,7 +674,16 @@ from its default. .. _option-flag-sleep: :at:`sleep`: :at-val:`[disable, enable], "disable"` - This flag enables sleeping. Feature under development, documentation pending. + This flag enables :ref:`sleeping`. Disabling this flag when some trees are sleeping will wake them. + + .. admonition:: flag value at initialization time + :class: attention + + Unlike any other :ref:`flag`, the :at:`sleep` flag has an effect during :ref:`mjData` + **initialization** (:ref:`mj_makeData` or :ref:`mj_resetData`). First, it must be set at initialization time in + order for the :ref:`sleep-init` policy to take effect. Second, it must be set in order for static + quantities to be computed. See :ref:`implementation notes` for more details. + .. _compiler: @@ -2072,7 +2081,31 @@ defined. Its body name is automatically defined as "world". .. _body-sleep: :at:`sleep`: :at-val:`[auto, never, allowed, init], "auto"` - Sleep policy for the tree under this body. Feature under development, documentation pending. + :ref:`Sleep` policy for the tree under this body. This attribute is only supported by moving bodies which + are the root of a kinematic :ref:`tree`. For the default :at-val:`auto`, the compiler will set the sleep + policy as follows: + + - A tree which is affected by actuators is not allowed to sleep (overridable). + - Trees which are connected by tendons which have non-zero stiffness and damping are not allowed to sleep + (overridable). + - Trees which are connected by tendons which connect more than two trees are not allowed to sleep (not overridable). + - :ref:`flexes` are not allowed to sleep (not overridable). + - All other trees are allowed to sleep (overridable). + + The policies :at-val:`never` and :at-val:`allowed` constitute user overrides of the automatic compiler policy. + + The :at-val:`init` sleep policy can only be specified by the user and means "initialize this tree as asleep". This + policy is implemented in :ref:`mj_resetData` and :ref:`mj_makeData` and only applies to the default configuration. If + a :ref:`keyframe` changes the configuration of (or assigns nonzero velocity to) a sleeping tree, it will be + woken up. This policy is useful for very large models where waiting for the automatic sleeping mechanism to kick in + can be expensive. Trees initialized as sleeping can be placed in unstable configurations like deep penetration or in + mid-air, but will only move when woken up. Also note that this policy can fail. For example if a tree marked as + sleep="init" is in contact with a tree not marked as such (i.e., they are in the same :ref:`island`) then + it is impossible to put the tree to sleep; such `models + `__ will lead to + a compilation error. + + See :ref:`implementation notes` for more details. .. _body-user: diff --git a/doc/changelog.rst b/doc/changelog.rst index ab5bd276..cf2e9485 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -6,9 +6,14 @@ Upcoming version (not yet released) ----------------------------------- General -^^^^^^^^^ +^^^^^^^ -- Initial changes related to sleeping. Feature under development, documentation pending. +.. youtube:: vct493lGQ8Q + :align: right + :width: 35% + +- Introduced a major new feature: :ref:`sleeping islands`. Preliminary release for early testing, see + documentation for details. - Added "quadratic" option to :ref:`flexcomp/dof`. This type of fast :ref:`deformable` flex object is similar to the "trilinear" option, but it includes curved deformations. - Raise an error if there are name collisions also during parsing. diff --git a/doc/computation/index.rst b/doc/computation/index.rst index 8a17981c..6762d667 100644 --- a/doc/computation/index.rst +++ b/doc/computation/index.rst @@ -1380,25 +1380,28 @@ Constraint islands :width: 58% :align: right -Consider the abstract graph defined by degrees of freedom (dofs) and constraints. A vertex is all the dofs in a single -kinematic subtree; an edge is a constraint (a contact or equality) between two bodies belonging to different subtrees. A -*constraint island* is a disjoint sub-graph which can be solved for independently, because constraint forces cannot -propagate between islands. Constraint island discovery and construction ("islanding") invloves finding the disjoint -subgraphs and reordering both the dofs and constraints to make them memory-contiguous. This amounts to a -block-diagonalization of the constraint Jacobian :math:`J`, as illustrated in the figure. On the left is the monolithic -Jacobian of size :math:`\nc \times \nv`, where we use the :ref:`corresponding ` size names from MuJoCo's data -structures ``mjData.nefc`` and ``mjModel.nv``. On the right is the block-diagonalized Jacobian with 3 islands that can -be solved indepenently. Note that islanding also identifies unconstrained dofs, so ``mjData.nidof``, the total number of -dofs in all islands, might be smaller than ``mjModel.nv``. While islanding is not free (see implementation in -`engine_island.c `__), it is worth the -effort: +Consider the abstract graph defined by degrees of freedom (:ref:`DOFs`) and constraints. A vertex is all the +DOFs in a single kinematic :ref:`tree`; an edge is a constraint (a contact, equality or tendon limit) between +two bodies belonging to different trees. A *constraint island* is a disjoint sub-graph which can be solved for +independently, because constraint forces cannot propagate between islands. Constraint island discovery and construction +("islanding") involves finding these disjoint subgraphs and reordering both the DOFs and constraints to make them +memory-contiguous. This amounts to a block-diagonalization of the constraint Jacobian :math:`J`, as illustrated in the +figure. On the left is the monolithic Jacobian of size :math:`\nc \times \nv`, where we use the :ref:`corresponding +` size names from MuJoCo's data structures ``mjData.nefc`` and ``mjModel.nv``. On the right is the +block-diagonalized Jacobian with 3 islands that can be solved independently. Note that islanding also identifies +unconstrained DOFs, so ``mjData.nidof``, the total number of DOFs in all islands, might be smaller than ``mjModel.nv``. +While islanding is not free (see implementation in `engine_island.c +`__), it is worth the effort: - Different islands require different numbers of iterations to converge, and a monolithic solve would run for the number required by the slowest island. -- Unconstrained dofs are completely untouched by the solver, which otherwise needs to discover that they are unaffected. +- Unconstrained DOFs are completely untouched by the solver, which otherwise needs to discover that they are unaffected. - Solving separate islands can be multi-threaded. -Islanding is not yet supported by the PGS solver. +.. admonition:: Known issues + :class: note + + Islanding is not yet supported by the PGS solver. .. _soParameters: @@ -1668,6 +1671,54 @@ callbacks. This can be used to incorporate a general-purpose "triangle soup" col do not recommend such an approach. Pre-processing the geometry and representing it as a union of convex geoms takes some work, but it pays off at runtime and yields both faster and more stable simulation. +.. _Sleeping: + +Sleeping islands +---------------- + +Sleeping is a performance optimization whereby movable elements of the simulation that are detected to be stationary are +temporarily removed from the pipeline ("put to sleep"). This optimization is most useful when the model contains a large +number of passive objects. The smallest unit which can be asleep or awake is a :ref:`kinematic tree`, however +trees are always put to sleep together with other trees to which they are connected by constraints, hence the term +"sleeping :ref:`islands`". + +.. youtube:: vct493lGQ8Q + :align: right + :width: 50% + +The video on the right demonstrates several aspects of sleeping. First we show the `dominos +`__ model, which simulates a traditional +"chain reaction" of falling domino bricks. All bricks except one are in a stable equilibrium and quickly go to sleep, +but the first brick, which is unstable, remains awake and starts falling. Every time a contact is made between awake and +sleeping bricks, the latter are woken automatically. After stabilizing on the ground, piles of bricks are put to sleep +again and their associated contacts disappear. This sequence is repeated with island visualization enabled, which +recolors geoms according to the first DOF of their island, using darker colors if asleep. At the end of the subclip, +sleeping is toggled off and on, demonstrating the speed gain afforded by sleeping (lower right). The second subclip +shows a variant of the `100 humanoids +`__ model, where all humanoids are +*initialized asleep*. Trees initialized as sleeping can be in any configuration, including floating in mid-air, deep +penetration, etc. One humanoid is manually woken by direct user perturbation and then dragged around to wake any other +humanoid that it touches. + +While the smooth dynamics benefit from sleeping, the largest speedup is due to the reduced number of contacts. Sleeping +islands behave like static bodies for the purpose of collision detection: all contacts within an island and between the +island and static bodies are skipped. In the case of static piles of objects, the number of skipped contacts can be +high, leading to substantial speed gains. Contacts between sleeping islands and awake trees are allowed, and indeed are +the main automatic trigger for waking, though manual waking is also supported. Because waking happens in the +:ref:`position stage ` of the pipeline it is effectively instantaneous, and the woken island will behave +exactly as if it was awake all along. + +Sleeping is off by default and enabled using the :ref:`sleep` flag. A detailed description of the +sleeping mechanism is provided in the :ref:`Simulation chapter` but here we provide a brief overview. + +Sleeping can occur in one of two ways: + +- **Automatic:** A tree whose maximum velocity in absolute value is less than the + :ref:`tolerance ` for :ref:`mjMINAWAKE ` time steps is marked as "ready to sleep". + If all trees in an island are ready to sleep, they are put to sleep during state advancement. +- **Initialized asleep:** By setting the :ref:`body/sleep` attribute of a tree root to "init", it is + marked as "initialized-asleep" and put to sleep during :ref:`mjData` initialization. + .. _Pipeline: Simulation pipeline diff --git a/doc/overview.rst b/doc/overview.rst index 85f48e1a..3faba39f 100644 --- a/doc/overview.rst +++ b/doc/overview.rst @@ -422,12 +422,19 @@ Kinematic tree ~~~~~~~~~~~~~~ MuJoCo simulates the dynamics of a collection of rigid bodies whose motion is usually constrained. The system state is -represented in joint coordinates and the bodies are explicitly organized into kinematic trees. Each body except for the -top-level "world" body has a unique parent. Kinematic loops are not allowed; if loop joints are needed they should be -modeled with equality constraints. Thus the backbone of a MuJoCo model is one or several kinematic trees formed by -nested body definitions; an isolated floating body counts as a tree. Several other elements listed below are defined -within a body and belong to that body. This is in contrast with the stand-alone elements listed later which cannot be -associated with a single body. +represented in joint coordinates and the bodies are explicitly organized into kinematic trees. The tree structure is +given by ``mjModel.body_parentid``, an integer array of length ``nbody >= 1``. The top-level "world" body always exists +(with id ``0``) and is its own parent, thus ``body_parentid[0] == 0`` and ``body_parentid[i] < i`` for all other ``i``. +Note that the world body and other static (joint-less) child bodies form a unique "static tree" with no associated +degrees of freedom. Below this top-level static tree, multiple kinematic trees can be attached, see :ref:`Tree +` below. + +Kinematic loops are not allowed; if loop joints are needed they should be modeled with equality constraints. Thus the +backbone of a MuJoCo model is one or several kinematic trees formed by nested body definitions; an isolated floating +body counts as a tree. Several other elements listed below are defined within a body and belong to that body. This is in +contrast with the stand-alone elements listed later which cannot be associated with a single body. + +.. _ElemBody: Body ^^^^ @@ -438,6 +445,9 @@ position other elements relative to it, and an inertial frame centered at the bo its principal axes of inertia. The body inertia matrix is therefore diagonal in this frame. At each time step MuJoCo computes the forward kinematics recursively, yielding all body positions and orientations in global Cartesian coordinates. This provides the basis for all subsequent computations. +The number of bodies is given by ``mjModel.nbody``. + +.. _ElemJoint: Joint ^^^^^ @@ -448,7 +458,7 @@ over-complete Cartesian coordinates, where joints remove DOFs instead of adding ball, slide, hinge, and a "free joint" which creates floating bodies. A single body can have multiple joints. In this way composite joints are created automatically, without having to define dummy bodies. The orientation components of ball and free joints are represented as unit quaternions, and all computations in MuJoCo respect the properties of -quaternions. +quaternions. The number of joints is given by ``mjModel.njnt``. Joint reference ''''''''''''''' @@ -475,6 +485,8 @@ spring reference pose is saved in ``mjModel.qpos_spring``. For slide and hinge j specified with the attribute springref. For ball and free joints, the spring reference corresponds to the initial model configuration. +.. _ElemDof: + DOF ^^^ @@ -485,7 +497,27 @@ system, while the joint velocities are coordinates over the tangent space to thi DOFs have velocity-related properties such as friction loss, damping, armature inertia. All generalized forces acting on the system are expressed in the space of DOFs. In contrast, joints have position-related properties such as limits and spring stiffness. DOFs are not specified directly by the user. Instead they are created by the compiler given the -joints. +joints. The number of DOFs is given by ``mjModel.nv``. + +.. _ElemTree: + +Tree +^^^^ + +As explained :ref:`above `, moving bodies are organized into kinematic trees. A kinematic tree or "tree" is +*a movable body and all of its descendants*. Thus the world and other static bodies are in the global tree structure but +not associated with any *tree*. Because the global tree structure uses a depth-first organization, all bodies, joints +and DOFs belonging to a single tree are always sequential. Note that unlike bodies which (if static) are not associated +with any tree, joints and DOFs are always associated with a tree. Both :ref:`island discovery ` and +:ref:`island sleeping ` operate on the level of trees. + +The number of trees is given by ``mjModel.ntree``. For example, a model containing three free bodies and the `standard +humanoid `__ has ``ntree = 4``. Note +that while trees are indeed subtrees of the global tree (whose root is the world), this should not be confused with the +specific term ``subtree``, which is reserved for per-body partial trees, thus ``mjModel.body_subtreemass`` gives the +total mass of the partial tree under each body, for all bodies. + +.. _ElemGeom: Geom ^^^^ @@ -497,6 +529,9 @@ subsequent computation of contact forces, geoms are used for rendering, as well and inertias when the latter are omitted. MuJoCo supports several primitive geometric shapes: plane, sphere, capsule, ellipsoid, cylinder, box. A geom can also be a mesh or a height field; this is done by referencing the corresponding asset. Geoms have a number of material properties that affect the simulation and visualization. +The number of geoms is given by ``mjModel.ngeom``. + +.. _ElemSite: Site ^^^^ @@ -504,6 +539,9 @@ Site Sites are essentially light geoms. They represent locations of interest within the body frame. Sites do not participate in collision detection or automated computation of inertial properties, however they can be used to specify the spatial properties of other objects like sensors, tendon routing, and slider-crank endpoints. +The number of sites is given by ``mjModel.nsite``. + +.. _ElemCamera: Camera ^^^^^^ @@ -515,6 +553,9 @@ orientation, the user can adjust the vertical field of view and the inter-pupila as well as create oblique projections needed for stereoscopic virtual environments. When modeling real cameras with imperfect optics, it is possible to specify separate focal lengths for the horizontal and vertical directions and a non-centered principal point. +The number of cameras is given by ``mjModel.ncam``. + +.. _ElemLight: Light ^^^^^ @@ -527,6 +568,7 @@ used with caution. Documenting the lighting model in detail is beyond the scope documentation `__ instead. Note that in addition to lights defined by the user in the kinematic tree, there is a default headlight that moves with the camera. Its properties are adjusted through the mjVisual options. +The number of lights is given by ``mjModel.nlight``. .. _Standalone: @@ -536,6 +578,8 @@ Stand-alone Here we describe the model elements which do not belong to an individual body, and therefore are described outside the kinematic tree. +.. _ElemTendon: + Tendon ^^^^^^ @@ -549,6 +593,8 @@ other, the user can also specify the preferred side. If there are multiple wrapp must be separated by sites, so as to avoid the need for an iterative solver. Spatial tendons can also be split into multiple branches using pulleys. +.. _ElemActuator: + Actuator ^^^^^^^^ @@ -561,6 +607,8 @@ biological muscles; using such actuators makes the overall system dynamics 3rd-o determines how the scalar control signal provided as input to the actuator is mapped into a scalar force, which is in turn mapped into a generalized force by the moment arms inferred from the transmission. +.. _ElemSensor: + Sensor ^^^^^^ @@ -574,6 +622,8 @@ other quantity of interest in the sensor data array. MuJoCo also has off-screen straightforward to simulate both color and depth camera sensors. This is not included in the standard sensor model and instead has to be done programmatically, as illustrated in the code sample :ref:`simulate.cc `. +.. _ElemEquality: + Equality ^^^^^^^^ @@ -584,11 +634,12 @@ available equality constraint types are: connect two bodies at a point (creating tree); weld two bodies together; fix the position of a joint or tendon; couple the positions of two joints or two tendons via a cubic polynomial; constrain the edges of a flex (i.e. deformable mesh) to their initial lengths. +.. _ElemFlex: Flex ^^^^ -Flexes were added in MuJoCo 3.0. They represent deformable meshes that can be 1, 2 or 3 dimensional (thus their elements +Flexes represent deformable meshes that can be 1, 2 or 3 dimensional (thus their elements are capsules, triangles or tetrahedra). Unlike geoms which are static shapes attached rigidly to a single body, the elements of a flex are deformable: they are constructed by connecting multiple bodies, thus the body positions and orientations determine the shape of the flex elements at runtime. These deformable elements support collisions and diff --git a/doc/programming/simulation.rst b/doc/programming/simulation.rst index 26bf3fe7..aa4fec64 100644 --- a/doc/programming/simulation.rst +++ b/doc/programming/simulation.rst @@ -106,7 +106,7 @@ The default (and recommended) way to control the system is to implement a contro .. code-block:: C - // simple controller applying damping to each dof + // simple controller applying damping to each DOF void mycontroller(const mjModel* m, mjData* d) { if (m->nu == m->nv) mju_scl(d->ctrl, d->qvel, -0.1, m->nv); @@ -923,6 +923,176 @@ is in [3-5] and the Z axis is in [6-8]. The reason for this arrangement is becau where only the normal axis is used, so it makes sense to have its coordinates in the first 3 positions of ``mjContact.frame``. +.. _siSleep: + +Sleeping islands +~~~~~~~~~~~~~~~~ + +Sleeping islands are described in broad strokes in the :ref:`Computation chapter `. Here we focus on +implementation details. + +The high level sleep state of :ref:`trees` is described by ``mjData.tree_asleep`` (though see caveat below). A +negative value means a tree is awake, non-negative means asleep. Maximally awake trees are given the value - |-| (1 |-| ++ |-| :ref:`mjMINAWAKE`), and for every timestep where their velocity falls below the sleep :ref:`tolerance +`, this integer is incremented, up to -1, which means "ready to sleep". If all trees in an +island are ready to sleep, they are put to sleep during state advancement and their associated values in ``tree_asleep`` +are set to a (non-negative) index cycle: the "sleeping island". If any tree in the island is woken, all are woken. + +Sleep policy +^^^^^^^^^^^^ + +The ability of a kinematic tree to sleep is governed by a policy determined at model compile time. The compiler +automatically determines the :ref:`policy` to be either "allowed" or "never", though these can be +overridden using the :ref:`body/sleep ` attribute (see documentation therein). There is also a special +"init" sleep policy, see next section. + +Sleeping +^^^^^^^^ + +Sleeping can happen in one of two ways: + +**Automatic:** + The velocity threshold described above is w.r.t. the infinity norm (largest absolute value) of all velocities + associated with an island. Before taking this norm, velocities are scaled elementwise by ``mjModel.dof_length`` + because rotational and translational velocities have different units. The length of a translational DOF is 1; the + length of a rotational DOF corresponds to the mean length of its associated geometry. Thus :ref:`sleep_tolerance + ` has units of [length/time]. + + When an island is put to sleep, its associated velocities are set to 0. Therefore, on any timestep where + islands are put to sleep, all velocity-dependent quantities must be recomputed before the sleep state is propagated + using a call to :ref:`mj_forwardSkip`. + + If any tree in the island has the "never" sleep policy, the entire island cannot sleep. + +**Initialized asleep:** + By setting the :ref:`body/sleep` attribute of a tree root to "init", it is marked as "initialized asleep" + and put to sleep during :ref:`mjData` initialization. This is useful for large models where waiting for many trees to + fall asleep can be expensive. + + Since trees which share contacts or are otherwise in the same island must sleep together, if some trees in an island + are initialized as sleeping, all of them must be marked as such. `This model + `__ contains an + example XML that will produce a compilation error because this condition is not met. Finally, note that the + initialized-asleep feature is only available for the default configuration (and not keyframes, see discussion below). + +Waking +^^^^^^ + +Waking happens at the beginning of the timestep, either during :ref:`mj_kinematics` or soon thereafter during the +:ref:`position stage ` of the simulation pipeline. A sleeping island is woken up according to the following +criteria: + +- Its associated configuration ``qpos`` is changed by the user, for example when repositioning the + configuration interactively when the simulation is paused. +- Its associated velocity ``qvel`` or applied forces ``qfrc_applied`` or ``xfrc_applied`` are set by the user to + a non-zero value, for example when perturbing the model interactively during simulation. + Note that the check is performed by bytewise comparison to 0, so setting an associated element to + the floating point value ``-0.0`` will wake the island but have no other side-effects. +- It comes into contact with an awake tree. Waking due to contact leads to collision detection being run *twice*, but + only on the timestep when it occurs. This is required in order to detect contacts inside the island and between the + island and the world, which were skipped in the first run when it was deemed asleep. +- It is connected to an awake tree by an active equality constraint or limited tendon. +- It is connected by an equality constraint to a sleeping tree in a different island. For this to occur, the equality + must have been disabled when both trees were put to sleep. + +The automatic wake criteria listed above are designed so that sleeping islands behave as if they were awake, but this +is not always the case. For example, if free bodies on the floor are put to sleep and then gravity is reversed, they +will remain sleeping in place until woken for another reason. The most extreme example of non-physicality are islands +which are initialized asleep. These can be placed in mid-air or in deep collisions, but will not move until woken. + +Notes +^^^^^ + +.. admonition:: New feature + :class: warning + + Sleeping is a new feature (Nov 2025) that is subject to change and may have latent bugs. + +**Sleeping actuators** + As explained in the :ref:`body/sleep ` documentation, trees with actuators are by default not allowed to + sleep, but this can be overridden by the user. The reason sleeping is not allowed by default is that once an actuator + is marked as asleep, the computation required to wake it is no longer performed. Even if it were performed (i.e. if + actuation forces were always computed for all actuators, regardless of their sleep state), this computation happens in + acceleration/force stage, by which time it is already too late to wake a tree, since waking must happen in the + position stage. Therefore, if a tree with actuators is allowed to sleep, waking must be done manually by touching the + associated velocities or forces, as described above. + +**Sleeping sensors** + The computation of sensor values are skipped if the objects associated with it are asleep or static, so the last + valid computed values remain untouched. This is straightforward for most sensors, but :ref:`contact` + sensors are an exception. Because contact sensors report contacts that occurred in the current timestep, and + sleeping implies that some contacts are not computed, skipping the sensor computation is not always possible. + For example, consider a scene with free bodies on the floor, some of which may be asleep. A sensor which reports + information from contacts of body A (with anything else), will keep reporting the same thing when the body goes to + sleep. However a sensor that reports all world contacts will report something different when some bodies go to sleep. + +**Provisional choices** + Some implementation choices are provisional and subject to change. + + A concrete example is the decision to hard-code the value of :ref:`mjMINAWAKE` instead of exposing it to + the user as a runtime option. This was done for two reasons. First, in our experiments, we've found that changing this + value is equivalent to changing the :ref:`sleep_tolerance`, which is the more useful knob. + Second, one could argue for a time-to-sleep semantic that is in units of time rather than an integer number of + timesteps. Until there is clear evidence that one or both of these reasons are invalid, we've opted for a simple + numeric constant. + +**Static bodies** + Besides the main optimization of allowing kinematic trees to sleep, the sleep feature also includes another, related + optimization: the skipping of computation related to static bodies. This can be valuable if, for example, + the world body or its static children contain a large number of geoms, whose poses will be computed only once. + + This leads to a subtle (if unlikely) "gotcha". Although it is allowed to enable sleeping during simulation, sleeping + must be enabled either at initialization time or after at least one :ref:`mj_step`. To wit: + + .. code-block:: C + + // this is OK: + mjData* d = mj_makeData(m); // sleeping is enabled at init time + mj_step(m, d); + ... + + // this is also OK: + mjData* d = mj_makeData(m); // sleeping is disabled at init time + mj_step(m, d); + ... + m->opt.enableflags |= mjENABLE_SLEEP; // enable sleeping after at least one step + mj_step(m, d); + + // this is an error: + mjData* d = mj_makeData(m); // sleeping is disabled at init time + m->opt.enableflags |= mjENABLE_SLEEP; // enable sleeping + mj_step(m, d); // undefined behavior, static elements not computed + + +**Violated assumptions** + Sleeping breaks several assumptions that are baked into the core of MuJoCo (and continue to hold if sleeping is + disabled). + + *Pipeline stages*: It is usually guaranteed that no velocity-related quantities will be read before the end of the + position stage and that no force-related quantities will be read before the end of velocity stage. This assumption, + which lies at the heart of the :ref:`mj_step1`/:ref:`mj_step2` split, is violated by the reading of ``qvel``, + ``qfrc_applied`` and ``xfrc_applied`` in :ref:`mj_kinematics`. + + *Compact state*: While the sleep state is notionally given by ``mjData.tree_asleep``, this is a mirage. Once an island + is asleep, the entire subset of position and velocity-dependent quantities in mjData associated with it becomes + a pre-computed latent state that is "waiting for the island to wake up". For this reason, the only way to fully save + and restore the state of a simulation with sleeping elements is to :ref:`copy` the entire mjData + structure. This is also the reason why sleep initialization is only available for the default configuration and not + for keyframes. Note that saving and loading the state using the :ref:`standard tools` remains a valid + operation, merely that sleeping islands will be implicitly woken up. + + +**RK4 integrator** + The RK4 integrator is not currently supported, due to the subtleties of waking inside the sub-steps. + +**Latent bugs** + Sleeping is a new feature (Nov 2025) and may have latent bugs. These bugs may generally come in two varieties: + + - Quantities which could be skipped are instead recomputed. The only observable effect of such a bug would be that + the simulation is slower than it could be. This type of bug can only be diagnosed with detailed profiling. + - Actual bugs. Hopefully these will lead to informative runtime errors, please report any to the development team. + + .. _siCoordinate: Coordinate frames and transformations diff --git a/model/sleep/100_humanoids.xml b/model/sleep/100_humanoids.xml new file mode 100644 index 00000000..73fd2974 --- /dev/null +++ b/model/sleep/100_humanoids.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model/sleep/dominos.xml b/model/sleep/dominos.xml new file mode 100644 index 00000000..b7fdd9b9 --- /dev/null +++ b/model/sleep/dominos.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model/sleep/humanoid.xml b/model/sleep/humanoid.xml new file mode 100644 index 00000000..8e19de97 --- /dev/null +++ b/model/sleep/humanoid.xml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +