Open loop and closed loop
Your car now sees through your perception box. This unit opens the last box, the controller: every 0.05 s it receives your LaneEstimate and returns a steering angle.
Record every steering angle the provided car used on its lap of gentle, then replay the recording without looking at the camera. Replayed exactly, it gives the same lap. Now set the wheel 0.002 rad (0.11 degrees) off. The replayed car leaves the road after 67.2 m, 8.4 s in, still on the first straight. The provided car, with the same 0.002 rad error, completes the lap with a mean |lateral error| of 0.381 m instead of 0.376 m.
How we got 0.11 degrees
Angles in the simulator are in radians: a full turn is radians, and also . So one radian is . Then

The replay is open loop: its commands were fixed in advance, so nothing notices the error and it adds up. The provided car is closed loop: each command is computed from the latest measurement, so an error is corrected as soon as it shows. Åström and Murray define it:
"The term feedback refers to a situation in which two (or more) dynamical systems are connected together such that each system influences the other" (§1.1)
Here the two systems are the car, turning a steering angle into a new position, and the controller, turning the camera's view of that position into the next steering angle.
The P controller
The 2006 paper on Stanford's robot car Stanley puts it in one line: "The larger this error, the stronger the steering response toward the trajectory." (Thrun et al., p. 684). The simplest law with that property is proportional control:
(Åström & Murray, Eq. 1.3). Here is the controller's output, the reference (the value you want), the measured value, the control error and the gain, how hard to push per unit of error.
For your car, is the steering angle (radians, left positive), the reference is offset 0 and the measurement is the offset . So and
with in radians of wheel per metre. The minus sign is the whole point: 0.4 m left of centre with gives rad, steering right, towards the centre. Steering against the error is negative feedback; drop the minus sign and you get unit 0.1.3's positive feedback.
Two guards complete the law:
- Clip the result to ±0.5 rad, the furthest the simulator's wheel turns, with
np.clip. - If
est.validisFalse, return 0.0: hold the wheel straight rather than steer on NaNs.
Two errors, two gains
A car on lane centre but pointing 0.05 rad left is fine now, not in a second: at 8 m/s it moves m sideways every second. A heading error is an offset in the making, so the controller uses both errors:
where is the heading in radians, left positive, and has no units. The provided car uses and .
How we got 0.40 m sideways per second
The car moves 8 m every second along the direction it points. If that direction is rad away from the road's direction, only part of the 8 m is along the road. The rest is sideways, and trigonometry gives the sideways part of a distance as the distance times of the angle:
(The must be taken with the angle in radians, as here.)
Worked example. The car is 0.4 m left of centre and pointing 0.05 rad left:
Firmly right. Same offset, but pointing 0.05 rad right, back towards the centre: rad, almost straight. The car is already coming back, so the controller eases off. Without the heading term it would steer right all the way to the centre, arrive pointing across the lane and swing past.
The lab pushes the car 1.10 m left, then hands over to the P law with . With the car swings 0.30 m past the centre. With it settles within 5 cm in 1.15 s, never a millimetre past. With it creeps back and takes 3.10 s. The heading gain is a brake on the swing: too little overshoots, too much is sluggish.
Tuning the gains
Keep and sweep on gentle:

- : offsets go uncorrected for seconds; at the first bend the car drifts inside and leaves the road at 125.3 m.
- , the provided value: completes, mean |lateral error| 0.376 m.
- : completes, 0.017 m.
- (in the lab): completes, 0.042 m.
- : leaves the road after 33.8 m, 4.3 s in, still on the first straight.
The tempting conclusion is "more gain, tighter tracking". It holds from 0.05 to 1.5, then fails: 3.0 is worse than 1.5, and 6.0 crashes. The reason is the wheel, which turns at most 1 rad/s. With , a 5 cm offset asks for 0.3 rad of wheel, which takes 0.3 s to reach, 2.4 m of travel at 8 m/s. By then the car has crossed the centre and the controller wants the opposite. The right panel shows it: at the wheel turns at full speed in 80% of steps, always late, each swing wider than the last. There is a best range of gains, and the car's limits set its top.
How we got 0.3 rad, 0.3 s and 2.4 m
Three small steps, each feeding the next.
- Wheel angle asked for. With only the offset term, in size. A 5 cm offset is m, so rad.
- Time to turn the wheel there. The wheel turns at most 1 radian per second, so 0.3 rad takes s. In the simulator's 0.05 s steps that is 6 steps.
- Distance driven meanwhile. At 8 m/s, m.
Why curves need error
To follow a circle of radius , a car with wheelbase m (front axle to rear axle) holds its wheel at about
because the car turns about the point where lines drawn square to its front and rear wheels meet. On gentle's bends your lane has radius 81.8 m (80 m plus 1.8 m: your lane is on the outside), so rad.
How we got rad
First the lane's radius. The road's centre line (the yellow line) bends left with radius 80 m. Your lane's centre is 1.8 m to the right of it, which on a left bend is the outside, so its circle is bigger: m.
Then the ratio of wheelbase to radius: . The turns this ratio into an angle in radians:
For a ratio this small the angle and the ratio are almost the same number.
A P law turns the wheel only when there is error. With both errors zero it says , and the car runs straight off the circle. So in a steady bend the car must carry errors that add up to the right .
Which errors? The car's position is measured at its front axle, which travels where the front wheels point: the car's heading plus . For the front axle to run along the lane, the front wheels point along the lane, so the body points to the outside: on a left bend. The heading term then gives , 2.7 times what is needed. The offset term must take back the extra : , so
Worked example. On gentle: m, to the left, the inside of the bend. Given perfect lane values instead of your box's, the car settles at 0.162 m.

On curvy the bends have radius 20 m, so your lane's radius is 21.8 m on left bends and 18.2 m on right bends: and rad, predicting 0.599 m and −0.715 m. Each bend is only 31 m long, so the offset is still moving when the next one begins; with perfect lane values the car stays within 0.67 m and completes curvy.
How we got 0.123, 0.147, 0.599 and −0.715
Do the gentle recipe twice, once for each side: first the lane's radius , then the wheel angle with m, then the offset .
- Left bends. The lane is on the outside: m. rad. Offset m, positive because the car sits to the left, the inside.
- Right bends. The lane is on the inside: m. rad. The bend is mirrored, so the offset is negative: m.
The offsets come from the unrounded . Starting from the rounded 0.123 and 0.147 would give 0.597 and 0.714 instead.
Åström and Murray name this: "proportional control has the drawback that the process variable often deviates from its reference value" (§1.6). Here the deviation grows as the bend tightens, because .
Set and the offset vanishes. Stanley's law, Eq. 7, is , where "The angle ψ […] describes the orientation of the nearest path segment, measured relative to the vehicle's own orientation" (p. 684): minus your heading, with gain exactly 1. Module 1.3 derives it.
Why your car leaves curvy
With perfect lane values the P law gets round curvy. The provided car does not: it leaves the road at 48.3 m, 6.1 s in, off the inside of the first left bend. Yours will too, if your box works like it.

The cause is unit 0.1.3's chord bias: on a left bend the box says "left of centre, pointing right" even when the car is centred, and the controller multiplies the heading by 2.7. It starts early: the far band sees 5.6 to 11.5 m ahead, so at 30.3 m, just after the bend begins at 30 m, the car is already 0.56 m left.
Worked example. At 44.3 m the car is truly 1.701 m left, heading −0.0848 rad; the box reports 2.321 m and −0.3416 rad.
- On the true values: rad, hard right, back to the centre.
- On the box's values: rad, left, deeper inside.
A heading error of rad times 2.7 is 0.69 rad of phantom left steering, outweighing everything else.
Retuning can hide this: , or , gets round curvy (mean errors 0.085 and 0.131 m), but the bias remains. The real fixes are perception that fits the curve (module 1.5) and a controller that looks ahead along the road (module 1.3).
Closing the loop
Your last step is my_car = Car(perception=estimate_lane, controller=my_controller), where my_controller(est, obs) returns p_steer(est, K_OFF, K_HEAD). A controller that returns a number is a steering angle; the Car pairs it with the provided speed controller, which holds 8 m/s (module 1.2).
Check 0.1.4.b requires a Car running neither provided box to complete gentle with a mean |lateral error| below 0.5 m. Then it drives curvy, ungraded, so you see what your own car does there. Every box is now yours.
A 1988 car that learned to steer
Your car has hand-written perception and control. In 1988, Dean Pomerleau replaced both with one network: ALVINN, "a 3-layer back-propagation network designed for the task of road following" (p. 305).
- Inputs: a 30x32 video retina fed the blue colour band, an 8x32 laser range-finder retina and one feedback unit: 1217 inputs, into 29 hidden units.
- Outputs: 45 direction units, from sharp left to sharp right, the middle one "travel straight ahead" (p. 306). Its training target is the curvature "which would bring the vehicle to the road center 7 meters ahead of its current position" (p. 306): it aims at a point ahead, not at the error at the car.
- Training: 1200 simulated road snapshots.
- Result: it drove the NAVLAB van "at a speed of 1/2 meter per second along a 400 meter path through a wooded area" (pp. 308–309). CMU's traditional system, on the faster Warp computer, managed 1 m/s.
- Limit: at a fork it could output two directions, and "The result is often an oscillation" (p. 312).
How we got 1217 inputs
Each retina is a grid of units, one per picture cell, so its size is rows times columns: the video retina has units and the range-finder retina has . Add the one feedback unit:
It is this module's paper.
Go deeper
- Åström & Murray, "Feedback Systems" (2nd ed.), §1.1 "What Is Feedback?" and §1.6 "Simple Forms of Feedback": proportional control and its offset.
- Thrun et al., "Stanley: The Robot that Won the DARPA Grand Challenge" (2006), §9.2 "Steering Control", Eq. 7 and Figure 24 (p. 684).
- Pomerleau, "ALVINN: An Autonomous Land Vehicle in a Neural Network" (1988): start with Figure 1 and "Network Architecture".