Lesson 9 · Capstone Project
Lesson 9 · Capstone Project
Teacher mode is on. Toggle it off (bottom-right) to preview the student view.
This is it — every piece you've built, assembled into one autonomous system. The robot visits several destinations, plans with Dijkstra, senses obstacles with the rangefinder, reroutes on the fly, and remembers what it learned in a file so the next run is smarter. You won't write much new code; the challenge is integration — making working parts work together. That's exactly what real software engineers do.
Learning Objectives
By the end of this project you will be able to:
- Integrate Dijkstra, the Navigator, rangefinder detection, and file memory into one program
- Visit four or more destinations while detecting and avoiding obstacles
- Save and load obstacles so knowledge persists between runs
- Demonstrate learning: Run 2 outperforms Run 1, with numbers to prove it
- Debug integration issues where individually-working parts don't cooperate
The mission
Your robot is an autonomous delivery vehicle. It must visit four or more grid destinations in sequence, and for each leg: plan a path, drive it while watching for obstacles, and reroute whenever the rangefinder finds one. Everything you need already exists — Dijkstra (Lessons 4–5), the swap into Navigator (Lesson 6), detection (Lesson 7), and file memory (Lesson 8). Today you connect them.
You'll demonstrate with two runs: Run 1 (delete the obstacle file, discover obstacles the hard way, save them) and Run 2 (load the file, plan around known obstacles, reroute less). The drop in step count between runs is the learning.
What is the main challenge of the capstone?
Program structure
The whole program has a clear skeleton:
1. Load obstacles from file
2. Define destinations
3. For each destination:
While not arrived:
Compute a path with Dijkstra (from current position, current blocked list)
Walk the path, checking the rangefinder at each intersection
If obstacle: add to blocked list, recompute
If clear all the way: arrived
4. Save obstacles to file
5. Print results (steps, reroutes)
The blocked list is the thread tying it together: load_obstacles produces it,
Dijkstra consumes it, detection appends to it, and save_obstacles writes it out.
What single data structure connects loading, pathfinding, detection, and saving?
The navigation loop
The core is a nested loop — destinations on the outside, rerouting in the middle, walking a single path on the inside:
for dest in destinations:
arrived = False
while not arrived:
pathfinder = Dijkstra(current, blocked)
path = pathfinder.compute_path(dest)
if len(path) == 0:
print("No path to", dest); break
rerouted = False
for i in range(len(path) - 1):
distance = rangefinder.distance() # or simulate
if distance < THRESHOLD:
blocked.append(path[i + 1]) # obstacle ahead
reroute_count += 1
rerouted = True
break
current = path[i + 1] # drive one intersection
step_count += 1
if not rerouted:
arrived = True
When an obstacle appears mid-path, you break out, having added it to blocked — and the
while loop computes a fresh path from where you now stand.
When an obstacle is found partway along a path, why break out of the inner for loop?
Activity · Test in simulation, then drive
Get the whole thing working with a simulated rangefinder before the robot — verify
paths compute, obstacles register, the blocked list grows, and obstacles.txt is written
correctly. Then swap in the real sensor and the DifferentialDrive.
ACTUAL_OBSTACLES = [(1, 0), (2, 2), (1, 3)]
def simulate_rangefinder(next_pos):
return 8.0 if next_pos in ACTUAL_OBSTACLES else 50.0
Watch for the classic integration bugs: the blocked list getting reset between destinations
(it must persist across all of them), current not updating after a move, or forgetting to
make a new Dijkstra after the blocked list changes.
Which is a classic capstone integration bug to watch for?
The rubric and demo
You're graded on the integrated system, component by component:
| Category | Points | What it takes |
|---|---|---|
| Dijkstra pathfinding | 10 | Correct, shortest paths from the class |
| Navigator integration | 10 | Robot physically drives the computed paths |
| Obstacle detection | 10 | Rangefinder detects and updates the blocked list |
| Path recomputation | 10 | Reroutes correctly around discovered obstacles |
| Experience (file I/O) | 10 | Obstacles saved after Run 1, loaded for Run 2 |
| Demonstration | 5 | Run 1 discovers & reroutes; Run 2 avoids known obstacles |
| Code quality | 5 | Readable, uses functions/classes, meaningful comments |
For the demo, run twice and have your numbers ready: "Run 1 took X steps with Y reroutes; Run 2 took A steps with B reroutes." Finished early? Try an extension — live mid-segment detection, visiting the nearest destination next, obstacle expiration (re-check old obstacles after a few runs), or a text-grid map of the route.
In the demo, what evidence shows the robot 'learned'?
Real-world connections
You've built, in miniature, what real autonomous systems do:
SLAM
Real robots build and reuse maps of their environment — the grown-up version of your obstacle memory.
Self-driving
Sense, plan, act, and learn from fleet data — the same loop at massive scale.
System integration
Connecting tested components into a reliable whole is the core of professional software work.
Wrap-up
- What connects all the components? (The blocked list flowing file → Dijkstra → detection → file.)
- Why test in simulation before the robot? (Separate logic bugs from hardware bugs.)
- What proves the robot learned? (Run 2's measured improvement over Run 1.)