The Code

Our hardware testing files:

Early on in development, our main focus was testing the hardware's functionality and familiarizing ourselves with the python PiCar commands. These next three files demostrate our simple hardware tests of the camera, the color sense (for line following use later), and the wheels and steering.

Camera hardware test file:

↓ camera-test.py

Sensor hardware test file:

↓ sensor-test.py

Wheels/steering hardware test file:

↓ wheel-test.py

Calibration

Next, our focus was on the calibration step of the robot. This step is essential to the precise functionality of the robot. The biggest step here was centering the steering servo by zeroing the angle, and similarly centering the pan and tilt of the camera. The file below is focused entirely on this early step, no line-following yet.

Early calibration file: (no line following)

↓ calibration1.py

Development Improvements (Line Following)

After initial calibration, the majority of our work focused on improving the robot’s ability to reliably follow a line. Below are the most important improvements made throughout development.

Sensor & Calibration Fixes

PID & Driving Improvements

Recovery System

Corner Handling

Smoothness & Reliability

Final Line-Following + Calibration File

The file below represents the final version of our system, combining calibration, PID control, recovery behavior, and all improvements listed above into one complete script.

↓ final-calibration.py

How It Works (in Plain English)

Prince has three small grayscale sensors pointing down at the floor: left, center, and right. A dark line on a light floor reads as a low number; bare floor reads high. From those three numbers the robot computes a single error: roughly “how far off the line am I, and in which direction?” Negative means the line is to the left, positive means it's to the right, zero means dead center.

That error feeds a PID controller, which turns it into a steering angle. The proportional term (P) reacts to how far off the line we are right now, and the derivative term (D) damps out the wobble that pure P-control creates. The integral term (I) is left at zero on this robot because straight-line tracking is already good enough without it, and adding I tended to cause slow oscillation. We also slow Prince down on sharp turns so the controller has time to react before he overshoots.

The trickier part is what happens when Prince loses the line entirely, for example on a 90° corner that's sharper than his turning radius. The system handles this with a small state machine:

State machine diagram for Prince's line-following system
The four states Prince moves between while following a line.

The reason recovery is split into two modes is purely practical: on straights we don't want Prince to throw himself backwards every time he wobbles off the tape, but on corners a gentle little sweep won't find a line that's now 35° off to the side. The error magnitude at the moment of line loss is a surprisingly reliable signal for which situation we're in.

Further Reading

We didn't arrive at any of this from first principles. When we first hit the runaway-oscillation problem on simple proportional-only steering, we asked Claude Code for help debugging and the suggestion was “you probably want a PID controller, here's the gist.” That kicked off a lot of reading.

Honestly, the best resource is Prof. Neller himself. He has a great set of readings on PID and control systems, and a quick conversation in office hours will get you further than most blog posts. Ask him before you spend a weekend tuning gains by trial and error. If you're getting started on your own, the resources below are also good:

Tuning Notes

For any future teams working with this robot, we highly reccommend testing the functionality of the hardware before you begin. We encountered some serious roadblocks with connecting to the robot, as well as with the hardware. Our robot tended to drift while intending to drive straight, an error we had to address before we could begin any line-following.