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
This commit is contained in:
committed by
Copybara-Service
parent
b942c9f922
commit
a264d0bc8b
@@ -1724,10 +1724,10 @@ int mj_broadphase(const mjModel* m, mjData* d, int* bfpair, int maxpair) {
|
||||
|
||||
//----------------------------- narrow-phase collision detection -----------------------------------
|
||||
|
||||
// compute contact condim, solref, solimp, friction
|
||||
// compute contact condim, solref, solimp, friction, adhesion
|
||||
static void mj_contactParam(const mjModel* m, int* condim,
|
||||
mjtNum* solref, mjtNum* solimp, mjtNum* friction,
|
||||
int g1, int g2, int f1, int f2) {
|
||||
mjtNum* adhesion, int g1, int g2, int f1, int f2) {
|
||||
mjtNum fri[3];
|
||||
|
||||
// get parameters from geom1 or flex1
|
||||
@@ -1737,6 +1737,7 @@ static void mj_contactParam(const mjModel* m, int* condim,
|
||||
const mjtNum* solref1 = (f1 < 0) ? m->geom_solref+g1*mjNREF : m->flex_solref+f1*mjNREF;
|
||||
const mjtNum* solimp1 = (f1 < 0) ? m->geom_solimp+g1*mjNIMP : m->flex_solimp+f1*mjNIMP;
|
||||
const mjtNum* friction1 = (f1 < 0) ? m->geom_friction+g1*3 : m->flex_friction+f1*3;
|
||||
mjtNum adhesion1 = (f1 < 0) ? m->geom_adhesion[g1] : 0;
|
||||
|
||||
// get parameters from geom2 or flex2
|
||||
int priority2 = (f2 < 0) ? m->geom_priority[g2] : m->flex_priority[f2];
|
||||
@@ -1745,6 +1746,10 @@ static void mj_contactParam(const mjModel* m, int* condim,
|
||||
const mjtNum* solref2 = (f2 < 0) ? m->geom_solref+g2*mjNREF : m->flex_solref+f2*mjNREF;
|
||||
const mjtNum* solimp2 = (f2 < 0) ? m->geom_solimp+g2*mjNIMP : m->flex_solimp+f2*mjNIMP;
|
||||
const mjtNum* friction2 = (f2 < 0) ? m->geom_friction+g2*3 : m->flex_friction+f2*3;
|
||||
mjtNum adhesion2 = (f2 < 0) ? m->geom_adhesion[g2] : 0;
|
||||
|
||||
// adhesion: each surface contributes its own attraction
|
||||
*adhesion = adhesion1 + adhesion2;
|
||||
|
||||
// different priority: copy from item with higher priority
|
||||
if (priority1 > priority2) {
|
||||
@@ -1752,12 +1757,14 @@ static void mj_contactParam(const mjModel* m, int* condim,
|
||||
mju_copy(solref, solref1, mjNREF);
|
||||
mju_copy(solimp, solimp1, mjNIMP);
|
||||
mju_copy(fri, friction1, 3);
|
||||
*adhesion = adhesion1;
|
||||
}
|
||||
else if (priority1 < priority2) {
|
||||
*condim = condim2;
|
||||
mju_copy(solref, solref2, mjNREF);
|
||||
mju_copy(solimp, solimp2, mjNIMP);
|
||||
mju_copy(fri, friction2, 3);
|
||||
*adhesion = adhesion2;
|
||||
}
|
||||
|
||||
// same priority
|
||||
@@ -1820,17 +1827,27 @@ static void mj_contactParam(const mjModel* m, int* condim,
|
||||
static void mj_setContact(const mjModel* m, mjContact* con,
|
||||
int condim, mjtNum includemargin,
|
||||
const mjtNum* solref, const mjtNum* solreffriction,
|
||||
const mjtNum* solimp, const mjtNum* friction) {
|
||||
const mjtNum* solimp, const mjtNum* friction,
|
||||
mjtNum adhesion) {
|
||||
// set parameters
|
||||
con->dim = condim;
|
||||
con->includemargin = includemargin;
|
||||
con->adhesion = adhesion;
|
||||
mj_assignRef(m, con->solref, solref);
|
||||
mj_assignRef(m, con->solreffriction, solreffriction);
|
||||
mj_assignImp(m, con->solimp, solimp);
|
||||
mj_assignFriction(m, con->friction, friction);
|
||||
|
||||
// exclude in gap
|
||||
con->exclude = (con->dist >= includemargin);
|
||||
// exclude in gap; adhesive contacts remain active in the gap, producing
|
||||
// rows with positive violation whose reference acceleration pulls (tether)
|
||||
con->exclude = (con->dist >= includemargin) && !adhesion;
|
||||
|
||||
// in-gap adhesive contacts have no surface contact and therefore no
|
||||
// friction: reduce to normal-only rows (with tangential rows present, fast
|
||||
// slip would recruit normal force and brake tangential motion at a distance)
|
||||
if (adhesion && con->dist >= includemargin) {
|
||||
con->dim = 1;
|
||||
}
|
||||
|
||||
// complete frame
|
||||
mju_makeFrame(con->frame);
|
||||
@@ -2010,7 +2027,7 @@ static void mj_narrowphase(const mjModel* m, mjData* d, const mjcPair* buffer, i
|
||||
continue;
|
||||
|
||||
int condim;
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP];
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
int g1 = pairbuffer[i].geom_geom.g1;
|
||||
int g2 = pairbuffer[i].geom_geom.g2;
|
||||
@@ -2021,11 +2038,12 @@ static void mj_narrowphase(const mjModel* m, mjData* d, const mjcPair* buffer, i
|
||||
mju_copy(solref, m->pair_solref+mjNREF*ipair, mjNREF);
|
||||
mju_copy(solimp, m->pair_solimp+mjNIMP*ipair, mjNIMP);
|
||||
mju_copy(friction, m->pair_friction+5*ipair, 5);
|
||||
adhesion = m->pair_adhesion[ipair];
|
||||
if (m->pair_solreffriction[mjNREF*ipair] || m->pair_solreffriction[mjNREF*ipair + 1]) {
|
||||
mju_copy(solreffriction, m->pair_solreffriction+mjNREF*ipair, mjNREF);
|
||||
}
|
||||
} else {
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, g1, g2, -1, -1);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, g1, g2, -1, -1);
|
||||
}
|
||||
|
||||
mjPreContact* bc = arg.conbuffer + pairbuffer[i].conpos;
|
||||
@@ -2044,7 +2062,7 @@ static void mj_narrowphase(const mjModel* m, mjData* d, const mjcPair* buffer, i
|
||||
c->elem[1] = -1;
|
||||
c->vert[0] = -1;
|
||||
c->vert[1] = -1;
|
||||
mj_setContact(m, c, condim, margin, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, c, condim, margin, solref, solreffriction, solimp, friction, adhesion);
|
||||
}
|
||||
conpos += ncon;
|
||||
}
|
||||
@@ -2065,9 +2083,9 @@ static void mj_collidePlaneFlex(const mjModel* m, mjData* d, int g, int f) {
|
||||
int condim;
|
||||
int flex_vertnum = m->flex_vertnum[f];
|
||||
mjtNum gap = m->geom_gap[g] + m->flex_gap[f];
|
||||
mjtNum solref[mjNREF], solimp[mjNIMP], friction[5];
|
||||
mjtNum solref[mjNREF], solimp[mjNIMP], friction[5], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, g, -1, -1, f);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, g, -1, -1, f);
|
||||
|
||||
// collide all flex vertices with plane
|
||||
for (int i=0; i < flex_vertnum; i++) {
|
||||
@@ -2099,7 +2117,7 @@ static void mj_collidePlaneFlex(const mjModel* m, mjData* d, int g, int f) {
|
||||
con.vert[1] = i;
|
||||
|
||||
// set remaining contact parameters
|
||||
mj_setContact(m, &con, condim, margin, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, &con, condim, margin, solref, solreffriction, solimp, friction, adhesion);
|
||||
|
||||
// add to mjData, abort if too many contacts
|
||||
if (mj_addContact(m, d, &con)) {
|
||||
@@ -2120,9 +2138,9 @@ static void mj_collideSdfFlex(const mjModel* m, mjData* d, int g, int f) {
|
||||
mjtNum margin = mj_assignMargin(m, m->geom_margin[g] + m->flex_margin[f]);
|
||||
mjtNum gap = m->geom_gap[g] + m->flex_gap[f];
|
||||
int condim;
|
||||
mjtNum solref[mjNREF], solimp[mjNIMP], friction[5];
|
||||
mjtNum solref[mjNREF], solimp[mjNIMP], friction[5], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, g, -1, -1, f);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, g, -1, -1, f);
|
||||
|
||||
// allocate temporary contact array on stack (zero-initialized)
|
||||
mj_markStack(d);
|
||||
@@ -2153,7 +2171,7 @@ static void mj_collideSdfFlex(const mjModel* m, mjData* d, int g, int f) {
|
||||
con.vert[1] = -1;
|
||||
|
||||
// set remaining contact parameters
|
||||
mj_setContact(m, &con, condim, margin, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, &con, condim, margin, solref, solreffriction, solimp, friction, adhesion);
|
||||
|
||||
// add to mjData, abort if too many contacts
|
||||
if (mj_addContact(m, d, &con)) {
|
||||
@@ -2221,9 +2239,9 @@ static void mj_collideFlexInternal(const mjModel* m, mjData* d, int f) {
|
||||
int condim;
|
||||
int flex_elemnum = m->flex_elemnum[f];
|
||||
mjtNum radius = m->flex_radius[f];
|
||||
mjtNum solref[mjNREF], solimp[mjNIMP], friction[5];
|
||||
mjtNum solref[mjNREF], solimp[mjNIMP], friction[5], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, -1, -1, f, f);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, -1, -1, f, f);
|
||||
condim = 1;
|
||||
|
||||
// process all elements
|
||||
@@ -2240,7 +2258,7 @@ static void mj_collideFlexInternal(const mjModel* m, mjData* d, int f) {
|
||||
mju_copy3(con.pos, precon.pos);
|
||||
mju_copy3(con.frame, precon.normal);
|
||||
mju_copy3(con.frame + 3, precon.tangent);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction, adhesion);
|
||||
if (mj_addContact(m, d, &con)) return;
|
||||
}
|
||||
|
||||
@@ -2251,7 +2269,7 @@ static void mj_collideFlexInternal(const mjModel* m, mjData* d, int f) {
|
||||
mju_copy3(con.pos, precon.pos);
|
||||
mju_copy3(con.frame, precon.normal);
|
||||
mju_copy3(con.frame + 3, precon.tangent);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction, adhesion);
|
||||
if (mj_addContact(m, d, &con)) return;
|
||||
}
|
||||
|
||||
@@ -2262,7 +2280,7 @@ static void mj_collideFlexInternal(const mjModel* m, mjData* d, int f) {
|
||||
mju_copy3(con.pos, precon.pos);
|
||||
mju_copy3(con.frame, precon.normal);
|
||||
mju_copy3(con.frame + 3, precon.tangent);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction, adhesion);
|
||||
if (mj_addContact(m, d, &con)) return;
|
||||
}
|
||||
|
||||
@@ -2273,7 +2291,7 @@ static void mj_collideFlexInternal(const mjModel* m, mjData* d, int f) {
|
||||
mju_copy3(con.pos, precon.pos);
|
||||
mju_copy3(con.frame, precon.normal);
|
||||
mju_copy3(con.frame + 3, precon.tangent);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, &con, condim, 0, solref, solreffriction, solimp, friction, adhesion);
|
||||
if (mj_addContact(m, d, &con)) return;
|
||||
}
|
||||
}
|
||||
@@ -2439,9 +2457,9 @@ void mj_collideGeomElem(const mjModel* m, mjData* d, int g, int f, int e) {
|
||||
|
||||
// get contact parameters
|
||||
int condim;
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP];
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, g, -1, -1, f);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, g, -1, -1, f);
|
||||
|
||||
// allocate mjContact[num] on the arena
|
||||
mjContact* con =
|
||||
@@ -2475,7 +2493,7 @@ void mj_collideGeomElem(const mjModel* m, mjData* d, int g, int f, int e) {
|
||||
}
|
||||
|
||||
// set remaining contact parameters
|
||||
mj_setContact(m, con + i, condim, margin, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, con + i, condim, margin, solref, solreffriction, solimp, friction, adhesion);
|
||||
}
|
||||
|
||||
// add to ncon
|
||||
@@ -2548,9 +2566,9 @@ void mj_collideElems(const mjModel* m, mjData* d, int f1, int e1, int f2, int e2
|
||||
|
||||
// get contact parameters
|
||||
int condim;
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP];
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, -1, -1, f1, f2);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, -1, -1, f1, f2);
|
||||
|
||||
mjContact* con = (mjContact*) mj_arenaAllocByte(d, sizeof(mjContact) * num, _Alignof(mjContact));
|
||||
if (!con) {
|
||||
@@ -2577,7 +2595,7 @@ void mj_collideElems(const mjModel* m, mjData* d, int f1, int e1, int f2, int e2
|
||||
mju_copy3(con[i].frame + 3, precon[i].tangent);
|
||||
|
||||
// set remaining contact parameters
|
||||
mj_setContact(m, con + i, condim, margin, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, con + i, condim, margin, solref, solreffriction, solimp, friction, adhesion);
|
||||
}
|
||||
|
||||
// add to ncon
|
||||
@@ -2638,9 +2656,9 @@ void mj_collideElemVert(const mjModel* m, mjData* d, int f, int e, int v) {
|
||||
|
||||
// get contact parameters
|
||||
int condim;
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP];
|
||||
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP], adhesion;
|
||||
mjtNum solreffriction[mjNREF] = {0};
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, -1, -1, f, f);
|
||||
mj_contactParam(m, &condim, solref, solimp, friction, &adhesion, -1, -1, f, f);
|
||||
|
||||
|
||||
// allocate mjContact[num] on the arena
|
||||
@@ -2671,7 +2689,7 @@ void mj_collideElemVert(const mjModel* m, mjData* d, int f, int e, int v) {
|
||||
mju_copy3(con[i].frame + 3, precon[i].tangent);
|
||||
|
||||
// set remaining contact parameters
|
||||
mj_setContact(m, con + i, condim, 0, solref, solreffriction, solimp, friction);
|
||||
mj_setContact(m, con + i, condim, 0, solref, solreffriction, solimp, friction, adhesion);
|
||||
}
|
||||
|
||||
// add to ncon
|
||||
|
||||
Reference in New Issue
Block a user