newton.sensors.SensorContact#
- class newton.sensors.SensorContact(model, *, sensing_bodies=None, sensing_shapes=None, counterpart_bodies=None, counterpart_shapes=None, measure_total=True, verbose=None, request_contact_attributes=True)[source]#
Bases:
objectMeasures contact forces, friction, and force-weighted positions on sensing objects (bodies or shapes).
In its simplest form the sensor reports
total_force— the total contact force on each sensing object — andtotal_force_friction, its tangential (friction) component. Optionally, specify counterparts to separate these measurements by interacting body or shape.force_matrixreports the per-counterpart contact forces,force_matrix_frictionreports their frictional (tangential) components, andposition_matrixreports the average interaction positions. In each matrix, rowicorresponds tosensing_indices[i]and columnjwithin that row corresponds tocounterpart_indices[i][j]. Columns beyond a row’s counterpart list are zero padding.Each
position_matrixentry is the average of the contact midpoints accumulated at that index, weighted by contact-force magnitude. It provides a representative location for where each counterpart interacts with the sensing object. Force weighting reduces the influence of weak contacts, so adding or removing a low-force contact perturbs the reported position less.total_forceandtotal_force_frictionareNonewhenmeasure_total=False. Per-counterpart outputsforce_matrix,force_matrix_friction, andposition_matrixareNonewhen 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 per-counterpart matrices have shape
(sum_of_sensors_across_worlds, max_counterparts), wheremax_counterpartsis the maximum counterpart count of any single world. Row order matchessensing_indices. Columns beyond a world’s own counterpart count are zero-padded.sensing_indicesandcounterpart_indicesare 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
Construct a
CollisionPipelineand pass its contacts toobservables()when requestingCONTACT_Ffrom the solver. Pass the resultingObservablesand the pipeline’sContactsbuffer to both the solver step andupdate(). The sensor uses only the linear part of eachCONTACT_Fwrench (force [N]); the torque part is ignored.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_shapes="ball", request_contact_attributes=False) solver = newton.solvers.SolverMuJoCo(model) state = model.state() collision_pipeline = newton.CollisionPipeline( model, rigid_contact_max=solver.get_max_contact_count(), soft_contact_max=0 ) contacts = collision_pipeline.contacts() observables = solver.observables(kinds=sensor.solver_observable_kinds, contacts=contacts) solver.step(state, state, None, contacts, dt=1.0 / 60.0, observables=observables) sensor.update(state, contacts, observables=observables) force = sensor.total_force.numpy() # (n_sensing, 3)
- Raises:
ValueError – If the configuration of sensing/counterpart objects is invalid.
- __init__(model, *, sensing_bodies=None, sensing_shapes=None, counterpart_bodies=None, counterpart_shapes=None, measure_total=True, verbose=None, request_contact_attributes=True)#
Initialize the SensorContact.
Exactly one of
sensing_bodiesorsensing_shapesmust be specified to define the sensing objects. At most one ofcounterpart_bodiesorcounterpart_shapesmay be specified. If neither is specified, onlytotal_forceandtotal_force_frictionare available (no per-counterpart breakdown or contact positions).- Parameters:
model (Model) – The simulation model providing shape/body definitions and world layout.
sensing_bodies (str | list[str] | Pattern[str] | list[int] | None) – Glob pattern, list of glob patterns, compiled regular-expression pattern to match against body labels, or list of body indices. Regular expressions use full matching.
sensing_shapes (str | list[str] | Pattern[str] | list[int] | None) – Glob pattern, list of glob patterns, compiled regular-expression pattern to match against shape labels, or list of shape indices. Regular expressions use full matching.
counterpart_bodies (str | list[str] | Pattern[str] | list[int] | None) – Glob pattern, list of glob patterns, compiled regular-expression pattern to match against body labels, or list of body indices. Regular expressions use full matching.
counterpart_shapes (str | list[str] | Pattern[str] | list[int] | None) – Glob pattern, list of glob patterns, compiled regular-expression pattern to match against shape labels, or list of shape indices. Regular expressions use full matching.
measure_total (bool) – If True (default),
total_forceandtotal_force_frictionare allocated. If False, both are None.verbose (bool | None) – If True, log details. If False, suppress details. If None, log details when
wp.config.log_levelis configured for debug logging.request_contact_attributes (bool) –
If True, request the deprecated
contacts.forceextended attribute for compatibility. Defaults to True during the deprecation period. Pass False and supply solver observables toupdate()instead.Deprecated since version 1.7: Passing True is deprecated. Allocate
solver_observable_kindsthrough the solver and passObservablestoupdate()instead.
- update(state, contacts, *, observables=None)#
Update the contact sensor readings based on the provided state and contacts.
Computes world-frame transforms for all sensing objects and evaluates contact forces and their friction (tangential) components (total and/or per-counterpart, depending on sensor configuration). When
stateprovides body transforms, also computes force-weighted per-counterpart contact positions.- Parameters:
state (State | None) – The simulation state providing body transforms. If None (or a state without
body_q),sensing_transformsis left unchanged andposition_matrixis reset to zero. Contact-force outputs are updated in either case.contacts (Contacts) – The contact data to evaluate.
observables (SolverBase.Observables | None) – Solver observable arrays containing
contact_f. If omitted, the deprecatedcontacts.forcearray is used when available.contactsmust be the buffer that producedcontact_f(observables.contacts). Before the first solver step, newly allocated observables report zero contact forces. See Solver Observables for allocation and usage examples.
- Raises:
ValueError – If
observablesbelong to a different model, no contact-force output is available, orcontact_fwas produced with a differentcontactsinstance.ValueError – If legacy contacts are on a different device from the sensor.
- counterpart_indices: list[list[int]]#
Counterpart body or shape indices per sensing object.
counterpart_indices[i]lists the counterparts for rowi. Global counterparts appear first, followed by per-world locals in ascending index order.
- counterpart_type: Literal['body', 'shape'] | None#
Whether
counterpart_indicescontains body indices ("body") or shape indices ("shape").Nonewhen no counterparts are specified.
- force_matrix: wp.array2d[wp.vec3f] | None#
Per-counterpart contact forces [N], shape
(n_sensing, max_counterparts), dtypevec3. Entry[i, j]is the force on sensing objectifrom counterpartcounterpart_indices[i][j], in world frame.Nonewhen no counterparts are specified.
- force_matrix_friction: wp.array2d[wp.vec3f] | None#
Per-counterpart friction (tangential) contact forces [N], shape
(n_sensing, max_counterparts), dtypevec3. Entry[i, j]is the friction force on sensing objectifrom counterpartcounterpart_indices[i][j], in world frame.Nonewhen no counterparts are specified.
- position_matrix: wp.array2d[wp.vec3f] | None#
Average contact positions [m] per counterpart, shape
(n_sensing, max_counterparts), dtypevec3. Entry[i, j]is the average world-frame interaction position betweensensing_indices[i]andcounterpart_indices[i][j]. It averages the midpoint of all contacts between these objects, weighted by linear contact-force magnitude. Entries are zero when the interaction force is zero orupdate()receives no body transforms.Nonewhen no counterparts are specified.
- sensing_indices: 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_transforms: wp.array[wp.transformf]#
World-frame transforms of sensing objects [m, unitless quaternion], shape
(n_sensing,), dtypetransform.
- sensing_type: Literal['body', 'shape']#
Whether
sensing_indicescontains body indices ("body") or shape indices ("shape").
- solver_observable_kinds = frozenset({'contact_f'})#
Solver observables required by
update().Experimental
The solver observable API may change while additional observables are migrated to it.