VEX V5 PYTHON | 10.6 AUTONOMOUS CAPSTONE
Guide objective
Combine movement, sensors, mechanisms and telemetry into an autonomous routine with bounded recovery, then validate it through repeatable trials.
01 | THE CAPSTONE BRIEF
Build a short autonomous that approaches a pickup, collects an object, travels to a verified delivery location and releases it. Add one justified recovery branch and prove the routine through recorded trials.
The goal is repeatable physical outcomes, not simply reaching the final line of code. Start with a modest route your robot can complete consistently. Complexity earns its place only when the evidence supports it.
This capstone is game independent. Set your route duration, object handling and scoring criteria using your actual task and current competition rules. The six-second software example below is a teaching budget, not a competition period.
02 | WRITE THE ACCEPTANCE CRITERIA FIRST
| Requirement | Example measurable evidence |
|---|---|
| Correct start | Selected route and confirmed starting pose match the physical setup |
| Collection | Independent object observation confirms the required load |
| Navigation | Endpoint and heading meet declared measured tolerances |
| Delivery | Release is observed at a verified location |
| Timing | All actions finish within the declared overall deadline |
| Recovery | One allowed jam is cleared without repeated or unbounded attempts |
| Failure response | Mode exit, invalid feedback and unrecoverable fault revoke outputs |
| Traceability | Trial links code, configuration, hardware and event log |
Execution DONE, observed release and legal score are different outcomes. Evaluate scoring with physical evidence and current rules. Do not count a completion message as proof that an object entered a goal.
03 | BUILD THE ROUTE FROM CHECKED ACTIONS
Use the action interfaces and output ownership developed in sections 09 and 10. Each action has entry conditions, ongoing guards, progress evidence, a deadline and a defined exit.
| Action | Entry / completion evidence | On failure |
|---|---|---|
| Approach pickup | Valid localisation; measured pickup arrival and low motion | Stop and record failure |
| Collect | Pickup verified; object presence checked | Classify jam or stop |
| Dock | Object retained; goal identified and motion settled | Stop, do not release blindly |
| Release | Goal and stationary guards remain true; release observed | Stop and report incomplete delivery |
A route should advance only after an action reports checked completion. Preserve the shared run deadline through retries. Resetting the collection timer must not reset the whole autonomous budget.
04 | DECIDE WHAT CAN BE RECOVERED
A recoverable failure has a recognised cause, trustworthy feedback and a tested response that can improve the situation within the remaining budget.
| Condition | Illustrative policy | Reason |
|---|---|---|
| Intake jam at a verified pickup | Stop drive; one short reverse pulse; pause; recheck | A local mechanism problem may be cleared |
| Object still missing after retry | End the route | Repeated attempts consume time without proof |
| Pose lost or sensor invalid | End the route | Recovery motion would rely on untrusted state |
| Mode exit / cancellation | End immediately; no recovery | Autonomous no longer owns the outputs |
| Large control-loop gap | Fault and stop requests | Timing assumptions have broken |
| Wrong location before release | Stop; no blind release | A physical prerequisite is missing |
Do not automatically drive backwards after every fault. The space behind the robot may not be clear, pose may be wrong and a mechanism may be trapped.
A fallback route is a separate validated plan. Use it only when localisation, clearance, mechanism state and time permit. Skipping an action does not automatically establish the next action’s prerequisites.
05 | BOUND THE RECOVERY
- Classify the failure and stop the affected outputs.
- Verify the robot is settled at the intended pickup.
- Confirm reversal is permitted for this mechanism and operating state.
- Check the retry count and remaining budget.
- Apply one limited clearing pulse.
- Stop the intake and wait for a recheck interval.
- Verify the jam has cleared and reassess object presence.
- Continue only with valid prerequisites; otherwise end the run.
In the teaching model, reversal lasts at most 300 ms, recheck takes 200 ms and only one attempt is allowed. These values are illustrative. Validate output, direction, mechanical clearance and sensor response on your robot.
A time reserve is a policy gate, not a promise of completion. All later actions still obey their deadlines and the original overall deadline.
06 | COMPLETE COMPUTER-BASED SUPERVISOR MODEL
Save this as capstone_model.py and run it with Python 3 on a computer. It uses scripted observations to exercise the supervisor. It has no VEX imports, sensors, motor outputs or physical motion simulation.
python capstone_model.py
"""Computer-only supervisor model: scripted feedback, no VEX or physics."""
class Supervisor:
TERMINAL = ("DONE", "FAULT", "CANCELLED")
LIMITS = {"APPROACH": 2000, "COLLECT": 1200, "CLEAR_JAM": 300,
"RECHECK": 200, "DOCK": 2000, "RELEASE": 800}
def __init__(self):
self.state = "APPROACH"
self.entered_ms = 0
self.last_ms = None
self.retries = 0
self.output = (0, 0) # drive percent, intake percent
self.events = [(0, "APPROACH", "start")]
def enter(self, now, state, reason):
self.state = state
self.entered_ms = now
self.output = (0, 0)
self.events.append((now, state, reason))
def update(self, now, f):
self.output = (0, 0) # Default zero on every update.
if self.state in self.TERMINAL:
return self.output
if self.last_ms is not None and (now < self.last_ms or now - self.last_ms > 100):
self.enter(now, "FAULT", "control_gap")
return self.output
self.last_ms = now
if not f["enabled"] or not f["autonomous"]:
self.enter(now, "CANCELLED", "mode_exit")
elif not f["valid"]:
self.enter(now, "FAULT", "invalid_feedback")
elif now >= 6000:
self.enter(now, "FAULT", "run_deadline")
else:
age = now - self.entered_ms
if self.state == "CLEAR_JAM":
if not f["recovery_allowed"] or not f["stationary"]:
self.enter(now, "FAULT", "recovery_guard_lost")
elif age >= self.LIMITS[self.state]:
self.enter(now, "RECHECK", "clear_pulse_finished")
else:
self.output = (0, -10)
elif self.state == "RECHECK":
if not f["recovery_allowed"] or not f["stationary"]:
self.enter(now, "FAULT", "recovery_guard_lost")
elif age >= self.LIMITS[self.state]:
if f["jam"]:
self.enter(now, "FAULT", "jam_remains")
elif f["object"]:
self.enter(now, "DOCK", "object_verified_after_recovery")
else:
self.enter(now, "COLLECT", "retry_once")
elif age >= self.LIMITS[self.state]:
self.enter(now, "FAULT", "action_deadline")
elif self.state == "APPROACH":
if f["at_pickup"] and f["stationary"]:
self.enter(now, "COLLECT", "pickup_verified")
else:
self.output = (10, 0)
elif self.state == "COLLECT":
if not f["at_pickup"] or not f["stationary"]:
self.enter(now, "FAULT", "pickup_guard_lost")
elif f["jam"]:
if self.retries == 0 and f["recovery_allowed"] and now + 1800 < 6000:
self.retries = 1
self.enter(now, "CLEAR_JAM", "bounded_recovery")
else:
self.enter(now, "FAULT", "jam_not_recoverable")
elif f["object"]:
self.enter(now, "DOCK", "object_verified")
else:
self.output = (0, 15)
elif self.state == "DOCK":
if not f["object"]:
self.enter(now, "FAULT", "object_lost")
elif f["at_goal"] and f["stationary"]:
self.enter(now, "RELEASE", "goal_verified")
else:
self.output = (10, 0)
elif self.state == "RELEASE":
if not f["at_goal"] or not f["stationary"]:
self.enter(now, "FAULT", "release_guard_lost")
elif f["released"]:
self.enter(now, "DONE", "release_observed")
else:
self.output = (0, -15)
return self.output
def fail(self, now):
if self.state not in self.TERMINAL:
self.enter(now, "FAULT", "adapter_exception")
self.output = (0, 0)
def feedback(now, case):
# Scripted truth values, NOT sensor inference or a robot motion model.
return {"enabled": not (case == "cancel" and now >= 700),
"autonomous": True, "valid": not (case == "invalid" and now >= 700),
"at_pickup": now >= 400, "stationary": True,
"recovery_allowed": True,
"jam": (case == "persistent_jam" and now >= 600) or
(case == "recover" and 600 <= now < 900),
"object": now >= (1300 if case == "recover" else 1000),
"at_goal": now >= 1800, "released": now >= 2200}
def run_case(case):
model = Supervisor()
for now in range(0, 6020, 20):
try:
model.update(now, feedback(now, case))
except Exception:
model.fail(now)
raise
if model.state in model.TERMINAL:
break
assert model.output == (0, 0)
return model
if __name__ == "__main__":
for case in ("normal", "recover", "persistent_jam", "cancel", "invalid"):
model = run_case(case)
print(case, model.state, "retries:", model.retries)
for event in model.events:
print(event)
The normal and recovered cases reach DONE at 2200 ms in the script. A persistent jam becomes FAULT after one attempt; cancellation and invalid feedback terminate at 700 ms. Terminal updates remain at zero requests and never resume automatically.
The output tuple means drive percent and intake percent requests. Both transitions and terminal states request zero. The simplified forward request is not a navigation controller; it contains no heading correction or obstacle handling.
The scripted stationary flag is always true. This deliberately isolates policy testing and does not demonstrate braking, settling or physical guard detection. Replace the script with measured, fresh observations when integrating real actions.
The model’s jam and object flags are also supplied observations. It does not implement current-based jam detection, debounce, object counting, goal recognition or sensor freshness timestamps. Validate those upstream components separately.
07 | INTEGRATE WITH THE REAL V5 PROGRAM
Keep handwritten code below # Begin project code and preserve the generated configuration region. Use your existing configured devices; do not duplicate Brain, motors, sensors or Competition objects.
| Model input / output | Real integration requirement |
|---|---|
| enabled / autonomous | Read competition mode each cycle; revoke old session on handover |
| valid | Include sensor validity, freshness, calibration and plausibility |
| at_pickup / at_goal | Measured pose / recognised station within declared tolerances |
| stationary | Low measured translation and rotation sustained for a settling interval |
| jam / object / released | Debounced mechanism and object observations with tested thresholds |
| recovery_allowed | Mechanism clearance and verified state; not a default true flag |
| drive request | Replace with a checked navigation action and one drivetrain owner |
| intake request | Bounded mechanism adapter with travel / jam protection where required |
Use a monotonic elapsed run clock. The teaching loop checks every 20 ms and faults after a gap exceeding 100 ms. A same-loop watchdog cannot pre-empt a blocked device call. Keep operations non-blocking where supported and check again after calls.
Wrap the active session with exception handling and final cleanup. Stop each output group independently so a failed stop call does not prevent attempts to stop the other groups. Record cleanup failures. A stop request is not physical proof of stopped motion.
On driver-control handover, revoke the autonomous session before the new output owner starts. Do not let delayed autonomous callbacks overwrite driver requests. Prevent overlapping runs and require deliberate preparation for a new run.
Choose coast, brake or hold according to the mechanism and test the resulting stopping distance. Zero velocity request and the appropriate stop behaviour are separate implementation decisions.
08 | LOG THE DECISIONS, NOT JUST THE FINAL RESULT
Record run ID, code/configuration IDs, selected route, confirmed starting pose, elapsed time, state transitions, first failure, recovery count, exit outcome and cleanup status.
For a recovery, preserve the evidence before the jam, the guard decision, pulse start/end, recheck result and subsequent outcome. Bound your telemetry buffers and export after the active run so printing does not become a control delay.
The model keeps a small transition list for demonstration. The production logger should use the bounded storage and dropped-record reporting from 10.2, with useful measurements alongside events.
| Example event | Useful meaning |
|---|---|
| 600 ms: bounded_recovery | Jam classified and recovery prerequisites passed |
| 900 ms: clear_pulse_finished | Command duration ended; jam clearance not yet established |
| 1100 ms: retry_once | Recheck passed, object still missing, one collection retry |
| 2200 ms: release_observed | Supervisor saw the release input; physical scoring still needs assessment |
09 | TEST FAILURES BEFORE THE FULL ROUTE
Begin with computer-based policy tests, then test sensors and mechanisms independently, short movements, the nominal route and finally approved recovery scenarios.
| Test | Expected behaviour |
|---|---|
| Nominal observations | No recovery; delivery outcome checked |
| Single clearable jam | One bounded attempt and successful recheck |
| Persistent / second jam | Fault; no repeated recovery loop |
| Recovery permission removed | Fault; zero requests |
| Invalid feedback during reverse | Fault; reversal ends |
| Disable during any action | Cancel; outputs revoked |
| Missed control cycle | Gap fault before new requests |
| No pickup / no release observation | Action deadline, no false DONE |
| Overall deadline | Fault even if an action timer was restarted |
| Terminal update with enabled mode | No automatic restart |
Use scripted input or controlled sensor tests to inject faults first. Do not create a dangerous physical jam to test software. For hardware trials, choose a controlled mechanism setup with limited outputs and clear access to stop the robot.
10 | RUN A DECLARED VALIDATION SERIES
Freeze the code and settings, confirm calibration, define success and exclusion rules, then record every attempt under documented conditions. Use the repeated-trial workflow from 10.4.
Report nominal success, overall success including allowed recovery, recovery frequency, recovery success among attempted recoveries, failure reasons, timing and endpoint error. Show each numerator and denominator.
| Illustrative result from 20 eligible trials | Report |
|---|---|
| 16 complete without recovery | Nominal success: 16 / 20 |
| Four attempt recovery; three succeed | Recovery success: 3 / 4 |
| 19 meet all declared criteria | Overall success: 19 / 20 |
| One unrecoverable failure | Failure retained in denominator |
Injected-fault tests establish policy behaviour under those scenarios. Keep them separate from ordinary operating trials when estimating normal reliability. A route that needs recovery frequently may still have a collection or alignment problem.
Use fresh validation after choosing the final configuration. A high observed success rate from a small sample is useful evidence, not a guarantee. Include uncertainty, unknown outcomes and measurement availability as taught in 10.4.
11 | WHAT TO HAND OVER
- The saved native VEX project and matching readable source.
- A README with setup, port map, calibration, route selection and download instructions.
- Action contracts, state transitions and recovery policy.
- The tested commit, configuration ID and hardware revision.
- Nominal and fault-test logs with physical outcome evidence.
- A complete trial table, success-rate analysis and remaining limitations.
A teammate should be able to reproduce the test without relying on your memory. Explain when the robot should refuse to run and how a new session is prepared after failure.
12 | COMMON MISTAKES
- Retrying every fault: classify the cause and check prerequisites.
- Resetting the run deadline during recovery: preserve the original budget.
- Reversing while moving: verify physical settling and mechanism permission.
- Calling a reverse pulse a cleared jam: stop and recheck.
- Releasing at an estimated wrong station: require positive location evidence.
- Restarting after mode returns: require a new prepared session.
- Hiding recovery in the success total: report its frequency and outcomes.
- Calling simulated success validated hardware: retain physical trial evidence.
CHALLENGES | YOUR FINAL AUTONOMOUS
- Define one modest route and measurable acceptance criteria.
- Build it from checked actions with one owner per output.
- Add one bounded, justified recovery branch with a recheck.
- Run the supervisor model and explain every transition.
- Modify scripted feedback to lose recovery permission and to miss the release observation. Verify fault exits.
- Integrate real sensor guards and action controllers, then test each component.
- Record a declared series with nominal, recovered, failed and unknown outcomes visible.
- Tag the validated code and give a teammate the complete reproduction record.
Completion evidence
A route that meets declared physical criteria, bounded recovery that is tested rather than assumed, fault exits that revoke requests and a traceable validation report with honest limitations.
REFERENCES AND COURSE LINKS