001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj.command;
009
010import edu.wpi.first.wpilibj.PIDController;
011import edu.wpi.first.wpilibj.PIDOutput;
012import edu.wpi.first.wpilibj.PIDSource;
013import edu.wpi.first.wpilibj.PIDSourceType;
014import edu.wpi.first.wpilibj.Sendable;
015import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
016
017/**
018 * This class defines a {@link Command} which interacts heavily with a PID loop.
019 *
020 * <p> It provides some convenience methods to run an internal {@link PIDController} . It will also
021 * start and stop said {@link PIDController} when the {@link PIDCommand} is first initialized and
022 * ended/interrupted. </p>
023 */
024public abstract class PIDCommand extends Command implements Sendable {
025  /**
026   * The internal {@link PIDController}.
027   */
028  private final PIDController m_controller;
029  /**
030   * An output which calls {@link PIDCommand#usePIDOutput(double)}.
031   */
032  private final PIDOutput m_output = this::usePIDOutput;
033  /**
034   * A source which calls {@link PIDCommand#returnPIDInput()}.
035   */
036  private final PIDSource m_source = new PIDSource() {
037    public void setPIDSourceType(PIDSourceType pidSource) {
038    }
039
040    public PIDSourceType getPIDSourceType() {
041      return PIDSourceType.kDisplacement;
042    }
043
044    public double pidGet() {
045      return returnPIDInput();
046    }
047  };
048
049  /**
050   * Instantiates a {@link PIDCommand} that will use the given p, i and d values.
051   *
052   * @param name the name of the command
053   * @param p    the proportional value
054   * @param i    the integral value
055   * @param d    the derivative value
056   */
057  @SuppressWarnings("ParameterName")
058  public PIDCommand(String name, double p, double i, double d) {
059    super(name);
060    m_controller = new PIDController(p, i, d, m_source, m_output);
061  }
062
063  /**
064   * Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will also space
065   * the time between PID loop calculations to be equal to the given period.
066   *
067   * @param name   the name
068   * @param p      the proportional value
069   * @param i      the integral value
070   * @param d      the derivative value
071   * @param period the time (in seconds) between calculations
072   */
073  @SuppressWarnings("ParameterName")
074  public PIDCommand(String name, double p, double i, double d, double period) {
075    super(name);
076    m_controller = new PIDController(p, i, d, m_source, m_output, period);
077  }
078
079  /**
080   * Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will use the
081   * class name as its name.
082   *
083   * @param p the proportional value
084   * @param i the integral value
085   * @param d the derivative value
086   */
087  @SuppressWarnings("ParameterName")
088  public PIDCommand(double p, double i, double d) {
089    m_controller = new PIDController(p, i, d, m_source, m_output);
090  }
091
092  /**
093   * Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will use the
094   * class name as its name.. It will also space the time between PID loop calculations to be equal
095   * to the given period.
096   *
097   * @param p      the proportional value
098   * @param i      the integral value
099   * @param d      the derivative value
100   * @param period the time (in seconds) between calculations
101   */
102  @SuppressWarnings("ParameterName")
103  public PIDCommand(double p, double i, double d, double period) {
104    m_controller = new PIDController(p, i, d, m_source, m_output, period);
105  }
106
107  /**
108   * Returns the {@link PIDController} used by this {@link PIDCommand}. Use this if you would like
109   * to fine tune the pid loop.
110   *
111   * @return the {@link PIDController} used by this {@link PIDCommand}
112   */
113  protected PIDController getPIDController() {
114    return m_controller;
115  }
116
117  @Override
118  @SuppressWarnings("MethodName")
119  void _initialize() {
120    m_controller.enable();
121  }
122
123  @Override
124  @SuppressWarnings("MethodName")
125  void _end() {
126    m_controller.disable();
127  }
128
129  @Override
130  @SuppressWarnings("MethodName")
131  void _interrupted() {
132    _end();
133  }
134
135  /**
136   * Adds the given value to the setpoint. If {@link PIDCommand#setInputRange(double, double)
137   * setInputRange(...)} was used, then the bounds will still be honored by this method.
138   *
139   * @param deltaSetpoint the change in the setpoint
140   */
141  public void setSetpointRelative(double deltaSetpoint) {
142    setSetpoint(getSetpoint() + deltaSetpoint);
143  }
144
145  /**
146   * Sets the setpoint to the given value. If {@link PIDCommand#setInputRange(double, double)
147   * setInputRange(...)} was called, then the given setpoint will be trimmed to fit within the
148   * range.
149   *
150   * @param setpoint the new setpoint
151   */
152  protected void setSetpoint(double setpoint) {
153    m_controller.setSetpoint(setpoint);
154  }
155
156  /**
157   * Returns the setpoint.
158   *
159   * @return the setpoint
160   */
161  protected double getSetpoint() {
162    return m_controller.getSetpoint();
163  }
164
165  /**
166   * Returns the current position.
167   *
168   * @return the current position
169   */
170  protected double getPosition() {
171    return returnPIDInput();
172  }
173
174  /**
175   * Sets the maximum and minimum values expected from the input and setpoint.
176   *
177   * @param minimumInput the minimum value expected from the input and setpoint
178   * @param maximumInput the maximum value expected from the input and setpoint
179   */
180  protected void setInputRange(double minimumInput, double maximumInput) {
181    m_controller.setInputRange(minimumInput, maximumInput);
182  }
183
184  /**
185   * Returns the input for the pid loop.
186   *
187   * <p>It returns the input for the pid loop, so if this command was based off of a gyro, then it
188   * should return the angle of the gyro.
189   *
190   * <p>All subclasses of {@link PIDCommand} must override this method.
191   *
192   * <p>This method will be called in a different thread then the {@link Scheduler} thread.
193   *
194   * @return the value the pid loop should use as input
195   */
196  protected abstract double returnPIDInput();
197
198  /**
199   * Uses the value that the pid loop calculated. The calculated value is the "output" parameter.
200   * This method is a good time to set motor values, maybe something along the lines of
201   * <code>driveline.tankDrive(output, -output)</code>
202   *
203   * <p>All subclasses of {@link PIDCommand} must override this method.
204   *
205   * <p>This method will be called in a different thread then the {@link Scheduler} thread.
206   *
207   * @param output the value the pid loop calculated
208   */
209  protected abstract void usePIDOutput(double output);
210
211  @Override
212  public void initSendable(SendableBuilder builder) {
213    m_controller.initSendable(builder);
214    super.initSendable(builder);
215    builder.setSmartDashboardType("PIDCommand");
216  }
217}