PID Control in Command-based¶
Note
For a description of the WPILib PID control features used by these command-based wrappers, see PID Control in WPILib.
One of the most common control algorithms used in FRC® and FTC® is the PID controller. WPILib offers its own PIDController class to help teams implement this functionality on their robots. The following example is from the RapidReactCommandBot example project (Java, C++) and shows how PIDControllers can be used within the command-based framework:
5package org.wpilib.examples.rapidreactcommandbot.subsystems;
6
7import static org.wpilib.command2.Commands.parallel;
8import static org.wpilib.command2.Commands.waitUntil;
9
10import org.wpilib.command2.Command;
11import org.wpilib.command2.SubsystemBase;
12import org.wpilib.epilogue.Logged;
13import org.wpilib.examples.rapidreactcommandbot.Constants.ShooterConstants;
14import org.wpilib.hardware.motor.PWMSparkMax;
15import org.wpilib.hardware.rotation.Encoder;
16import org.wpilib.math.controller.PIDController;
17import org.wpilib.math.controller.SimpleMotorFeedforward;
18
19@Logged
20public class Shooter extends SubsystemBase {
21 private final PWMSparkMax shooterMotor = new PWMSparkMax(ShooterConstants.kShooterMotorPort);
22 private final PWMSparkMax feederMotor = new PWMSparkMax(ShooterConstants.kFeederMotorPort);
23 private final Encoder shooterEncoder =
24 new Encoder(
25 ShooterConstants.kEncoderPorts[0],
26 ShooterConstants.kEncoderPorts[1],
27 ShooterConstants.kEncoderReversed);
28 private final SimpleMotorFeedforward shooterFeedforward =
29 new SimpleMotorFeedforward(ShooterConstants.kS, ShooterConstants.kV);
30 private final PIDController shooterFeedback = new PIDController(ShooterConstants.kP, 0.0, 0.0);
31
32 /** The shooter subsystem for the robot. */
33 public Shooter() {
34 shooterFeedback.setTolerance(ShooterConstants.kShooterToleranceRPS);
35 shooterEncoder.setDistancePerPulse(ShooterConstants.kEncoderDistancePerPulse);
36
37 // Set default command to turn off both the shooter and feeder motors, and then idle
38 setDefaultCommand(
39 runOnce(
40 () -> {
41 shooterMotor.disable();
42 feederMotor.disable();
43 })
44 .andThen(run(() -> {}))
45 .withName("Idle"));
46 }
47
48 /**
49 * Returns a command to shoot the balls currently stored in the robot. Spins the shooter flywheel
50 * up to the specified setpoint, and then runs the feeder motor.
51 *
52 * @param setpointRotationsPerSecond The desired shooter velocity
53 */
54 public Command shootCommand(double setpointRotationsPerSecond) {
55 return parallel(
56 // Run the shooter flywheel at the desired setpoint using feedforward and feedback
57 run(
58 () -> {
59 shooterMotor.setThrottle(
60 shooterFeedforward.calculate(setpointRotationsPerSecond)
61 + shooterFeedback.calculate(
62 shooterEncoder.getRate(), setpointRotationsPerSecond));
63 }),
64
65 // Wait until the shooter has reached the setpoint, and then run the feeder
66 waitUntil(shooterFeedback::atSetpoint).andThen(() -> feederMotor.setThrottle(1)))
67 .withName("Shoot");
68 }
69}
5#pragma once
6
7#include <functional>
8
9#include "Constants.hpp"
10#include "wpi/commands2/CommandPtr.hpp"
11#include "wpi/commands2/SubsystemBase.hpp"
12#include "wpi/hardware/motor/PWMSparkMax.hpp"
13#include "wpi/hardware/rotation/Encoder.hpp"
14#include "wpi/math/controller/PIDController.hpp"
15#include "wpi/math/controller/SimpleMotorFeedforward.hpp"
16#include "wpi/units/angle.hpp"
17#include "wpi/units/angular_velocity.hpp"
18
19class Shooter : public wpi::cmd::SubsystemBase {
20 public:
21 Shooter();
22
23 /**
24 * Returns a command to shoot the balls currently stored in the robot. Spins
25 * the shooter flywheel up to the specified setpoint, and then runs the feeder
26 * motor.
27 *
28 * @param setpointRotationsPerSecond The desired shooter velocity
29 */
30 wpi::cmd::CommandPtr ShootCommand(wpi::units::turns_per_second_t setpoint);
31
32 private:
33 wpi::PWMSparkMax shooterMotor{ShooterConstants::kShooterMotorPort};
34 wpi::PWMSparkMax feederMotor{ShooterConstants::kFeederMotorPort};
35
36 wpi::Encoder shooterEncoder{ShooterConstants::kEncoderPorts[0],
37 ShooterConstants::kEncoderPorts[1],
38 ShooterConstants::kEncoderReversed};
39 wpi::math::SimpleMotorFeedforward<wpi::units::radians> shooterFeedforward{
40 ShooterConstants::kS, ShooterConstants::kV};
41 wpi::math::PIDController shooterFeedback{ShooterConstants::kP, 0.0, 0.0};
42};
5#include "subsystems/Shooter.hpp"
6
7#include "wpi/commands2/Commands.hpp"
8
9Shooter::Shooter() {
10 shooterFeedback.SetTolerance(ShooterConstants::kShooterTolerance.value());
11 shooterEncoder.SetDistancePerPulse(
12 ShooterConstants::kEncoderDistancePerPulse);
13
14 SetDefaultCommand(RunOnce([this] {
15 shooterMotor.Disable();
16 feederMotor.Disable();
17 })
18 .AndThen(Run([] {}))
19 .WithName("Idle"));
20}
21
22wpi::cmd::CommandPtr Shooter::ShootCommand(
23 wpi::units::turns_per_second_t setpoint) {
24 return wpi::cmd::Parallel(
25 // Run the shooter flywheel at the desired setpoint using
26 // feedforward and feedback
27 Run([this, setpoint] {
28 shooterMotor.SetVoltage(
29 shooterFeedforward.Calculate(setpoint) +
30 wpi::units::volt_t(shooterFeedback.Calculate(
31 shooterEncoder.GetRate(), setpoint.value())));
32 }),
33 // Wait until the shooter has reached the setpoint, and then
34 // run the feeder
35 wpi::cmd::WaitUntil([this] {
36 return shooterFeedback.AtSetpoint();
37 }).AndThen([this] { feederMotor.SetThrottle(1.0); }))
38 .WithName("Shoot");
39}
A PIDController is declared inside the Shooter subsystem. It is used by ShootCommand alongside a feedforward to spin the shooter flywheel to the specified velocity. Once the PIDController reaches the specified velocity, the ShootCommand runs the feeder.