diff --git a/doc/APIreference/APIglobals.rst b/doc/APIreference/APIglobals.rst
index 5f6d824f..e237d907 100644
--- a/doc/APIreference/APIglobals.rst
+++ b/doc/APIreference/APIglobals.rst
@@ -444,6 +444,10 @@ shown in the table below. Their names are in the format ``mjKEY_XXX``. They corr
- 5
- The maximal number of real-valued parameters used to define the impedance of each scalar constraint.
Determines the size of all ``mjModel.XXX_solimp`` fields.
+ * - ``mjNSENS``
+ - 2
+ - The number of sensor parameters.
+ Determines the size of ``mjModel.sensor_intprm``.
* - ``mjNSOLVER``
- 200
- The number of iterations where solver statistics can be stored in ``mjData.solver``. This array is used
diff --git a/doc/includes/references.h b/doc/includes/references.h
index 3fc2d13d..894cf25b 100644
--- a/doc/includes/references.h
+++ b/doc/includes/references.h
@@ -1458,6 +1458,7 @@ struct mjModel_ {
int* sensor_objid; // id of sensorized object (nsensor x 1)
int* sensor_reftype; // type of reference frame (mjtObj) (nsensor x 1)
int* sensor_refid; // id of reference frame; -1: global frame (nsensor x 1)
+ int* sensor_intprm; // sensor parameters (nsensor x mjNSENS)
int* sensor_dim; // number of scalar outputs (nsensor x 1)
int* sensor_adr; // address in sensor array (nsensor x 1)
mjtNum* sensor_cutoff; // cutoff for real and positive; 0: ignore (nsensor x 1)
@@ -2324,6 +2325,7 @@ typedef struct mjsSensor_ { // sensor specification
mjString* objname; // name of sensorized object
mjtObj reftype; // type of referenced object
mjString* refname; // name of referenced object
+ int intprm[mjNSENS]; // integer parameters
// user-defined sensors
mjtDataType datatype; // data type for sensor measurement
diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h
index f7b94b90..c92edd75 100644
--- a/include/mujoco/mjmodel.h
+++ b/include/mujoco/mjmodel.h
@@ -41,6 +41,7 @@
#define mjNFLUID 12 // number of fluid interaction parameters
#define mjNREF 2 // number of solver reference parameters
#define mjNIMP 5 // number of solver impedance parameters
+#define mjNSENS 2 // number of sensor parameters
#define mjNSOLVER 200 // size of one mjData.solver array
#define mjNISLAND 20 // number of mjData.solver arrays
@@ -1130,6 +1131,7 @@ struct mjModel_ {
int* sensor_objid; // id of sensorized object (nsensor x 1)
int* sensor_reftype; // type of reference frame (mjtObj) (nsensor x 1)
int* sensor_refid; // id of reference frame; -1: global frame (nsensor x 1)
+ int* sensor_intprm; // sensor parameters (nsensor x mjNSENS)
int* sensor_dim; // number of scalar outputs (nsensor x 1)
int* sensor_adr; // address in sensor array (nsensor x 1)
mjtNum* sensor_cutoff; // cutoff for real and positive; 0: ignore (nsensor x 1)
diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h
index 2d98c1bd..138b1937 100644
--- a/include/mujoco/mjspec.h
+++ b/include/mujoco/mjspec.h
@@ -692,6 +692,7 @@ typedef struct mjsSensor_ { // sensor specification
mjString* objname; // name of sensorized object
mjtObj reftype; // type of referenced object
mjString* refname; // name of referenced object
+ int intprm[mjNSENS]; // integer parameters
// user-defined sensors
mjtDataType datatype; // data type for sensor measurement
diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h
index 3da0df54..68032375 100644
--- a/include/mujoco/mjxmacro.h
+++ b/include/mujoco/mjxmacro.h
@@ -540,6 +540,7 @@
X ( int, sensor_objid, nsensor, 1 ) \
X ( int, sensor_reftype, nsensor, 1 ) \
X ( int, sensor_refid, nsensor, 1 ) \
+ X ( int, sensor_intprm, nsensor, mjNSENS ) \
X ( int, sensor_dim, nsensor, 1 ) \
X ( int, sensor_adr, nsensor, 1 ) \
X ( mjtNum, sensor_cutoff, nsensor, 1 ) \
diff --git a/python/mujoco/introspect/structs.py b/python/mujoco/introspect/structs.py
index bde6a25d..a969052b 100644
--- a/python/mujoco/introspect/structs.py
+++ b/python/mujoco/introspect/structs.py
@@ -4149,6 +4149,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='id of reference frame; -1: global frame',
array_extent=('nsensor',),
),
+ StructFieldDecl(
+ name='sensor_intprm',
+ type=PointerType(
+ inner_type=ValueType(name='int'),
+ ),
+ doc='sensor parameters',
+ array_extent=('nsensor', 'mjNSENS'),
+ ),
StructFieldDecl(
name='sensor_dim',
type=PointerType(
@@ -10388,6 +10396,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
doc='name of referenced object',
),
+ StructFieldDecl(
+ name='intprm',
+ type=ArrayType(
+ inner_type=ValueType(name='int'),
+ extents=(2,),
+ ),
+ doc='integer parameters',
+ ),
StructFieldDecl(
name='datatype',
type=ValueType(name='mjtDataType'),
diff --git a/src/user/user_model.cc b/src/user/user_model.cc
index a1b358ac..5a9ae0ba 100644
--- a/src/user/user_model.cc
+++ b/src/user/user_model.cc
@@ -3454,6 +3454,7 @@ void mjCModel::CopyObjects(mjModel* m) {
m->sensor_objid[i] = psen->obj ? psen->obj->id : -1;
m->sensor_reftype[i] = psen->reftype;
m->sensor_refid[i] = psen->ref ? psen->ref->id : -1;
+ mjuu_copyvec(m->sensor_intprm+i*mjNSENS, psen->intprm, mjNSENS);
m->sensor_dim[i] = psen->dim;
m->sensor_cutoff[i] = (mjtNum)psen->cutoff;
m->sensor_noise[i] = (mjtNum)psen->noise;
diff --git a/test/engine/engine_sensor_test.cc b/test/engine/engine_sensor_test.cc
index b81feb66..b6c8faa1 100644
--- a/test/engine/engine_sensor_test.cc
+++ b/test/engine/engine_sensor_test.cc
@@ -544,7 +544,37 @@ TEST_F(SensorTest, Clock) {
mj_deleteModel(model);
}
-// test clock sensor
+// test that integer parameters pass through
+TEST_F(SensorTest, IntPrm) {
+ constexpr char xml[] = R"(
+
+
+
+
+
+ )";
+ ASSERT_EQ(mjNSENS, 2);
+
+ char err[1024];
+ mjSpec* spec = mj_parseXMLString(xml, 0, err, sizeof(err));
+ ASSERT_THAT(spec, NotNull()) << err;
+
+ mjModel* model = mj_compile(spec, nullptr);
+ EXPECT_EQ(model->sensor_intprm[0], 0);
+ EXPECT_EQ(model->sensor_intprm[1], 0);
+ mj_deleteModel(model);
+
+ mjsSensor* s = mjs_asSensor(mjs_findElement(spec, mjOBJ_SENSOR, "dummy"));
+ s->intprm[0] = 3;
+ s->intprm[1] = 4;
+ model = mj_compile(spec, nullptr);
+ EXPECT_EQ(model->sensor_intprm[0], 3);
+ EXPECT_EQ(model->sensor_intprm[1], 4);
+ mj_deleteModel(model);
+ mj_deleteSpec(spec);
+}
+
+// test sequential collision sensors
TEST_F(SensorTest, CollisionSequential) {
constexpr char xml[] = R"(
diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs
index ba5f8b23..2c540d15 100644
--- a/unity/Runtime/Bindings/MjBindings.cs
+++ b/unity/Runtime/Bindings/MjBindings.cs
@@ -49,6 +49,7 @@ public const int mjNBIAS = 10;
public const int mjNFLUID = 12;
public const int mjNREF = 2;
public const int mjNIMP = 5;
+public const int mjNSENS = 2;
public const int mjNSOLVER = 200;
public const int mjNISLAND = 20;
public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJPLUGIN_H_ = true;
@@ -5683,6 +5684,7 @@ public unsafe struct mjModel_ {
public int* sensor_objid;
public int* sensor_reftype;
public int* sensor_refid;
+ public int* sensor_intprm;
public int* sensor_dim;
public int* sensor_adr;
public double* sensor_cutoff;