pub struct ContactSolverConfig {
pub coupling: ContactCoupling,
pub newton: bool,
pub regularization: f64,
pub tolerance: f64,
pub max_iterations: usize,
pub relaxation: f64,
pub restitution_threshold: f64,
pub mujoco_compat: bool,
}Expand description
Tuning for the contact solve.
Fields§
§coupling: ContactCouplingWhich Delassus operator the sweep iterates on. See ContactCoupling.
newton: boolAllow the active-set Newton stage.
Off in the GPU-equivalent preset: a dense 3n x 3n factorization is
not something a contact shader can do, so leaving it on would put the
CPU reference out of the GPU’s reach by construction.
regularization: f64Diagonal regularization R added to the Delassus operator.
Larger values soften contact: more penetration, but a better conditioned solve and smoother sensitivities. Smaller values approach rigid contact and sharpen both the physics and the gradients.
tolerance: f64Residual tolerance for the fixed point (units of velocity).
max_iterations: usizeIteration cap, counted in units of solver work: one PGS sweep or one dense KKT solve each. Reaching it means the solve did not converge; the result is still feasible but is not a valid point to differentiate.
The solve may also stop short of the cap when the residual has stopped
falling fast enough to reach Self::tolerance within it. That is
reported identically — converged: false — because it means the same
thing: this is not a KKT point.
relaxation: f64Successive over-relaxation factor in (0, 2).
restitution_threshold: f64Below this approach speed, restitution ramps smoothly to zero.
Without it a resting body micro-bounces forever and a stack never
settles. The ramp is smoothstep, not a hard cutoff, so restitution
stays differentiable in both e and the approach speed.
mujoco_compat: boolReproduce MuJoCo’s regularization on the tangential rows as well as the normal one. Off by default, and it should stay off outside tests.
regularization_diag documents the divergence in full: MuJoCo applies
the impedance-derived R = (1-d)/d * A_nn to all three rows of a
contact frame, which on a friction row means a fraction 1-d of the
slip velocity survives every step. This crate applies it to the normal
row only, so a block below the friction angle holds exactly.
Turning this on re-introduces MuJoCo’s steady-state creep —
g sin(alpha) dt (1-d)/d, roughly a millimetre per second on a
25-degree slope at dt = 2 ms — for a block Coulomb’s law says is
stuck. That is the whole reason it is not the default.
It exists so MuJoCo trajectories remain usable as a cross-validation
oracle: a divergence in the tangential regularizer makes a step-by-step
comparison against MuJoCo meaningless, and without a way to match its
behaviour there is no way to tell “phyz is right and MuJoCo creeps”
apart from a real bug. mujoco_creep_matches_the_analytic_rate in
tests/stabilization.rs measures both modes side by side.
Implementations§
Source§impl ContactSolverConfig
impl ContactSolverConfig
Sourcepub fn simulation() -> Self
pub fn simulation() -> Self
Fidelity-biased preset: crisp contact, minimal penetration.
Sourcepub fn gradients() -> Self
pub fn gradients() -> Self
Gradient-biased preset: softer contact, better conditioned, smoother
sensitivities. Documented in docs/design/differentiable-contact.md
§2.5 — these differ on purpose, and a caller optimizing through the
simulator should validate the result under Self::simulation.
Sourcepub fn mujoco_compat() -> Self
pub fn mujoco_compat() -> Self
Self::simulation with Self::mujoco_compat set — a test-only
oracle preset for cross-validating trajectories against MuJoCo.
Sited alongside Self::gradients because it is the same kind of
thing: a named point in config space that trades one property away on
purpose. gradients trades fidelity for smoothness; this one trades
exact stiction for bit-comparability with a reference implementation.
Unlike gradients, it is not a preset anyone should ship with — see
the field docs for the creep it buys back.
Sourcepub fn gpu_equivalent() -> Self
pub fn gpu_equivalent() -> Self
The exact problem, operator and schedule the GPU contact pass runs.
This is the CPU’s model of the GPU: same convex problem, same staged Coulomb update, same solref bias and impedance regularizer, but the block-diagonal Delassus operator, no Newton stage, and a fixed sweep budget instead of a residual tolerance. Nothing here is a different contact model — it is the same model under a documented restriction.
It is the referee for GPU parity. A gap between this and the GPU is an
implementation bug; a gap between this and Self::simulation is the
price of the restriction. The two must be measured separately or
neither number means anything.
tolerance is zero so the sweep budget always runs to completion: the
shader has no early exit (a workgroup cannot cheaply agree that every
contact has converged), so an early-exiting reference would drift from
it on exactly the easy steps where the GPU keeps sweeping. converged
is therefore false here by construction, which is honest — a
fixed-budget iterate is not a KKT point and must not anchor an IFT
gradient.
Trait Implementations§
Source§impl Clone for ContactSolverConfig
impl Clone for ContactSolverConfig
Source§fn clone(&self) -> ContactSolverConfig
fn clone(&self) -> ContactSolverConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more