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

Choose thresholds before collecting validation results
RequirementExample measurable evidence
Correct startSelected route and confirmed starting pose match the physical setup
CollectionIndependent object observation confirms the required load
NavigationEndpoint and heading meet declared measured tolerances
DeliveryRelease is observed at a verified location
TimingAll actions finish within the declared overall deadline
RecoveryOne allowed jam is cleared without repeated or unbounded attempts
Failure responseMode exit, invalid feedback and unrecoverable fault revoke outputs
TraceabilityTrial 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.

Avoid treating timed motor operation as a measured outcome
ActionEntry / completion evidenceOn failure
Approach pickupValid localisation; measured pickup arrival and low motionStop and record failure
CollectPickup verified; object presence checkedClassify jam or stop
DockObject retained; goal identified and motion settledStop, do not release blindly
ReleaseGoal and stationary guards remain true; release observedStop 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.

Recovery policy must match the mechanism and measured conditions
ConditionIllustrative policyReason
Intake jam at a verified pickupStop drive; one short reverse pulse; pause; recheckA local mechanism problem may be cleared
Object still missing after retryEnd the routeRepeated attempts consume time without proof
Pose lost or sensor invalidEnd the routeRecovery motion would rely on untrusted state
Mode exit / cancellationEnd immediately; no recoveryAutonomous no longer owns the outputs
Large control-loop gapFault and stop requestsTiming assumptions have broken
Wrong location before releaseStop; no blind releaseA 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

  1. Classify the failure and stop the affected outputs.
  2. Verify the robot is settled at the intended pickup.
  3. Confirm reversal is permitted for this mechanism and operating state.
  4. Check the retry count and remaining budget.
  5. Apply one limited clearing pulse.
  6. Stop the intake and wait for a recheck interval.
  7. Verify the jam has cleared and reassess object presence.
  8. 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.

The supervisor policy is one layer, not the complete robot controller
Model input / outputReal integration requirement
enabled / autonomousRead competition mode each cycle; revoke old session on handover
validInclude sensor validity, freshness, calibration and plausibility
at_pickup / at_goalMeasured pose / recognised station within declared tolerances
stationaryLow measured translation and rotation sustained for a settling interval
jam / object / releasedDebounced mechanism and object observations with tested thresholds
recovery_allowedMechanism clearance and verified state; not a default true flag
drive requestReplace with a checked navigation action and one drivetrain owner
intake requestBounded 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.

Explain each event label precisely
Example eventUseful meaning
600 ms: bounded_recoveryJam classified and recovery prerequisites passed
900 ms: clear_pulse_finishedCommand duration ended; jam clearance not yet established
1100 ms: retry_onceRecheck passed, object still missing, one collection retry
2200 ms: release_observedSupervisor 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 policy boundaries and transitions, not just the happy path
TestExpected behaviour
Nominal observationsNo recovery; delivery outcome checked
Single clearable jamOne bounded attempt and successful recheck
Persistent / second jamFault; no repeated recovery loop
Recovery permission removedFault; zero requests
Invalid feedback during reverseFault; reversal ends
Disable during any actionCancel; outputs revoked
Missed control cycleGap fault before new requests
No pickup / no release observationAction deadline, no false DONE
Overall deadlineFault even if an action timer was restarted
Terminal update with enabled modeNo 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 counts, not results from this software model
Illustrative result from 20 eligible trialsReport
16 complete without recoveryNominal success: 16 / 20
Four attempt recovery; three succeedRecovery success: 3 / 4
19 meet all declared criteriaOverall success: 19 / 20
One unrecoverable failureFailure 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

  1. Define one modest route and measurable acceptance criteria.
  2. Build it from checked actions with one owner per output.
  3. Add one bounded, justified recovery branch with a recheck.
  4. Run the supervisor model and explain every transition.
  5. Modify scripted feedback to lose recovery permission and to miss the release observation. Verify fault exits.
  6. Integrate real sensor guards and action controllers, then test each component.
  7. Record a declared series with nominal, recovered, failed and unknown outcomes visible.
  8. 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