Skip to content

Commit 246bb1e

Browse files
committed
fixup! [FEATURE] Speed up and add noise options for tactile sensors: hysteresis, dead taxels, probe gain
1 parent e656ba8 commit 246bb1e

4 files changed

Lines changed: 26 additions & 99 deletions

File tree

genesis/engine/sensors/kinematic_tactile.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
triangle_face_normal,
2525
)
2626

27-
from .raycaster import ensure_solver_bvhs, update_solver_bvhs
27+
from .raycaster import RaycastContext
2828

2929
from .base_sensor import RigidSensorMetadataMixin, RigidSensorMixin, SimpleSensor, SimpleSensorMetadata
3030
from .probe import (
@@ -914,12 +914,13 @@ def build(self):
914914
# SDF activation is the default fallback; the idempotent call keeps it active even in mixed-mode scenes.
915915
self._shared_metadata.solver.collider.activate_sdf()
916916

917-
# Last-value-wins propagation of contact_depth_query. Build the shared collision BVH lazily on raycast opt-in.
917+
# Last-value-wins propagation of contact_depth_query. In raycast mode, activate the shared RaycastContext so it
918+
# builds its BVH (only on raycast opt-in); SDF-mode scenes never pay for it.
918919
mode = self._options.contact_depth_query
919920
if mode is not None:
920921
self._shared_metadata.contact_depth_query = mode
921922
if self._shared_metadata.contact_depth_query == "raycast":
922-
self._shared_metadata.collision_bvh = ensure_solver_bvhs(self._manager._sim)
923+
self._shared_context.activate()
923924

924925

925926
@dataclass
@@ -939,7 +940,7 @@ class ContactDepthProbeSensor(
939940
KinematicTactileSensorMixin[ContactDepthProbeMetadata],
940941
ProbesWithNormalSensorMixin[ContactDepthProbeMetadata],
941942
RigidSensorMixin[ContactDepthProbeMetadata],
942-
SimpleSensor[ContactDepthProbeOptions, None, ContactDepthProbeMetadata, tuple],
943+
SimpleSensor[ContactDepthProbeOptions, RaycastContext, ContactDepthProbeMetadata, tuple],
943944
):
944945
"""
945946
Returns contact depth in meters per probe.
@@ -965,7 +966,7 @@ def _get_cache_dtype(cls) -> torch.dtype:
965966
@classmethod
966967
def _update_current_timestep_data(
967968
cls,
968-
shared_context: None,
969+
shared_context: RaycastContext,
969970
shared_metadata: ContactDepthProbeMetadata,
970971
current_ground_truth_data_T: torch.Tensor,
971972
ground_truth_data_timeline: "TensorRingBuffer | None",
@@ -1013,8 +1014,7 @@ def _update_current_timestep_data(
10131014
solver.collider._collider_state,
10141015
shared_metadata.sensor_candidate_geom_mask,
10151016
)
1016-
update_solver_bvhs(shared_metadata.collision_bvh)
1017-
rigid_entry = next(e for e in shared_metadata.collision_bvh if e.raycast_mask is None)
1017+
rigid_entry = next(e for e in shared_context.bvh_contexts if e.raycast_mask is None)
10181018
_kernel_contact_depth_probe_bvh(
10191019
shared_metadata.probe_positions,
10201020
shared_metadata.probe_local_normal,
@@ -1059,7 +1059,9 @@ class ContactProbeMetadata(ContactDepthProbeMetadata):
10591059
release_threshold_row: torch.Tensor = make_tensor_field((0,))
10601060

10611061

1062-
class ContactProbeSensor(ContactDepthProbeSensor, SimpleSensor[ContactProbeOptions, None, ContactProbeMetadata, tuple]):
1062+
class ContactProbeSensor(
1063+
ContactDepthProbeSensor, SimpleSensor[ContactProbeOptions, RaycastContext, ContactProbeMetadata, tuple]
1064+
):
10631065
"""
10641066
Returns boolean contact per probe with optional Schmitt-trigger hysteresis. Shares the depth-probe kernel.
10651067
@@ -1321,7 +1323,7 @@ class KinematicTaxelSensor(
13211323
KinematicTactileSensorMixin[KinematicTaxelMetadata],
13221324
ProbesWithNormalSensorMixin[KinematicTaxelMetadata],
13231325
RigidSensorMixin[KinematicTaxelMetadata],
1324-
SimpleSensor[KinematicTaxelOptions, None, KinematicTaxelMetadata, KinematicTaxelReturnType],
1326+
SimpleSensor[KinematicTaxelOptions, RaycastContext, KinematicTaxelMetadata, KinematicTaxelReturnType],
13251327
):
13261328
"""Kinematic taxels: spring-damper force and torque per probe from contact geometry and relative motion."""
13271329

@@ -1409,7 +1411,7 @@ def _get_cache_dtype(cls) -> torch.dtype:
14091411
@classmethod
14101412
def _update_current_timestep_data(
14111413
cls,
1412-
shared_context: None,
1414+
shared_context: RaycastContext,
14131415
shared_metadata: KinematicTaxelMetadata,
14141416
current_ground_truth_data_T: torch.Tensor,
14151417
ground_truth_data_timeline: "TensorRingBuffer | None",
@@ -1472,8 +1474,7 @@ def _update_current_timestep_data(
14721474
solver.collider._collider_state,
14731475
shared_metadata.sensor_candidate_geom_mask,
14741476
)
1475-
update_solver_bvhs(shared_metadata.collision_bvh)
1476-
rigid_entry = next(e for e in shared_metadata.collision_bvh if e.raycast_mask is None)
1477+
rigid_entry = next(e for e in shared_context.bvh_contexts if e.raycast_mask is None)
14771478
_kernel_kinematic_taxel_bvh(
14781479
shared_metadata.probe_positions,
14791480
shared_metadata.probe_local_normal,

genesis/engine/sensors/point_cloud_tactile.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
func_noised_probe_radius,
3030
get_measured_bufs,
3131
)
32-
from .raycaster import ensure_solver_bvhs, update_solver_bvhs
32+
from .raycaster import RaycastContext
3333
from .tactile_shared import (
3434
BVH_LEAF_SIZE,
3535
BVH_STACK_SIZE,
@@ -1754,7 +1754,7 @@ class ElastomerTaxelSensor(
17541754
PointCloudTactileSensorMixin[ElastomerTaxelSensorMetadata],
17551755
ProbesWithNormalSensorMixin[ElastomerTaxelSensorMetadata],
17561756
RigidSensorMixin[ElastomerTaxelSensorMetadata],
1757-
SimpleSensor[ElastomerTaxelSensorOptions, None, ElastomerTaxelSensorMetadata],
1757+
SimpleSensor[ElastomerTaxelSensorOptions, RaycastContext, ElastomerTaxelSensorMetadata],
17581758
):
17591759
def __init__(
17601760
self,
@@ -1796,14 +1796,14 @@ def build(self):
17961796
gs.raise_exception("ElastomerTaxel must be attached to a rigid link with collision geometry.")
17971797

17981798
# Last-value-wins propagation of contact_depth_query. Skip activate_sdf only when this sensor explicitly opts
1799-
# into raycast -- with last-wins, mixed-mode scenes may end up with SDF activated unused (harmless).
1799+
# into raycast - with last-wins, mixed-mode scenes may end up with SDF activated unused (harmless).
18001800
mode = self._options.contact_depth_query
18011801
if mode is not None:
18021802
self._shared_metadata.contact_depth_query = mode
18031803
if mode != "raycast":
18041804
solver.collider.activate_sdf()
18051805
if self._shared_metadata.contact_depth_query == "raycast":
1806-
self._shared_metadata.collision_bvh = ensure_solver_bvhs(self._manager._sim)
1806+
self._shared_context.activate()
18071807

18081808
elastomer_geom_start_row = self._shared_metadata.elastomer_geom_idx.shape[0]
18091809
elastomer_geom_idx, elastomer_geom_active_envs_mask = _collect_collision_geom_idx(
@@ -2021,7 +2021,7 @@ def _apply_transform(
20212021
@classmethod
20222022
def _update_current_timestep_data(
20232023
cls,
2024-
shared_context: None,
2024+
shared_context: RaycastContext,
20252025
shared_metadata: ElastomerTaxelSensorMetadata,
20262026
current_ground_truth_data_T: torch.Tensor,
20272027
ground_truth_data_timeline: "TensorRingBuffer | None",
@@ -2051,8 +2051,7 @@ def _update_current_timestep_data(
20512051
shared_metadata.probe_depth_buf,
20522052
)
20532053
else:
2054-
update_solver_bvhs(shared_metadata.collision_bvh)
2055-
rigid_entry = next(e for e in shared_metadata.collision_bvh if e.raycast_mask is None)
2054+
rigid_entry = next(e for e in shared_context.bvh_contexts if e.raycast_mask is None)
20562055
_kernel_elastomer_probe_depth_bvh(
20572056
shared_metadata.probe_positions,
20582057
shared_metadata.probe_sensor_idx,
@@ -2136,7 +2135,7 @@ def _update_current_timestep_data(
21362135
shared_metadata.surface_candidate_buf,
21372136
)
21382137
else:
2139-
rigid_entry = next(e for e in shared_metadata.collision_bvh if e.raycast_mask is None)
2138+
rigid_entry = next(e for e in shared_context.bvh_contexts if e.raycast_mask is None)
21402139
_kernel_elastomer_surface_state_via_global_bvh(
21412140
shared_metadata.links_idx,
21422141
shared_metadata.sensor_elastomer_geom_start,

genesis/engine/sensors/raycaster.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -215,78 +215,6 @@ def destroy(self):
215215
self._bvh_contexts.clear()
216216

217217

218-
# Per-process cache of collision/visual BVH lists keyed by ``id(sim)``, used by tactile-probe sensors in
219-
# ``contact_depth_query="raycast"`` mode to lazily build (only on raycast opt-in) and share one BVH per simulator.
220-
# Their mode is a runtime option, so the BVH cannot be declared as a static ``RaycastContext``; it is built on demand.
221-
_SIM_BVHS: dict[int, list[BVHContext]] = {}
222-
223-
224-
def ensure_solver_bvhs(sim) -> list[BVHContext]:
225-
"""
226-
Idempotently build the per-(solver, mesh-type) collision/visual BVHs for ``sim`` and return the shared list.
227-
228-
Both ``RaycasterSensor`` and tactile-probe sensors in ``contact_depth_query="raycast"`` mode reuse the same list
229-
so they share the BVH state for a given sim.
230-
"""
231-
sim_key = id(sim)
232-
existing = _SIM_BVHS.get(sim_key)
233-
if existing is not None:
234-
return existing
235-
236-
solver_bvhs: list[BVHContext] = []
237-
for solver in (sim.rigid_solver, sim.kinematic_solver):
238-
if not solver.is_active:
239-
continue
240-
n_envs = solver._B
241-
if isinstance(solver, RigidSolver):
242-
n_faces = solver.faces_info.geom_idx.shape[0]
243-
aabb = AABB(n_batches=n_envs, n_aabbs=n_faces)
244-
bvh = LBVH(aabb, max_n_query_result_per_aabb=0, n_radix_sort_groups=64)
245-
solver_bvhs.append(BVHContext(solver, bvh, aabb, None))
246-
n_vfaces = solver.vfaces_info.vgeom_idx.shape[0]
247-
if n_vfaces > 0:
248-
mask = RaycastContext._compute_visual_raycast_mask(solver)
249-
if mask.any():
250-
aabb = AABB(n_batches=n_envs, n_aabbs=n_vfaces)
251-
bvh = LBVH(aabb, max_n_query_result_per_aabb=0, n_radix_sort_groups=64)
252-
solver_bvhs.append(BVHContext(solver, bvh, aabb, mask))
253-
254-
_SIM_BVHS[sim_key] = solver_bvhs
255-
return solver_bvhs
256-
257-
258-
def update_solver_bvhs(solver_bvhs: list[BVHContext]) -> None:
259-
"""Rebuild ``solver_bvhs`` from current scene state. Cheap dedup is intentionally NOT applied here: callers can
260-
invoke this between scene mutations (e.g. ``set_pos``) without an intervening simulator substep, so any cross-call
261-
deduplication would mask real geometry changes."""
262-
for entry in solver_bvhs:
263-
if entry.raycast_mask is None:
264-
kernel_update_verts_and_aabbs(
265-
geoms_info=entry.solver.geoms_info,
266-
geoms_state=entry.solver.geoms_state,
267-
verts_info=entry.solver.verts_info,
268-
faces_info=entry.solver.faces_info,
269-
free_verts_state=entry.solver.free_verts_state,
270-
fixed_verts_state=entry.solver.fixed_verts_state,
271-
links_info=entry.solver.links_info,
272-
static_rigid_sim_config=entry.solver._static_rigid_sim_config,
273-
aabb_state=entry.aabb,
274-
)
275-
entry.bvh.build()
276-
else:
277-
entry.solver.update_forward_pos()
278-
entry.solver.update_vgeoms()
279-
kernel_update_visual_aabbs(
280-
vverts_info=entry.solver.vverts_info,
281-
vverts_state=entry.solver.vverts_state,
282-
vfaces_info=entry.solver.vfaces_info,
283-
vgeoms_state=entry.solver.vgeoms_state,
284-
face_mask=entry.raycast_mask,
285-
aabb_state=entry.aabb,
286-
)
287-
entry.bvh.build()
288-
289-
290218
@dataclass
291219
class RaycasterSharedMetadata(KinematicSensorMetadataMixin, SimpleSensorMetadata):
292220
# The BVHs cast against each frame live on the shared ``RaycastContext`` (one per active solver per mesh type),

genesis/engine/sensors/tactile_shared.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from genesis.utils.misc import concat_with_tensor, make_tensor_field
1212

1313
if TYPE_CHECKING:
14-
from genesis.engine.sensors.raycaster import _SolverBVH
1514
from genesis.utils.ring_buffer import TensorRingBuffer
1615

1716

@@ -442,18 +441,18 @@ class ContactDepthQueryMetadataMixin:
442441
"""
443442
Shared per-sensor-class state for the contact-depth query backend.
444443
445-
``contact_depth_query`` is the resolved mode for every sensor of this class -- ``"sdf"`` or ``"raycast"``. Each
444+
``contact_depth_query`` is the resolved mode for every sensor of this class - ``"sdf"`` or ``"raycast"``. Each
446445
sensor's ``build()`` overwrites the field with its own option when non-``None`` (last-value-wins); ``None`` at
447446
update time falls back to ``"sdf"``.
448447
449-
When mode is ``"raycast"``, ``collision_bvh`` references the per-sim shared BVH list built by
450-
``ensure_solver_bvhs``. ``sensor_candidate_geom_mask`` is a per-(env, sensor, geom) bool gate -- scattered per
451-
step from the contact prefilter (KinematicTactile family) or once at build from ``sensor_track_geom_idx``
452-
(ElastomerTaxel) -- so BVH leaves whose ``faces_info.geom_idx`` falls outside the mask are skipped.
448+
When mode is ``"raycast"``, the collision/visual BVHs come from the shared ``RaycastContext`` (the sensor's
449+
``shared_context``), built lazily on raycast opt-in and refreshed once per step by ``SensorManager``.
450+
``sensor_candidate_geom_mask`` is a per-(env, sensor, geom) bool gate - scattered per step from the contact
451+
prefilter (KinematicTactile family) or once at build from ``sensor_track_geom_idx`` (ElastomerTaxel) - so BVH
452+
leaves whose ``faces_info.geom_idx`` falls outside the mask are skipped.
453453
"""
454454

455455
contact_depth_query: str | None = None
456-
collision_bvh: "list[_SolverBVH] | None" = None
457456
sensor_candidate_geom_mask: torch.Tensor = make_tensor_field((0, 0, 0), dtype_factory=lambda: gs.tc_bool)
458457

459458

0 commit comments

Comments
 (0)