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
@@ -238,7 +238,8 @@ std::vector<const char*> MJCF[nMJCF] = {
|
||||
{"geom", "?", "type", "pos", "quat", "contype", "conaffinity", "condim",
|
||||
"group", "priority", "size", "material", "friction", "mass", "density",
|
||||
"shellinertia", "solmix", "solref", "solimp",
|
||||
"margin", "gap", "surfacevel", "fromto", "axisangle", "xyaxes", "zaxis", "euler",
|
||||
"margin", "gap", "surfacevel", "adhesion", "fromto", "axisangle", "xyaxes", "zaxis",
|
||||
"euler",
|
||||
"hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"},
|
||||
{"site", "?", "type", "group", "pos", "quat", "material",
|
||||
"size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
|
||||
@@ -249,7 +250,7 @@ std::vector<const char*> MJCF[nMJCF] = {
|
||||
"directional", "type", "castshadow", "active", "attenuation", "cutoff", "exponent",
|
||||
"ambient", "diffuse", "specular", "mode"},
|
||||
{"pair", "?", "condim", "friction", "solref", "solreffriction", "solimp",
|
||||
"gap", "margin"},
|
||||
"gap", "margin", "adhesion"},
|
||||
{"equality", "?", "active", "solref", "solimp"},
|
||||
{"tendon", "?", "group", "limited", "range",
|
||||
"solreflimit", "solimplimit", "solreffriction", "solimpfriction",
|
||||
@@ -349,9 +350,9 @@ std::vector<const char*> MJCF[nMJCF] = {
|
||||
{"geom", "*", "name", "class", "type", "contype", "conaffinity", "condim",
|
||||
"group", "priority", "size", "material", "friction", "mass", "density",
|
||||
"shellinertia", "solmix", "solref", "solimp",
|
||||
"margin", "gap", "surfacevel", "fromto", "pos", "quat", "axisangle", "xyaxes",
|
||||
"zaxis", "euler", "hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef",
|
||||
"user"},
|
||||
"margin", "gap", "surfacevel", "adhesion", "fromto", "pos", "quat", "axisangle",
|
||||
"xyaxes", "zaxis", "euler", "hfield", "mesh", "fitscale", "rgba", "fluidshape",
|
||||
"fluidcoef", "user"},
|
||||
{"<"},
|
||||
{"plugin", "*", "plugin", "instance"},
|
||||
{"<"},
|
||||
@@ -381,7 +382,8 @@ std::vector<const char*> MJCF[nMJCF] = {
|
||||
{"skin", "?", "texcoord", "material", "group", "rgba", "inflate", "subgrid"},
|
||||
{"geom", "?", "type", "contype", "conaffinity", "condim",
|
||||
"group", "priority", "size", "material", "rgba", "friction", "mass",
|
||||
"density", "solmix", "solref", "solimp", "margin", "gap", "surfacevel"},
|
||||
"density", "solmix", "solref", "solimp", "margin", "gap", "surfacevel",
|
||||
"adhesion"},
|
||||
{"site", "?", "group", "size", "material", "rgba"},
|
||||
{"plugin", "*", "plugin", "instance"},
|
||||
{"<"},
|
||||
@@ -427,7 +429,7 @@ std::vector<const char*> MJCF[nMJCF] = {
|
||||
{"contact", "*"},
|
||||
{"<"},
|
||||
{"pair", "*", "name", "class", "geom1", "geom2", "condim", "friction",
|
||||
"solref", "solreffriction", "solimp", "gap", "margin"},
|
||||
"solref", "solreffriction", "solimp", "gap", "margin", "adhesion"},
|
||||
{"exclude", "*", "name", "body1", "body2"},
|
||||
{">"},
|
||||
|
||||
@@ -1975,6 +1977,7 @@ void mjXReader::OneGeom(XMLElement* elem, mjsGeom* geom) {
|
||||
ReadAttr(elem, "margin", 1, &geom->margin, text);
|
||||
ReadAttr(elem, "gap", 1, &geom->gap, text);
|
||||
ReadAttr(elem, "surfacevel", 6, geom->surfacevel, text, false, false);
|
||||
ReadAttr(elem, "adhesion", 1, &geom->adhesion, text);
|
||||
if (ReadAttrTxt(elem, "hfield", hfieldname)) {
|
||||
mjs_setString(geom->hfieldname, hfieldname.c_str());
|
||||
}
|
||||
@@ -2197,6 +2200,7 @@ void mjXReader::OnePair(XMLElement* elem, mjsPair* pair) {
|
||||
ReadAttr(elem, "solimp", mjNIMP, pair->solimp, text, false, false);
|
||||
ReadAttr(elem, "margin", 1, &pair->margin, text);
|
||||
ReadAttr(elem, "gap", 1, &pair->gap, text);
|
||||
ReadAttr(elem, "adhesion", 1, &pair->adhesion, text);
|
||||
ReadAttr(elem, "friction", 5, pair->friction, text, false, false);
|
||||
|
||||
// write error info
|
||||
@@ -2785,6 +2789,7 @@ void mjXReader::OneComposite(XMLElement* elem, mjsBody* body, mjsFrame* frame, c
|
||||
ReadAttr(egeom, "margin", 1, &dgeom.margin, text);
|
||||
ReadAttr(egeom, "gap", 1, &dgeom.gap, text);
|
||||
ReadAttr(egeom, "surfacevel", 6, dgeom.surfacevel, text, false, false);
|
||||
ReadAttr(egeom, "adhesion", 1, &dgeom.adhesion, text);
|
||||
if (ReadAttrTxt(egeom, "material", material)) {
|
||||
mjs_setString(dgeom.material, material.c_str());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user