Why Open-Loop Control Isn't Enough
In Unit 5 you wrote m_motor.set(0.5). The motor spun. It felt like control. It wasn't — not really. Open-loop control is a command without awareness. It cannot know what the motor is actually doing, and it cannot adapt when the physical world doesn't cooperate. This lesson explains what open-loop control is, where it breaks down, and why the rest of Unit 8 exists.
By the end of this lesson, you will:
- Define open-loop control and explain why it cannot guarantee a specific output
- Identify the three main sources of open-loop error in FRC mechanisms: battery voltage sag, load variation, and friction
- Explain why percent-output commands produce different physical speeds at different battery voltages
- Describe the four levels of the control hierarchy — open-loop, feedforward, feedback, combined — and what each adds
- Identify at least three FRC mechanisms where open-loop control produces measurably wrong behavior
What "Open-Loop" Actually Means
A control loop is open when there is no feedback path from the output back to the input. You send a command. The mechanism executes it. The mechanism never reports back whether it achieved what you asked for. The loop has no closing path — hence "open."
In FRC code, open-loop control is m_motor.set(0.5): command 50% output and assume the motor does what 50% output means. The assumption is the problem. That 50% command does not control speed, does not control position, does not control force. It controls one thing: the fraction of battery voltage applied to the motor. And that fraction produces wildly different physical outcomes depending on conditions.
Open-loop control is appropriate for mechanisms where the output just needs to be "running" or "not running" — an intake roller that picks up game pieces, a conveyor that moves pieces toward a shooter. These mechanisms don't require a specific speed or position; any reasonable speed works. The problems begin when your mechanism needs to reach a specific speed (shooter flywheel), a specific position (arm angle, elevator height), or maintain constant behavior across matches (autonomous routines).
The Three Open-Loop Failure Modes
👇 Click each card to see what open-loop can't account forA fully charged battery supplies ~12.5V. After 2 minutes of driving, it may be at 11.0V. At 9.5V late in the match, the same set(0.5) produces 4.75V instead of 6.25V — 24% less. Your shooter spins slower. Your autonomous path is shorter. Your arm falls short of its target. Open-loop code is unaware of any of this.
A shooter flywheel at 0.6 output with an empty chamber spins at 4000 RPM. With a game piece loaded and pressing against the wheels, the same command might produce 3400 RPM — 15% slower. The ball arrives at the goal on a different trajectory. Open-loop doesn't know a piece is present; closed-loop would immediately compensate.
A freshly assembled gearbox with new bearings has less friction than one after 50 matches of competition. Grease migrates. Bearings wear. Gear mesh tightens as bolts settle. The open-loop command that worked at bag night might produce 8% less speed at week six because the mechanical system has changed. Open-loop is calibrated once and drifts forever.
The Voltage Sag Problem — Made Concrete
Adjust the sliders below to see how the same percent-output command produces dramatically different actual voltages — and therefore different actual speeds — depending on battery state and motor demand.
Where It Breaks in Competition
Three mechanisms illustrate exactly where open-loop fails — and why closed-loop control exists:
The Control Hierarchy
Open-loop → feedforward → feedback → combined isn't a list of options. It's a progression. Each level adds a layer of intelligence that addresses a specific failure mode of the level below it. Unit 8 walks this hierarchy from bottom to top.
I can identify a team's control approach in the first 30 seconds of their driver practice. Open-loop robots have a drift: each shot is slightly different from the last, autonomous paths are shorter late in the match, mechanisms hesitate or overshoot. Feedforward robots snap into position quickly but might oscillate slightly at steady state. Combined feedforward + PID robots are the ones where the arm just stops at the right position, every time, without a visible approach phase. The difference isn't magic — it's understanding the physics of what the motor is actually doing and writing code that accounts for it.
Before building any feedforward or PID controller, observe the open-loop problem directly:
- Command your shooter flywheel at 60% output (
m_motor.set(0.6)). PublishgetVelocity().getValueAsDouble() * kWheelCircumferenceto SmartDashboard. Record the velocity at three points: fresh battery, after 5 minutes of driving, after 10 minutes. The drop should be visible — 5–15% reduction is typical. - Command your arm to move to a setpoint with open-loop. Record the final angle with a fresh battery. Then lower the battery to 11V (drive aggressively to discharge it). Command the same setpoint. Record the new final angle. The difference is your open-loop position error due to battery sag.
- This is not a debugging exercise — this is calibration. You need to see the problem to understand why feedforward exists. Teams that never observe open-loop failure don't understand what they're building when they implement feedforward.
Knowledge Check
1. A robot's autonomous routine drives 2 meters forward and scores a game piece in testing. At competition, the same code drives only 1.82 meters. No code was changed. What is the most likely cause?
2. The team programs an intake roller at set(0.4). It works reliably at the start of a match. By Q3, the intake occasionally misses game pieces. No code changed. What systemic cause should be investigated first?
3. Feedforward control is described as "predictive" and PID as "reactive." Why is combining both better than using either one alone?
Observe Open-Loop Variability
- Choose a mechanism on your robot — a drive motor, a shooter flywheel, or an arm motor. Write a simple open-loop command:
m_motor.set(0.5). Publish the actual velocity to SmartDashboard using the encoder feedback. Measure the velocity three times: immediately after enabling, after 2 minutes of heavy driving, and after 5 minutes. Record the three values. Calculate the percentage change from start to end. - Now calculate what feedforward would need to compensate: if the battery dropped from 12.5V to 10.5V and you wanted to maintain the same physical speed, what percent-output command would be needed at 10.5V to match the original output at 12.5V? (Hint: output = desired_voltage / battery_voltage. Desired voltage = 0.5 × 12.5 = 6.25V. At 10.5V: 6.25 / 10.5 = 59.5%.)
- Look at the four levels of the control hierarchy in this lesson. For each mechanism on your robot (intake, shooter, arm, elevator), decide which level is appropriate and write a one-sentence justification. Not all mechanisms need Level 4 — the intake might be fine at Level 1. The decision should match the precision requirements of the mechanism.
- Stretch goal: Research WPILib's
MotorSafetywatchdog — what happens if a motor is not commanded within 100 ms? How does this interact with open-loop control? This is a real safety feature that prevents runaway motors from stuck code. Understand it before it surprises you at competition.