Robot Motion
Chapter 21PART VDynamics, Trajectories, and ConstraintsDifficulty: AdvancedEstimated reading time: 85 min

Nonholonomic Systems II — Steering Cars, Trailers, and Underactuated Systems

Chapter 20 proved Hitch can reach every pose; this chapter is the catalog of ways to actually get there — Brockett's sinusoids and chained forms, gradient steering, differential flatness, Dubins and Reeds–Shepp words, Choset's CAR GRID SEARCH and its descendants (state lattices, hybrid A*), forward-propagation trees and Kinodynamic RRT*, and Reach's dead third joint driven like a car along decoupling vector fields.

This implies that it is possible to parallel-park your car into any parking space ϵ > 0 longer than your car.
Howie Choset, Kevin Lynch, Seth Hutchinson, George Kantor, Wolfram Burgard, Lydia Kavraki, and Sebastian ThrunPrinciples of Robot Motion (2005), Chapter 12

In this chapter

Chapter 20 proved that Hitch can reach every pose in the Lot. It did not say how, and the proof's own recipe — nested four-flow loops — is a terrible plan: each loop of amplitude ϵ\epsilon buys ϵ2\epsilon^2 of sideways motion, so a small sideways correction costs a great many reversals. This chapter is the catalog of better answers, and its one idea is that every steering method is a different way of spending the controllability you already own. You can solve an optimal-control problem exactly (Dubins, Reeds–Shepp). You can change coordinates until the brackets become a ladder you climb with sinusoids (chained forms). You can push on the Jacobian of the end-state map until the error vanishes (gradient steering). You can find two coordinates from which the whole state is read off by differentiation (flatness). Or you can let a search integrate the dynamics forward and never invert anything at all — Barraquand and Latombe's CAR GRID SEARCH, and the kinodynamic trees that descend from it.

None of these methods is "the" way to park a car, and the chapter's widgets are built to make that visible: one query, six fares. Exact methods ignore obstacles; search methods stop in a goal region; flat polynomials ignore the steering limit; sinusoids ignore everything except the chained coordinates and swing the trailer past jackknife. The second half of the chapter is about how today's autonomous vehicles recombine these pieces — state lattices and hybrid A* are Algorithm 22 with lattice-consistent primitives, one continuous state per cell, Reeds–Shepp shots and a dual heuristic; Kinodynamic RRT* is RRT* with an exact optimal-control steer. It ends with Reach's third motor switched off and the dead link driven like a car.

Honesty items kept throughout: Algorithm 22 is resolution complete, not exact; Reeds–Shepp and Algorithm 22 paths have curvature jumps a real steering actuator cannot follow; flatness handles neither control bounds nor obstacles without numerical optimization; forward propagation is metric-sensitive and offers no optimality without an exact steer; the decoupled planner stops at every switch and so forfeits global time-optimality; and the Θ(1/ϵ2)\Theta(1/\epsilon^2) cusp count for tight parking is a theorem, not a bug.

The problem: same destination, six fares

Hitch is at the south curb of the Lot, heading east. Bay 3 is up and to the left. Three copies of Hitch leave at once.

Figure One query, three planners from this chapter, replayed side by side (seed 21). Dotted: the Reeds–Shepp shortest path — four crisp segments, one cusp, exact, blind to obstacles. Solid: a quintic in the flat output — smooth, no cusp, a different length, blind to the steering limit. Dashed: a forward-propagated tree on Hitch's dynamic model — no steering function at all, collision-aware, ending somewhere inside a goal region. The scoreboard is computed live by planPark.

The three paths are all correct, and all different, because each one answers a different question. The Reeds–Shepp path is the shortest path for a car that may reverse; it has a cusp because stopping and backing up is free in its cost. The flat-output path is the simplest polynomial consistent with the two poses; it never stops, but nothing in its derivation stops it from asking for more steering than Hitch has. The tree path is the first thing a randomized search found by simulating Hitch; it is jittery and does not end exactly on the goal, but it is the only one of the three that looked at the pillars while planning. Chapter 20's controllability theorem guaranteed all three exist. It said nothing about which one you want, and the rest of this chapter is about making that choice deliberately.

Building intuition

One query, several optimality criteria

Start with the flagship. Hitch now pulls its trailer, and the scoreboard grows a column the planners were never asked about: how far the trailer swings.

Three observations, each a section below.

The trailer is a hidden cost. With the trailer on, the chained-form sinusoids reach the bay exactly — in the car's chained coordinates — while the trailer, simply towed along, swings far past ∣θ1∣=π/2|\theta_1| = \pi/2. The sinusoid method steers the four-state car; it does not know the trailer exists. The flat-output method with the trailer is different in kind: its flat output is the trailer's axle, so the trailer is parked by construction and the car's motion is derived from it.

Exactness and obstacle-awareness trade off. The Reeds–Shepp word and the sinusoids land on the goal pose to round-off and do not look at the pillars. Turn "obstacles" on and the third method becomes Algorithm 22, which looks at everything and stops anywhere within ±0.5\pm 0.5 m and ±0.2\pm 0.2 rad of the goal. Exact steering is an obstacle-free subroutine; search is how obstacles enter.

Control bounds are where each method is weakest. The flat planner's red max⁡∣ϕ∣\max|\phi| is the curvature its polynomial asks for, not what Hitch can do. The sinusoids ask for 1.221.22 rad of steering on the default query — twice Hitch's limit. Only the methods built from full-lock arcs (Reeds–Shepp, Algorithm 22) respect ∣ϕ∣≤0.6|\phi| \le 0.6 by construction.

Shortest paths have words

The shortest path between two poses of a car with a minimum turning radius ρ\rho is a short word in three letters: LL and RR for full-lock arcs, SS for straight. Forward-only (Dubins), six words suffice; allowed to reverse (Reeds–Shepp), a handful of families with cusps join them.

The tint is the synthesis: for the goal heading on the slider, every point of the plane is colored by the family of its shortest path. Drag the goal along a boundary and the word flips — but the length in the readout moves continuously, because the optimal length is a continuous function of the goal even though the optimal word is not. Toggle "Dubins only" and compare: near the start, behind it and beside it, the forward-only car pays enormously (a loop of nearly 2πρ2\pi\rho) for what the Reeds–Shepp car does with one cusp. The Reeds–Shepp length is never longer — the chapter's check confirms it on 400 random pairs — and reversing is not a Dubins word run backward: the reversing words are new words.

State from a curve

Here is a planner with no differential equation in it.

Drag the curve. Hitch is placed on it by differentiation alone: its heading is the curve's tangent, its steering angle is arctan⁡\arctan of the wheelbase times the curve's curvature. Nothing was integrated. That is differential flatness: the car has two outputs — the rear-axle midpoint — from which state and inputs are algebraic functions of finitely many derivatives. Turn on the trailer and the flat output moves to the trailer's axle; now the car's position is a derivative away (the trailer tangent tells you where the hitch is) and its steering is two more. Make a corner tight and part of the curve turns red: the curve is still a perfectly good flat output, but it asks for more curvature than 1/ρ1/\rho, and flatness alone has no way to say no.

A tree without a steering function

Every planner so far needed to invert the car — find controls that go from here to there. Choset's §7.5.1 points out that a tree planner needs much less: a way to propagate a state forward under a control.

With one sampled control per extension the tree drifts; with eight it reaches. Push the heading weight wθw_\theta up and the tree spends its effort matching headings of random samples instead of covering the Lot — the planner is only as good as its notion of "near", and the metric is a tuning knob, not a derived quantity. Turn on Kinodynamic RRT* and the tree changes model: rewiring compares the cost of reaching a node through two different parents, which needs the exact optimal cost between two states. For the kinematic car that is the Reeds–Shepp length; for Hitch's dynamic model nobody has a closed form, which is why the toggle switches systems rather than pretending.

Grid search, then and now

Barraquand and Latombe's 1990s answer to car planning among obstacles was a best-first search over six motion primitives — full left, straight, full right, forward and reverse — with an occupancy grid on (x,y,θ)(x, y, \theta) to stop it revisiting. Today's planners in autonomous vehicles are recognizably the same algorithm.

The left panel is a state lattice: the primitives were built so that every motion ends exactly on a lattice state, so the search is plain A* on a graph. The right panel is hybrid A*: the states float freely as in Algorithm 22, each grid cell keeps the best one, and every few expansions the planner tries an analytic Reeds–Shepp "shot" to the goal (the dashed purple fans are shots that hit something). Coarsen the grid: the lattice, whose primitives must land on coarse lattice points, takes long detours; hybrid A* barely notices because its states are continuous. Neither is new; both are Algorithm 22 plus a named change.

Driving a dead joint

The last widget leaves the Lot. Reach's three-link arm lies flat on the Workbench with its third motor switched off.

The free third link cannot be torqued. Chapter 20 showed the arm is nevertheless accessible from rest, and Choset's §12.5.7 shows how to use that: there are two motions the dead link can perform at any speed — sliding along its own length, and rotating about its center of percussion, the one point on the link that a force at the joint perpendicular to the link leaves momentarily unaccelerated. Drive with those two (forward and reverse each) and the link behaves like a car with four actions. The planner is Algorithm 22 with cost "number of switches"; the execution is Chapter 18's time-optimal scaling, segment by segment, coming to rest at every switch. In the inset one torque bar is always full.

Notation used in this chapter
SymbolMeaningNote
ρ=rmin⁡=L/tan⁡γ; v, ω\rho = r_{\min} = L/\tan\gamma;\ v,\ \omegaminimum turning radius; inputs of the reduced car (Choset eq. 12.35)|ω| ≤ |v|/ρ; Hitch: L = 2.6 m, γ = 0.6 rad, ρ = 3.80 m
L±, R±, S±L_\pm,\ R_\pm,\ S_\pmCAR GRID SEARCH actions: full left / right / straight, forward / reverseChoset Alg. 22
C, S, ∣, Ca, Cπ/2; (t,p,q)C,\ S,\ |,\ C_a,\ C_{\pi/2};\ (t, p, q)Reeds–Shepp alphabet: arc, straight, cusp, arc of angle a, quarter arc; normalized segment lengthsarcs in radians, straights in units of ρ
z=A(x)x, v=B(x)uz = A(x)x,\ v = B(x)uchained-form coordinates and inputsChoset §12.5.1–12.5.2
up, f(xstart,up), eu^p,\ f(x_{start}, u^p),\ efinite control parameterization, end-state map, end-state error§12.5.4
y, φ, ψuy,\ \varphi,\ \psi_uflat outputs and the maps x = φ(y, ẏ, …), u = ψ_u(y, ẏ, …)Choset writes ψ for the input map; here ψ is the trailer heading, so the input map is ψ_u
ψ, θ1=θ−ψ, d\psi,\ \theta_1 = \theta - \psi,\ dtrailer heading, hitch angle, hitch lengthChapter 2 integrates ψ; d = 2.5 m
Vi(q), V, wV_i(q),\ \mathcal{V},\ wdecoupling vector fields, a set of them, kinematic inputs§12.4.2
G(qgoal), prop(x,u,Δt)G(q_{goal}),\ \mathrm{prop}(x, u, \Delta t)goal region of Alg. 22; forward propagation of ẋ = f(x, u)§12.5.6, §7.5.1

The mathematics

Throughout, the steering problem is: given xstart\htmlClass{term-start}{x_{start}} and xgoal\htmlClass{term-goal}{x_{goal}}, find controls driving the system from one to the other, ignoring obstacles. It is exactly what PRM and RRT call a local planner. Choset splits the state of many systems into shape variables, driven directly by the inputs, and fiber variables, which change only when the shape variables move around closed loops — the astronaut who reorients by cycling her arms. Every exact method below is a way to engineer the right loop.

Brockett's sinusoids

The simplest system with a fiber is Choset's (12.23):

x˙1=u1,x˙2=u2,x˙3=x2u1,\dot x_1 = u_1, \qquad \dot x_2 = u_2, \qquad \dot x_3 = x_2 u_1,

with g1=[1,0,x2]⊤g_1 = [1, 0, x_2]^\top, g2=[0,1,0]⊤g_2 = [0, 1, 0]^\top and [g1,g2]=−e3[g_1, g_2] = -e_3. Shape (x1,x2)(x_1, x_2), fiber x3x_3.

DerivationPontryagin for the fiber
  1. Hamiltonian. H=12(u12+u22)+λ1u1+λ2u2+λ3x2u1H = \tfrac12(u_1^2 + u_2^2) + \lambda_1 u_1 + \lambda_2 u_2 + \lambda_3 x_2 u_1.
  2. Stationarity. ∂H/∂u=0\partial H/\partial u = 0 gives u1=−λ1−λ3x2u_1 = -\lambda_1 - \lambda_3 x_2 and u2=−λ2u_2 = -\lambda_2.
  3. Adjoint. λ˙=−∂H/∂x\dot\lambda = -\partial H/\partial x: λ˙1=0\dot\lambda_1 = 0, λ˙3=0\dot\lambda_3 = 0, λ˙2=−λ3u1\dot\lambda_2 = -\lambda_3 u_1.
  4. Close the loop. Differentiate step 2: u˙1=−λ3x˙2=−λ3u2\dot u_1 = -\lambda_3\dot x_2 = -\lambda_3 u_2 and u˙2=−λ˙2=λ3u1\dot u_2 = -\dot\lambda_2 = \lambda_3 u_1 — a rotation of (u1,u2)(u_1, u_2) at rate λ3\lambda_3, which integrates to the quadrature sinusoids.
  5. Return the shape. ∫01ui dt=0\int_0^1 u_i\,dt = 0 requires a whole number of periods: λ3=2kπ\lambda_3 = 2k\pi. Then J=12(u1(0)2+u2(0)2)J = \tfrac12(u_1(0)^2 + u_2(0)^2) for every kk, so pick ∣k∣=1|k| = 1. With u2(0)=0u_2(0) = 0, x2(t)=u1(0)(1−cos⁡2kπt)/(2kπ)x_2(t) = u_1(0)(1 - \cos 2k\pi t)/(2k\pi) and x3(1)=∫01x2u1 dt=−u1(0)2/(4kπ)x_3(1) = \int_0^1 x_2 u_1\,dt = -u_1(0)^2/(4k\pi): negative for k=1k = 1, positive for k=−1k = -1.

So to move the fiber by 0.30.3 choose k=−1k = -1 and any (u1(0),u2(0))(u_1(0), u_2(0)) on the circle of radius 4π⋅0.3=1.9416\sqrt{4\pi \cdot 0.3} = 1.9416, at cost J=0.6π≈1.885J = 0.6\pi \approx 1.885; the library's steerBrockett returns that circle's point on the u1u_1-axis, and integrating (12.23) under it lands on (0,0,0.3)(0, 0, 0.3) to round-off. The unicycle is this system in disguise: with Choset's

z1=x3,z2=x1cos⁡x3+x2sin⁡x3,z3=x1sin⁡x3−x2cos⁡x3,v1=ω,v2=v−z3ω,z_1 = x_3,\quad z_2 = x_1\cos x_3 + x_2 \sin x_3,\quad z_3 = x_1\sin x_3 - x_2\cos x_3,\qquad v_1 = \omega,\quad v_2 = v - z_3\omega,

the unicycle's (x˙1,x˙2,x˙3)=(vcos⁡x3,vsin⁡x3,ω)(\dot x_1, \dot x_2, \dot x_3) = (v\cos x_3, v\sin x_3, \omega) becomes z˙=(v1,v2,z2v1)\dot z = (v_1, v_2, z_2 v_1) — check it: z˙3=v(cos⁡x3sin⁡x3−sin⁡x3cos⁡x3)+(x1cos⁡x3+x2sin⁡x3)ω=z2v1\dot z_3 = v(\cos x_3\sin x_3 - \sin x_3\cos x_3) + (x_1\cos x_3 + x_2\sin x_3)\omega = z_2 v_1. The heading is a shape variable, the position along the heading is the other, and the sideways offset is the fiber. Brockett's cost is not a physical one for a car, which is Choset's own caveat.

Chained form and the sinusoid ladder

Generalize to nn states and two inputs (Choset eq. 12.28):

x˙=u1g1(x)+u2g2(x),g1=[1, 0, x2, x3, …, xn−1]⊤,g2=e2.\dot x = u_1 g_1(x) + u_2 g_2(x),\qquad g_1 = [1,\ 0,\ x_2,\ x_3,\ \dots,\ x_{n-1}]^\top,\qquad g_2 = e_2 .
DerivationWhy the k-th cycle touches only x_{k+2} and above
  1. The ladder. [g1,g2]=∂g2∂xg1−∂g1∂xg2=−∂g1∂xe2=−e3[g_1, g_2] = \tfrac{\partial g_2}{\partial x}g_1 - \tfrac{\partial g_1}{\partial x}g_2 = -\tfrac{\partial g_1}{\partial x}e_2 = -e_3, because g2g_2 is constant and the only entry of g1g_1 depending on x2x_2 is the third. Inductively [g1,ej]=−ej+1[g_1, e_j] = -e_{j+1}, so adg1kg2=(−1)kek+2\mathrm{ad}^k_{g_1}g_2 = (-1)^k e_{k+2} and {g1,g2,adg2,…,adn−2g2}\{g_1, g_2, \mathrm{ad}g_2, \dots, \mathrm{ad}^{n-2}g_2\} spans Rn\mathbb{R}^n — Chapter 20's lieBracket confirms the signs to better than 10−1010^{-10} at a random point for n=5n = 5.
  2. Linearity in the initial state. Given u1(t)u_1(t), the chain x˙j=xj−1u1\dot x_j = x_{j-1}u_1 is linear. A contribution of xm(0)x_m(0) to xj(t)x_j(t) is xm(0) s(t)j−m/(j−m)!x_m(0)\,s(t)^{j-m}/(j-m)! with s(t)=∫0tu1s(t) = \int_0^t u_1 — a polynomial in ss, and s(1)=0s(1) = 0, so every such contribution returns.
  3. The forced part. What remains is driven by w(t)=∫0tu2=b2kπsin⁡2kπtw(t) = \int_0^t u_2 = \tfrac{b}{2k\pi}\sin 2k\pi t. Each pass down the chain multiplies by u1=asin⁡2πtu_1 = a\sin 2\pi t and integrates; by the product-to-sum identities a frequency-2kπ2k\pi signal multiplied kk times by a frequency-2π2\pi one contains a zero-frequency (DC) term only at the kk-th multiplication, i.e. in x˙k+2\dot x_{k+2}. States above xk+2x_{k+2} also drift, which is why the ladder is climbed upward.
  4. The constant. Tracking the DC coefficient through the kk multiplications gives (a/4π)kb/k!(a/4\pi)^k b/k! (Murray and Sastry's computation). The check runs the five-state ladder to (1,−0.5,0.3,0.2,−0.4)(1, -0.5, 0.3, 0.2, -0.4) and finds every cycle's increment equal to (12.31) to better than 10−1010^{-10} — integration error, not formula error.

Hitch's four-state car q=(x,y,ϕ,θ)q = (x, y, \phi, \theta) has a chained form too (Murray and Sastry):

z=(x′, tan⁡ϕLcos⁡3θ′, tan⁡θ′, y′),v1=vcos⁡θ′,z = \Bigl(x',\ \frac{\tan\phi}{L\cos^3\theta'},\ \tan\theta',\ y'\Bigr),\qquad v_1 = v\cos\theta',

where the primes are coordinates in a chart rotated by an angle β\beta. In the original chart the transformation is singular at θ=±π/2\theta = \pm\pi/2 — which is exactly where a parking query into a north-facing bay ends. The library sets β\beta to the bisector of the start and goal headings, so both ends sit inside (−π/2,π/2)(-\pi/2, \pi/2); inside the motion θ′=arctan⁡z3\theta' = \arctan z_3 never reaches ±π/2\pm\pi/2 by construction. On w21.1's default query, (5.5,1.2,0)→(9.25,6.4,π/2)(5.5, 1.2, 0) \to (9.25, 6.4, \pi/2), two sinusoid cycles land on the goal in chained coordinates to better than 10−910^{-9} — with three cusps and a peak steering angle of 1.221.22 rad, twice what Hitch can do. The method is exact in its coordinates and blind to everything else. Car-and-trailer systems with any number of trailers also have chained forms (Sørdalen's result, Choset ref. 393), at the price of coordinate changes that blow up at the jackknife.

Exact methods need structure. Choset's §12.5.4 needs only a simulator: parameterize the control history by a finite vector up∈Rru^p \in \mathbb{R}^r (here, piecewise-constant (v,ω)(v, \omega) on K=6K = 6 equal intervals of [0,1][0, 1] for the reduced car), call f(xstart,up)f(x_{start}, u^p) the end-state map, and descend the error e=f(xstart,up)−xgoale = f(x_{start}, u^p) - x_{goal}.

DerivationDescent, rank, and the generic loop
  1. Descent. With J=∂f/∂upJ = \partial f/\partial u^p, 12∥e(up+αv)∥2=12∥e∥2−α∥J⊤e∥2+O(α2)\tfrac12\|e(u^p + \alpha v)\|^2 = \tfrac12\|e\|^2 - \alpha\|J^\top e\|^2 + O(\alpha^2), so the step decreases the error unless J⊤e=0J^\top e = 0.
  2. Rank. If rank J=n\mathrm{rank}\,J = n then J⊤J^\top is injective and J⊤e=0J^\top e = 0 forces e=0e = 0: the only stationary point is the goal. Any error direction is correctable by a small change of upu^p.
  3. The singular control. At up=0u^p = 0 every column of JJ is a first-order variation — a combination of g1g_1 and g2g_2 at xstartx_{start} — so rank J=2<3\mathrm{rank}\,J = 2 < 3 and the sideways error is invisible. A driftless system retraces itself under a control followed by its time-reversed negative, so appending such a pair changes nothing about where the car ends and everything about the Jacobian.

For a pure sideways move of 0.50.5 m from rest the library's steerGradient finds rank 22 at up=0u^p = 0, appends a two-piece generic loop (rank 33), and then needs 5858 backtracking gradient steps to bring ∥e∥\|e\| below 10−410^{-4} — slow, because the sideways direction is a bracket direction and the Jacobian's singular value along it is small; Chapter 20's ϵ2\epsilon^2 reappears as conditioning. Hand the same end-state map to Chapter 19's Levenberg–Marquardt — §12.5.3's "nonlinear optimization", with no re-derivation needed — and five iterations reach round-off. Choset's two caveats carry over unchanged: a good initial guess is required, and nothing guarantees convergence; here the generic loop is the initial guess that makes it work.

Differential flatness

DerivationHeading from the tangent, steering from curvature, the car from the trailer
  1. Heading. No slip means the rear axle's velocity points along the body: y˙=v(cos⁡θ,sin⁡θ)\dot y = v(\cos\theta, \sin\theta). Read θ\theta and ∣v∣|v| off y˙\dot y; the sign of vv is a choice that must stay consistent with θ\theta (Choset's warning: y˙=(1,1)\dot y = (1, 1) and (−1,−1)(-1, -1) give the same atan).
  2. Turn rate and curvature. ω=θ˙=ddtatan2⁡(y˙2,y˙1)=(y˙1y¨2−y¨1y˙2)/∥y˙∥2\omega = \dot\theta = \frac{d}{dt}\operatorname{atan2}(\dot y_2, \dot y_1) = (\dot y_1\ddot y_2 - \ddot y_1\dot y_2)/\|\dot y\|^2, the cross product over speed squared; κ=ω/v\kappa = \omega/v is that over speed cubed, and the front-wheel geometry gives tan⁡ϕ=Lκ\tan\phi = L\kappa.
  3. The trailer. By (12.36)–(12.37) the trailer axle moves along the trailer's heading, so ψ=atan2⁡(y˙)\psi = \operatorname{atan2}(\dot y) when yy is the trailer axle; the hitch — the car's rear axle — sits at p=y+d(cos⁡ψ,sin⁡ψ)p = y + d(\cos\psi, \sin\psi). Differentiate: p˙=y˙+dψ˙(−sin⁡ψ,cos⁡ψ)\dot p = \dot y + d\dot\psi(-\sin\psi, \cos\psi) and p¨=y¨+dψ¨(−sin⁡ψ,cos⁡ψ)−dψ˙2(cos⁡ψ,sin⁡ψ)\ddot p = \ddot y + d\ddot\psi(-\sin\psi, \cos\psi) - d\dot\psi^2(\cos\psi, \sin\psi), where ψ˙\dot\psi is step 2 applied to yy and ψ¨\ddot\psi needs y...\dddot y.
  4. Apply steps 1–2 to pp. The car's heading needs p˙\dot p (two derivatives of yy), its steering p¨\ddot p (four). One more pair of derivatives per body: "differentiate once more per trailer". Every formula divides by a speed, so the lift is singular wherever the flat output stops.

Choset's Example 12.5.1 fits y1=ty_1 = t, y2=3t2−2t3y_2 = 3t^2 - 2t^3 between (0,0,0)(0, 0, 0) and (1,1,0)(1, 1, 0); the library's Hermite fit with unit end speed returns exactly those polynomials. At t=0.25t = 0.25: y=(0.25,0.15625)y = (0.25, 0.15625), y˙=(1,1.125)\dot y = (1, 1.125), y¨=(0,3)\ddot y = (0, 3), so

θ=atan2⁡(1.125,1)=0.844154 rad (48.37°),v=2.265625=1.505199,ω=32.265625=1.324138,\theta = \operatorname{atan2}(1.125, 1) = 0.844154\ \text{rad}\ (48.37°),\quad v = \sqrt{2.265625} = 1.505199,\quad \omega = \frac{3}{2.265625} = 1.324138,

κ=ω/v=0.879709\kappa = \omega/v = 0.879709, and for wheelbase L=1L = 1 the steering angle is ϕ=arctan⁡0.879709=0.721491\phi = \arctan 0.879709 = 0.721491 rad (41.34°41.34°). At t=0.5t = 0.5 the curve inflects and ω=ϕ=0\omega = \phi = 0 exactly. The chapter's check goes one step further: it takes the controls (v(t),ϕ(t))(v(t), \phi(t)) read off a quintic flat output, integrates the true car (and car-with-trailer) ODE forward from the lifted initial state, and finds the integrated state agreeing with the lifted one along the whole trajectory to better than 10−1110^{-11}. Flatness is not an approximation.

What it is not, either, is a planner with constraints. A polynomial of minimal degree has no freedom left to respect ∣ϕ∣≤γ|\phi| \le \gamma or avoid a pillar; extra coefficients plus a numerical optimizer can, and Choset calls that "a topic of current research". On w21.1's trailer query the trailer-axle quintic asks for ∣ϕ∣=1.39|\phi| = 1.39 rad and clips an obstacle.

Shortest paths: Dubins and Reeds–Shepp

DerivationFrom Pontryagin to six words, and LSL in closed form
  1. Bang-bang or singular. Minimize time with v∈[−1,1]v \in [-1, 1], ω=vκ\omega = v\kappa, ∣κ∣≤1/ρ|\kappa| \le 1/\rho. The Hamiltonian is linear in κ\kappa, so κ\kappa sits at ±1/ρ\pm 1/\rho (bang) except on intervals where its switching function vanishes identically (singular), which forces κ=0\kappa = 0.
  2. Arcs and straights. Optimal paths are concatenations of full-lock arcs CC and straights SS — exactly CAR GRID SEARCH's primitives, which is why Algorithm 22 loses nothing essential by discretizing the steering.
  3. Case analysis. Reeds and Shepp's argument bounds the number of segments and the arc angles between cusps, leaving the nine families; with v≥0v \ge 0 forced there are no cusps and only CSCCSC and CCCCCC remain — Dubins' six words.
  4. Normalize. Put the start at the origin, the goal at (d,0)(d, 0) with d=∥q1−q0∥xy/ρd = \|q_1 - q_0\|_{xy}/\rho, and measure both headings from the bearing of the goal: α=θ0−bearing\alpha = \theta_0 - \text{bearing}, β=θ1−bearing\beta = \theta_1 - \text{bearing}.
  5. LSL. The start's left circle has center c0=(−sin⁡α,cos⁡α)c_0 = (-\sin\alpha, \cos\alpha), the goal's c1=(d−sin⁡β,cos⁡β)c_1 = (d - \sin\beta, \cos\beta). Two equal circles turning the same way are joined by their external tangent, parallel to c1−c0c_1 - c_0 and of the same length: p2=∥c1−c0∥2=(d−sin⁡β+sin⁡α)2+(cos⁡β−cos⁡α)2p^2 = \|c_1 - c_0\|^2 = (d - \sin\beta + \sin\alpha)^2 + (\cos\beta - \cos\alpha)^2, which expands to the boxed formula. The tangent's direction is atan2⁡(cos⁡β−cos⁡α, d+sin⁡α−sin⁡β)\operatorname{atan2}(\cos\beta - \cos\alpha,\ d + \sin\alpha - \sin\beta); the first arc turns from α\alpha to it (tt), the last from it to β\beta (qq), both taken mod 2π2\pi.
  6. The other five. Reflect (y→−yy \to -y, headings negated) and LSL becomes RSR. Reverse time and read the word backward and LSR and RSL swap; the internal tangent replaces the external one for the mixed words (p2=d2−2+2cos⁡(α−β)+2d(sin⁡α+sin⁡β)p^2 = d^2 - 2 + 2\cos(\alpha - \beta) + 2d(\sin\alpha + \sin\beta) for LSR, infeasible when negative), and three mutually tangent circles give RLRRLR and LRLLRL, infeasible when the circles are more than 4ρ4\rho apart.

The micro-example. q0=(0,0,0)q_0 = (0, 0, 0), q1=(3,3,π/2)q_1 = (3, 3, \pi/2), ρ=1\rho = 1. The start's left circle is centered at (0,1)(0, 1), the goal's at (3,3)+(−sin⁡π2,cos⁡π2)=(2,3)(3, 3) + (-\sin\tfrac\pi2, \cos\tfrac\pi2) = (2, 3); the centers are 222\sqrt2 apart along direction π/4\pi/4, so LSLLSL has t=π/4t = \pi/4, p=22p = 2\sqrt2, q=π/4q = \pi/4 and length π/2+22=4.39922\pi/2 + 2\sqrt2 = 4.39922. By integration: after the first arc Hitch is at (0.70711,0.29289)(0.70711, 0.29289) heading π/4\pi/4; the straight adds (2,2)(2, 2); the last arc about (2,3)(2, 3) ends at (3,3)(3, 3) heading π/2\pi/2. The others are longer — LSR=RSL=10.56698LSR = RSL = 10.56698, LRL=10.99557LRL = 10.99557, RSR=16.65243RSR = 16.65243 — and RLRRLR is infeasible. Reeds–Shepp returns the same L+S+L+L^+S^+L^+: here a cusp cannot help.

Reeds–Shepp by symmetry. Nobody codes forty-eight formulas. Reeds and Shepp's §8 gives a handful of base formulas — L+S+L+L^+S^+L^+, L+S+R+L^+S^+R^+, L+R−LL^+R^-L, L+Ra+La−R−L^+R^+_aL^-_aR^-, L+Ra−La−R+L^+R^-_aL^-_aR^+, L+Rπ/2−S−L−L^+R^-_{\pi/2}S^-L^-, L+Rπ/2−S−R−L^+R^-_{\pi/2}S^-R^-, L+Rπ/2−S−Lπ/2−R+L^+R^-_{\pi/2}S^-L^-_{\pi/2}R^+ — and three symmetries of the normalized goal (x,y,φ)(x, y, \varphi) generate the rest:

time-flip: (x,y,φ)↦(−x,y,−φ),reflect: (x,y,φ)↦(x,−y,−φ),backwards: (x,y,φ)↦(xcos⁡φ+ysin⁡φ, xsin⁡φ−ycos⁡φ, φ).\text{time-flip: } (x, y, \varphi) \mapsto (-x, y, -\varphi),\qquad \text{reflect: } (x, y, \varphi) \mapsto (x, -y, -\varphi),\qquad \text{backwards: } (x, y, \varphi) \mapsto (x\cos\varphi + y\sin\varphi,\ x\sin\varphi - y\cos\varphi,\ \varphi).

Time-flip negates every segment (L+↔L−L^+ \leftrightarrow L^-), reflect swaps LL and RR, backwards reads the word right to left. The library evaluates eleven base formulas (four of them backwards readings) under the four flip/reflect combinations — forty-four evaluations, the construction OMPL's ReedsSheppStateSpace uses. Several base formulas leave the sign of their last segment free, so the forty-four evaluations cover the forty-eight words; the module does not prove that itself, and the check tests the two consequences that would fail if it were false. On 400 seeded pairs, every one of the 2,700 candidate words integrates to its goal to round-off (worst error 10−1410^{-14}), and the shortest Reeds–Shepp length never exceeds the shortest Dubins length (Dubins paths are Reeds–Shepp candidates, so a missing family or a mistyped formula would show up as a violation). All nine families appear among the optimal words. The Reeds–Shepp length is also symmetric — d(q0,q1)=d(q1,q0)d(q_0, q_1) = d(q_1, q_0) — because reversing time maps a path to a path; that is what makes it a metric on SE(2)SE(2), and the reason PRM can use it as an undirected local planner while a Dubins-steered PRM needs directed edges.

Local planners with the topological property

DerivationSTLC, the topological property, and compactness
  1. The topological property (Choset Fig. 12.28). For every ball Bδ(q)B_\delta(q) there is a ball Bϵ(q)B_\epsilon(q) such that the local planner's path from qq to any q′∈Bϵ(q)q' \in B_\epsilon(q) stays inside Bδ(q)B_\delta(q). Reeds–Shepp has it because the car is STLC and the shortest path between close poses is short: its length is O(∥q−q′∥1/2)O(\|q - q'\|^{1/2}) in the sub-Riemannian sense, so it cannot wander far.
  2. Uniformity. The free-flying path is compact and at positive clearance δ\delta from obstacles; by compactness one ϵ\epsilon works along its whole length.
  3. Bisection terminates. Halve the parameter interval until consecutive samples are within ϵ\epsilon; each Reeds–Shepp piece then stays in a δ\delta-ball around a free configuration, hence is free.
  4. PRM. Chapter 11's completeness proof covered the path with a chain of balls and needed only that the local planner connects samples in consecutive balls; it never used that the planner's path is a straight line, nor that it is symmetric. The cusp count: each reversal of a car of length ℓ\ell in a slot of length ℓ+ϵ\ell + \epsilon gains O(ϵ2)O(\epsilon^2) of sideways motion (Chapter 20's bracket), so a fixed sideways offset needs Θ(1/ϵ2)\Theta(1/\epsilon^2) of them.

The library implements both: ReedsSheppLocal and DubinsLocal satisfy Chapter 11's Steer<Pose2> interface (symmetric and not, respectively), and pathTransform is Laumond's bisection. Their price is the next honesty item: the curvature jumps at every arc–straight junction, so either the steering wheel turns instantaneously, the car stops at each junction, or execution has error. Smoothed primitives — clothoids, or flat-output polynomials — are the standard repair.

DerivationTermination, optimality, and the bucketed OPEN list
  1. Termination. A node is expanded only if its cell has not been marked; there are d3d^3 cells, so at most d3d^3 expansions and 6d36d^3 children. The step must be long enough to leave the current cell, and the goal region must be larger than a cell — otherwise the search can loop inside a cell or jump over the goal.
  2. Best-first on integer costs is Dijkstra. With nonnegative integer a,b,ca, b, c, every edge cost is a nonnegative integer, and popping the cheapest open node is Chapter 6's Dijkstra on the tree. Testing the goal at pop time (line 4), not at generation, is what makes the returned path the cheapest one: Choset's Fig. 12.25 has a node in the goal region waiting in OPEN behind a cheaper one.
  3. Constant-time insertion. Integer costs index an array of FIFO buckets; insertion is an append, and the pop scans forward from the lowest non-empty bucket, which only ever moves up.
  4. Full lock suffices. By the Pontryagin argument of the previous section, optimal paths between cusps are full-lock arcs and straights; Barraquand and Latombe show more — that any pp-cusp path is approximable by a pp-cusp full-lock path — so minimizing cusps with a=b=0a = b = 0 loses nothing to the discretization of steering.
f21.7CAR GRID SEARCH tree and OPEN list
blockq0: start, cost 0 — expandedq0q1: L+, cost 6 — in OPENq1 L+ (6)q2: S+, cost 1 — expandedq2 S+q3: R+, cost 6 — motion collides, not in OPENq3 R+q4: L+, cost 7 — in OPENq4 L+ (7)q5: S+, cost 2 — expandedq5 S+q6: R+, cost 7 — motion collides, not in OPENq6 R+q7: L+, cost 8 — in OPENq7 L+ (8)q8: S+, cost 3 — in OPENq8 S+ (3)q9: R+, cost 8 — motion collides, not in OPENq9 R+popped: q0, q2, q5OPEN = { q8 (3), q1 (6), q4 (7), q7 (8) }
Algorithm 22 on forward actions only, cost 1 per motion and 5 per steering change, wheels straight at q0. Filled nodes have been popped and expanded, hollow ones wait in OPEN with their cost, gray dashed motions hit the block and never enter OPEN. The next pop is the cheapest — three straight steps (cost 3) — even though a left turn reached farther: OPEN is sorted by cost, and with integer costs it is an array of FIFO buckets. Choset's Fig. 12.25 shows the same bookkeeping on a different tree (costs 3, 7, 7, 7, 8).

Choset's description includes two practicalities the implementation keeps. The occupancy grid is checked when a node is popped (line 7), so a cell can be reached several times but expanded once; and with a trailer the grid gains a fourth axis for the hitch angle, the trailer's change over one action being "numerically integrated, or stored in a lookup table" — here by Chapter 2's Hitch.step, whose RK4 trailer integration is the lookup table computed on demand. On the Lot's reverse-parking query the search with a=1,c=0a = 1, c = 0 and the search with c=300c = 300 both find a 20-step path with one cusp; the second expands half as many nodes, because the cusp penalty prunes every branch that reverses early. Choset's remark that the planner "actually runs faster in cluttered spaces because the obstacles prune the search tree" is the same effect from the other side.

Forward propagation

DerivationA tube, a chain of cross-sections, and the Voronoi bias
  1. A tube. Take a feasible trajectory reaching the interior of GG with clearance. Lipschitz continuity of prop\mathrm{prop} in xx (and continuity in uu) gives a tube of nearby trajectories: from any state within rir_i of the ii-th waypoint, some open set of controls lands within ri+1r_{i+1} of the next.
  2. Cross-sections. Discretize the trajectory into KK steps of Δt\Delta t and ball BiB_i around each waypoint.
  3. One step has positive probability. If the tree has a node in BiB_i, an extension succeeds in placing one in Bi+1B_{i+1} with probability at least p>0p > 0: the random sample lands in the Voronoi region of that node with positive probability (Chapter 12's bias), and some sampled control falls in the open good set with positive probability.
  4. Chain. The event "progress from BiB_i to Bi+1B_{i+1} within mm extensions" fails with probability at most (1−p)m(1 - p)^m; multiply over KK stages and let m→∞m \to \infty.
  5. What the proof never used. Symmetry of the edge relation, reflexivity, or any ability to steer to a state — the theorem holds for the asymmetric, forward-only relation "some uu drives xx to x′x' in Δt\Delta t".

Two honesty items follow from the proof. The constant pp depends on the metric through step 3 — a metric that ranks a node with the wrong heading as "near" wastes extensions, which is the w21.5 slider — and nothing in the proof bounds path quality. On the Lot query from w21.1, Hitch's dynamic model (acceleration and steering-rate inputs, speed and steering angle in the state) with best-of-eight controls and Δt=0.8\Delta t = 0.8 s first lands in the goal region at extension 106 for seed 21, and re-running the simulator on the stored controls reproduces every state bit for bit. Time can be added to the state to handle moving obstacles (Choset ref. 195); the theorem does not change.

Kinodynamic RRT*: optimality needs a steer

Chapter 13's RRT* is asymptotically optimal because its rewiring step asks, for a node vv near a new node xx, whether reaching vv through xx is cheaper — a question about the exact optimal cost c∗(x,v)c^*(x, v) and the trajectory achieving it. Forward propagation cannot answer it. Webb and van den Berg's Kinodynamic RRT* answers it for linear dynamics x˙=Ax+Bu\dot x = Ax + Bu with cost ∫0τ(1+u⊤Ru) dt\int_0^\tau (1 + u^\top R u)\,dt: for a fixed arrival time τ\tau the minimum-energy cost is

c(τ)=τ+(x1−eAτx0)⊤G(τ)−1(x1−eAτx0),G(τ)=∫0τeA(τ−t)BR−1B⊤eA⊤(τ−t) dt,c(\tau) = \tau + \bigl(x_1 - e^{A\tau}x_0\bigr)^\top G(\tau)^{-1}\bigl(x_1 - e^{A\tau}x_0\bigr),\qquad G(\tau) = \int_0^\tau e^{A(\tau - t)}BR^{-1}B^\top e^{A^\top(\tau - t)}\,dt,

and the free final time is τ∗=arg⁡min⁡τc(τ)\tau^* = \arg\min_\tau c(\tau). For the planar double integrator with R=rIR = rI each axis gives G(τ)=1r[τ3/3τ2/2τ2/2τ]G(\tau) = \tfrac1r\left[\begin{smallmatrix}\tau^3/3 & \tau^2/2\\ \tau^2/2 & \tau\end{smallmatrix}\right] and c(τ)=τ+r∑(12Δp2/τ3−12Δp Δv/τ2+4Δv2/τ)c(\tau) = \tau + r\sum(12\Delta p^2/\tau^3 - 12\Delta p\,\Delta v/\tau^2 + 4\Delta v^2/\tau) with Δp=p1−p0−v0τ\Delta p = p_1 - p_0 - v_0\tau, Δv=v1−v0\Delta v = v_1 - v_0. From (0,0,1,0)(0, 0, 1, 0) to (2,1,0,0.5)(2, 1, 0, 0.5) with r=0.5r = 0.5 the library finds τ∗=2.3893\tau^* = 2.3893 s at cost 3.00753.0075, and integrating the closed-form optimal control u(t)=R−1B⊤eA⊤(τ−t)G(τ)−1du(t) = R^{-1}B^\top e^{A^\top(\tau - t)}G(\tau)^{-1}d lands on the target to round-off. For the reduced kinematic car the exact steer is Reeds–Shepp, which is what w21.5's toggle uses; Hitch's dynamic model has neither, and Exercise 6 asks you to linearize it about rest and use the LQR edge anyway.

Kinematic reductions and decoupling vector fields

Chapter 20 left Reach's dead-motor arm with an uncomfortable verdict: accessible from rest, with Lewis–Murray unable to certify STLC. §12.4.2 changes the question from "can it get anywhere?" to "can I plan it like a kinematic system?".

DerivationWhy V and ∇_V V must both be actuated
  1. Substitute. Along q˙=V(q)w\dot q = V(q)w, q¨=Vw˙+∂V∂qVw2\ddot q = V\dot w + \tfrac{\partial V}{\partial q}V w^2 (Choset 12.16–12.17).
  2. Covariant form. ∇q˙q˙=q¨+M−1q˙⊤Γq˙=Vw˙+(∂V∂qV+M−1V⊤ΓV)w2=Vw˙+∇VV w2\nabla_{\dot q}\dot q = \ddot q + M^{-1}\dot q^\top\Gamma\dot q = V\dot w + \bigl(\tfrac{\partial V}{\partial q}V + M^{-1}V^\top\Gamma V\bigr)w^2 = V\dot w + \nabla_V V\,w^2.
  3. Feasibility for all speed profiles. The left side must equal ∑Yiui\sum Y_i u_i for some uu, for every ww and w˙\dot w independently: the w˙\dot w term forces V∈span(Y)V \in \mathrm{span}(\mathcal{Y}), the w2w^2 term forces ∇VV∈span(Y)\nabla_V V \in \mathrm{span}(\mathcal{Y}). Conversely, both memberships let you solve for uu.
  4. Finding them. Write V=∑hi(q)YiV = \sum h_i(q)Y_i and solve ⟨Xc,∇VV⟩=0\langle X_c, \nabla_V V\rangle = 0 for the MM-orthogonal complement XcX_c of span(Y)(\mathcal{Y}) — quadratic equations in the hih_i. Then plan in Q\mathcal{Q} and time-scale each segment with Chapter 18.

For the planar body with thrusters Choset finds V1=Y1V_1 = Y_1 (translation along the thrust line) and V2=Y2V_2 = Y_2 (rotation about the center of percussion at distance 1/d1/d), with ∇V1V1=0\nabla_{V_1}V_1 = 0 and ∇V2V2∈span(Y1)\nabla_{V_2}V_2 \in \mathrm{span}(Y_1); but ⟨Y1:Y2⟩∉span(Y)\langle Y_1 : Y_2\rangle \notin \mathrm{span}(\mathcal{Y}), so V1+V2V_1 + V_2 is not decoupling (Example 12.4.8). For the 3R arm with u3=0u_3 = 0 the free third link is that body, its "thrusters" the forces joints 1 and 2 apply at joint 3. The two decoupling fields are translation along link 3 and rotation about its center of percussion with respect to joint 3, at distance

k=I3+m3r32m3r3=0.004125+0.5⋅0.1520.5⋅0.15=0.205 mk = \frac{I_3 + m_3 r_3^2}{m_3 r_3} = \frac{0.004125 + 0.5 \cdot 0.15^2}{0.5 \cdot 0.15} = 0.205\ \text{m}

for Choset's Table 12.1 robot. In joint coordinates both are built through the inverse of the 2×22\times2 Jacobian of joint 3's position. The check evaluates Theorem 12.4.7 numerically with Chapter 20's kinetic-energy connection at three configurations: both fields pass with residuals below 5×10−105 \times 10^{-10}; rotation about the center of mass fails (it is not even in span(Y\mathcal{Y}) — a rotation about the COM needs a torque the dead joint cannot supply); and the sum of the two fails the second condition, exactly as for the free body.

Modern descendants: lattices and hybrid A*

Choset's chapter ends in 2005 with Algorithm 22 and PRM-with-Reeds–Shepp. The planners that parked the DARPA Urban Challenge vehicles, and their successors, are those two ideas recombined, with three deltas:

  1. Lattice-consistent primitives (state lattices, Pivtoraiko, Knepper and Kelly). Choose primitives that start and end exactly on lattice states — grid points times a set of headings — so the search graph is a real graph and A* runs unchanged. The library builds them with this chapter's Dubins solver: for each start heading and each category (straight, ±45°, ±90°, lane change), the cheapest Dubins path to a lattice endpoint whose total turning equals the heading change. The cost of exactness is that the start and goal must be lattice states.
  2. One continuous state per cell (hybrid A*, Dolgov, Thrun, Montemerlo and Diebel). Keep Algorithm 22's free-running continuous configurations and its occupancy grid, but let each cell hold the cheapest state that reached it rather than the first.
  3. Analytic expansions and a dual heuristic (hybrid A*). Every NN expansions try a Reeds–Shepp shot from the popped state straight to the goal and accept it if it is free — the PRM local planner, inside the search. Order OPEN by max⁡(Reeds–Shepp length ignoring obstacles, 2-D shortest distance ignoring kinematics)\max(\text{Reeds–Shepp length ignoring obstacles},\ \text{2-D shortest distance ignoring kinematics}): each term is a lower bound on the true cost, so their maximum is too. (The library's 2-D table is scaled by 1/1.08241/1.0824, the worst octile-to-Euclidean ratio, to keep it admissible.)

On the Lot, reversing into bay 3 from (2,1,0)(2, 1, 0) to (9.5,8.5,−π/2)(9.5, 8.5, -\pi/2) at 0.50.5 m resolution with the dual heuristic: the six-primitive state lattice expands 298 states and returns a 24.36824.368 m path with three cusps; hybrid A* with a shot every ten expansions expands 161, fires seventeen shots, and returns 17.10717.107 m with three cusps, ending exactly on the goal. Both are at least the obstacle-blind Reeds–Shepp distance, 15.18915.189 m. Hybrid A* is not optimal (the cell pruning discards states that might have led to cheaper paths) and the lattice is optimal only over its primitives; w21.3 lets you watch both trade-offs.

The algorithm

AlgorithmAlgorithm 22 — CAR GRID SEARCH (Barraquand and Latombe)Costat most d³ expansions (d⁴ with a trailer), six motions each; O(1) OPEN insertion by cost buckets
In
start q_start, goal region G(q_goal), action set {L±, S±, R±}, step Δ, grid resolution d, integer weights (a, b, c), MAXTREESIZE, collision checker
Out
a path of actions from q_start into G(q_goal), or FAILURE
  1. initialize the tree TT and the bucket array OPEN with qstartq_{start} at cost 0
  2. while OPEN is not empty and ∣T∣<|T| < MAXTREESIZE:
  3. q←\quad q \leftarrow first node of the lowest non-empty bucket; remove it
  4. \quadif q∈G(qgoal)q \in G(q_{goal}): return SUCCESS and the path root → qq
  5. \quadif the cell of qq in the d3d^3 grid is not marked:
  6. \quad\quadmark it
  7. \quad\quadfor each action ∈{L±,R±,S±}\in \{L_\pm, R_\pm, S_\pm\}: integrate for arc length Δ\Delta (Hitch.step, with the trailer if hitched) to qnewq_{new}, checking the footprint at substeps
  8. \quad\quad\quadif the motion is free: add qnewq_{new} under qq; cost == cost(q)+a+b [steer changed]+c [direction changed](q) + a + b\,[\text{steer changed}] + c\,[\text{direction changed}]; append to that cost's bucket
  9. return FAILURE
Algorithmsteer_sinusoids(x_start, x_goal) — Choset §12.5.2Costn − 1 integrations of the chain
In
chained-form start and goal in ℝⁿ, optional amplitude cap
Out
n − 1 control phases on unit time and the integrated trace
  1. phase 0: u1=xgoal,1−xstart,1u_1 = x_{goal,1} - x_{start,1}, u2=xgoal,2−xstart,2u_2 = x_{goal,2} - x_{start,2}, constant on [0,1][0, 1]
  2. for k=1,…,n−2k = 1, \dots, n - 2: Δ←xgoal,k+2−xk+2\Delta \leftarrow x_{goal,k+2} - x_{k+2}; a=b=(∣Δ∣ k! (4π)k)1/(k+1)a = b = (|\Delta|\,k!\,(4\pi)^k)^{1/(k+1)} with the sign of Δ\Delta on bb; if aa exceeds the cap, clamp it and set b=Δ k! (4π/a)kb = \Delta\,k!\,(4\pi/a)^k
  3. \quadapply u1=asin⁡2πtu_1 = a\sin 2\pi t, u2=bcos⁡2kπtu_2 = b\cos 2k\pi t for unit time and integrate
  4. for Hitch's car: map poses to z=(x′,tan⁡ϕ/(Lcos⁡3θ′),tan⁡θ′,y′)z = (x', \tan\phi/(L\cos^3\theta'), \tan\theta', y') in the chart rotated to the heading bisector, and back
Algorithmsteer_gradient(x_start, x_goal, u⁰, ε) — Choset §12.5.4Costper iteration: one end-state Jacobian (2r propagations) and a backtracking line search
In
start and goal, initial parameter vector (zero by default), tolerance, a seeded RNG for generic loops
Out
u^p with ‖f(x_start, u^p) − x_goal‖ < ε, or the iterate where the line search stalled
  1. if rank ∂f/∂up<n\partial f/\partial u^p < n: append a generic loop (random pieces, then the same pieces reversed and negated)
  2. repeat until ∥e∥<ϵ\|e\| < \epsilon: J←∂f/∂upJ \leftarrow \partial f/\partial u^p by central differences; v←−J⊤ev \leftarrow -J^\top e
  3. \quadif v=0v = 0: append another generic loop and continue
  4. \quaddouble α\alpha, then halve it until 12∥e(up+αv)∥2≤12∥e∥2−10−4α∥v∥2\tfrac12\|e(u^p + \alpha v)\|^2 \le \tfrac12\|e\|^2 - 10^{-4}\alpha\|v\|^2; accept
  5. (§12.5.3 variant: hand r(up)=er(u^p) = e and JJ to Chapter 19's levenbergMarquardt)
Algorithmkinodynamic_rrt(prop, x_start, G, n, metric, k) — Choset §7.5.1Costper extension: one nearest-neighbor query and k propagations of `substeps` collision checks
In
a simulator prop(x, u, Δt), a control sampler, a state sampler with goal bias, a metric, controls per extension k
Out
a tree whose root-to-goal branch is a list of (control, Δt) pairs that re-integrates exactly
  1. tree ←{xstart}\leftarrow \{x_{start}\}
  2. for i=1,…,ni = 1, \dots, n: xrand←x_{rand} \leftarrow goal sample with probability pgp_g, else uniform
  3. xnear←arg⁡min⁡v∈treemetric(v,xrand)\quad x_{near} \leftarrow \arg\min_{v \in \text{tree}} \mathrm{metric}(v, x_{rand})
  4. \quadfor j=1,…,kj = 1, \dots, k: sample uju_j; propagate xnearx_{near} for Δt\Delta t in substeps, discarding uju_j at the first collision
  5. \quadkeep the free endpoint closest to xrandx_{rand}; add it with its control and segment
  6. \quadif any state of the segment is in GG: return the branch
Algorithmhybrid_a_star(q_start, q_goal, cell, h, N)CostA* over at most (cells × heading bins) expansions; one RS evaluation per heuristic call
In
start and goal poses, cell size, heuristic (Euclidean, RS, or max(RS, 2-D)), shot period N, heading bins
Out
arc primitives followed by one Reeds–Shepp shot, ending exactly at q_goal
  1. push qstartq_{start}; best gg per cell ←∞\leftarrow \infty except the start's
  2. while OPEN not empty: pop the lowest f=g+hf = g + h; if its cell is closed, continue; close it
  3. \quadevery NN-th expansion: RS shot to qgoalq_{goal}; if free, return path to here + shot
  4. \quadfor each of L±,S±,R±L_\pm, S_\pm, R_\pm (arc length just long enough to leave the cell and, at full lock, the heading bin — otherwise a turning child ties with its straight sibling in the same cell and is pruned): integrate, check, compute the child's cell
  5. \quad\quadif the cell is open and gchild<g_{child} < best gg of that cell: record it, push with f=gchild+h(qchild)f = g_{child} + h(q_{child})
Algorithmdecoupling_grid_search(V, q_start, G, ik) then time_scale — Choset §12.5.7CostAlg. 22 over link-3 poses with 4 primitives and lexicographic cost (switches, steps); one time scaling per segment
In
decoupling primitives (translate ±, rotate ± about the center of percussion), link-3 start and goal poses, IK with a fixed elbow, collision and singularity margin
Out
a switch-minimizing sequence of decoupling segments and its time-optimal, rest-to-rest execution
  1. best-first over link-3 poses (xJ3,yJ3,α)(x_{J3}, y_{J3}, \alpha) with a d3d^3 occupancy grid; children by the four primitives, valid if IK exists with ∣sin⁡q2∣≥|\sin q_2| \ge margin and the whole arm is free
  2. cost == (number of switches, number of steps) compared lexicographically; goal tested at pop
  3. merge equal consecutive primitives into segments
  4. for each segment: path q(s)q(s) by IK; Chapter 18's time scaling of the two actuated rows from rest to rest (the third row is identically zero along a decoupling path)

Implementation in Rust

The nonholonomic crate of Chapter 20 gains a steer/ directory, car_grid.rs, lattice/ and reduction.rs; forward propagation gets its own crate, kinodynamic, which owns the Propagate trait Chapter 14's preview borrowed. The TypeScript port in web/lib/nonholonomic/ — dubins.ts, reeds-shepp.ts, chained.ts, gradient.ts, flat.ts, car-grid.ts, lattice.ts, kinodynamic.ts, reduction.ts, steer.ts, park.ts — is the code behind every widget on this page, and its __checks_ch21__.ts pins every number in the prose.

crates/nonholonomic/src/steer/dubins.rs
use manifold::SE2;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Word { LSL, RSR, LSR, RSL, RLR, LRL }

/// Dubins shortest path for the reduced car (Choset eq. 12.35). Segment lengths are
/// normalized by ρ — arcs in radians, the straight in units of ρ — so the closed forms
/// are scale-free; every word returned is checked by integration in the tests.
#[derive(Clone, Copy, Debug)]
pub struct DubinsPath { pub word: Word, pub segs: [f64; 3], pub rho: f64 }

impl DubinsPath {
    pub fn len(&self) -> f64 { self.rho * self.segs.iter().sum::<f64>() }
}

fn mod2pi(a: f64) -> f64 { a.rem_euclid(std::f64::consts::TAU) }

/// Start at the origin, goal at (d, 0): headings measured from the bearing of the goal.
fn normalize(q0: &SE2, q1: &SE2, rho: f64) -> (f64, f64, f64) {
    let (dx, dy) = (q1.x - q0.x, q1.y - q0.y);
    let bearing = dy.atan2(dx);
    (dx.hypot(dy) / rho, mod2pi(q0.theta - bearing), mod2pi(q1.theta - bearing))
}

/// LSL: the two left circles and their external tangent (Derivation 5, step 5).
fn lsl(d: f64, a: f64, b: f64) -> Option<[f64; 3]> {
    let p2 = 2.0 + d * d - 2.0 * (a - b).cos() + 2.0 * d * (a.sin() - b.sin());
    if p2 < 0.0 { return None; }
    let th = (b.cos() - a.cos()).atan2(d + a.sin() - b.sin());
    Some([mod2pi(-a + th), p2.sqrt(), mod2pi(b - th)])
}

pub fn dubins_shortest_path(q0: &SE2, q1: &SE2, rho: f64) -> Option<DubinsPath> {
    let (d, a, b) = normalize(q0, q1, rho);
    // RSR, LSR, RSL, RLR, LRL follow from LSL by reflection (L <-> R, headings negated)
    // and by reading the word backward; each has its own feasibility test.
    [(Word::LSL, lsl(d, a, b)), (Word::RSR, rsr(d, a, b)), (Word::LSR, lsr(d, a, b)),
     (Word::RSL, rsl(d, a, b)), (Word::RLR, rlr(d, a, b)), (Word::LRL, lrl(d, a, b))]
        .into_iter()
        .filter_map(|(word, s)| s.map(|segs| DubinsPath { word, segs, rho }))
        .min_by(|x, y| x.len().total_cmp(&y.len()))
}
crates/nonholonomic/src/steer/reeds_shepp.rs
use manifold::SE2;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Steer { L, S, R }

/// A signed segment: arcs in radians, straights in units of ρ; negative means reverse.
#[derive(Clone, Copy, Debug)]
pub struct Seg { pub steer: Steer, pub len: f64 }

/// The three symmetries of the normalized goal (x, y, φ). Time-flip negates every
/// segment, reflect swaps L and R; "backwards" (applied separately) reads the word
/// right to left. Eleven base formulas under these four maps: 44 evaluations that
/// cover the 48 Reeds–Shepp words.
const SYMMETRIES: [(f64, f64, f64, bool, bool); 4] = [
    (1.0, 1.0, 1.0, false, false),   // base
    (-1.0, 1.0, -1.0, true, false),  // time-flip
    (1.0, -1.0, -1.0, false, true),  // reflect
    (-1.0, -1.0, 1.0, true, true),   // both
];

pub fn reeds_shepp_candidates(x: f64, y: f64, phi: f64) -> Vec<Vec<Seg>> {
    let mut out = Vec::new();
    for &(sx, sy, sp, flip, refl) in &SYMMETRIES {
        let sign = if flip { -1.0 } else { 1.0 };
        let word = |w: [Steer; 3], l: [f64; 3]| -> Vec<Seg> {
            w.iter().zip(l).map(|(&s, len)| Seg {
                steer: if refl { s.mirrored() } else { s },
                len: sign * len,
            }).collect()
        };
        if let Some((t, u, v)) = lp_sp_lp(sx * x, sy * y, sp * phi) {
            out.push(word([Steer::L, Steer::S, Steer::L], [t, u, v]));
        }
        if let Some((t, u, v)) = lp_sp_rp(sx * x, sy * y, sp * phi) {
            out.push(word([Steer::L, Steer::S, Steer::R], [t, u, v]));
        }
        // … L+R-L (and its backwards reading), the two CCCC, the four CCSC, the CCSCC.
    }
    out
}

/// The shortest word in world units, or None (never, for ρ > 0).
pub fn reeds_shepp_shortest_path(q0: &SE2, q1: &SE2, rho: f64) -> Option<(Vec<Seg>, f64)> {
    let local = q0.inverse() * *q1;           // goal in the start's frame
    reeds_shepp_candidates(local.x / rho, local.y / rho, local.theta)
        .into_iter()
        .map(|w| { let l = rho * w.iter().map(|s| s.len.abs()).sum::<f64>(); (w, l) })
        .min_by(|a, b| a.1.total_cmp(&b.1))
}

impl Steer {
    fn mirrored(self) -> Self { match self { Steer::L => Steer::R, Steer::R => Steer::L, Steer::S => Steer::S } }
}
crates/nonholonomic/src/car_grid.rs
use std::collections::VecDeque;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Action { Lp, Sp, Rp, Lm, Sm, Rm }

/// Integer weights: OPEN becomes an array of FIFO buckets indexed by cost (Choset's
/// "one-dimensional array with cost as the index"), so insertion is O(1).
#[derive(Clone, Copy, Debug)]
pub struct GridSearchCost { pub a: u32, pub b: u32, pub c: u32 }

pub struct Node<C> { pub q: C, pub parent: Option<usize>, pub action: Option<Action>, pub cost: u32 }

/// C Alg. 22, generic over the car model so the trailer is one type away.
pub fn car_grid_search<C: CarModel>(
    car: &C, q_start: C::Config, goal: &GoalRegion<C::Config>, step: f64,
    cells_per_dim: usize, cost: GridSearchCost, cs: &dyn collide::Collision<C::Config>,
    max_tree: usize,
) -> Result<Vec<(C::Config, Action)>, Failure> {
    let mut tree = vec![Node { q: q_start, parent: None, action: None, cost: 0 }];
    let mut open: Vec<VecDeque<usize>> = vec![VecDeque::from([0])];
    let mut occupied = vec![false; car.cells(cells_per_dim)];    // d³, or d⁴ with a trailer
    let mut lowest = 0usize;
    while tree.len() < max_tree {
        // Line 3: the first node of the lowest non-empty bucket.
        while lowest < open.len() && open[lowest].is_empty() { lowest += 1; }
        let Some(id) = open.get_mut(lowest).and_then(|b| b.pop_front()) else { break };
        let q = tree[id].q.clone();
        // Line 4: the goal is tested when popped, which is what makes the path cost-optimal.
        if goal.contains(&q) { return Ok(path_to(&tree, id)); }
        // Lines 7-8: expand each cell once.
        let cell = car.cell_of(&q, cells_per_dim);
        if std::mem::replace(&mut occupied[cell], true) { continue; }
        for act in Action::ALL {
            let (q_new, sweep) = car.integrate(&q, act, step);
            if !sweep.iter().all(|s| cs.is_free(s)) { continue; }   // line 11
            let prev = tree[id].action;
            let c = tree[id].cost + cost.a
                + if prev.is_some_and(|p| p.steer() != act.steer()) { cost.b } else { 0 }
                + if prev.is_some_and(|p| p.dir() != act.dir()) { cost.c } else { 0 };
            if open.len() <= c as usize { open.resize_with(c as usize + 1, VecDeque::new); }
            open[c as usize].push_back(tree.len());
            lowest = lowest.min(c as usize);
            tree.push(Node { q: q_new, parent: Some(id), action: Some(act), cost: c });
        }
    }
    Err(Failure::TreeExhausted { size: tree.len() })
}
crates/kinodynamic/src/rrt.rs
use rand::Rng;
use rand_pcg::Pcg64;

/// §7.5.1: planning with a simulator instead of a steer.
pub trait Propagate {
    type State: Clone;
    type Control: Clone;
    fn propagate(&self, x: &Self::State, u: &Self::Control, dt: f64) -> Self::State;  // RK4 inside
    fn sample_control(&self, rng: &mut Pcg64) -> Self::Control;
}

pub struct KinoRrt<P: Propagate> {
    pub sys: P,
    pub controls_per_extend: usize,
    pub dt: f64,
    pub substeps: usize,
    pub goal_bias: f64,
    nodes: Vec<(P::State, Option<(usize, P::Control)>)>,
}

impl<P: Propagate> KinoRrt<P> {
    pub fn plan(
        &mut self, x_start: P::State, goal: &dyn Fn(&P::State) -> bool, n: usize,
        sample: &dyn Fn(&mut Pcg64) -> P::State, metric: &dyn Fn(&P::State, &P::State) -> f64,
        is_free: &dyn Fn(&P::State) -> bool, rng: &mut Pcg64,
    ) -> Option<Vec<(P::Control, f64)>> {
        self.nodes = vec![(x_start, None)];
        for _ in 0..n {
            let x_rand = sample(rng);
            // The metric is the planner's only notion of "near" — and its most sensitive knob.
            let near = (0..self.nodes.len())
                .min_by(|&a, &b| metric(&self.nodes[a].0, &x_rand).total_cmp(&metric(&self.nodes[b].0, &x_rand)))?;
            let mut best: Option<(P::State, P::Control, f64)> = None;
            for _ in 0..self.controls_per_extend {
                let u = self.sys.sample_control(rng);
                let h = self.dt / self.substeps as f64;
                let mut x = self.nodes[near].0.clone();
                let mut free = true;
                for _ in 0..self.substeps {
                    x = self.sys.propagate(&x, &u, h);
                    if !is_free(&x) { free = false; break; }
                }
                let d = metric(&x, &x_rand);
                if free && best.as_ref().is_none_or(|b| d < b.2) { best = Some((x, u, d)); }
            }
            let Some((x_new, u, _)) = best else { continue };
            self.nodes.push((x_new.clone(), Some((near, u))));
            if goal(&x_new) { return Some(self.controls_to(self.nodes.len() - 1)); }
        }
        None
    }
}
crates/nonholonomic/src/reduction.rs
use nalgebra::{SMatrix, SVector};

/// Theorem 12.4.7: V is decoupling iff V ∈ span(Y) and ∇_V V ∈ span(Y).
pub fn is_decoupling<const Q: usize>(
    v: &dyn VectorField<Q>, inputs: &[&dyn VectorField<Q>],
    model: &dynamics::Model<Q>, q: &SVector<f64, Q>, tol: f64,
) -> bool {
    let y: Vec<SVector<f64, Q>> = inputs.iter().map(|f| f.eval(q)).collect();
    let vq = v.eval(q);
    // ∇_V V = (∂V/∂q) V + M⁻¹ (Vᵀ Γ V), Chapter 17's Christoffel symbols, Choset's convention.
    let nabla = v.jacobian(q) * vq + model.mass_matrix(q).try_inverse().unwrap()
        * model.christoffel_form(q, &vq, &vq);
    relative_residual(&vq, &y) < tol && relative_residual(&nabla, &y) < tol
}

/// Reach 3R with u₃ = 0: rotation of link 3 about its center of percussion with respect
/// to joint 3, at k = (I₃ + m₃r₃²)/(m₃r₃) — 0.205 m for Choset's Table 12.1 robot.
pub fn rotate_about_percussion(arm: &PlanarArm<3>) -> impl VectorField<3> + '_ {
    let k = arm.percussion_distance();
    move |q: &SVector<f64, 3>| {
        let a = q.sum();                                   // link 3's absolute heading
        let j: SMatrix<f64, 2, 2> = arm.joint3_jacobian(q); // ∂(x_J3, y_J3)/∂(q₁, q₂)
        let r = j.try_inverse().unwrap() * SVector::<f64, 2>::new(k * a.sin(), -k * a.cos());
        SVector::<f64, 3>::new(r[0], r[1], 1.0 - r[0] - r[1])
    }
}

The worked example, printed

crates/nonholonomic/examples/dubins_check.rs
fn main() {
    let q0 = SE2::new(0.0, 0.0, 0.0);
    let q1 = SE2::new(3.0, 3.0, std::f64::consts::FRAC_PI_2);
    for p in dubins_all(&q0, &q1, 1.0) {
        println!("{:?}  t={:.6} p={:.6} q={:.6}  len={:.6}", p.word, p.segs[0], p.segs[1], p.segs[2], p.len());
    }
    let best = dubins_shortest_path(&q0, &q1, 1.0).unwrap();
    let end = best.integrate(&q0);
    println!("integrate(LSL) -> ({:.6}, {:.6}, {:.6})", end.x, end.y, end.theta);
    let (rs, len) = reeds_shepp_shortest_path(&q0, &q1, 1.0).unwrap();
    println!("reeds-shepp: {}  len={:.6}", word_string(&rs), len);
}
cargo run --example dubins_check -p nonholonomic
LSL  t=0.785398 p=2.828427 q=0.785398  len=4.399223
LSR  t=0.927295 p=4.000000 q=5.639684  len=10.566979
RSL  t=5.639684 p=4.000000 q=0.927295  len=10.566979
LRL  t=3.141593 p=4.712389 q=3.141593  len=10.995574
RSR  t=5.497787 p=5.656854 q=5.497787  len=16.652429
integrate(LSL) -> (3.000000, 3.000000, 1.570796)
reeds-shepp: L+S+L+  len=4.399223
cargo run --example flat_car -p nonholonomic
t=0.25  y=(0.250000, 0.156250)  theta=0.844154 v=1.505199 omega=1.324138 kappa=0.879709 phi(L=1)=0.721491
t=0.50  y=(0.500000, 0.500000)  theta=0.982794 v=1.802776 omega=0.000000 kappa=0.000000 phi(L=1)=0.000000
cargo run --example reach_dead_joint_drive -p nonholonomic
percussion distance k = 0.205000 m
plan: T- 0.240 | R+ 1.100 | T+ 0.390 | R- 0.200   (3 switches)
time-optimal, rest to rest: 0.151 + 0.157 + 0.230 + 0.050 = 0.589 s
max |tau3| along every segment: 0 (to finite-difference accuracy); one actuator saturated at every node

#[test] fn reproduces_micro_example() asserts the first two blocks to 10−610^{-6}; tests/words_integrate.rs draws 400 seeded pose pairs and asserts that every Dubins and Reeds–Shepp word integrates to its endpoint within 10−910^{-9} and that Euclidean ≤\le Reeds–Shepp ≤\le Dubins length. The dead-joint example cannot reproduce Choset's 0.8900.890 s, because his Fig. 12.33 path is drawn but not given numerically; it reproduces his structure — four decoupling segments, one actuator always saturated — on the query of w21.6.

Putting it together

The Integration lab is park_six_ways live: Hitch with its 2.5 m trailer, from (5.5,1.2,0)(5.5, 1.2, 0) into bay 3, every planner of the chapter, one scoreboard (seed 21; the trailer is simulated along each path, or produced by the method itself for the trailer-axle flat planner and Algorithm 22's trailer extension).

methodlength (m)cuspsmax ∣θ1∣\lvert\theta_1\rvert (rad)clearance (m)ends
Reeds–Shepp (obstacle-blind)7.3710.620.10exactly on the goal
flat-output quintic, trailer axle9.1901.03collidesexactly; ∣ϕ∣\lvert\phi\rvert up to 1.39 rad
chained-form sinusoids27.8932.39collidesexactly, in chained coordinates
CAR GRID SEARCH (Alg. 22, trailer grid)7.2000.630.11inside ±0.5\pm 0.5 m, ±0.2\pm 0.2 rad
hybrid A* (car), trailer towed7.3710.620.10exactly (its RS shot)
forward-propagation RRT, dynamic car6.8500.660.13inside 0.80.8 m, 0.350.35 rad

Read it as a summary of the chapter. Here the query is open enough that hybrid A*'s first shot is already free, so it is the Reeds–Shepp path; Algorithm 22 finds a slightly shorter path only because it is allowed to stop half a metre short. The flat planner's single smooth curve is the only one built around the trailer, and it is also the one that asks for more steering than Hitch has. The sinusoids are exact in the coordinates where they are exact and absurd everywhere else: the trailer swings to 2.392.39 rad, a jackknife, because the chained form of the four-state car simply does not contain it. The forward-propagated tree is the shortest because its goal is a region — the honest comparison is the column on the right.

Two more experiments belong to the lab. The failure button: shrink the bay until Hitch is only a little shorter than it and run Algorithm 22 with c=0c = 0; the number of cusps in the solution grows as the slack shrinks, as Choset's Θ(1/ϵ2)\Theta(1/\epsilon^2) theorem says it must, and no planner in this chapter can beat that — it is a property of the car, paid in Chapter 20's currency of brackets. The randomized pillar: in w21.1 turn obstacles on and re-roll the seed; a third pillar lands in the open floor, the obstacle-blind methods start reporting collisions, and Algorithm 22 routes around it. Chapter 23 reuses the trailer flat-output planner and the lattice for its jackknife tour, and Chapter 22 learns hybrid A*'s heuristic from data.

The sister book's motion-planning chapter previewed the Dubins dial and sketched hybrid A* for a car under uncertainty; this chapter is the derivation and the tested implementation behind both.

Exercises

  1. Foundation exerciseDifficulty 2 of 3RSL from LSL, and when CCC can never win

    Derive the RSLRSL closed form from LSLLSL by reflection and reversal, and show where p2<0p^2 < 0 makes it infeasible. Then prove that for ∥q1−q0∥xy≥4ρ\|q_1 - q_0\|_{xy} \ge 4\rho no CCCCCC word is optimal — note that CCCCCC words can still be feasible there.

  2. Foundation exerciseDifficulty 2 of 3Chained forms are flat

    Prove that every chained-form system is flat with (x1,xn)(x_1, x_n) as flat outputs (Choset Problem 25) and give φ\varphi and ψu\psi_u explicitly for n=4n = 4. Relate x4x_4 to Hitch's trailer flat output when the car-with-trailer is put in chained form.

  3. Conceptual exerciseDifficulty 1 of 3Predict the word, then drag
    Predict first

    In the Reeds–Shepp Atlas with ρ = 1, start (0, 0, 0) and goal (2, −1, 0): which word is the shortest Dubins path, and does allowing cusps (Reeds–Shepp) shorten it?

    Length of the shortest path to (2, −1, 0) at ρ = 1

  4. Conceptual exerciseDifficulty 2 of 3Make the sinusoids swing and the flat planner fail

    In w21.1 with the trailer on, place the bay so that the sinusoid planner's trailer swing exceeds ∣θ1∣=1.2|\theta_1| = 1.2 rad while the flat planner stays under 0.60.6; then make the flat planner fail (its steering demand over 0.60.6 rad) by lowering the end-speed slider. Say where control constraints enter each method.

  5. Practical exerciseDifficulty 2 of 3CAR GRID SEARCH with a trailer, and the jackknife guard

    Implement the trailer extension of car_grid_search with a lookup table of Δθ1\Delta\theta_1 per primitive (indexed by the hitch angle at the start of the motion, Choset §12.5.6), check it against integration on three seeded queries, and add the jackknife guard ∣θ1∣<π/2−δ|\theta_1| < \pi/2 - \delta as pruning. Measure the expansions it saves.

  6. Practical exerciseDifficulty 3 of 3Kinodynamic RRT* for Hitch's dynamic model (stretch)

    Implement KinoRrtStar for Hitch's dynamic model linearized about rest (Webb and van den Berg: fixed-final-state, free-final-time LQR edges), compare its cost curve with KinoRrt (best of 8) over 20 seeds, then swap the LQR edge for steer_flat and report which assumption each connection violates.

References

  1. 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.4.2 and §12.5 (Brockett's sinusoids, chained forms, gradient steering, flatness, cars and trailers, Algorithm 22, kinematic reductions, Table 12.1) and §7.5.1 (control-based planning), followed section by section. The Souères–Laumond synthesis and the Sussmann–Tang reduction of the Reeds–Shepp list are its refs. 395 and 402.

  2. Dubins, L. E. (1957) On Curves of Minimal Length with a Constraint on Average Curvature, and with Prescribed Initial and Terminal Positions and Tangents. American Journal of Mathematics 79(3).doi:10.2307/2372560 (opens in a new tab)

    The six-word result for the forward-only car: the CSC/CCC words implemented in dubins.ts and pinned by the micro-example.

  3. Reeds, J. A. and Shepp, L. A. (1990) Optimal Paths for a Car That Goes Both Forwards and Backwards. Pacific Journal of Mathematics 145(2).doi:10.2140/pjm.1990.145.367 (opens in a new tab)

    The forty-eight words and the §8 base formulas that reeds-shepp.ts evaluates under the time-flip and reflect symmetries.

  4. Murray, R. M. and Sastry, S. S. (1993) Nonholonomic Motion Planning: Steering Using Sinusoids. IEEE Transactions on Automatic Control 38(5).doi:10.1109/9.277235 (opens in a new tab)

    Chained forms, the sinusoid ladder of eq. (12.31), and the chained coordinates of the kinematic car used by steerCarSinusoids.

  5. Fliess, M., Lévine, J., Martin, P., and Rouchon, P. (1995) Flatness and Defect of Non-linear Systems: Introductory Theory and Examples. International Journal of Control 61(6).doi:10.1080/00207179508921959 (opens in a new tab)

    Differential flatness, with the car and the car with trailers among its examples — Choset's refs. 152–153 and the basis of flat.ts.

  6. Barraquand, J. and Latombe, J.-C. (1993) Nonholonomic Multibody Mobile Robots: Controllability and Motion Planning in the Presence of Obstacles. Algorithmica 10.doi:10.1007/BF01891837 (opens in a new tab)

    The grid search over full-lock primitives with a cusp-weighted cost that Choset presents as Algorithm 22, including the trailer extension.

  7. Laumond, J.-P., Jacobs, P. E., Taïx, M., and Murray, R. M. (1994) A Motion Planner for Nonholonomic Mobile Robots. IEEE Transactions on Robotics and Automation 10(5).doi:10.1109/70.326564 (opens in a new tab)

    The omnidirectional-to-nonholonomic path transformation by recursive Reeds–Shepp bisection (pathTransform) and the topological property behind its completeness.

  8. LaValle, S. M. and Kuffner, J. J. (2001) Randomized Kinodynamic Planning. International Journal of Robotics Research 20(5).doi:10.1177/02783640122067453 (opens in a new tab)

    Forward-propagation RRTs for systems with differential constraints — the KinoRrt of this chapter and the metric-sensitivity discussion.

  9. Bullo, F. and Lynch, K. M. (2001) Kinematic Controllability for Decoupled Trajectory Planning in Underactuated Mechanical Systems. IEEE Transactions on Robotics and Automation 17(4).doi:10.1109/70.954753 (opens in a new tab)

    Decoupling vector fields, Theorem 12.4.7 and kinematic controllability, with the 3R arm whose third joint is unactuated.

  10. Pivtoraiko, M., Knepper, R. A., and Kelly, A. (2009) Differentially Constrained Mobile Robot Motion Planning in State Lattices. Journal of Field Robotics 26(3).doi:10.1002/rob.20285 (opens in a new tab)

    State lattices with lattice-consistent primitives: delta 1 of the chapter's three deltas from Algorithm 22.

  11. Dolgov, D., Thrun, S., Montemerlo, M., and Diebel, J. (2010) Path Planning for Autonomous Vehicles in Unknown Semi-structured Environments. International Journal of Robotics Research 29(5).doi:10.1177/0278364909359210 (opens in a new tab)

    Hybrid A*: continuous states in a discrete grid, analytic Reeds–Shepp expansions and the dual heuristic — deltas 2 and 3.

  12. Webb, D. J. and van den Berg, J. (2013) Kinodynamic RRT*: Asymptotically Optimal Motion Planning for Robots with Linear Dynamics. IEEE International Conference on Robotics and Automation (preprint arXiv:1205.5088).link to Kinodynamic RRT*: Asymptotically Optimal Motion Planning for Robots with Linear Dynamics (opens in a new tab)

    The fixed-final-state, free-final-time optimal edge that turns RRT* into a kinodynamic planner; the double-integrator closed form in kinodynamic.ts.

  13. Karaman, S. and Frazzoli, E. (2011) Sampling-based Algorithms for Optimal Motion Planning. International Journal of Robotics Research 30(7).doi:10.1177/0278364911406761 (opens in a new tab)

    RRT* and its asymptotic optimality, which Chapter 13 proves and Kinodynamic RRT* inherits once the steer is exact.