There's usually a clever way to solve the problem. Find it.
"Drive across the field and park on the platform." Three approaches, three trade-offs.
Drive 48 inches at 70%. Easy to code. Breaks when the battery is low or the floor changes.
Drive forward at 70% until the distance sensor sees the platform 4 inches away. Robust to battery changes. Fails if the sensor angle is off or the platform has gaps.
Drive to (48, 0) using PID and the tracked pose. Cleanest, most adaptable. Slowest to set up, but reusable across every routine.
This is the order:
Teams that try to write fast, clever, refactored code on the first attempt usually end up with buggy, slow, clever, refactored code. Get it working first.
At competition:
Smart code accounts for this. Add timeouts so a stuck PID doesn't run forever. Add fallbacks so a missing sensor reading doesn't crash autonomous. Validate gyro calibration before the match.
// PID with timeout fallback, good defensive programming
double startTime = Brain.timer(msec);
double timeoutMs = 2500;
while (true) {
// ... normal PID loop ...
if (Brain.timer(msec) - startTime > timeoutMs) {
Brain.Screen.print("PID timed out, moving on");
break;
}
wait(20, msec);
}
For any autonomous routine, ask: "what if the gyro fails?" Can you still complete it? What if the optical sensor breaks? What if a motor stalls? Walking through these scenarios uncovers brittle code before competition does.
The Pilons (team 5225), 2018 World Champions, built their autonomous around a small library of well-tuned primitives: turnToAngle(), driveToPoint(), driveCurve(). Each routine was a short list of calls to these primitives. The result: easier to write new routines, fewer bugs, more time to test.
Write a line-following program (using the optical or color sensor) three different ways, then pick which one your team should ship.
Two programs that look defensive but aren't. Find the trap.
void drivePID(double target) {
double kp = 0.4;
double error = target - Distance2.objectDistance(mm);
while (error > 0) {
error = target - Distance2.objectDistance(mm);
double output = kp * error;
if (output > 100) output = 100;
if (output < -100) output = -100;
Motor1.setVelocity(output, percent);
wait(0.04, seconds);
}
Motor1.stop();
}
int main() {
drivePID(200);
turnTo(90);
}
AutoState state = DRIVE_1;
double stateStart = Brain.timer(msec);
while (state != DONE) {
switch (state) {
case DRIVE_1:
drivePID(24);
if (Brain.timer(msec) - stateStart > 3000) state = DONE;
else state = TURN_1;
break;
case TURN_1:
turnTo(90);
if (Brain.timer(msec) - stateStart > 3000) state = DONE;
else state = APPROACH;
break;
// ...
}
wait(20, msec);
}