phyz-contact
Soft contact dynamics and friction for phyz.
Penalty-based contact resolution on top of
phyz-collision's Collision records.
| Function | Purpose |
|---|---|
find_contacts |
body-body contacts from model geometry |
find_ground_contacts |
contacts against a horizontal ground plane (one centred shape per body) |
find_ground_contacts_model |
ground contacts over Body::collisions — every shape, offsets and orientations included |
contact_forces |
explicit penalty wrenches |
contact_forces_implicit |
semi-implicit variant, stable at larger dt |
compute_contact_force, compute_contact_force_implicit |
single-contact kernels |
The convex solve (assemble + solve_contacts) is what the stepper uses, and
what supersedes the penalty functions above. It carries MuJoCo-style
solref/solimp position stabilization, so penetration is repaid rather than
frozen in place, combines the two contacting bodies' materials
(ContactMaterial::combine), and warm starts from the previous step's impulses
via ContactCache.
ContactMaterial carries stiffness, damping and friction. The implicit form
solves for the normal impulse that the next step will produce, rather than the
one the current penetration implies, which is what keeps stacks from exploding
at practical timesteps.
Per-body materials, and which body to put one on
A material belongs to a body, via phyz_model::Body::material. None —
the default — means "the scene material", so a model that sets nothing behaves
exactly as it did when every caller passed vec![one; n_bodies].
# use ContactMaterial;
#
Friction combines by max, and that decides which body you attach a
material to. ContactMaterial::combine takes the elementwise maximum of the
pair's friction (MuJoCo's rule — see its docs for why), so a grippy body grips
everything it touches, not just the surface you had in mind. A body's
material applies to all of its contacts; there is no per-pair override.
The worked example, from a skateboarding robot where it was measured:
- Grip tape on rubber soles is about
mu = 1.5, while 95A urethane wheels want0.75. With both on one shared0.9, the feet slid on the deck: a searched "ollie" turned out to be a foot hooking the deck's edge and dragging the board up by friction. - Putting that
1.5on the deck body instead of the shoes fixes the foot-on-deck contact —max(1.5, 0.9) = 1.5either way — but it also raises the deck-on-ground contact, and the deck's underside is bare wood that scrapes the asphalt during the pop. Measured, that braked the board from 0.6 to 0.39 m/s and collapsed the trick to 2 ms of air. - Putting it on the shoes raises only foot-on-deck and leaves
deck-on-ground at wood's
0.6, which is the physics.
The ground has no material of its own
A contact against the ground plane or a heightfield uses the touching body's
material verbatim — there is no second material, so nothing is combined.
Setting the deck to wood's 0.6 and passing asphalt's 0.8 as the scene
material gives a deck-on-ground friction of 0.6, not max(0.6, 0.8). The
scene material is a default filler for bodies that carry none, not a property
of the ground. Express a road surface by leaving the bodies that touch it on
the scene material.
The rule of thumb: attach the material to the part whose surface it
describes, then check what else that part touches. Two bodies that must grip
each other but slide on everything else cannot be expressed by per-body
materials at all — split the geometry into separate bodies, or lower the scene
material so max lands where you want it.
Example
use ;
// `state.body_xform` must be up to date — call
// `phyz_rigid::forward_kinematics` first.
#
Part of phyz
phyz is an open-source differentiable
multi-physics simulation workspace in pure Rust. Each crate is independent —
adding this one does not pull in the rest.
Licensed under MIT.