newton.sensors.SensorContact#

class newton.sensors.SensorContact(model, *, sensing_obj_bodies=None, sensing_obj_shapes=None, counterpart_bodies=None, counterpart_shapes=None, measure_total=True, verbose=None, request_contact_attributes=True, include_total=None)[source]#

Bases: object

Measures contact forces on a set of sensing objects (bodies or shapes).

In its simplest form the sensor reports total_force — the total contact force on each sensing object. Optionally, specify counterparts to get a per-counterpart breakdown in force_matrix.

total_force and force_matrix are each nullable: total_force is None when measure_total=False; force_matrix is None when no counterparts are specified.

Multi-world behavior

When the model contains multiple worlds, counterpart mappings are resolved per-world. The collision pipeline and solver are expected to produce only within-world contacts, so cross-world force accumulation does not arise in practice. Global counterparts (e.g. ground plane) contribute to every world they contact.

In single-world models where no add_world() call was made (all entities are global / world=-1), the sensor treats the entire model as one implicit world and all entities are valid sensing objects.

When counterparts are specified, the force matrix has shape (sum_of_sensors_across_worlds, max_counterparts) where max_counterparts is the maximum counterpart count of any single world. Row order matches sensing_obj_idx. Columns beyond a world’s own counterpart count are zero-padded.

sensing_obj_idx and counterpart_indices are flat lists that describe the structure of the output arrays.

Terms

  • Sensing object – body or shape carrying a contact sensor.

  • Counterpart – the other body or shape in a contact interaction.

Construction and update order

SensorContact requests the force extended attribute from the model at init, so a Contacts object created afterwards (via Model.contacts() or directly) will include it automatically.

update() reads from contacts.force. Call solver.update_contacts(contacts) before sensor.update() so that contact forces are current.

Parameters that select bodies or shapes accept label patterns – see Label Matching.

Example

Measure total contact force on a sphere resting on the ground:

import warp as wp
import newton
from newton.sensors import SensorContact

builder = newton.ModelBuilder()
builder.add_ground_plane()
body = builder.add_body(xform=wp.transform((0, 0, 0.1), wp.quat_identity()))
builder.add_shape_sphere(body, radius=0.1, label="ball")
model = builder.finalize()

sensor = SensorContact(model, sensing_obj_shapes="ball")
solver = newton.solvers.SolverMuJoCo(model)
state = model.state()
contacts = model.contacts()

solver.step(state, state, None, None, dt=1.0 / 60.0)
solver.update_contacts(contacts)
sensor.update(state, contacts)
force = sensor.total_force.numpy()  # (n_sensing_objs, 3)
Raises:

ValueError – If the configuration of sensing/counterpart objects is invalid.

class ObjectType(*values)#

Bases: Enum

Deprecated. Type tag for entries in legacy sensing_objs and counterparts properties.

BODY = 2#

Individual body.

SHAPE = 1#

Individual shape.

TOTAL = 0#

Total force entry.

__init__(model, *, sensing_obj_bodies=None, sensing_obj_shapes=None, counterpart_bodies=None, counterpart_shapes=None, measure_total=True, verbose=None, request_contact_attributes=True, include_total=None)#

Initialize the SensorContact.

Exactly one of sensing_obj_bodies or sensing_obj_shapes must be specified to define the sensing objects. At most one of counterpart_bodies or counterpart_shapes may be specified. If neither is specified, only total_force is available (no per-counterpart breakdown).

Parameters:
  • model (Model) – The simulation model providing shape/body definitions and world layout.

  • sensing_obj_bodies (str | list[str] | list[int] | None) – List of body indices, single pattern to match against body labels, or list of patterns where any one matches.

  • sensing_obj_shapes (str | list[str] | list[int] | None) – List of shape indices, single pattern to match against shape labels, or list of patterns where any one matches.

  • counterpart_bodies (str | list[str] | list[int] | None) – List of body indices, single pattern to match against body labels, or list of patterns where any one matches.

  • counterpart_shapes (str | list[str] | list[int] | None) – List of shape indices, single pattern to match against shape labels, or list of patterns where any one matches.

  • measure_total (bool) – If True (default), total_force is allocated. If False, total_force is None.

  • verbose (bool | None) – If True, print details. If None, uses wp.config.verbose.

  • request_contact_attributes (bool) – If True (default), transparently request the extended contact attribute force from the model.

  • include_total (bool | None) – Deprecated. Use measure_total instead.

update(state, contacts)#

Update the contact sensor readings based on the provided state and contacts.

Computes world-frame transforms for all sensing objects and evaluates contact forces (total and/or per-counterpart, depending on sensor configuration).

Parameters:
  • state (State | None) – The simulation state providing body transforms, or None to skip the transform update.

  • contacts (Contacts) – The contact data to evaluate.

Raises:
  • ValueError – If contacts.force is None.

  • ValueError – If contacts.device does not match the sensor’s device.

counterpart_indices: list[list[int]]#

Counterpart body or shape indices per sensing object. counterpart_indices[i] lists the counterparts for row i. Global counterparts appear first, followed by per-world locals in ascending index order.

counterpart_type: Literal['body', 'shape'] | None#

Whether counterpart_indices contains body indices ("body") or shape indices ("shape"). None when no counterparts are specified.

property counterparts: list[list[tuple[int, ObjectType]]]#

Deprecated. Use counterpart_indices and counterpart_type instead.

force_matrix: wp.array2d(dtype=wp.vec3) | None#

Per-counterpart contact forces [N], shape (n_sensing_objs, max_counterparts), dtype vec3. Entry [i, j] is the force on sensing object i from counterpart counterpart_indices[i][j], in world frame. None when no counterparts are specified.

property net_force: array2d#

Deprecated. Use total_force and force_matrix instead.

property reading_indices: list[list[list[int]]]#

Deprecated. Active counterpart indices per sensing object, per world.

sensing_obj_idx: list[int]#

Body or shape index per sensing object, matching the row of output arrays. For list[int] inputs the caller’s order is preserved; for string patterns the order follows ascending body/shape index.

sensing_obj_transforms: wp.array(dtype=wp.transform)#

World-frame transforms of sensing objects [m, unitless quaternion], shape (n_sensing_objs,), dtype transform.

sensing_obj_type: Literal['body', 'shape']#

Whether sensing_obj_idx contains body indices ("body") or shape indices ("shape").

property sensing_objs: list[list[tuple[int, ObjectType]]]#

Deprecated. Use sensing_obj_idx and sensing_obj_type instead.

property shape: tuple[int, int]#

Deprecated. Dimensions of net_force.

total_force: wp.array(dtype=wp.vec3) | None#

Total contact force [N] per sensing object, shape (n_sensing_objs,), dtype vec3. None when measure_total=False.