Runtime disabling of actuators according to group.
Added `option-actuatorgroupdisable` attribute and associated `mjOption.disableactuator` integer bitfield, used to disable sets of actuators at runtime according to their group. - The first 6 actuator groups are toggleable in the `simulate` viewer. - Minor refactor and cleanup of actuator documentation. https://youtu.be/H9qG9Zf2W44 Fixes #1092. PiperOrigin-RevId: 578335600 Change-Id: I4cf663b90ea768e4380acfa2fe3b8c15c7cbb568
This commit is contained in:
committed by
Copybara-Service
parent
45878b7eef
commit
893c404230
@@ -498,7 +498,7 @@ shown in the table below. Their names are in the format ``mjKEY_XXX``. They corr
|
||||
- Maximum number of UI sections.
|
||||
Defined in `mjui.h <https://github.com/google-deepmind/mujoco/blob/main/include/mujoco/mjui.h>`_.
|
||||
* - ``mjMAXUIITEM``
|
||||
- 80
|
||||
- 200
|
||||
- Maximum number of items per UI section.
|
||||
Defined in `mjui.h <https://github.com/google-deepmind/mujoco/blob/main/include/mujoco/mjui.h>`_.
|
||||
* - ``mjMAXUITEXT``
|
||||
|
||||
@@ -1879,6 +1879,12 @@ adjust it properly through the XML.
|
||||
:at:`sdf_initpoints`: :at-val:`int, "40"`
|
||||
Number of starting points used for fining contacts with Signed Distance Field collisions.
|
||||
|
||||
.. _option-actuatorgroupdisable:
|
||||
|
||||
:at:`actuatorgroupdisable`: :at-val:`int(30), ""`
|
||||
List of actuator groups to disable. Actuators whose :ref:`group<actuator-general-group>` is in this list will produce
|
||||
no force. If they are stateful, their activation states will not be integrated. Internally this list is
|
||||
implemented as an integer bitfield, so values must be in the range ``0 <= group <= 30``.
|
||||
|
||||
.. _option-flag:
|
||||
|
||||
@@ -4883,7 +4889,7 @@ multiplied by the corresponding coef value, and added up to obtain the tendon le
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is a grouping element for actuator definitions. Recall the discussion of MuJoCo's :ref:`Actuation model
|
||||
<geActuation>` in the Computation chapter, and the :ref:`Actuator shortcuts <CActuator>` discussed earlier in this
|
||||
<geActuation>` in the Computation chapter, and the :ref:`Actuator shortcuts <CActShortcuts>` discussed earlier in this
|
||||
chapter. The first 13 attributes of all actuator-related elements below are the same, so we document them only once,
|
||||
under the :el:`general` actuator.
|
||||
|
||||
@@ -5156,7 +5162,7 @@ specify them independently.
|
||||
:el-prefix:`actuator/` |-| **motor** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This and the next three elements are the :ref:`Actuator shortcuts <CActuator>` discussed earlier. When a
|
||||
This and the next three elements are the :ref:`Actuator shortcuts <CActShortcuts>` discussed earlier. When a
|
||||
such shortcut is encountered, the parser creates a :el:`general` actuator and sets its dynprm, gainprm and biasprm
|
||||
attributes to the internal defaults shown above, regardless of any default settings. It then adjusts dyntype, gaintype
|
||||
and biastype depending on the shortcut, parses any custom attributes (beyond the common ones), and translates them
|
||||
@@ -7526,7 +7532,7 @@ if omitted.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This and the next three elements set the attributes of the :ref:`general <actuator-general>` element using
|
||||
:ref:`Actuator shortcuts <CActuator>`. It does not make sense to use more than one such shortcut in the same defaults
|
||||
:ref:`Actuator shortcuts <CActShortcuts>`. It does not make sense to use more than one such shortcut in the same defaults
|
||||
class, because they set the same underlying attributes, replacing any previous settings. All
|
||||
:ref:`motor <actuator-motor>` attributes are available here except: name, class, joint, jointinparent, site, tendon,
|
||||
slidersite, cranksite.
|
||||
|
||||
+1
-1
@@ -230,7 +230,7 @@
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`iterations<option-iterations>` | :ref:`ls_iterations<option-ls_iterations>` | :ref:`noslip_iterations<option-noslip_iterations>` | :ref:`mpr_iterations<option-mpr_iterations>` | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
| | | | :ref:`sdf_iterations<option-sdf_iterations>` | :ref:`sdf_initpoints<option-sdf_initpoints>` | | | |
|
||||
| | | | :ref:`sdf_iterations<option-sdf_iterations>` | :ref:`sdf_initpoints<option-sdf_initpoints>` | :ref:`actuatorgroupdisable<option-actuatorgroupdisable>` | | |
|
||||
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
|
||||
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| |_| option |br| |_| |L| | | .. table:: |
|
||||
|
||||
+30
-17
@@ -7,41 +7,54 @@ Upcoming version (not yet released)
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
- Added sub-terms of total passive forces in ``mjData.qfrc_passive`` to :ref:`mjData`:
|
||||
``qfrc_{spring, damper, gravcomp, fluid}``. The sum of these vectors equals ``qfrc_passive``.
|
||||
|
||||
1. Added sub-terms of total passive forces in ``mjData.qfrc_passive`` to :ref:`mjData`:
|
||||
``qfrc_{spring, damper, gravcomp, fluid}``. The sum of these vectors equals ``qfrc_passive``.
|
||||
2. Increased ``mjMAXUIITEM`` (maximum number of UI elements per section in Simulate) to 200.
|
||||
.. youtube:: H9qG9Zf2W44
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
- Added :ref:`actuatorgroupdisable<option-actuatorgroupdisable>` attribute and associated
|
||||
:ref:`mjOption.disableactuator<mjOption>` integer bitfield, which can be used to disable sets of actuators at runtime
|
||||
according to their :ref:`group<actuator-general-group>`. Fixes :github:issue:`1092`. See :ref:`CActDisable`.
|
||||
|
||||
- The first 6 actuator groups are toggleable in the :ref:`simulate<saSimulate>` viewer. See `example model
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/test/engine/testdata/actuation/actuator_group_disable.xml>`__
|
||||
and associated screen-capture on the right.
|
||||
|
||||
|
||||
|
||||
- Increased ``mjMAXUIITEM`` (maximum number of UI elements per section in Simulate) to 200.
|
||||
|
||||
MJX
|
||||
^^^
|
||||
2. Added support for joint equality constraints (``mjEQ_JOINT`` in :ref:`mjtEq`).
|
||||
3. Fixed bug where mixed ``jnt_limited`` joints were not being constrained correctly.
|
||||
4. Made ``device_put`` type validation more verbose (fixes :github:issue:`1113`).
|
||||
5. Removed empty EFC rows from `MJX`, for joints with no limits (fixes :github:issue:`1117`).
|
||||
6. Fixed bug where equality constraints became inactive (fixes :github:issue:`1129`).
|
||||
7. Added an error when loading a model with tendons (fixes :github:issue:`1149`).
|
||||
- Added support for joint equality constraints (``mjEQ_JOINT`` in :ref:`mjtEq`).
|
||||
- Fixed bug where mixed ``jnt_limited`` joints were not being constrained correctly.
|
||||
- Made ``device_put`` type validation more verbose (fixes :github:issue:`1113`).
|
||||
- Removed empty EFC rows from `MJX`, for joints with no limits (fixes :github:issue:`1117`).
|
||||
- Fixed bug where equality constraints became inactive (fixes :github:issue:`1129`).
|
||||
- Added an error when loading a model with tendons (fixes :github:issue:`1149`).
|
||||
|
||||
Python bindings
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
6. Fix the macOS ``mjpython`` launcher to work with the Python interpreter from Apple Command Line
|
||||
Tools.
|
||||
7. Fixed a crash when copying instances of ``mujoco.MjData`` for models that use plugins. Introduced a ``model``
|
||||
attribute to ``MjData`` which is reference to the model that was used to create that ``MjData`` instance.
|
||||
- Fix the macOS ``mjpython`` launcher to work with the Python interpreter from Apple Command Line Tools.
|
||||
- Fixed a crash when copying instances of ``mujoco.MjData`` for models that use plugins. Introduced a ``model``
|
||||
attribute to ``MjData`` which is reference to the model that was used to create that ``MjData`` instance.
|
||||
|
||||
Simulate
|
||||
^^^^^^^^
|
||||
8. :ref:`simulate<saSimulate>`: correct handling of "Pause update", "Fullscreen" and "VSync" buttons.
|
||||
- :ref:`simulate<saSimulate>`: correct handling of "Pause update", "Fullscreen" and "VSync" buttons.
|
||||
|
||||
Documentation
|
||||
^^^^^^^^^^^^^
|
||||
9. Added documentation for the :ref:`UI` framework.
|
||||
10. Fixed typos and supported fields in docs (fixes :github:issue:`1105` and :github:issue:`1106`).
|
||||
- Added documentation for the :ref:`UI` framework.
|
||||
- Fixed typos and supported fields in docs (fixes :github:issue:`1105` and :github:issue:`1106`).
|
||||
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
11. Fixed bug relating to welds modified with :ref:`torquescale<equality-weld-torquescale>`.
|
||||
- Fixed bug relating to welds modified with :ref:`torquescale<equality-weld-torquescale>`.
|
||||
|
||||
Version 3.0.0 (October 18, 2023)
|
||||
--------------------------------
|
||||
|
||||
+19
-17
@@ -271,7 +271,7 @@ the force outputs are stored in ``mjData.actuator_force``, and the activation st
|
||||
|
||||
These three components of an actuator - transmission, activation dynamics, and force generation - determine how the
|
||||
actuator works. The user can set them independently for maximum flexibility, or use :ref:`Actuator shortcuts
|
||||
<CActuator>` which instantiate common actuator types.
|
||||
<CActShortcuts>` which instantiate common actuator types.
|
||||
|
||||
.. _geTransmission:
|
||||
|
||||
@@ -316,8 +316,8 @@ is attached; the possible attachment object types are :at:`joint`, :at:`tendon`,
|
||||
|
||||
.. _geActivation:
|
||||
|
||||
Activation dynamics
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Stateful actuators
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some actuators such as pneumatic and hydraulic cylinders as well as biological muscles have an internal state called
|
||||
"activation". This is a true dynamic state, beyond the joint positions :math:`q` and velocities :math:`v`. Including
|
||||
@@ -333,27 +333,33 @@ independent of the other actuators. The activation types currently implemented a
|
||||
.. math::
|
||||
\begin{aligned}
|
||||
\text{integrator}: & & \dot{w}_i &= u_i \\
|
||||
\text{filter}: & & \dot{w}_i &= (u_i - w_i) / t \\
|
||||
\text{filterexact}: & & \dot{w}_i &= (u_i - w_i) / t \\
|
||||
\text{filter}: & & \dot{w}_i &= (u_i - w_i) / \texttt{t} \\
|
||||
\text{filterexact}: & & \dot{w}_i &= (u_i - w_i) / \texttt{t} \\
|
||||
\text{muscle}: & & \dot{w}_i &= \textrm{muscle}(u_i, w_i, l_i, \dot{l}_i)
|
||||
\end{aligned}
|
||||
|
||||
where :math:`t` is an actuator-specific time constant stored in ``mjModel.actuator_dynprm``. In addition the type can
|
||||
be "user", in which case :math:`w_i` is computed by the user-defined callback :ref:`mjcb_act_dyn`. The type can also
|
||||
be "none" which corresponds to a regular actuator with no activation state. The dimensionality of :math:`w` equals
|
||||
where :math:`\texttt{t}` is an actuator-specific time-constant stored in ``mjModel.actuator_dynprm``. In addition, the
|
||||
type can be "user", in which case :math:`w_i` is computed by the user-defined callback :ref:`mjcb_act_dyn`. The type can
|
||||
also be "none" which corresponds to a regular actuator with no activation state. The dimensionality of :math:`w` equals
|
||||
the number of actuators whose activation type is different from "none".
|
||||
|
||||
For more information regarding muscle activation dynamics, see :ref:`CMuscle`.
|
||||
|
||||
For ``filterexact`` activation dynamics, Euler integration of :math:`\dot{w}` is replaced with the analytic integral:
|
||||
|
||||
.. math::
|
||||
\begin{aligned}
|
||||
\text{filter}: & & w_{i+1} &= w_i + h (u_i - w_i) / t \\
|
||||
\text{filterexact}: & & w_{i+1} &= w_i + (u_i - w_i) (1 - e^{-h / t}) \\
|
||||
\text{filter}: & & w_{i+1} &= w_i + h (u_i - w_i) / \texttt{t} \\
|
||||
\text{filterexact}: & & w_{i+1} &= w_i + (u_i - w_i) (1 - e^{-h / \texttt{t}}) \\
|
||||
\end{aligned}
|
||||
|
||||
The two expressions converge to the same value in the :math:`h \rightarrow 0` limit.
|
||||
The two expressions converge to the same value in the :math:`h \rightarrow 0` limit. Note that Euler-integrated filters
|
||||
diverge for :math:`\texttt{t} < h`, while exactly-integrated filters are stable for any positive :math:`\texttt{t}`.
|
||||
|
||||
Note that Euler-integrated filters diverge for :math:`t < h`, while exactly-integrated filters are stable for any
|
||||
:math:`t > 0`.
|
||||
:ref:`actearly<actuator-general-actearly>`:
|
||||
If the :ref:`actearly<actuator-general-actearly>` attribute is set to "true", ``mjData.actuator_force`` is computed
|
||||
based on :math:`w_{i+1}` (the next activation), reducing the delay between changes to :math:`u` and their effects on
|
||||
the acceleration by one time step (so the total dynamics are second-order rather than third order).
|
||||
|
||||
.. _geActuatorForce:
|
||||
|
||||
@@ -391,10 +397,6 @@ This quantity is stored in ``mjData.qfrc_actuator``. It is added to the applied
|
||||
with any user-defined forces in joint or Cartesian coordinates (which are stored in ``mjData.qfrc_applied`` and
|
||||
``mjData.xfrc_applied`` respectively).
|
||||
|
||||
Optionally, the :ref:`actearly<actuator-general-actearly>` attribute on an actuator computes ``mjData.qfrc_actuator``
|
||||
based on the value of :math:`w_{i+1}` after integration, reducing the delay between changes to :math:`u` and
|
||||
:math:`t`.
|
||||
|
||||
.. _gePassive:
|
||||
|
||||
Passive forces
|
||||
|
||||
@@ -732,6 +732,7 @@ struct mjOption_ { // physics options
|
||||
int mpr_iterations; // maximum number of MPR solver iterations
|
||||
int disableflags; // bit flags for disabling standard features
|
||||
int enableflags; // bit flags for enabling optional features
|
||||
int disableactuator; // bit flags for disabling actuators by group id
|
||||
|
||||
// sdf collision settings
|
||||
int sdf_initpoints; // number of starting points for gradient descent
|
||||
|
||||
+105
-62
@@ -190,7 +190,7 @@ element, it must be undefined in the active defaults class.
|
||||
|
||||
A final twist here is actuators. They are different because some of the actuator-related elements are actually
|
||||
shortcuts, and shortcuts interact with the defaults setting mechanism in a non-obvious way. This is explained in the
|
||||
:ref:`Actuator shortcuts <CActuator>` section below.
|
||||
:ref:`Actuator shortcuts <CActShortcuts>` section below.
|
||||
|
||||
.. _CFrame:
|
||||
|
||||
@@ -567,10 +567,44 @@ general guidelines and observations:
|
||||
setup operation for the main PGS and Noslip PGS is the same, thus the setup cost is paid only once when both are
|
||||
enabled.
|
||||
|
||||
.. _CActuator:
|
||||
.. _CActuators:
|
||||
|
||||
Actuator shortcuts
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Actuators
|
||||
~~~~~~~~~
|
||||
|
||||
This section describes various aspects of using actuators in MuJoCo. See the :ref:`Actuation model <geActuation>`
|
||||
regarding the computational model.
|
||||
|
||||
.. _CActDisable:
|
||||
|
||||
Group disable
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The :ref:`actuatorgroupdisable<option-actuatorgroupdisable>` attribute, which can be changed at runtime by setting the
|
||||
:ref:`mjOption.disableactuator<mjOption>` integer bitfield, allows the user to disable sets of actuators according to
|
||||
their :ref:`group<actuator-general-group>`. This feature is convenient when one would like to use multiple types of
|
||||
actuators for the same kinematic tree. For example consider a robot with firmware that supports mutiple control modes
|
||||
e.g., torque-control and position-control. In this case, one can define both types of actuators in the same MJCF
|
||||
model, assigning one type of actuator to group 0 and the other to group 1.
|
||||
|
||||
.. youtube:: H9qG9Zf2W44
|
||||
:align: right
|
||||
:width: 40%
|
||||
|
||||
The :ref:`actuatorgroupdisable<option-actuatorgroupdisable>` MJCF attribute selects which groups are disabled by
|
||||
default, and :ref:`mjOption.disableactuator<mjOption>` can be set at runtime to switch the active set. Note that the
|
||||
total number of actuators ``mjModel.nu`` remains unchanged, as do the actuator indices, so it is up to the user to know
|
||||
that the respective ``mjData.ctrl`` values of disabled actuators will be ignored and produce no force. `This example
|
||||
model <https://github.com/google-deepmind/mujoco/blob/main/test/engine/testdata/actuation/actuator_group_disable.xml>`__
|
||||
has three actuator groups which can be toggled at runtime in the :ref:`simulate<saSimulate>` interactive viewer.
|
||||
See `example model
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/test/engine/testdata/actuation/actuator_group_disable.xml>`__
|
||||
and associated screen-capture on the right.
|
||||
|
||||
.. _CActShortcuts:
|
||||
|
||||
Shortcuts
|
||||
^^^^^^^^^
|
||||
|
||||
As explained in the :ref:`Actuation model <geActuation>` section of the Computation chapter, MuJoCo offers a flexible
|
||||
actuator model with transmission, activation dynamics and force generation components that can be specified
|
||||
@@ -605,8 +639,8 @@ explicitly.
|
||||
|
||||
.. _CForceRange:
|
||||
|
||||
Actuator force clamping
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Force limits
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Actuator forces are usually limited between lower and upper bounds. These limits can be enforced in three ways:
|
||||
|
||||
@@ -645,62 +679,14 @@ Force clamping at joint input with :ref:`joint/actuatorfrcrange<body-joint-actua
|
||||
|
||||
The three clamping options above are non-exclusive and can be combined as required.
|
||||
|
||||
.. _CActRange:
|
||||
|
||||
Activation clamping
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As described in the :ref:`Actuation model <geActuation>` section of the Computation chapter, MuJoCo supports actuators
|
||||
with internal dynamics whose states are called "activations". One useful application of these stateful actuators is the
|
||||
"integrated-velocity" actuator, implemented by the :ref:`intvelocity<actuator-intvelocity>` shortcut. Different from the
|
||||
:ref:`pure velocity<actuator-velocity>` actuators, which implement direct feedback on transmission target's velocity,
|
||||
*integrated-velocity* actuators couple an *integrator* with a *position-feedback* actuator. In this case the semantics
|
||||
of the activation state are "the setpoint of the position actuator", and the semantics of the control signal are "the
|
||||
velocity of the setpoint of the position actuator". Note that in real robotic systems this integrated-velocity actuator
|
||||
is the most common implementation of actuators with velocity semantics, rather than pure feedback on velocity which is
|
||||
often quite unstable (both in real life and in simulation).
|
||||
|
||||
In the case of integrated-velocity actuators, it is often desirable to *clamp* the activation state, since otherwise the
|
||||
position target would keep integrating beyond the joint limits, leading to loss of controllabillity. To see the effect
|
||||
of activation clamping, load the example model below:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<mujoco>
|
||||
<default>
|
||||
<joint axis="0 0 1" limited="true" range="-90 90" damping="0.3"/>
|
||||
<geom size=".1 .1 .1" type="box"/>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="joint1"/>
|
||||
<geom/>
|
||||
</body>
|
||||
<body pos=".3 0 0">
|
||||
<joint name="joint2"/>
|
||||
<geom/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<general name="unclamped" joint="joint1" gainprm="1" biastype="affine"
|
||||
biasprm="0 -1" dyntype="integrator"/>
|
||||
<intvelocity name="clamped" joint="joint2" actrange="-1.57 1.57"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
|
||||
Note that the :at:`actrange` attribute is always specified in native units (radians), even though the joint range
|
||||
can be either in degrees (the default) or radians, depending on the :ref:`compiler/angle <compiler>` attribute.
|
||||
|
||||
.. _CLengthRange:
|
||||
|
||||
Actuator length range
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Length range
|
||||
^^^^^^^^^^^^
|
||||
|
||||
As of MuJoCo 2.0, the field mjModel.actuator_lengthrange contains the range of feasible actuator lengths (or more
|
||||
precisely, lengths of the actuator's transmission). This is needed to simulate :ref:`muscle actuators <CMuscle>` as
|
||||
explained below. Here we focus on what actuator_lengthrange means and how to set it.
|
||||
The field ``mjModel.actuator_lengthrange`` contains the range of feasible actuator lengths (or more
|
||||
precisely, lengths of the actuator's transmission). This is needed to simulate :ref:`muscle actuators <CMuscle>`.
|
||||
Here we focus on what actuator_lengthrange means and how to set it.
|
||||
|
||||
Unlike all other fields of mjModel which are exact physical or geometric quantities, actuator_lengthrange is an
|
||||
approximation. Intuitively it corresponds to the minimum and maximum length that the actuator's transmission can reach
|
||||
@@ -744,12 +730,69 @@ practice length ranges will almost always be used with muscle actuators attached
|
||||
joint limits defined in the model, effectively limiting the lengths of the muscle actuators. If you get a convergence
|
||||
error in such a model, the most likely explanation is that you forgot to include joint limits.
|
||||
|
||||
.. _CActivation:
|
||||
|
||||
Stateful actuators
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As described in the :ref:`Actuation model <geActuation>` section of the Computation chapter, MuJoCo supports actuators
|
||||
with internal dynamics whose states are called "activations".
|
||||
|
||||
.. _CActRange:
|
||||
|
||||
Activation limits
|
||||
'''''''''''''''''
|
||||
|
||||
One useful application of stateful actuators is the
|
||||
"integrated-velocity" actuator, implemented by the :ref:`intvelocity<actuator-intvelocity>` shortcut. Different from the
|
||||
:ref:`pure velocity<actuator-velocity>` actuators, which implement direct feedback on transmission target's velocity,
|
||||
*integrated-velocity* actuators couple an *integrator* with a *position-feedback* actuator. In this case the semantics
|
||||
of the activation state are "the setpoint of the position actuator", and the semantics of the control signal are "the
|
||||
velocity of the setpoint of the position actuator". Note that in real robotic systems this integrated-velocity actuator
|
||||
is the most common implementation of actuators with velocity semantics, rather than pure feedback on velocity which is
|
||||
often quite unstable (both in real life and in simulation).
|
||||
|
||||
In the case of integrated-velocity actuators, it is often desirable to *clamp* the activation state, since otherwise the
|
||||
position target would keep integrating beyond the joint limits, leading to loss of controllabillity. To see the effect
|
||||
of activation clamping, load the example model below:
|
||||
|
||||
.. collapse:: Example model with activation limits
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<mujoco>
|
||||
<default>
|
||||
<joint axis="0 0 1" limited="true" range="-90 90" damping="0.3"/>
|
||||
<geom size=".1 .1 .1" type="box"/>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="joint1"/>
|
||||
<geom/>
|
||||
</body>
|
||||
<body pos=".3 0 0">
|
||||
<joint name="joint2"/>
|
||||
<geom/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<general name="unclamped" joint="joint1" gainprm="1" biastype="affine"
|
||||
biasprm="0 -1" dyntype="integrator"/>
|
||||
<intvelocity name="clamped" joint="joint2" actrange="-1.57 1.57"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
|
||||
Note that the :at:`actrange` attribute is always specified in native units (radians), even though the joint range
|
||||
can be either in degrees (the default) or radians, depending on the :ref:`compiler/angle <compiler>` attribute.
|
||||
|
||||
.. _CMuscle:
|
||||
|
||||
Muscle actuators
|
||||
~~~~~~~~~~~~~~~~
|
||||
Muscles
|
||||
'''''''
|
||||
|
||||
As of MuJoCo 2.0, we provide a set of tools for modeling biological muscles. Users who want to add muscles with minimum
|
||||
MuJoCo 2.0 provides a set of tools for modeling biological muscles. Users who want to add muscles with minimum
|
||||
effort can do so with a single line of XML in the actuator section:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
#define mjDISABLED(x) (m->opt.disableflags & (x))
|
||||
#define mjENABLED(x) (m->opt.enableflags & (x))
|
||||
|
||||
// is actuator disabled
|
||||
#define mjACTUATORDISABLED(i) (m->opt.disableactuator & (1 << m->actuator_group[i]))
|
||||
|
||||
// annotation for functions that accept printf-like variadic arguments
|
||||
#ifndef mjPRINTFLIKE
|
||||
#if defined(__GNUC__)
|
||||
|
||||
@@ -433,6 +433,7 @@ struct mjOption_ { // physics options
|
||||
int mpr_iterations; // maximum number of MPR solver iterations
|
||||
int disableflags; // bit flags for disabling standard features
|
||||
int enableflags; // bit flags for enabling optional features
|
||||
int disableactuator; // bit flags for disabling actuators by group id
|
||||
|
||||
// sdf collision settings
|
||||
int sdf_initpoints; // number of starting points for gradient descent
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
X( int, mpr_iterations ) \
|
||||
X( int, disableflags ) \
|
||||
X( int, enableflags ) \
|
||||
X( int, disableactuator ) \
|
||||
X( int, sdf_initpoints ) \
|
||||
X( int, sdf_iterations )
|
||||
|
||||
|
||||
@@ -276,6 +276,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=ValueType(name='int'),
|
||||
doc='bit flags for enabling optional features',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='disableactuator',
|
||||
type=ValueType(name='int'),
|
||||
doc='bit flags for disabling actuators by group id',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='sdf_initpoints',
|
||||
type=ValueType(name='int'),
|
||||
|
||||
+71
-14
@@ -716,6 +716,16 @@ void MakePhysicsSection(mj::Simulate* sim, int oldstate) {
|
||||
{mjITEM_EDITNUM, "Friction", 2, &(opt->o_friction), "5"},
|
||||
{mjITEM_END}
|
||||
};
|
||||
mjuiDef defDisableActuator[] = {
|
||||
{mjITEM_SEPARATOR, "Actuator Group Enable", 1},
|
||||
{mjITEM_CHECKBYTE, "Act Group 0", 2, sim->enableactuator+0, " 0"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 1", 2, sim->enableactuator+1, " 1"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 2", 2, sim->enableactuator+2, " 2"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 3", 2, sim->enableactuator+3, " 3"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 4", 2, sim->enableactuator+4, " 4"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 5", 2, sim->enableactuator+5, " 5"},
|
||||
{mjITEM_END}
|
||||
};
|
||||
|
||||
// add physics
|
||||
mjui_add(&sim->ui0, defPhysics);
|
||||
@@ -736,9 +746,11 @@ void MakePhysicsSection(mj::Simulate* sim, int oldstate) {
|
||||
defFlag[0].pdata = sim->enable + i;
|
||||
mjui_add(&sim->ui0, defFlag);
|
||||
}
|
||||
|
||||
// add contact override
|
||||
mjui_add(&sim->ui0, defOverride);
|
||||
|
||||
// add actuator group enable/disable
|
||||
mjui_add(&sim->ui0, defDisableActuator);
|
||||
}
|
||||
|
||||
|
||||
@@ -1002,7 +1014,7 @@ void MakeJointSection(mj::Simulate* sim, int oldstate) {
|
||||
|
||||
// add scalar joints, exit if UI limit reached
|
||||
int itemcnt = 0;
|
||||
for (int i=0; i < sim->jnt_type_.size() && itemcnt<mjMAXUIITEM; i++)
|
||||
for (int i=0; i < sim->jnt_type_.size() && itemcnt<mjMAXUIITEM; i++) {
|
||||
if ((sim->jnt_type_[i]==mjJNT_HINGE || sim->jnt_type_[i]==mjJNT_SLIDE)) {
|
||||
// skip if joint group is disabled
|
||||
if (!sim->opt.jointgroup[mjMAX(0, mjMIN(mjNGROUP-1, sim->jnt_group_[i]))]) {
|
||||
@@ -1035,6 +1047,7 @@ void MakeJointSection(mj::Simulate* sim, int oldstate) {
|
||||
mjui_add(&sim->ui1, defSlider);
|
||||
itemcnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make control section of UI
|
||||
@@ -1051,15 +1064,21 @@ void MakeControlSection(mj::Simulate* sim, int oldstate) {
|
||||
|
||||
// add section
|
||||
mjui_add(&sim->ui1, defControl);
|
||||
defSlider[0].state = 2;
|
||||
|
||||
// add controls, exit if UI limit reached (Clear button already added)
|
||||
int itemcnt = 1;
|
||||
for (int i=0; i < sim->actuator_ctrlrange_.size() && itemcnt<mjMAXUIITEM; i++) {
|
||||
// skip if actuator group is disabled
|
||||
if (!sim->opt.actuatorgroup[mjMAX(0, mjMIN(mjNGROUP-1, sim->actuator_group_[i]))]) {
|
||||
// skip if actuator vis group is disabled
|
||||
int group = sim->actuator_group_[i];
|
||||
if (!sim->opt.actuatorgroup[mjMAX(0, mjMIN(mjNGROUP-1, group))]) {
|
||||
continue;
|
||||
}
|
||||
// grey out if actuator group is disabled
|
||||
if (group >= 0 && group <= 30 && sim->m_->opt.disableactuator & (1 << group)) {
|
||||
defSlider[0].state = 0;
|
||||
} else {
|
||||
defSlider[0].state = 2;
|
||||
}
|
||||
|
||||
// set data and name
|
||||
if (!sim->is_passive_) {
|
||||
@@ -1189,19 +1208,27 @@ void CopyCamera(mj::Simulate* sim) {
|
||||
void UpdateSettings(mj::Simulate* sim, const mjModel* m) {
|
||||
// physics flags
|
||||
for (int i=0; i<mjNDISABLE; i++) {
|
||||
int new_value = ((m->opt.disableflags & (1<<i)) !=0);
|
||||
int new_value = ((m->opt.disableflags & (1<<i)) != 0);
|
||||
if (sim->disable[i] != new_value) {
|
||||
sim->disable[i] = new_value;
|
||||
sim->pending_.ui_update_physics = true;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<mjNENABLE; i++) {
|
||||
int new_value = ((m->opt.enableflags & (1<<i)) !=0);
|
||||
int new_value = ((m->opt.enableflags & (1<<i)) != 0);
|
||||
if (sim->enable[i] != new_value) {
|
||||
sim->enable[i] = new_value;
|
||||
sim->pending_.ui_update_physics = true;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<mjNGROUP; i++) {
|
||||
int enabled = ((m->opt.disableactuator & (1<<i)) == 0);
|
||||
if (sim->enableactuator[i] != enabled) {
|
||||
sim->enableactuator[i] = enabled;
|
||||
sim->pending_.ui_update_physics = true;
|
||||
sim->pending_.ui_remake_ctrl = true;
|
||||
}
|
||||
}
|
||||
|
||||
// camera
|
||||
int old_camera = sim->camera;
|
||||
@@ -1404,17 +1431,39 @@ void UiEvent(mjuiState* state) {
|
||||
|
||||
// update disable flags in mjOption
|
||||
opt->disableflags = 0;
|
||||
for (int i=0; i<mjNDISABLE; i++)
|
||||
for (int i=0; i<mjNDISABLE; i++) {
|
||||
if (sim->disable[i]) {
|
||||
opt->disableflags |= (1<<i);
|
||||
}
|
||||
}
|
||||
|
||||
// update enable flags in mjOption
|
||||
opt->enableflags = 0;
|
||||
for (int i=0; i<mjNENABLE; i++)
|
||||
for (int i=0; i<mjNENABLE; i++) {
|
||||
if (sim->enable[i]) {
|
||||
opt->enableflags |= (1<<i);
|
||||
}
|
||||
}
|
||||
|
||||
// update disableactuator bitflag in mjOption
|
||||
bool group_changed = false;
|
||||
for (int i=0; i<mjNGROUP; i++) {
|
||||
if ((!sim->enableactuator[i]) != (opt->disableactuator & (1<<i))) {
|
||||
group_changed = true;
|
||||
if (!sim->enableactuator[i]) {
|
||||
// disable actuator group i
|
||||
opt->disableactuator |= (1<<i);
|
||||
} else {
|
||||
// enable actuator group i
|
||||
opt->disableactuator &= ~(1<<i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remake control section if actuator disable group changed
|
||||
if (group_changed) {
|
||||
sim->pending_.ui_remake_ctrl = true;
|
||||
}
|
||||
}
|
||||
|
||||
// rendering section
|
||||
@@ -1461,10 +1510,7 @@ void UiEvent(mjuiState* state) {
|
||||
|
||||
// remake control section if actuator group changed
|
||||
if (it->name[0]=='A' && it->name[1]=='c') {
|
||||
sim->ui1.nsect = SECT_CONTROL;
|
||||
MakeControlSection(sim, sim->ui1.sect[SECT_CONTROL].state);
|
||||
sim->ui1.nsect = NSECT1;
|
||||
UiModify(&sim->ui1, state, &sim->platform_ui->mjr_context());
|
||||
sim->pending_.ui_remake_ctrl = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1790,7 +1836,7 @@ void Simulate::Sync() {
|
||||
range.emplace(m_->actuator_ctrlrange[2*i], m_->actuator_ctrlrange[2*i + 1]);
|
||||
}
|
||||
if (actuator_ctrlrange_[i] != range) {
|
||||
pending_.ui_update_ctrl = true;
|
||||
pending_.ui_remake_ctrl = true;
|
||||
actuator_ctrlrange_[i].swap(range);
|
||||
}
|
||||
}
|
||||
@@ -1851,6 +1897,7 @@ void Simulate::Sync() {
|
||||
X(mpr_iterations);
|
||||
X(disableflags);
|
||||
X(enableflags);
|
||||
X(disableactuator);
|
||||
X(sdf_initpoints);
|
||||
X(sdf_iterations);
|
||||
|
||||
@@ -2399,6 +2446,16 @@ void Simulate::Render() {
|
||||
pending_.ui_update_joint = false;
|
||||
}
|
||||
|
||||
if (pending_.ui_remake_ctrl) {
|
||||
if (this->ui1_enable && this->ui1.sect[SECT_CONTROL].state) {
|
||||
this->ui1.nsect = SECT_CONTROL;
|
||||
MakeControlSection(this, this->ui1.sect[SECT_CONTROL].state);
|
||||
this->ui1.nsect = NSECT1;
|
||||
UiModify(&this->ui1, &this->uistate, &this->platform_ui->mjr_context());
|
||||
}
|
||||
pending_.ui_remake_ctrl = false;
|
||||
}
|
||||
|
||||
if (pending_.ui_update_ctrl) {
|
||||
if (this->ui1_enable && this->ui1.sect[SECT_CONTROL].state) {
|
||||
mjui_update(SECT_CONTROL, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context());
|
||||
|
||||
@@ -153,6 +153,7 @@ class Simulate {
|
||||
bool ui_update_rendering;
|
||||
bool ui_update_joint;
|
||||
bool ui_update_ctrl;
|
||||
bool ui_remake_ctrl;
|
||||
} pending_ = {};
|
||||
|
||||
SimulateMutex mtx;
|
||||
@@ -228,6 +229,7 @@ class Simulate {
|
||||
// physics: need sync
|
||||
int disable[mjNDISABLE] = {0};
|
||||
int enable[mjNENABLE] = {0};
|
||||
int enableactuator[mjNGROUP] = {0};
|
||||
|
||||
// rendering: need sync
|
||||
int camera = 0;
|
||||
|
||||
@@ -213,6 +213,8 @@ void mj_fwdVelocity(const mjModel* m, mjData* d) {
|
||||
TM_END(mjTIMER_VELOCITY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// returns the next act given the current act_dot, after clamping
|
||||
static mjtNum nextActivation(const mjModel* m, const mjData* d,
|
||||
int actuator_id, int act_adr, mjtNum act_dot) {
|
||||
@@ -239,6 +241,8 @@ static mjtNum nextActivation(const mjModel* m, const mjData* d,
|
||||
return act;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// (qpos, qvel, ctrl, act) => (qfrc_actuator, actuator_force, act_dot)
|
||||
void mj_fwdActuation(const mjModel* m, mjData* d) {
|
||||
TM_START;
|
||||
@@ -246,12 +250,12 @@ void mj_fwdActuation(const mjModel* m, mjData* d) {
|
||||
mjtNum gain, bias, tau;
|
||||
mjtNum *prm, *moment = d->actuator_moment, *force = d->actuator_force;
|
||||
|
||||
// clear outputs
|
||||
mju_zero(d->qfrc_actuator, nv);
|
||||
mju_zero(d->actuator_force, nu);
|
||||
// clear actuator_force
|
||||
mju_zero(force, nu);
|
||||
|
||||
// disabled or no actuation: return
|
||||
if (nu == 0 || mjDISABLED(mjDSBL_ACTUATION)) {
|
||||
mju_zero(d->qfrc_actuator, nv);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -328,6 +332,11 @@ void mj_fwdActuation(const mjModel* m, mjData* d) {
|
||||
|
||||
// force = gain .* [ctrl/act] + bias
|
||||
for (int i=0; i < nu; i++) {
|
||||
// skip if disabled
|
||||
if (mj_actuatorDisabled(m, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip actuator plugins -- these are handled after builtin actuator types
|
||||
if (m->actuator_plugin[i] >= 0) {
|
||||
continue;
|
||||
@@ -457,7 +466,6 @@ void mj_fwdActuation(const mjModel* m, mjData* d) {
|
||||
|
||||
// add up all non-constraint forces, compute qacc_smooth
|
||||
void mj_fwdAcceleration(const mjModel* m, mjData* d) {
|
||||
mj_markStack(d);
|
||||
int nv = m->nv;
|
||||
|
||||
// qforce = sum of all non-constraint forces
|
||||
@@ -468,8 +476,6 @@ void mj_fwdAcceleration(const mjModel* m, mjData* d) {
|
||||
|
||||
// qacc_smooth = M \ qfr_smooth
|
||||
mj_solveM(m, d, d->qacc_smooth, d->qfrc_smooth, 1);
|
||||
|
||||
mj_freeStack(d);
|
||||
}
|
||||
|
||||
|
||||
@@ -685,7 +691,8 @@ static void mj_advance(const mjModel* m, mjData* d,
|
||||
int actadr = m->actuator_actadr[i];
|
||||
int actadr_end = actadr + m->actuator_actnum[i];
|
||||
for (int j=actadr; j < actadr_end; j++) {
|
||||
d->act[j] = nextActivation(m, d, i, j, act_dot[j]);
|
||||
// if disabled, set act_dot to 0
|
||||
d->act[j] = nextActivation(m, d, i, j, mj_actuatorDisabled(m, i) ? 0 : act_dot[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ void mj_defaultOption(mjOption* opt) {
|
||||
opt->mpr_iterations = 50;
|
||||
opt->disableflags = 0;
|
||||
opt->enableflags = 0;
|
||||
opt->disableactuator = 0;
|
||||
|
||||
// sdf collisions
|
||||
opt->sdf_initpoints = 40;
|
||||
|
||||
@@ -1784,7 +1784,15 @@ void mj_local2Global(mjData* d, mjtNum xpos[3], mjtNum xmat[9],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// return 1 if actuator i is disabled, 0 otherwise
|
||||
int mj_actuatorDisabled(const mjModel* m, int i) {
|
||||
int group = m->actuator_group[i];
|
||||
if (group < 0 || group > 30) {
|
||||
return 0;
|
||||
} else {
|
||||
return m->opt.disableactuator & (1 << group) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// sum all body masses
|
||||
mjtNum mj_getTotalmass(const mjModel* m) {
|
||||
|
||||
@@ -204,6 +204,9 @@ MJAPI void mj_local2Global(mjData* d, mjtNum xpos[3], mjtNum xmat[9],
|
||||
const mjtNum pos[3], const mjtNum quat[4],
|
||||
int body, mjtByte sameframe);
|
||||
|
||||
// return 1 if actuator i is disabled, 0 otherwise
|
||||
MJAPI int mj_actuatorDisabled(const mjModel* m, int i);
|
||||
|
||||
// sum all body masses
|
||||
MJAPI mjtNum mj_getTotalmass(const mjModel* m);
|
||||
|
||||
|
||||
@@ -1072,6 +1072,11 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
if (vopt->flags[mjVIS_ACTUATOR] && (category & catmask)) {
|
||||
for (int i=0; i < m->nu; i++) {
|
||||
if (vopt->actuatorgroup[mjMAX(0, mjMIN(mjNGROUP-1, m->actuator_group[i]))]) {
|
||||
// skip if disabled
|
||||
if (mj_actuatorDisabled(m, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine extended range
|
||||
mjtNum rng[3] = {-1, 0, +1};
|
||||
mjtNum rmin = -1, rmax = 1, act = 0;
|
||||
|
||||
@@ -93,13 +93,13 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
"inttotal", "interval", "tolrange"},
|
||||
{">"},
|
||||
|
||||
{"option", "*", "26",
|
||||
{"option", "*", "27",
|
||||
"timestep", "apirate", "impratio", "tolerance", "ls_tolerance", "noslip_tolerance",
|
||||
"mpr_tolerance", "gravity", "wind", "magnetic", "density", "viscosity",
|
||||
"o_margin", "o_solref", "o_solimp", "o_friction",
|
||||
"integrator", "cone", "jacobian",
|
||||
"solver", "iterations", "ls_iterations", "noslip_iterations", "mpr_iterations",
|
||||
"sdf_iterations", "sdf_initpoints"},
|
||||
"sdf_iterations", "sdf_initpoints", "actuatorgroupdisable"},
|
||||
{"<"},
|
||||
{"flag", "?", "22", "constraint", "equality", "frictionloss", "limit", "contact",
|
||||
"passive", "gravity", "clampctrl", "warmstart",
|
||||
@@ -1036,6 +1036,22 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
|
||||
ReadAttrInt(section, "sdf_iterations", &opt->sdf_iterations);
|
||||
ReadAttrInt(section, "sdf_initpoints", &opt->sdf_initpoints);
|
||||
|
||||
// actuatorgroupdisable
|
||||
constexpr int num_bitflags = 31;
|
||||
int disabled_act_groups[num_bitflags];
|
||||
int num_found = ReadAttr(section, "actuatorgroupdisable", num_bitflags, disabled_act_groups,
|
||||
text, false, false);
|
||||
for (int i=0; i < num_found; i++) {
|
||||
int group = disabled_act_groups[i];
|
||||
if (group < 0 ) {
|
||||
throw mjXError(section, "disabled actuator group value must be non-negative");
|
||||
}
|
||||
if (group > num_bitflags - 1) {
|
||||
throw mjXError(section, "disabled actuator group value cannot exceed 30");
|
||||
}
|
||||
opt->disableactuator |= (1 << group);
|
||||
}
|
||||
|
||||
// read disable sub-element
|
||||
XMLElement* elem = FindSubElem(section, "flag");
|
||||
if (elem) {
|
||||
|
||||
@@ -909,6 +909,16 @@ void mjXWriter::Option(XMLElement* root) {
|
||||
WriteAttrInt(section, "sdf_iterations", model->option.sdf_iterations, opt.sdf_iterations);
|
||||
WriteAttrInt(section, "sdf_initpoints", model->option.sdf_initpoints, opt.sdf_initpoints);
|
||||
|
||||
// actuator group disable
|
||||
int disabled_groups[31];
|
||||
int ndisabled = 0;
|
||||
for (int i = 0; i < 31; ++i) {
|
||||
if (model->option.disableactuator & (1 << i)) {
|
||||
disabled_groups[ndisabled++] = i;
|
||||
}
|
||||
}
|
||||
WriteAttr(section, "actuatorgroupdisable", ndisabled, disabled_groups);
|
||||
|
||||
// write disable/enable flags if any of them are set; invert while writing
|
||||
if (model->option.disableflags || model->option.enableflags) {
|
||||
XMLElement* sub = InsertEnd(section, "flag");
|
||||
|
||||
@@ -984,5 +984,79 @@ TEST_F(ActEarlyTest, DoesntChangeStateInMjForward) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(ActuatorTest, DisableActuator) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="slide" type="slide" axis="1 0 0"/>
|
||||
<geom size="1" mass="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<motor joint="slide" gear="2" group="0"/>
|
||||
<position joint="slide" kp="1" group="1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
mjData* data = mj_makeData(model);
|
||||
|
||||
data->ctrl[0] = 1.0;
|
||||
data->ctrl[1] = 1.0;
|
||||
|
||||
mj_forward(model, data);
|
||||
EXPECT_EQ(data->qfrc_actuator[0], 3.0);
|
||||
|
||||
model->opt.disableactuator = 1 << 0;
|
||||
mj_forward(model, data);
|
||||
EXPECT_EQ(data->qfrc_actuator[0], 1.0);
|
||||
|
||||
model->opt.disableactuator = 1 << 1;
|
||||
mj_forward(model, data);
|
||||
EXPECT_EQ(data->qfrc_actuator[0], 2.0);
|
||||
|
||||
mj_deleteData(data);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(ActuatorTest, DisableActuatorOutOfRange) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="slide" type="slide" axis="1 0 0"/>
|
||||
<geom size="1" mass="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<motor joint="slide" gear="-1" group="-1"/>
|
||||
<motor joint="slide" gear="5" group="0"/>
|
||||
<motor joint="slide" gear="31" group="31"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
mjData* data = mj_makeData(model);
|
||||
|
||||
data->ctrl[0] = 1.0;
|
||||
data->ctrl[1] = 1.0;
|
||||
data->ctrl[2] = 1.0;
|
||||
|
||||
// all actuators active
|
||||
mj_forward(model, data);
|
||||
EXPECT_EQ(data->qfrc_actuator[0], 35.0);
|
||||
|
||||
// set all bits of disableactuator, only group 1 is disabled
|
||||
model->opt.disableactuator = ~0;
|
||||
mj_forward(model, data);
|
||||
EXPECT_EQ(data->qfrc_actuator[0], 30.0);
|
||||
|
||||
mj_deleteData(data);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<mujoco>
|
||||
<visual>
|
||||
<global elevation="-10"/>
|
||||
</visual>
|
||||
|
||||
<compiler angle="radian"/>
|
||||
|
||||
<!-- Note: actuator groups 1, 2, are disabled by default -->
|
||||
<option actuatorgroupdisable="1 2" cone="elliptic"/>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1=".3 .5 .7" rgb2="0 0 0" width="32" height="512"/>
|
||||
<texture name="body" type="cube" builtin="flat" mark="cross" width="128" height="128" rgb1="0.8 0.6 0.4" rgb2="0.8 0.6 0.4" markrgb="1 1 1" random="0.01"/>
|
||||
<material name="body" texture="body" texuniform="true" rgba="0.8 0.6 .4 1"/>
|
||||
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
|
||||
<material name="grid" texture="grid" texrepeat="6 2" texuniform="false" reflectance=".2"/>
|
||||
</asset>
|
||||
|
||||
<default>
|
||||
<general ctrlrange="-1 1"/>
|
||||
<geom friction=".4"/>
|
||||
<default class="decor">
|
||||
<geom type="cylinder" size=".03" fromto="0 -.03 0 0 .03 0" rgba="1 1 1 1"/>
|
||||
</default>
|
||||
<default class="arm">
|
||||
<joint damping="5" axis="0 -1 0"/>
|
||||
<geom type="capsule" size=".02" material="body"/>
|
||||
</default>
|
||||
<default class="object">
|
||||
<geom type="cylinder" size="0.1" rgba="0 .9 0 1" fromto="0 -.03 0 0 .03 0"
|
||||
solref="-10000 -10" priority="1"/>
|
||||
<joint group="3"/>
|
||||
</default>
|
||||
|
||||
<!-- Note: motors, position, intvelocity actuators are in groups 0, 1, 2, respectively -->
|
||||
<default class="motor">
|
||||
<motor group="0" gear="30" ctrlrange="-1 1"/>
|
||||
</default>
|
||||
<default class="position">
|
||||
<position group="1" kp="60"/>
|
||||
</default>
|
||||
<default class="intvelocity">
|
||||
<intvelocity group="2" kp="60" ctrlrange="-.5 .5"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom name="floor" type="plane" size=".6 .2 0.01" material="grid"/>
|
||||
<geom name="wall0" type="plane" size=".4 .2 0.01" material="grid" pos="-.6 0 .4" zaxis="1 0 0"/>
|
||||
<geom name="wall1" type="plane" size=".4 .2 0.01" material="grid" pos=".6 0 .4" zaxis="-1 0 0"/>
|
||||
<light pos="-.6 0 3" mode="targetbody" target="hand"/>
|
||||
<light pos=".6 0 3" mode="targetbody" target="hand"/>
|
||||
<body name="arm" pos="-.6 0 .6" childclass="arm">
|
||||
<joint name="arm" range="-1.5 1.5"/>
|
||||
<geom class="decor"/>
|
||||
<geom name="arm" fromto="0 0 0 .7 0 0"/>
|
||||
<body pos=".7 0 0">
|
||||
<joint name="hand" range="-4.65 1.5"/>
|
||||
<geom class="decor"/>
|
||||
<geom fromto="0 0 0 0 0 0.4"/>
|
||||
<body name="hand" pos="0 0 0.4">
|
||||
<geom name="hand" type="sphere" size=".06" mass="0"/>
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
|
||||
<body childclass="object" pos="-.3 0 .2">
|
||||
<joint axis="1 0 0" type="slide"/>
|
||||
<joint axis="0 0 1" type="slide"/>
|
||||
<joint axis="0 1 0"/>
|
||||
<geom/>
|
||||
</body>
|
||||
<body childclass="object" pos="-.2 0 .4">
|
||||
<joint axis="1 0 0" type="slide"/>
|
||||
<joint axis="0 0 1" type="slide"/>
|
||||
<joint axis="0 1 0"/>
|
||||
<geom/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<contact>
|
||||
<exclude body1="world" body2="arm"/>
|
||||
<pair geom1="arm" geom2="hand"/>
|
||||
</contact>
|
||||
|
||||
<actuator>
|
||||
<motor class="motor" name="arm torque" joint="arm"/>
|
||||
<motor class="motor" name="hand torque" joint="hand"/>
|
||||
<position class="position" name="arm position" joint="arm" ctrlrange="-1.5 1.5"/>
|
||||
<position class="position" name="hand position" joint="hand" ctrlrange="-1.5 4.65"/>
|
||||
<intvelocity class="intvelocity" name="arm velocity" joint="arm" actrange="-1.5 1.5"/>
|
||||
<intvelocity class="intvelocity" name="hand velocity" joint="hand" actrange="-1.5 4.65"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
@@ -31,6 +31,15 @@ namespace {
|
||||
|
||||
using HeaderTest = MujocoTest;
|
||||
|
||||
TEST_F(HeaderTest, IntsHave4Bytes) {
|
||||
EXPECT_EQ(4, sizeof(int));
|
||||
}
|
||||
|
||||
TEST_F(HeaderTest, IntsHaveAtLeast31Bits) {
|
||||
int shift_left_30 = 1 << 30;
|
||||
EXPECT_GT(shift_left_30, 0);
|
||||
}
|
||||
|
||||
TEST_F(HeaderTest, EnumsAreInts) {
|
||||
EXPECT_EQ(sizeof(mjtDisableBit), sizeof(int));
|
||||
EXPECT_EQ(sizeof(mjtEnableBit), sizeof(int));
|
||||
|
||||
@@ -1049,6 +1049,44 @@ TEST_F(ActuatorParseTest, MusclesSmoothdynNegative) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("muscle tausmooth cannot be negative"));
|
||||
}
|
||||
|
||||
TEST_F(ActuatorParseTest, GroupDisable) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<option actuatorgroupdisable="0 3 8 3"/>
|
||||
<!-- note: repeated numbers are okay -->
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->opt.disableactuator, (1<<0) + (1<<3) + (1<<8));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(ActuatorParseTest, GroupDisableNegative) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<option actuatorgroupdisable="0 -3 5"/>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("must be non-negative"));
|
||||
}
|
||||
|
||||
TEST_F(ActuatorParseTest, GroupDisableTooBig) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<option actuatorgroupdisable="0 31"/>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("cannot exceed 30"));
|
||||
}
|
||||
|
||||
// ------------- test sensor parsing -------------------------------------------
|
||||
|
||||
using SensorParseTest = MujocoTest;
|
||||
|
||||
@@ -4968,6 +4968,7 @@ public unsafe struct mjOption_ {
|
||||
public int mpr_iterations;
|
||||
public int disableflags;
|
||||
public int enableflags;
|
||||
public int disableactuator;
|
||||
public int sdf_initpoints;
|
||||
public int sdf_iterations;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user