Add mjModel.tendon_armature (in preparation, not yet implemented)

PiperOrigin-RevId: 743649968
Change-Id: I1d893e3e06afc48b652575bc7837750ab3573670
This commit is contained in:
Yuval Tassa
2025-04-03 12:05:30 -07:00
committed by Copybara-Service
parent f96f3e1c22
commit e1f5ceb65a
14 changed files with 140 additions and 11 deletions
+10 -1
View File
@@ -835,7 +835,7 @@ has any effect. The settings here are global and apply to the entire model.
.. _compiler-saveinertial:
:at:`saveinertial`: :at-val:`[false, true], "false"`
If set to "true", the compiler will save explicit :ref:`inertial <body-inerital>` clauses for all bodies.
If set to "true", the compiler will save explicit :ref:`inertial <body-inertial>` clauses for all bodies.
.. _compiler-lengthrange:
@@ -4706,6 +4706,13 @@ length X, as in the clip on the right of `this example model
joint damping which is integrated implicitly by the Euler method, tendon damping is not integrated implicitly, thus
joint damping should be used if possible.
.. TODO(tassa): Update here once the feature is implemented.
.. _tendon-spatial-armature:
:at:`armature`: :at-val:`real, "0"`
Inertia associated with tendon. This feature is not yet implemented.
.. _tendon-spatial-user:
:at:`user`: :at-val:`real(nuser_tendon), "0 0 ..."`
@@ -4815,6 +4822,8 @@ as above.
.. _tendon-fixed-damping:
.. _tendon-fixed-armature:
.. _tendon-fixed-user:
.. |tendon/fixed attrib list| replace::
+2 -2
View File
@@ -627,7 +627,7 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`material<tendon-spatial-material>` | :ref:`margin<tendon-spatial-margin>` | :ref:`stiffness<tendon-spatial-stiffness>` | :ref:`damping<tendon-spatial-damping>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`rgba<tendon-spatial-rgba>` | :ref:`user<tendon-spatial-user>` | | | |
| | | | :ref:`armature<tendon-spatial-armature>` | :ref:`rgba<tendon-spatial-rgba>` | :ref:`user<tendon-spatial-user>` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| spatial |br| |_2| |L| | | .. table:: |
@@ -661,7 +661,7 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`solimpfriction<tendon-fixed-solimpfriction>` | :ref:`frictionloss<tendon-fixed-frictionloss>` | :ref:`springlength<tendon-fixed-springlength>` | :ref:`margin<tendon-fixed-margin>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`stiffness<tendon-fixed-stiffness>` | :ref:`damping<tendon-fixed-damping>` | :ref:`user<tendon-fixed-user>` | | |
| | | | :ref:`stiffness<tendon-fixed-stiffness>` | :ref:`damping<tendon-fixed-damping>` | :ref:`armature<tendon-fixed-armature>` | :ref:`user<tendon-fixed-user>` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| fixed |br| |_2| |L| | | .. table:: |
+3 -1
View File
@@ -1339,6 +1339,7 @@ struct mjModel_ {
mjtNum* tendon_margin; // min distance for limit detection (ntendon x 1)
mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1)
mjtNum* tendon_damping; // damping coefficient (ntendon x 1)
mjtNum* tendon_armature; // inertia associated with tendon velocity (ntendon x 1)
mjtNum* tendon_frictionloss; // loss due to friction (ntendon x 1)
mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2)
mjtNum* tendon_length0; // tendon length in qpos0 (ntendon x 1)
@@ -2174,13 +2175,14 @@ typedef struct mjsTendon_ { // tendon specification
mjsElement* element; // element type
mjString* name; // name
// stiffness, damping, friction
// stiffness, damping, friction, armature
double stiffness; // stiffness coefficient
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
double damping; // damping coefficient
double frictionloss; // friction loss
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction
double armature; // inertia associated with tendon velocity
// length range
int limited; // does tendon have limits (mjtLimited)
+1
View File
@@ -1040,6 +1040,7 @@ struct mjModel_ {
mjtNum* tendon_margin; // min distance for limit detection (ntendon x 1)
mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1)
mjtNum* tendon_damping; // damping coefficient (ntendon x 1)
mjtNum* tendon_armature; // inertia associated with tendon velocity (ntendon x 1)
mjtNum* tendon_frictionloss; // loss due to friction (ntendon x 1)
mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2)
mjtNum* tendon_length0; // tendon length in qpos0 (ntendon x 1)
+2 -1
View File
@@ -617,13 +617,14 @@ typedef struct mjsTendon_ { // tendon specification
mjsElement* element; // element type
mjString* name; // name
// stiffness, damping, friction
// stiffness, damping, friction, armature
double stiffness; // stiffness coefficient
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
double damping; // damping coefficient
double frictionloss; // friction loss
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction
double armature; // inertia associated with tendon velocity
// length range
int limited; // does tendon have limits (mjtLimited)
+1
View File
@@ -484,6 +484,7 @@
X ( mjtNum, tendon_margin, ntendon, 1 ) \
XMJV( mjtNum, tendon_stiffness, ntendon, 1 ) \
XMJV( mjtNum, tendon_damping, ntendon, 1 ) \
X ( mjtNum, tendon_armature, ntendon, 1 ) \
XMJV( mjtNum, tendon_frictionloss, ntendon, 1 ) \
XMJV( mjtNum, tendon_lengthspring, ntendon, 2 ) \
X ( mjtNum, tendon_length0, ntendon, 1 ) \
+2
View File
@@ -771,6 +771,7 @@ class Model(PyTreeNode):
tendon_margin: min distance for limit detection (ntendon,)
tendon_stiffness: stiffness coefficient (ntendon,)
tendon_damping: damping coefficient (ntendon,)
tendon_armature: inertia associated with tendon velocity (ntendon,)
tendon_frictionloss: loss due to friction (ntendon,)
tendon_lengthspring: spring resting length range (ntendon, 2)
tendon_length0: tendon length in qpos0 (ntendon,)
@@ -1113,6 +1114,7 @@ class Model(PyTreeNode):
tendon_margin: jax.Array
tendon_stiffness: jax.Array
tendon_damping: jax.Array
tendon_armature: jax.Array
tendon_frictionloss: jax.Array
tendon_lengthspring: jax.Array
tendon_length0: jax.Array
+13
View File
@@ -3687,6 +3687,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='damping coefficient',
array_extent=('ntendon',),
),
StructFieldDecl(
name='tendon_armature',
type=PointerType(
inner_type=ValueType(name='mjtNum'),
),
doc='inertia associated with tendon velocity',
array_extent=('ntendon',),
),
StructFieldDecl(
name='tendon_frictionloss',
type=PointerType(
@@ -11324,6 +11332,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
doc='solver impedance: tendon friction',
),
StructFieldDecl(
name='armature',
type=ValueType(name='double'),
doc='inertia associated with tendon velocity',
),
StructFieldDecl(
name='limited',
type=ValueType(name='int'),
+2
View File
@@ -3390,6 +3390,7 @@ void mjCModel::CopyObjects(mjModel* m) {
m->tendon_margin[i] = (mjtNum)pte->margin;
m->tendon_stiffness[i] = (mjtNum)pte->stiffness;
m->tendon_damping[i] = (mjtNum)pte->damping;
m->tendon_armature[i] = (mjtNum)pte->armature;
m->tendon_frictionloss[i] = (mjtNum)pte->frictionloss;
m->tendon_lengthspring[2*i] = (mjtNum)pte->springlength[0];
m->tendon_lengthspring[2*i+1] = (mjtNum)pte->springlength[1];
@@ -4931,6 +4932,7 @@ bool mjCModel::CopyBack(const mjModel* m) {
tendons_[i]->margin = (double)m->tendon_margin[i];
tendons_[i]->stiffness = (double)m->tendon_stiffness[i];
tendons_[i]->damping = (double)m->tendon_damping[i];
tendons_[i]->armature = (double)m->tendon_armature[i];
tendons_[i]->frictionloss = (double)m->tendon_frictionloss[i];
if (nuser_tendon) {
+12
View File
@@ -5616,6 +5616,12 @@ void mjCTendon::Compile(void) {
// spatial path
else {
if (armature < 0) {
throw mjCError(this,
"tendon '%s' (id = %d): tendon armature cannot be negative",
name.c_str(), id);
}
switch (path[i]->type) {
case mjWRAP_PULLEY:
// pulley should not follow other pulley
@@ -5657,6 +5663,12 @@ void mjCTendon::Compile(void) {
name.c_str(), id, i);
}
if (armature > 0) {
throw mjCError(this,
"tendon '%s' (id = %d): geom wrapping not supported by tendon armature",
name.c_str(), id);
}
// mark geoms as non visual
model->Geoms()[path[i]->obj->id]->SetNotVisual();
break;
+6 -5
View File
@@ -363,18 +363,18 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
{"tendon", "*", "0"},
{"<"},
{"spatial", "*", "18", "name", "class", "group", "limited", "range",
{"spatial", "*", "19", "name", "class", "group", "limited", "range",
"solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "width", "material",
"margin", "stiffness", "damping", "rgba", "user"},
"margin", "stiffness", "damping", "armature", "rgba", "user"},
{"<"},
{"site", "*", "1", "site"},
{"geom", "*", "2", "geom", "sidesite"},
{"pulley", "*", "1", "divisor"},
{">"},
{"fixed", "*", "15", "name", "class", "group", "limited", "range",
{"fixed", "*", "16", "name", "class", "group", "limited", "range",
"solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "margin", "stiffness", "damping", "user"},
"frictionloss", "springlength", "margin", "stiffness", "damping", "armature", "user"},
{"<"},
{"joint", "*", "2", "joint", "coef"},
{">"},
@@ -2061,6 +2061,7 @@ void mjXReader::OneTendon(XMLElement* elem, mjsTendon* tendon) {
ReadAttr(elem, "margin", 1, &tendon->margin, text);
ReadAttr(elem, "stiffness", 1, &tendon->stiffness, text);
ReadAttr(elem, "damping", 1, &tendon->damping, text);
ReadAttr(elem, "armature", 1, &tendon->armature, text);
ReadAttr(elem, "frictionloss", 1, &tendon->frictionloss, text);
// read springlength, either one or two values; if one, copy to second value
if (ReadAttr(elem, "springlength", 2, tendon->springlength, text, false, false) == 1) {
@@ -3800,7 +3801,7 @@ void mjXReader::Tendon(XMLElement* section) {
def = mjs_getSpecDefault(spec);
}
// create equality constraint and parse
// create tendon and parse
mjsTendon* tendon = mjs_addTendon(spec, def);
OneTendon(elem, tendon);
+1
View File
@@ -729,6 +729,7 @@ void mjXWriter::OneTendon(XMLElement* elem, const mjCTendon* tendon, mjCDef* def
WriteAttr(elem, "margin", 1, &tendon->margin, &def->Tendon().margin);
WriteAttr(elem, "stiffness", 1, &tendon->stiffness, &def->Tendon().stiffness);
WriteAttr(elem, "damping", 1, &tendon->damping, &def->Tendon().damping);
WriteAttr(elem, "armature", 1, &tendon->armature, &def->Tendon().armature);
WriteAttr(elem, "frictionloss", 1, &tendon->frictionloss, &def->Tendon().frictionloss);
if (tendon->springlength[0] != tendon->springlength[1] ||
def->Tendon().springlength[0] != def->Tendon().springlength[1]) {
+84 -1
View File
@@ -15,6 +15,7 @@
// Tests for xml/xml_native_reader.cc.
#include <array>
#include <cmath>
#include <limits>
#include <memory>
#include <string>
@@ -1148,6 +1149,88 @@ TEST_F(XMLReaderTest, ParsePolycoef) {
mj_deleteModel(m);
}
TEST_F(XMLReaderTest, TendonArmature) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<site name="a"/>
<body pos="1 0 0">
<joint name="slide" type="slide"/>
<geom size=".1"/>
<site name="b"/>
</body>
</worldbody>
<tendon>
<spatial armature="1.5">
<site site="a"/>
<site site="b"/>
</spatial>
<fixed armature="2.5">
<joint joint="slide" coef="1"/>
</fixed>
<fixed>
<joint joint="slide" coef="2"/>
</fixed>
</tendon>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m, NotNull()) << error.data();
EXPECT_EQ(m->ntendon, 3);
EXPECT_FLOAT_EQ(m->tendon_armature[0], 1.5);
EXPECT_FLOAT_EQ(m->tendon_armature[1], 2.5);
EXPECT_FLOAT_EQ(m->tendon_armature[2], 0);
mj_deleteModel(m);
}
TEST_F(XMLReaderTest, TendonArmatureNegative) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<site name="a"/>
<site name="b" pos="1 0 0"/>
</worldbody>
<tendon>
<spatial armature="-1.5">
<site site="a"/>
<site site="b"/>
</spatial>
</tendon>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m, IsNull());
EXPECT_THAT(error.data(), HasSubstr("tendon armature cannot be negative"));
}
TEST_F(XMLReaderTest, TendonArmatureGeomWrap) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<site name="a"/>
<geom name="g" type="sphere" size=".1"/>
<site name="b" pos="1 0 0"/>
</worldbody>
<tendon>
<spatial armature="1.5">
<site site="a"/>
<geom geom="g"/>
<site site="b"/>
</spatial>
</tendon>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m, IsNull());
EXPECT_THAT(error.data(), HasSubstr("geom wrapping not supported"));
}
// ------------------------ test frame parsing ---------------------------------
TEST_F(XMLReaderTest, ParseFrame) {
static constexpr char xml[] = R"(
@@ -1862,7 +1945,7 @@ TEST_F(XMLReaderTest, CameraInvalidFovyAndSensorsize) {
EXPECT_THAT(error.data(), HasSubstr("line 6"));
}
TEST_F(XMLReaderTest, CameraPricipalRequiresSensorsize) {
TEST_F(XMLReaderTest, CameraPrincipalRequiresSensorsize) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
+1
View File
@@ -5578,6 +5578,7 @@ public unsafe struct mjModel_ {
public double* tendon_margin;
public double* tendon_stiffness;
public double* tendon_damping;
public double* tendon_armature;
public double* tendon_frictionloss;
public double* tendon_lengthspring;
public double* tendon_length0;