A shooter has to reach a consistent surface speed before it launches, or the shots scatter. We'll run a NEO Vortex (free speed 6784 RPM, Kv 565) on a SPARK Flex in closed-loop velocity mode, using REVLib 2025's declarative configuration API.
The 2025 config model#
In REVLib 2025 you no longer call setters one at a time on the controller. Instead you build a SparkFlexConfig and configure() it once, with explicit reset and persist modes:
private final SparkFlex m_motor = new SparkFlex(31, MotorType.kBrushless);
private final SparkClosedLoopController m_pid = m_motor.getClosedLoopController();
private final RelativeEncoder m_enc = m_motor.getEncoder();
public Shooter() {
SparkFlexConfig config = new SparkFlexConfig();
config.closedLoop
.feedbackSensor(FeedbackSensor.kPrimaryEncoder)
.pid(0.0001, 0.0, 0.0) // velocity P,I,D
.velocityFF(1.0 / 6784.0); // ~1/free-speed-RPM for a NEO Vortex (6784 RPM)
config.smartCurrentLimit(60);
m_motor.configure(config,
ResetMode.kResetSafeParameters,
PersistMode.kPersistParameters);
}
PersistMode.kPersistParameters writes to flash, so the config survives a brown-out reboot. That is worth doing for a one-time setup, but never call it every loop: flash writes block CAN comms.
Command a target RPM#
Command the velocity through the SparkClosedLoopController with setReference(). REVLib 2026 deprecates that method in favor of an identical setSetpoint(), so use that one on the 2026 library:
private static final double kTargetRpm = 4800;
public Command spinUp() {
return run(() ->
m_pid.setReference(kTargetRpm, ControlType.kVelocity));
}
public boolean atSpeed() {
return Math.abs(m_enc.getVelocity() - kTargetRpm) < 100; // RPM tolerance
}
Gate the feeder on "at speed"#
Expose atSpeed() as a Trigger and run the feeder only when the flywheel is ready. This is the BoVLB best practice of asking yes/no questions in problem-domain language:
Trigger ready = new Trigger(m_shooter::atSpeed);
// hold to spin up
m_driver.rightTrigger().whileTrue(m_shooter.spinUp());
// feed only once the wheel has recovered to speed
m_driver.rightTrigger().and(ready).whileTrue(m_feeder.feed());
Why velocityFF matters more than P#
A flywheel's steady-state voltage is almost entirely feedforward: V = kV * rpm. If velocityFF is set correctly, the controller jumps to near the right voltage immediately, and P only has to trim the last few percent. Teams that leave FF at zero and crank P instead get a sluggish, oscillating flywheel that dips badly when a game piece loads it. The SPARK's velocityFF multiplies the RPM setpoint to produce a duty-cycle output, so its value is roughly 1 / free-speed-RPM. Find it from a SysId run, or empirically: command a fixed duty cycle, read the steady RPM, and velocityFF = appliedOutput / rpm. (REVLib is moving toward a feedForward config with explicit kS/kV terms, but velocityFF() still works in 2025.)
Plot m_enc.getVelocity() against the setpoint in AdvantageScope. A good shooter recovers to within tolerance in well under half a second after each shot.
the part worth keeping
Key takeaways
- REVLib 2025 uses declarative SparkFlexConfig/SparkMaxConfig objects applied with configure(), not per-parameter setters.
- In REVLib 2025 use SparkClosedLoopController.setSetpoint() -- setReference() is deprecated.
- Use PersistMode.kPersistParameters once at setup; never persist every loop -- flash writes block CAN.
- Flywheels are feedforward-dominated: set velocityFF (~1/Kv) correctly and keep P small; gate the feeder on an atSpeed() Trigger.
Programming, Controls & SensorsWorked Examples and Mini-Projectslesson 2 of 5
Keep going
Take the quiz+10 XP with an accountMore in Worked Examples and Mini-Projects
where this came from
Sources and corrections
This lesson is AI-assisted: drafted from primary sources, then reviewed and edited by hand. Errors still get through. When one is reported we fix it and write down what changed, in public, in the corrections log.
sources and further reading
- docs.revrobotics.comREVLib: Velocity Control Mode
- docs.revrobotics.comREVLib: Configuring a SPARK
- docs.revrobotics.comMigrating to REVLib 2025
clipped to this lesson
Articles that go further on this
The lesson gets you through the topic. These go wider on it, and they read in one sitting.
- 17 min readFRC Flywheel Shooter Design: Compression, Speed, Backspin, and HoodingHow to design an FRC flywheel shooter: exit velocity, compression, flywheel inertia and RPM recovery, single vs dual wheels, backspin, hooding, motors, and tuning./blogread it
- 20 min readREV Robotics for FRC: NEO Motors, Spark MAX/Flex, PDH & MAXSwerveA practical guide to the REV Robotics FRC ecosystem: NEO, NEO Vortex and NEO 550 motors, Spark MAX and Spark Flex controllers, the PDH, and MAXSwerve./blogread it
- 13 min readFRC Code Structure Best Practices: Command-Based Project ArchitectureHow to structure an FRC command-based robot project the right way — subsystems, commands, RobotContainer, and Constants — verified against official WPILib docs./blogread it
answer sheet
Lesson quiz
All 3 right completes the lesson. Miss one and only that question comes back, anything you already answered correctly stays banked.
0 of 3 answered
01When should you apply PersistMode.kPersistParameters when configuring a SPARK Flex in REVLib 2025?
02What units does the SPARK's onboard velocity closed loop use for its setpoint by default?
03For a flywheel shooter in REVLib, how should feedforward and PID gains be combined?
Answer every question to submit.
All 51 lessons in Programming, Controls & Sensorsopenclose
01 / prerequisites
02 / foundations-tools-and-first-program
03 / robot-program-and-command-based
04 / motors-and-control
05 / autonomous-trajectories-simulation
06 / sensing-fundamentals
07 / encoders
08 / gyros-imus-orientation
09 / closed-loop-control
10 / vision-pose-estimation
11 / worked-examples-mini-projects
- Not read yet:Mini-Project: A Closed-Loop Elevator with Motion Magic
- Not read yet:Mini-Project: A Velocity-Controlled Shooter on REVLib
- Not read yet:Mini-Project: A Teleop Swerve Drive Subsystem
- Not read yet:Mini-Project: An Autonomous Routine with PathPlanner
- Not read yet:Mini-Project: Vision-Aligned Scoring with Limelight
12 / common-mistakes-troubleshooting
13 / advanced-techniques-case-studies
- Not read yet:State-Space Control and Kalman Filtering
- Not read yet:Log Replay Architecture with AdvantageKit
- Not read yet:Advanced Pose Estimation: Multi-Tag Fusion and Standard Deviations
- Not read yet:Robot Coordination, Alerts, and Operator Feedback
- Not read yet:Case Study: Hardening Software Before an Event