Closed-Loop Mechanisms That Oscillate, Sag, or Stall
Symptom A: It oscillates / buzzes at the setpoint
Section titled “Symptom A: It oscillates / buzzes at the setpoint”Usually too much kP or kD , or a feedback delay. Fix: back kP off (or, per WPILib’s guidance for position loops, increase kD instead) until the oscillation stops — WPILib’s tuning docs describe this qualitatively, not as a fixed percentage cut. Confirm your loop reads a fresh sensor each iteration and that you’re not double-applying feedforward and integral windup. Lower the velocity/acceleration constraints to see if the mechanism is being asked to move impossibly fast.
Symptom B: It sags or drifts under gravity
Section titled “Symptom B: It sags or drifts under gravity”Missing or wrong gravity feedforward. For an arm , kG must be multiplied by cos(angle) (use ArmFeedforward, which does this); a constant kG on an arm will hold at one angle and sag at others. For an elevator , kG is constant (use ElevatorFeedforward). Tune kG first, raise it until the mechanism just holds against gravity without drifting.
Symptom C: It runs away or fights itself
Section titled “Symptom C: It runs away or fights itself”Classic encoder phase / sign error or wrong conversion factor. The controller sees the mechanism move the ‘wrong’ way and commands harder, accelerating the runaway. Diagnose: disable the motor, move the mechanism by hand, and confirm the reported position increases in the commanded-positive direction. Fix the encoder inversion or conversion factor (counts-to-radians/meters) before touching gains.
Symptom D: It slams into the setpoint
Section titled “Symptom D: It slams into the setpoint”No motion profile, or profile constraints set too aggressively. Use a ProfiledPIDController / TrapezoidProfile so the controller chases a physically realistic position+velocity+acceleration trajectory instead of an instantaneous step. WPILib recommends starting with modest constraints and increasing.
The correct tuning order (don’t improvise)
Section titled “The correct tuning order (don’t improvise)”For gravity-loaded mechanisms, WPILib’s sequence is: kG -> kV -> kA -> kP, then add kD only if needed, and find kS via SysId. Tuning kP before kG is the most common reason a mechanism ‘won’t tune’, the feedback is fighting uncompensated gravity.
Use SysId, then refine
Section titled “Use SysId, then refine”Run a SysId quasistatic + dynamic routine to get kS/kV/kA/(kG) empirically. These get you most of the way; only then hand-tune kP/kD for the last bit of tracking.
Workflow
Section titled “Workflow”- Verify sensor direction/scale by hand.
- Add/verify the correct gravity feedforward (arm = cosine, elevator = constant).
- Set conservative profile constraints.
- Characterize with SysId; apply kS/kV/kA/kG.
- Raise kP until it just starts to oscillate, then back it off (or add kD) until the oscillation stops — WPILib doesn’t specify a fixed percentage.
Key takeaways
Section titled “Key takeaways”- Tune in the order kG -> kV -> kA -> kP; tuning kP first while gravity is uncompensated is why mechanisms ‘won’t tune’.
- Sag means missing gravity FF: arms need cosine-scaled kG (ArmFeedforward), elevators need a constant kG (ElevatorFeedforward).
- Runaway is almost always an encoder sign/conversion error, verify by hand before changing gains, and use a motion profile to stop slamming.
This lesson was adapted from learnfrc.com.
