Automatically infer *limited field from *range field for joints, tendons and actuators.
PiperOrigin-RevId: 467980594 Change-Id: If635c55a90cf949796b99a41395a6c2c4f7ea094
This commit is contained in:
committed by
Copybara-Service
parent
29535a8eca
commit
4bfc2c0311
+18
-14
@@ -3154,9 +3154,10 @@ unit quaternions.
|
||||
the desired time constant (first value) and damping ratio (second value). This is done by taking into account the
|
||||
joint inertia in the model reference configuration. Note that the format is the same as the solref parameter of the
|
||||
constraint solver.
|
||||
:at:`limited`: :at-val:`[false, true], "false"`
|
||||
This attribute specifies if the joint has limits. It interacts with the range attribute below. Both must be set to
|
||||
enable joint limits. If this attribute is "false", any joint range data will be ignored.
|
||||
: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
|
||||
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.
|
||||
:at:`solreflimit`, :at:`solimplimit`
|
||||
Constraint solver parameters for simulating joint limits. See :ref:`CSolver`.
|
||||
:at:`solreffriction`, :at:`solimpfriction`
|
||||
@@ -3167,7 +3168,7 @@ unit quaternions.
|
||||
:at:`range`: :at-val:`real(2), "0 0"`
|
||||
The joint limits. Limits can be imposed on all joint types except for free joints. For hinge and ball joints, the
|
||||
range is specified in degrees or radians depending on the angle attribute of :ref:`compiler <compiler>`. For ball
|
||||
joints, the limit is imposed on the angle of rotation (relative to the 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
|
||||
0. See the :ref:`Limit <coLimit>` section in the Computation chapter for more information.
|
||||
:at:`margin`: :at-val:`real, "0"`
|
||||
@@ -4103,9 +4104,9 @@ the obstacle geom.
|
||||
:at:`group`: :at-val:`int, "0"`
|
||||
Integer group to which the tendon belongs. This attribute can be used for custom tags. It is also used by the
|
||||
visualizer to enable and disable the rendering of entire groups of tendons.
|
||||
:at:`limited`: :at-val:`[false, true], "false"`
|
||||
: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
|
||||
solver.
|
||||
solver. If this attribute is "auto", length limits will be enabled if range is defined and disabled otherwise.
|
||||
: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
|
||||
the present value.
|
||||
@@ -4250,17 +4251,20 @@ specify them independently.
|
||||
:at:`group`: :at-val:`int, "0"`
|
||||
Integer group to which the actuator belongs. This attribute can be used for custom tags. It is also used by the
|
||||
visualizer to enable and disable the rendering of entire groups of actuators.
|
||||
:at:`ctrllimited`: :at-val:`[false, true], "false"`
|
||||
: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
|
||||
input clamping is disabled. 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], "false"`
|
||||
input clamping is disabled. If auto, control clamping will automatically be set to true if :at:`ctrlrange` is
|
||||
defined without explicitly setting this attribute 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"`
|
||||
If true, the force output of this actuator is automatically clamped to :at:`forcerange` at runtime. If false, force
|
||||
clamping is disabled.
|
||||
:at:`actlimited`: :at-val:`[false, true], "false"`
|
||||
clamping is disabled. If auto, force clamping will 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"`
|
||||
If true, the internal state (activation) associated with this actuator is automatically clamped to :at:`actrange` at
|
||||
runtime. If false, activation clamping is disabled. See the :ref:`Activation clamping <CActRange>` section for more
|
||||
details.
|
||||
runtime. If false, activation clamping is disabled. If auto, activation clamping will automatically be set to true
|
||||
if :at:`actrange` is defined 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"`
|
||||
Range for clamping the control input. The compiler expects the first value to be smaller than the second value.
|
||||
:at:`forcerange`: :at-val:`real(2), "0 0"`
|
||||
|
||||
@@ -20,6 +20,13 @@ General
|
||||
the free camera at model load time.
|
||||
- Added ``mjv_defaultFreeCamera`` which sets the default free camera, respecting the above attributes.
|
||||
- ``simulate`` now supports taking a screenshot via a button in the File section or via ``Ctrl-P``.
|
||||
- Joint and tendon ``limited`` attribute and actuator ``ctrllimited``, ``forcelimited`` and ``actlimited`` attributes
|
||||
now default to ``"auto"``, which means they are automatically set to ``true`` if the corresponding range is
|
||||
defined and disabled otherwise.
|
||||
|
||||
.. attention::
|
||||
This is a minor breaking change. Code where a range was defined and limited was unspecified will now
|
||||
be limited. Please explicitly set limited to ``false`` to revert to the previous behavior.
|
||||
|
||||
Deleted/deprecated features
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -797,7 +797,7 @@ mjCJoint::mjCJoint(mjCModel* _model, mjCDef* _def) {
|
||||
group = 0;
|
||||
mjuu_setvec(pos, 0, 0, 0);
|
||||
mjuu_setvec(axis, 0, 0, 1);
|
||||
limited = false;
|
||||
limited = 2;
|
||||
stiffness = 0;
|
||||
range[0] = 0;
|
||||
range[1] = 0;
|
||||
@@ -851,6 +851,15 @@ int mjCJoint::Compile(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// free joints cannot be limited
|
||||
if (type==mjJNT_FREE) {
|
||||
limited = 0;
|
||||
}
|
||||
// otherwise if limited is auto, set according to whether range is specified
|
||||
else if (limited==2) {
|
||||
limited = (range[0]==0 && range[1]==0) ? 0 : 1;
|
||||
}
|
||||
|
||||
// resolve limits
|
||||
if (limited) {
|
||||
// check data
|
||||
@@ -2975,7 +2984,7 @@ mjCTendon::mjCTendon(mjCModel* _model, mjCDef* _def) {
|
||||
group = 0;
|
||||
material.clear();
|
||||
width = 0.003;
|
||||
limited = false;
|
||||
limited = 2;
|
||||
range[0] = 0;
|
||||
range[1] = 0;
|
||||
mj_defaultSolRefImp(solref_limit, solimp_limit);
|
||||
@@ -3205,6 +3214,11 @@ void mjCTendon::Compile(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// if limited is auto, set to 1 if range is specified, otherwise unlimited
|
||||
if (limited==2) {
|
||||
limited = (range[0]==0 && range[1]==0) ? 0 : 1;
|
||||
}
|
||||
|
||||
// check limits
|
||||
if (range[0]>=range[1] && limited) {
|
||||
throw mjCError(this, "invalid limits in tendon '%s (id = %d)'", name.c_str(), id);
|
||||
@@ -3317,9 +3331,9 @@ void mjCWrap::Compile(void) {
|
||||
mjCActuator::mjCActuator(mjCModel* _model, mjCDef* _def) {
|
||||
// actuator defaults
|
||||
group = 0;
|
||||
ctrllimited = false;
|
||||
forcelimited = false;
|
||||
actlimited = false;
|
||||
ctrllimited = 2;
|
||||
forcelimited = 2;
|
||||
actlimited = 2;
|
||||
trntype = mjTRN_UNDEFINED;
|
||||
dyntype = mjDYN_NONE;
|
||||
gaintype = mjGAIN_FIXED;
|
||||
@@ -3366,6 +3380,17 @@ void mjCActuator::Compile(void) {
|
||||
}
|
||||
userdata.resize(model->nuser_actuator);
|
||||
|
||||
// if limited is auto, set to 1 if range is specified, otherwise unlimited
|
||||
if (forcelimited==2) {
|
||||
forcelimited = (forcerange[0]==0 && forcerange[1]==0) ? 0 : 1;
|
||||
}
|
||||
if (ctrllimited==2) {
|
||||
ctrllimited = (ctrlrange[0]==0 && ctrlrange[1]==0) ? 0 : 1;
|
||||
}
|
||||
if (actlimited==2) {
|
||||
actlimited = (actrange[0]==0 && actrange[1]==0) ? 0 : 1;
|
||||
}
|
||||
|
||||
// check limits
|
||||
if (forcerange[0]>=forcerange[1] && forcelimited) {
|
||||
throw mjCError(this, "invalid force range for actuator '%s' (id = %d)", name.c_str(), id);
|
||||
@@ -3437,7 +3462,7 @@ void mjCActuator::Compile(void) {
|
||||
if (pjnt->urdfeffort>0) {
|
||||
forcerange[0] = -pjnt->urdfeffort;
|
||||
forcerange[1] = pjnt->urdfeffort;
|
||||
forcelimited = true;
|
||||
forcelimited = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ class mjCJoint : public mjCBase {
|
||||
// variables set by user: joint properties
|
||||
mjtJoint type; // type of Joint
|
||||
int group; // used for rendering
|
||||
bool limited; // does joint have limits
|
||||
int limited; // does joint have limits: 0 false, 1 true, 2 auto
|
||||
double pos[3]; // anchor position
|
||||
double axis[3]; // joint axis
|
||||
double stiffness; // stiffness coefficient
|
||||
@@ -783,7 +783,7 @@ class mjCTendon : public mjCBase {
|
||||
// variables set by user
|
||||
int group; // group for visualization
|
||||
std::string material; // name of material for rendering
|
||||
bool limited; // does tendon have limits
|
||||
int limited; // does tendon have limits: 0 false, 1 true, 2 auto
|
||||
double width; // width for rendering
|
||||
mjtNum solref_limit[mjNREF]; // solver reference: tendon limits
|
||||
mjtNum solimp_limit[mjNIMP]; // solver impedance: tendon limits
|
||||
@@ -843,9 +843,9 @@ class mjCActuator : public mjCBase {
|
||||
public:
|
||||
// variables set by user or API
|
||||
int group; // group for visualization
|
||||
bool ctrllimited; // are control limits defined
|
||||
bool forcelimited; // are force limits defined
|
||||
bool actlimited; // are activation limits defined
|
||||
int ctrllimited; // are control limits defined: 0 false, 1 true, 2 auto
|
||||
int forcelimited; // are force limits defined: 0 false, 1 true, 2 auto
|
||||
int actlimited; // are activation limits defined: 0 false, 1 true, 2 auto
|
||||
mjtDyn dyntype; // dynamics type
|
||||
mjtTrn trntype; // transmission type
|
||||
mjtGain gaintype; // gain type
|
||||
|
||||
@@ -1092,9 +1092,7 @@ void mjXReader::OneJoint(XMLElement* elem, mjCJoint* pjoint) {
|
||||
if (MapValue(elem, "type", &n, joint_map, joint_sz)) {
|
||||
pjoint->type = (mjtJoint)n;
|
||||
}
|
||||
if (MapValue(elem, "limited", &n, bool_map, 2)) {
|
||||
pjoint->limited = (n==1);
|
||||
}
|
||||
MapValue(elem, "limited", &pjoint->limited, TFAuto_map, 3);
|
||||
ReadAttrInt(elem, "group", &pjoint->group);
|
||||
ReadAttr(elem, "solreflimit", mjNREF, pjoint->solref_limit, text, false, false);
|
||||
ReadAttr(elem, "solimplimit", mjNIMP, pjoint->solimp_limit, text, false, false);
|
||||
@@ -1350,7 +1348,6 @@ void mjXReader::OneEquality(XMLElement* elem, mjCEquality* pequality) {
|
||||
|
||||
// tendon element parser
|
||||
void mjXReader::OneTendon(XMLElement* elem, mjCTendon* pten) {
|
||||
int n;
|
||||
string text;
|
||||
|
||||
// read attributes
|
||||
@@ -1358,9 +1355,7 @@ void mjXReader::OneTendon(XMLElement* elem, mjCTendon* pten) {
|
||||
ReadAttrTxt(elem, "class", pten->classname);
|
||||
ReadAttrInt(elem, "group", &pten->group);
|
||||
ReadAttrTxt(elem, "material", pten->material);
|
||||
if (MapValue(elem, "limited", &n, bool_map, 2)) {
|
||||
pten->limited = (n==1);
|
||||
}
|
||||
MapValue(elem, "limited", &pten->limited, TFAuto_map, 3);
|
||||
ReadAttr(elem, "width", 1, &pten->width, text);
|
||||
ReadAttr(elem, "solreflimit", mjNREF, pten->solref_limit, text, false, false);
|
||||
ReadAttr(elem, "solimplimit", mjNIMP, pten->solimp_limit, text, false, false);
|
||||
@@ -1392,15 +1387,9 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
ReadAttrTxt(elem, "name", pact->name);
|
||||
ReadAttrTxt(elem, "class", pact->classname);
|
||||
ReadAttrInt(elem, "group", &pact->group);
|
||||
if (MapValue(elem, "ctrllimited", &n, bool_map, 2)) {
|
||||
pact->ctrllimited = (n==1);
|
||||
}
|
||||
if (MapValue(elem, "forcelimited", &n, bool_map, 2)) {
|
||||
pact->forcelimited = (n==1);
|
||||
}
|
||||
if (MapValue(elem, "actlimited", &n, bool_map, 2)) {
|
||||
pact->actlimited = (n==1);
|
||||
}
|
||||
MapValue(elem, "ctrllimited", &pact->ctrllimited, TFAuto_map, 3);
|
||||
MapValue(elem, "forcelimited", &pact->forcelimited, TFAuto_map, 3);
|
||||
MapValue(elem, "actlimited", &pact->actlimited, TFAuto_map, 3);
|
||||
ReadAttr(elem, "ctrlrange", 2, pact->ctrlrange, text);
|
||||
ReadAttr(elem, "forcerange", 2, pact->forcerange, text);
|
||||
ReadAttr(elem, "actrange", 2, pact->actrange, text);
|
||||
@@ -1518,7 +1507,7 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
pact->dyntype = mjDYN_INTEGRATOR;
|
||||
pact->gaintype = mjGAIN_FIXED;
|
||||
pact->biastype = mjBIAS_AFFINE;
|
||||
pact->actlimited = true;
|
||||
pact->actlimited = 1;
|
||||
pact->biasprm[1] = -pact->gainprm[0];
|
||||
// require actrange
|
||||
if (!ReadAttr(elem, "actrange", 2, pact->actrange, text)) {
|
||||
@@ -1544,7 +1533,7 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
}
|
||||
|
||||
// implied parameters
|
||||
pact->ctrllimited = true;
|
||||
pact->ctrllimited = 1;
|
||||
pact->dyntype = mjDYN_NONE;
|
||||
pact->gaintype = mjGAIN_AFFINE;
|
||||
pact->biastype = mjBIAS_NONE;
|
||||
@@ -1622,7 +1611,7 @@ void mjXReader::OneActuator(XMLElement* elem, mjCActuator* pact) {
|
||||
}
|
||||
|
||||
// implied parameters
|
||||
pact->ctrllimited = true;
|
||||
pact->ctrllimited = 1;
|
||||
pact->dyntype = mjDYN_NONE;
|
||||
pact->gaintype = mjGAIN_FIXED;
|
||||
pact->biastype = mjBIAS_NONE;
|
||||
@@ -1728,9 +1717,7 @@ void mjXReader::OneComposite(XMLElement* elem, mjCBody* pbody, mjCDef* def) {
|
||||
ReadAttr(ejnt, "solimpfix", mjNIMP, comp.def[kind].equality.solimp, text, false, false);
|
||||
|
||||
// joint attributes
|
||||
if (MapValue(ejnt, "limited", &n, bool_map, 2)) {
|
||||
comp.def[kind].joint.limited = (n==1);
|
||||
}
|
||||
MapValue(elem, "limited", &comp.def[kind].joint.limited, TFAuto_map, 3);
|
||||
ReadAttrInt(ejnt, "group", &comp.def[kind].joint.group);
|
||||
ReadAttr(ejnt, "solreflimit", mjNREF, comp.def[kind].joint.solref_limit, text, false, false);
|
||||
ReadAttr(ejnt, "solimplimit", mjNIMP, comp.def[kind].joint.solimp_limit, text, false, false);
|
||||
@@ -1762,9 +1749,7 @@ void mjXReader::OneComposite(XMLElement* elem, mjCBody* pbody, mjCDef* def) {
|
||||
ReadAttr(eten, "solimpfix", mjNIMP, comp.def[kind].equality.solimp, text, false, false);
|
||||
|
||||
// tendon attributes
|
||||
if (MapValue(eten, "limited", &n, bool_map, 2)) {
|
||||
comp.def[kind].tendon.limited = (n==1);
|
||||
}
|
||||
MapValue(elem, "limited", &comp.def[kind].tendon.limited, TFAuto_map, 3);
|
||||
ReadAttrInt(eten, "group", &comp.def[kind].tendon.group);
|
||||
ReadAttr(eten, "solreflimit", mjNREF, comp.def[kind].tendon.solref_limit, text, false, false);
|
||||
ReadAttr(eten, "solimplimit", mjNIMP, comp.def[kind].tendon.solimp_limit, text, false, false);
|
||||
|
||||
@@ -207,6 +207,13 @@ void mjXWriter::OneJoint(XMLElement* elem, mjCJoint* pjoint, mjCDef* def) {
|
||||
}
|
||||
}
|
||||
|
||||
// special handling of limits
|
||||
bool range_defined = pjoint->range[0]!=0 || pjoint->range[1]!=0;
|
||||
bool limited_inferred = def->joint.limited==2 && pjoint->limited==range_defined;
|
||||
if (writingdefaults || !limited_inferred) {
|
||||
WriteAttrKey(elem, "limited", TFAuto_map, 3, pjoint->limited, def->joint.limited);
|
||||
}
|
||||
|
||||
// defaults and regular
|
||||
if (pjoint->type != def->joint.type) {
|
||||
WriteAttrTxt(elem, "type", FindValue(joint_map, joint_sz, pjoint->type));
|
||||
@@ -214,7 +221,6 @@ void mjXWriter::OneJoint(XMLElement* elem, mjCJoint* pjoint, mjCDef* def) {
|
||||
WriteAttrInt(elem, "group", pjoint->group, def->joint.group);
|
||||
WriteAttr(elem, "ref", 1, &pjoint->ref, &zero);
|
||||
WriteAttr(elem, "springref", 1, &pjoint->springref, &zero);
|
||||
WriteAttrKey(elem, "limited", bool_map, 2, pjoint->limited, def->joint.limited);
|
||||
WriteAttr(elem, "solreflimit", mjNREF, pjoint->solref_limit, def->joint.solref_limit);
|
||||
WriteAttr(elem, "solimplimit", mjNIMP, pjoint->solimp_limit, def->joint.solimp_limit);
|
||||
WriteAttr(elem, "solreffriction", mjNREF, pjoint->solref_friction, def->joint.solref_friction);
|
||||
@@ -491,9 +497,15 @@ void mjXWriter::OneTendon(XMLElement* elem, mjCTendon* pten, mjCDef* def) {
|
||||
WriteAttrTxt(elem, "class", pten->classname);
|
||||
}
|
||||
|
||||
// special handling of limits
|
||||
bool range_defined = pten->range[0]!=0 || pten->range[1]!=0;
|
||||
bool limited_inferred = def->tendon.limited==2 && pten->limited==range_defined;
|
||||
if (writingdefaults || !limited_inferred) {
|
||||
WriteAttrKey(elem, "limited", TFAuto_map, 3, pten->limited, def->tendon.limited);
|
||||
}
|
||||
|
||||
// defaults and regular
|
||||
WriteAttrInt(elem, "group", pten->group, def->tendon.group);
|
||||
WriteAttrKey(elem, "limited", bool_map, 2, pten->limited, def->tendon.limited);
|
||||
WriteAttr(elem, "solreflimit", mjNREF, pten->solref_limit, def->tendon.solref_limit);
|
||||
WriteAttr(elem, "solimplimit", mjNIMP, pten->solimp_limit, def->tendon.solimp_limit);
|
||||
WriteAttr(elem, "solreffriction", mjNREF, pten->solref_friction, def->tendon.solref_friction);
|
||||
@@ -563,11 +575,26 @@ void mjXWriter::OneActuator(XMLElement* elem, mjCActuator* pact, mjCDef* def) {
|
||||
}
|
||||
}
|
||||
|
||||
// special handling of limits
|
||||
bool range_defined, limited_inferred;
|
||||
range_defined = pact->ctrlrange[0]!=0 || pact->ctrlrange[1]!=0;
|
||||
limited_inferred = def->actuator.ctrllimited==2 && pact->ctrllimited==range_defined;
|
||||
if (writingdefaults || !limited_inferred) {
|
||||
WriteAttrKey(elem, "ctrllimited", TFAuto_map, 3, pact->ctrllimited, def->actuator.ctrllimited);
|
||||
}
|
||||
range_defined = pact->forcerange[0]!=0 || pact->forcerange[1]!=0;
|
||||
limited_inferred = def->actuator.forcelimited==2 && pact->forcelimited==range_defined;
|
||||
if (writingdefaults || !limited_inferred) {
|
||||
WriteAttrKey(elem, "forcelimited", TFAuto_map, 3, pact->forcelimited, def->actuator.forcelimited);
|
||||
}
|
||||
range_defined = pact->actrange[0]!=0 || pact->actrange[1]!=0;
|
||||
limited_inferred = def->actuator.actlimited==2 && pact->actlimited==range_defined;
|
||||
if (writingdefaults || !limited_inferred) {
|
||||
WriteAttrKey(elem, "actlimited", TFAuto_map, 3, pact->actlimited, def->actuator.actlimited);
|
||||
}
|
||||
|
||||
// defaults and regular
|
||||
WriteAttrInt(elem, "group", pact->group, def->actuator.group);
|
||||
WriteAttrKey(elem, "ctrllimited", bool_map, 2, pact->ctrllimited, def->actuator.ctrllimited);
|
||||
WriteAttrKey(elem, "forcelimited", bool_map, 2, pact->forcelimited, def->actuator.forcelimited);
|
||||
WriteAttrKey(elem, "actlimited", bool_map, 2, pact->actlimited, def->actuator.actlimited);
|
||||
WriteAttr(elem, "ctrlrange", 2, pact->ctrlrange, def->actuator.ctrlrange);
|
||||
WriteAttr(elem, "forcerange", 2, pact->forcerange, def->actuator.forcerange);
|
||||
WriteAttr(elem, "actrange", 2, pact->actrange, def->actuator.actrange);
|
||||
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
<mujoco>
|
||||
<default>
|
||||
<default class="limited_false">
|
||||
<joint limited="false"/>
|
||||
</default>
|
||||
<default class="limited_true">
|
||||
<joint limited="true"/>
|
||||
<default class="limited_auto">
|
||||
<joint limited="auto"/>
|
||||
</default>
|
||||
</default>
|
||||
<default class="range_defined">
|
||||
<joint range="-1.57 1.57"/>
|
||||
</default>
|
||||
</default>
|
||||
<!--
|
||||
`user` attributes of the joints below indicate the expected state of the associated
|
||||
jnt_limited property in the compiled model.
|
||||
-->
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint user="0"/>
|
||||
<joint user="1" range="0 1"/>
|
||||
<joint user="0" range="0 1" limited="false"/>
|
||||
<joint user="1" range="0 1" limited="true"/>
|
||||
<joint user="0" range="0 1" class="limited_false"/>
|
||||
<joint user="1" range="0 1" class="limited_true"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
<!-- adding another body since bodies are allowed a maximum of 6 DoFs -->
|
||||
<body>
|
||||
<joint user="1" range="0 1" class="limited_false" limited="true"/>
|
||||
<joint user="0" range="0 1" class="limited_true" limited="false"/>
|
||||
<joint user="1" class="range_defined"/>
|
||||
<joint user="1" range="0 1" class="limited_auto"/>
|
||||
<joint user="1" range="0 1" limited="auto"/>
|
||||
<joint user="0" limited="auto"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
<body>
|
||||
<!-- free joints should remain unlimited even if a (meaningless) range is defined -->
|
||||
<joint user="0" type="free" class="range_defined"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -39,7 +39,7 @@ using ::testing::NotNull;
|
||||
|
||||
// ------------------------ test keyframes -------------------------------------
|
||||
|
||||
static const char* const kKeyframePath = "user/testdata/keyframe.xml";
|
||||
constexpr char kKeyframePath[] = "user/testdata/keyframe.xml";
|
||||
|
||||
TEST_F(MujocoTest, KeyFrameTest) {
|
||||
const std::string xml_path = GetTestDataFilePath(kKeyframePath);
|
||||
@@ -567,5 +567,25 @@ TEST_F(UserDataTest, NSensorTooSmall) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_sensor"));
|
||||
}
|
||||
|
||||
// ------------- test for auto parsing of *limited fields -------------
|
||||
|
||||
using LimitedTest = MujocoTest;
|
||||
|
||||
constexpr char kKeyAutoLimits[] = "user/testdata/auto_limits.xml";
|
||||
|
||||
// check joint limit values when automatically inferred based on range
|
||||
TEST_F(LimitedTest, JointLimited) {
|
||||
const std::string xml_path = GetTestDataFilePath(kKeyAutoLimits);
|
||||
mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, nullptr, 0);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
|
||||
// see `user/testdata/auto_limits.xml` for expected values
|
||||
for (int i=0; i < model->njnt; i++) {
|
||||
EXPECT_EQ(model->jnt_limited[i], (mjtByte)model->jnt_user[i]);
|
||||
}
|
||||
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -127,7 +127,164 @@ TEST_F(XMLWriterTest, DropsInertialIfFromGeom) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsActlimited) {
|
||||
TEST_F(XMLWriterTest, DoesNotKeepInferredJointLimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler angle="radian"/>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge" range="-1 1"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepExplicitJointLimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler angle="radian"/>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge" limited="true" range="-1 1"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsJointLimitedFalse) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler angle="radian"/>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge" limited="false" range="-1 1"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("limited=\"false\" range=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepInferredTendonLimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler angle="radian"/>
|
||||
<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>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepExplicitTendonLimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler angle="radian"/>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint type="slide"/>
|
||||
<geom size="1"/>
|
||||
<site name="s1"/>
|
||||
</body>
|
||||
<site name="s2"/>
|
||||
</worldbody>
|
||||
<tendon>
|
||||
<spatial limited="true" range="-1 1">
|
||||
<site site="s1"/>
|
||||
<site site="s2"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsTendonLimitedFalse) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler angle="radian"/>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint type="slide"/>
|
||||
<geom size="1"/>
|
||||
<site name="s1"/>
|
||||
</body>
|
||||
<site name="s2"/>
|
||||
</worldbody>
|
||||
<tendon>
|
||||
<spatial limited="false" range="-1 1">
|
||||
<site site="s1"/>
|
||||
<site site="s2"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("limited=\"false\" range=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepInferredActlimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general dyntype="filter" joint="hinge" actrange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("actlimited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepExplicitActlimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -143,7 +300,152 @@ TEST_F(XMLWriterTest, KeepsActlimited) {
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actlimited=\"true\" actrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("actlimited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsActlimitedFalse) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general dyntype="filter" joint="hinge" actlimited="false" actrange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actlimited=\"false\" actrange=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepInferredCtrllimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="hinge" ctrlrange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("ctrlrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("ctrllimited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepExplicitCtrllimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="hinge" ctrllimited="true" ctrlrange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("ctrlrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("ctrllimited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsCtrllimitedFalse) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="hinge" ctrllimited="false" ctrlrange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("ctrllimited=\"false\" ctrlrange=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepInferredForcelimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="hinge" forcerange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("forcelimited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, DoesNotKeepExplicitForcelimited) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="hinge" forcelimited="true" forcerange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("forcelimited=\"true\"")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsForcelimitedFalse) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body>
|
||||
<joint name="hinge"/>
|
||||
<geom size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
<general joint="hinge" forcelimited="false" forcerange="-1 1"/>
|
||||
</actuator>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("forcelimited=\"false\" forcerange=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user