Source code for pyb_utils.contact
import numpy as np
import pybullet as pyb
from .named_tuples import getContactPoints
def _right_handed(point):
"""Returns True if the coordinate system of the contact point is
right-handed, False otherwise.
See https://math.stackexchange.com/q/327841.
"""
v1 = point.contactNormalOnB
v2 = point.lateralFrictionDir1
v3 = point.lateralFrictionDir2
return np.cross(v1, v2) @ v3 > 0
[docs]
def get_point_contact_wrench(point, origin=None):
"""Compute the contact wrench at a contact point.
The wrench is the force and torque acting on body A due to contact with
body B at contact point ``point``. The wrench is aligned with the world
frame.
Parameters
----------
point : ContactPoint
The contact point, as returned by
:func:`pyb_utils.named_tuples.getContactPoints`.
origin :
The origin point about which to compute the torque. If ``None`` (the
default), then the torque is computed about the world frame origin.
Returns
-------
:
A tuple ``(force, torque)`` representing the contact wrench on body A.
"""
if origin is None:
# world origin
origin = np.zeros(3)
origin = np.array(origin)
assert origin.shape == (3,), "Origin must be of length 3."
nf = point.normalForce * np.array(point.contactNormalOnB)
ff1 = point.lateralFriction1 * np.array(point.lateralFrictionDir1)
ff2 = point.lateralFriction2 * np.array(point.lateralFrictionDir2)
# lateral friction forces are always the same reported as the same
# regardless of the order of body A and B, but we can get the correct
# order by ensuring they make a right-handed frame together with the
# contact normal (which does depend on the order of A and B)
if not _right_handed(point):
ff1 *= -1
ff2 *= -1
force = nf + ff1 + ff2
torque = np.cross(point.positionOnA - origin, force)
return force, torque
[docs]
def get_points_contact_wrench(points, origin=None):
"""Compute the contact wrench resulting from a set of contact points.
The wrench is the force and torque acting on body A due to contact with
body B at contact points ``points``. The wrench is aligned with the world
frame.
Parameters
----------
points :
List of contact points, as returned by
:func:`pyb_utils.named_tuples.getContactPoints`.
origin :
The origin point about which to compute the torque. If ``None`` (the
default), then the torque is computed about the world frame origin.
Returns
-------
:
A tuple ``(force, torque)`` representing the contact wrench.
"""
force = np.zeros(3)
torque = np.zeros(3)
for point in points:
f, τ = get_point_contact_wrench(point, origin=origin)
force += f
torque += τ
return force, torque
[docs]
def get_total_contact_wrench(
bodyA,
bodyB,
linkIndexA=-2,
linkIndexB=-2,
origin=None,
max_contacts=None,
physicsClientId=0,
):
"""Compute the contact wrench resulting from a set of contact points.
The wrench is the force and torque acting on body A due to contact with
body B. The wrench is aligned with the world frame.
If :func:`pyb_utils.named_tuples.getContactPoints` has already been called,
you can use the :func:`get_points_contact_wrench` function instead.
Parameters
----------
bodyA : int
The UID of the first body.
bodyB : int
The UID of the second body.
linkIndexA : int
The index of the link on the first body (optional). If not provided,
checks all links on the body.
linkIndexB : int
The index of the link on the second body (optional). If not provided,
checks all links on the body.
origin :
The origin point about which to compute the torque. If ``None`` (the
default), then the torque is computed about the world frame origin.
max_contacts :
The maximum number of contact points expected (optional).
physicsClientId : int
The UID of the physics server to use.
Raises
------
AssertionError
If ``max_contacts`` is not ``None`` and the number of contact points
exceeds ``max_contacts``.
Returns
-------
:
A tuple ``(force, torque)`` representing the contact wrench.
"""
points = getContactPoints(
bodyA, bodyB, linkIndexA, linkIndexB, physicsClientId=physicsClientId
)
if max_contacts is not None:
assert len(points) <= max_contacts, f"Found {len(points)} contact points."
return get_points_contact_wrench(points, origin=origin)