Version 2.1.2: Python bindings, OBJ assets support, bugfixes.
PiperOrigin-RevId: 434731612 Change-Id: I0cfda3e7a3d1c72036764986efc252ffa1b8c6b0
This commit is contained in:
+213
-202
@@ -129,10 +129,15 @@ mjrender.h `(source) <https://github.com/deepmind/mujoco/blob/main/include/m
|
||||
This file defines the primitive types and structures needed by the OpenGL renderer.
|
||||
mjui.h `(source) <https://github.com/deepmind/mujoco/blob/main/include/mjui.h>`__
|
||||
This file defines the primitive types and structures needed by the UI framework.
|
||||
mjtnum.h `(source) <https://github.com/deepmind/mujoco/blob/main/include/mjtnum.h>`__
|
||||
Defines MuJoCo's ``mjtNum`` floating-point type to be either ``double`` or ``float``. See :ref:`mjtNum`.
|
||||
mjxmacro.h `(source) <https://github.com/deepmind/mujoco/blob/main/include/mjxmacro.h>`__
|
||||
This file is optional and is not included by mujoco.h. It defines :ref:`X Macros <tyXMacro>` that can
|
||||
automate the mapping of mjModel and mjData into scripting languages, as well as other operations that require
|
||||
accessing all fields of mjModel and mjData. See code sample :ref:`testxml.cc <saTestXML>`.
|
||||
mjexport.h `(source) <https://github.com/deepmind/mujoco/blob/main/include/mjexport.h>`__
|
||||
Macros used for exporting public symbols from the MuJoCo library. This header should not be used directly by client
|
||||
code.
|
||||
glfw3.h
|
||||
This file is optional and is not included by mujoco.h. It is the only header file needed for the GLFW library. See
|
||||
code sample :ref:`simulate.cc <saSimulate>`.
|
||||
@@ -379,9 +384,10 @@ is extracted from the diagnostic fields of mjData. It is a very useful tool for
|
||||
constraint solver algorithms. The outputs of the sensors defined in the model are visualized as a bar graph.
|
||||
|
||||
Note that the profiler shows timing information collected with high-resolution timers. On Windows, depending on the
|
||||
power settings, the OS may reduce the CPU frequency; this is because simulate.cc sleeps most of the time in order to
|
||||
slow down to realtime. This results in inaccurate timings. To avoid this problem, change the Windows power plan so
|
||||
that the minimum processor state is 100%.
|
||||
power settings, the OS may reduce the CPU frequency; this is because `simulate.cc
|
||||
<https://github.com/deepmind/mujoco/blob/main/sample/simulate.cc>`_ sleeps most of the time in order to slow down to
|
||||
realtime. This results in inaccurate timings. To avoid this problem, change the Windows power plan so that the minimum
|
||||
processor state is 100%.
|
||||
|
||||
.. _saRecord:
|
||||
|
||||
@@ -390,11 +396,12 @@ that the minimum processor state is 100%.
|
||||
|
||||
This code sample simulates the passive dynamics of a given model, renders it offscreen, reads the color and depth pixel
|
||||
values, and saves them into a raw data file that can then be converted into a movie file with tools such as ffmpeg. The
|
||||
rendering is simplified compared to simulate.cc because there is no user interaction, visualization options or timing;
|
||||
instead we simply render with the default settings as fast as possible. The dimensions and number of multi-samples for
|
||||
the offscreen buffer are specified in the MuJoCo model, while the simulation duration, frames-per-second to be rendered
|
||||
(usually much less than the physics simulation rate), and output file name are specified as command-line arguments. For
|
||||
example, a 5 second animation at 60 frames per second is created with:
|
||||
rendering is simplified compared to `simulate.cc <https://github.com/deepmind/mujoco/blob/main/sample/simulate.cc>`_
|
||||
because there is no user interaction, visualization options or timing; instead we simply render with the default
|
||||
settings as fast as possible. The dimensions and number of multi-samples for the offscreen buffer are specified in the
|
||||
MuJoCo model, while the simulation duration, frames-per-second to be rendered (usually much less than the physics
|
||||
simulation rate), and output file name are specified as command-line arguments. For example, a 5 second animation at 60
|
||||
frames per second is created with:
|
||||
|
||||
.. code-block:: Shell
|
||||
|
||||
@@ -456,12 +463,12 @@ more accurate. Here ``f`` is one of the functions
|
||||
|
||||
The code sample computes six Jacobian matrices, containing the derivative of each function with respect to its three
|
||||
arguments. The results are stored in the array ``deriv``. All six Jacobian matrices are square, with dimensionality
|
||||
equal to the number of degrees of freedom mjModel.nv. When the model configuration includes quaternion joints,
|
||||
equal to the number of degrees of freedom ``mjModel.nv``. When the model configuration includes quaternion joints,
|
||||
mjData.qpos has larger dimensionality than the other vectors, however the derivative is only defined in the tangent
|
||||
space to the configuration manifold. This is why, when differentiating with respect to the elements of mjData.qpos, we
|
||||
do not directly add ``eps`` but instead use the function :ref:`mju_quatIntegrate`
|
||||
to perturb the quaternion in the tangent space, keeping it normalized. This technique should also be used in any other
|
||||
situation where quaternions need to be perturbed.
|
||||
space to the configuration manifold. This is why, when differentiating with respect to the elements of ``mjData.qpos``,
|
||||
we do not directly add ``eps`` but instead use the function :ref:`mju_quatIntegrate` to perturb the quaternion in the
|
||||
tangent space, keeping it normalized. This technique should also be used in any other situation where quaternions need
|
||||
to be perturbed.
|
||||
|
||||
There are some important subtleties in this code that improve speed as well as accuracy. To speed up the computation,
|
||||
we re-use intermediate results whenever possible. This relies on the skip mechanism described under :ref:`forward
|
||||
@@ -475,9 +482,9 @@ Accuracy depends on the value of ``eps`` which is user-adjustable, as well as th
|
||||
of forward dynamics however, the function evaluation involves an iterative constraint solver, and this must be handled
|
||||
with care. In general, the difference between ``f(x+eps)`` and ``f(x)`` is very small, thus any noise affecting the
|
||||
two function evaluations differently can make the resulting derivatives meaningless. Different warm-starts or
|
||||
different number of solver iterations can act as such noise here. Therefore we fix the warm-start mjData.qacc to a
|
||||
different number of solver iterations can act as such noise here. Therefore we fix the warm-start ``mjData.qacc`` to a
|
||||
value pre-computed at the center point, using ``nwarmup`` extra major iterations to obtain a more accurate warm-start.
|
||||
We also fix the number of solver iterations to ``niter`` and set mjModel.opt.tolerance = 0; this disables the early
|
||||
We also fix the number of solver iterations to ``niter`` and set ``mjModel.opt.tolerance = 0``; this disables the early
|
||||
termination mechanism. Note that the original simulation options are restored in the serial code which advances the
|
||||
state.
|
||||
|
||||
@@ -485,18 +492,19 @@ We emphasize that the above subtleties are not high-order corrections that can b
|
||||
of unilateral constraints, numerical derivatives are hard to compute and there is no shortcut around it; indeed they
|
||||
would not even be defined if it wasn't for our soft-constraint model. Making the constraints softer results in more
|
||||
accurate results. This effect is so strong that in some situations it makes sense to intentionally work with the wrong
|
||||
model, i.e. a model that is softer than desired, so as to obtain more accurate derivatives.
|
||||
model, i.e., a model that is softer than desired, so as to obtain more accurate derivatives.
|
||||
|
||||
.. _saUItools:
|
||||
|
||||
uitools
|
||||
~~~~~~~
|
||||
|
||||
`(uitools.h) <https://github.com/deepmind/mujoco/blob/main/sample/uitools.h>`_
|
||||
`(uitools.c) <https://github.com/deepmind/mujoco/blob/main/sample/uitools.c>`_
|
||||
This is not a stand-alone code sample, but rather a small utility used to hook up the new UI to GLFW. It is used in
|
||||
simulate.cc and can also be used in user projects that involve the new UI. If GLFW is replaced with a different window
|
||||
library, this is the only file that would have to be changed in order to access the UI functionality.
|
||||
`(uitools.h) <https://github.com/deepmind/mujoco/blob/main/sample/uitools.h>`_ `(uitools.c)
|
||||
<https://github.com/deepmind/mujoco/blob/main/sample/uitools.c>`_ This is not a stand-alone code sample, but rather a
|
||||
small utility used to hook up the new UI to GLFW. It is used in `simulate.cc
|
||||
<https://github.com/deepmind/mujoco/blob/main/sample/simulate.cc>`_ and can also be used in user projects that involve
|
||||
the new UI. If GLFW is replaced with a different window library, this is the only file that would have to be changed in
|
||||
order to access the UI functionality.
|
||||
|
||||
.. _Simulation:
|
||||
|
||||
@@ -514,7 +522,7 @@ be discussed later.
|
||||
|
||||
mjModel and mjData should never be allocated directly by the user. Instead they are allocated and initialized by the
|
||||
corresponding API functions. These are very elaborate data structures, containing (arrays of) other structures,
|
||||
pre-allocated data arrays for all intermediate results, as well as an :ref:`internal stack <siStack>`. Our strategy is
|
||||
preallocated data arrays for all intermediate results, as well as an :ref:`internal stack <siStack>`. Our strategy is
|
||||
to allocate all necessary heap memory at the beginning of the simulation, and free it after the simulation is done, so
|
||||
that we never have to call the C memory allocation and deallocation functions during the simulation. This is done for
|
||||
speed, avoidance of memory fragmentation, future GPU portability, and ease of managing the state of the entire
|
||||
@@ -532,7 +540,7 @@ available options are
|
||||
mjModel* m = mj_loadXML("mymodel.xml", NULL, errstr, errstr_sz);
|
||||
|
||||
// option 2: parse and compile XML from virtual file system
|
||||
mjModel* m = mj_loadXML("mymodel.xml", vfs, errstr_sz);
|
||||
mjModel* m = mj_loadXML("mymodel.xml", vfs, errstr, errstr_sz);
|
||||
|
||||
// option 3: load precompiled model from MJB file
|
||||
mjModel* m = mj_loadModel("mymodel.mjb", NULL);
|
||||
@@ -613,12 +621,12 @@ The default (and recommended) way to control the system is to implement a contro
|
||||
mju_scl(d->ctrl, d->qvel, -0.1, m->nv);
|
||||
}
|
||||
|
||||
This illustrates two concepts. First, we are checking if the number of controls mjModel.nu equals the number of dofs
|
||||
mjModel.nv. In general, the same callback may be used with multiple models depending on how the user code is structured,
|
||||
and so it is a good idea to check the model dimensions in the callback. Second, MuJoCo has a library of BLAS-like
|
||||
functions that are very useful; indeed a large part of the code base consists of calling such functions internally. The
|
||||
:ref:`mju_scl` function above scales the velocity vector mjData.qvel by a constant feedback
|
||||
gain and copies the result into the control vector mjData.ctrl. To install this callback, we simply assign it to the
|
||||
This illustrates two concepts. First, we are checking if the number of controls ``mjModel.nu`` equals the number of
|
||||
DoFs ``mjModel.nv``. In general, the same callback may be used with multiple models depending on how the user code is
|
||||
structured, and so it is a good idea to check the model dimensions in the callback. Second, MuJoCo has a library of
|
||||
BLAS-like functions that are very useful; indeed a large part of the code base consists of calling such functions
|
||||
internally. The :ref:`mju_scl` function above scales the velocity vector ``mjData.qvel`` by a constant feedback
|
||||
gain and copies the result into the control vector ``mjData.ctrl``. To install this callback, we simply assign it to the
|
||||
global control callback pointer :ref:`mjcb_control`:
|
||||
|
||||
.. code-block:: C
|
||||
@@ -631,10 +639,10 @@ signal is needed by the simulation pipeline, and as a result we will end up simu
|
||||
damping does not really do justice to the notion of control, and is better implemented as a passive joint property,
|
||||
but these are finer points).
|
||||
|
||||
Instead of relying on a control callback, we could set the control vector mjData.ctrl directly. Alternatively we could
|
||||
set applied forces as explained in :ref:`state and control <siStateControl>`. If we could compute these
|
||||
control-related quantities before mj_step is called, then the simulation loop for the controlled dynamics (without
|
||||
using a control callback) would become
|
||||
Instead of relying on a control callback, we could set the control vector ``mjData.ctrl`` directly. Alternatively we
|
||||
could set applied forces as explained in :ref:`state and control <siStateControl>`. If we could compute these control-
|
||||
related quantities before mj_step is called, then the simulation loop for the controlled dynamics (without using a
|
||||
control callback) would become
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
@@ -646,18 +654,18 @@ using a control callback) would become
|
||||
|
||||
Why would we not be able to compute the controls before mj_step is called? After all, isn't this what causality means?
|
||||
The answer is subtle but important, and has to do with the fact that we are simulating in discrete time. The top-level
|
||||
simulation function mj_step basically does two things: compute the :ref:`forward dynamics <siForward>` in continuous
|
||||
time, and then integrate over a time period specified by mjModel.opt.timestep. Forward dynamics computes the
|
||||
acceleration mjData.qacc at time mjData.time, given the :ref:`state and control <siStateControl>` at time mjData.time.
|
||||
The numerical integrator then advances the state and time to mjData.time + mjModel.opt.timestep. Now, the control is
|
||||
required to be a function of the state at time mjData.time. However a general feedback controller can be a very complex
|
||||
function, depending on various features of the state - in particular all the features computed by MuJoCo as intermediate
|
||||
results of the simulation. These may include contacts, Jacobians, passive forces. None of these quantities are available
|
||||
before mj_step is called (or rather, they are available but outdated by one time step). In contrast, when mj_step calls
|
||||
our control callback, it does so as late in the computation as possible - namely after all the intermediate results
|
||||
dependent on the state but not on the control have been computed.
|
||||
simulation function ``mj_step`` basically does two things: compute the :ref:`forward dynamics <siForward>` in continuous
|
||||
time, and then integrate over a time period specified by ``mjModel.opt.timestep``. Forward dynamics computes the
|
||||
acceleration ``mjData.qacc`` at time ``mjData.time``, given the :ref:`state and control <siStateControl>` at time
|
||||
``mjData.time``. The numerical integrator then advances the state and time to ``mjData.time + mjModel.opt.timestep``.
|
||||
Now, the control is required to be a function of the state at time ``mjData.time``. However a general feedback
|
||||
controller can be a very complex function, depending on various features of the state - in particular all the features
|
||||
computed by MuJoCo as intermediate results of the simulation. These may include contacts, Jacobians, passive forces.
|
||||
None of these quantities are available before ``mj_step`` is called (or rather, they are available but outdated by one
|
||||
time step). In contrast, when ``mj_step`` calls our control callback, it does so as late in the computation as possible
|
||||
- namely after all the intermediate results dependent on the state but not on the control have been computed.
|
||||
|
||||
The same effect can be achieved without using a control callback. This is done by breaking mj_step in two parts:
|
||||
The same effect can be achieved without using a control callback. This is done by breaking ``mj_step`` in two parts:
|
||||
before the control is needed, and after the control is needed. The simulation loop now becomes
|
||||
|
||||
.. code-block:: C
|
||||
@@ -672,7 +680,7 @@ before the control is needed, and after the control is needed. The simulation lo
|
||||
There is one complication however: this only works with Euler integration. The Runge-Kutta integrator (as well as other
|
||||
advanced integrators we plan to implement) need to evaluate the entire dynamics including the feedback control law
|
||||
multiple times per step, which can only be done using a control callback. But with Euler integration, the above
|
||||
separation of mj_step into :ref:`mj_step1` and :ref:`mj_step2` is sufficient to provide the control law with the
|
||||
separation of ``mj_step`` into :ref:`mj_step1` and :ref:`mj_step2` is sufficient to provide the control law with the
|
||||
intermediate results of the computation.
|
||||
|
||||
To make the above discussion more clear, we provide the internal implementation of mj_step, mj_step1 and mj_step2,
|
||||
@@ -705,7 +713,7 @@ The control callback (if any) is called from within the forward dynamics functio
|
||||
Next we show the implementation of the two-part stepping approach, although the specifics will make sense only after
|
||||
we explain the :ref:`forward dynamics <siForward>` later. Note that the control callback is now called directly, since
|
||||
we have essentially unpacked the forward dynamics function. Note also that we always call the Euler integrator in
|
||||
mj_step2 regardless of the setting of mjModel.opt.integrator.
|
||||
mj_step2 regardless of the setting of ``mjModel.opt.integrator``.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
@@ -767,19 +775,19 @@ The state vector in MuJoCo is:
|
||||
|
||||
For a second-order dynamical system the state contains only position and velocity, however MuJoCo can also model
|
||||
actuators (such as cylinders and biological muscles) that have their own activation states assembled in the vector
|
||||
mjData.act. While the physics model is time-invariant, user-defined control laws may be time-varying; in particular
|
||||
control laws obtained from trajectory optimizers would normally be indexed by mjData.time.
|
||||
``mjData.act``. While the physics model is time-invariant, user-defined control laws may be time-varying; in particular
|
||||
control laws obtained from trajectory optimizers would normally be indexed by ``mjData.time``.
|
||||
|
||||
The reason for the "official" caveat above is because user callbacks may store additional state variables that change
|
||||
over time and affect the callback outputs; indeed the field mjData.userdata exists mostly for that purpose. Other
|
||||
state-like quantities that are part of mjData and are treated as inputs by forward dynamics are mjData.mocap_pos and
|
||||
mjData.mocap_quat. These quantities are unusual in that they are meant to change at each time step (normally driven by
|
||||
a motion capture device), however this change is implemented by the user, while the simulator treats them as
|
||||
constants. In that sense they are no different from all the constants in mjModel, or the function callback pointers
|
||||
set by the user: such constants affect the computation, but are not part of the state vector of a dynamical system.
|
||||
over time and affect the callback outputs; indeed the field ``mjData.userdata`` exists mostly for that purpose. Other
|
||||
state-like quantities that are part of mjData and are treated as inputs by forward dynamics are ``mjData.mocap_pos`` and
|
||||
mjData.mocap_quat. These quantities are unusual in that they are meant to change at each time step (normally driven by a
|
||||
motion capture device), however this change is implemented by the user, while the simulator treats them as constants. In
|
||||
that sense they are no different from all the constants in mjModel, or the function callback pointers set by the user:
|
||||
such constants affect the computation, but are not part of the state vector of a dynamical system.
|
||||
|
||||
The warm-start mechanism in the constraint solver effectively introduces another state variable. This mechanism uses
|
||||
the output of forward dynamics from the previous time step, namely the acceleration vector mjData.qacc, to estimate
|
||||
the output of forward dynamics from the previous time step, namely the acceleration vector ``mjData.qacc``, to estimate
|
||||
the current constraint forces via inverse dynamics. This estimate then initializes the optimization algorithm in the
|
||||
solver. If this algorithm runs until convergence the warm-start will affect the speed of convergence but not the final
|
||||
solution (since the underlying optimization problem is convex and does not have local minima), but in practice the
|
||||
@@ -791,8 +799,8 @@ Next we turn to the controls and applied forces. The control vector in MuJoCo is
|
||||
|
||||
u = (mjData.ctrl, mjData.qfrc_applied, mjData.xfrc_applied)
|
||||
|
||||
These quantities specify control signals (mjData.ctrl) for the actuators defined in the model, or directly apply
|
||||
forces and torques specified in joint space (mjData.qfrc_applied) or in Cartesian space (mjData.xfrc_applied).
|
||||
These quantities specify control signals (``mjData.ctrl``) for the actuators defined in the model, or directly apply
|
||||
forces and torques specified in joint space (``mjData.qfrc_applied``) or in Cartesian space (mjData.xfrc_applied).
|
||||
|
||||
Finally, calling mj_forward which corresponds to the abstract dynamics function ``f(t,x,u)`` computes the
|
||||
time-derivative of the state vector. The corresponding fields of mjData are
|
||||
@@ -801,8 +809,8 @@ time-derivative of the state vector. The corresponding fields of mjData are
|
||||
|
||||
dx/dt = f(t,x,u) = (1, mjData.qvel, mjData.qacc, mjData.act_dot)
|
||||
|
||||
In the presence of quaternions (i.e. when free or ball joints are used), the position vector mjData.qpos has higher
|
||||
dimensionality than the velocity vector mjData.qvel and so this is not a simple time-derivative in the sense of
|
||||
In the presence of quaternions (i.e., when free or ball joints are used), the position vector ``mjData.qpos`` has higher
|
||||
dimensionality than the velocity vector ``mjData.qvel`` and so this is not a simple time-derivative in the sense of
|
||||
scalars, but instead takes quaternion algebra into account.
|
||||
|
||||
To illustrate how the simulation state can be manipulated, suppose we have two mjData pointers src and dst
|
||||
@@ -851,9 +859,9 @@ If the user has installed a control callback :ref:`mjcb_control` different from
|
||||
pointer), the user callback would be expected to set some of the above fields to non-zero. Note that MuJoCo will not
|
||||
clear these controls/forces at the end of the time step. This is the responsibility of the user.
|
||||
|
||||
Also relevant in this context is the function :ref:`mj_resetData`. It sets mjData.qpos equal to the model reference
|
||||
configuration mjModel.qpos0; mjData.mocap_pos and mjData.mocap_quat equal to the corresponding fixed body poses from
|
||||
mjModel; and all other state and control variables to 0.
|
||||
Also relevant in this context is the function :ref:`mj_resetData`. It sets ``mjData.qpos`` equal to the model reference
|
||||
configuration ``mjModel.qpos0``, ``mjData.mocap_pos`` and ``mjData.mocap_quat`` equal to the corresponding fixed body
|
||||
poses from mjModel; and all other state and control variables to 0.
|
||||
|
||||
.. _siForward:
|
||||
|
||||
@@ -861,7 +869,7 @@ Forward dynamics
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The goal of forward dynamics is to compute the time-derivative of the state, namely the acceleration vector
|
||||
mjData.qacc and the activation time-derivative mjData.act_dot. Along the way it computes everything else needed to
|
||||
mjData.qacc and the activation time-derivative ``mjData.act_dot``. Along the way it computes everything else needed to
|
||||
simulate the dynamics, including active contacts and other constraints, joint-space inertia and its LTDL
|
||||
decomposition, constraint forces, sensor data and so on. All these intermediate results are available in mjData and
|
||||
can be used in custom computations. As illustrated in the :ref:`simulation loop <siSimulation>` section above, the
|
||||
@@ -951,31 +959,31 @@ inverse dynamics (with the acceleration computed at the previous time step) to w
|
||||
solver in forward dynamics.
|
||||
|
||||
The inputs to inverse dynamics are the same as the state vector in forward dynamics as illustrated in :ref:`state and
|
||||
control <siStateControl>`, but without mjData.act and mjData.time. Assuming no callbacks that depend on user-defined
|
||||
state variables, the inputs to inverse dynamics are the following fields of mjData:
|
||||
control <siStateControl>`, but without ``mjData.act`` and ``mjData.time``. Assuming no callbacks that depend on user-
|
||||
defined state variables, the inputs to inverse dynamics are the following fields of mjData:
|
||||
|
||||
::
|
||||
|
||||
(mjData.qpos, mjData.qvel, mjData.qacc, mjData.mocap_pos, mjData.mocap_quat)
|
||||
|
||||
The main output is mjData.qfrc_inverse. This is the force that must have acted on the system in order to achieve the
|
||||
observed acceleration mjData.qacc. If forward dynamics were to be computed exactly, by running the iterative solver to
|
||||
full convergence, we would have
|
||||
The main output is ``mjData.qfrc_inverse``. This is the force that must have acted on the system in order to achieve the
|
||||
observed acceleration ``mjData.qacc``. If forward dynamics were to be computed exactly, by running the iterative solver
|
||||
to full convergence, we would have
|
||||
|
||||
::
|
||||
|
||||
mjData.qfrc_inverse = mjData.qfrc_applied + Jacobian'*mjData.xfrc_applied + mjData.qfrc_actuator
|
||||
|
||||
where mjData.qfrc_actuator is the joint-space force produced by the actuators and the Jacobian is the mapping from
|
||||
joint to Cartesian space. When the "fwdinv" flag in mjModel.opt.enableflags is set, the above identity is used to
|
||||
monitor the quality of the forward dynamics solution. In particular, the two components of mjData.solver_fwdinv are
|
||||
where ``mjData.qfrc_actuator`` is the joint-space force produced by the actuators and the Jacobian is the mapping from
|
||||
joint to Cartesian space. When the "fwdinv" flag in ``mjModel.opt.enableflags`` is set, the above identity is used to
|
||||
monitor the quality of the forward dynamics solution. In particular, the two components of ``mjData.solver_fwdinv`` are
|
||||
set to the L2 norm of the difference between the forward and inverse solutions, in terms of joint forces and
|
||||
constraint forces respectively.
|
||||
|
||||
Similar to forward dynamics, mj_inverse internally calls :ref:`mj_inverseSkip` with skip arguments (mjSTAGE_NONE, 0).
|
||||
The skip mechanism is the same as in forward dynamics, and can be used to speed up structured sampling. The result
|
||||
mjData.qfrc_inverse is obtained by using the Recursive Newton-Euler algorithm to compute the net force acting on the
|
||||
system, and then subtracting from it all internal forces.
|
||||
Similar to forward dynamics, ``mj_inverse`` internally calls :ref:`mj_inverseSkip` with skip arguments
|
||||
``(mjSTAGE_NONE, 0)``. The skip mechanism is the same as in forward dynamics, and can be used to speed up structured
|
||||
sampling. The result ``mjData.qfrc_inverse`` is obtained by using the Recursive Newton-Euler algorithm to compute the
|
||||
net force acting on the system, and then subtracting from it all internal forces.
|
||||
|
||||
Inverse dynamics can be used as an analytical tool when experimental data are available. This is common in robotics as
|
||||
well as biomechanics. It can also be used to compute the joint torques needed to drive the system along a given
|
||||
@@ -1073,7 +1081,7 @@ Model changes
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -1082,16 +1090,16 @@ because that may result in incorrect sizes or indexing. This rule does not hold
|
||||
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_rgb which is texture data represented as mjtByte.
|
||||
features. The only exception here is ``mjModel.tex_rgb`` which is texture data represented as mjtByte.
|
||||
|
||||
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
|
||||
corresponding upload function: ``mjr_uploadTexture``, ``mjr_uploadMesh``, ``mjr_uploadHField``. Otherwise the data used for
|
||||
simulation and for rendering will no longer be consistent.
|
||||
|
||||
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.
|
||||
corresponding to precomputed quantities when the model is in the reference configuration ``mjModel.qpos0``.
|
||||
|
||||
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
|
||||
@@ -1123,17 +1131,17 @@ essential to keep it in mind at all times. All MuJoCo utility functions that ope
|
||||
difference between row-major and column-major formats.
|
||||
|
||||
When possible, MuJoCo exploits sparsity. This can make all the difference between O(N) and O(N^3) scaling. The inertia
|
||||
matrix mjData.qM and its LTDL factorization mjData.qLD are always represented as sparse, using a custom indexing
|
||||
format designed for matrices that correspond to tree topology. The functions :ref:`mj_factorM`, :ref:`mj_solveM`,
|
||||
:ref:`mj_solveM2` and :ref:`mj_mulM` are used for sparse factorization, substitution and matrix-vector multiplication.
|
||||
The user can also convert these matrices to dense format with the function :ref:`mj_fullM` although MuJoCo never does
|
||||
that internally.
|
||||
matrix ``mjData.qM`` and its LTDL factorization ``mjData.qLD`` are always represented as sparse, using a custom
|
||||
indexing format designed for matrices that correspond to tree topology. The functions :ref:`mj_factorM`,
|
||||
:ref:`mj_solveM`, :ref:`mj_solveM2` and :ref:`mj_mulM` are used for sparse factorization, substitution and
|
||||
matrix-vector multiplication. The user can also convert these matrices to dense format with the function
|
||||
:ref:`mj_fullM` although MuJoCo never does that internally.
|
||||
|
||||
The constraint Jacobian matrix mjData.efc_J is represented as sparse whenever the sparse Jacobian option is enabled.
|
||||
The function :ref:`mj_isSparse` can be used to determine if sparse format is currently in use. In that case the
|
||||
transposed Jacobian mjData.efc_JT is also computed, and the inverse constraint inertia mjData.efc_AR becomes sparse.
|
||||
Sparse matrices are stored in the compressed sparse row (CSR) format. For a generic matrix A with dimensionality
|
||||
m-by-n, this format is:
|
||||
The constraint Jacobian matrix ``mjData.efc_J`` is represented as sparse whenever the sparse Jacobian option is
|
||||
enabled. The function :ref:`mj_isSparse` can be used to determine if sparse format is currently in use. In that case
|
||||
the transposed Jacobian ``mjData.efc_JT`` is also computed, and the inverse constraint inertia ``mjData.efc_AR``
|
||||
becomes sparse. Sparse matrices are stored in the compressed sparse row (CSR) format. For a generic matrix A with
|
||||
dimensionality m-by-n, this format is:
|
||||
|
||||
======== ====== ============================================
|
||||
Variable Size Meaning
|
||||
@@ -1163,13 +1171,13 @@ translational component. We do not provide utility functions for working with th
|
||||
scope here. See Roy Featherstone's webpage on `Spatial Algebra <http://royfeatherstone.org/spatial/>`__. The unusual
|
||||
order (rotation before translation) is based on this material, and was apparently standard convention in the past.
|
||||
|
||||
The data structures mjModel and mjData contain many pointers to pre-allocated buffers. The constructors of these data
|
||||
structures (mj_makeModel and mj_makeData) allocate one large buffer, namely mjModel.buffer and mjData.buffer, and then
|
||||
partition it and set all the other pointers in it. mjData also contains a stack outside this main buffer, as discussed
|
||||
below. Even if two pointers appear one after the other, say mjData.qpos and mjData.qvel, do not assume that the data
|
||||
arrays are contiguous and there is no gap between them. The constructors implement byte-alignment for each data array,
|
||||
and skip bytes when necessary. So if you want to copy mjData.qpos and mjData.qvel, the correct way to do it is the
|
||||
hard way:
|
||||
The data structures mjModel and mjData contain many pointers to preallocated buffers. The constructors of these data
|
||||
structures (mj_makeModel and mj_makeData) allocate one large buffer, namely ``mjModel.buffer`` and ``mjData.buffer``,
|
||||
and then partition it and set all the other pointers in it. mjData also contains a stack outside this main buffer, as
|
||||
discussed below. Even if two pointers appear one after the other, say ``mjData.qpos`` and ``mjData.qvel``, do not
|
||||
assume that the data arrays are contiguous and there is no gap between them. The constructors implement byte-alignment
|
||||
for each data array, and skip bytes when necessary. So if you want to copy ``mjData.qpos`` and ``mjData.qvel``, the
|
||||
correct way to do it is the hard way:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
@@ -1180,10 +1188,10 @@ hard way:
|
||||
// DO NOT do this, there may be padding at the end of d->qpos
|
||||
mju_copy(myqposqvel, d->qpos, m->nq + m->nv);
|
||||
|
||||
The :ref:`X Macros <tyXMacro>` defined in the optional header file mjxmacro.h can be used to automate allocation of data
|
||||
structure that match mjModel and mjData, for example when writing a MuJoCo wrapper for a scripting language. In the code
|
||||
sample :ref:`testxml.cc <saTestXML>` we use these unusual macros to compare all data arrays from two instances of
|
||||
mjModel and find the one with the largest difference. Apparently X Macros were invented in the 1960's for assembly
|
||||
The :ref:`X Macros <tyXMacro>` defined in the optional header file ``mjxmacro.h`` can be used to automate allocation of
|
||||
data structure that match mjModel and mjData, for example when writing a MuJoCo wrapper for a scripting language. In
|
||||
the code sample :ref:`testxml.cc <saTestXML>` we use these unusual macros to compare all data arrays from two instances
|
||||
of mjModel and find the one with the largest difference. Apparently X Macros were invented in the 1960's for assembly
|
||||
language, and remain a great idea.
|
||||
|
||||
.. _siStack:
|
||||
@@ -1191,23 +1199,23 @@ language, and remain a great idea.
|
||||
Internal stack
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
MuJoCo allocates and manages its own stack of mjtNums. mjData.stack is the pointer to the pre-allocated memory buffer.
|
||||
mjData.nstack is the maximum number of mjtNums that the stack can hold, as determined by the :at:`nstack` attribute of
|
||||
the :ref:`size <size>` element in MJCF. mjData.pstack is the first available address in the stack; this is our custom
|
||||
stack pointer.
|
||||
MuJoCo allocates and manages its own stack of mjtNums. ``mjData.stack`` is the pointer to the preallocated memory
|
||||
buffer. ``mjData.nstack`` is the maximum number of mjtNums that the stack can hold, as determined by the :at:`nstack`
|
||||
attribute of the :ref:`size <size>` element in MJCF. ``mjData.pstack`` is the first available address in the stack;
|
||||
this is our custom stack pointer.
|
||||
|
||||
Most top-level MuJoCo functions allocate space on the stack, use it for internal computations, and then deallocate it.
|
||||
They cannot do this with the regular C stack because the allocation size is determined dynamically at runtime. And
|
||||
calling the heap memory management functions would be inefficient and result in fragmentation - thus a custom stack.
|
||||
When any MuJoCo function is called, upon return the value of mjData.pstack is the same. The only exception is the
|
||||
function :ref:`mj_resetData` and its variants: they set mjData.pstack = 0. Note that this function is called
|
||||
internally when an instability is detected in mj_step, mj_step1 and mj_step2. So if user functions take advantage of
|
||||
the custom stack (as they should), this needs to be done in-between MuJoCo calls that have the potential to reset the
|
||||
simulation.
|
||||
When any MuJoCo function is called, upon return the value of ``mjData.pstack`` is the same. The only exception is the
|
||||
function :ref:`mj_resetData` and its variants: they set ``mjData.pstack = 0``. Note that this function is called
|
||||
internally when an instability is detected in ``mj_step``, ``mj_step1`` and ``mj_step2``. So if user functions take
|
||||
advantage of the custom stack (as they should), this needs to be done in-between MuJoCo calls that have the potential
|
||||
to reset the simulation.
|
||||
|
||||
Below is the general template for using the custom stack in user code. This assumes that mjData\* d is defined in the
|
||||
scope. If not, saving and restoring the stack pointer should be done manually instead of using the :ref:`mjMARKSTACK`
|
||||
and :ref:`mjFREESTACK` macros.
|
||||
Below is the general template for using the custom stack in user code. This assumes that ``mjData\* d`` is defined in
|
||||
the scope. If not, saving and restoring the stack pointer should be done manually instead of using the
|
||||
:ref:`mjMARKSTACK` and :ref:`mjFREESTACK` macros.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
@@ -1289,20 +1297,20 @@ in the diagnostics section at the beginning of mjData.
|
||||
|
||||
When the simulator encounters a situation that is not a terminal error but is nevertheless suspicious and likely to
|
||||
result in inaccurate numerical results, it triggers a warning. There are several possible warning types, indexed by
|
||||
the enum type :ref:`mjtWarning`. The array mjData.warning contains one :ref:`mjWarningStat` data structure per warning
|
||||
type, indicating how many times each warning type has been triggered since the last reset and any information about
|
||||
the warning (usually the index of the problematic model element). The counters are cleared upon reset. When a warning
|
||||
of a given type is first triggered, the warning text is also printed by mju_warning as documented in :ref:`error and
|
||||
memory <siError>` above. All this is done by the function :ref:`mj_warning` which the simulator calls internally when
|
||||
it encounters a warning. The user can also call this function directly to emulate a warning.
|
||||
the enum type :ref:`mjtWarning`. The array ``mjData.warning`` contains one :ref:`mjWarningStat` data structure per
|
||||
warning type, indicating how many times each warning type has been triggered since the last reset and any information
|
||||
about the warning (usually the index of the problematic model element). The counters are cleared upon reset. When a
|
||||
warning of a given type is first triggered, the warning text is also printed by mju_warning as documented in
|
||||
:ref:`error and memory <siError>` above. All this is done by the function :ref:`mj_warning` which the simulator calls
|
||||
internally when it encounters a warning. The user can also call this function directly to emulate a warning.
|
||||
|
||||
When a model needs to be optimized for high-speed simulation, it is important to know where in the pipeline the CPU
|
||||
time is spent. This can in turn suggest which parts of the model to simplify or how to design the user application.
|
||||
MuJoCo provides an extensive profiling mechanism. It involves multiple timers indexed by the enum type
|
||||
:ref:`mjtTimer`. Each timer corresponds to a top-level API function, or to a component of such a function. Similar to
|
||||
warnings, timer information accumulates and is only cleared on reset. The array mjData.timer contains one
|
||||
:ref:`mjTimerStat` data structure per timer. The average duration per call for a given timer (corresponding to mj_step
|
||||
in the example below) can be computed as:
|
||||
warnings, timer information accumulates and is only cleared on reset. The array ``mjData.timer`` contains one
|
||||
:ref:`mjTimerStat` data structure per timer. The average duration per call for a given timer (corresponding to
|
||||
``mj_step`` in the example below) can be computed as:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
@@ -1314,43 +1322,46 @@ to implement high-resolution timers in C without bringing in additional dependen
|
||||
does not need timing, and in that case there is no reason to call timing functions.
|
||||
|
||||
One part of the simulation pipeline that needs to be monitored closely is the iterative constraint solver. The
|
||||
simplest diagnostic here is mjData.solver_iter which shows how many iterations the solver took on the last call to
|
||||
mj_step or mj_forward. Note that the solver has tolerance parameters for early termination, so this number is usually
|
||||
smaller than the maximum number of iterations allowed. The array mjData.solver contains one :ref:`mjSolverStat` data
|
||||
structure per iteration of the constraint solver, with information about the constraint state and line search.
|
||||
simplest diagnostic here is ``mjData.solver_iter`` which shows how many iterations the solver took on the last call to
|
||||
mj_step or ``mj_forward``. Note that the solver has tolerance parameters for early termination, so this number is
|
||||
usually smaller than the maximum number of iterations allowed. The array ``mjData.solver`` contains one
|
||||
:ref:`mjSolverStat` data structure per iteration of the constraint solver, with information about the constraint state
|
||||
and line search.
|
||||
|
||||
When the option :at:`fwdinv` is enabled in mjModel.opt.enableflags, the field mjData.fwdinv is also populated. It
|
||||
contains the difference between the forward and inverse dynamics, in terms of generalized forces and constraint
|
||||
When the option :at:`fwdinv` is enabled in ``mjModel.opt.enableflags``, the field ``mjData.fwdinv`` is also populated.
|
||||
It contains the difference between the forward and inverse dynamics, in terms of generalized forces and constraint
|
||||
forces. Recall that that the inverse dynamics use analytical formulas and are always exact, thus any discrepancy is
|
||||
due to poor convergence of the iterative solver in the forward dynamics. The numbers in mjData.solver near termination
|
||||
have similar order-of-magnitude as the numbers in mjData.fwdinv, but nevertheless these are two different diagnostics.
|
||||
due to poor convergence of the iterative solver in the forward dynamics. The numbers in ``mjData.solver`` near
|
||||
termination have similar order-of-magnitude as the numbers in ``mjData.fwdinv``, but nevertheless these are two
|
||||
different diagnostics.
|
||||
|
||||
Since MuJoCo's runtime works with compiled models, memory is preallocated when a model is compiled or loaded. Recall the
|
||||
:ref:`size <size>` element in MJCF, which has the attributes :at:`njmax`, :at:`nconmax` and :at:`nstack`. They determine
|
||||
the maximum number of scalar constraints that can be active simultaneously, the maximum number of contact points that
|
||||
can be included in mjData.contact, and the size of the internal stack. How is the user supposed to know what the
|
||||
can be included in ``mjData.contact``, and the size of the internal stack. How is the user supposed to know what the
|
||||
appropriate settings are? If there were a reliable recipe we would have implemented it in the compiler, but there isn't
|
||||
one. The theoretical worst-case, namely all geoms contacting all other geoms, calls for huge allocation which is almost
|
||||
never needed in practice. So our approach is to provide default settings in MJCF which are sufficient for most models,
|
||||
and allow the user to adjust them manually with the above attributes. If the simulator runs out of stack space at
|
||||
runtime it will trigger an error. If it runs out of space for contacts or scalar constraints, it will trigger a warning
|
||||
and omit the contacts and constraints that do not fit in the allocated buffers. When such errors or warnings are
|
||||
triggered, the user should adjust the sizes. The fields mjData.maxuse_stack, mjData.maxuse_con, mjData.maxuse_efc are
|
||||
designed to help with this adjustment. They keep track of the maximum stack allocation, number of contacts and number of
|
||||
scalar constraints respectively since the last reset. So one strategy is to make very large allocation, then monitor
|
||||
these maxuse_XXX statistics during typical simulations, and use them to reduce the allocation. Of course modern
|
||||
computers have so much memory that most users will not bother with such adjustment once they get rid of the
|
||||
out-of-memory errors and warnings, but nevertheless we provide this mechanism for the perfectionist.
|
||||
triggered, the user should adjust the sizes. The fields ``mjData.maxuse_stack``, ``mjData.maxuse_con``,
|
||||
``mjData.maxuse_efc`` are designed to help with this adjustment. They keep track of the maximum stack allocation,
|
||||
number of contacts and number of scalar constraints respectively since the last reset. So one strategy is to make very
|
||||
large allocation, then monitor these ``maxuse_XXX`` statistics during typical simulations, and use them to reduce the
|
||||
allocation. Of course modern computers have so much memory that most users will not bother with such adjustment once
|
||||
they get rid of the out-of-memory errors and warnings, but nevertheless we provide this mechanism for the
|
||||
perfectionist.
|
||||
|
||||
The kinetic and potential energy are computed and stored in mjData.energy when the corresponding flag in
|
||||
mjModel.opt.enableflags is set. This can be used as another diagnostic. In general, simulation instability is
|
||||
The kinetic and potential energy are computed and stored in ``mjData.energy`` when the corresponding flag in
|
||||
``mjModel.opt.enableflags`` is set. This can be used as another diagnostic. In general, simulation instability is
|
||||
associated with increasing energy. In some special cases (when all unilateral constraints, actuators and dissipative
|
||||
forces are disabled) the underlying physical system is energy-conserving. In that case any temporal fluctuations in
|
||||
the total energy indicate inaccuracies in numerical integration. For such systems the Runge-Kutta integrator has much
|
||||
better performance than the default semi-implicit Euler integrator.
|
||||
|
||||
Finally, the user can implement additional diagnostics as needed. Two examples were provided in the code samples
|
||||
testxml.cc and derivative.cc, where we computed model mismatches after save and load, and assessed the accuracy of the
|
||||
``testxml.cc`` and ``derivative.cc``, where we computed model mismatches after save and load, and assessed the accuracy of the
|
||||
numerical derivatives respectively. Key to such diagnostics is to implement two different algorithms or simulation
|
||||
paths that compute the same quantity, and compare the results numerically. This type of sanity check is essential when
|
||||
dealing with complex dynamical systems where we do not really know what the numerical output should be; if we knew
|
||||
@@ -1365,30 +1376,30 @@ The derivative of any vector function with respect to its vector argument is cal
|
||||
in multi-joint kinematics and dynamics, it refers to the derivative of some spatial quantity as a function of the
|
||||
system configuration. In that case the Jacobian is also a linear map that operates on vectors in the (co)tangent space
|
||||
to the configuration manifold - such as velocities, momenta, accelerations, forces. One caveat here is that the system
|
||||
configuration encoded in mjData.qpos has dimensionality mjModel.nq, while the tangent space has dimensionality
|
||||
mjModel.nv, and the latter is smaller when quaternion joints are present. So the size of the Jacobian matrix is
|
||||
N-by-mjModel.nv where N is the dimensionality of the spatial quantity being differentiated.
|
||||
configuration encoded in ``mjData.qpos`` has dimensionality ``mjModel.nq``, while the tangent space has dimensionality
|
||||
``mjModel.nv``, and the latter is smaller when quaternion joints are present. So the size of the Jacobian matrix is
|
||||
N-by-``mjModel.nv`` where N is the dimensionality of the spatial quantity being differentiated.
|
||||
|
||||
MuJoCo can differentiate analytically many spatial quantities. These include tendon lengths, actuator transmission
|
||||
lengths, end-effector poses, contact and other constraint violations. In the case of tendons and actuator
|
||||
transmissions the corresponding quantities are mjData.ten_moment and mjData.actuator_moment; we call them moment arms
|
||||
but mathematically they are Jacobians. The Jacobian matrix of all scalar constraint violations is stored in
|
||||
mjData.efc_J. Note that we are talking about constraint violations rather than the constraints themselves. This is
|
||||
because constraint violations have units of length, i.e. they are spatial quantities that we can differentiate.
|
||||
transmissions the corresponding quantities are ``mjData.ten_moment`` and ``mjData.actuator_moment``; we call them
|
||||
moment arms but mathematically they are Jacobians. The Jacobian matrix of all scalar constraint violations is stored in
|
||||
``mjData.efc_J``. Note that we are talking about constraint violations rather than the constraints themselves. This is
|
||||
because constraint violations have units of length, i.e., they are spatial quantities that we can differentiate.
|
||||
Constraints are more abstract entities and it is not clear what it means to differentiate them.
|
||||
|
||||
Beyond these automatically-computed Jacobians, we provide support functions allowing the user to compute additional
|
||||
Jacobians on demand. The main function for doing this is :ref:`mj_jac`. It is given a 3D point and a MuJoCo body to
|
||||
which this point is considered to be attached. mj_jac then computes both the translational and rotational Jacobians,
|
||||
which tell us how a spatial frame anchored at the given point will translate and rotate if we make a small change to
|
||||
the kinematic configuration. More precisely, the Jacobian maps joint velocities to end-effector velocities, while the
|
||||
transpose of the Jacobian maps end-effector forces to joint forces. There are also several other mj_jacXXX functions;
|
||||
these are convenience functions that call the main mj_jac function with different points of interest - such as a body
|
||||
center of mass, geom center etc.
|
||||
which this point is considered to be attached. ``mj_jac`` then computes both the translational and rotational
|
||||
Jacobians, which tell us how a spatial frame anchored at the given point will translate and rotate if we make a small
|
||||
change to the kinematic configuration. More precisely, the Jacobian maps joint velocities to end-effector velocities,
|
||||
while the transpose of the Jacobian maps end-effector forces to joint forces. There are also several other
|
||||
``mj_jacXXX`` functions; these are convenience functions that call the main ``mj_jac`` function with different points
|
||||
of interest - such as a body center of mass, geom center etc.
|
||||
|
||||
The ability to compute end-effector Jacobians exactly and efficiently is a key advantage of working in joint
|
||||
coordinates. Such Jacobians are the foundation of many control schemes that map end-effector errors to actuator
|
||||
commands suitable for suppressing those errors. The computation of end-effector Jacobians in MuJoCo via the mj_jac
|
||||
commands suitable for suppressing those errors. The computation of end-effector Jacobians in MuJoCo via the ``mj_jac``
|
||||
function is essentially free in terms of CPU cost; so do not hesitate to use this function.
|
||||
|
||||
.. _siContact:
|
||||
@@ -1399,7 +1410,7 @@ Contacts
|
||||
Collision detection and solving for contact forces were explained in detail in the :doc:`computation` chapter. Here we
|
||||
further clarify contact processing from a programming perspective.
|
||||
|
||||
The collision detection stage finds contacts between geoms, and records them in the array mjData.contact of
|
||||
The collision detection stage finds contacts between geoms, and records them in the array ``mjData.contact`` of
|
||||
:ref:`mjContact` data structures. They are sorted such that multiple contacts between the same pair of bodies are
|
||||
contiguous (note that one body can have multiple geoms attached to it), and the body pairs themselves are sorted such
|
||||
that the first body acts as the major index and the second body as the minor index. Not all detected contacts are
|
||||
@@ -1408,26 +1419,26 @@ mjContact.efc_address is the address in the list of active scalar constraints. R
|
||||
:at:`gap` attribute of :ref:`geom <geom>`, as well as certain kinds of internal processing that use virtual contacts
|
||||
for intermediate computations.
|
||||
|
||||
The list mjData.contact is generated by the position stage of both forward and inverse dynamics. This is done
|
||||
The list ``mjData.contact`` is generated by the position stage of both forward and inverse dynamics. This is done
|
||||
automatically. However the user can override the internal collision detection functions, for example to implement
|
||||
non-convex mesh collisions, or to replace some of the convex collision functions we use with geom-specific primitives
|
||||
beyond the ones provided by MuJoCo. The global 2D array :ref:`mjCOLLISIONFUNC` contains the collision function pointer
|
||||
for each pair of geom types (in the upper-left triangle). To replace them, simply set these pointers to your
|
||||
functions. The collision function type is :ref:`mjfCollision`. When user collision functions detect contacts, they
|
||||
should construct an mjvContact structure for each contact and then call the function :ref:`mj_addContact` to add that
|
||||
contact to mjData.contact. The reference documentation of mj_addContact explains which fields of mjContact must be
|
||||
contact to ``mjData.contact``. The reference documentation of mj_addContact explains which fields of mjContact must be
|
||||
filled in by custom collision functions. Note that the functions we are talking about here correspond to near-phase
|
||||
collisions, and are called only after the list of candidate geom pairs has been constructed by the internal
|
||||
broad-phase collision mechanism.
|
||||
|
||||
After the constraint forces have been computed, the vector of forces for contact i starts at:
|
||||
After the constraint forces have been computed, the vector of forces for contact ``i`` starts at:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
mjtNum* contactforce = d->efc_force + d->contact[i].efc_address;
|
||||
|
||||
and similarly for all other efc_XXX vectors. Keep in mind that the contact friction cone can be pyramidal or elliptic,
|
||||
depending on which solver is selected in mjModel.opt. The function :ref:`mj_isPyramidal`
|
||||
and similarly for all other ``efc_XXX`` vectors. Keep in mind that the contact friction cone can be pyramidal or
|
||||
elliptic, depending on which solver is selected in ``mjModel.opt``. The function :ref:`mj_isPyramidal`
|
||||
can be used to determine which friction cone type is used. For pyramidal cones, the interpretation of the contact force
|
||||
(whose address we computed above) is non-trivial, because the components are forces along redundant non-orthogonal axes
|
||||
corresponding to the edges of the pyramid. The function :ref:`mj_contactForce` can be
|
||||
@@ -1439,7 +1450,7 @@ columns. Here the axes are along the rows of the matrix. Thus, given that MuJoCo
|
||||
normal axis (which is the X axis of the contact frame by our convention) is in position mjContact.frame[0-2], the Y axis
|
||||
is in [3-5] and the Z axis is in [6-8]. The reason for this arrangement is because we can have frictionless contacts
|
||||
where only the normal axis is used, so it makes sense to have its coordinates in the first 3 positions of
|
||||
mjContact.frame.
|
||||
``mjContact.frame``.
|
||||
|
||||
.. _siCoordinate:
|
||||
|
||||
@@ -1450,43 +1461,43 @@ There are multiple coordinate frames used in MuJoCo. The top-level distinction i
|
||||
Cartesian coordinates. The mapping from the vector of joints coordinates to the Cartesian positions and orientations
|
||||
of all bodies is called forward kinematics and is the first step in the physics pipeline. The opposite mapping is
|
||||
called inverse kinematics but it is not uniquely defined and is not implemented in MuJoCo. Recall that mappings
|
||||
between the tangent spaces (i.e. joint velocities and forces to Cartesian velocities and forces) are given by the body
|
||||
between the tangent spaces (i.e., joint velocities and forces to Cartesian velocities and forces) are given by the body
|
||||
Jacobians.
|
||||
|
||||
Here we explain further subtleties and subdivisions of the coordinate frames, and summarize the available
|
||||
transformation functions. In joint coordinates, the only complication is that the position vector mjData.qpos has
|
||||
different dimensionality than the velocity and acceleration vectors mjData.qvel and mjData.qacc due to quaternion
|
||||
joints. The function :ref:`mj_differentiatePos` "subtracts" two joint position vectors and returns a velocity vector.
|
||||
Conversely, the function :ref:`mj_integratePos` takes a position vector and a velocity vector, and returns a new
|
||||
position vector which has been displaced by the given velocity.
|
||||
transformation functions. In joint coordinates, the only complication is that the position vector ``mjData.qpos`` has
|
||||
different dimensionality than the velocity and acceleration vectors ``mjData.qvel`` and ``mjData.qacc`` due to
|
||||
quaternion joints. The function :ref:`mj_differentiatePos` "subtracts" two joint position vectors and returns a
|
||||
velocity vector. Conversely, the function :ref:`mj_integratePos` takes a position vector and a velocity vector, and
|
||||
returns a new position vector which has been displaced by the given velocity.
|
||||
|
||||
Cartesian coordinates are more complicated because there are three different coordinate frames that we use: local,
|
||||
global, and com-based. Local coordinates are used in mjModel to represent the static offsets between a parent and a
|
||||
child body, as well as the static offsets between a body and any geoms, sites, cameras and lights attached to it.
|
||||
These static offsets are applied in addition to any joint transformations. So mjModel.body_pos, mjModel.body_quat and
|
||||
all other spatial quantities in mjModel are expressed in local coordinates. The job of forward kinematics is to
|
||||
accumulate the joint transformations and static offsets along the kinematic tree and compute all positions and
|
||||
orientations in global coordinates. The quantities in mjData that start with "x" are expressed in global coordinates.
|
||||
These are mjData.xpos, mjData.geom_xpos etc. Frame orientations are usually stored as 3-by-3 matrices (xmat), except
|
||||
for bodies whose orientation is also stored as a unit quaternion mjData.xquat. Given this body quaternion, the
|
||||
quaternions of all other objects attached to the body can be reconstructed by a quaternion multiplication. The
|
||||
function :ref:`mj_local2Global` converts from local body coordinates to global
|
||||
Cartesian coordinates.
|
||||
These static offsets are applied in addition to any joint transformations. So ``mjModel.body_pos``,
|
||||
``mjModel.body_quat`` and all other spatial quantities in mjModel are expressed in local coordinates. The job of
|
||||
forward kinematics is to accumulate the joint transformations and static offsets along the kinematic tree and compute
|
||||
all positions and orientations in global coordinates. The quantities in mjData that start with "x" are expressed in
|
||||
global coordinates. These are ``mjData.xpos``, ``mjData.geom_xpos`` etc. Frame orientations are usually stored as
|
||||
3-by-3 matrices (xmat), except for bodies whose orientation is also stored as a unit quaternion ``mjData.xquat``. Given
|
||||
this body quaternion, the quaternions of all other objects attached to the body can be reconstructed by a quaternion
|
||||
multiplication. The function :ref:`mj_local2Global` converts from local body coordinates to global Cartesian
|
||||
coordinates.
|
||||
|
||||
:ref:`mju_negPose` and :ref:`mju_trnVecPose`. A pose is a grouping of a 3D position and a unit quaternion orientation.
|
||||
There is no separate data structure; the grouping is in terms of logic. This represents a position and orientation in
|
||||
space, or in other words a spatial frame. Note that OpenGL uses 4-by-4 matrices to represent the same information,
|
||||
except here we use a quaternion for orientation. The function mju_mulPose multiplies two poses, meaning that it
|
||||
transforms the first pose by the second pose (the order is important). mju_negPose constructs the opposite pose, while
|
||||
mju_trnVecPose transforms a 3D vector by a pose, mapping it from local coordinates to global coordinates if we think
|
||||
of the pose as a coordinate frame. If we want to manipulate only the orientation part, we can do that with the
|
||||
transforms the first pose by the second pose (the order is important). ``mju_negPose`` constructs the opposite pose,
|
||||
while ``mju_trnVecPose`` transforms a 3D vector by a pose, mapping it from local coordinates to global coordinates if
|
||||
we think of the pose as a coordinate frame. If we want to manipulate only the orientation part, we can do that with the
|
||||
analogous quaternion utility functions :ref:`mju_mulQuat`, :ref:`mju_negQuat` and :ref:`mju_rotVecQuat`.
|
||||
|
||||
Finally, there is the com-based frame. This is used to represent 6D spatial vectors containing a 3D angular velocity
|
||||
or acceleration or torque, followed by a 3D linear velocity or acceleration or force. Note the backwards order:
|
||||
rotation followed by translation. mjData.cdof and mjData.cacc are example of such vectors; the names start with "c".
|
||||
These vectors play a key role in the multi-joint dynamics computation. Explaining this is beyond our scope here; see
|
||||
Featherstone's excellent `slides <http://royfeatherstone.org/spatial>`__ on the subject. In general, the user should
|
||||
rotation followed by translation. ``mjData.cdof`` and ``mjData.cacc`` are example of such vectors; the names start with
|
||||
"c". These vectors play a key role in the multi-joint dynamics computation. Explaining this is beyond our scope here;
|
||||
see Featherstone's excellent `slides <http://royfeatherstone.org/spatial>`__ on the subject. In general, the user should
|
||||
avoid working with such quantities directly. Instead use the functions :ref:`mj_objectVelocity`,
|
||||
:ref:`mj_objectAcceleration` and the low-level :ref:`mju_transformSpatial` to obtain linear and angular velocities,
|
||||
accelerations and forces for a given body. Still, for the interested reader, we summarize the most unusual aspect of
|
||||
@@ -1646,7 +1657,7 @@ mjCAMERA_FIXED
|
||||
targeting mode, it will move.
|
||||
mjCAMERA_USER
|
||||
This means that the abstract camera is ignored during an update and the low-level OpenGL cameras are not changed. It
|
||||
is equivalent to not specifying an abstract camera at all, i.e. passing a NULL pointer to mjvCamera in the update
|
||||
is equivalent to not specifying an abstract camera at all, i.e., passing a NULL pointer to mjvCamera in the update
|
||||
functions explained below.
|
||||
|
||||
The low-level mjvGLCamera is what determines the actual rendering. There are two such cameras embedded in mjvScene, one
|
||||
@@ -1655,9 +1666,9 @@ frame, while up corresponds to the positive Y axis. There is also a frustum in t
|
||||
average of the left and right frustum edges and then during rendering compute the actual edges from the viewport aspect
|
||||
ratio assuming 1:1 pixel aspect ratio. The distance between the two camera positions corresponds to the inter-pupilary
|
||||
distance (ipd). When the low-level camera parameters are computed automatically from an abstract camera, the ipd as well
|
||||
as vertical field of view (fovy) are taken from mjModel.vis.global.ipd/fovy for free and tracking cameras, and from the
|
||||
camera-specific mjModel.cam_ipd/fovy for cameras defined in the model. When stereoscopic mode is not enabled, as
|
||||
determined by mjvScene.stereo, the camera data for the two eyes are internally averaged during rendering.
|
||||
as vertical field of view (fovy) are taken from ``mjModel.vis.global.ipd``/``fovy`` for free and tracking cameras, and
|
||||
from the camera-specific ``mjModel.cam_ipd/fovy`` for cameras defined in the model. When stereoscopic mode is not
|
||||
enabled, as determined by mjvScene.stereo, the camera data for the two eyes are internally averaged during rendering.
|
||||
|
||||
.. _viSelect:
|
||||
|
||||
@@ -1686,24 +1697,24 @@ Perturbations
|
||||
|
||||
Interactive perturbations have proven very useful in exploring the model dynamics as well as probing closed-loop
|
||||
control systems. The user is free to implement any perturbation mechanism of their choice by setting
|
||||
mjData.qfrc_applied or mjData.xfrc_applied to suitable forces (in generalized and Cartesian coordinates respectively).
|
||||
``mjData.qfrc_applied`` or ``mjData.xfrc_applied`` to suitable forces (in generalized and Cartesian coordinates respectively).
|
||||
|
||||
Prior to MuJoCo version 1.40, user code had to maintain a collection of objects in order to implement perturbations.
|
||||
All these objects are now grouped into the data structure :ref:`mjvPerturb`. Its use is illustrated in
|
||||
:ref:`simulate.cc <saSimulate>`.
|
||||
The idea is to select a MuJoCo body of interest, and provide a reference pose (i.e. a 3D position and quaternion
|
||||
The idea is to select a MuJoCo body of interest, and provide a reference pose (i.e., a 3D position and quaternion
|
||||
orientation) for that body. These are stored in mjPerturb.respos/quat. The function :ref:`mjv_movePerturb` is a mouse
|
||||
hook for controlling the reference pose with the mouse. The function :ref:`mjv_initPerturb` is used to set the
|
||||
reference pose equal to the selected body pose at the onset of perturbation, so as to avoid jumps.
|
||||
|
||||
This perturbation object can then be used to move the selected body directly (when the simulation is paused or when
|
||||
the selected body is a mocap body), or to apply forces and torques to the body. This is done with the functions
|
||||
This perturbation object can then be used to move the selected body directly (when the simulation is paused or when the
|
||||
selected body is a mocap body), or to apply forces and torques to the body. This is done with the functions
|
||||
:ref:`mjv_applyPerturbPose` and :ref:`mjv_applyPerturbForce` respectively. The latter function writes the external
|
||||
perturbation force to mjData.xfrc_applied for the selected body. However it does not clear mjData.xfrc_applied for the
|
||||
remaining bodies, thus it is recommended to clear it in user code, in case the selected body changed and some
|
||||
perturbation force to ``mjData.xfrc_applied`` for the selected body. However it does not clear ``mjData.xfrc_applied``
|
||||
for the remaining bodies, thus it is recommended to clear it in user code, in case the selected body changed and some
|
||||
perturbation force was left over from a previous time step. If there is more than one device that can apply
|
||||
perturbations or user code needs to add perturbations from other sources, the user must implement the necessary logic
|
||||
so that only the desired perturbations are present in mjData.xfrc_applied and any old perturbations are cleared.
|
||||
perturbations or user code needs to add perturbations from other sources, the user must implement the necessary logic so
|
||||
that only the desired perturbations are present in ``mjData.xfrc_applied`` and any old perturbations are cleared.
|
||||
|
||||
In addition to affecting the simulation, the perturbation object is recognized by the abstract visualizer and can be
|
||||
rendered. This is done by adding a visual string to denote the positional difference, and a rotating cube to denote
|
||||
@@ -1728,13 +1739,13 @@ else needed for specify how rendering should be done. mjvScene also contains up
|
||||
copied from the model, as well as a headlight which is in light position 0 when present.
|
||||
|
||||
The above procedure is the most common approach, and it updates the entire scene at each frame. In addition, we
|
||||
provide two functions for finer control. :ref:`mjv_updateCamera` updates only the camera (i.e. maps the abstract
|
||||
provide two functions for finer control. :ref:`mjv_updateCamera` updates only the camera (i.e., maps the abstract
|
||||
mjvCamera to the low-level mjvGLCamera) but does not touch the geoms or lights. This is useful when the user is moving
|
||||
the camera rapidly but the simulation state has not changed - in that case there is no point in re-creating the lists
|
||||
of geoms and lights.
|
||||
|
||||
More advanced rendering effects can be achieved by manipulating the list of abstract geoms. For example, the user can
|
||||
add custom geoms at the end of the list. Sometimes it is desirable to render a sequence of simulation states (i.e. a
|
||||
add custom geoms at the end of the list. Sometimes it is desirable to render a sequence of simulation states (i.e., a
|
||||
trajectory) and not just the current state. For this purpose, we have provided the function :ref:`mjv_addGeoms` which
|
||||
adds the geoms corresponding to the current simulation state to the list already in mjvScene. It does not change the
|
||||
list of lights, because lighting is additive and having too many lights will make the scene too bright. Importantly,
|
||||
@@ -1809,7 +1820,7 @@ yet the user is expected to move them programmatically at each simulation step.
|
||||
through contacts, or better yet, through soft equality constraints to regular bodies which in turn make contacts. The
|
||||
latter approach is illustrated in the MPL models available on the Forum. It provides effective dynamic filtering and
|
||||
avoids contacts involving bodies that behave as if they are infinitely heavy (which is what a fixed body is). Note
|
||||
that the time-varying positions and orientations of the mocap bodies are stored in mjData.mocap_pos/quat, as opposed
|
||||
that the time-varying positions and orientations of the mocap bodies are stored in ``mjData.mocap_pos/quat``, as opposed
|
||||
to storing them in mjModel. This is because mjModel is supposed to remain constant. The fixed mocap body pose stored
|
||||
in mjModel is only used at initialization and reset, when user code has not yet had a chance to update
|
||||
mjData.mocap_pos/quat.
|
||||
@@ -1864,11 +1875,11 @@ mjrContext is specific to MuJoCo. After creation, it contains references (called
|
||||
resources that were uploaded to the GPU by mjr_makeContext. These include model-specific resources such as meshes and
|
||||
textures, as well as generic resources such as font bitmaps for the specified font scale, framebuffer objects for
|
||||
shadow mapping and offscreen rendering, and associated renderbuffers. It also contains OpenGL-related options copied
|
||||
from mjModel.vis, capabilities of the default window framebuffer that are discovered automatically, and the currently
|
||||
active buffer for rendering; see :ref:`buffers <reBuffer>` below. Note that even though MuJoCo uses fixed-function
|
||||
OpenGL, it avoids immediate mode rendering and instead uploads all resources to the GPU upfront. This makes it as
|
||||
efficient as a modern shader, and possibly more efficient, because fixed-function OpenGL is now implemented via
|
||||
internal shaders that have been written by the video driver developers and tuned extensively.
|
||||
from ``mjModel.vis``, capabilities of the default window framebuffer that are discovered automatically, and the
|
||||
currently active buffer for rendering; see :ref:`buffers <reBuffer>` below. Note that even though MuJoCo uses
|
||||
fixed-function OpenGL, it avoids immediate mode rendering and instead uploads all resources to the GPU upfront. This
|
||||
makes it as efficient as a modern shader, and possibly more efficient, because fixed-function OpenGL is now implemented
|
||||
via internal shaders that have been written by the video driver developers and tuned extensively.
|
||||
|
||||
Most of the fields of mjrContext remain constant after the call to mjr_makeContext. The only exception is
|
||||
mjrContext.currentBuffer which changes whenever the active buffer changes. Some of the GPU resources may also change
|
||||
@@ -1918,7 +1929,7 @@ code samples. All OpenGL can do is detect these properties; we do this in mjr_ma
|
||||
various window capabilities fields of mjrContext. This is why such properties are not part of the MuJoCo model; they are
|
||||
session/software-specific and not model-specific. In contrast, the offscreen framebuffer is managed entirely by OpenGL,
|
||||
and so we can create that buffer with whatever properties we want, namely with the resolution and multi-sample
|
||||
properties specified in mjModel.vis.
|
||||
properties specified in ``mjModel.vis``.
|
||||
|
||||
The user can directly access the pixels in the two buffers. This is done with the functions :ref:`mjr_readPixels`,
|
||||
:ref:`mjr_drawPixels` and :ref:`mjr_blitBuffer`. Read/draw transfer pixels from/to the active buffer to/from the CPU.
|
||||
|
||||
Reference in New Issue
Block a user