This is an old revision of the document!
2 hours:
After this laboratory, students should be able to:
The lab focuses on programming and tuning an autonomous line-following vehicle. The system utilizes a PixyCam2 camera for line detection, a PID controller for trajectory correction, and a steering servomotor.
``` [PixyCam2 Camera] → vector path → [Error Estimator (Ex 2)] → error → [PID Controller] → output → [Servo Command (Ex 3)] → PWM → [Steering Servo] ```
In each control loop iteration, the vehicle executes the following sequence:
1. Read the path vectors reported by PixyCam2. 2. Estimate the lateral steering error from the detected vector. 3. Compute the tracking error: error = 0.0f - estimated_error. 4. Apply the PID control algorithm to the tracking error. 5. Map the PID controller output to a physical servo command. 6. Adjust the motor speed profile based on path curvature and stability. 7. Write commands to the actuators (servo and ESC). 8. Wait for the next control cycle.
The vehicle does not know the track geometry in advance; it reacts dynamically to real-time measurements. If the line shifts left, it steers left; if it shifts right, it steers right; if the line is centered, the wheels remain straight.
An NXP Cup autonomous vehicle consists of the following primary components:
| Component | Role | Practical Engineering Challenges |
|---|---|---|
| Microcontroller | Runs the real-time control loop | sampling frequency, latency, memory constraints |
| PixyCam2 | Detects path vectors in the image frame | calibration, ambient light, lost vectors, intersections |
| Steering Servo | Controls the front wheel angle | mechanical limits, saturation, non-linear response |
| Motor / ESC | Drives the vehicle's propulsion | inertia, wheel slippage, current limits, battery sag |
| Encoder | Measures real-time wheel speed | missing pulses, quantization noise, sampling delay |
| Battery | Powers the logic board and motors | voltage drops, performance degradation under load |
The PID controller computes the steering adjustment using three distinct terms:
| Term | Conceptual Formula | Physical Effect |
|---|---|---|
| P - Proportional | proportional to current error | reacts quickly to immediate deviations from the line |
| I - Integral | accumulates error over time | corrects persistent steady-state offsets |
| D - Derivative | proportional to error rate-of-change | dampens overshoot and suppresses oscillations |
The discrete implementation used in the vehicle firmware is:
integral = integral + error * dt; derivative = (error - previous_error) / dt; output = kp * error + ki * integral + kd * derivative; previous_error = error;
After computing the output, it is saturated to the safety limits of the steering servo:
if (output > max_output) output = max_output; if (output < min_output) output = min_output;
The workspace is organized into three functional directories:
pid_tuning/ — Parameters and deployment scripts for the physical vehicle.pid_config.txt: Contains the runtime PID gains ($K_p$, $K_i$, $K_d$) and speed profile.flash_car.sh: The script to upload configurations to the car.exercise2_pixy_vector/ — Coordinate mapping and error estimation.pixy_vector.c / .h: Student implementation file and interface.test_pixy_vector.c: The local unit test suite.exercise3_steering/ — Steering actuator command conversion.steering.c / .h: Student implementation file and interface.test_steering.c: The local unit test suite.Makefile — Compiles and executes both test suites (via make test).Students do not need to modify the full vehicle firmware. You will implement and validate the core modules locally, and then tune the PID parameters on the physical car.
—
Tune the kp, ki, and kd gains of the vehicle controller to achieve stable and fast line-following behavior on the physical track using a live Bluetooth connection.
Serial1.Example serial commands sent from the mobile app:
P1.80 (Sets steering Kp to 1.80) I0.01 (Sets steering Ki to 0.01) D0.20 (Sets steering Kd to 0.20)
ki = 0.00).kp progressively using the app sliders or terminal inputs until the car follows the line, but starts to oscillate left-and-right around the center.kd to dampen the oscillations and smooth the vehicle's trajectory.ki only if the vehicle exhibits a persistent offset to one side (due to mechanical misalignment).kp or slightly increase kd.kp.
The Teensy car code automatically listens for incoming serial tuning commands on both USB Serial and the Bluetooth Serial port (Serial1 at 9600 baud). Adjustments are applied instantly to the running controllers, allowing you to tune parameters on the fly without re-flashing.
Test the vehicle on the track and document your findings:
| Run | Kp | Ki | Kd | Observations |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 |
kp is too low?kp is too high?kd?ki necessary for stable tracking? Explain why.—
Implement a C function that parses a raw tracking vector from PixyCam2 and returns a normalized steering error relative to the image frame center.
The PixyCam2 reports line tracking vectors in image coordinates. The $x$ coordinate increases from left to right, and the $y$ coordinate increases from top to bottom. To determine the immediate direction of the path, you must track the endpoint of the vector that is closest to the front of the car (the bottom of the image frame, which corresponds to the larger $y$ coordinate).
The function must return a normalized error in the range [-1.0, 1.0]:
-1.0 represents the far-left edge of the frame;0.0 represents the exact center of the frame;1.0 represents the far-right edge of the frame.
If the input frame_width ⇐ 1, return 0.0f.
Clamp the selected $x$ coordinate to the valid image boundaries $[0, \text{frame\_width} - 1]$ before normalization.
Recommended normalization formula:
center = (frame_width - 1) / 2.0 error = (x - center) / center
| Vector | Result | Explanation |
|---|---|---|
{39, 0, 39, 51} | 0.0 | Bottom endpoint is exactly in the center |
{39, 0, 0, 51} | -1.0 | Bottom endpoint is at the far-left edge |
{39, 0, 78, 51} | 1.0 | Bottom endpoint is at the far-right edge |
{39, 0, 52, 51} | approx. 0.333 | Bottom endpoint is slightly to the right |
{20, 20, 58, 20} | 0.0 | Horizontal vector: uses average x |
Implement the function in exercise2_pixy_vector/pixy_vector.c:
typedef struct { int x0; int y0; int x1; int y1; } PixyVector; float estimate_pixy_vector_error(PixyVector vector, int frame_width);
Compile and run the local unit tests from the workspace:
cd nxp_car_lab_skeleton make test-pixy
—
Implement a C function to map the abstract floating-point PID controller output to a physical PWM pulse width in microseconds for the steering servomotor.
The PID controller produces an abstract output intended to represent steering direction. The function must:
1. Clamp the input `pid_output` to the safe operating range of ''[-1.0, 1.0]'', where ''-1.0'' represents maximum left steer and ''1.0'' represents maximum right steer. 2. Map this clamped value linearly to a standard servo PWM pulse width in the range ''[1000, 2000]'' microseconds, where ''1500'' microseconds represents the center position (wheels straight).
Recommended conversion formula:
servo_us = 1500 + pid_output * 500
| pid_output | Expected PWM (us) |
|---|---|
-1.0 | 1000 |
-0.5 | 1250 |
0.0 | 1500 |
0.5 | 1750 |
1.0 | 2000 |
2.0 | 2000 (clamped) |
-2.0 | 1000 (clamped) |
Implement the function in exercise3_steering/steering.c:
int pid_output_to_servo_us(float pid_output);
Compile and run the local unit tests:
cd nxp_car_lab_skeleton make test-steering
—
Upon completion of the laboratory, submit the following:
exercise2_pixy_vector/pixy_vector.c and exercise3_steering/steering.c;| Component | Percentage |
|---|---|
| PID tuning analysis and track characterization | 30% |
| Functional PID parameters on the physical track | 25% |
| Exercise 2: PixyCam2 error estimation implementation | 20% |
| Exercise 3: Servo command conversion implementation | 15% |
| Code cleanliness, proper clamping, and local validation | 10% |
make test) before deploying or tuning.