Every control cycle, a legged whole-body controller must turn a desired net body wrench (force + moment) into individual ground-reaction forces fi at each stance foot — the classic "contact-force QP" at the core of whole-body control:
minimize Σ‖f_i‖²
subject to Σ f_i = F_des
Σ (p_i × f_i) = M_des
f_i,y ≥ 0 (unilateral)
‖f_i,xz‖ ≤ μ f_i,y (friction cone)
This demo solves it with alternating projections: a min-norm pseudoinverse enforces the linear wrench-equality constraint, and each contact force is then projected exactly onto its friction cone, iterated until both hold. The right-hand panel draws that cone projection in its native 2D space — normal force fy on the horizontal axis, tangential force magnitude ‖fxz‖ on the vertical axis — which is exactly the plane the projection happens in, one wedge per foot.
Two corrections versus the 3D version of this sim, both verified numerically against a brute-force grid search (a standalone Node script, not shipped):
1) That version's cone projection scaled the normal component by μ before applying the isotropic (μ=1) closed-form projection formula — that shortcut is only exact when μ=1. This 2D engine instead projects onto the true wedge boundary line ‖fxz‖ = μ·fy by resolving it into the line's own unit direction (cosθ, sinθ) with θ = atan(μ), the correct closed-form Euclidean projection for every μ. The two formulas agree only at μ=1; at μ=0.2 the original was off by roughly 60% of the true projection distance.
2) That version (and the shared alternating-projection scheme) flagged infeasibility purely from the wrench-equality residual — but the loop's last step is always an exact affine-equality projection, which drives that residual to ~0 whenever any point on the 6-constraint affine subspace is reachable, regardless of whether the friction cones themselves are satisfied. A 200 N desired push against μ=0.6 and 90 N of body weight — far beyond what friction can support — converges to a residual of ~0.00003 (reads "feasible") while every foot sits at 370% of its cone limit. This engine instead flags infeasibility from cone utilization exceeding 100%, which is what the warning box below actually reacts to.
- Push Fx/Fz sliders — the net horizontal force the body must exert on the ground (e.g. to accelerate or resist a shove). Drag the arrow handle at the robot's center in the top-down view for the same effect.
- Roll/Pitch torque sliders — a net moment about the body, as when leaning or carrying an offset load.
- μ slider — the Coulomb friction coefficient; the wedge half-angle atan(μ) widens or narrows in every per-foot cone diagram.
- Body weight — the vertical load the four legs must jointly support before any push/lean is added.
- FL/FR/BL/BR buttons — lift a leg into swing, removing it from the stance set and forcing the QP to redistribute load across fewer contacts, shrinking the feasible region (at least two feet always stay down).
- Arrow and dot colour encode per-foot cone utilization (green → safely inside the cone, red → saturated against it); when the desired wrench cannot be realized by the active feet at all, the residual stays nonzero and the solver flags it as infeasible.
Real-world relevance: this exact QP (or a receding-horizon version of it) runs at 100s of Hz inside legged robots like Boston Dynamics' Spot/Atlas and MIT's Mini Cheetah to keep every stance foot's push physically realizable before the low-level joint torques are computed.