001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj2.command;
006
007import static edu.wpi.first.math.trajectory.TrapezoidProfile.State;
008import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
009
010import edu.wpi.first.math.controller.ProfiledPIDController;
011import edu.wpi.first.math.trajectory.TrapezoidProfile;
012
013/**
014 * A subsystem that uses a {@link ProfiledPIDController} to control an output. The controller is run
015 * synchronously from the subsystem's periodic() method.
016 */
017public abstract class ProfiledPIDSubsystem extends SubsystemBase {
018  protected final ProfiledPIDController m_controller;
019  protected boolean m_enabled;
020
021  private TrapezoidProfile.State m_goal;
022
023  /**
024   * Creates a new ProfiledPIDSubsystem.
025   *
026   * @param controller the ProfiledPIDController to use
027   * @param initialPosition the initial goal position of the controller
028   */
029  public ProfiledPIDSubsystem(ProfiledPIDController controller, double initialPosition) {
030    m_controller = requireNonNullParam(controller, "controller", "ProfiledPIDSubsystem");
031    setGoal(initialPosition);
032  }
033
034  /**
035   * Creates a new ProfiledPIDSubsystem. Initial goal position is zero.
036   *
037   * @param controller the ProfiledPIDController to use
038   */
039  public ProfiledPIDSubsystem(ProfiledPIDController controller) {
040    this(controller, 0);
041  }
042
043  @Override
044  public void periodic() {
045    if (m_enabled) {
046      useOutput(m_controller.calculate(getMeasurement(), m_goal), m_controller.getSetpoint());
047    }
048  }
049
050  public ProfiledPIDController getController() {
051    return m_controller;
052  }
053
054  /**
055   * Sets the goal state for the subsystem.
056   *
057   * @param goal The goal state for the subsystem's motion profile.
058   */
059  public void setGoal(TrapezoidProfile.State goal) {
060    m_goal = goal;
061  }
062
063  /**
064   * Sets the goal state for the subsystem. Goal velocity assumed to be zero.
065   *
066   * @param goal The goal position for the subsystem's motion profile.
067   */
068  public void setGoal(double goal) {
069    setGoal(new TrapezoidProfile.State(goal, 0));
070  }
071
072  /**
073   * Uses the output from the ProfiledPIDController.
074   *
075   * @param output the output of the ProfiledPIDController
076   * @param setpoint the setpoint state of the ProfiledPIDController, for feedforward
077   */
078  protected abstract void useOutput(double output, State setpoint);
079
080  /**
081   * Returns the measurement of the process variable used by the ProfiledPIDController.
082   *
083   * @return the measurement of the process variable
084   */
085  protected abstract double getMeasurement();
086
087  /** Enables the PID control. Resets the controller. */
088  public void enable() {
089    m_enabled = true;
090    m_controller.reset(getMeasurement());
091  }
092
093  /** Disables the PID control. Sets output to zero. */
094  public void disable() {
095    m_enabled = false;
096    useOutput(0, new State());
097  }
098
099  /**
100   * Returns whether the controller is enabled.
101   *
102   * @return Whether the controller is enabled.
103   */
104  public boolean isEnabled() {
105    return m_enabled;
106  }
107}