Unit 7: Advanced FRC Coding
Kinematics tells a swerve module what to do (e.g., "go to 45 degrees"). The control system figures out how to do it. For this, we use two powerful but different strategies: PID control for steering and Feedforward control for driving.
The goal of steering is to turn the wheel to an exact angle as quickly and accurately as possible. A PID Controller is a feedback loop that constantly looks at the difference between where the wheel is and where it wants to be and adjusts its output accordingly.
// In the SwerveModule class...
private final PIDController turningPidController;
public SwerveModule(...) {
turningPidController = new PIDController(kP, kI, kD);
// This is crucial for swerve: it makes the PID controller understand circular movement (like a compass).
// Turning from 359° to 1° is a small 2° turn, not a huge 358° turn.
turningPidController.enableContinuousInput(-Math.PI, Math.PI);
}
public void setDesiredState(SwerveModuleState desiredState) {
// Use the PID controller to calculate the output for the turning motor.
double pidOutput = turningPidController.calculate(getTurningAngle(), desiredState.angle.getRadians());
turningMotor.set(pidOutput);
// ... drive motor logic ...
}
For the drive wheels, we use a more predictive method: a Feedforward Controller. While PID is reactive (it corrects errors after they happen), Feedforward is predictive. It uses a mathematical model of the motor to predict the exact voltage needed to achieve a desired speed without waiting for an error.
Think of an outfielder catching a fly ball. Based on the initial trajectory, they immediately run to a spot where they predict the ball will land (Feedforward). While running, they look up to make small corrections (Feedback/PID). Feedforward does 95% of the work upfront.
WPILib's `SimpleMotorFeedforward` class uses a few key constants (found using the SysId tool) to model our motor: `kS` (to overcome static friction), `kV` (to hold velocity), and `kA` (to accelerate).
// In the SwerveModule class...
private final SimpleMotorFeedforward driveFeedforward;
public SwerveModule(...) {
driveFeedforward = new SimpleMotorFeedforward(kS, kV, kA);
}
public void setDesiredState(SwerveModuleState desiredState) {
// Use the feedforward controller to predict the voltage for the drive motor.
double ffOutput = driveFeedforward.calculate(desiredState.speedMetersPerSecond);
driveMotor.setVoltage(ffOutput);
// ... turning motor logic ...
}
Question: Which control strategy is "predictive" and uses a physical model of the motor to calculate the necessary output, and which is "reactive," constantly correcting for errors?