001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. 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.tables.ITable;
016
017/**
018 * This class is designed to handle the case where there is a {@link Subsystem} which uses a single
019 * {@link PIDController} almost constantly (for instance, an elevator which attempts to stay at a
020 * constant height).
021 *
022 * <p>It provides some convenience methods to run an internal {@link PIDController} . It also
023 * allows access to the internal {@link PIDController} in order to give total control to the
024 * programmer.
025 */
026public abstract class PIDSubsystem extends Subsystem implements Sendable {
027
028  /**
029   * The internal {@link PIDController}.
030   */
031  private final PIDController m_controller;
032  /**
033   * An output which calls {@link PIDCommand#usePIDOutput(double)}.
034   */
035  private final PIDOutput m_output = this::usePIDOutput;
036
037  /**
038   * A source which calls {@link PIDCommand#returnPIDInput()}.
039   */
040  private final PIDSource m_source = new PIDSource() {
041    public void setPIDSourceType(PIDSourceType pidSource) {
042    }
043
044    public PIDSourceType getPIDSourceType() {
045      return PIDSourceType.kDisplacement;
046    }
047
048    public double pidGet() {
049      return returnPIDInput();
050    }
051  };
052
053  /**
054   * Instantiates a {@link PIDSubsystem} that will use the given p, i and d values.
055   *
056   * @param name the name
057   * @param p    the proportional value
058   * @param i    the integral value
059   * @param d    the derivative value
060   */
061  @SuppressWarnings("ParameterName")
062  public PIDSubsystem(String name, double p, double i, double d) {
063    super(name);
064    m_controller = new PIDController(p, i, d, m_source, m_output);
065  }
066
067  /**
068   * Instantiates a {@link PIDSubsystem} that will use the given p, i and d values.
069   *
070   * @param name the name
071   * @param p    the proportional value
072   * @param i    the integral value
073   * @param d    the derivative value
074   * @param f    the feed forward value
075   */
076  @SuppressWarnings("ParameterName")
077  public PIDSubsystem(String name, double p, double i, double d, double f) {
078    super(name);
079    m_controller = new PIDController(p, i, d, f, m_source, m_output);
080  }
081
082  /**
083   * Instantiates a {@link PIDSubsystem} that will use the given p, i and d values. It will also
084   * space the time between PID loop calculations to be equal to the given period.
085   *
086   * @param name   the name
087   * @param p      the proportional value
088   * @param i      the integral value
089   * @param d      the derivative value
090   * @param period the time (in seconds) between calculations
091   */
092  @SuppressWarnings("ParameterName")
093  public PIDSubsystem(String name, double p, double i, double d, double f, double period) {
094    super(name);
095    m_controller = new PIDController(p, i, d, f, m_source, m_output, period);
096  }
097
098  /**
099   * Instantiates a {@link PIDSubsystem} that will use the given p, i and d values. It will use the
100   * class name as its name.
101   *
102   * @param p the proportional value
103   * @param i the integral value
104   * @param d the derivative value
105   */
106  @SuppressWarnings("ParameterName")
107  public PIDSubsystem(double p, double i, double d) {
108    m_controller = new PIDController(p, i, d, m_source, m_output);
109  }
110
111  /**
112   * Instantiates a {@link PIDSubsystem} that will use the given p, i and d values. It will use the
113   * class name as its name. It will also space the time between PID loop calculations to be equal
114   * to the given period.
115   *
116   * @param p      the proportional value
117   * @param i      the integral value
118   * @param d      the derivative value
119   * @param f      the feed forward coefficient
120   * @param period the time (in seconds) between calculations
121   */
122  @SuppressWarnings("ParameterName")
123  public PIDSubsystem(double p, double i, double d, double period, double f) {
124    m_controller = new PIDController(p, i, d, f, m_source, m_output, period);
125  }
126
127  /**
128   * Instantiates a {@link PIDSubsystem} that will use the given p, i and d values. It will use the
129   * class name as its name. It will also space the time between PID loop calculations to be equal
130   * to the given period.
131   *
132   * @param p      the proportional value
133   * @param i      the integral value
134   * @param d      the derivative value
135   * @param period the time (in seconds) between calculations
136   */
137  @SuppressWarnings("ParameterName")
138  public PIDSubsystem(double p, double i, double d, double period) {
139    m_controller = new PIDController(p, i, d, m_source, m_output, period);
140  }
141
142  /**
143   * Returns the {@link PIDController} used by this {@link PIDSubsystem}. Use this if you would like
144   * to fine tune the pid loop.
145   *
146   * @return the {@link PIDController} used by this {@link PIDSubsystem}
147   */
148  public PIDController getPIDController() {
149    return m_controller;
150  }
151
152
153  /**
154   * Adds the given value to the setpoint. If {@link PIDSubsystem#setInputRange(double, double)
155   * setInputRange(...)} was used, then the bounds will still be honored by this method.
156   *
157   * @param deltaSetpoint the change in the setpoint
158   */
159  public void setSetpointRelative(double deltaSetpoint) {
160    setSetpoint(getPosition() + deltaSetpoint);
161  }
162
163  /**
164   * Sets the setpoint to the given value. If {@link PIDSubsystem#setInputRange(double, double)
165   * setInputRange(...)} was called, then the given setpoint will be trimmed to fit within the
166   * range.
167   *
168   * @param setpoint the new setpoint
169   */
170  public void setSetpoint(double setpoint) {
171    m_controller.setSetpoint(setpoint);
172  }
173
174  /**
175   * Returns the setpoint.
176   *
177   * @return the setpoint
178   */
179  public double getSetpoint() {
180    return m_controller.getSetpoint();
181  }
182
183  /**
184   * Returns the current position.
185   *
186   * @return the current position
187   */
188  public double getPosition() {
189    return returnPIDInput();
190  }
191
192  /**
193   * Sets the maximum and minimum values expected from the input.
194   *
195   * @param minimumInput the minimum value expected from the input
196   * @param maximumInput the maximum value expected from the output
197   */
198  public void setInputRange(double minimumInput, double maximumInput) {
199    m_controller.setInputRange(minimumInput, maximumInput);
200  }
201
202  /**
203   * Sets the maximum and minimum values to write.
204   *
205   * @param minimumOutput the minimum value to write to the output
206   * @param maximumOutput the maximum value to write to the output
207   */
208  public void setOutputRange(double minimumOutput, double maximumOutput) {
209    m_controller.setOutputRange(minimumOutput, maximumOutput);
210  }
211
212  /**
213   * Set the absolute error which is considered tolerable for use with OnTarget. The value is in the
214   * same range as the PIDInput values.
215   *
216   * @param t the absolute tolerance
217   */
218  @SuppressWarnings("ParameterName")
219  public void setAbsoluteTolerance(double t) {
220    m_controller.setAbsoluteTolerance(t);
221  }
222
223  /**
224   * Set the percentage error which is considered tolerable for use with OnTarget. (Value of 15.0 ==
225   * 15 percent).
226   *
227   * @param p the percent tolerance
228   */
229  @SuppressWarnings("ParameterName")
230  public void setPercentTolerance(double p) {
231    m_controller.setPercentTolerance(p);
232  }
233
234  /**
235   * Return true if the error is within the percentage of the total input range, determined by
236   * setTolerance. This assumes that the maximum and minimum input were set using setInput.
237   *
238   * @return true if the error is less than the tolerance
239   */
240  public boolean onTarget() {
241    return m_controller.onTarget();
242  }
243
244  /**
245   * Returns the input for the pid loop.
246   *
247   * <p>It returns the input for the pid loop, so if this Subsystem was based off of a gyro, then
248   * it should return the angle of the gyro.
249   *
250   * <p>All subclasses of {@link PIDSubsystem} must override this method.
251   *
252   * @return the value the pid loop should use as input
253   */
254  protected abstract double returnPIDInput();
255
256  /**
257   * Uses the value that the pid loop calculated. The calculated value is the "output" parameter.
258   * This method is a good time to set motor values, maybe something along the lines of
259   * <code>driveline.tankDrive(output, -output)</code>.
260   *
261   * <p>All subclasses of {@link PIDSubsystem} must override this method.
262   *
263   * @param output the value the pid loop calculated
264   */
265  protected abstract void usePIDOutput(double output);
266
267  /**
268   * Enables the internal {@link PIDController}.
269   */
270  public void enable() {
271    m_controller.enable();
272  }
273
274  /**
275   * Disables the internal {@link PIDController}.
276   */
277  public void disable() {
278    m_controller.disable();
279  }
280
281  @Override
282  public String getSmartDashboardType() {
283    return "PIDSubsystem";
284  }
285
286  @Override
287  public void initTable(ITable table) {
288    m_controller.initTable(table);
289    super.initTable(table);
290  }
291}