Nonholonomic Systems I — Controllability
A velocity constraint is not a configuration constraint. The Lie bracket is the exact bookkeeping of what wiggling buys, Chow's theorem says when the brackets span everything, Frobenius says when they never will, and the symmetric product carries the same test to Reach with a dead motor and Rusty with mass.
We know by experience, however, that this velocity constraint does not imply a constraint on configurations; the car can reach any position and orientation in the obstacle-free plane. In fact, the prevented sideways translation can be approximated by parallel-parking maneuvers.
In this chapter
Hitch cannot slide sideways. Every reader also knows Hitch can end up half a metre to the left of where it started, parallel-parked, by wiggling. This chapter turns that folk knowledge into a theorem, and the theorem has a precise shape: a velocity constraint is not a configuration constraint, and the Lie bracket is the exact bookkeeping of what wiggling buys. Follow drive for a time , then steer for , then undo both in order. You do not return home. You are displaced by in a direction neither input produces directly — the bracket — plus a remainder of order that this chapter measures rather than waves away.
Stack brackets until they span the tangent space and Chow's theorem says the system reaches everything nearby; stop short and Frobenius says it is trapped on a lower-dimensional leaf forever. That is the first half. The second half asks the same question of second-order systems — Reach with its third motor dead, Rusty with mass — where drift complicates the bookkeeping and the symmetric product of Lewis and Murray halves the work.
Everything here is analysis. Nothing in this chapter produces a plan; the catalogue of steering methods that cash these verdicts in is Chapter 21. Three honesty items the prose keeps throughout: the Lie algebra rank condition is necessary only for analytic fields; Sussmann's condition is sufficient, not necessary; and STLC for a second-order system is always a statement at rest.
The problem: sideways is forbidden, sideways is reachable
Tell Hitch "half a metre to the left, same heading". A holonomic ghost — the free-flying rectangle of Chapter 4 — slides there in a second. Hitch has two inputs, a speed and a steering rate, and no combination of them has a sideways component. Yet Hitch gets there, by spinning a little, driving a little, unspinning, undriving, fourteen times over.
Two things in that picture are the whole chapter. The sideways gain per loop is quadratic in the wiggle amplitude, which is why parallel parking is slow and why a planner that only has the four-flow recipe would be a terrible planner. And the gain has a direction that neither input has — the Lie bracket of the two input fields, which is the one new mathematical object you need.
Building intuition
Wiggling has a direction
The loop below is Choset's equation (12.3): from , follow for , then for , then for , then for . For the unicycle's drive and spin fields the four legs are exact — two arcs of length and two spins of angle — so the residue can be written in closed form and compared to the prediction .
Three things to notice, each a theorem below.
There is no first-order term. Halve and the residue drops by four, not two: the blue dot in the inset slides down a line of slope two. At the loop from heading moves Hitch by against the prediction ; the gap is , the remainder.
The direction is new. is perpendicular to the heading — exactly the direction the no-slip constraint forbids. Two fields that cannot move you sideways produce a loop that does.
Brackets stack. Switch to the four-state car. Its inputs are speed and steering rate, and the first bracket only buys heading change — a car that can set its steering angle but has not yet driven has turned nothing. Loop with and the residue is sideways: the second-degree bracket . Hitch needs two rungs of brackets for the same direction the unicycle needed one, and that number — the degree at which the brackets fill the tangent space — is what the filtration counts.
Four words for four sets
"Controllable" is one word in linear systems theory and four in this chapter, because for a nonlinear system the shape of the reachable set matters: does it have interior, does it have interior without leaving a small ball, does it contain a neighbourhood of the start inside that ball?
With drive alone the reachable set is a curve — the integral curve of — and no controllability property holds. With drive and spin, controls in , the sampled set fills a blob with the start in its interior: small-time locally controllable. Flip to the forward-only, left-only control set and the set keeps its interior but hugs the start's boundary — nothing is behind Hitch, because in small time the heading cannot swing past . That system is accessible, STLA and (in the open plane) controllable, yet not STLC. The macro adds as if it were a third input, and sideways becomes first-order: the widget is pretending to be a system whose Lie algebra is spanned at degree one, which is what every steering method of Chapter 21 constructs by other means.
Rank is a property of the point
Choset's Example 12.1.7 is two innocent-looking fields on , and , whose bracket has determinant against them.
Start anywhere off the coordinate planes and the system reaches a full three-dimensional neighbourhood. Start on the plane and loses its first component for good — the reachable set is a half-plane, and no sequence of controls ever leaves it. Start on the -axis and it is a line. Nine leaves in all. A rank test at one configuration settles controllability there; the trailer's jackknife in Chapter 23 is the same lesson in a less innocent system.
| Symbol | Meaning | Note |
|---|---|---|
| state manifold, state, its dimension | M = 𝒬 for a kinematic system; M = T𝒬, x = (q, q̇) for a second-order one | |
| tangent space, tangent bundle, a covector (one-form), the flow of g for time t | ω(x)ẋ = 0 is the negative form of a constraint | |
| distribution, k-th step of the filtration, involutive closure, Lie algebra generated | Choset §12.1.2–12.1.3 | |
| the Lie bracket (eq. 12.5) | the sign under which the four-flow loop moves by +ε²[g₁, g₂] | |
| drift field, control fields, control vector and set | ẋ = g₀ + Σ gᵢuᵢ (eq. 12.6) | |
| control sets with the origin interior to the convex hull / merely spanning ℝᵐ | [−1, 1]ᵐ and its vertices / [0, 1]ᵐ | |
| states reachable within time T without leaving the neighbourhood V | the set the four notions are about | |
| kinetic-energy metric, covariant derivative, symmetric product | Chapter 17's Christoffel symbols, Choset's convention (no M⁻¹ inside Γ) | |
| columns of M⁻¹(q)T(q); the symmetric closure | input fields on 𝒬, not on T𝒬 | |
| constraint projection, constrained inputs and connection | Chapter 17's projectionP | |
| Hitch: rear-axle position, steering angle, heading (Choset's ordering), wheelbase, steering limit; trailer heading, hitch angle, hitch length | Chapter 2 integrates ψ; Choset writes θ₁ |
The mathematics
Following Choset, the state space is a vector space throughout: every manifold in
this book is locally diffeomorphic to , and the price — angles wrap, so a heading
coordinate is only a chart — is one this chapter pays knowingly. The global structure returns in
Chapter 5's Manifold, which is where the planners of Part III
live; here we differentiate, and differentiation is local.
Vector fields, flows, distributions
A vector field is a smooth map with — in coordinates, a column vector that depends on . Its flow solves from for time , and the integral curve through is . A set of fields generates the distribution , a linear subspace of at each ; it is regular if that subspace has the same dimension everywhere.
The unicycle's two fields, drive and spin, are the positive form of its distribution,
and the no-slip constraint is its negative form: with the covector (Choset eq. 12.1). A velocity constraint that cannot be integrated to a configuration constraint is nonholonomic; the Pfaffian ones, , are the kind Chapter 17 handled with the projection , and the whole question of this chapter is whether they integrate.
The Lie bracket from the four-flow loop
DerivationExpanding the four flows to second order
Step 1 — one flow. Taylor-expand in : and , so .
Step 2 — compose after . Write . Then , and re-expanding at gives . The cross term appears; the self terms are carried along.
Step 3 — compose . The term cancels the one from step 1. Re-expanding at the current point produces from the first-order displacement accumulated so far, and the self term from the flow of itself (the sign of enters twice). The two -self contributions cancel: . What survives from this leg is .
Step 4 — compose . Likewise the term cancels, the -self terms cancel, and the re-expansion of at a point displaced by (the displacement is gone) gives , which together with step 2's sums to zero.
Step 5 — collect. Every first-order term and every self term is gone; the survivors are the two cross terms, — the bracket.
The unicycle in closed form. Drive by at heading , spin by , drive back by at heading , spin back:
The term is ; the term points along the heading, so the loop also drifts forward by half a cube — the leak the hook measures and the number w20.1 plots.
The linear case and the sign (Choset Problem 8). For , equation (12.5) gives , the negative of the matrix commutator that the sister book's Lie-group chapter uses. The sign is immaterial for spans and determinants' zero sets, and material for the direction of the loop residue; the library keeps Choset's and pins it in a check.
The library computes the bracket by formula (12.5) from each field's Jacobian — analytic for every system of this chapter, a fourth-order central difference otherwise — and the check compares the finite-difference bracket with the hand formula to at the micro-example's configuration. Two properties hold for every pair and triple of fields and are property-tested on seeded random polynomial fields: skew-symmetry and the Jacobi identity .
Filtrations, the involutive closure, Frobenius
A Lie product of degree is a bracket expression in which the original fields appear times. The Lie algebra is the span of all Lie products of all degrees, built by Choset's recursion
whose span sequence is the filtration of . If the filtration is regular, each step either raises the dimension or stops, so it terminates at a finite with , the involutive closure; is involutive if . If all products vanish beyond some degree the algebra is nilpotent, which Chapter 21's chained forms exploit.
DerivationWhy involutive is exactly what integrates
Step 1 — necessity. If the fields of are tangent to a submanifold , their flows stay in , so the four-flow loop stays in , so its residue is tangent to in the limit: the bracket lies in . Involutive is forced.
Step 2 — sufficiency, in one picture. Choose independent fields of an involutive near . Because their brackets stay in , the fields can be re-combined (pointwise, by an invertible matrix) into commuting fields, and commuting flows can be straightened into coordinate flows by the inverse function theorem. The leaf through is then .
Step 3 — regularity and termination. If every is regular, is constant in and non-decreasing in , bounded by ; so it strictly increases or the recursion stops, after at most steps. Without regularity nothing bounds the degree a priori — the library runs to a stated maximum and reports a stall separately from a closure.
Step 4 — the planning consequence. A system whose closure has dimension at never leaves a -dimensional leaf: it is neither controllable nor accessible, and no amount of cleverness with the inputs changes that. The unicycle with drive only () is the canonical case.
Example 12.1.7, by the numbers. , ,
and . The
library's larc returns STLC at , a two-dimensional leaf at — the
ladder stays at through degree five — and a one-dimensional leaf on the -axis.
Nine leaves: a line, four half-planes, four quadrants (Choset Figure 12.9).
The Philip Hall basis. Skew-symmetry and Jacobi mean most products in are redundant; the Hall basis chooses the smallest set at each degree. The library prunes only the trivial redundancies (, at degree two) and products that vanish identically near the test point, and never prunes a product merely because it is dependent at the point — Example 12.1.7 on a coordinate plane is exactly a field that is zero at with nonzero brackets. Exercise 6 asks for the Hall basis proper.
Control-affine systems and the four notions
Choset's class is the control-affine system
with drift and independent control fields ; driftless if . Kinematic systems may be driftless; second-order ones never are, because the state carries the velocity and the velocity carries the configuration whether or not any input acts. Two classes of control set matter: , whose convex hull contains the origin in its interior (positive combinations reach every direction — the cube , or just its vertices), and the larger , which merely spans (the non-negative cube). A driftless system with is symmetric: every motion can be run backward.
Let be the states reachable within time by trajectories that never leave the neighbourhood of . The system is, from :
- controllable if every is in for some finite ;
- accessible if contains a full -dimensional set for some ;
- small-time locally accessible (STLA) if contains a full -dimensional set for every and every ;
- small-time locally controllable (STLC) if contains a neighbourhood of for every and .
STLC is the one planners want: a system STLC everywhere can follow any curve in arbitrarily closely, so it can thread any clutter a free-flying body can thread. The implications among the four, and the conditions on the ones that are only sometimes true, are Choset's Figure 12.12:
small-time locally controllable
R^V(x, ≤ T) contains a neighborhood of x for every V and T: the system can wiggle in every direction without leaving any ball.
separating example
Symmetric systems with the LARC (Chow); systems with drift at an equilibrium when every bad bracket is neutralized (Sussmann).
in the lab
Hitch with and without the trailer (ladder 2→3→4(→5)); the PBWT with two thrusters at rest; Reach's 2R on the Workbench.
The linear double integrator is the warning. with is "controllable" in the Kalman sense, yet it is STLC only from rest: at reaching a point behind you requires the velocity to change sign, and the velocity coordinate must leave any small first. For second-order systems STLC always means STLC at zero velocity, and the linearizations of the nonholonomic systems of this chapter fail the Kalman rank test outright — their controllability is inherently nonlinear.
Chow's theorem and Sussmann's refinement
DerivationBrackets as motions, and why bad ones are one-way
Step 1 — every Lie product is a motion. A degree- product is realized by nesting four-flow loops: is a loop whose second leg is itself a loop. The net motion is in time — slower by one power per degree, which is why parking into a space longer than the car costs reversals (Chapter 21).
Step 2 — full rank gives interior. If the products span , there are independent motion directions available in arbitrarily small time inside arbitrarily small , and small combinations of them sweep an open set: STLA. For analytic fields the converse holds because an analytic system's reachable set is determined by all derivatives at , which is the Lie algebra.
Step 3 — symmetry gives both signs. Reverse every leg of a loop and the residue flips sign; for a symmetric system the reversed controls are available, so each bracket direction comes with both signs and the open set of step 2 can be centred on : STLC.
Step 4 — bad brackets. Write the loop for a bracket with its control amplitudes explicit. A control appearing an even number of times contributes to the net motion, so flipping changes nothing: the direction can be followed one way only, like a drift. A bad bracket is a one-way direction. It is harmless if a faster motion (a good bracket of lower degree) can cancel it — the bad bracket is neutralized — and fatal otherwise.
Step 5 — the degree-one bad bracket. itself is bad (drift once, controls zero times) and there is nothing of lower degree to neutralize it, hence the hypothesis : Sussmann's theorem speaks only at equilibria.
Step 6 — from local to global. On a connected , STLC everywhere gives controllability: chain neighbourhoods along any curve. The converse fails — the forward-only unicycle is controllable in the open plane and STLC nowhere.
Choset's four counterexamples, each in the library. The one-way watch knob (accessible,
controllable, STLA, not STLC). , : at the origin
and is bad — sussmannStlc reports the LARC
satisfied at degree three and a neutralization residual of exactly (nothing of lower degree points
along ), hence STLA only; indeed never decreases. The double integrator: STLC at
, undecided at because . The unicycle with :
STLA by Chow, STLC unavailable because symmetry is unavailable.
For Hitch the verdict is the one Choset promises in §12.5.6 and the micro-example below computes: the car is STLC at every configuration with , whatever the steering limit . A car that can only turn left reaches every pose in the Lot that a normal car can — slowly.
Global controllability from recurrent drift
DerivationRecurrence supplies the backward motions symmetry would have
Step 1. The LARC gives forward local reachability: an open set of states is reached from any in short time (STLA).
Step 2. To move against the drift, wait: recurrence returns the unforced flow arbitrarily close to where it started, so a neighbourhood of any state is reached again later without controls, and forward-only motions plus waiting generate backward displacements too.
Step 3. Chain neighbourhoods along any path, as in the connected-and-STLC argument.
The example. (circular orbits — WPPS), : and everywhere, so the system is controllable, though STLC only at the origin where vanishes. The library returns STLA by the LARC and for the bracket. The robotics case: energy-conserving drift on a compact configuration space is WPPS by Poincaré recurrence — a frictionless Reach on the horizontal Workbench (Exercise 1), a satellite on with a single body-fixed torque.
Second-order systems: the symmetric product
A simple mechanical control system is Chapter 17's standard form without potential terms, , so that with the columns of ,
One can lift this to with , , and bracket away — correct, and twice the dimension it needs to be. The acceleration is not intrinsic (a free particle in polar coordinates has ); the intrinsic acceleration is , the coordinate acceleration with the centripetal term projected out, and the object that does the projecting is the covariant derivative of the kinetic-energy metric,
are Chapter 17's Christoffel symbols in Choset's convention (no folded in; the amber term is the velocity-product form ). The equations of motion become — Newton's with the right — and unforced motions are geodesics.
DerivationWhy the symmetric product halves the computation
Step 1 — the intrinsic acceleration. On a point mass at angle has . Only the term is visible to an observer confined to the circle; the term keeps the mass on it. Subtracting the centripetal term is what does in general coordinates.
Step 2 — the connection in coordinates. For a metric the Levi-Civita connection is , with the Christoffel symbols of Chapter 17's equation (10.9). Euclidean metric: and is the directional derivative.
Step 3 — block-differentiate on . With and , the Jacobian of has only a lower-left block , acting on the upper half of its argument — which is zero for every , so . For the upper-right block of (the identity) hits and gives the in the configuration slot; the velocity slot is linear in and vanishes at rest. For the velocity-product's second derivative in survives at rest and is exactly , while the two terms assemble the rest of .
Step 4 — the reading. is the set of velocity directions reachable from rest; the set of configuration directions. Sussmann's good/bad bookkeeping carries over verbatim because the lift makes appear once in every symmetric product.
Step 5 — in the library. liftToStateSpace builds the six-dimensional fields for the planar body
with thrusters, and a check confirms the three identities numerically to against
symmetricProduct on .
The planar body with thrusters (Choset Example 12.4.3) — and Reach's free third link. Unit mass and inertia, , (thrust through the centre of mass), (the offset thruster). Then , rank 3: STLA from rest. The bad products are and — Choset prints ; the factor is immaterial — neutralized by : STLC at rest, confirming the Lie-bracket computation of Example 12.3.4, where . A single offset thruster gives rank, STLA, with nothing to neutralize the bad products: Sussmann is silent, and the system is in fact controllable but not STLC (Choset Figure 12.14). Section "Putting it together" asks whether this body is Reach's third link when the third motor is dead; the honest answer is "kinematically, yes; dynamically, not quite".
The hopper (Example 12.4.4). , , , , . Angular momentum is conserved, so velocities fill at most two dimensions: , the other products vanish, and is two-dimensional (not STLA) while completes : STLEC and equilibrium controllable. The library reports exactly this, and that — the hopper is maximally reducible to a kinematic system, Chapter 21's Theorem 12.4.5.
Nonholonomic constraints through the projection
DerivationThe knife-edge: Rusty with mass, force and torque
Step 1 — the projection. , (no sideways velocity), so has the upper block and a in the corner: it projects planar velocity onto the heading.
Step 2 — the inputs. A force along and a torque about : , , hence — the forward force projected onto the heading — and .
Step 3 — the connection. , so and ; projecting with gives . Both self products vanish. So : the knife-edge is maximally reducible — it is "secretly kinematic", the unicycle with velocity inputs, which is why Chapter 21 can steer it with Chapter 20's kinematic tools.
Step 4 — configurations. and , which vanishes at — there the projected force itself vanishes, and the distribution is not regular. One more bracket, , gives : STLCA and STLEC everywhere by Theorem 12.4.2.
In the library (with , so the numbers are not all ones): every field above
matches Choset's closed form to , at against the formula's
, ; larc on the reduction at
returns rank 2 and "undecided" at degree two and STLC at degree three — the LARC refusing to
certify the constrained system until it has the bracket that actually moves it.
The micro-example, by hand
Unicycle at . , . , and has one nonzero column, , so
The exact loop with moves against ; the gap, , is — half a cube is exactly.
Hitch's car (Choset eq. 12.34), , . , . Then , and , with . At the columns are and the determinant is ; the filtration climbs and the car is STLC by symmetry. At a generic configuration with the finite-difference agree with these formulas to .
With the trailer, state and , the ladder is : one more degree for one more coordinate, STLC again. The library finds the same ladder at hitch angle — the trailer's Lie algebra is full rank everywhere, as Laumond proved; the jackknife of Chapter 23 is not a loss of controllability, and the chart that does go singular at is Chapter 21's chained form.
The algorithm
Choset numbers none of these; the names match the Rust module.
- In
- two vector fields with Jacobians (analytic, or fourth-order central differences at h = 10⁻³), a point x
- Out
- [g₁, g₂](x) = (∂g₂/∂x) g₁ − (∂g₁/∂x) g₂
- , — analytic if the field has one, else per column
- return
- as a field: the closure step 2, whose own Jacobian falls back to differences of an exactly evaluated bracket — one difference level per nesting depth
- In
- generators g₀ (if drift), g₁ … g_m; the test point; the highest degree; a relative rank tolerance
- Out
- dims = (dim 𝒟₁, dim 𝒟₂, …), every product with its generator counts and good/bad tag, a greedy basis, terminated / stalled flags
- with counts ;
- for : for each and each new at the previous degree, form (at degree 2 only ); drop it if it vanishes at and at four probe points nearby; record counts and bad odd and all other counts even
- append of all values so far to dims; stop early if the rank is or nothing new was formed (closure certain); otherwise continue — a stall is not a stop for a non-regular filtration
- return dims, products, a greedy independent subset, and stalled = trailing degrees without growth
- In
- a control-affine system (drift or null, controls, control-set class), a point, a degree budget
- Out
- Verdict ∈ { stlc-symmetric, stlc-sussmann, stla, confined(dim), undecided } with the filtration and, for Sussmann, every bad bracket's neutralization residual
- filtration
- larc: if : driftless and ⇒ stlc-symmetric, else stla; if terminated below or stalled two degrees ⇒ confined(rank); else undecided
- sussmann: require and , else undecided
- the least degree at which the good products alone have rank (none ⇒ stla if the LARC holds)
- for every bad product of degree : residual relative least-squares residual against the good products of degree ; neutralized iff residual
- all neutralized ⇒ stlc-sussmann at degree ; else stla with the offending brackets named
- In
- a connection (kinetic, or constrained P∇), input fields Y₁ … Y_m on 𝒬, a configuration, degree budgets, the velocity dimension to fill (n_𝒬, or n_𝒬 − k under constraints)
- Out
- MechVerdict ∈ { stlc, stla, stlec, stlca, confined, undecided }, dim Sym, dim Lie(Sym), maximally-reducible flag, bad products with residuals
- the recursion , with at degree 2, counts added, bad iff all counts even; dims recorded at
- maximally reducible (Theorem 12.4.5, for Chapter 21)
- neutralized every bad product's residual against lower-degree good ones is
- if target and : neutralized ⇒ stlc, else stla (Theorem 12.4.1)
- else filtration of a basis of Sym (as fields) to lie_deg; and neutralized ⇒ stlec (and STLCC); alone ⇒ stlca; closed below ⇒ confined; else undecided (Theorem 12.4.2)
Implementation in Rust
The nonholonomic crate is introduced here; Chapter 21 adds steer/, lattice/, reduction/ and
car_grid.rs to it. Brackets, filtrations and symmetric products are hand-rolled per the book's policy;
nalgebra supplies the fixed-size vectors and the rank. The TypeScript port in web/lib/nonholonomic/
is the code behind every widget on this page.
use nalgebra::{SMatrix, SVector};
/// A smooth vector field on a chart of an N-dimensional state manifold.
///
/// Brackets need Jacobians, so the trait carries one. The default is a
/// fourth-order central difference with a deliberately *large* step: a nested
/// bracket differentiates a bracket, and a difference of a difference loses
/// half its digits per level if h is tiny. Concrete systems override it with
/// the analytic Jacobian, which is what makes degree-4 products trustworthy.
pub trait VectorField<const N: usize> {
fn eval(&self, x: &SVector<f64, N>) -> SVector<f64, N>;
fn jacobian(&self, x: &SVector<f64, N>) -> SMatrix<f64, N, N> {
central_difference(|y| self.eval(y), x, 1e-3)
}
}
/// A borrowed or boxed field is still a field, so `Bracket(&g1, &g2)` needs no clones
/// and the filtration can bracket `Box<dyn VectorField<N>>` generators.
impl<const N: usize, F: VectorField<N> + ?Sized> VectorField<N> for &F {
fn eval(&self, x: &SVector<f64, N>) -> SVector<f64, N> { (**self).eval(x) }
fn jacobian(&self, x: &SVector<f64, N>) -> SMatrix<f64, N, N> { (**self).jacobian(x) }
}
impl<const N: usize, F: VectorField<N> + ?Sized> VectorField<N> for Box<F> {
fn eval(&self, x: &SVector<f64, N>) -> SVector<f64, N> { (**self).eval(x) }
fn jacobian(&self, x: &SVector<f64, N>) -> SMatrix<f64, N, N> { (**self).jacobian(x) }
}
/// ∂f/∂x by the 5-point stencil (f(x−2h) − 8f(x−h) + 8f(x+h) − f(x+2h)) / 12h, per column.
pub fn central_difference<const N: usize>(
f: impl Fn(&SVector<f64, N>) -> SVector<f64, N>,
x: &SVector<f64, N>,
h: f64,
) -> SMatrix<f64, N, N> {
let mut j = SMatrix::<f64, N, N>::zeros();
for k in 0..N {
let mut e = SVector::<f64, N>::zeros();
e[k] = h;
let col = (f(&(x - 2.0 * e)) - 8.0 * f(&(x - e)) + 8.0 * f(&(x + e)) - f(&(x + 2.0 * e))) / (12.0 * h);
j.set_column(k, &col);
}
j
}
/// [g1, g2] = (∂g2/∂x) g1 − (∂g1/∂x) g2 — Choset eq. (12.5), same sign convention,
/// so the loop g1 → g2 → −g1 → −g2 moves by +ε²[g1, g2]. A `Bracket` is itself a
/// `VectorField`, so brackets nest: `Bracket(g1, Bracket(g1, g2))` is the car's g4.
pub struct Bracket<F, G>(pub F, pub G);
impl<const N: usize, F: VectorField<N>, G: VectorField<N>> VectorField<N> for Bracket<F, G> {
fn eval(&self, x: &SVector<f64, N>) -> SVector<f64, N> {
self.1.jacobian(x) * self.0.eval(x) - self.0.jacobian(x) * self.1.eval(x)
}
}
/// Choset's unicycle (Example 12.1.3): drive and spin, with analytic Jacobians.
pub struct Drive;
pub struct Spin;
impl VectorField<3> for Drive {
fn eval(&self, x: &SVector<f64, 3>) -> SVector<f64, 3> {
SVector::<f64, 3>::new(x[2].cos(), x[2].sin(), 0.0)
}
fn jacobian(&self, x: &SVector<f64, 3>) -> SMatrix<f64, 3, 3> {
let mut j = SMatrix::<f64, 3, 3>::zeros();
j[(0, 2)] = -x[2].sin();
j[(1, 2)] = x[2].cos();
j
}
}
impl VectorField<3> for Spin {
fn eval(&self, _x: &SVector<f64, 3>) -> SVector<f64, 3> {
SVector::<f64, 3>::new(0.0, 0.0, 1.0)
}
fn jacobian(&self, _x: &SVector<f64, 3>) -> SMatrix<f64, 3, 3> {
SMatrix::<f64, 3, 3>::zeros()
}
}
/// The four-flow loop of eq. (12.3) by RK4, returning x(4ε) − x₀ and the
/// prediction ε²[g1, g2](x₀); their difference is the O(ε³) remainder.
pub fn four_flow_loop<const N: usize>(
g1: &impl VectorField<N>,
g2: &impl VectorField<N>,
x0: &SVector<f64, N>,
eps: f64,
) -> (SVector<f64, N>, SVector<f64, N>) {
let mut x = *x0;
x = flow(g1, &x, eps);
x = flow(g2, &x, eps);
x = flow(g1, &x, -eps);
x = flow(g2, &x, -eps);
(x - x0, eps * eps * Bracket(g1, g2).eval(x0))
}The filtration follows Choset's recursion literally, with the generator counts that make Sussmann's tagging a counting exercise.
use nalgebra::SVector;
/// A Lie product with its provenance: which generators, how many times.
pub struct LieProduct<const N: usize> {
pub field: Box<dyn VectorField<N>>,
pub degree: usize,
/// counts[i] = occurrences of generator i; index 0 is the drift when there is one.
pub counts: Vec<usize>,
pub value: SVector<f64, N>,
/// Sussmann: bad iff the drift appears an odd number of times and every control an even number.
pub bad: bool,
}
pub struct Filtration<const N: usize> {
/// dim D₁, dim D₂, … — (2, 3, 4) for the car, (2, 3, 4, 5) with the trailer.
pub dims: Vec<usize>,
pub products: Vec<LieProduct<N>>,
/// Full rank, or nothing left to bracket: the closure is certain.
pub terminated: bool,
/// Trailing degrees that added no dimension. A stall is reported, never used to stop:
/// a non-regular filtration may pause and grow again (the knife-edge at q₃ = ±π/2).
pub stalled: usize,
}
pub fn filtration<const N: usize>(
fields: &[Box<dyn VectorField<N>>],
x: &SVector<f64, N>,
max_deg: usize,
drift_index: Option<usize>,
tol: f64,
) -> Filtration<N> {
let m = fields.len();
let mut products: Vec<LieProduct<N>> = (0..m)
.map(|i| {
let mut counts = vec![0; m];
counts[i] = 1;
LieProduct { field: fields[i].clone_box(), degree: 1, counts: counts.clone(),
value: fields[i].eval(x), bad: tag_bad(&counts, drift_index) }
})
.collect();
let mut dims = vec![rank(products.iter().map(|p| p.value), tol)];
let mut frontier: Vec<usize> = (0..m).collect();
let mut terminated = false;
for deg in 2..=max_deg {
let mut fresh = Vec::new();
for j in 0..m {
for &h in &frontier {
// Degree 2: [g_j, g_k] with j < k only — [g, g] = 0 and antisymmetry.
if deg == 2 && j >= products[h].counts.iter().position(|&c| c == 1).unwrap() { continue; }
let g = Bracket(fields[j].clone_box(), products[h].field.clone_box());
if vanishes_near(&g, x) { continue; } // identically zero, not merely at x
let mut counts = products[h].counts.clone();
counts[j] += 1;
let bad = tag_bad(&counts, drift_index);
fresh.push(LieProduct { value: g.eval(x), field: Box::new(g), degree: deg, counts, bad });
}
}
let start = products.len();
products.extend(fresh);
frontier = (start..products.len()).collect();
dims.push(rank(products.iter().map(|p| p.value), tol));
if *dims.last().unwrap() == N || frontier.is_empty() { terminated = true; break; }
}
let stalled = dims.windows(2).rev().take_while(|w| w[0] == w[1]).count();
Filtration { dims, products, terminated, stalled }
}
pub enum Verdict { StlcSymmetric { degree: usize }, StlcSussmann { degree: usize, neutralized: Vec<String> },
Stla { degree: usize }, Confined { dim: usize }, Undecided(String) }
/// Chow / LARC (Theorem 12.3.1) with the symmetry shortcut.
pub fn larc<const N: usize>(sys: &ControlAffine<N>, x: &SVector<f64, N>, max_deg: usize) -> Verdict {
let f = filtration(&sys.generators(), x, max_deg, sys.drift_index(), 1e-7);
let rank = *f.dims.last().unwrap();
let degree = f.dims.iter().position(|&d| d == N).map_or(0, |i| i + 1);
match (rank == N, sys.drift.is_none() && sys.control_set == ControlSet::PlusMinus) {
(true, true) => Verdict::StlcSymmetric { degree },
(true, false) => Verdict::Stla { degree },
(false, _) if f.terminated || f.stalled >= 2 => Verdict::Confined { dim: rank },
_ => Verdict::Undecided(format!("rank {rank} < {N} after degree {max_deg}")),
}
}
/// Sussmann (Theorem 12.3.3) at an equilibrium: good brackets span, bad ones are
/// neutralized by lower-degree good ones (relative least-squares residual < 1e-6).
pub fn sussmann_stlc<const N: usize>(sys: &ControlAffine<N>, x: &SVector<f64, N>, max_deg: usize) -> Verdict {
let Some(g0) = &sys.drift else { return larc(sys, x, max_deg) };
if g0.eval(x).norm() > 1e-8 { return Verdict::Undecided("g0(x) ≠ 0: only equilibria".into()); }
let f = filtration(&sys.generators(), x, max_deg, Some(0), 1e-7);
let good_rank_at = |deg| rank(f.products.iter().filter(|p| !p.bad && p.degree <= deg).map(|p| p.value), 1e-7);
let Some(k) = (1..=f.dims.len()).find(|&d| good_rank_at(d) == N) else {
return Verdict::Stla { degree: f.dims.len() };
};
let mut neutralized = Vec::new();
for p in f.products.iter().filter(|p| p.bad && p.degree <= k) {
let lower: Vec<_> = f.products.iter().filter(|q| !q.bad && q.degree < p.degree).map(|q| q.value).collect();
if relative_residual(&p.value, &lower) > 1e-6 { return Verdict::Stla { degree: k }; }
neutralized.push(p.label());
}
Verdict::StlcSussmann { degree: k, neutralized }
}The mechanical half imports Chapter 17's Model for , and the projection , and
differentiates only the input fields.
use dynamics::{Model, Pfaffian};
use nalgebra::{SMatrix, SVector};
/// ∇_{Y1} Y2 = (∂Y2/∂q) Y1 + M⁻¹ Y1ᵀ Γ Y2, Choset's convention for Γ (no M⁻¹ inside).
pub fn covariant<const Q: usize>(
y1: &dyn VectorField<Q>, y2: &dyn VectorField<Q>, model: &Model<Q>, q: &SVector<f64, Q>,
) -> SVector<f64, Q> {
let gamma = model.christoffel(q); // Q slices of Q×Q
let (v1, v2) = (y1.eval(q), y2.eval(q));
let mut form = SVector::<f64, Q>::zeros();
for i in 0..Q { form[i] = (v1.transpose() * gamma[i] * v2)[(0, 0)]; }
y2.jacobian(q) * v1 + model.inertia(q).try_inverse().unwrap() * form
}
/// ⟨Y1 : Y2⟩ = ∇_{Y1}Y2 + ∇_{Y2}Y1; with a Pfaffian constraint, P ∇ in place of ∇ (eq. 12.22).
pub fn symmetric_product<const Q: usize>(
y1: &dyn VectorField<Q>, y2: &dyn VectorField<Q>, model: &Model<Q>,
constraint: Option<&Pfaffian<Q>>, q: &SVector<f64, Q>,
) -> SVector<f64, Q> {
let s = covariant(y1, y2, model, q) + covariant(y2, y1, model, q);
match constraint {
None => s,
Some(c) => dynamics::projection_p(&model.inertia(q), &c.a(q)) * s,
}
}
/// Ỹ_i = P Y_i — the constrained inputs of §12.4.3.
pub fn constrained_inputs<const Q: usize>(
inputs: &[Box<dyn VectorField<Q>>], constraint: &Pfaffian<Q>, model: &Model<Q>,
) -> Vec<Box<dyn VectorField<Q>>> {
inputs.iter().map(|y| Box::new(Projected { y: y.clone_box(), constraint: constraint.clone(), model: model.clone() })
as Box<dyn VectorField<Q>>).collect()
}
pub enum MechVerdict { Stlc, Stla, Stlec, Stlca, Confined { dim: usize }, Undecided }
/// Theorems 12.4.1–12.4.2 from rest. `target` is n_Q, or n_Q − k under k constraints.
pub fn lewis_murray<const Q: usize>(
inputs: &[Box<dyn VectorField<Q>>], model: &Model<Q>, constraint: Option<&Pfaffian<Q>>,
q: &SVector<f64, Q>, max_deg: usize, lie_deg: usize, target: usize,
) -> MechVerdict {
let sym = sym_closure(inputs, model, constraint, q, max_deg); // same recursion, ⟨:⟩ for [,], j ≤ k at degree 2
let neutralized = sym.products.iter().filter(|p| p.bad).all(|p| {
let lower: Vec<_> = sym.products.iter().filter(|s| !s.bad && s.degree < p.degree).map(|s| s.value).collect();
relative_residual(&p.value, &lower) < 1e-6
});
if target == Q && sym.rank == Q {
return if neutralized { MechVerdict::Stlc } else { MechVerdict::Stla };
}
let lie = filtration(&sym.basis_fields(), q, lie_deg, None, 1e-7);
match (*lie.dims.last().unwrap() == Q, neutralized, lie.terminated) {
(true, true, _) => MechVerdict::Stlec,
(true, false, _) => MechVerdict::Stlca,
(false, _, true) => MechVerdict::Confined { dim: *lie.dims.last().unwrap() },
_ => MechVerdict::Undecided,
}
}The worked example, printed
cargo run --example chow_car -p nonholonomic prints the micro-example and the lab's verdicts. Every
line is reproduced by the TypeScript checks (npm run check, the nonholonomic: block) to the digits shown.
unicycle at x3 = pi/6:
[g1, g2] = [0.500000, -0.866025, 0.000000] (hand formula: identical)
loop residue, eps = 0.1 = [0.005424, -0.008396, 0.000000]
eps^2 [g1, g2] = [0.005000, -0.008660, 0.000000] gap 5.00e-4 = O(eps^3)
det[g1 g2 [g1,g2]] = 1.000000
log-log slope of |residue| over eps = 0.4 ... 0.025: 1.993 1.998 2.000 2.000
Hitch (eq. 12.34), L = 1, q = 0:
columns e1, e3, -e4, e2 det = -1.000000 dims = [2, 3, 4] Verdict::StlcSymmetric
generic q, L = 2.6: |g3 - hand| = 0.0e+0 |g4 - hand| = 2.3e-15 det = -0.189977 = -sec^4(phi)/L^2
with trailer (L = d = 1): dims = [2, 3, 4, 5] at q = 0 and at hitch angle pi/2 — regular everywhere
Example 12.1.7: interior StlcSymmetric; half-plane Confined { dim: 2 } dims [2,2,2,2,2]; axis Confined { dim: 1 }
PBWT, d = 1.3, a_g = 0: det[g1..g6] = 2.856100 = d^4; at rest StlcSussmann, [g2,[g0,g2]] = 2d g1 neutralized (1.4e-15)
u1 only: rank 2 (Confined); u2 only: rank 6 at degree 6, det of Choset's six columns = -130.5169 = -16 d^8, Stla
symmetric product: <Y1:Y2> = [d sin q3, -d cos q3, 0]; <Y2:Y2> = 2d Y1; Lewis-Murray Stlc
knife-edge, m = 2, I = 0.5: <Y~1:Y~2> = -(tan q3 / I) Y~1; Sym = span (maximally reducible)
det2 = 0.584984 = cos^2(q3)/(m^2 I^2) at q3 = 0.7; det3 = 8.000000 = 2/(m^2 I^4); Stlec
reduction LARC at q3 = pi/2: degree 2 rank 2 (undecided), degree 3 StlcSymmetric dims [1,2,3]
Reach 3R, u3 = 0, a_g = 0, q = (0.3, 1.1, -0.5): Sym dims 2 -> 3 (Stla); bad <Y1:Y1> residual 0.105, <Y2:Y2> 0.048
-> Lewis-Murray cannot certify STLC; STLKC does the planner's job in Chapter 21Putting it together
The Integration lab drops the chapter's tests on the lab's three systems.
Hitch, with and without the trailer. larc(carSystem(2.6), q) at every configuration with
returns STLC with the ladder — including rad, hard
against the steering stop, where Choset's remark that a car which can only turn one way reaches every
pose becomes a number. With the trailer the ladder is at hitch angle , at , and
at . This is a deviation from the design brief, which expected the ladder to stall at
; the computation does not stall and the literature agrees (the one-trailer system is STLC
everywhere). What is singular at is the trailer's velocity and
with it the chained-form coordinate change and the flat-output lift of Chapter 21 — the jackknife is a
planning and dynamics failure, not a controllability one, and Chapter 23 will say so.
Reach's 3R with the third motor dead, horizontal Workbench. Choset's §12.5.7 says the free third
link "is equivalent to the PBWT in zero gravity, except the thruster forces at the third joint are
generated by the actuators at the first two joints". Kinematically that is right, and Chapter 21 drives
the link like a car along its two decoupling fields. Dynamically the arm is not the free body: its two
input fields carry the arm's inertia, and the Lewis–Murray test at
returns (STLA from rest) with the bad products
and outside —
relative residuals and . Sussmann's condition is sufficient, not necessary, so the arm
may well be STLC; the test cannot say, and the chapter does not claim it. The design brief's expectation
of a printed Stlc is therefore not met, and the honest line in its place is: STLA, with STLKC — hence
STLEC — supplied by the kinematic reduction of the next chapter, which is exactly the property the
switch-minimizing planner needs.
Rusty with mass, the knife-edge: maximally reducible, so everything Part III did with Rusty's kinematic model was legitimate for the dynamic one too, and STLEC everywhere once the degree-three bracket is admitted.
Which leaves the question this chapter cannot answer and the next one is about: controllable — with what controls? The four-flow loop is a proof, not a plan. Chapter 21 spends the controllability established here six different ways.
Exercises
- Foundation exerciseDifficulty 2 of 3Reach's 2R with one motor, on the horizontal Workbench
Write Reach's 2R dynamics (Chapter 17, ) in the control-affine form on with : , . Argue that is WPPS from energy conservation on the compact , attempt the LARC for Theorem 12.3.5 (the library's
liftToStateSpaceandlarcwill do the brackets), and explain why the arm is not controllable with but . - Foundation exerciseDifficulty 2 of 3The car's ladder, symbolically, and the trailer's extra rung
Prove Choset's Problem 13 — the car (12.34) is STLC — by exhibiting the ladder : compute and by hand and show . Then add the trailer with for the hitch angle , show one more degree is needed, and locate the configurations where loses its -component.
det[g₁ g₂ g₃ g₄] for L = 1 at φ = π/4
- Conceptual exerciseDifficulty 2 of 3Predict the residue, then its errorPredict first
In the Parallel-Parker at ε = 0.2 with the unicycle pair, from heading π/6, the sideways (second) component of the exact residue is compared with ε²[g₁, g₂]₂ = −0.0346. Which is it?
- Conceptual exerciseDifficulty 2 of 3How far behind, how soon
In the Reachable Set Grower switch to the forward-only control set with both inputs. Find the smallest horizon for which a sampled state lies behind the start (), and relate it to the turning radius ( at unit speed and unit turn rate) and to the "controllable, not STLC" rung of the ladder.
- Practical exerciseDifficulty 2 of 3Reach's free third link as VectorField<6>
Implement
impl VectorField<6>for the planar body with thrusters of mass and inertia (Choset Problems 14–16) with the analytic Jacobians, build Choset's withBracket, and reproduce for in a test. Then show thatsussmann_stlcreturnsStlcSussmannat rest only when . - Practical exerciseDifficulty 3 of 3A Philip Hall basis, and a symbolic determinant (stretch)
Replace Choset's recursion in
filtrationby a Philip Hall basis so that only the independent products of each degree are formed; benchmark bracket evaluations for the car-with-trailer to degree five against the current implementation. Then add a small symbolic backend (polynomials in of the state) so that the car's is produced as an expression rather than a number.
References
- Choset, H., Lynch, K. M., Hutchinson, S., Kantor, G., Burgard, W., Kavraki, L. E., and Thrun, S. (2005) Principles of Robot Motion: Theory, Algorithms, and Implementations. MIT Press.link to Principles of Robot Motion: Theory, Algorithms, and Implementations (opens in a new tab)
Chapter 12, §12.1–12.4: the vector-field preliminaries, the four-flow derivation (eq. 12.3–12.5), Examples 12.1.1–12.1.8 and 12.4.3–12.4.4, 12.4.9, Theorems 12.3.1, 12.3.3, 12.3.5, 12.4.1–12.4.2, and Problems 1–29, which this chapter follows section by section. Chow's 1939 paper and the Lian–Wang–Fu recurrence theorem are cited there as refs. 112 and 289.
- Sussmann, H. J. (1987) A General Theorem on Local Controllability. SIAM Journal on Control and Optimization 25(1).doi:10.1137/0325011 (opens in a new tab)
The good-and-bad-bracket sufficient condition for small-time local controllability at an equilibrium, stated here as Theorem 12.3.3 and implemented as sussmannStlc with explicit neutralization residuals.
- Lewis, A. D. and Murray, R. M. (1997) Configuration Controllability of Simple Mechanical Control Systems. SIAM Journal on Control and Optimization 35(3).doi:10.1137/S0363012995287155 (opens in a new tab)
The symmetric product, the symmetric closure and the tests for STLA, STLC, STLCA, STLCC and equilibrium controllability from rest (Theorems 12.4.1–12.4.2), implemented as lewisMurray.
- Murray, R. M., Li, Z., and Sastry, S. S. (1994) A Mathematical Introduction to Robotic Manipulation. CRC Press.link to A Mathematical Introduction to Robotic Manipulation (opens in a new tab)
Chapter 7's treatment of nonholonomic systems — distributions, Frobenius, Chow, the Philip Hall basis and chained forms — is the standard long-form companion to Choset's §12.1–12.3 and the source of Exercise 6.
- Bullo, F. and Lewis, A. D. (2005) Geometric Control of Mechanical Systems: Modeling, Analysis, and Design for Simple Mechanical Control Systems. Springer, Texts in Applied Mathematics 49.doi:10.1007/978-1-4899-7276-7 (opens in a new tab)
The affine-connection formulation of mechanical control systems in full: covariant derivatives, the symmetric product, constrained connections, and the kinematic reductions Chapter 21 uses.
- Solà, J., Deray, J., and Atchuthan, D. (2018) A micro Lie theory for state estimation in robotics. arXiv:1812.01537.link to A micro Lie theory for state estimation in robotics (opens in a new tab)
The bridge from this chapter's vector-field brackets to the sister book's matrix Lie groups: the Lie bracket of left-invariant fields on SE(2) is the matrix commutator (up to Choset's sign), which is why the four-flow residue of Hitch's drive and spin is the se(2) bracket of their generators.
