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, print details. If False, suppress details. If None, print 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. Must use the same contacts supplied toobservables(). 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, or the observables are bound to 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.