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: .. _compiler-saveinertial:
:at:`saveinertial`: :at-val:`[false, true], "false"` :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: .. _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 which is integrated implicitly by the Euler method, tendon damping is not integrated implicitly, thus
joint damping should be used if possible. 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: .. _tendon-spatial-user:
:at:`user`: :at-val:`real(nuser_tendon), "0 0 ..."` :at:`user`: :at-val:`real(nuser_tendon), "0 0 ..."`
@@ -4815,6 +4822,8 @@ as above.
.. _tendon-fixed-damping: .. _tendon-fixed-damping:
.. _tendon-fixed-armature:
.. _tendon-fixed-user: .. _tendon-fixed-user:
.. |tendon/fixed attrib list| replace:: .. |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:`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:: | | |_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:`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:: | | |_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_margin; // min distance for limit detection (ntendon x 1)
mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1) mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1)
mjtNum* tendon_damping; // damping 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_frictionloss; // loss due to friction (ntendon x 1)
mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2) mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2)
mjtNum* tendon_length0; // tendon length in qpos0 (ntendon x 1) mjtNum* tendon_length0; // tendon length in qpos0 (ntendon x 1)
@@ -2174,13 +2175,14 @@ typedef struct mjsTendon_ { // tendon specification
mjsElement* element; // element type mjsElement* element; // element type
mjString* name; // name mjString* name; // name
// stiffness, damping, friction // stiffness, damping, friction, armature
double stiffness; // stiffness coefficient double stiffness; // stiffness coefficient
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
double damping; // damping coefficient double damping; // damping coefficient
double frictionloss; // friction loss double frictionloss; // friction loss
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction
double armature; // inertia associated with tendon velocity
// length range // length range
int limited; // does tendon have limits (mjtLimited) 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_margin; // min distance for limit detection (ntendon x 1)
mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1) mjtNum* tendon_stiffness; // stiffness coefficient (ntendon x 1)
mjtNum* tendon_damping; // damping 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_frictionloss; // loss due to friction (ntendon x 1)
mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2) mjtNum* tendon_lengthspring; // spring resting length range (ntendon x 2)
mjtNum* tendon_length0; // tendon length in qpos0 (ntendon x 1) 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 mjsElement* element; // element type
mjString* name; // name mjString* name; // name
// stiffness, damping, friction // stiffness, damping, friction, armature
double stiffness; // stiffness coefficient double stiffness; // stiffness coefficient
double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring double springlength[2]; // spring resting length; {-1, -1}: use qpos_spring
double damping; // damping coefficient double damping; // damping coefficient
double frictionloss; // friction loss double frictionloss; // friction loss
mjtNum solref_friction[mjNREF]; // solver reference: tendon friction mjtNum solref_friction[mjNREF]; // solver reference: tendon friction
mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction mjtNum solimp_friction[mjNIMP]; // solver impedance: tendon friction
double armature; // inertia associated with tendon velocity
// length range // length range
int limited; // does tendon have limits (mjtLimited) int limited; // does tendon have limits (mjtLimited)
+1
View File
@@ -484,6 +484,7 @@
X ( mjtNum, tendon_margin, ntendon, 1 ) \ X ( mjtNum, tendon_margin, ntendon, 1 ) \
XMJV( mjtNum, tendon_stiffness, ntendon, 1 ) \ XMJV( mjtNum, tendon_stiffness, ntendon, 1 ) \
XMJV( mjtNum, tendon_damping, ntendon, 1 ) \ XMJV( mjtNum, tendon_damping, ntendon, 1 ) \
X ( mjtNum, tendon_armature, ntendon, 1 ) \
XMJV( mjtNum, tendon_frictionloss, ntendon, 1 ) \ XMJV( mjtNum, tendon_frictionloss, ntendon, 1 ) \
XMJV( mjtNum, tendon_lengthspring, ntendon, 2 ) \ XMJV( mjtNum, tendon_lengthspring, ntendon, 2 ) \
X ( mjtNum, tendon_length0, ntendon, 1 ) \ 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_margin: min distance for limit detection (ntendon,)
tendon_stiffness: stiffness coefficient (ntendon,) tendon_stiffness: stiffness coefficient (ntendon,)
tendon_damping: damping coefficient (ntendon,) tendon_damping: damping coefficient (ntendon,)
tendon_armature: inertia associated with tendon velocity (ntendon,)
tendon_frictionloss: loss due to friction (ntendon,) tendon_frictionloss: loss due to friction (ntendon,)
tendon_lengthspring: spring resting length range (ntendon, 2) tendon_lengthspring: spring resting length range (ntendon, 2)
tendon_length0: tendon length in qpos0 (ntendon,) tendon_length0: tendon length in qpos0 (ntendon,)
@@ -1113,6 +1114,7 @@ class Model(PyTreeNode):
tendon_margin: jax.Array tendon_margin: jax.Array
tendon_stiffness: jax.Array tendon_stiffness: jax.Array
tendon_damping: jax.Array tendon_damping: jax.Array
tendon_armature: jax.Array
tendon_frictionloss: jax.Array tendon_frictionloss: jax.Array
tendon_lengthspring: jax.Array tendon_lengthspring: jax.Array
tendon_length0: jax.Array tendon_length0: jax.Array
+13
View File
@@ -3687,6 +3687,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='damping coefficient', doc='damping coefficient',
array_extent=('ntendon',), array_extent=('ntendon',),
), ),
StructFieldDecl(
name='tendon_armature',
type=PointerType(
inner_type=ValueType(name='mjtNum'),
),
doc='inertia associated with tendon velocity',
array_extent=('ntendon',),
),
StructFieldDecl( StructFieldDecl(
name='tendon_frictionloss', name='tendon_frictionloss',
type=PointerType( type=PointerType(
@@ -11324,6 +11332,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
), ),
doc='solver impedance: tendon friction', doc='solver impedance: tendon friction',
), ),
StructFieldDecl(
name='armature',
type=ValueType(name='double'),
doc='inertia associated with tendon velocity',
),
StructFieldDecl( StructFieldDecl(
name='limited', name='limited',
type=ValueType(name='int'), 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_margin[i] = (mjtNum)pte->margin;
m->tendon_stiffness[i] = (mjtNum)pte->stiffness; m->tendon_stiffness[i] = (mjtNum)pte->stiffness;
m->tendon_damping[i] = (mjtNum)pte->damping; m->tendon_damping[i] = (mjtNum)pte->damping;
m->tendon_armature[i] = (mjtNum)pte->armature;
m->tendon_frictionloss[i] = (mjtNum)pte->frictionloss; m->tendon_frictionloss[i] = (mjtNum)pte->frictionloss;
m->tendon_lengthspring[2*i] = (mjtNum)pte->springlength[0]; m->tendon_lengthspring[2*i] = (mjtNum)pte->springlength[0];
m->tendon_lengthspring[2*i+1] = (mjtNum)pte->springlength[1]; 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]->margin = (double)m->tendon_margin[i];
tendons_[i]->stiffness = (double)m->tendon_stiffness[i]; tendons_[i]->stiffness = (double)m->tendon_stiffness[i];
tendons_[i]->damping = (double)m->tendon_damping[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]; tendons_[i]->frictionloss = (double)m->tendon_frictionloss[i];
if (nuser_tendon) { if (nuser_tendon) {
+12
View File
@@ -5616,6 +5616,12 @@ void mjCTendon::Compile(void) {
// spatial path // spatial path
else { else {
if (armature < 0) {
throw mjCError(this,
"tendon '%s' (id = %d): tendon armature cannot be negative",
name.c_str(), id);
}
switch (path[i]->type) { switch (path[i]->type) {
case mjWRAP_PULLEY: case mjWRAP_PULLEY:
// pulley should not follow other pulley // pulley should not follow other pulley
@@ -5657,6 +5663,12 @@ void mjCTendon::Compile(void) {
name.c_str(), id, i); 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 // mark geoms as non visual
model->Geoms()[path[i]->obj->id]->SetNotVisual(); model->Geoms()[path[i]->obj->id]->SetNotVisual();
break; break;
+6 -5
View File
@@ -363,18 +363,18 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
{"tendon", "*", "0"}, {"tendon", "*", "0"},
{"<"}, {"<"},
{"spatial", "*", "18", "name", "class", "group", "limited", "range", {"spatial", "*", "19", "name", "class", "group", "limited", "range",
"solreflimit", "solimplimit", "solreffriction", "solimpfriction", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "width", "material", "frictionloss", "springlength", "width", "material",
"margin", "stiffness", "damping", "rgba", "user"}, "margin", "stiffness", "damping", "armature", "rgba", "user"},
{"<"}, {"<"},
{"site", "*", "1", "site"}, {"site", "*", "1", "site"},
{"geom", "*", "2", "geom", "sidesite"}, {"geom", "*", "2", "geom", "sidesite"},
{"pulley", "*", "1", "divisor"}, {"pulley", "*", "1", "divisor"},
{">"}, {">"},
{"fixed", "*", "15", "name", "class", "group", "limited", "range", {"fixed", "*", "16", "name", "class", "group", "limited", "range",
"solreflimit", "solimplimit", "solreffriction", "solimpfriction", "solreflimit", "solimplimit", "solreffriction", "solimpfriction",
"frictionloss", "springlength", "margin", "stiffness", "damping", "user"}, "frictionloss", "springlength", "margin", "stiffness", "damping", "armature", "user"},
{"<"}, {"<"},
{"joint", "*", "2", "joint", "coef"}, {"joint", "*", "2", "joint", "coef"},
{">"}, {">"},
@@ -2061,6 +2061,7 @@ void mjXReader::OneTendon(XMLElement* elem, mjsTendon* tendon) {
ReadAttr(elem, "margin", 1, &tendon->margin, text); ReadAttr(elem, "margin", 1, &tendon->margin, text);
ReadAttr(elem, "stiffness", 1, &tendon->stiffness, text); ReadAttr(elem, "stiffness", 1, &tendon->stiffness, text);
ReadAttr(elem, "damping", 1, &tendon->damping, text); ReadAttr(elem, "damping", 1, &tendon->damping, text);
ReadAttr(elem, "armature", 1, &tendon->armature, text);
ReadAttr(elem, "frictionloss", 1, &tendon->frictionloss, text); ReadAttr(elem, "frictionloss", 1, &tendon->frictionloss, text);
// read springlength, either one or two values; if one, copy to second value // read springlength, either one or two values; if one, copy to second value
if (ReadAttr(elem, "springlength", 2, tendon->springlength, text, false, false) == 1) { if (ReadAttr(elem, "springlength", 2, tendon->springlength, text, false, false) == 1) {
@@ -3800,7 +3801,7 @@ void mjXReader::Tendon(XMLElement* section) {
def = mjs_getSpecDefault(spec); def = mjs_getSpecDefault(spec);
} }
// create equality constraint and parse // create tendon and parse
mjsTendon* tendon = mjs_addTendon(spec, def); mjsTendon* tendon = mjs_addTendon(spec, def);
OneTendon(elem, tendon); 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, "margin", 1, &tendon->margin, &def->Tendon().margin);
WriteAttr(elem, "stiffness", 1, &tendon->stiffness, &def->Tendon().stiffness); WriteAttr(elem, "stiffness", 1, &tendon->stiffness, &def->Tendon().stiffness);
WriteAttr(elem, "damping", 1, &tendon->damping, &def->Tendon().damping); 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); WriteAttr(elem, "frictionloss", 1, &tendon->frictionloss, &def->Tendon().frictionloss);
if (tendon->springlength[0] != tendon->springlength[1] || if (tendon->springlength[0] != tendon->springlength[1] ||
def->Tendon().springlength[0] != def->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. // Tests for xml/xml_native_reader.cc.
#include <array> #include <array>
#include <cmath>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -1148,6 +1149,88 @@ TEST_F(XMLReaderTest, ParsePolycoef) {
mj_deleteModel(m); 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 frame parsing ---------------------------------
TEST_F(XMLReaderTest, ParseFrame) { TEST_F(XMLReaderTest, ParseFrame) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
@@ -1862,7 +1945,7 @@ TEST_F(XMLReaderTest, CameraInvalidFovyAndSensorsize) {
EXPECT_THAT(error.data(), HasSubstr("line 6")); EXPECT_THAT(error.data(), HasSubstr("line 6"));
} }
TEST_F(XMLReaderTest, CameraPricipalRequiresSensorsize) { TEST_F(XMLReaderTest, CameraPrincipalRequiresSensorsize) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<worldbody> <worldbody>
+1
View File
@@ -5578,6 +5578,7 @@ public unsafe struct mjModel_ {
public double* tendon_margin; public double* tendon_margin;
public double* tendon_stiffness; public double* tendon_stiffness;
public double* tendon_damping; public double* tendon_damping;
public double* tendon_armature;
public double* tendon_frictionloss; public double* tendon_frictionloss;
public double* tendon_lengthspring; public double* tendon_lengthspring;
public double* tendon_length0; public double* tendon_length0;