Base Mission
11 points Final judgedOne Blue Pom and one Orange Pom are the same PVC enclosure.
Unit 5 · Big Idea 1
Student Lab · Tracking What the Robot Believes About Itself
Student PIN:
The Long Run taught you that error piles up over a mission, and that square-ups and backward touches reset it back to zero. But here’s a question those labs never asked: how would the robot know it had drifted, if nobody ever wrote the number down? A reset only helps if something is keeping track of what the robot currently believes about its own position. Today you build that something: a small array that holds your robot’s believed pose — its x, y, and heading — and you update it honestly every time you get a real chance to check it against the truth.
The Big Idea of This Unit
A system can’t recognize failure it isn’t tracking. Before a robot can debug itself, recover from a bad turn, or know its plan has gone wrong, it needs some internal record of where it thinks it is — a record it can compare against reality.
Mission 7
One Blue Pom and one Orange Pom are the same PVC enclosure.
A second PVC enclosure contains at least one Blue Pom and at least one Orange Pom, each the enclosure. The Base and Bonus must use different enclosures.
Say the mission back in your own words. Which two enclosures will you use, and why did you pick those two (think about which is easier to reach first)?
So far, every value you’ve stored has had its own name — score, ticks_per_inch, pin. An array is what you use when you have several related values and giving each one a separate name would hide the fact that they belong together.
// three separate variables: works, but they're not obviously connected
double x;
double y;
double heading;
// one array: three related values, one structure
double pose[3];Think of pose[3] as reserving 3 numbered lockers under one locker-bank name, pose. Locker pose[0], locker pose[1], locker pose[2] — counting starts at 0, so a 3-locker array’s lockers are numbered 0 through 2, never 1 through 3. pose[0] = 14.5; puts a value in locker 0, the exact same way you’d assign any variable. pose[0] on its own reads back whatever’s currently in that locker.
In your own words, what is an array? Why does grouping x, y, and heading into one pose[3] array make more sense here than three separate variables?
A regular variable can change while your program runs. A constant cannot. Adding const tells the that the value may be set when it is declared, but cannot be reassigned later. Constants are useful for values that should always mean the same thing, such as the numbered slots in an array.
// a variable: reserves memory, can be reassigned later
double heading = 0;
// a constant: has a type and a value, but cannot be reassigned later
const int POSE_X = 0;A constant declaration looks much like a variable declaration: it has a type, a name, an equals sign, a starting value, and a . The word const goes before the type to say the value cannot change. These constants use int because array indexes are whole numbers.
const int POSE_X = 0;
const int POSE_Y = 1;
const int POSE_R = 2; // R = heading, in degrees
// now pose[POSE_X] means exactly what it says, instead of a bare, meaningless pose[0]
Why does const int POSE_X = 0; use int, and what prevents it from being reassigned later like double heading = 0; can be?
Nothing stops you from writing pose[POSE_X] = 14.0; directly anywhere in your code. But that gets messy fast and makes it easy to update the wrong slot by accident. Instead, write small helper functions that are the only places that touch the array directly.
initPose(x, y, r) runs once, at the very top of main() — before the robot moves. It sets your robot’s starting belief. You’ll determine this starting (x, y) by measuring where the centerpoint between your two wheels sits — that’s the exact point the robot pivots around during a zero-point turn, so it’s the natural place to call “the robot’s position.”
setX(knownX) and setY(knownY) are for later — for the moments during the run when you get real evidence of where you are, not a guess.
printPose() prints the current believed pose so you (and later, the robot) can inspect it.
| What to measure | Your value |
|---|---|
| Distance from your reference wall to the wheel centerpoint (x, inches) | |
| Distance from your reference wall to the wheel centerpoint (y, inches) | |
| Starting heading, facing straight out of the box | 0.0° (by convention) |
Why does it matter that initPose runs only once, before the robot ever moves — what would go wrong if you called it again in the middle of the run?
Back in Unit 4, Turn(char dir, double degrees) already returned 1 for success or 0 for an invalid direction character — but nothing in your code actually used that value. Today it matters: Turn() should only update pose[POSE_R] when the turn actually succeeds. A failed call shouldn’t change what the robot believes about its own heading.
Convention
Turning left increases heading (pose[POSE_R] += degrees); turning right decreases it (pose[POSE_R] -= degrees). Heading 0° faces straight out of the starting box. Stay consistent with this the whole run.
int Turn(char dir, double degrees)
{
if (dir == 'L' || dir == 'l')
{
// ...existing tick-turn logic for a left turn...
pose[POSE_R] += degrees; // only on a real, successful turn
return 1;
}
else if (dir == 'R' || dir == 'r')
{
// ...existing tick-turn logic for a right turn...
pose[POSE_R] -= degrees;
return 1;
}
else
{
printf("Invalid direction: %c\n", dir);
return 0; // no movement happened: don't touch pose
}
}Why should a failed Turn() call leave pose[POSE_R] unchanged? What would happen to your believed heading if it updated R even on failure?
Walk your path from the starting box to both enclosures. Mark every leg, whether a real reset (back_until_pressed or square_up) happens there, and whether you print the pose. You need at least 2 resets tied to a setX/setY call, and 5 total prints: one right after initPose, then one after each of your 4 pom drop-offs.
| # | Leg (what the robot does) | Library call(s) | Pose update | Print pose? |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | ||||
| 6 | ||||
| 7 | ||||
| 8 |
Where did you place your 2 resets, and what known value did you set x or y to at each one? How did you know that value was actually true (not a guess)?
First add the pose array, the names, and the helper functions to your library. Then write the run in main(), following your Phase 5 plan.
const int POSE_X = 0;
const int POSE_Y = 1;
const int POSE_R = 2;
double pose[3]; // pose[POSE_X], pose[POSE_Y], pose[POSE_R]: believed x, y, heading
void initPose(double startX, double startY, double startR)
{
pose[POSE_X] = startX;
pose[POSE_Y] = startY;
pose[POSE_R] = startR;
}
void setX(double knownX)
{
pose[POSE_X] = knownX;
}
void setY(double knownY)
{
pose[POSE_Y] = knownY;
}
void printPose()
{
printf("Pose: x=%.2f y=%.2f R=%.2f\n", pose[POSE_X], pose[POSE_Y], pose[POSE_R]);
}// Unit 5, Big Idea 1: The Second Attempt
// Name: _______________________ Date: ___________
#include <kipr/wombat.h>
#include <yourname.h> // your full library
int main()
{
enable_servo(0);
enable_servo(1);
// ===== INITIALIZE BELIEF =====
initPose(START_X, START_Y, 0.0); // measured wheel-centerpoint, facing out
printPose(); // PRINT 1: starting belief
// ===== VERIFY START =====
back_until_pressed(); // backward touch against the wall
setY(0.0); // RESET #1: known truth, y = 0 at this wall
// ===== LEG 1: pom 1 (orange) to Enclosure A =====
// Drive(...) / Turn(...) to pom 1, pick it up
// Drive(...) / Turn(...) to Enclosure A, drop it
printPose(); // PRINT 2: after drop-off 1
// ===== LEG 2: pom 2 (blue) to Enclosure A, Base Mission complete =====
// Drive(...) / Turn(...) to pom 2, pick it up
// Drive(...) / Turn(...) to Enclosure A, drop it
printPose(); // PRINT 3: after drop-off 2
// ===== RESET before crossing to the second enclosure =====
square_up(); // known heading/position against a line
setX(KNOWN_X); // RESET #2: known truth from this square-up
// ===== LEG 3: pom 3 (orange) to Enclosure B =====
// Drive(...) / Turn(...) to pom 3, pick it up
// Drive(...) / Turn(...) to Enclosure B, drop it
printPose(); // PRINT 4: after drop-off 3
// ===== LEG 4: pom 4 (blue) to Enclosure B, Bonus Mission complete =====
// Drive(...) / Turn(...) to pom 4, pick it up
// Drive(...) / Turn(...) to Enclosure B, drop it
printPose(); // PRINT 5: final belief
return 0;
}check
2 orange + 2 blue poms delivered, split across two different PVC enclosures. initPose called once. At least 2 real resets each paired with a setX/setY call. 5 total printPose() calls. Turn() only updates pose[POSE_R] on success.
Run the mission. Each time it prints a pose, pause and physically measure where the robot actually is. Compare the printed number to your measurement — that gap is your robot’s drift, and it’s the first real evidence you’ve collected about where your model breaks down.
| Print # | Printed pose (x, y, R) | Measured pose (x, y, R) | Gap / likely cause |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 |
Where was the gap biggest? Was it right after a reset, or right before the next one? What does that tell you about where error was actually coming from — a bad turn, a drive distance being off, or something else?
AI Literacy Thread
A system can’t catch its own failures unless it keeps track of what it believes about itself.
Every intelligent system that operates reliably keeps some version of what you built today: a running record of its own believed state, updated honestly at moments of real evidence and left alone otherwise. A GPS-guided drone tracks believed position between satellite fixes. A robot vacuum tracks believed position between wall bumps. None of them are ever perfectly right — but because they keep the number written down, they can catch the moment it drifts too far, and that is the very first requirement for a failure at all: you have to know what you expected before you can recognize that something went wrong.
Complete the reflection on your own.
1. What is an array, and why did grouping x, y, and R into pose[3] make more sense than three separate variables?
2. Why should setX/setY only ever be called right after a real reset (a square-up or backward touch), never as a guess?
3. How did making Turn()’s return value actually matter (updating R only on success) connect back to what you learned about return values in Unit 4?
4. Complete in 2–3 sentences: “A system can’t recognize its own failure unless it keeps track of what it believes about itself. This means that before a robot can debug or recover from a mistake, it must first…”
Finished early? Try one or more of these.
initPose, setX, setY, printPose) would likely be bundled together into one “Pose” object — the data and the functions that use it, packaged as a single unit. This idea is called encapsulation.pose[3] with its own functions into one object, instead of keeping the array and the functions separate the way we did?printPose() and friends reach out and grab the global pose array directly. If you instead wrote void printPose(double p[3]) and called printPose(pose);, the array would be passed by reference — the function receives the array’s actual memory location, not a copy.When you are finished, press the button to turn in your work and save a copy.
KIPR · Botball Explorer · Unit 5 Big Idea 1