Skip to content

Stabilization, bounces and drives

Position error is corrected through velocity only. A penetrating contact adds βϕ/h\beta\phi/h to its target with β\beta = baumgarte (0.2). A separated contact within its gap adds sϕ/hs\phi/h with ss = contact_speculative_scale (1), which lets it close exactly the gap in one step and blocks faster approach; these speculative contacts stand in for continuous collision detection at robot speeds. velocity_iterations adds sweeps without the position bias, so stabilization does not add velocity to bodies. contact_regularization turns penetrating rows into an implicit spring, which makes indeterminate load splits unique at a small resting penetration.

Restitution uses the closing speed at the start of the step: an impacting contact faster than restitution_velocity_threshold (0.5 m/s) gets the rebound target un≥−e ustartu_n \ge -e\,u_\text{start}. Resting contacts pressed by gravity or a drive never bounce. Shape restitution defaults to zero.

Drives are implicit by default (drive_mode="augmented"): hkd+h2keh k_d + h^2 k_e joins the mass-matrix diagonal, so stiff gains stay stable at ordinary steps. Only the explicit part is clamped to joint_effort_limit; the implicit reaction is not, so under a large external load the drive can exceed the limit. drive_mode="rows" solves each driven DOF as a row with the PhysX articulation drive update and clamps its impulse at the effort limit times hh; below saturation and without contacts it converges to the implicit drive.

Figure 8Stabilization, bounces, drives and limits.

One measured Steel run per rule, from the tutorials. Tutorial: Velocity iterations · Depenetration speed limit · Speculative contacts · Restitution · Restitution threshold · PD drives · Drive saturation · Joint limits · Joint velocity limits

Soft contact and adhesion. contact_compliance solves hydroelastic contacts as implicit spring-dampers, and the per-shape steel:adhesion (integration branch) lets contacts pull within their gap.

Joint limits are off by default. With enable_joint_limits=False authored limits are not simulated. Turn them on whenever the model authors limits. They cover PRISMATIC, REVOLUTE and D6 DOFs: a DISTANCE joint’s min_distance and max_distance are not enforced even with limits on, and the joint moves like a free joint. A change that enforces them on DISTANCE joints that root their articulation is in review in skild-ai/newton#23; until then, build a tether from a ball joint and a limited slider. Tutorial: Tethers and distance joints

Loop closures are bilateral rows in the same sweep: three for BALL, five for REVOLUTE, six for FIXED. They read joint_enabled, joint_X_p, joint_X_c and the hinge axis every step, so a weld can be released or re-anchored mid-run, also between graph replays. enable_bilateral_preelimination removes most of their dependence on iterations.

Two smaller details: kinematic DOFs are handled inside the factorization with an armature of 101010^{10}, and single-body free objects replace the explicit gyroscopic term with an energy-preserving update over 1 to 32 adaptive microsteps, so spinning, thin objects do not gain energy.