Add adhesion actuators.
- Adhesion actuators using contact normals as force transmission mechanism. - Related video: https://youtu.be/HdBue4MUZys Closes #229 PiperOrigin-RevId: 464389367 Change-Id: I9f69b3cd152d957e8f65870d208788463c036a6d
This commit is contained in:
committed by
Copybara-Service
parent
5c5449bf82
commit
3d77eb1ef4
+60
-5
@@ -2386,6 +2386,11 @@ slidersite, cranksite.
|
||||
All :ref:`muscle <muscle>` attributes are available here except: name, class, joint, jointinparent, site, tendon,
|
||||
slidersite, cranksite.
|
||||
|
||||
:el-prefix:`default/` **adhesion** (?)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All :ref:`adhesion <adhesion>` attributes are available here except: name, class, body.
|
||||
|
||||
.. _custom:
|
||||
|
||||
**custom** (*)
|
||||
@@ -4287,7 +4292,7 @@ specify them independently.
|
||||
Identical to joint, except that for ball and free joints, the 3d rotation axis given by gear is defined in the parent
|
||||
frame (which is the world frame for free joints) rather than the child frame.
|
||||
:at:`site`: :at-val:`string, optional`
|
||||
This actuator can applies force and torque at a site. The gear vector defines a 3d translation axis followed by a 3d
|
||||
This transmission can apply force and torque at a site. The gear vector defines a 3d translation axis followed by a 3d
|
||||
rotation axis. Both are defined in the site's frame. This can be used to model jets and propellers. The effect is
|
||||
similar to actuating a free joint, and the actuator length is again defined as zero. One difference from the joint
|
||||
and jointinparent transmissions above is that here the actuator operates on a site rather than a joint, but this
|
||||
@@ -4295,6 +4300,11 @@ specify them independently.
|
||||
that for site transmissions both the translation and rotation axes are defined in local coordinates. In contrast,
|
||||
translation is global and rotation is local for joint, and both translation and rotation are global for
|
||||
jointinparent.
|
||||
:at:`body`: :at-val:`string, optional`
|
||||
This transmission can apply linear forces at contact points in the direction of the contact normal. The set of
|
||||
contacts is all those belonging to the specified :at:`body`. This can be used to model natural active adhesion
|
||||
mechanisms like the feet of geckos and insects. The actuator length is again defined as zero. For more information,
|
||||
see the :ref:`adhesion<adhesion>` shortcut below.
|
||||
:at:`tendon`: :at-val:`string, optional`
|
||||
If specified, the actuator acts on the given tendon. The actuator length equals the tendon length times the gear
|
||||
ratio. Both spatial and fixed tendons can be used.
|
||||
@@ -4487,7 +4497,9 @@ This element has one custom attribute in addition to the common attributes:
|
||||
:el-prefix:`actuator/` **damper** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This element is an active damper which produces a force proportional to both velocity and control: ``F = - kv * velocity * control``, where ``kv`` must be nonnegative. :at:`ctrlrange` is required and must also be nonnegative. The underlying :el:`general` attributes are set as follows:
|
||||
This element is an active damper which produces a force proportional to both velocity and control: ``F = - kv * velocity
|
||||
* control``, where ``kv`` must be nonnegative. :at:`ctrlrange` is required and must also be nonnegative. The underlying
|
||||
:el:`general` attributes are set as follows:
|
||||
|
||||
=========== ======= ========= =======
|
||||
Attribute Setting Attribute Setting
|
||||
@@ -4501,8 +4513,9 @@ ctrllimited true
|
||||
|
||||
This element has one custom attribute in addition to the common attributes:
|
||||
|
||||
.. |actuator/damper attrib list| replace::
|
||||
:at:`name`, :at:`class`, :at:`group`, :at:`ctrllimited`, :at:`forcelimited`, :at:`ctrlrange`, :at:`forcerange`, :at:`lengthrange`, :at:`gear`, :at:`cranklength`, :at:`joint`, :at:`jointinparent`, :at:`tendon`, :at:`cranksite`, :at:`slidersite`, :at:`site`, :at:`user`
|
||||
.. |actuator/damper attrib list| replace:: :at:`name`, :at:`class`, :at:`group`, :at:`ctrllimited`, :at:`forcelimited`,
|
||||
:at:`ctrlrange`, :at:`forcerange`, :at:`lengthrange`, :at:`gear`, :at:`cranklength`, :at:`joint`,
|
||||
:at:`jointinparent`, :at:`tendon`, :at:`cranksite`, :at:`slidersite`, :at:`site`, :at:`user`
|
||||
|
||||
|actuator/damper attrib list|
|
||||
Same as in actuator/ :ref:`general <general>`.
|
||||
@@ -4593,10 +4606,52 @@ This element has nine custom attributes in addition to the common attributes:
|
||||
:at:`fvmax`: :at-val:`real, "1.2"`
|
||||
Active force generated at saturating lengthening velocity, relative to the peak rest force.
|
||||
|
||||
.. _adhesion:
|
||||
|
||||
:el-prefix:`actuator/` **adhesion** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. youtube:: HdBue4MUZys
|
||||
:align: right
|
||||
:height: 200px
|
||||
|
||||
This element defines an active adhesion actuator which injects force at contacts in the normal direction. On the right
|
||||
is a video demonstrating the this actuator type. The model shown in the video can be found `here
|
||||
<https://github.com/deepmind/mujoco/tree/main/model/adhesion>`_. The transmission target is a :el:`body`, and adhesive
|
||||
forces are injected into all contacts invloving geoms which belong to this body. The force is devided equally between
|
||||
multiple active contacts. Because it requires contact, it cannot apply force at a distance, and is more like the active
|
||||
adhesion on the feet of geckos and insects rather than an industrial vaccum gripper. Adhesion actuators' length is
|
||||
always 0. :at:`ctrlrange` is required and must also be nonnegative (no repulsive forces are allowed). The underlying
|
||||
:el:`general` attributes are set as follows:
|
||||
|
||||
=========== ======= =========== ========
|
||||
Attribute Setting Attribute Setting
|
||||
=========== ======= =========== ========
|
||||
dyntype none dynprm 1 0 0
|
||||
gaintype fixed gainprm gain 0 0
|
||||
biastype none biasprm 0 0 0
|
||||
trntype body ctrllimited true
|
||||
=========== ======= =========== ========
|
||||
|
||||
|
||||
This element has a subset of the common attributes and two custom attributes.
|
||||
|
||||
.. |actuator/adhesion attrib list| replace:: :at:`name`, :at:`class`, :at:`group`,
|
||||
:at:`forcelimited`, :at:`ctrlrange`, :at:`forcerange`, :at:`user`
|
||||
|
||||
|actuator/adhesion attrib list|
|
||||
Same as in actuator/ :ref:`general <general>`.
|
||||
:at:`body`: :at-val:`string, required`
|
||||
The actuator acts on all contacts involving this body's geoms.
|
||||
:at:`gain`: :at-val:`real, "1"`
|
||||
Gain of the adhesion actuator, units of force. The total adhesion force applied by the actuator is the control value
|
||||
multiplied by the gain. This force is distributed equally between all the contacts involving geoms belonging to the
|
||||
target body.
|
||||
|
||||
.. _sensor:
|
||||
|
||||
**sensor** (*)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This is a grouping element for sensor definitions. It does not have attributes. The outputs of all sensors are
|
||||
concatenated in the field mjData.sensordata which has size mjModel.nsensordata. This data is not used in any internal
|
||||
|
||||
+36
-33
@@ -7,9 +7,11 @@ Upcoming version (not yet released)
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
- Added :ref:`adhesion actuators<adhesion>`.
|
||||
- Added an `active adhesion example model <https://github.com/deepmind/mujoco/tree/main/model/adhesion>`_.
|
||||
- Added :ref:`mj_jacSubtreeCom` for computing the translational Jacobian of the center-of-mass of a subtree.
|
||||
- Added moment of inertia computation for concave meshes. This is a breaking change, to get back to the previous
|
||||
behavior set the compiler flag :at:`exactmeshinertia` to false.
|
||||
- Added moment of inertia computation for concave meshes. This is currently activated by setting the compiler flag
|
||||
:at:`exactmeshinertia` to ``true`` (defaults to ``false``). This default may change in the future.
|
||||
- Added parameter :at:`shellinertia` in :at:`geom` for treating a mesh as a boundary mesh (shell) for inertia
|
||||
computations. This is currently supported only for meshes.
|
||||
- Raise error if the orientation of mesh faces is not consistent, which causes the inertia computations to be
|
||||
@@ -17,6 +19,7 @@ General
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
- Fixed rendering of some transparent geoms in relflection.
|
||||
|
||||
Version 2.2.1 (July 18, 2022)
|
||||
-----------------------------
|
||||
@@ -24,45 +27,45 @@ Version 2.2.1 (July 18, 2022)
|
||||
General
|
||||
^^^^^^^
|
||||
|
||||
- Added ``mjd_transitionFD`` to compute efficient finite difference approximations of the state-transition and
|
||||
control-transition matrices, :ref:`see here<derivatives>` for more details.
|
||||
- Added derivatives for the ellipsoid fluid model.
|
||||
- Added ``ctrl`` attribute to :ref:`keyframes<keyframe>`.
|
||||
- Added ``clock`` sensor which :ref:`measures time<sensor-clock>`.
|
||||
- Added visualisation groups to skins.
|
||||
- Added actuator visualisation for ``free`` and ``ball`` joints and for actuators with ``site`` transmission.
|
||||
- Added visualisation for actuator activations.
|
||||
- Added ``<intvelocity>`` actuator shortcut for "integrated velocity" actuators, documented :ref:`here <intvelocity>`.
|
||||
- Added ``<damper>`` actuator shortcut for active-damping actuators, documented :ref:`here <damper>`.
|
||||
- ``mju_rotVecMat`` and ``mju_rotVecMatT`` now support in-place multiplication.
|
||||
- ``mjData.ctrl`` values are no longer clamped in-place, remain untouched by the engine.
|
||||
- Arrays in mjData's buffer now align to 64-byte boundaries rather than 8-byte.
|
||||
- Add memory poisoning when building with Address Sanitizer (ASAN) and Memory Sanitizer (MSAN). This allows ASAN to
|
||||
detect reads and writes to regions in ``mjModel.buffer`` and ``mjData.buffer`` that do not lie within an array, and
|
||||
for MSAN to detect reads from uninitialised fields in ``mjData`` following ``mj_resetData``.
|
||||
- Add a `slider-crank example <https://github.com/deepmind/mujoco/tree/2.2.1/model/slider_crank>`_ to ``model/``.
|
||||
1. Added ``mjd_transitionFD`` to compute efficient finite difference approximations of the state-transition and
|
||||
control-transition matrices, :ref:`see here<derivatives>` for more details.
|
||||
#. Added derivatives for the ellipsoid fluid model.
|
||||
#. Added ``ctrl`` attribute to :ref:`keyframes<keyframe>`.
|
||||
#. Added ``clock`` sensor which :ref:`measures time<sensor-clock>`.
|
||||
#. Added visualisation groups to skins.
|
||||
#. Added actuator visualisation for ``free`` and ``ball`` joints and for actuators with ``site`` transmission.
|
||||
#. Added visualisation for actuator activations.
|
||||
#. Added ``<intvelocity>`` actuator shortcut for "integrated velocity" actuators, documented :ref:`here <intvelocity>`.
|
||||
#. Added ``<damper>`` actuator shortcut for active-damping actuators, documented :ref:`here <damper>`.
|
||||
#. ``mju_rotVecMat`` and ``mju_rotVecMatT`` now support in-place multiplication.
|
||||
#. ``mjData.ctrl`` values are no longer clamped in-place, remain untouched by the engine.
|
||||
#. Arrays in mjData's buffer now align to 64-byte boundaries rather than 8-byte.
|
||||
#. Added memory poisoning when building with Address Sanitizer (ASAN) and Memory Sanitizer (MSAN). This allows ASAN to
|
||||
detect reads and writes to regions in ``mjModel.buffer`` and ``mjData.buffer`` that do not lie within an array, and
|
||||
for MSAN to detect reads from uninitialised fields in ``mjData`` following ``mj_resetData``.
|
||||
#. Added a `slider-crank example model <https://github.com/deepmind/mujoco/tree/main/model/slider_crank>`_.
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
|
||||
- :ref:`Activation clamping <CActRange>` was not being applied in the :ref:`implicit integrator<geIntegration>`.
|
||||
- Stricter parsing of orientation specifiers. Before this change, a specification that included both ``quat`` and an
|
||||
:ref:`alternative specifier<COrientation>` e.g., ``<geom ... quat=".1 .2 .3 .4" euler="10 20 30">``, would lead to the
|
||||
``quat`` being ignored and only ``euler`` being used. After this change a parse error will be thrown.
|
||||
- Stricter parsing of XML attributes. Before this change an erroneous XML snippet like ``<geom size="1/2 3 4">`` would
|
||||
have been parsed as ``size="1 0 0"`` and no error would have been thrown. Now throws an error.
|
||||
- Trying to load a ``NaN`` via XML like ``<geom size="1 NaN 4">``, while allowed for debugging purposes, will now print
|
||||
a warning.
|
||||
- Fixed null pointer dereference in ``mj_loadModel``.
|
||||
- Fixed memory leaks when loading an invalid model from MJB.
|
||||
- Integer overflows are now avoided when computing ``mjModel`` buffer sizes.
|
||||
- Added missing warning string for ``mjWARN_BADCTRL``.
|
||||
15. :ref:`Activation clamping <CActRange>` was not being applied in the :ref:`implicit integrator<geIntegration>`.
|
||||
#. Stricter parsing of orientation specifiers. Before this change, a specification that included both ``quat`` and an
|
||||
:ref:`alternative specifier<COrientation>` e.g., ``<geom ... quat=".1 .2 .3 .4" euler="10 20 30">``, would lead to
|
||||
the ``quat`` being ignored and only ``euler`` being used. After this change a parse error will be thrown.
|
||||
#. Stricter parsing of XML attributes. Before this change an erroneous XML snippet like ``<geom size="1/2 3 4">`` would
|
||||
have been parsed as ``size="1 0 0"`` and no error would have been thrown. Now throws an error.
|
||||
#. Trying to load a ``NaN`` via XML like ``<geom size="1 NaN 4">``, while allowed for debugging purposes, will now print
|
||||
a warning.
|
||||
#. Fixed null pointer dereference in ``mj_loadModel``.
|
||||
#. Fixed memory leaks when loading an invalid model from MJB.
|
||||
#. Integer overflows are now avoided when computing ``mjModel`` buffer sizes.
|
||||
#. Added missing warning string for ``mjWARN_BADCTRL``.
|
||||
|
||||
Packaging
|
||||
^^^^^^^^^
|
||||
|
||||
- Changed MacOS packaging so that the copy of ``mujoco.framework`` embedded in ``MuJoCo.app`` can be used to build
|
||||
applications externally.
|
||||
23. Changed MacOS packaging so that the copy of ``mujoco.framework`` embedded in ``MuJoCo.app`` can be used to build
|
||||
applications externally.
|
||||
|
||||
|
||||
Version 2.2.0 (May 23, 2022)
|
||||
|
||||
+22
-6
@@ -256,12 +256,28 @@ actuator works. The user can set them independently for maximum flexibility, or
|
||||
<CActuator>` which instantiate common actuator types.
|
||||
|
||||
Transmission
|
||||
|
||||
Each actuator has a scalar length :math:`l_i(q)` defined by the type of transmission and its parameters. The gradient
|
||||
:math:`\nabla l_i` is an :math:`n_V`-dimensional column vector of moment arms. It determines the mapping from scalar
|
||||
:math:`\nabla l_i` is an :math:`n_V`-dimensional vector of moment arms. It determines the mapping from scalar
|
||||
actuator force to joint force. The transmission properties are determined by the MuJoCo object to which the actuator
|
||||
is attached; the possible attachment object types are joint, tendon, site and slider-crank. The latter can also be
|
||||
modeled explicitly by creating MuJoCo bodies and coupling them with equality constraints to the rest of the system,
|
||||
but that would be less efficient.
|
||||
is attached; the possible attachment object types are :at:`joint`, :at:`tendon`, :at:`jointinparent`,
|
||||
:at:`slider-crank`, :at:`site`, and :at:`body`. The :at:`joint` and :at:`tendon` transmission types act as expected
|
||||
mechanically and correspond to the actuator applying forces or torques to the target object.
|
||||
|
||||
The :at:`jointinparent` transmission is unique to ball and free joint and asserts that rotation should be measured
|
||||
in the parent rather than child frame.
|
||||
|
||||
:at:`slider-crank` `transmissions <https://en.wikipedia.org/wiki/Slider-crank_linkage>`_ transform a linear force to
|
||||
a torque, as in a piston-driven combustion engine. `This model
|
||||
<https://github.com/deepmind/mujoco/tree/main/model/slider_crank>`_ contains pedagogical examples. Slider-cranks can
|
||||
also be modeled explicitly by creating MuJoCo bodies and coupling them with equality constraints to the rest of the
|
||||
system, but that would be less efficient.
|
||||
|
||||
:at:`site` and :at:`body` are degenerate transmission targets, as their length :math:`l_i(q)` is always 0.
|
||||
They can therefore not be used to maintain a desired length value, as with a position actuator. Site
|
||||
transmissions correspond to applying a Cartsian force/torque at the site, while :el:`body` transmissions correspond
|
||||
to applying forces at contact points belonging to a body. For more information about adhesion, see the
|
||||
:ref:`adhesion<adhesion>` shorcut documentation.
|
||||
|
||||
Activation dynamics
|
||||
Some actuators such as pneumatic and hydraulic cylinders as well as biological muscles have an internal state called
|
||||
@@ -1458,14 +1474,14 @@ The top-level function :ref:`mj_step` invokes the sequence of computations below
|
||||
cameras and lights. It also normalizes all quaternions, just in case.
|
||||
#. Compute the body inertias and joint axes, in global frames centered at the centers of mass of the corresponding
|
||||
kinematic subtrees (to improve floating-point accuracy).
|
||||
#. Compute the tendon lengths and moment arms. This includes the computation of minimal-length paths for spatial
|
||||
tendons.
|
||||
#. Compute the actuator lengths and moment arms.
|
||||
#. Compute the composite rigid body inertias and construct the joint-space inertia matrix.
|
||||
#. Compute the sparse factorization of the joint-space inertia matrix.
|
||||
#. Construct the list of active contacts. This includes both broad-phase and near-phase collision detection.
|
||||
#. Construct the constraint Jacobian and compute the constraint residuals.
|
||||
#. Compute the matrices and vectors needed by the constraint solvers.
|
||||
#. Compute the tendon lengths and moment arms. This includes the computation of minimal-length paths for spatial
|
||||
tendons.
|
||||
#. Compute sensor data that only depends on position, and the potential energy if enabled.
|
||||
#. Compute the tendon and actuator velocities.
|
||||
#. Compute the body velocities and rates of change of the joint axes, again in the global coordinate frames centered at
|
||||
|
||||
+15
-5
@@ -492,9 +492,10 @@ addition, the user can specify the maximum number of iterations, and tolerance l
|
||||
There is also a second Noslip solver, which is a post-processing step enabled by specifying a positive number of
|
||||
noslip iterations. All these algorithm settings can be specified in the :ref:`option <option>` element.
|
||||
|
||||
The default settings work well for most models, but in some cases it is necessary to tune the algorithm. The best way
|
||||
to do this is to experiment with the relevant settings and use the visual profiler in `simulate.cc`_, which shows the timing of different computations as
|
||||
well as solver statistics per iteration. We can offer the following general guidelines and observations:
|
||||
The default settings work well for most models, but in some cases it is necessary to tune the algorithm. The best way to
|
||||
do this is to experiment with the relevant settings and use the visual profiler in `simulate.cc`_, which shows the
|
||||
timing of different computations as well as solver statistics per iteration. We can offer the following general
|
||||
guidelines and observations:
|
||||
|
||||
- The constraint Jacobian should be dense for small models and sparse for large models. The default setting is 'auto';
|
||||
it resolves to dense when the number of degrees of freedom is up to 60, and sparse over 60. Note however that the
|
||||
@@ -545,9 +546,18 @@ Actuator 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 independently. The full functionality can be accessed via the XML element :ref:`general <general>` which allows the user
|
||||
actuator model with transmission, activation dynamics and force generation components that can be specified
|
||||
independently. The full functionality can be accessed via the XML element :ref:`general <general>` which allows the user
|
||||
to create a variety of custom actuators. In addition, MJCF provides shortcuts for configuring common actuators. This is
|
||||
done via the XML elements :ref:`motor <motor>`, :ref:`position <position>`, :ref:`velocity <velocity>`, :ref:`intvelocity <intvelocity>`, :ref:`damper<damper>`, :ref:`cylinder<cylinder>`, and :ref:`muscle <muscle>`. These are *not* separate model elements. Internally MuJoCo supports only one actuator type - which is why when an MJCF model is saved all actuators are written as :el:`general`. Shortcuts create general actuators implicitly, set their attributes to suitable values, and expose a subset of attributes with possibly different names. For example, :el:`position` creates a position servo with attribute :at:`kp` which is the servo gain. However :el:`general` does not have an attribute :at:`kp`. Instead the parser adjusts the gain and bias parameters of the general actuator in a coordinated way so as to mimic a position servo. The same effect could have been achieved by using :el:`general` directly, and setting its attributes to certain values as described below.
|
||||
done via the XML elements :ref:`motor <motor>`, :ref:`position <position>`, :ref:`velocity <velocity>`,
|
||||
:ref:`intvelocity <intvelocity>`, :ref:`damper<damper>`, :ref:`cylinder<cylinder>`, :ref:`muscle <muscle>`, and
|
||||
:ref:`adhesion <adhesion>`. These are *not* separate model elements. Internally MuJoCo supports only one actuator type -
|
||||
which is why when an MJCF model is saved all actuators are written as :el:`general`. Shortcuts create general actuators
|
||||
implicitly, set their attributes to suitable values, and expose a subset of attributes with possibly different names.
|
||||
For example, :el:`position` creates a position servo with attribute :at:`kp` which is the servo gain. However
|
||||
:el:`general` does not have an attribute :at:`kp`. Instead the parser adjusts the gain and bias parameters of the
|
||||
general actuator in a coordinated way so as to mimic a position servo. The same effect could have been achieved by using
|
||||
:el:`general` directly, and setting its attributes to certain values as described below.
|
||||
|
||||
Actuator shortcuts also interact with defaults. Recall that the :ref:`default setting <CDefault>` mechanism involves
|
||||
classes, each of which has a complete collection of dummy elements (one of each element type) used to initialize the
|
||||
|
||||
@@ -183,6 +183,7 @@ typedef enum mjtTrn_ { // type of actuator transmission
|
||||
mjTRN_SLIDERCRANK, // force via slider-crank linkage
|
||||
mjTRN_TENDON, // force on tendon
|
||||
mjTRN_SITE, // force on site
|
||||
mjTRN_BODY, // adhesion force on a body's geoms
|
||||
|
||||
mjTRN_UNDEFINED = 1000 // undefined transmission type
|
||||
} mjtTrn;
|
||||
|
||||
@@ -195,6 +195,7 @@ ENUMS: Mapping[str, EnumDecl] = dict([
|
||||
('mjTRN_SLIDERCRANK', 2),
|
||||
('mjTRN_TENDON', 3),
|
||||
('mjTRN_SITE', 4),
|
||||
('mjTRN_BODY', 5),
|
||||
('mjTRN_UNDEFINED', 1000),
|
||||
]),
|
||||
)),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Active adhesion example
|
||||
|
||||
This example model shows how to use adhesion actuators.
|
||||
|
||||
The video below is a screen capture of a user interacting with the model:
|
||||
|
||||
|
||||
[](https://www.youtube.com/watch?v=HdBue4MUZys)
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<mujoco model="Active adhesion example">
|
||||
|
||||
<size nconmax="500" njmax="1000"/>
|
||||
|
||||
<visual>
|
||||
<headlight diffuse=".2 .2 .2"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<joint damping=".3" axis="0 1 0"/>
|
||||
<!--
|
||||
By adding 3mm of margin yet making solimp barely apply any force until 3mm penetration, we get
|
||||
3mm of "action at a distance". This is important so that small changes in distance don't lead
|
||||
to loss of adhesion
|
||||
-->
|
||||
<geom type="box" friction=".5" margin=".003" solimp="0 .99 .003 .9 6"/>
|
||||
<default class="wall">
|
||||
<geom rgba=".5 .5 .5 .4"/>
|
||||
</default>
|
||||
<default class="mechanical">
|
||||
<geom rgba=".5 .5 .8 1"/>
|
||||
<tendon rgba=".5 .5 .8 1"/>
|
||||
</default>
|
||||
<default class="active_adhesion">
|
||||
<geom rgba=".8 .5 .5 1"/>
|
||||
</default>
|
||||
<default class="object">
|
||||
<geom rgba=".5 .8 .5 1" density="100"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<light pos="0.4 -.15 .6" dir="-1 .5 -1" diffuse=".7 .7 .7"/>
|
||||
<light pos="-.4 -.15 .6" dir="1 .5 -1" diffuse=".7 .7 .7"/>
|
||||
|
||||
<body name="two crates">
|
||||
<joint name="conveyor" type="slide" damping="100" axis="1 0 0"/>
|
||||
<geom size=".2 .1 .01" pos="0 0 -.01"/>
|
||||
<geom size=".01 .08 .031" pos="-.19 0 .03" class="wall"/>
|
||||
<geom size=".01 .08 .031" pos="0 0 .03" class="wall"/>
|
||||
<geom size=".01 .08 .031" pos="0.19 0 .03" class="wall"/>
|
||||
<geom size="0.2 .01 .031" pos="0 -.09 .03" class="wall"/>
|
||||
<geom size="0.2 .01 .031" pos="0 0.09 .03" class="wall"/>
|
||||
</body>
|
||||
|
||||
<body name="arm1" pos="-.1 0 .3" childclass="mechanical">
|
||||
<joint name="arm1"/>
|
||||
<geom type="cylinder" size=".015 .01" zaxis="0 1 0"/>
|
||||
<geom type="capsule" size=".01" fromto="0 0 0 -.12 0 -.07"/>
|
||||
<body name="arm2" pos="-.12 0 -.07">
|
||||
<joint name="arm2"/>
|
||||
<geom type="cylinder" size=".015 .01" zaxis="0 1 0"/>
|
||||
<geom type="capsule" size=".01" fromto="0 0 0 .12 0 -.07"/>
|
||||
<body name="4boxes" pos=".12 0 -.08" childclass="active_adhesion">
|
||||
<site name="force_sensor" group="3"/>
|
||||
<joint name="arm3" damping=".01" pos="0 -.03 0"/>
|
||||
<geom size=".015 .015 .01" pos="0.015 0.015 0"/>
|
||||
<geom size=".015 .015 .01" pos="0.015 -.015 0"/>
|
||||
<geom size=".015 .015 .01" pos="-.015 0.015 0"/>
|
||||
<geom size=".015 .015 .01" pos="-.015 -.015 0"/>
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
|
||||
<body name="box" pos="-.1 0 .05">
|
||||
<freejoint/>
|
||||
<geom size=".05 .05 .05" class="object"/>
|
||||
</body>
|
||||
|
||||
<body name="winch" pos="-.01 0 .35" childclass="mechanical">
|
||||
<joint name="winch" damping="1"/>
|
||||
<geom type="cylinder" size=".015 .01" zaxis="0 1 0"/>
|
||||
<geom type="capsule" size=".01" fromto="0 0 0 .1 0 .05"/>
|
||||
<site name="anchor" pos=".1 0 .04"/>
|
||||
</body>
|
||||
<site name="pulley" pos=".1 0 .32"/>
|
||||
<site name="hook_left" pos=".08 0 .3"/>
|
||||
<site name="hook_right" pos=".12 0 .3"/>
|
||||
<body name="sphere" pos=".1 0 .2" childclass="active_adhesion">
|
||||
<!--
|
||||
Note we are adding damping to a free joint. This is an easy yet unphysical way to prevent
|
||||
the sphere from jiggling too much on the winch tendons. An alternative would be to add fluid
|
||||
density or viscosity.
|
||||
-->
|
||||
<joint type="free" damping=".1"/>
|
||||
<!--
|
||||
The composite balls in the crate have only 3 linear DoFs with condim=1, effectively
|
||||
frictionless point particles. In order to make them stick to the sphere we give the sphere
|
||||
priority 2, to force condim=3.
|
||||
-->
|
||||
<geom type="sphere" size=".03" priority="2"/>
|
||||
<site name="pin_left" pos="-.025 0 .025"/>
|
||||
<site name="pin_right" pos=".025 0 .025"/>
|
||||
</body>
|
||||
|
||||
<composite type="particle" count="4 4 4" spacing="0.025" offset=".11 .01 .1">
|
||||
<geom size=".012" rgba=".5 .8 .5 1" solref=".005 1"/>
|
||||
</composite>
|
||||
</worldbody>
|
||||
|
||||
<equality>
|
||||
<joint joint1="arm1" joint2="arm2" polycoef="0 -.5 0 0 0"/>
|
||||
<joint joint1="arm3" joint2="arm1"/>
|
||||
</equality>
|
||||
|
||||
<!--
|
||||
By using divisor=3 in the pullies we increase the distance by which the hanging sphere moves
|
||||
relative to the motion of the winch arm. One should imagine a double spindle with two radii that
|
||||
creates a ratio of 1.5 between the motion of tendon before the pullies and after the pullies.
|
||||
(1.5 rather than 3 because the tendon has 2 branches and the length is split between them)
|
||||
-->
|
||||
<tendon>
|
||||
<spatial range="0 .19" limited="true" solreflimit=".01 2" class="mechanical">
|
||||
<site site="anchor"/>
|
||||
<site site="pulley"/>
|
||||
<pulley divisor="3"/>
|
||||
<site site="pulley"/>
|
||||
<site site="hook_left"/>
|
||||
<site site="pin_left"/>
|
||||
<pulley divisor="3"/>
|
||||
<site site="pulley"/>
|
||||
<site site="hook_right"/>
|
||||
<site site="pin_right"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
|
||||
<actuator>
|
||||
<position name="conveyor" joint="conveyor" ctrlrange="-.2 .2" ctrllimited="true" kp="400"/>
|
||||
<position name="arm" joint="arm2" ctrlrange="-.8 1" ctrllimited="true" kp="10"/>
|
||||
<adhesion name="adhere_arm" body="4boxes" ctrlrange="0 1" gain="20"/>
|
||||
<position name="winch" joint="winch" ctrlrange="-.7 .5" ctrllimited="true" kp="10"/>
|
||||
<adhesion name="adhere_winch" body="sphere" ctrlrange="0 1" gain="20"/>
|
||||
</actuator>
|
||||
|
||||
<sensor>
|
||||
<force site="force_sensor"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
@@ -65,7 +65,7 @@ PYBIND11_MODULE(_functions, pymodule) {
|
||||
DEF_WITH_OMITTED_PY_ARGS(traits::mj_printSchema,
|
||||
"filename", "buffer", "buffer_sz")(
|
||||
pymodule, [](bool flg_html, bool flg_pad) {
|
||||
constexpr int kBufferSize = 28000;
|
||||
constexpr int kBufferSize = 30000;
|
||||
auto buffer = std::unique_ptr<char[]>(new char[kBufferSize]);
|
||||
const int out_length = InterceptMjErrors(::mj_printSchema)(
|
||||
nullptr, buffer.get(), kBufferSize, flg_html, flg_pad);
|
||||
|
||||
@@ -785,6 +785,48 @@ void mj_transmission(const mjModel* m, mjData* d) {
|
||||
mju_addTo(moment+i*nv, jac, nv); // add the two
|
||||
break;
|
||||
|
||||
case mjTRN_BODY: // body (adhesive contacts)
|
||||
// cannot compute meaningful length, set to 0
|
||||
length[i] = 0;
|
||||
|
||||
// moment is average of all contact normal Jacobians
|
||||
{
|
||||
// find and count all relevant contacts, mark them in efc_force
|
||||
int counter = 0;
|
||||
mjtNum* efc_force = mj_stackAlloc(d, d->nefc);
|
||||
mju_zero(efc_force, d->nefc);
|
||||
for (int j=0; j<d->ncon; j++) {
|
||||
const mjContact* con = d->contact+j;
|
||||
if (m->geom_bodyid[con->geom1]==id || m->geom_bodyid[con->geom2]==id) {
|
||||
if (!con->exclude) {
|
||||
counter++;
|
||||
|
||||
// condim 1 or elliptic cones: normal is in the first row
|
||||
if (con->dim == 1 || m->opt.cone==mjCONE_ELLIPTIC) {
|
||||
efc_force[con->efc_address] = 1;
|
||||
}
|
||||
|
||||
// pyramidal cones: average all pyramid directions
|
||||
else {
|
||||
int npyramid = con->dim-1; // number of frictional directions
|
||||
for (int k=0; k<2*npyramid; k++) {
|
||||
efc_force[con->efc_address+k] = 0.5/npyramid;
|
||||
}
|
||||
}
|
||||
} else if (con->exclude == 1) {
|
||||
// TODO(b/240848298): compute Jacobians for excluded contact (in gap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// moment is average over contact normal Jacobians, make negative for adhesion
|
||||
if (counter) {
|
||||
mj_mulJacTVec(m, d, moment+i*nv, efc_force);
|
||||
mju_scl(moment+i*nv, moment+i*nv, -1.0/counter, nv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
mju_error_i("Unknown transmission type %d", m->actuator_trntype[i]); // SHOULD NOT OCCUR
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ void mj_fwdPosition(const mjModel* m, mjData* d) {
|
||||
mj_comPos(m, d);
|
||||
mj_camlight(m, d);
|
||||
mj_tendon(m, d);
|
||||
mj_transmission(m, d);
|
||||
TM_END(mjTIMER_POS_KINEMATICS);
|
||||
|
||||
TM_RESTART;
|
||||
@@ -111,6 +110,7 @@ void mj_fwdPosition(const mjModel* m, mjData* d) {
|
||||
|
||||
TM_RESTART;
|
||||
mj_makeConstraint(m, d);
|
||||
mj_transmission(m, d);
|
||||
TM_END(mjTIMER_POS_MAKE);
|
||||
|
||||
TM_RESTART;
|
||||
|
||||
@@ -847,8 +847,8 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
|
||||
// site actuators
|
||||
if (m->actuator_trntype[i]==mjTRN_SITE) {
|
||||
// set size of the geometry
|
||||
mju_scl3(sz, m->site_size+3*j, 1.1);
|
||||
// inflate sizes by 5%
|
||||
mju_scl3(sz, m->site_size+3*j, 1.05);
|
||||
|
||||
// make geom
|
||||
mjv_initGeom(thisgeom,
|
||||
@@ -895,6 +895,34 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
FINISH
|
||||
}
|
||||
|
||||
// body actuators
|
||||
else if (m->actuator_trntype[i]==mjTRN_BODY) {
|
||||
// iterate over body's geoms
|
||||
int geomnum = m->body_geomnum[j];
|
||||
int geomadr = m->body_geomadr[j];
|
||||
for (int k=geomadr; k<geomadr+geomnum; k++) {
|
||||
int geomtype = m->geom_type[k];
|
||||
// add inflated geom if it is a regular primitive
|
||||
if (geomtype != mjGEOM_PLANE && geomtype != mjGEOM_HFIELD && geomtype != mjGEOM_MESH) {
|
||||
START
|
||||
// inflate sizes by 5%
|
||||
mju_scl3(sz, m->geom_size+3*k, 1.05);
|
||||
|
||||
// make geom
|
||||
mjv_initGeom(thisgeom,
|
||||
m->geom_type[k], sz,
|
||||
d->geom_xpos + 3*k,
|
||||
d->geom_xmat + 9*k,
|
||||
thisgeom->rgba);
|
||||
|
||||
// set interpolated color
|
||||
f2f(thisgeom->rgba, rgba, 4);
|
||||
|
||||
FINISH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spatial tendon actuators
|
||||
else if (m->actuator_trntype[i]==mjTRN_TENDON && d->ten_wrapnum[j]) {
|
||||
for (int k=d->ten_wrapadr[j]; k<d->ten_wrapadr[j]+d->ten_wrapnum[j]-1; k++) {
|
||||
|
||||
@@ -3461,6 +3461,11 @@ void mjCActuator::Compile(void) {
|
||||
ptarget = model->FindObject(mjOBJ_SITE, target);
|
||||
break;
|
||||
|
||||
case mjTRN_BODY:
|
||||
// get body
|
||||
ptarget = model->FindObject(mjOBJ_BODY, target);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw mjCError(this, "invalid transmission type in actuator '%s' (id = %d)", name.c_str(), id);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ using tinyxml2::XMLElement;
|
||||
|
||||
//---------------------------------- MJCF schema ---------------------------------------------------
|
||||
|
||||
static const int nMJCF = 163;
|
||||
static const int nMJCF = 165;
|
||||
static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
{"mujoco", "!", "1", "model"},
|
||||
{"<"},
|
||||
@@ -145,6 +145,8 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
"gear", "cranklength", "user", "group",
|
||||
"timeconst", "range", "force", "scale",
|
||||
"lmin", "lmax", "vmax", "fpmax", "fvmax"},
|
||||
{"adhesion", "?", "6", "forcelimited", "ctrlrange", "forcerange",
|
||||
"gain", "user", "group"},
|
||||
{">"},
|
||||
|
||||
{"custom", "*", "0"},
|
||||
@@ -262,10 +264,10 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
|
||||
{"actuator", "*", "0"},
|
||||
{"<"},
|
||||
{"general", "*", "25", "name", "class", "group",
|
||||
{"general", "*", "26", "name", "class", "group",
|
||||
"ctrllimited", "forcelimited", "actlimited", "ctrlrange", "forcerange", "actrange",
|
||||
"lengthrange", "gear", "cranklength", "user",
|
||||
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site",
|
||||
"joint", "jointinparent", "tendon", "slidersite", "cranksite", "site", "body",
|
||||
"dyntype", "gaintype", "biastype", "dynprm", "gainprm", "biasprm"},
|
||||
{"motor", "*", "17", "name", "class", "group",
|
||||
"ctrllimited", "forcelimited", "ctrlrange", "forcerange",
|
||||
@@ -303,6 +305,8 @@ static const char* MJCF[nMJCF][mjXATTRNUM] = {
|
||||
"joint", "jointinparent", "tendon", "slidersite", "cranksite",
|
||||
"timeconst", "range", "force", "scale",
|
||||
"lmin", "lmax", "vmax", "fpmax", "fvmax"},
|
||||
{"adhesion", "*", "9", "name", "class", "group",
|
||||
"forcelimited", "ctrlrange", "forcerange", "user", "body", "gain"},
|
||||
{">"},
|
||||
|
||||
{"sensor", "*", "0"},
|
||||
@@ -1426,7 +1430,10 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
pact->trntype = mjTRN_SITE;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (ReadAttrTxt(elem, "body", pact->target)) {
|
||||
pact->trntype = mjTRN_BODY;
|
||||
cnt++;
|
||||
}
|
||||
// check for repeated transmission
|
||||
if (cnt>1) {
|
||||
throw mjXError(elem, "actuator can have at most one of transmission target");
|
||||
@@ -1522,7 +1529,7 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
|
||||
// damper
|
||||
else if (type=="damper") {
|
||||
// clear bias
|
||||
// clear gain
|
||||
mjuu_zerovec(pact->gainprm, mjNGAIN);
|
||||
|
||||
// explicit attributes
|
||||
@@ -1531,10 +1538,10 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
throw mjXError(elem, "damping coefficient cannot be negative");
|
||||
pact->gainprm[2] = -pact->gainprm[2];
|
||||
|
||||
// Require nonnegative range
|
||||
ReadAttr(elem, "ctrlrange", 2, pact->ctrlrange, text);
|
||||
// require nonnegative range
|
||||
ReadAttr(elem, "ctrlrange", 2, pact->ctrlrange, text, true);
|
||||
if (pact->ctrlrange[0]<0 || pact->ctrlrange[1]<0) {
|
||||
throw mjXError(elem, "control range cannot be negative");
|
||||
throw mjXError(elem, "damper control range cannot be negative");
|
||||
}
|
||||
|
||||
// implied parameters
|
||||
@@ -1597,6 +1604,31 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
pact->biastype = mjBIAS_MUSCLE;
|
||||
}
|
||||
|
||||
// adhesion
|
||||
else if (type=="adhesion") {
|
||||
// clear bias, set default gain
|
||||
mjuu_zerovec(pact->biasprm, mjNBIAS);
|
||||
mjuu_zerovec(pact->gainprm, mjNGAIN);
|
||||
pact->gainprm[0] = 1;
|
||||
|
||||
// explicit attributes
|
||||
ReadAttr(elem, "gain", 1, pact->gainprm, text);
|
||||
if (pact->gainprm[0]<0)
|
||||
throw mjXError(elem, "adhesion gain cannot be negative");
|
||||
|
||||
// require nonnegative range
|
||||
ReadAttr(elem, "ctrlrange", 2, pact->ctrlrange, text, true);
|
||||
if (pact->ctrlrange[0]<0 || pact->ctrlrange[1]<0) {
|
||||
throw mjXError(elem, "adhesion control range cannot be negative");
|
||||
}
|
||||
|
||||
// implied parameters
|
||||
pact->ctrllimited = true;
|
||||
pact->dyntype = mjDYN_NONE;
|
||||
pact->gaintype = mjGAIN_FIXED;
|
||||
pact->biastype = mjBIAS_NONE;
|
||||
}
|
||||
|
||||
else { // SHOULD NOT OCCUR
|
||||
throw mjXError(elem, "unrecognized actuator type: %s", type.c_str());
|
||||
}
|
||||
|
||||
@@ -554,6 +554,10 @@ void mjXWriter::OneActuator(XMLElement* elem, mjCActuator* pact, mjCDef* def) {
|
||||
WriteAttrTxt(elem, "site", pact->target);
|
||||
break;
|
||||
|
||||
case mjTRN_BODY:
|
||||
WriteAttrTxt(elem, "body", pact->target);
|
||||
break;
|
||||
|
||||
default: // SHOULD NOT OCCUR
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ TEST_F(CoreSmoothTest, RnePostWeldForceTorqueFreeRotated) {
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------- fluidshape --------------------------------
|
||||
// ------------------------ ellipsoid fluid model ------------------------------
|
||||
|
||||
using EllipsoidFluidTest = MujocoTest;
|
||||
|
||||
@@ -348,5 +348,73 @@ TEST_F(EllipsoidFluidTest, DefaultsPropagate) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
// -------------------------- adhesion actuators -------------------------------
|
||||
|
||||
using AdhesionTest = MujocoTest;
|
||||
|
||||
TEST_F(AdhesionTest, ExpectedAdhesionForce) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<option gravity="0 0 -1"/>
|
||||
|
||||
<worldbody>
|
||||
<body name="static">
|
||||
<!-- small increase to size to ensure contact -->
|
||||
<geom size=".02001" pos=" .01 .01 .07"/>
|
||||
<geom size=".02001" pos="-.01 .01 .07"/>
|
||||
<geom size=".02001" pos=" .01 -.01 .07"/>
|
||||
<geom size=".02001" pos="-.01 -.01 .07"/>
|
||||
</body>
|
||||
<body name="free">
|
||||
<freejoint/>
|
||||
<geom type="box" size=".05 .05 .05" mass="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<adhesion body="static" ctrlrange="0 2"/>
|
||||
<adhesion body="free" ctrlrange="0 2"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
mjData* data = mj_makeData(model);
|
||||
|
||||
// iterate over cone type
|
||||
for (mjtCone cone : {mjCONE_ELLIPTIC, mjCONE_PYRAMIDAL}) {
|
||||
// set cone
|
||||
model->opt.cone = cone;
|
||||
// iterate over condim
|
||||
for (int condim : {1, 3, 4, 6}) {
|
||||
// set condim
|
||||
for (int id=0; id < model->ngeom; id++) {
|
||||
model->geom_condim[id] = condim;
|
||||
}
|
||||
// iterate over actuators
|
||||
for (int id=0; id < 2; id++) {
|
||||
// set ctrl > 1, expect free body to not fall
|
||||
mj_resetData(model, data);
|
||||
data->ctrl[id] = 1.01;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
mj_step(model, data);
|
||||
}
|
||||
// moved down at most 10 microns
|
||||
EXPECT_GT(data->qpos[2], -1e-5);
|
||||
|
||||
// set ctrl < 1, expect free body to fall below 1cm
|
||||
mj_resetData(model, data);
|
||||
data->ctrl[id] = 0.99;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
mj_step(model, data);
|
||||
}
|
||||
// fell lower than 1cm
|
||||
EXPECT_LT(data->qpos[2], -0.01);
|
||||
}
|
||||
}
|
||||
}
|
||||
mj_deleteData(data);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -375,7 +375,7 @@ TEST_F(UserDataTest, RequiresControlRange) {
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("invalid control range for actuator"));
|
||||
EXPECT_THAT(error.data(), HasSubstr("required attribute missing: 'ctrlrange'"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, PositiveControlRange) {
|
||||
|
||||
Reference in New Issue
Block a user