⚡ EZ Template · Advanced Autonomous

Exit Conditions & Chained Movements

The single biggest speed gain available in EZ Template. A 15-second autonomous that waits for full PID settle between every movement leaves 2–3 seconds of scoring time on the table. Chaining movements eliminates that waste entirely.

1
Why It Matters
2
pid_wait_until
3
Quick Chain
4
Swing Moves
5
Full Example
// Section 01
Why Exit Conditions Change Everything
Every time your autonomous finishes a movement, EZ Template waits for the PID to fully settle — the robot reaches its target, velocity drops to near zero, and the controller confirms it has stopped. This is safe and accurate. It is also slow.
The core insight: a robot does not need to fully stop between movements. It only needs to reach a point where starting the next movement produces accurate results. Exit conditions let you define exactly where that handoff happens — which is almost always much earlier than full settle.

The Cost of Full Settle — A Real Example

Here is a typical 3-movement autonomous using only pid_wait():

0.0s
Drive 24 inches
Robot drives forward at full PID speed
1.8s
PID settle wait
Robot decelerates, oscillates, settles at target — 0.6 seconds of waiting
2.4s
Turn 90°
Robot begins turning
3.9s
PID settle wait
Turn settles — another 0.5 seconds
4.4s
Drive 12 inches to score
Final movement begins
5.4s
Done — 1.1s wasted in settle waits

That 1.1 seconds of settle time does nothing for scoring. With chained movements, the robot starts the next action the moment it is ready — not when the PID controller has fully relaxed.

ℹ️
EZ Template provides three exit condition tools: pid_wait() (full settle, default), pid_wait_until(distance) (exit at a specific point in the movement), and pid_wait_quick_chain() (exit early and immediately start the next movement). You pick the right tool for each transition.
// Section 02
pid_wait_until( distance )
Exit the current movement early when the robot reaches a specific point along its path. Use this when you need to trigger a mechanism action mid-movement, or when the robot needs to start braking before the endpoint.

How It Works

pid_wait_until(x) tells EZ Template: “do not fully complete this movement — exit when the robot has traveled x inches (or degrees for turns).” The PID is still running and controlling the robot. You are just moving on from the wait condition earlier than full settle.

Without pid_wait_until
Drive 36 inches. Wait for full settle (robot stops completely). Then deploy intake. The robot has already traveled past the optimal pickup position by the time the intake deploys.
With pid_wait_until
Drive 36 inches. At 28 inches, deploy intake — the robot is still moving forward, timing the intake to be open exactly when it reaches the game element. Saves 0.3–0.5 seconds per pickup.

The Code Pattern

src/autons.cpp
// Example: Deploy intake during a drive movement chassis.pid_drive_set(36, 110); // Start driving 36 inches at 110 speed chassis.pid_wait_until(28); // Wait until 28 inches traveled... intake.move(127); // ...then deploy intake while still moving chassis.pid_wait(); // Now wait for the drive to fully finish // Example: Exit a turn early and immediately act chassis.pid_turn_set(90, 90); // Start turning 90 degrees chassis.pid_wait_until(70); // Exit at 70 degrees chassis.pid_wait(); // Complete the remaining 20 degrees normally
💡
You can use multiple pid_wait_until calls in a single movement. Set the drive, call pid_wait_until(15) to deploy one mechanism, then pid_wait_until(30) to trigger a second action, then pid_wait() to complete. Each call is a checkpoint along the same movement.

When to Use It

// Section 03
pid_wait_quick_chain()
The most powerful chaining tool. Exit the current movement as soon as velocity drops below a threshold, and immediately start the next movement. The robot never fully stops between actions.

What “Quick Chain” Actually Does

pid_wait_quick_chain() exits the current PID movement the moment the robot’s velocity drops below a small threshold — the robot is slowing down and about to stop, but has not fully settled yet. The next movement begins immediately, often while the robot still has a tiny amount of momentum from the previous one.

The result: movements flow into each other rather than stopping and restarting. The robot looks smoother, moves faster, and scores more in 15 seconds.

⚠️
Quick chain trades settle accuracy for speed. The robot will not be perfectly positioned at the end of a chained movement — it will be close. Use pid_wait() for movements that require precision (parking, scoring into a tight zone). Use pid_wait_quick_chain() for transition movements where the next action’s PID will correct any small error.

The Code Pattern

src/autons.cpp
// Without chaining — full settle after every movement (slow) chassis.pid_drive_set(24, 110); chassis.pid_wait(); // Full settle — 0.5s wasted chassis.pid_turn_set(90, 90); chassis.pid_wait(); // Full settle — another 0.4s wasted chassis.pid_drive_set(12, 110); chassis.pid_wait(); // Final settle — OK here, precision needed // With chaining — transitions are instant (fast) chassis.pid_drive_set(24, 110); chassis.pid_wait_quick_chain(); // Exit as velocity drops — no full stop chassis.pid_turn_set(90, 90); chassis.pid_wait_quick_chain(); // Exit turn early — immediately drives chassis.pid_drive_set(12, 110); chassis.pid_wait(); // Last movement: full settle for accuracy

When to Use Quick Chain vs Full Wait

Movement Type Use Reason
Transition between zonesquick_chainNext movement corrects small error
Approach for pickupquick_chain or wait_untilStart intake before full stop
Final approach for scoringpid_wait()Accuracy required — full settle
Park or climb positionpid_wait()Must stop in exact position
AWP task movementpid_wait()Reliability matters more than speed here
// Section 04
Swing Movements
A swing turn locks one side of the drive and swings the other — like a gate on a hinge. Significantly faster than a point turn for many routing situations.

Swing vs Point Turn

Point Turn
Both sides of the drive turn in opposite directions. Robot spins in place. Maximum rotation rate but the robot stays near the same position. Requires precise heading before driving again.
Swing Turn
One side locks (or runs slower). Other side drives. Robot arcs in a curve. Often faster for getting from A to B because the forward progress of the arc means you cover ground during the turn itself.

When Swing Beats Point Turn

If your next action after the turn requires being in a different location (not just a different heading at the same spot), a swing turn often gets you there faster because it combines rotation and travel into one movement.

EZ Template Swing Code

src/autons.cpp
// Left swing: left side locked, right side drives // Robot swings to the LEFT (left is the pivot point) chassis.pid_swing_set(ez::LEFT_SWING, 45, 90); chassis.pid_wait(); // Right swing: right side locked, left side drives // Robot swings to the RIGHT chassis.pid_swing_set(ez::RIGHT_SWING, -30, 90); chassis.pid_wait(); // Swing with a speed for the locked side (slows arc radius) // Third argument: locked side power (0 = fully locked, 30 = slight crawl) chassis.pid_swing_set(ez::LEFT_SWING, 90, 110, 20); chassis.pid_wait(); // Swing chained into next movement chassis.pid_swing_set(ez::RIGHT_SWING, 60, 100); chassis.pid_wait_quick_chain(); // Chain into the next drive chassis.pid_drive_set(18, 110); chassis.pid_wait();
💡
Swing PID uses its own constants. Add these to your initialize() the same way you set drive and turn PID constants. The EZ Template docs cover swing constants under “Swing Movements” — they are typically similar to turn constants but may need separate tuning since swing behavior differs.
// Section 05
A Complete Chained Autonomous Route
Putting it all together — a full 4-action autonomous using every technique from this guide. Compare the slow version (full wait) and the fast version (chained) side by side.

The Route: Score + Return to AWP Position

The robot needs to: drive 30 inches to a game element (intaking along the way), turn 90° to face the goal, drive 14 inches to score, then swing back 70° to reach the AWP position.

src/autons.cpp — Slow version (full waits)
// Total time: approximately 6.2 seconds chassis.pid_drive_set(30, 110); chassis.pid_wait(); // ← 0.6s settle waste intake.move(127); // intake ON after stop chassis.pid_turn_set(90, 90); chassis.pid_wait(); // ← 0.5s settle waste chassis.pid_drive_set(14, 80); chassis.pid_wait(); // ← 0.4s settle waste intake.move(-127); // eject after stop pros::delay(300); chassis.pid_swing_set(ez::LEFT_SWING, -70, 90); chassis.pid_wait(); // ← 0.5s settle waste // Grand total settle waste: ~2.0 seconds
src/autons.cpp — Fast version (chained)
// Total time: approximately 4.1 seconds (2 seconds faster!) chassis.pid_drive_set(30, 110); chassis.pid_wait_until(18); // ← intake starts at 18 inches intake.move(127); chassis.pid_wait_quick_chain(); // ← chain into turn chassis.pid_turn_set(90, 90); chassis.pid_wait_quick_chain(); // ← chain into drive chassis.pid_drive_set(14, 80); chassis.pid_wait_until(10); // ← eject starts at 10 inches intake.move(-127); chassis.pid_wait(); // ← full wait here: accuracy needed chassis.pid_swing_set(ez::LEFT_SWING, -70, 90); chassis.pid_wait_quick_chain(); // ← chain into whatever comes next // 2 seconds recovered for additional scoring
🏆
Build the route without chaining first. Get the unoptimized version working at 95% reliability, then add chaining one transition at a time. Test after each addition. Chaining rarely breaks routes — but if something does go wrong, you want to know exactly which transition caused it.

Quick Reference — Which Function to Use

⚙ STEM Highlight Engineering: Settling Time, Tolerance Bands & Control Performance
pid_wait() waits for the robot to reach a tolerance band around the target and stay there for a minimum time — this is the classic settling time criterion in control engineering. pid_wait_until() releases control at an intermediate error value, allowing chained movements. This is analogous to a multi-phase controller — full PID during acquisition, then early exit. The time saved (typically 200–800ms per movement) compounds across a 15-second autonomous: recovering 6 movements × 400ms = 2.4 seconds — enough for one complete scoring cycle.
🎤 Interview line: “pid_wait_until() implements an early-exit condition based on position error. In control engineering terms, we are replacing a full settling-time criterion with a position-threshold trigger, which trades final accuracy for throughput. We quantified the time savings across our full autonomous to justify the trade-off.”
🔬 Check for Understanding
pid_wait_quick_chain() exits the PID movement before it fully settles. What is the engineering trade-off?
There is no trade-off — it is always better to chain movements
Faster transition between movements, but the robot may not be exactly at the target position — subsequent movements start from a slightly imprecise position
The PID is disabled, so the robot drives open-loop
Chaining only works if movements are in the same direction
Related Guides
📍 Odometry →🔬 PID Diagnostics →🏆 Auton Strategy →
← ALL GUIDES