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.util.ErrorMessages.requireNonNullParam;
009
010import edu.wpi.first.math.controller.ProfiledPIDController;
011import java.util.Set;
012import java.util.function.BiConsumer;
013import java.util.function.DoubleSupplier;
014import java.util.function.Supplier;
015
016/**
017 * A command that controls an output with a {@link ProfiledPIDController}. Runs forever by default -
018 * to add exit conditions and/or other behavior, subclass this class. The controller calculation and
019 * output are performed synchronously in the command's execute() method.
020 *
021 * <p>This class is provided by the NewCommands VendorDep
022 */
023public class ProfiledPIDCommand extends Command {
024  protected final ProfiledPIDController m_controller;
025  protected DoubleSupplier m_measurement;
026  protected Supplier<State> m_goal;
027  protected BiConsumer<Double, State> m_useOutput;
028
029  /**
030   * Creates a new PIDCommand, which controls the given output with a ProfiledPIDController. Goal
031   * velocity is specified.
032   *
033   * @param controller the controller that controls the output.
034   * @param measurementSource the measurement of the process variable
035   * @param goalSource the controller's goal
036   * @param useOutput the controller's output
037   * @param requirements the subsystems required by this command
038   */
039  public ProfiledPIDCommand(
040      ProfiledPIDController controller,
041      DoubleSupplier measurementSource,
042      Supplier<State> goalSource,
043      BiConsumer<Double, State> useOutput,
044      Subsystem... requirements) {
045    requireNonNullParam(controller, "controller", "ProfiledPIDCommand");
046    requireNonNullParam(measurementSource, "measurementSource", "ProfiledPIDCommand");
047    requireNonNullParam(goalSource, "goalSource", "ProfiledPIDCommand");
048    requireNonNullParam(useOutput, "useOutput", "ProfiledPIDCommand");
049
050    m_controller = controller;
051    m_useOutput = useOutput;
052    m_measurement = measurementSource;
053    m_goal = goalSource;
054    m_requirements.addAll(Set.of(requirements));
055  }
056
057  /**
058   * Creates a new PIDCommand, which controls the given output with a ProfiledPIDController. Goal
059   * velocity is implicitly zero.
060   *
061   * @param controller the controller that controls the output.
062   * @param measurementSource the measurement of the process variable
063   * @param goalSource the controller's goal
064   * @param useOutput the controller's output
065   * @param requirements the subsystems required by this command
066   */
067  public ProfiledPIDCommand(
068      ProfiledPIDController controller,
069      DoubleSupplier measurementSource,
070      DoubleSupplier goalSource,
071      BiConsumer<Double, State> useOutput,
072      Subsystem... requirements) {
073    requireNonNullParam(controller, "controller", "SynchronousPIDCommand");
074    requireNonNullParam(measurementSource, "measurementSource", "SynchronousPIDCommand");
075    requireNonNullParam(goalSource, "goalSource", "SynchronousPIDCommand");
076    requireNonNullParam(useOutput, "useOutput", "SynchronousPIDCommand");
077
078    m_controller = controller;
079    m_useOutput = useOutput;
080    m_measurement = measurementSource;
081    m_goal = () -> new State(goalSource.getAsDouble(), 0);
082    m_requirements.addAll(Set.of(requirements));
083  }
084
085  /**
086   * Creates a new PIDCommand, which controls the given output with a ProfiledPIDController. Goal
087   * velocity is specified.
088   *
089   * @param controller the controller that controls the output.
090   * @param measurementSource the measurement of the process variable
091   * @param goal the controller's goal
092   * @param useOutput the controller's output
093   * @param requirements the subsystems required by this command
094   */
095  public ProfiledPIDCommand(
096      ProfiledPIDController controller,
097      DoubleSupplier measurementSource,
098      State goal,
099      BiConsumer<Double, State> useOutput,
100      Subsystem... requirements) {
101    this(controller, measurementSource, () -> goal, useOutput, requirements);
102  }
103
104  /**
105   * Creates a new PIDCommand, which controls the given output with a ProfiledPIDController. Goal
106   * velocity is implicitly zero.
107   *
108   * @param controller the controller that controls the output.
109   * @param measurementSource the measurement of the process variable
110   * @param goal the controller's goal
111   * @param useOutput the controller's output
112   * @param requirements the subsystems required by this command
113   */
114  public ProfiledPIDCommand(
115      ProfiledPIDController controller,
116      DoubleSupplier measurementSource,
117      double goal,
118      BiConsumer<Double, State> useOutput,
119      Subsystem... requirements) {
120    this(controller, measurementSource, () -> goal, useOutput, requirements);
121  }
122
123  @Override
124  public void initialize() {
125    m_controller.reset(m_measurement.getAsDouble());
126  }
127
128  @Override
129  public void execute() {
130    m_useOutput.accept(
131        m_controller.calculate(m_measurement.getAsDouble(), m_goal.get()),
132        m_controller.getSetpoint());
133  }
134
135  @Override
136  public void end(boolean interrupted) {
137    m_useOutput.accept(0.0, new State());
138  }
139
140  /**
141   * Returns the ProfiledPIDController used by the command.
142   *
143   * @return The ProfiledPIDController
144   */
145  public ProfiledPIDController getController() {
146    return m_controller;
147  }
148}