Don't infer limited attributes automatically by default.

This is an amendment to 4bfc2c0311.

Add an autolimits compiler option. When true, *limited will be inferred from the presence of the *range attributes.
When false, models where limited is not specified, and range is will fail to compile.

PiperOrigin-RevId: 472438650
Change-Id: I489098e879ee695030f44e0d8cd969787e75fb90
This commit is contained in:
Nimrod Gileadi
2022-09-06 06:11:17 -07:00
committed by Copybara-Service
parent 0784a46f62
commit a693a2d981
15 changed files with 205 additions and 36 deletions
+33 -12
View File
@@ -47,6 +47,8 @@ in the second column of the table have the following meaning:
| | | +-------------------------+-------------------------+-------------------------+ | | | | +-------------------------+-------------------------+-------------------------+ |
| | | | :at:`inertiafromgeom` | :at:`inertiagrouprange` | :at:`exactmeshinertia` | | | | | | :at:`inertiafromgeom` | :at:`inertiagrouprange` | :at:`exactmeshinertia` | |
| | | +-------------------------+-------------------------+-------------------------+ | | | | +-------------------------+-------------------------+-------------------------+ |
| | | | :at:`autolimits` | | | |
| | | +-------------------------+-------------------------+-------------------------+ |
+--------------------------+----+------------------------------------------------------------------------------------+ +--------------------------+----+------------------------------------------------------------------------------------+
| |_2|:el:`lengthrange` | ? | .. table:: | | |_2|:el:`lengthrange` | ? | .. table:: |
| | | :class: mjcf-attributes | | | | :class: mjcf-attributes |
@@ -1524,6 +1526,13 @@ The unique top-level element, identifying the XML file as an MJCF model file.
This element is used to set options for the built-in parser and compiler. After parsing and compilation it no longer has This element is used to set options for the built-in parser and compiler. After parsing and compilation it no longer has
any effect. The settings here are global and apply to the entire model. any effect. The settings here are global and apply to the entire model.
:at:`autolimits`: :at-val:`[false, true], "false"`
This attribute affects the behavior of attributes such as "limited" (on <joint> or <tendon>), "forcelimited",
"ctrllimited", and "actlimited" (on <actuator>). If "true", these attributes are unnecessary and their value
will be inferred from the presence of their corresponding "range" attribute.
If "false", no such inference will happen: For a joint to be limited, both limited="true" and range="min max" must
be specified. In this mode, it is an error to specify a range without a limit.
|br| The default for this option will be set to "true" in an upcoming release.
:at:`boundmass`: :at-val:`real, "0"` :at:`boundmass`: :at-val:`real, "0"`
This attribute imposes a lower bound on the mass of each body except for the world body. Setting this attribute to This attribute imposes a lower bound on the mass of each body except for the world body. Setting this attribute to
a value greater than 0 can be used as a quick fix for poorly designed models that contain massless moving bodies, a value greater than 0 can be used as a quick fix for poorly designed models that contain massless moving bodies,
@@ -3162,7 +3171,8 @@ unit quaternions.
:at:`limited`: :at-val:`[false, true, auto], "auto"` :at:`limited`: :at-val:`[false, true, auto], "auto"`
This attribute specifies if the joint has limits. It interacts with the range attribute below. If this attribute This attribute specifies if the joint has limits. It interacts with the range attribute below. If this attribute
is "false", joint limits are disabled. If this attribute is "true", joint limits are enabled. If this is "false", joint limits are disabled. If this attribute is "true", joint limits are enabled. If this
attribute is "auto", joint limits will be enabled if range is defined and disabled otherwise. attribute is "auto", and :at:`autolimits` is set in :ref:`compiler <compiler>`, joint limits will be enabled
if range is defined.
:at:`solreflimit`, :at:`solimplimit` :at:`solreflimit`, :at:`solimplimit`
Constraint solver parameters for simulating joint limits. See :ref:`CSolver`. Constraint solver parameters for simulating joint limits. See :ref:`CSolver`.
:at:`solreffriction`, :at:`solimpfriction` :at:`solreffriction`, :at:`solimpfriction`
@@ -3176,6 +3186,8 @@ unit quaternions.
joints, the limit is imposed on the angle of rotation (relative to the reference configuration) regardless of the joints, the limit is imposed on the angle of rotation (relative to the reference configuration) regardless of the
axis of rotation. Only the second range parameter is used for ball joints; the first range parameter should be set to axis of rotation. Only the second range parameter is used for ball joints; the first range parameter should be set to
0. See the :ref:`Limit <coLimit>` section in the Computation chapter for more information. 0. See the :ref:`Limit <coLimit>` section in the Computation chapter for more information.
|br| Setting this attribute without specifying :at:`limited` is an error, unless :at:`autolimits` is set in
:ref:`compiler <compiler>`.
:at:`margin`: :at-val:`real, "0"` :at:`margin`: :at-val:`real, "0"`
The distance threshold below which limits become active. Recall that the :ref:`Constraint solver <Solver>` normally The distance threshold below which limits become active. Recall that the :ref:`Constraint solver <Solver>` normally
generates forces as soon as a constraint becomes active, even if the margin parameter makes that happen at a generates forces as soon as a constraint becomes active, even if the margin parameter makes that happen at a
@@ -4122,10 +4134,11 @@ the obstacle geom.
visualizer to enable and disable the rendering of entire groups of tendons. visualizer to enable and disable the rendering of entire groups of tendons.
:at:`limited`: :at-val:`[false, true, auto], "auto"` :at:`limited`: :at-val:`[false, true, auto], "auto"`
If this attribute is "true", the length limits defined by the range attribute below are imposed by the constraint If this attribute is "true", the length limits defined by the range attribute below are imposed by the constraint
solver. If this attribute is "auto", length limits will be enabled if range is defined and disabled otherwise. solver. If this attribute is "auto", and :at:`autolimits` is set in :ref:`compiler <compiler>`, length limits
will be enabled if range is defined.
:at:`range`: :at-val:`real(2), "0 0"` :at:`range`: :at-val:`real(2), "0 0"`
Range of allowed tendon lengths. To enable length limits, set the limited attribute to "true" in addition to defining Range of allowed tendon lengths. Setting this attribute without specifying :at:`limited` is an error, unless
the present value. :at:`autolimits` is set in :ref:`compiler <compiler>`.
:at:`solreflimit`, :at:`solimplimit` :at:`solreflimit`, :at:`solimplimit`
Constraint solver parameters for simulating tendon limits. See :ref:`CSolver`. Constraint solver parameters for simulating tendon limits. See :ref:`CSolver`.
:at:`solreffriction`, :at:`solimpfriction` :at:`solreffriction`, :at:`solimpfriction`
@@ -4269,25 +4282,33 @@ specify them independently.
visualizer to enable and disable the rendering of entire groups of actuators. visualizer to enable and disable the rendering of entire groups of actuators.
:at:`ctrllimited`: :at-val:`[false, true, auto], "auto"` :at:`ctrllimited`: :at-val:`[false, true, auto], "auto"`
If true, the control input to this actuator is automatically clamped to :at:`ctrlrange` at runtime. If false, control If true, the control input to this actuator is automatically clamped to :at:`ctrlrange` at runtime. If false, control
input clamping is disabled. If auto, control clamping will automatically be set to true if :at:`ctrlrange` is input clamping is disabled. If "auto" and :at:`autolimits` is set in :ref:`compiler <compiler>`, control
defined without explicitly setting this attribute to "true". Note that control input clamping can also be globally clamping will automatically be set to true if :at:`ctrlrange` is defined without explicitly setting this attribute
disabled with the :at:`clampctrl` attribute of :ref:`option/flag <option-flag>`. to "true". Note that control input clamping can also be globally disabled with the :at:`clampctrl` attribute of
:ref:`option/flag <option-flag>`.
:at:`forcelimited`: :at-val:`[false, true, auto], "auto"` :at:`forcelimited`: :at-val:`[false, true, auto], "auto"`
If true, the force output of this actuator is automatically clamped to :at:`forcerange` at runtime. If false, force If true, the force output of this actuator is automatically clamped to :at:`forcerange` at runtime. If false, force
clamping is disabled. If auto, force clamping will automatically be set to true if :at:`forcerange` is clamping is disabled. If "auto" and :at:`autolimits` is set in :ref:`compiler <compiler>`, force clamping will
defined without explicitly setting this attribute to "true". automatically be set to true if :at:`forcerange` is defined without explicitly setting this attribute to "true".
:at:`actlimited`: :at-val:`[false, true, auto], "auto"` :at:`actlimited`: :at-val:`[false, true, auto], "auto"`
If true, the internal state (activation) associated with this actuator is automatically clamped to :at:`actrange` at If true, the internal state (activation) associated with this actuator is automatically clamped to :at:`actrange` at
runtime. If false, activation clamping is disabled. If auto, activation clamping will automatically be set to true runtime. If false, activation clamping is disabled. If "auto" and :at:`autolimits` is set in
if :at:`actrange` is defined without explicitly setting this attribute to "true". See the :ref:`Activation clamping <CActRange>` :ref:`compiler <compiler>`, activation clamping will automatically be set to true if :at:`actrange` is defined
section for more details. without explicitly setting this attribute to "true". See the :ref:`Activation clamping <CActRange>` section for more
details.
:at:`ctrlrange`: :at-val:`real(2), "0 0"` :at:`ctrlrange`: :at-val:`real(2), "0 0"`
Range for clamping the control input. The compiler expects the first value to be smaller than the second value. Range for clamping the control input. The compiler expects the first value to be smaller than the second value.
|br| Setting this attribute without specifying :at:`ctrllimited` is an error, unless :at:`autolimits` is set in
:ref:`compiler <compiler>`.
:at:`forcerange`: :at-val:`real(2), "0 0"` :at:`forcerange`: :at-val:`real(2), "0 0"`
Range for clamping the force output. The compiler expects the first value to be no greater than the second value. Range for clamping the force output. The compiler expects the first value to be no greater than the second value.
|br| Setting this attribute without specifying :at:`forcelimited` is an error, unless :at:`autolimits` is set in
:ref:`compiler <compiler>`.
:at:`actrange`: :at-val:`real(2), "0 0"` :at:`actrange`: :at-val:`real(2), "0 0"`
Range for clamping the activation state. The compiler expects the first value to be no greater than the second value. Range for clamping the activation state. The compiler expects the first value to be no greater than the second value.
See the :ref:`Activation clamping <CActRange>` section for more details. See the :ref:`Activation clamping <CActRange>` section for more details.
|br| Setting this attribute without specifying :at:`actlimited` is an error, unless :at:`autolimits` is set in
:ref:`compiler <compiler>`.
:at:`lengthrange`: :at-val:`real(2), "0 0"` :at:`lengthrange`: :at-val:`real(2), "0 0"`
Range of feasible lengths of the actuator's transmission. See :ref:`Length Range <CLengthRange>`. Range of feasible lengths of the actuator's transmission. See :ref:`Length Range <CLengthRange>`.
:at:`gear`: :at-val:`real(6), "1 0 0 0 0 0"` :at:`gear`: :at-val:`real(6), "1 0 0 0 0 0"`
+10 -5
View File
@@ -31,13 +31,18 @@ General
- Cartesian 6D end-effector control is now possible by adding a reference site to actuators with :at:`site` - Cartesian 6D end-effector control is now possible by adding a reference site to actuators with :at:`site`
transmission. See description of new :at:`refsite` attribute in the :ref:`actuator<general>` documentation and transmission. See description of new :at:`refsite` attribute in the :ref:`actuator<general>` documentation and
`refsite.xml <https://github.com/deepmind/mujoco/tree/main/test/engine/testdata/refsite.xml>`_ example model. `refsite.xml <https://github.com/deepmind/mujoco/tree/main/test/engine/testdata/refsite.xml>`_ example model.
- Joint and tendon ``limited`` attribute and actuator ``ctrllimited``, ``forcelimited`` and ``actlimited`` attributes
now default to ``auto`` rather than ``false``. Limits are automatically set to ``true`` if the corresponding range *is - Added :at:`autolimits` compiler option. If ``true``, joint and tendon :at:`limited` attributes and actuator
defined* and ``false`` otherwise. :at:`ctrllimited`, :at:`forcelimited` and :at:`actlimited` attributes will automatically be set to ``true`` if the
corresponding range *is defined* and ``false`` otherwise.
If ``autolimits="false"`` (the default) models where a :at:`range` attribute is specified without the :at:`limited`
attribute will fail to compile. A future release will change the default of :at:`autolimits` to ``true``, and this
compilation error allows users to catch this future change of behavior.
.. attention:: .. attention::
This is a minor breaking change. In models where a range was defined but :at:`limited` was unspecified, the target This is a breaking change. In models where a range was defined but :at:`limited` was unspecified, explicitly set
element will now be limited. Explicitly set limited to ``false`` to revert to the previous behavior. limited to ``false`` or remove the range to maintain the current behavior of your model.
- Added moment of inertia computation for all well-formed meshes. This option is activated by setting the compiler - Added moment of inertia computation for all well-formed meshes. This option is activated by setting the compiler
flag :at:`exactmeshinertia` to ``true`` (defaults to ``false``). This default may change in the future. flag :at:`exactmeshinertia` to ``true`` (defaults to ``false``). This default may change in the future.
+2
View File
@@ -79,6 +79,8 @@ mjCModel::mjCModel() {
modelfiledir.clear(); modelfiledir.clear();
//------------------------ compiler settings //------------------------ compiler settings
// TODO(b/245077553): Toggle to true by default.
autolimits = false;
boundmass = 0; boundmass = 0;
boundinertia = 0; boundinertia = 0;
settotalmass = -1; settotalmass = -1;
+1
View File
@@ -107,6 +107,7 @@ class mjCModel {
std::vector<mjCDef*> defaults; // settings for each defaults class std::vector<mjCDef*> defaults; // settings for each defaults class
//------------------------ compiler settings //------------------------ compiler settings
bool autolimits; // infer "limited" attribute based on range
double boundmass; // enfore minimum body mass double boundmass; // enfore minimum body mass
double boundinertia; // enfore minimum body diagonal inertia double boundinertia; // enfore minimum body diagonal inertia
double settotalmass; // rescale masses and inertias; <=0: ignore double settotalmass; // rescale masses and inertias; <=0: ignore
+28 -5
View File
@@ -19,6 +19,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -62,6 +63,18 @@ static void checksize(double* size, mjtGeom type, mjCBase* object, const char* n
} }
} }
// error message for missing "limited" attribute
static void checklimited(
const mjCBase* obj,
bool autolimits, const char* entity, const char* attr, int limited, bool hasrange) {
if (!autolimits && limited == 2 && hasrange) {
std::stringstream ss;
ss << entity << " has `" << attr << "range` but not `" << attr << "limited`. "
<< "set the autolimits=\"true\" compiler option, specify `" << attr << "limited` "
<< "explicitly (\"true\" or \"false\"), or remove the `" << attr << "range` attribute.";
throw mjCError(obj, "%s", ss.str().c_str());
}
}
//------------------------- class mjCError implementation ------------------------------------------ //------------------------- class mjCError implementation ------------------------------------------
@@ -857,7 +870,9 @@ int mjCJoint::Compile(void) {
} }
// otherwise if limited is auto, set according to whether range is specified // otherwise if limited is auto, set according to whether range is specified
else if (limited==2) { else if (limited==2) {
limited = (range[0]==0 && range[1]==0) ? 0 : 1; bool hasrange = !(range[0]==0 && range[1]==0);
checklimited(this, model->autolimits, "joint", "", limited, hasrange);
limited = hasrange ? 1 : 0;
} }
// resolve limits // resolve limits
@@ -3222,7 +3237,9 @@ void mjCTendon::Compile(void) {
// if limited is auto, set to 1 if range is specified, otherwise unlimited // if limited is auto, set to 1 if range is specified, otherwise unlimited
if (limited==2) { if (limited==2) {
limited = (range[0]==0 && range[1]==0) ? 0 : 1; bool hasrange = !(range[0]==0 && range[1]==0);
checklimited(this, model->autolimits, "tendon", "", limited, hasrange);
limited = hasrange ? 1 : 0;
} }
// check limits // check limits
@@ -3389,13 +3406,19 @@ void mjCActuator::Compile(void) {
// if limited is auto, set to 1 if range is specified, otherwise unlimited // if limited is auto, set to 1 if range is specified, otherwise unlimited
if (forcelimited==2) { if (forcelimited==2) {
forcelimited = (forcerange[0]==0 && forcerange[1]==0) ? 0 : 1; bool hasrange = !(forcerange[0]==0 && forcerange[1]==0);
checklimited(this, model->autolimits, "actuator", "force", forcelimited, hasrange);
forcelimited = hasrange ? 1 : 0;
} }
if (ctrllimited==2) { if (ctrllimited==2) {
ctrllimited = (ctrlrange[0]==0 && ctrlrange[1]==0) ? 0 : 1; bool hasrange = !(ctrlrange[0]==0 && ctrlrange[1]==0);
checklimited(this, model->autolimits, "actuator", "ctrl", ctrllimited, hasrange);
ctrllimited = hasrange ? 1 : 0;
} }
if (actlimited==2) { if (actlimited==2) {
actlimited = (actrange[0]==0 && actrange[1]==0) ? 0 : 1; bool hasrange = !(actrange[0]==0 && actrange[1]==0);
checklimited(this, model->autolimits, "actuator", "act", actlimited, hasrange);
actlimited = hasrange ? 1 : 0;
} }
// check limits // check limits
+5 -2
View File
@@ -46,8 +46,8 @@ static const int nMJCF = 165;
static const char* MJCF[nMJCF][mjXATTRNUM] = { static const char* MJCF[nMJCF][mjXATTRNUM] = {
{"mujoco", "!", "1", "model"}, {"mujoco", "!", "1", "model"},
{"<"}, {"<"},
{"compiler", "*", "18", "boundmass", "boundinertia", "settotalmass", "balanceinertia", {"compiler", "*", "19", "autolimits", "boundmass", "boundinertia", "settotalmass",
"strippath", "coordinate", "angle", "fitaabb", "eulerseq", "balanceinertia", "strippath", "coordinate", "angle", "fitaabb", "eulerseq",
"meshdir", "texturedir", "discardvisual", "convexhull", "usethread", "meshdir", "texturedir", "discardvisual", "convexhull", "usethread",
"fusestatic", "inertiafromgeom", "inertiagrouprange", "exactmeshinertia"}, "fusestatic", "inertiafromgeom", "inertiagrouprange", "exactmeshinertia"},
{"<"}, {"<"},
@@ -765,6 +765,9 @@ void mjXReader::Compiler(XMLElement* section, mjCModel* mod) {
int n; int n;
// top-level attributes // top-level attributes
if (MapValue(section, "autolimits", &n, bool_map, 2)) {
mod->autolimits = (n==1);
}
ReadAttr(section, "boundmass", 1, &mod->boundmass, text); ReadAttr(section, "boundmass", 1, &mod->boundmass, text);
ReadAttr(section, "boundinertia", 1, &mod->boundinertia, text); ReadAttr(section, "boundinertia", 1, &mod->boundinertia, text);
ReadAttr(section, "settotalmass", 1, &mod->settotalmass, text); ReadAttr(section, "settotalmass", 1, &mod->settotalmass, text);
+3
View File
@@ -696,6 +696,9 @@ void mjXWriter::Compiler(XMLElement* root) {
if (model->exactmeshinertia) { if (model->exactmeshinertia) {
WriteAttrTxt(section, "exactmeshinertia", "true"); WriteAttrTxt(section, "exactmeshinertia", "true");
} }
// always enable autolimits. limited attributes will be written appropriately
// TODO(b/245077553): Remove this when the default is true.
WriteAttrTxt(section, "autolimits", "true");
} }
+1
View File
@@ -206,6 +206,7 @@ TEST_F(CoreSmoothTest, RefsiteBringsToPose) {
constexpr char kRefsitePath[] = "engine/testdata/refsite.xml"; constexpr char kRefsitePath[] = "engine/testdata/refsite.xml";
const std::string xml_path = GetTestDataFilePath(kRefsitePath); const std::string xml_path = GetTestDataFilePath(kRefsitePath);
mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, 0, 0); mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, 0, 0);
ASSERT_THAT(model, ::testing::NotNull());
mjData* data = mj_makeData(model); mjData* data = mj_makeData(model);
// set pose target in ctrl (3 positions, 3 rotations) // set pose target in ctrl (3 positions, 3 rotations)
+1
View File
@@ -1,4 +1,5 @@
<mujoco> <mujoco>
<compiler autolimits="true"/>
<option density="1000"/> <option density="1000"/>
<worldbody> <worldbody>
+1
View File
@@ -1,4 +1,5 @@
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<geom type="plane" size="1 1 .01"/> <geom type="plane" size="1 1 .01"/>
<light pos="0 0 2"/> <light pos="0 0 2"/>
+1
View File
@@ -1,4 +1,5 @@
<mujoco> <mujoco>
<compiler autolimits="true"/>
<!-- <!--
Adding a high fluid viscosity and using implicit integration for extra stabillity. Adding a high fluid viscosity and using implicit integration for extra stabillity.
Extra stabillity is required because the abstract "arm" model is not very realistic. Extra stabillity is required because the abstract "arm" model is not very realistic.
+1
View File
@@ -1,4 +1,5 @@
<mujoco> <mujoco>
<compiler autolimits="true" />
<default> <default>
<default class="limited_false"> <default class="limited_false">
<joint limited="false"/> <joint limited="false"/>
+92
View File
@@ -604,5 +604,97 @@ TEST_F(LimitedTest, JointLimited) {
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(LimitedTest, ErrorIfLimitedMissingOnJoint) {
static constexpr char xml[] = R"(
<mujoco>
<compiler autolimits="false"/>
<worldbody>
<body>
<joint user="1" range="0 1"/>
<geom size="1"/>
</body>
</worldbody>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, IsNull());
EXPECT_THAT(error.data(), HasSubstr("limited"));
}
TEST_F(LimitedTest, ExplicitLimitedFalseIsOk) {
static constexpr char xml[] = R"(
<mujoco>
<compiler autolimits="false"/>
<worldbody>
<body>
<joint user="1" limited="false" range="0 1"/>
<geom size="1"/>
</body>
</worldbody>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << error.data();
EXPECT_EQ(model->jnt_limited[0], 0);
mj_deleteModel(model);
}
TEST_F(LimitedTest, ErrorIfLimitedMissingOnTendon) {
static constexpr char xml[] = R"(
<mujoco>
<compiler autolimits="false"/>
<worldbody>
<body>
<joint type="slide"/>
<geom size="1"/>
<site name="s1"/>
</body>
<site name="s2"/>
</worldbody>
<tendon>
<spatial range="-1 1">
<site site="s1"/>
<site site="s2"/>
</spatial>
</tendon>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, IsNull());
EXPECT_THAT(error.data(), HasSubstr("limited"));
EXPECT_THAT(error.data(), HasSubstr("tendon"));
}
TEST_F(LimitedTest, ErrorIfForceLimitedMissingOnActuator) {
static constexpr char xml[] = R"(
<mujoco>
<compiler autolimits="false"/>
<worldbody>
<body>
<joint type="slide" name="J1"/>
<geom size="1"/>
</body>
</worldbody>
<actuator>
<position joint="J1" forcerange="0 100"/>
</actuator>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, IsNull());
EXPECT_THAT(error.data(), HasSubstr("forcelimited"));
EXPECT_THAT(error.data(), HasSubstr("forcerange"));
EXPECT_THAT(error.data(), HasSubstr("actuator"));
}
} // namespace } // namespace
} // namespace mujoco } // namespace mujoco
+1
View File
@@ -316,6 +316,7 @@ TEST_F(UserDataTest, ReadShellParameter) {
TEST_F(UserDataTest, ReadsDamper) { TEST_F(UserDataTest, ReadsDamper) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true"/>
<worldbody> <worldbody>
<body> <body>
<geom size="1"/> <geom size="1"/>
+25 -12
View File
@@ -144,7 +144,7 @@ TEST_F(XMLWriterTest, DropsInertialIfFromGeom) {
TEST_F(XMLWriterTest, DoesNotKeepInferredJointLimited) { TEST_F(XMLWriterTest, DoesNotKeepInferredJointLimited) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler angle="radian"/> <compiler angle="radian" autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge" range="-1 1"/> <joint name="hinge" range="-1 1"/>
@@ -160,10 +160,10 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredJointLimited) {
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(XMLWriterTest, DoesNotKeepExplicitJointLimited) { TEST_F(XMLWriterTest, DoesNotKeepExplicitJointLimitedIfAutoLimits) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler angle="radian"/> <compiler angle="radian" autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge" limited="true" range="-1 1"/> <joint name="hinge" limited="true" range="-1 1"/>
@@ -174,15 +174,16 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitJointLimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(XMLWriterTest, KeepsJointLimitedFalse) { TEST_F(XMLWriterTest, KeepsJointLimitedFalseIfAutoLimits) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler angle="radian"/> <compiler angle="radian" autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge" limited="false" range="-1 1"/> <joint name="hinge" limited="false" range="-1 1"/>
@@ -200,7 +201,7 @@ TEST_F(XMLWriterTest, KeepsJointLimitedFalse) {
TEST_F(XMLWriterTest, DoesNotKeepInferredTendonLimited) { TEST_F(XMLWriterTest, DoesNotKeepInferredTendonLimited) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler angle="radian"/> <compiler angle="radian" autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint type="slide"/> <joint type="slide"/>
@@ -219,15 +220,16 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredTendonLimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(XMLWriterTest, DoesNotKeepExplicitTendonLimited) { TEST_F(XMLWriterTest, DoesNotKeepExplicitTendonLimitedIfAutoLimits) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler angle="radian"/> <compiler angle="radian" autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint type="slide"/> <joint type="slide"/>
@@ -246,15 +248,16 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitTendonLimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(XMLWriterTest, KeepsTendonLimitedFalse) { TEST_F(XMLWriterTest, KeepsTendonLimitedFalseIfAutoLimits) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler angle="radian"/> <compiler angle="radian" autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint type="slide"/> <joint type="slide"/>
@@ -280,6 +283,7 @@ TEST_F(XMLWriterTest, KeepsTendonLimitedFalse) {
TEST_F(XMLWriterTest, DoesNotKeepInferredActlimited) { TEST_F(XMLWriterTest, DoesNotKeepInferredActlimited) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge"/> <joint name="hinge"/>
@@ -293,14 +297,16 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredActlimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("actlimited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("actlimited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(XMLWriterTest, DoesNotKeepExplicitActlimited) { TEST_F(XMLWriterTest, DoesNotKeepExplicitActlimitedIfAutoLimits) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge"/> <joint name="hinge"/>
@@ -314,6 +320,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitActlimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("actlimited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("actlimited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);
@@ -322,6 +329,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitActlimited) {
TEST_F(XMLWriterTest, KeepsActlimitedFalse) { TEST_F(XMLWriterTest, KeepsActlimitedFalse) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge"/> <joint name="hinge"/>
@@ -342,6 +350,7 @@ TEST_F(XMLWriterTest, KeepsActlimitedFalse) {
TEST_F(XMLWriterTest, DoesNotKeepInferredCtrllimited) { TEST_F(XMLWriterTest, DoesNotKeepInferredCtrllimited) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge"/> <joint name="hinge"/>
@@ -360,9 +369,10 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredCtrllimited) {
mj_deleteModel(model); mj_deleteModel(model);
} }
TEST_F(XMLWriterTest, DoesNotKeepExplicitCtrllimited) { TEST_F(XMLWriterTest, DoesNotKeepExplicitCtrllimitedIfAutoLimits) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge"/> <joint name="hinge"/>
@@ -404,6 +414,7 @@ TEST_F(XMLWriterTest, KeepsCtrllimitedFalse) {
TEST_F(XMLWriterTest, DoesNotKeepInferredForcelimited) { TEST_F(XMLWriterTest, DoesNotKeepInferredForcelimited) {
static constexpr char xml[] = R"( static constexpr char xml[] = R"(
<mujoco> <mujoco>
<compiler autolimits="true" />
<worldbody> <worldbody>
<body> <body>
<joint name="hinge"/> <joint name="hinge"/>
@@ -417,6 +428,7 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredForcelimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("forcelimited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("forcelimited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);
@@ -438,6 +450,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitForcelimited) {
)"; )";
mjModel* model = LoadModelFromString(xml); mjModel* model = LoadModelFromString(xml);
std::string saved_xml = SaveAndReadXml(model); std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\"")); EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\""));
EXPECT_THAT(saved_xml, Not(HasSubstr("forcelimited=\"true\""))); EXPECT_THAT(saved_xml, Not(HasSubstr("forcelimited=\"true\"")));
mj_deleteModel(model); mj_deleteModel(model);