# Proximity
`gs.sensors.SurfaceDistanceProbe` reports the nearest distance from one or more probe points to the mesh surfaces of a set of tracked rigid links. Each probe is mounted in the local frame of a link and moves with it, so the sensor answers "how close is this point on my robot to those objects?" as the scene evolves.
The complete script is [`examples/sensors/surface_distance_shadowhand.py`](https://github.com/Genesis-Embodied-AI/genesis-world/blob/main/examples/sensors/surface_distance_shadowhand.py), which mounts probes on the palm and fingertips of a Shadow Hand and measures distance to a duck mesh and a box under keyboard teleoperation.
## Minimal example
```python
import genesis as gs
gs.init(backend=gs.cpu)
scene = gs.Scene()
robot = scene.add_entity(gs.morphs.URDF(file="urdf/shadow_hand/shadow_hand.urdf"))
duck = scene.add_entity(
gs.morphs.Mesh(file="meshes/duck.obj", scale=0.06, pos=(-0.2, 0.4, 0.6)),
)
sensor = scene.add_sensor(
gs.sensors.SurfaceDistanceProbe(
entity_idx=robot.idx,
link_idx_local=robot.get_link("palm").idx_local,
probe_local_pos=((0.0, 0.0, 0.0),), # one probe at the palm-link origin
probe_radius=0.5, # max sensing range, meters
track_link_idx=(duck.base_link_idx,), # global link idx to measure against
)
)
scene.build()
scene.step()
distances = sensor.read() # shape ([n_envs,] n_probes), meters
points = sensor.nearest_points # shape ([n_envs,] n_probes, 3), world frame
```
## Probes and tracked links
A probe is a point fixed in a link's local frame. `probe_local_pos` is a sequence of `(x, y, z)` offsets in meters, one per probe, all mounted on the link named by `link_idx_local` on the entity `entity_idx`. Mounting several probes on fingertips and the palm, as the example does, gives a cheap spatial picture of how the hand is approaching an object.
`track_link_idx` lists the links to measure against, as **global** link indices in solver link space, not link-local indices. Read them off the entities you want to track: `duck.base_link_idx` for a single-link mesh, or `entity.get_link(name).idx` for a specific link of an articulated entity. The sensor queries the triangle faces of every mesh geom on those links, so distances are to actual surfaces, not bounding boxes or centers.
## Reading the sensor
`read()` returns the nearest surface distance for each probe:
```python
distances = sensor.read() # shape ([n_envs,] n_probes), meters
```
The matching nearest points are a separate attribute rather than part of `read()`:
```python
points = sensor.nearest_points # shape ([n_envs,] n_probes, 3), world frame
```
Both leading dimensions follow the batched-optional convention: the `[n_envs,]` axis is present only when the scene is built with multiple environments. `nearest_points` is written on each step, so read it after at least one `scene.step()`; before the first step it holds zeros.
## Behavior and units
- **Units.** Distances and probe positions are in meters. The scene uses a right-handed, Z-up frame, and `nearest_points` are in world coordinates.
- **Clamping at `probe_radius`.** `probe_radius` is the maximum sensing range. When no tracked surface lies within it, the reported distance is clamped to `probe_radius` and the nearest point is the probe's own world position. `probe_radius` accepts a scalar shared by every probe, or a per-probe array matching the probe count; it defaults to 10.0 m.
- **Debug drawing.** With `draw_debug=True` and an active visualizer, the sensor draws a sphere at each probe and a line to its nearest surface point, sized by `debug_sphere_radius` (default 0.008 m).
## See also
- {doc}`Sensors overview `: how sensors are sampled, read, and configured with noise, delay, and history.
- {doc}`Raycaster sensors `: distance measurements by casting rays instead of querying nearest surface points.