KIPR · Botball Explorer
Activity Sections

Unit 5 · Big Idea 1

The Second Attempt

Student Lab · Tracking What the Robot Believes About Itself

Unit Guiding Question
How can a machine operate reliably in an imperfect world?
Big Idea
Systems Must Handle Failure
AI Literacy Thread
A system can’t catch its own failures unless it keeps track of what it believes about itself.
CS1 Concepts
· Indexed State · Helper · Conditional State Updates
Game Context
Mission 7 — deliver 2 orange + 2 blue poms to two different PVC
What You Need
Explorer robot · full · 4 poms (2 orange, 2 blue) · two PVC enclosures · the game field · this lab sheet
Before you start: type your PIN in the box at the top of the page. Your teacher gave you this number. When you finish, press Submit & Download to turn in your work and save a copy.

Overview

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.

By the end of this activity you will be able to:

  • Declare and use a small array to hold related values as one structure.
  • Write helper functions that read and write specific elements of that array.
  • Update believed position only at real, verified reset checkpoints.
  • Explain why an untracked system can’t tell the difference between success and failure.

Phase 1 — The Mission: Hazard Containment

Watch Mission 7 video

Base Mission

11 points Final judged

One Blue Pom and one Orange Pom are the same PVC enclosure.

Bonus Mission

9 points Final judged

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.

Scores

  • One Blue Pom and one Orange Pom are the same PVC enclosure.
  • Multiple Blue Poms and one Orange Pom are the same PVC enclosure.
  • Two different enclosures each containing at least one Blue and one Orange Pom.

Does Not Score

  • A Blue Pom in one enclosure and an Orange Pom in a different enclosure.
  • Only one pom type in any enclosure.
  • The same enclosure used to satisfy both Base and Bonus Missions.

What counts

  • A pom counts as “in” an enclosure if any part of it crosses into the enclosure’s interior, or it’s the interior-facing surface.
  • Base and Bonus must use different enclosures — the field has three to choose from.
  • A pom used for the Base Mission can’t also be counted for the Bonus Mission.

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)?

Phase 2 — Concept: Arrays

What is an array?

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?

What is const?

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?

Phase 3 — Concept: Reading and Writing the Pose

Helper functions keep the array honest

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.

Measure your robot’s starting pose
What to measureYour 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 box0.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?

Phase 4 — Concept: Turn() Now Updates R Too

The finally does something

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.

yourname.h
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?

Phase 5 — Plan: Legs, Resets, and Prints

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.

Plan each leg of the run
#Leg (what the robot does)Library call(s)Pose updatePrint 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)?

Phase 6 — Build: The Run in main()

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.

yourname.h
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]);
}
main.c
// 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.

Phase 7 — Run It and Check the Belief Against Reality

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.

Compare believed pose to measured pose, at each print
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?

Phase 8 — Connect & Reflect

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…”

Extension Challenges

Finished early? Try one or more of these.

Extension A — A Third Enclosure

  • Could your pose array help you plan a path to a third PVC enclosure too? Sketch how you’d extend the run, including where a new reset would go.

Extension B — Detect the Failure, Don’t Just Log It

  • Right now you compare printed vs. measured pose by hand. Sketch (in words, not code) how a program could compare two pose values itself and decide “that’s too far off — something failed.”

Extension C — A Fourth Array Slot

  • If you added a 4th slot to track something else about the robot’s state during this run (battery voltage, pom count carried, elapsed time), what would you add, and why would it help catch a failure?

Extension D — Chart the Drift

  • Sketch a simple bar chart (or line chart) of the 5 gap sizes from your Phase 7 table, one bar per checkpoint.
  • Which checkpoint had the least drift? Does the chart make the pattern easier to see than the table did?

Extension E — Bundled Like an Object

  • In a language with objects (like Java or Python), your pose array plus its helper functions (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.
  • What’s one advantage of bundling pose[3] with its own functions into one object, instead of keeping the array and the functions separate the way we did?

Extension F — Passing the Array Itself

  • Right now, 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.
  • Why would passing a large array by reference be more efficient than copying all its values every time you call a function?

Extension G — Write the Guide

  • Imagine a teammate’s robot is drifting a lot more than yours. Using what you learned in Phase 7, write a short troubleshooting guide (3-4 steps) they could follow to find out why — without you being there to help directly.

When you are finished, press the button to turn in your work and save a copy.

KIPR · Botball Explorer · Unit 5 Big Idea 1