diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 81bb0c3d..497bb654 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -2129,12 +2129,6 @@ from its default. When this flag is enabled, :ref:`mj_inverse` will interpret ``qacc`` as having been computed from the difference of two sequential velocities, and undo the above modification. -.. _option-flag-sensornoise: - -:at:`sensornoise`: :at-val:`[disable, enable], "disable"` - This flag enables the simulation of sensor noise. When disabled (which is the default) noise is not added to - sensordata, even if the sensors specify non-zero noise amplitudes. When enabled, zero-mean Gaussian noise is added to - the underlying deterministic sensor data. Its standard deviation is determined by the noise parameter of each sensor. .. _option-flag-multiccd: diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst index 3327930d..5b516790 100644 --- a/doc/XMLschema.rst +++ b/doc/XMLschema.rst @@ -247,9 +247,9 @@ | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`sensor` | :ref:`midphase` | :ref:`eulerdamp` | :ref:`override` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`energy` | :ref:`fwdinv` | :ref:`invdiscrete` | :ref:`sensornoise` | | +| | | | :ref:`energy` | :ref:`fwdinv` | :ref:`invdiscrete` | :ref:`multiccd` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`multiccd` | :ref:`island` | | | | +| | | | :ref:`island` | | | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | mujoco |br| |L| | | .. table:: | diff --git a/doc/changelog.rst b/doc/changelog.rst index 027d3221..8e773523 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -7,29 +7,45 @@ Upcoming version (not yet released) General ^^^^^^^ -1. Added the :ref:`actuatorgravcomp` joint attribute. When enabled, gravity compensation +.. admonition:: Breaking API changes + :class: attention + + 1. Removed the ability to natively add noise to sensors. Note that the ``mjModel.sensor_noise`` field and + :ref:`corresponding attribute` are kept and now function as a convenient location for the user to save + standard-deviation information for their own use. This feature was removed because: + + - There was no mechanism to seed the randon noise generator. + - It was not thread-safe, even if seeding would have been provided, sampling on multiple threads would lead to + non-reproducible results. + - This feature was seen as overreach by the engine. Adding noise should be the user's responsibility. + - We are not aware of anyone who was actually using the feature. + + **Migration:** Add noise to sensor values yourself. + +2. Added the :ref:`actuatorgravcomp` joint attribute. When enabled, gravity compensation forces on the joint are treated as applied by actuators. See attribute documentation for more details. The example model `refsite.xml `__, which demostrates Cartesian actuation of an arm, has been updated to use this attribute. -2. Added support for gmsh format 2.2, as generated by e.g. `fTetwild `__. +3. Added support for gmsh format 2.2, as generated by e.g. `fTetwild `__. + MJX ^^^ -3. Improved performance of SAT for convex collisions. -4. Fixed bug for sphere/capsule-convex deep penetration. -5. Fixed bug where ``mjx.Data`` produced by ``mjx.put_data`` had different treedef than ``mjx.make_data``. -6. Throw an error for margin/gap for convex mesh collisions, since they are not supported. +4. Improved performance of SAT for convex collisions. +5. Fixed bug for sphere/capsule-convex deep penetration. +6. Fixed bug where ``mjx.Data`` produced by ``mjx.put_data`` had different treedef than ``mjx.make_data``. +7. Throw an error for margin/gap for convex mesh collisions, since they are not supported. Simulate ^^^^^^^^ -7. Fixed bug in order of enable flag strings. Before this change, using the simulate UI to toggle the - :ref:`invdiscrete` or :ref:`sensornoise` - flags would actually toggle the other flag. +8. Fixed bug in order of enable flag strings. Before this change, using the simulate UI to toggle the + :ref:`invdiscrete` or the (now removed) ``sensornoise`` flags would actually toggle the + other flag. Python bindings ^^^^^^^^^^^^^^^ -8. Added the ``mujoco.minimize`` Python module for nonlinear least-squares, designed for System Identification (sysID). +9. Added the ``mujoco.minimize`` Python module for nonlinear least-squares, designed for System Identification (sysID). The sysID tutorial is work in progress, but a pedagogical colab notebook with examples, including Inverse Kinematics, is available here: |ls_colab| diff --git a/doc/includes/references.h b/doc/includes/references.h index 8bb4ee1a..440b6e7d 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -426,12 +426,11 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags mjENBL_ENERGY = 1<<1, // energy computation mjENBL_FWDINV = 1<<2, // record solver statistics mjENBL_INVDISCRETE = 1<<3, // discrete-time inverse dynamics - mjENBL_SENSORNOISE = 1<<4, // add noise to sensor data // experimental features: - mjENBL_MULTICCD = 1<<5, // multi-point convex collision detection - mjENBL_ISLAND = 1<<6, // constraint island discovery + mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection + mjENBL_ISLAND = 1<<5, // constraint island discovery - mjNENABLE = 7 // number of enable flags + mjNENABLE = 6 // number of enable flags } mjtEnableBit; typedef enum mjtJoint_ { // type of degree of freedom mjJNT_FREE = 0, // global position and orientation (quat) (7) diff --git a/doc/modeling.rst b/doc/modeling.rst index aa6d1693..36edf643 100644 --- a/doc/modeling.rst +++ b/doc/modeling.rst @@ -1057,9 +1057,10 @@ Here we describe the XML attributes common to all sensor types, so as to avoid r :at:`name`: :at-val:`string, optional` Name of the sensor. :at:`noise`: :at-val:`real, "0"` - The standard deviation of zero-mean Gaussian noise added to the sensor output, when the :at:`sensornoise` - attribute of :ref:`flag ` is enabled. Sensor noise respects the sensor data type: - quaternions and unit vectors remain normalized, non-negative quantities remain non-negative. + The standard deviation of the noise model of this sensor. In versions prior to 3.1.4, this would lead to noise being + added to the sensors. In release 3.1.4 this feature was removed, see :doc:`3.1.4 changelog ` for a + detailed justification. As of subsequent versions, this attrbute serves as a convenient location for saving standard + deviation information for later use. :at:`cutoff`: :at-val:`real, "0"` When this value is positive, it limits the absolute value of the sensor output. It is also used to normalize the sensor output in the sensor data plots in :ref:`simulate.cc `. diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index f4c480c6..2f0031e2 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -74,12 +74,11 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags mjENBL_ENERGY = 1<<1, // energy computation mjENBL_FWDINV = 1<<2, // record solver statistics mjENBL_INVDISCRETE = 1<<3, // discrete-time inverse dynamics - mjENBL_SENSORNOISE = 1<<4, // add noise to sensor data // experimental features: - mjENBL_MULTICCD = 1<<5, // multi-point convex collision detection - mjENBL_ISLAND = 1<<6, // constraint island discovery + mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection + mjENBL_ISLAND = 1<<5, // constraint island discovery - mjNENABLE = 7 // number of enable flags + mjNENABLE = 6 // number of enable flags } mjtEnableBit; diff --git a/introspect/enums.py b/introspect/enums.py index a1fab80a..c2efcc25 100644 --- a/introspect/enums.py +++ b/introspect/enums.py @@ -54,10 +54,9 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjENBL_ENERGY', 2), ('mjENBL_FWDINV', 4), ('mjENBL_INVDISCRETE', 8), - ('mjENBL_SENSORNOISE', 16), - ('mjENBL_MULTICCD', 32), - ('mjENBL_ISLAND', 64), - ('mjNENABLE', 7), + ('mjENBL_MULTICCD', 16), + ('mjENBL_ISLAND', 32), + ('mjNENABLE', 6), ]), )), ('mjtJoint', diff --git a/introspect/enums_test.py b/introspect/enums_test.py index 3580d4c1..fe1981a9 100644 --- a/introspect/enums_test.py +++ b/introspect/enums_test.py @@ -42,10 +42,9 @@ class EnumsTest(absltest.TestCase): ('mjENBL_ENERGY', 1<<1), ('mjENBL_FWDINV', 1<<2), ('mjENBL_INVDISCRETE', 1<<3), - ('mjENBL_SENSORNOISE', 1<<4), - ('mjENBL_MULTICCD', 1<<5), - ('mjENBL_ISLAND', 1<<6), - ('mjNENABLE', 7))) + ('mjENBL_MULTICCD', 1<<4), + ('mjENBL_ISLAND', 1<<5), + ('mjNENABLE', 6))) # values mostly increment by one with occasional overrides def test_mjtGeom(self): # pylint: disable=invalid-name diff --git a/python/mujoco/bindings_test.py b/python/mujoco/bindings_test.py index 4a1da339..ea2e8f7c 100644 --- a/python/mujoco/bindings_test.py +++ b/python/mujoco/bindings_test.py @@ -623,7 +623,7 @@ class MuJoCoBindingsTest(parameterized.TestCase): self.assertEqual(struct, struct2) self.assertNotEqual(struct, 3) - self.assertNotEqual(struct, None) + self.assertIsNotNone(struct) # mutable structs shouldn't declare __hash__ with self.assertRaises(TypeError): @@ -705,7 +705,8 @@ class MuJoCoBindingsTest(parameterized.TestCase): # Check that the output argument must have the correct dtype. with self.assertRaises(TypeError): - mujoco.mju_rotVecQuat(vec, quat, res=np.zeros(3, int)) + res = np.zeros(3, np.int32) + mujoco.mju_rotVecQuat(res, vec, quat) def test_getsetstate(self): # pylint: disable=invalid-name mujoco.mj_step(self.model, self.data) @@ -850,8 +851,7 @@ Euler integrator, semi-implicit in velocity. self.assertEqual(mujoco.mjtEnableBit.mjENBL_OVERRIDE, 1<<0) self.assertEqual(mujoco.mjtEnableBit.mjENBL_ENERGY, 1<<1) self.assertEqual(mujoco.mjtEnableBit.mjENBL_FWDINV, 1<<2) - self.assertEqual(mujoco.mjtEnableBit.mjENBL_SENSORNOISE, 1<<4) - self.assertEqual(mujoco.mjtEnableBit.mjNENABLE, 7) + self.assertEqual(mujoco.mjtEnableBit.mjNENABLE, 6) self.assertEqual(mujoco.mjtGeom.mjGEOM_PLANE, 0) self.assertEqual(mujoco.mjtGeom.mjGEOM_HFIELD, 1) self.assertEqual(mujoco.mjtGeom.mjGEOM_SPHERE, 2) diff --git a/src/engine/engine_sensor.c b/src/engine/engine_sensor.c index 0e7be718..e1758260 100644 --- a/src/engine/engine_sensor.c +++ b/src/engine/engine_sensor.c @@ -32,78 +32,9 @@ #include "engine/engine_util_spatial.h" + //-------------------------------- utility --------------------------------------------------------- -// add sensor noise after each stage -static void add_noise(const mjModel* m, mjData* d, mjtStage stage) { - int adr, dim; - mjtNum rnd[4], noise, quat[4], res[4]; - - // process sensors matching stage and having positive noise - for (int i=0; i < m->nsensor; i++) { - if (m->sensor_needstage[i] == stage && m->sensor_noise[i] > 0) { - // get sensor info - adr = m->sensor_adr[i]; - dim = m->sensor_dim[i]; - noise = m->sensor_noise[i]; - - // real or positive: add noise directly, with clamp for positive - if (m->sensor_datatype[i] == mjDATATYPE_REAL || - m->sensor_datatype[i] == mjDATATYPE_POSITIVE) { - for (int j=0; j < dim; j++) { - // get random numbers; use only the first one - rnd[0] = mju_standardNormal(rnd+1); - - // positive - if (m->sensor_datatype[i] == mjDATATYPE_POSITIVE) { - // add noise only if positive, keep it positive - if (d->sensordata[adr+j] > 0) { - d->sensordata[adr+j] = mju_max(0, d->sensordata[adr+j]+rnd[0]*noise); - } - } - - // real - else { - d->sensordata[adr+j] += rnd[0]*noise; - } - } - } - - // axis or quat: rotate around random axis by random angle - else { - // get four random numbers - rnd[0] = mju_standardNormal(rnd+1); - rnd[2] = mju_standardNormal(rnd+3); - - // scale angle, normalize axis, make quaternion - rnd[0] *= noise; - mju_normalize3(rnd+1); - mju_axisAngle2Quat(quat, rnd+1, rnd[0]); - - // axis - if (m->sensor_datatype[i] == mjDATATYPE_AXIS) { - // apply quaternion rotation to axis, assign - mju_rotVecQuat(res, d->sensordata+adr, quat); - mju_copy3(d->sensordata+adr, res); - } - - // quaternion - else if (m->sensor_datatype[i] == mjDATATYPE_QUATERNION) { - // apply quaternion rotation to quaternion, assign - mju_mulQuat(d->sensordata+adr, d->sensordata+adr, quat); - } - - // unknown datatype - else { - mjERROR("unknown datatype in sensor %d", i); - } - } - } - } -} - - - // apply cutoff after each stage static void apply_cutoff(const mjModel* m, mjData* d, mjtStage stage) { // process sensors matching stage and having positive cutoff @@ -442,11 +373,6 @@ void mj_sensorPos(const mjModel* m, mjData* d) { mjcb_sensor(m, d, mjSTAGE_POS); } - // add noise if enabled - if (mjENABLED(mjENBL_SENSORNOISE)) { - add_noise(m, d, mjSTAGE_POS); - } - // compute plugin sensor values if (m->nplugin) { const int nslot = mjp_pluginCount(); @@ -623,11 +549,6 @@ void mj_sensorVel(const mjModel* m, mjData* d) { mjcb_sensor(m, d, mjSTAGE_VEL); } - // add noise if enabled - if (mjENABLED(mjENBL_SENSORNOISE)) { - add_noise(m, d, mjSTAGE_VEL); - } - // trigger computation of plugins if (m->nplugin) { const int nslot = mjp_pluginCount(); @@ -839,11 +760,6 @@ void mj_sensorAcc(const mjModel* m, mjData* d) { mjcb_sensor(m, d, mjSTAGE_ACC); } - // add noise if enabled - if (mjENABLED(mjENBL_SENSORNOISE)) { - add_noise(m, d, mjSTAGE_ACC); - } - // trigger computation of plugins if (m->nplugin) { const int nslot = mjp_pluginCount(); diff --git a/src/engine/engine_support.c b/src/engine/engine_support.c index 5deb1e72..1c3b928f 100644 --- a/src/engine/engine_support.c +++ b/src/engine/engine_support.c @@ -67,7 +67,6 @@ const char* mjENABLESTRING[mjNENABLE] = { "Energy", "Fwdinv", "InvDiscrete", - "Sensornoise", "MultiCCD", "Island" }; diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 11811c2b..55e02d03 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -101,10 +101,10 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { "solver", "iterations", "ls_iterations", "noslip_iterations", "mpr_iterations", "sdf_iterations", "sdf_initpoints", "actuatorgroupdisable"}, {"<"}, - {"flag", "?", "22", "constraint", "equality", "frictionloss", "limit", "contact", + {"flag", "?", "21", "constraint", "equality", "frictionloss", "limit", "contact", "passive", "gravity", "clampctrl", "warmstart", "filterparent", "actuation", "refsafe", "sensor", "midphase", "eulerdamp", - "override", "energy", "fwdinv", "invdiscrete", "sensornoise", "multiccd", "island"}, + "override", "energy", "fwdinv", "invdiscrete", "multiccd", "island"}, {">"}, {"size", "*", "14", "memory", "njmax", "nconmax", "nstack", "nuserdata", "nkey", @@ -1091,7 +1091,6 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) { READENBL("energy", mjENBL_ENERGY) READENBL("fwdinv", mjENBL_FWDINV) READENBL("invdiscrete", mjENBL_INVDISCRETE) - READENBL("sensornoise", mjENBL_SENSORNOISE) READENBL("multiccd", mjENBL_MULTICCD) READENBL("island", mjENBL_ISLAND) #undef READENBL diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 9f6d14f0..c70ee22b 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -938,7 +938,6 @@ void mjXWriter::Option(XMLElement* root) { WRITEENBL("energy", mjENBL_ENERGY) WRITEENBL("fwdinv", mjENBL_FWDINV) WRITEENBL("invdiscrete", mjENBL_INVDISCRETE) - WRITEENBL("sensornoise", mjENBL_SENSORNOISE) WRITEENBL("multiccd", mjENBL_MULTICCD) WRITEENBL("island", mjENBL_ISLAND) #undef WRITEENBL diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 76a72196..4f7b7698 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -164,10 +164,9 @@ public enum mjtEnableBit : int{ mjENBL_ENERGY = 2, mjENBL_FWDINV = 4, mjENBL_INVDISCRETE = 8, - mjENBL_SENSORNOISE = 16, - mjENBL_MULTICCD = 32, - mjENBL_ISLAND = 64, - mjNENABLE = 7, + mjENBL_MULTICCD = 16, + mjENBL_ISLAND = 32, + mjNENABLE = 6, } public enum mjtJoint : int{ mjJNT_FREE = 0,