Feedforward Control: kS, kV, kA
A DC motor is physics. It has friction to overcome, back-EMF proportional to its speed, and inertia that resists acceleration. If you can model those three properties mathematically, you can predict the exact voltage needed for any desired speed — instantly, without waiting for an error. That model is feedforward control.
By the end of this lesson, you will:
- Explain the physical meaning of kS, kV, and kA in terms of motor forces and voltage
- State the feedforward formula: V = kS·sgn(v) + kV·v + kA·a
- Calculate a predicted output voltage given kS, kV, kA values and a target velocity and acceleration
- Use the interactive calculator to build intuition for how each gain contributes to the output
- Describe at a conceptual level how WPILib's SysId tool measures these constants from a real mechanism
- Explain why kA is often 0 for velocity-controlled mechanisms and when it matters
Prediction Before Reaction
In Lesson 1, we established that open-loop control has no awareness of the system it's commanding. PID (which comes in Lesson 5) is aware — but it's reactive: it waits for an error to develop before applying a correction. Waiting costs time. In a 50 Hz control loop, one missed update cycle is 20 ms of uncorrected error.
Feedforward control is different. It doesn't wait for error. Instead, it asks: "Given a desired speed, what voltage does the physics of this motor require right now?" Then it applies that voltage immediately, before any error exists. The question "does it work?" is answered by whether the physics model is accurate, not by measuring error and correcting.
The outfielder analogy from the project knowledge is exactly right: feedforward is the outfielder running to the predicted landing spot as soon as the ball is hit — not waiting until the ball is overhead to decide where to go. Feedback (PID) is the correction step: looking up and adjusting the last few steps. Feedforward handles 90% of the work instantly. PID handles the remaining 10%.
The DC Motor: Three Forces, Three Constants
Every DC motor in FRC obeys the same physics. When you apply a voltage, three things happen simultaneously:
The Three Gains — Click Each to Learn
The Feedforward Formula
SimpleMotorFeedforward — the prediction equation
sgn(v) is the sign of velocity: +1 when moving forward, −1 when moving backward. This term always opposes the direction of motion, modeling static friction as a constant drag that must be overcome before any useful motion happens.
Interactive Feedforward Calculator
Adjust the sliders to build intuition for how each gain contributes to the predicted output voltage. These are realistic values for a typical Falcon 500 drive module.
How These Constants Are Measured: SysId
kS, kV, and kA are not guessed — they are measured from the physical mechanism through a process called system identification. WPILib's SysId tool automates this completely. It runs two tests:
- Quasistatic test: slowly ramps voltage up from 0V. Captures the relationship between voltage and velocity at many points. The slope of the voltage-vs-velocity line is kV; the y-intercept (minimum voltage to start moving) is kS.
- Dynamic test: applies a sudden voltage step. Measures how fast the mechanism accelerates. The ratio of voltage-to-acceleration is kA.
SysId then runs a regression analysis on the collected data and outputs kS, kV, and kA values calibrated to your specific mechanism. Unit 8 covers SysId in detail in Lesson 3 (SimpleMotorFeedforward) and the broader characterization workflow in the tuning methodology lessons. For now, know that:
- kS typical range: 0.1–0.5V for most FRC motors and gearboxes
- kV typical range: 1.5–4.0 V·s/m for drivetrain; higher for slower mechanisms
- kA typical range: 0.05–0.3 V·s²/m; often negligible at constant velocity (kA=0 is a common starting point)
For mechanisms running at constant velocity — a shooter flywheel maintaining a steady RPM, a drive motor holding a constant speed — the acceleration is zero and the kA term vanishes. V_ff = kS·sgn(v) + kV·v is sufficient. kA becomes important when you're profiling motion (deliberately accelerating a mechanism through a speed ramp) or when acceleration is large enough to produce visible lag. For most FRC velocity control applications, starting with kA=0 and adding it later if needed is the correct approach.
SimpleMotorFeedforward in Code
public static final double kShooterKS = 0.23; // V
public static final double kShooterKV = 0.12; // V·s/rot
public static final double kShooterKA = 0.01; // V·s²/rot (small)
// In ShooterSubsystem.java
private final SimpleMotorFeedforward m_ff =
new SimpleMotorFeedforward(
Constants.kShooterKS,
Constants.kShooterKV,
Constants.kShooterKA);
// In a command's execute() or the subsystem's setVelocity() method
public void setVelocityRPS(double targetRPS) {
// Feedforward predicts the voltage needed
double ffVolts = m_ff.calculate(targetRPS);
// Command the motor with the predicted voltage
m_motor.setVoltage(ffVolts);
// Publish for tuning visibility
SmartDashboard.putNumber("Shooter/FFVolts", ffVolts);
SmartDashboard.putNumber("Shooter/ActualRPS",
m_motor.getVelocity().getValueAsDouble());
}
// With acceleration (for profiled motion):
double ffVolts = m_ff.calculate(currentVelocity, targetVelocity,
0.020); // dt in seconds
Feedforward outputs a voltage prediction. That voltage must be passed to setVoltage(), not set(). The set() method takes a percentage of battery voltage — if you pass a feedforward voltage to it, the motor receives feedforward_voltage × battery_voltage, which is wrong (and dangerous — high at low battery, dangerously high at full battery). setVoltage() normalizes the voltage against the measured battery voltage internally, ensuring the motor sees the correct physical voltage regardless of battery state. This is the most common feedforward implementation mistake in FRC code.
After implementing feedforward, verify it before adding PID:
- Command a single target velocity (e.g., 50 RPS for a shooter wheel). Publish both the commanded voltage (
ffVolts) and the actual velocity to SmartDashboard. With just feedforward (no PID), the actual velocity should reach within 5–10% of the target. If it consistently undershoots by 20%+, kV is too small. If it overshoots, kV is too large. - Verify that
setVoltage()is used, notset(). Add a temporary check: publishRobotController.getBatteryVoltage()alongside the commanded voltage. If ffVolts divided by battery_voltage equals what you'd pass toset(), the conversion is correct. If not, you're using the wrong method. - Command velocity zero (
setVelocityRPS(0)). The motor should stop, not spin slowly. If it continues spinning at a small positive velocity, kS is too large — the static friction voltage at zero is still keeping it moving. Reduce kS until zero command produces zero motion.
Knowledge Check
1. A Falcon 500 driving a shooter flywheel has: kS = 0.25V, kV = 0.12 V·s/rot, kA = 0.0. The target is 80 RPS with no acceleration change. What voltage does the feedforward predict?
2. You run a shooter wheel with feedforward only (no PID). The target is 4000 RPM. SmartDashboard shows the actual RPM at 3700 RPM when holding steady. No game piece is loaded. What does this tell you about kV?
3. You implement a feedforward shooter controller with m_motor.set(ffVolts) instead of m_motor.setVoltage(ffVolts). The battery is at 10V. The feedforward predicts 7V. What voltage does the motor actually receive?
Build a Feedforward Controller
- Choose a mechanism with a velocity target: shooter flywheel, conveyor belt, or drive motor. Start with estimated constants: kS = 0.25, kV = 0.12 (V·s/rot), kA = 0. Create a
SimpleMotorFeedforwardfield in your subsystem. ImplementsetVelocityRPS(double)that callsm_ff.calculate(targetRPS)and passes the result tosetVoltage(). - Deploy and command a target velocity. Publish the feedforward output voltage and the actual velocity. Observe: does the velocity reach the target? Is it consistently under or over? Adjust kV by ±10% and re-test until the steady-state velocity is within 5% of the target. This is manual kV tuning — SysId automates this in Lesson 3, but doing it manually first builds understanding.
- Now deliberately test what happens with
set()instead ofsetVoltage(). Command a moderate target velocity and compare the actual behavior. Notice the difference in speed at different battery voltages. Then switch back tosetVoltage()and confirm the velocity is more consistent. This exercise makes the setVoltage() vs set() distinction tactile, not just theoretical. - Stretch goal: Use the interactive calculator in this lesson to predict what voltage your mechanism should receive at two different target velocities. Then command those velocities and measure the SmartDashboard feedforward output voltage. How close are the calculator predictions to the actual code output? Any differences would be due to differences in your kS/kV values vs. the calculator defaults.