Sensors of type user no longer require objtype and objname. If unspecified, the objtype will be mjOBJ_UNKNOWN. Also, `user sensors datatype default is now "real"`.
PiperOrigin-RevId: 489652972 Change-Id: I0a8f21ee1a2f5292e6bbcfe3785536cfd1498b85
This commit is contained in:
committed by
Copybara-Service
parent
6f2092bef2
commit
221c63d744
@@ -3913,7 +3913,7 @@ at a specified body, in global coordinates.
|
||||
.. _sensor-clock:
|
||||
|
||||
:el-prefix:`sensor/` **clock** (*)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This element creates sensor that returns the simulation time.
|
||||
|
||||
@@ -3934,12 +3934,12 @@ bodies whose center of mass is of interest.
|
||||
|
||||
:at:`name`, :at:`noise`, :at:`cutoff`, :at:`user`
|
||||
See :ref:`CSensor`.
|
||||
:at:`objtype`: :at-val:`(any element type that can be named), required`
|
||||
:at:`objtype`: :at-val:`(any element type that can be named), optional`
|
||||
Type of the MuJoCo object to which the sensor is attached. This together with the objname attribute determines the
|
||||
actual object.
|
||||
:at:`objname`: :at-val:`string, required`
|
||||
actual object. If unspecified, will be :ref:`mjOBJ_UNKNOWN<mjtObj>`.
|
||||
:at:`objname`: :at-val:`string, optional`
|
||||
Name of the MuJoCo object to which the sensor is attached.
|
||||
:at:`datatype`: :at-val:`[real, positive, axis, quaternion], required`
|
||||
:at:`datatype`: :at-val:`[real, positive, axis, quaternion], "real"`
|
||||
The type of output generated by this sensor. "axis" means a unit-length 3D vector. "quat" means a unit quaternion.
|
||||
These need to be declared because when MuJoCo adds noise, it must respect the vector normalization. "real" means a
|
||||
generic array (or scalar) of real values to which noise can be added independently.
|
||||
@@ -3952,7 +3952,7 @@ bodies whose center of mass is of interest.
|
||||
.. _keyframe:
|
||||
|
||||
**keyframe** (*)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
This is a grouping element for keyframe definitions. It does not have attributes. Keyframes can be used to create a
|
||||
library of states that are of interest to the user, and to initialize the simulation state to one of the states in the
|
||||
|
||||
@@ -54,6 +54,8 @@ General
|
||||
``dlopen`` on a POSIX system, and to ``LoadLibraryA`` on Windows. The second function scans a specified directory for
|
||||
all dynamic libraries file and loads each library found. Dynamic libraries opened by these functions are assumed to
|
||||
register one or more MuJoCo plugins on load.
|
||||
- Sensors of type :ref:`user<sensor-user>` no longer require :at:`objtype` and :at:`objname`. If unspecified, the
|
||||
objtype will be :ref:`mjOBJ_UNKNOWN<mjtObj>`. ``user`` sensors :at:`datatype` default is now :at-val:`"real"`.
|
||||
|
||||
Simulate
|
||||
^^^^^^^^
|
||||
|
||||
@@ -3697,7 +3697,7 @@ void mjCSensor::Compile(void) {
|
||||
|
||||
// get sensorized object id
|
||||
objid = pobj->id;
|
||||
} else if (type != mjSENS_CLOCK && type != mjSENS_PLUGIN) {
|
||||
} else if (type != mjSENS_CLOCK && type != mjSENS_PLUGIN && type != mjSENS_USER) {
|
||||
throw mjCError(this, "invalid type in sensor '%s' (id = %d)", name.c_str(), id);
|
||||
}
|
||||
|
||||
@@ -3779,7 +3779,7 @@ void mjCSensor::Compile(void) {
|
||||
"joint must be slide or hinge in sensor '%s' (id = %d)", name.c_str(), id);
|
||||
}
|
||||
|
||||
//set
|
||||
// set
|
||||
dim = 1;
|
||||
datatype = mjDATATYPE_REAL;
|
||||
if (type==mjSENS_JOINTPOS) {
|
||||
|
||||
@@ -3147,16 +3147,23 @@ void mjXReader::Sensor(XMLElement* section) {
|
||||
// user-defined sensor
|
||||
else if (type=="user") {
|
||||
psen->type = mjSENS_USER;
|
||||
ReadAttrTxt(elem, "objtype", text, true);
|
||||
psen->objtype = (mjtObj)mju_str2Type(text.c_str());
|
||||
ReadAttrTxt(elem, "objname", psen->objname, true);
|
||||
bool objname_given = ReadAttrTxt(elem, "objname", psen->objname);
|
||||
if (ReadAttrTxt(elem, "objtype", text)) {
|
||||
if (!objname_given) {
|
||||
throw mjXError(elem, "objtype '%s' given but objname is missing", text.c_str());
|
||||
}
|
||||
psen->objtype = (mjtObj)mju_str2Type(text.c_str());
|
||||
} else if (objname_given) {
|
||||
throw mjXError(elem, "objname '%s' given but objtype is missing", psen->objname.c_str());
|
||||
}
|
||||
ReadAttrInt(elem, "dim", &psen->dim, true);
|
||||
|
||||
// keywords
|
||||
MapValue(elem, "needstage", &n, stage_map, stage_sz, true);
|
||||
psen->needstage = (mjtStage)n;
|
||||
MapValue(elem, "datatype", &n, datatype_map, datatype_sz, true);
|
||||
psen->datatype = (mjtDataType)n;
|
||||
if (MapValue(elem, "datatype", &n, datatype_map, datatype_sz)) {
|
||||
psen->datatype = (mjtDataType)n;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type=="plugin") {
|
||||
|
||||
@@ -1768,7 +1768,7 @@ void mjXWriter::Sensor(XMLElement* root) {
|
||||
// user-defined sensor
|
||||
case mjSENS_USER:
|
||||
elem = InsertEnd(section, "user");
|
||||
WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||
if (mju_type2Str(psen->objtype)) WriteAttrTxt(elem, "objtype", mju_type2Str(psen->objtype));
|
||||
WriteAttrTxt(elem, "objname", psen->objname);
|
||||
WriteAttrInt(elem, "dim", psen->dim);
|
||||
WriteAttrKey(elem, "needstage", stage_map, stage_sz, (int)psen->needstage);
|
||||
|
||||
@@ -202,6 +202,29 @@ TEST_F(RelativeFrameSensorParsingTest, BadRefType) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("reference frame object must be"));
|
||||
}
|
||||
|
||||
// ------------- sensor compilation --------------------------------------------
|
||||
|
||||
using SensorTest = MujocoTest;
|
||||
TEST_F(SensorTest, OjbtypeParsedButNotRequired) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<sensor>
|
||||
<user dim="3" needstage="vel" datatype="axis"/>
|
||||
<user dim="2" needstage="pos" objtype="body" objname="world"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, 0, 0);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->sensor_datatype[0], mjDATATYPE_AXIS);
|
||||
EXPECT_EQ(model->sensor_objtype[0], mjOBJ_UNKNOWN);
|
||||
EXPECT_EQ(model->sensor_dim[0], 3);
|
||||
EXPECT_EQ(model->sensor_datatype[1], mjDATATYPE_REAL);
|
||||
EXPECT_EQ(model->sensor_objtype[1], mjOBJ_BODY);
|
||||
EXPECT_EQ(model->sensor_objid[1], 0);
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
// ------------- test capsule inertias -----------------------------------------
|
||||
|
||||
static const char* const kCapsuleInertiaPath =
|
||||
|
||||
@@ -935,6 +935,38 @@ TEST_F(ActuatorParseTest, ActdimDefaultsPropagate) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
// ------------- test sensor parsing -------------------------------------------
|
||||
|
||||
using SensorParseTest = MujocoTest;
|
||||
|
||||
TEST_F(SensorParseTest, UserObjTypeNoName) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<sensor>
|
||||
<user dim="1" needstage="vel" objtype="site"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("objtype 'site' given but"));
|
||||
}
|
||||
|
||||
TEST_F(SensorParseTest, UserObjNameNoType) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<sensor>
|
||||
<user dim="1" needstage="vel" objname="kevin"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("objname 'kevin' given but"));
|
||||
}
|
||||
|
||||
// ------------- test general parsing ------------------------------------------
|
||||
|
||||
TEST_F(XMLReaderTest, ZnearZeroNotAllowed) {
|
||||
|
||||
@@ -129,6 +129,24 @@ TEST_F(XMLWriterTest, SavesDisableSensor) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, EmptyUserSensor) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<sensor>
|
||||
<user dim="2" needstage="vel"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("dim=\"2\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("needstage=\"vel\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("datatype=\"real\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("objtype")));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("objname")));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, KeepsEmptyClasses) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
|
||||
Reference in New Issue
Block a user