Skip to content

Commit a264d0b

Browse files
yuvaltassacopybara-github
authored andcommitted
Add geom adhesion: contacts that pull, via translated friction cones.
https://youtu.be/GioWwB36XHI The new geom attribute adhesion (units of force, signed; pair-level override) translates the contact friction cone along its normal so that the force origin lies strictly inside it. Consequences: each contact can pull with up to the given force before breaking, and the tangential friction budget becomes mu*(f_N + adhesion) -- the Mohr-Coulomb yield condition with cohesion c = mu*adhesion -- so lightly-squeezed grasps retain a guaranteed friction floor. A translated cone factors exactly into {constant attractive force} + {original cone}, so no solver kernels change. The implementation is this factorization: a constant attraction along contact normals accumulated into the new mjData.qfrc_adhesion (summed into qfrc_passive), plus a bias of adhesive contact rows' reference acceleration (aref += R*adhesion), which makes resting penetration exactly independent of adhesion. Contacts of adhesive pairs remain active throughout the gap zone, producing rows with positive violation whose reference acceleration pulls: a tether that resists pull-off smoothly, captures objects released within the band into steady contact, and detaches at the specified force. Adhesion values of the two geoms combine by sum; explicit pairs override. mj_contactForce reports the net interface force (cone force minus the adhesive pull), whose normal component can now be negative. Negative adhesion is allowed and produces a repulsive offset (air hockey). PiperOrigin-RevId: 950858148 Change-Id: I879c08eba7ae501e5c0f8c2f807167344da4c2bc
1 parent b942c9f commit a264d0b

28 files changed

Lines changed: 784 additions & 50 deletions

doc/XMLreference.rst

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2797,6 +2797,29 @@ helps clarify the role of bodies and geoms in MuJoCo.
27972797
additionally displays an arrow along the tangential surface velocity at the contact point. This attribute can be
27982798
modified at runtime.
27992799

2800+
.. _body-geom-adhesion:
2801+
2802+
.. youtube:: GioWwB36XHI
2803+
:align: right
2804+
:width: 40%
2805+
2806+
:at:`adhesion`: :at-val:`real, "0"`
2807+
Adhesive force of contacts with this geom, in units of force. Geometrically, the friction cone is translated down
2808+
along the normal so that the force origin lies strictly inside it: each contact can pull with up to ``adhesion``
2809+
before breaking, and the friction budget becomes :math:`\mu(f_N + \text{adhesion})`. Contacts resist sliding even
2810+
under zero normal force, the defining property of cohesive materials. This is useful for sticky materials (tape,
2811+
gecko feet, tacky rubber) and as a physical stabilizer for grasping. The adhesion of a contact is the sum of the
2812+
values of the two contacting geoms, or the value of the higher-:ref:`priority<body-geom-priority>` geom if priorities
2813+
differ; an explicit contact :ref:`pair<contact-pair>` overrides both. Note that adhesion is *per contact*: a box face
2814+
resting on a plane generates four contact points and therefore four times the pull-off force of a single-point
2815+
contact. To let adhesion act across a small separation (attraction at a distance), set :ref:`gap<body-geom-gap>` to
2816+
the desired interaction range. This can be used to model magnets. Resting penetration is unaffected by adhesion (the
2817+
compression behavior of the contact is unchanged; only a tensile branch is added), and :ref:`mj_contactForce` reports
2818+
the net interface force, whose normal component can be negative under tension. The underlying model is described in
2819+
the :ref:`Computation chapter<soAdhesion>`. For adhesion as a *controlled* force — switched on and off like a vacuum
2820+
gripper, dividing a total force between a body's contacts and pressing the bodies together — see the :ref:`adhesion
2821+
actuator<actuator-adhesion>`.
2822+
28002823
.. _body-geom-fromto:
28012824

28022825
:at:`fromto`: :at-val:`real(6), optional`
@@ -3488,10 +3511,12 @@ joints and tendons have different sets of attributes, while all geoms in the com
34883511

34893512
.. _composite-geom-surfacevel:
34903513

3514+
.. _composite-geom-adhesion:
3515+
34913516
.. |body/composite/geom attrib list| replace::
34923517
:at:`type`, :at:`contype`, :at:`conaffinity`, :at:`condim`, :at:`group`, :at:`priority`, :at:`size`, :at:`material`,
34933518
:at:`rgba`, :at:`friction`, :at:`mass`, :at:`density`, :at:`solmix`, :at:`solref`, :at:`solimp`, :at:`margin`,
3494-
:at:`gap`, :at:`surfacevel`
3519+
:at:`gap`, :at:`surfacevel`, :at:`adhesion`
34953520

34963521
|body/composite/geom attrib list|
34973522
Same meaning as regular :ref:`geom <body-geom>` attributes.
@@ -4224,6 +4249,12 @@ friction can only be created with this element.
42244249
``margin`` and ``margin + gap`` are included in ``mjData.contact`` as inactive contacts but no contact forces are
42254250
generated.
42264251

4252+
.. _contact-pair-adhesion:
4253+
4254+
:at:`adhesion`: :at-val:`real, "0"`
4255+
Adhesive force of contacts generated by this pair, overriding the sum of the geoms'
4256+
:ref:`adhesion<body-geom-adhesion>` values. See there for detailed semantics.
4257+
42274258

42284259
.. _contact-exclude:
42294260

@@ -6415,7 +6446,9 @@ geckos and insects rather than an industrial vacuum gripper. In order to enable
64156446
:ref:`gap<body-geom-gap>` attribute of the body's geoms to a positive value. This creates a layer around each geom where
64166447
contacts are detected but no contact forces are generated, and the adhesive force can act across this gap. In the video
64176448
above, such inactive contacts are blue, while active contacts are orange. An adhesion actuator's length is always 0.
6418-
:at:`ctrlrange` is required and must also be nonnegative (no repulsive forces are allowed). The underlying :el:`general`
6449+
:at:`ctrlrange` is required and must also be nonnegative (no repulsive forces are allowed). For adhesion as a
6450+
*passive* property of the contacting surfaces — always on, per contact, and leaving resting penetration unaffected —
6451+
see the :ref:`geom/adhesion<body-geom-adhesion>` attribute. The underlying :el:`general`
64196452
attributes are set as follows:
64206453

64216454
=========== ======= =========== ========
@@ -9586,6 +9619,8 @@ if omitted.
95869619

95879620
.. _default-geom-surfacevel:
95889621

9622+
.. _default-geom-adhesion:
9623+
95899624
.. _default-geom-fromto:
95909625

95919626
.. _default-geom-axisangle:
@@ -9754,6 +9789,8 @@ if omitted.
97549789

97559790
.. _default-pair-margin:
97569791

9792+
.. _default-pair-adhesion:
9793+
97579794
:el-prefix:`default/` |-| **pair** |?|
97589795
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
97599796

doc/XMLschema.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,9 @@
911911
.. grid-item::
912912
:ref:`surfacevel<body-geom-surfacevel>`
913913

914+
.. grid-item::
915+
:ref:`adhesion<body-geom-adhesion>`
916+
914917
.. grid-item::
915918
:ref:`fromto<body-geom-fromto>`
916919

@@ -1379,6 +1382,9 @@
13791382
.. grid-item::
13801383
:ref:`surfacevel<composite-geom-surfacevel>`
13811384

1385+
.. grid-item::
1386+
:ref:`adhesion<composite-geom-adhesion>`
1387+
13821388

13831389
.. dropdown:: :ref:`site<composite-site>` :octicon:`dot`
13841390

@@ -1871,6 +1877,9 @@
18711877
.. grid-item::
18721878
:ref:`margin<contact-pair-margin>`
18731879

1880+
.. grid-item::
1881+
:ref:`adhesion<contact-pair-adhesion>`
1882+
18741883

18751884
.. dropdown:: :ref:`exclude<contact-exclude>` |*|
18761885

@@ -5504,6 +5513,9 @@
55045513
.. grid-item::
55055514
:ref:`surfacevel<default-geom-surfacevel>`
55065515

5516+
.. grid-item::
5517+
:ref:`adhesion<default-geom-adhesion>`
5518+
55075519
.. grid-item::
55085520
:ref:`fromto<default-geom-fromto>`
55095521

@@ -5726,6 +5738,9 @@
57265738
.. grid-item::
57275739
:ref:`margin<default-pair-margin>`
57285740

5741+
.. grid-item::
5742+
:ref:`adhesion<default-pair-adhesion>`
5743+
57295744

57305745
.. dropdown:: :ref:`equality<default-equality>` :octicon:`dot`
57315746

doc/changelog.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ Engine
2121
velocity relative to the moving surface rather than to the geom, since that is the quantity the constraint acts
2222
on; for geoms without :at:`surfacevel` the two are identical. Contact-point visualization draws an arrow along the
2323
surface velocity at contacts with moving surfaces.
24+
25+
.. youtube:: GioWwB36XHI
26+
:align: right
27+
:width: 35%
28+
29+
- Added :ref:`geom/adhesion<body-geom-adhesion>` and :ref:`pair/adhesion<contact-pair-adhesion>`: an adhesive force
30+
associated with a contact, useful for modeling sticky materials. Contacts can pull with up to the given force before
31+
breaking, and the friction budget becomes :math:`\mu(f_N + \text{adhesion})`. Combined with :ref:`gap<body-geom-gap>`,
32+
adhesive contacts apply "adhesion at a distance", useful for modeling magnets. Resting penetration is unaffected by
33+
adhesion. :ref:`mj_contactForce` reports the net interface force, whose normal component can now be negative.
2434
- Replaced midpoint integration of free bodies with :ref:`gyroscopic derivatives<geFreeBody>` in the ``implicitfast``
2535
:ref:`integrator<geIntegrators>`: the bias-force derivative of every standalone free body is applied via a local
2636
unsymmetric solve of its decoupled block, making ``implicitfast`` identical to ``implicit`` for such bodies.
@@ -56,7 +66,8 @@ Engine
5666
.. admonition:: Breaking ABI changes
5767
:class: caution
5868

59-
- Added ``texid``, ``texuniform`` and ``texrepeat`` fields to ``mjvGeom``.
69+
- Added ``texid``, ``texuniform`` and ``texrepeat`` fields to :ref:`mjvGeom`.
70+
- The :ref:`mjContact`` struct gained an ``adhesion`` member, changing its size and layout.
6071

6172
.. admonition:: Bug fixes
6273
:class: admonition

doc/computation/index.rst

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,17 +1507,18 @@ here is to construct a sensible and intuitive parameterization of the constraint
15071507

15081508
.. _soExactDiag:
15091509

1510-
**Diagonal approximation:** The approximation has three sources of error: (i) it is frozen at ``qpos0`` rather than
1511-
evaluated at the current configuration; (ii) it averages the directional inverse inertia into a scalar, assuming
1512-
isotropy; and (iii) it treats the contributions of different bodies as independent, ignoring kinematic coupling through
1513-
shared DOFs. These errors are usually modest, but can become significant for models with highly anisotropic inertias or
1514-
long kinematic chains that operate far from ``qpos0``. In severe cases — particularly when the averaged inertia becomes
1515-
near-zero despite finite directional inertia — the regularizer :math:`R` becomes near-zero, making constraints
1516-
infinitely hard and causing divergence. The :ref:`diagexact<option-flag-diagexact>` flag replaces the approximation with
1517-
the exact diagonal :math:`A_{ii} = \|Y_i\|^2`, where :math:`Y = J M^{-1/2}` is the whitened Jacobian, computed at the
1518-
current configuration. This eliminates all three sources of error at a modest runtime cost: computing :math:`Y` requires
1519-
a back-substitution with the Cholesky factor of the mass matrix for each active constraint row; if
1520-
:ref:`dual solvers<soAlgorithms>` are used (PGS or NoSlip), the cost is negligible since :math:`Y` is computed anyway.
1510+
Diagonal approximation
1511+
The approximation has three sources of error: (i) it is frozen at ``qpos0`` rather than evaluated at the current
1512+
configuration; (ii) it averages the directional inverse inertia into a scalar, assuming isotropy; and (iii) it treats
1513+
the contributions of different bodies as independent, ignoring kinematic coupling through shared DOFs. These errors
1514+
are usually modest, but can become significant for models with highly anisotropic inertias or long kinematic chains
1515+
that operate far from ``qpos0``. In severe cases — particularly when the averaged inertia becomes near-zero despite
1516+
finite directional inertia — the regularizer :math:`R` becomes near-zero, making constraints infinitely hard and
1517+
causing divergence. The :ref:`diagexact<option-flag-diagexact>` flag replaces the approximation with the exact
1518+
diagonal :math:`A_{ii} = \|Y_i\|^2`, where :math:`Y = J M^{-1/2}` is the whitened Jacobian, computed at the current
1519+
configuration. This eliminates all three sources of error at a modest runtime cost: computing :math:`Y` requires a
1520+
back-substitution with the Cholesky factor of the mass matrix for each active constraint row; if :ref:`dual
1521+
solvers<soAlgorithms>` are used (PGS or NoSlip), the cost is negligible since :math:`Y` is computed anyway.
15211522

15221523
Next we explain how the reference acceleration is computed. As already mentioned, we use a spring-damper model
15231524
parameterized by *damping* and *stiffness* coefficients element-wise:
@@ -1537,6 +1538,28 @@ velocity of the surface material, so that the reference acceleration drives the
15371538
surface; this is how conveyor belts and turntables are implemented, and it is also the quantity reported in the
15381539
contact rows of ``mjData.efc_vel``.
15391540

1541+
.. _soAdhesion:
1542+
1543+
Adhesion
1544+
Contacts of geoms with nonzero :ref:`adhesion<body-geom-adhesion>` force :math:`\delta` can pull: the feasible force
1545+
set is the friction cone *translated down the contact normal* by :math:`\delta`. This is implemented with an exact
1546+
factorization which leaves the cone machinery untouched. A constant attractive force :math:`\delta` along the contact
1547+
normal is accumulated into the passive force ``mjData.qfrc_adhesion``, and the reference acceleration of the contact's
1548+
normal row is biased:
1549+
1550+
.. math::
1551+
\ar \rightarrow \ar + R \, \delta
1552+
1553+
(for pyramidal cones the bias is distributed equally over the :math:`2(\mathrm{dim}-1)` edges). To see that this
1554+
factorization is exactly cone translation, combine :math:`f = (A+R)^{-1}(\ar - \au)` with :eq:`eq:identity` to obtain
1555+
the force relation :math:`R f = \ar - \ac`, and consider the net interface force :math:`f - \delta`: the passive
1556+
attraction cancels :math:`A \delta` in :eq:`eq:identity` while the bias cancels :math:`R \delta` in the force
1557+
relation, so the pair :math:`(f - \delta, \ac)` satisfies exactly the unbiased equations, with the cone membership of
1558+
:math:`f` becoming membership of the translated cone for :math:`f - \delta`. Consequently the compression branch of
1559+
the net contact force is independent of adhesion — resting penetration is unaffected — while a tensile branch of depth
1560+
:math:`\delta` is added. Adhesive contacts remain active when separated within the :ref:`gap<body-geom-gap>` band and
1561+
the biased reference acceleration continues to pull the geoms together across this distance.
1562+
15401563
To summarize, the constraint behavior is determined by three per-constraint quantities: impedance :math:`0<d<1`, damping
15411564
:math:`b > 0`, and stiffness :math:`k \geq 0`. These are computed from the :at:`solimp` and :at:`solref` attributes as
15421565
described in the :ref:`solver parameters <soRefScaling>` section of the Modeling chapter, which also offers additional

doc/includes/references.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ typedef struct mjContact_ { // result of collision detection functions
3434
mjtNum solref[mjNREF]; // constraint solver reference, normal direction
3535
mjtNum solreffriction[mjNREF]; // constraint solver reference, friction directions
3636
mjtNum solimp[mjNIMP]; // constraint solver impedance
37+
mjtNum adhesion; // adhesive force along the contact normal
3738

3839
// internal storage used by solver
3940
mjtNum mu; // friction of regularized cone, set by mj_makeConstraint
@@ -263,6 +264,7 @@ typedef struct mjData_ {
263264
mjtNum* qfrc_damper; // passive damper force (nv x 1)
264265
mjtNum* qfrc_gravcomp; // passive gravity compensation force (nv x 1)
265266
mjtNum* qfrc_fluid; // passive fluid force (nv x 1)
267+
mjtNum* qfrc_adhesion; // passive contact adhesion force (nv x 1)
266268
mjtNum* qfrc_passive; // total passive force (nv x 1)
267269

268270
// computed by mj_sensorVel/mj_subtreeVel if needed
@@ -679,6 +681,7 @@ typedef struct mjModel_ {
679681

680682
mjtBool flg_gravcomp; // whether any body has nonzero gravcomp
681683
mjtBool flg_surfacevel; // whether any geom has nonzero surfacevel
684+
mjtBool flg_adhesion; // whether any geom or pair has nonzero adhesion
682685

683686
// ------------------------------- options and statistics
684687

@@ -807,6 +810,7 @@ typedef struct mjModel_ {
807810
mjtNum* geom_margin; // geometric inflation for contact (ngeom x 1)
808811
mjtNum* geom_gap; // additional contact detection buffer (ngeom x 1)
809812
mjtNum* geom_surfacevel; // surface velocity in local frame: lin,ang (ngeom x 6)
813+
mjtNum* geom_adhesion; // adhesive force of contacts (ngeom x 1)
810814
mjtNum* geom_fluid; // fluid interaction parameters (ngeom x mjNFLUID)
811815
mjtNum* geom_user; // user data (ngeom x nuser_geom)
812816
float* geom_rgba; // rgba when material is omitted (ngeom x 4)
@@ -1050,6 +1054,7 @@ typedef struct mjModel_ {
10501054
mjtNum* pair_solimp; // solver impedance: contact (npair x mjNIMP)
10511055
mjtNum* pair_margin; // geometric inflation for contact (npair x 1)
10521056
mjtNum* pair_gap; // additional contact detection buffer (npair x 1)
1057+
mjtNum* pair_adhesion; // adhesive force of contacts (npair x 1)
10531058
mjtNum* pair_friction; // tangent1, 2, spin, roll1, 2 (npair x 5)
10541059

10551060
// excluded body pairs for collision detection
@@ -1902,6 +1907,7 @@ typedef struct mjsGeom_ { // geom specification
19021907
double margin; // margin for contact detection
19031908
double gap; // additional contact detection buffer
19041909
double surfacevel[6]; // surface velocity in local frame: linear, angular
1910+
double adhesion; // adhesive force of contacts
19051911

19061912
// inertia inference
19071913
double mass; // used to compute density
@@ -2164,6 +2170,7 @@ typedef struct mjsPair_ { // pair specification
21642170
mjtNum solimp[mjNIMP]; // solver impedance
21652171
double margin; // margin for contact detection
21662172
double gap; // additional contact detection buffer
2173+
double adhesion; // adhesive force of contacts
21672174
double friction[5]; // full contact friction
21682175
mjString* info; // message appended to errors
21692176
} mjsPair;

include/mujoco/mjdata.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ typedef struct mjContact_ { // result of collision detection functions
4646
mjtNum solref[mjNREF]; // constraint solver reference, normal direction
4747
mjtNum solreffriction[mjNREF]; // constraint solver reference, friction directions
4848
mjtNum solimp[mjNIMP]; // constraint solver impedance
49+
mjtNum adhesion; // adhesive force along the contact normal
4950

5051
// internal storage used by solver
5152
mjtNum mu; // friction of regularized cone, set by mj_makeConstraint
@@ -287,6 +288,7 @@ typedef struct mjData_ {
287288
mjtNum* qfrc_damper; // passive damper force (nv x 1)
288289
mjtNum* qfrc_gravcomp; // passive gravity compensation force (nv x 1)
289290
mjtNum* qfrc_fluid; // passive fluid force (nv x 1)
291+
mjtNum* qfrc_adhesion; // passive contact adhesion force (nv x 1)
290292
mjtNum* qfrc_passive; // total passive force (nv x 1)
291293

292294
// computed by mj_sensorVel/mj_subtreeVel if needed

include/mujoco/mjmodel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ typedef struct mjModel_ {
350350

351351
mjtBool flg_gravcomp; // whether any body has nonzero gravcomp
352352
mjtBool flg_surfacevel; // whether any geom has nonzero surfacevel
353+
mjtBool flg_adhesion; // whether any geom or pair has nonzero adhesion
353354

354355
// ------------------------------- options and statistics
355356

@@ -478,6 +479,7 @@ typedef struct mjModel_ {
478479
mjtNum* geom_margin; // geometric inflation for contact (ngeom x 1)
479480
mjtNum* geom_gap; // additional contact detection buffer (ngeom x 1)
480481
mjtNum* geom_surfacevel; // surface velocity in local frame: lin,ang (ngeom x 6)
482+
mjtNum* geom_adhesion; // adhesive force of contacts (ngeom x 1)
481483
mjtNum* geom_fluid; // fluid interaction parameters (ngeom x mjNFLUID)
482484
mjtNum* geom_user; // user data (ngeom x nuser_geom)
483485
float* geom_rgba; // rgba when material is omitted (ngeom x 4)
@@ -721,6 +723,7 @@ typedef struct mjModel_ {
721723
mjtNum* pair_solimp; // solver impedance: contact (npair x mjNIMP)
722724
mjtNum* pair_margin; // geometric inflation for contact (npair x 1)
723725
mjtNum* pair_gap; // additional contact detection buffer (npair x 1)
726+
mjtNum* pair_adhesion; // adhesive force of contacts (npair x 1)
724727
mjtNum* pair_friction; // tangent1, 2, spin, roll1, 2 (npair x 5)
725728

726729
// excluded body pairs for collision detection

include/mujoco/mjspec.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ typedef struct mjsGeom_ { // geom specification
359359
double margin; // margin for contact detection
360360
double gap; // additional contact detection buffer
361361
double surfacevel[6]; // surface velocity in local frame: linear, angular
362+
double adhesion; // adhesive force of contacts
362363

363364
// inertia inference
364365
double mass; // used to compute density
@@ -642,6 +643,7 @@ typedef struct mjsPair_ { // pair specification
642643
mjtNum solimp[mjNIMP]; // solver impedance
643644
double margin; // margin for contact detection
644645
double gap; // additional contact detection buffer
646+
double adhesion; // adhesive force of contacts
645647
double friction[5]; // full contact friction
646648
mjString* info; // message appended to errors
647649
} mjsPair;

0 commit comments

Comments
 (0)