diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 85b889df..87a6d928 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -8514,15 +8514,14 @@ Extraction :width: 30% :target: https://github.com/google-deepmind/mujoco/blob/main/model/tactile/tactile.xml -The tactile sensor returns the penetration pressure and the sliding velocities in the tangent frame at given points -between the geom associated with the sensor and the SDF geoms in contact with it. We define the penetration pressure as -a function of the penetration depth :math:`p(d) = \frac{d}{d_{max}-d}`, which is zero at the surface and goes to -infinity as the maximum depth is reached. The sensor is associated with a geom and a mesh. It is activated by the -contact between its associated geom and other geoms. The vertices of the mesh, when positioned in the geom frame, are -the points at which sensor values are computed, so the dimension of the output is 3 times the number of vertices in the -mesh. The mesh must have 3 normal vectors per vertex, which are used to compute the tangent frame. If the penetration -depth is positive (no contact), then all values are 0 for the corresponding vertex. Only contacts with geoms of type SDF -contribute to the sensor output. The sensor can be visualized by enabling the visualization of contact points. +The tactile sensor returns the maximum penetration depth and the sliding velocities in the tangent frame at given points +between the geom associated with the sensor and the SDF geoms in contact with it. The sensor is associated with a geom +and a mesh. It is activated by the contact between its associated geom and other geoms. The vertices of the mesh, when +positioned in the geom frame, are the points at which sensor values are computed, so the dimension of the output is 3 +times the number of vertices in the mesh. The mesh must have 3 normal vectors per vertex, which are used to compute the +tangent frame. If the penetration depth is positive (no contact), then all values are 0 for the corresponding vertex. +Only contacts with geoms of type SDF contribute to the sensor output. The sensor can be visualized by enabling the +visualization of contact points. .. _sensor-tactile-geom: diff --git a/doc/changelog.rst b/doc/changelog.rst index c09fe3dd..dfbd187e 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -79,6 +79,8 @@ General `mjtype.h ` and now includes all enum type definitions. + - The :ref:`tactile` sensor now reports raw depth instead of an estimated pressure. + - MJX: Removed the deprecated ``nconmax`` argument from ``mjx.make_data`` and ``mjx.put_data`` in favor of ``naconmax``. diff --git a/mjx/mujoco/mjx/third_party/mujoco_warp/_src/sensor.py b/mjx/mujoco/mjx/third_party/mujoco_warp/_src/sensor.py index 0610612b..472fa617 100644 --- a/mjx/mujoco/mjx/third_party/mujoco_warp/_src/sensor.py +++ b/mjx/mujoco/mjx/third_party/mujoco_warp/_src/sensor.py @@ -2252,18 +2252,14 @@ def _sensor_tactile( ) vel_rel = vel_sensor - vel_other - kMaxDepth = 0.05 - pressure = depth / wp.max(kMaxDepth - depth, MJ_MINVAL) - force = wp.mul(normal, pressure) - forceT = wp.vec3(0.0, 0.0, 0.0) - forceT[0] = wp.dot(force, normal) + forceT[0] = -depth if has_frame: forceT[1] = wp.abs(wp.dot(vel_rel, tang1)) forceT[2] = wp.abs(wp.dot(vel_rel, tang2)) dim = sensor_dim[sensor_id] // 3 - wp.atomic_add(sensordata_out, worldid, sensor_adr[sensor_id] + 0 * dim + vertid, forceT[0]) + wp.atomic_max(sensordata_out, worldid, sensor_adr[sensor_id] + 0 * dim + vertid, forceT[0]) wp.atomic_add(sensordata_out, worldid, sensor_adr[sensor_id] + 1 * dim + vertid, forceT[1]) wp.atomic_add(sensordata_out, worldid, sensor_adr[sensor_id] + 2 * dim + vertid, forceT[2]) diff --git a/src/engine/engine_sensor.c b/src/engine/engine_sensor.c index d6a2eb13..83db1d49 100644 --- a/src/engine/engine_sensor.c +++ b/src/engine/engine_sensor.c @@ -172,14 +172,8 @@ static void* tactile_taxel_batch(void* args) { mesh_normal[normal_stride*j + 2]}; mju_rotVecQuat(normal, normal, m->mesh_quat + 4 * mesh_id); - // get contact force - mjtNum force[3]; - mjtNum kMaxDepth = 0.05; - mjtNum pressure = depth / mju_max(kMaxDepth - depth, mjMINVAL); - mju_scl3(force, normal, pressure); - - // accumulate into forcesT (disjoint writes per taxel j) - t->forcesT[0*ncon + j] += mju_dot3(force, normal); + // take max penetration depth (SDF distance is negative; negate for positive output) + t->forcesT[0*ncon + j] = mju_max(t->forcesT[0*ncon + j], -depth); if (has_frame) { mjtNum tang1[3] = {mesh_normal[normal_stride*j + 3], diff --git a/test/engine/engine_sensor_test.cc b/test/engine/engine_sensor_test.cc index a12fdec0..a71c5061 100644 --- a/test/engine/engine_sensor_test.cc +++ b/test/engine/engine_sensor_test.cc @@ -1702,7 +1702,7 @@ TEST_F(SensorTest, TactileSkipTangents) { EXPECT_EQ(data->time, 0.0); EXPECT_GT(data->ncon, 0) << "No contacts generated"; - // Tactile sensor layout: [normal_forces..., tang1_forces..., tang2_forces...] + // Tactile sensor layout: [depths..., tang1_vel..., tang2_vel...] int ntaxel = model->nsensordata / 3; ASSERT_EQ(model->nsensordata % 3, 0) << "Sensor dim should be divisible by 3"; @@ -1712,13 +1712,13 @@ TEST_F(SensorTest, TactileSkipTangents) { << "Tangent component at index " << i << " should be 0"; } - // Normal force components: verify count, sign, and magnitude ~-0.8 + // Penetration depth components: verify count, sign, and magnitude ~0.2 int nonzero_count = 0; for (int i = 0; i < ntaxel; i++) { if (data->sensordata[i] != 0) { nonzero_count++; - EXPECT_NEAR(data->sensordata[i], -0.8, 0.1) - << "Normal force at taxel " << i; + EXPECT_NEAR(data->sensordata[i], 0.2, 0.1) + << "Penetration depth at taxel " << i; } } EXPECT_EQ(nonzero_count, 2) << "Expected 2 taxels in contact";