PID Control in Command-based

Nota

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® 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 edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems;
 6
 7import static edu.wpi.first.wpilibj2.command.Commands.parallel;
 8import static edu.wpi.first.wpilibj2.command.Commands.waitUntil;
 9
10import edu.wpi.first.epilogue.Logged;
11import edu.wpi.first.math.controller.PIDController;
12import edu.wpi.first.math.controller.SimpleMotorFeedforward;
13import edu.wpi.first.wpilibj.Encoder;
14import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.ShooterConstants;
15import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
16import edu.wpi.first.wpilibj2.command.Command;
17import edu.wpi.first.wpilibj2.command.SubsystemBase;
18
19@Logged
20public class Shooter extends SubsystemBase {
21  private final PWMSparkMax m_shooterMotor = new PWMSparkMax(ShooterConstants.kShooterMotorPort);
22  private final PWMSparkMax m_feederMotor = new PWMSparkMax(ShooterConstants.kFeederMotorPort);
23  private final Encoder m_shooterEncoder =
24      new Encoder(
25          ShooterConstants.kEncoderPorts[0],
26          ShooterConstants.kEncoderPorts[1],
27          ShooterConstants.kEncoderReversed);
28  private final SimpleMotorFeedforward m_shooterFeedforward =
29      new SimpleMotorFeedforward(
30          ShooterConstants.kSVolts, ShooterConstants.kVVoltSecondsPerRotation);
31  private final PIDController m_shooterFeedback = new PIDController(ShooterConstants.kP, 0.0, 0.0);
32
33  /** The shooter subsystem for the robot. */
34  public Shooter() {
35    m_shooterFeedback.setTolerance(ShooterConstants.kShooterToleranceRPS);
36    m_shooterEncoder.setDistancePerPulse(ShooterConstants.kEncoderDistancePerPulse);
37
38    // Set default command to turn off both the shooter and feeder motors, and then idle
39    setDefaultCommand(
40        runOnce(
41                () -> {
42                  m_shooterMotor.disable();
43                  m_feederMotor.disable();
44                })
45            .andThen(run(() -> {}))
46            .withName("Idle"));
47  }
48
49  /**
50   * Returns a command to shoot the balls currently stored in the robot. Spins the shooter flywheel
51   * up to the specified setpoint, and then runs the feeder motor.
52   *
53   * @param setpointRotationsPerSecond The desired shooter velocity
54   */
55  public Command shootCommand(double setpointRotationsPerSecond) {
56    return parallel(
57            // Run the shooter flywheel at the desired setpoint using feedforward and feedback
58            run(
59                () -> {
60                  m_shooterMotor.set(
61                      m_shooterFeedforward.calculate(setpointRotationsPerSecond)
62                          + m_shooterFeedback.calculate(
63                              m_shooterEncoder.getRate(), setpointRotationsPerSecond));
64                }),
65
66            // Wait until the shooter has reached the setpoint, and then run the feeder
67            waitUntil(m_shooterFeedback::atSetpoint).andThen(() -> m_feederMotor.set(1)))
68        .withName("Shoot");
69  }
70}
 5#pragma once
 6
 7#include <functional>
 8
 9#include <frc/Encoder.h>
10#include <frc/controller/PIDController.h>
11#include <frc/controller/SimpleMotorFeedforward.h>
12#include <frc/motorcontrol/PWMSparkMax.h>
13#include <frc2/command/CommandPtr.h>
14#include <frc2/command/SubsystemBase.h>
15#include <units/angle.h>
16#include <units/angular_velocity.h>
17
18#include "Constants.h"
19
20class Shooter : public frc2::SubsystemBase {
21 public:
22  Shooter();
23
24  /**
25   * Returns a command to shoot the balls currently stored in the robot. Spins
26   * the shooter flywheel up to the specified setpoint, and then runs the feeder
27   * motor.
28   *
29   * @param setpointRotationsPerSecond The desired shooter velocity
30   */
31  [[nodiscard]]
32  frc2::CommandPtr ShootCommand(units::turns_per_second_t setpoint);
33
34 private:
35  frc::PWMSparkMax m_shooterMotor{ShooterConstants::kShooterMotorPort};
36  frc::PWMSparkMax m_feederMotor{ShooterConstants::kFeederMotorPort};
37
38  frc::Encoder m_shooterEncoder{ShooterConstants::kEncoderPorts[0],
39                                ShooterConstants::kEncoderPorts[1],
40                                ShooterConstants::kEncoderReversed};
41  frc::SimpleMotorFeedforward<units::radians> m_shooterFeedforward{
42      ShooterConstants::kS, ShooterConstants::kV};
43  frc::PIDController m_shooterFeedback{ShooterConstants::kP, 0.0, 0.0};
44};
 5#include "subsystems/Shooter.h"
 6
 7#include <frc2/command/Commands.h>
 8
 9Shooter::Shooter() {
10  m_shooterFeedback.SetTolerance(ShooterConstants::kShooterTolerance.value());
11  m_shooterEncoder.SetDistancePerPulse(
12      ShooterConstants::kEncoderDistancePerPulse);
13
14  SetDefaultCommand(RunOnce([this] {
15                      m_shooterMotor.Disable();
16                      m_feederMotor.Disable();
17                    })
18                        .AndThen(Run([] {}))
19                        .WithName("Idle"));
20}
21
22frc2::CommandPtr Shooter::ShootCommand(units::turns_per_second_t setpoint) {
23  return frc2::cmd::Parallel(
24             // Run the shooter flywheel at the desired setpoint using
25             // feedforward and feedback
26             Run([this, setpoint] {
27               m_shooterMotor.SetVoltage(
28                   m_shooterFeedforward.Calculate(setpoint) +
29                   units::volt_t(m_shooterFeedback.Calculate(
30                       m_shooterEncoder.GetRate(), setpoint.value())));
31             }),
32             // Wait until the shooter has reached the setpoint, and then
33             // run the feeder
34             frc2::cmd::WaitUntil([this] {
35               return m_shooterFeedback.AtSetpoint();
36             }).AndThen([this] { m_feederMotor.Set(1.0); }))
37      .WithName("Shoot");
38}

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.