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 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. A list 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 work, but they are not obviously connected.
x = 0.0
y = 0.0
heading = 0.0
# One list stores three related values in one structure.
pose = [0.0, 0.0, 0.0]Think of pose as one locker-bank holding 3 numbered lockers. Locker pose[0], locker pose[1], locker pose[2] — counting starts at 0, so a 3-locker list’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 a list? Why does grouping x, y, and heading into one pose list make more sense here than three separate variables?
A bare pose[0] doesn’t say what it means. Give the index a name instead, so pose[POSE_X] reads exactly like what it is:
POSE_X = 0
POSE_Y = 1
POSE_R = 2 # R = heading, in degrees
# now pose[POSE_X] means exactly what it says, instead of a bare, meaningless pose[0]POSE_X is written in ALL_CAPS on purpose — that’s a Python convention meaning “treat this as a constant; don’t reassign it.” But notice it’s still just a regular variable — nothing in the language actually stops you from writing POSE_X = 5 later and breaking everything that depends on it. The ALL_CAPS name is a promise you make to yourself and anyone reading your code, not a rule Python enforces for you.
What does writing a name in ALL_CAPS signal to someone reading your code? Since Python doesn’t actually stop you from changing it, why write it that way at all?
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 list 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(direction, angle) already returned True for success or False for an invalid direction — 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.
def Turn(direction, angle):
if direction == 'L' or direction == 'l':
# ...existing tick-turn logic for a left turn...
pose[POSE_R] += angle # only on a real, successful turn
return True
elif direction == 'R' or direction == 'r':
# ...existing tick-turn logic for a right turn...
pose[POSE_R] -= angle
return True
else:
print(f"Invalid direction: {direction}")
return False # No movement happened, so do not change the 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 list, the index names, and the helper functions to your library. Then write the run in main(), following your Phase 5 plan.
POSE_X = 0
POSE_Y = 1
POSE_R = 2
pose = [0.0, 0.0, 0.0] # These values represent the believed x, y, and heading.
def initPose(startX, startY, startR):
pose[POSE_X] = startX
pose[POSE_Y] = startY
pose[POSE_R] = startR
def setX(knownX):
pose[POSE_X] = knownX
def setY(knownY):
pose[POSE_Y] = knownY
def printPose():
print(f"Pose: x={pose[POSE_X]:.2f} y={pose[POSE_Y]:.2f} R={pose[POSE_R]:.2f}")#!/usr/bin/python3
# Unit 5, Big Idea 1: The Second Attempt
# Name: _______________________ Date: ___________
import os, sys
sys.path.append("/usr/lib")
import _kipr as k
from yourname import * # your full library
def main():
k.enable_servo(0)
k.enable_servo(1)
# ===== INITIALIZE BELIEF =====
initPose(START_X, START_Y, 0.0) # measured wheel-centerpoint, facing out
printPose() # PRINT 1 reports the starting belief.
# ===== VERIFY START =====
back_until_pressed() # backward touch against the wall
setY(0.0) # RESET #1 records the known truth that 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 reports the pose after drop-off 1.
# ===== LEG 2: Move pom 2 (blue) to Enclosure A. The Base Mission is complete. =====
# Drive(...) / Turn(...) to pom 2, pick it up
# Drive(...) / Turn(...) to Enclosure A, drop it
printPose() # PRINT 3 reports the pose after drop-off 2.
# ===== RESET before crossing to the second enclosure =====
square_up() # known heading/position against a line
setX(KNOWN_X) # RESET #2 records the 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 reports the pose after drop-off 3.
# ===== LEG 4: Move pom 4 (blue) to Enclosure B. The Bonus Mission is complete. =====
# Drive(...) / Turn(...) to pom 4, pick it up
# Drive(...) / Turn(...) to Enclosure B, drop it
printPose() # PRINT 5 reports the final belief.
main()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 a list, and why did grouping x, y, and R into pose 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.
pose list 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.pose with its own functions into one object, instead of keeping the list and the functions separate the way we did?printPose() and friends reach out and grab the global pose list directly. Try rewriting printPose to instead take the list as a : def printPose(p): called as printPose(pose). Python passes the list itself — the function works with the exact same list in memory, not a copy. You didn’t have to ask for that; it’s just how Python handles lists.def addOne(n): n = n + 1) and you called it with a variable, would the caller’s variable change? Try it. Why does a list behave differently from a plain number when you pass it into a function?When you are finished, press the button to turn in your work and save a copy.
KIPR · Botball Explorer · Unit 5 Big Idea 1