Skip to content

Commit 5618666

Browse files
yuvaltassacopybara-github
authored andcommitted
Add body/simple attribute to control simple body optimization.
PiperOrigin-RevId: 942164766 Change-Id: I1e83bb99b6a1955917eb9b2e9bed0dc7b8e6a161
1 parent 4ecfe5c commit 5618666

19 files changed

Lines changed: 205 additions & 10 deletions

doc/XMLreference.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,31 @@ defined. Its body name is automatically defined as "world".
21522152

21532153
See :ref:`implementation notes<siSleep>` for more details.
21542154

2155+
.. _body-simple:
2156+
2157+
:at:`simple`: :at-val:`[false, auto], "auto"`
2158+
Controls the *simple body* optimization. When a body qualifies as "simple", its inertial matrix block in the mass
2159+
matrix is diagonal, representing independent translational and rotational degrees of freedom. The optimization
2160+
omits storing of the zero-valued off-diagonal entries, reducing memory footprint and computation.
2161+
2162+
A body qualifies for this optimization if it satisfies all of the following:
2163+
2164+
- **Inertial frame alignment**: The body's inertial frame coincides with its body frame.
2165+
- **Kinematic root**: The body's parent is either the world body or a static body.
2166+
- **Leaf body**: The body is a leaf node in the kinematic tree (it has no child bodies).
2167+
- **Origin-centered joints**: All joints belonging to this body must reside at the body's origin.
2168+
- **Aligned joint axes**: Any hinge or slide joint axes must be aligned with the local coordinate axes, and at most
2169+
one joint with rotational degrees of freedom (hinge or ball) is permitted.
2170+
- **No inertia-bearing tendons**: The body must not contain sites or geoms used as wrap objects by any tendon that
2171+
has non-zero :ref:`armature<tendon-spatial-armature>`.
2172+
2173+
Setting this attribute to :at-val:`false` disables the optimization for this body. This is necessary for domain
2174+
randomization workflows where model parameters (such as joint/inertial offsets or angles) are perturbed dynamically
2175+
during simulation and updated via :ref:`mj_setConst`. Because a body compiled with the simple optimization active
2176+
cannot dynamically lose its simple state at runtime (which would require reallocation of sparse matrix structures),
2177+
any runtime parameter change that violates the simple conditions will trigger a validation error unless
2178+
``simple="false"`` was explicitly declared in the XML.
2179+
21552180
.. _body-user:
21562181

21572182
:at:`user`: :at-val:`real(nbody_user), "0 0 ..."`

doc/XMLschema.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,9 @@
713713
.. grid-item::
714714
:ref:`sleep<body-sleep>`
715715

716+
.. grid-item::
717+
:ref:`simple<body-simple>`
718+
716719
.. grid-item::
717720
:ref:`user<body-user>`
718721

doc/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ General
1212
- :ref:`mj_encode` now supports encoding of MJB and TXT files.
1313
- :ref:`mj_setConst` now recomputes the ``mjModel.{body,geom,site}_sameframe`` flags, to account for changes in
1414
body/geom/site frames after compilation.
15+
- Added :ref:`body/simple<body-simple>` attribute ("false"/"auto") to disable the *simple body* mass matrix
16+
optimization. This is useful for domain randomization, where model parameters may change post-compilation.
1517
- The :el:`attach` element now supports self-attachment (attaching elements of the current model to itself) by omitting
1618
the :at:`model` attribute. It also supports attaching a frame via the new :at:`frame` attribute, which is mutually
1719
exclusive with :at:`body`.

doc/includes/references.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,7 @@ typedef struct mjsBody_ { // body specification
16611661
mjtByte mocap; // is this a mocap body
16621662
double gravcomp; // gravity compensation
16631663
mjtSleepPolicy sleep; // sleep policy
1664+
mjtByte simple; // simple body optimization (0: false, 1: auto)
16641665
mjDoubleVec* userdata; // user data
16651666
mjtByte explicitinertial; // whether to save the body with explicit inertial clause
16661667
mjsPlugin plugin; // passive force plugin

include/mujoco/mjspec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ typedef struct mjsBody_ { // body specification
279279
mjtByte mocap; // is this a mocap body
280280
double gravcomp; // gravity compensation
281281
mjtSleepPolicy sleep; // sleep policy
282+
mjtByte simple; // simple body optimization (0: false, 1: auto)
282283
mjDoubleVec* userdata; // user data
283284
mjtByte explicitinertial; // whether to save the body with explicit inertial clause
284285
mjsPlugin plugin; // passive force plugin

include/mujoco/mjspecmacro.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
X ( mjtByte, mocap, 1 ) \
115115
X ( double, gravcomp, 1 ) \
116116
X ( mjtSleepPolicy, sleep, 1 ) \
117+
X ( mjtByte, simple, 1 ) \
117118
X ( mjDoubleVec*, userdata, 1 ) \
118119
X ( mjtByte, explicitinertial, 1 ) \
119120
X ( mjsPlugin, plugin, 1 ) \

python/mujoco/introspect/structs.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7391,6 +7391,11 @@
73917391
type=ValueType(name='mjtSleepPolicy'),
73927392
doc='sleep policy',
73937393
),
7394+
StructFieldDecl(
7395+
name='simple',
7396+
type=ValueType(name='mjtByte'),
7397+
doc='simple body optimization (0: false, 1: auto)',
7398+
),
73947399
StructFieldDecl(
73957400
name='userdata',
73967401
type=PointerType(

src/engine/engine_setconst.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,14 @@ void mj_setConst(mjModel* m, mjData* d) {
13241324
// recompute sameframe flags from current model geometry
13251325
setSameframe(m);
13261326

1327+
// error if simple body lost sameframe (user must set simple="false")
1328+
for (int i = 1; i < m->nbody; i++) {
1329+
if (m->body_simple[i] > 0 && m->body_sameframe[i] != mjSAMEFRAME_BODY) {
1330+
mjERROR("body %d is compiled as simple but sameframe no longer holds, "
1331+
"use body/simple='false'", i);
1332+
}
1333+
}
1334+
13271335
// set fixed quantities
13281336
setFixed(m, d);
13291337

src/user/user_init.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ void mjs_defaultBody(mjsBody* body) {
8282
body->ipos[0] = mjNAN;
8383
body->iquat[0] = 1;
8484
body->fullinertia[0] = mjNAN;
85+
86+
// simple optimization: auto
87+
body->simple = 1;
8588
}
8689

8790

src/user/user_model.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,11 @@ void mjCModel::ComputeSparseSizes() {
10021002
bodies_[parentid]->parent &&
10031003
bodies_[parentid]->parent->id == 0 &&
10041004
bodies_[parentid]->dofnum == 0)));
1005+
1006+
// user override: disable simple optimization
1007+
if (!pb->simple) {
1008+
body_simple_pre[i] = 0;
1009+
}
10051010
}
10061011

10071012
// a parent body is never simple (unless world)
@@ -2816,6 +2821,11 @@ void mjCModel::CopyTree(mjModel* m) {
28162821
(m->body_parentid[parentid] == 0 &&
28172822
m->body_dofnum[parentid] == 0)));
28182823

2824+
// user override: disable simple optimization
2825+
if (!pb->simple) {
2826+
m->body_simple[i] = 0;
2827+
}
2828+
28192829
// a parent body is never simple (unless world)
28202830
if (m->body_parentid[i] > 0) {
28212831
m->body_simple[m->body_parentid[i]] = 0;

0 commit comments

Comments
 (0)