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.math.controller;
006
007import edu.wpi.first.math.MathSharedStore;
008import edu.wpi.first.math.MathUsageId;
009import edu.wpi.first.math.MathUtil;
010import edu.wpi.first.util.sendable.Sendable;
011import edu.wpi.first.util.sendable.SendableBuilder;
012import edu.wpi.first.util.sendable.SendableRegistry;
013
014/** Implements a PID control loop. */
015public class PIDController implements Sendable, AutoCloseable {
016  private static int instances;
017
018  // Factor for "proportional" control
019  private double m_kp;
020
021  // Factor for "integral" control
022  private double m_ki;
023
024  // Factor for "derivative" control
025  private double m_kd;
026
027  // The period (in seconds) of the loop that calls the controller
028  private final double m_period;
029
030  private double m_maximumIntegral = 1.0;
031
032  private double m_minimumIntegral = -1.0;
033
034  private double m_maximumInput;
035
036  private double m_minimumInput;
037
038  // Do the endpoints wrap around? e.g. Absolute encoder
039  private boolean m_continuous;
040
041  // The error at the time of the most recent call to calculate()
042  private double m_positionError;
043  private double m_velocityError;
044
045  // The error at the time of the second-most-recent call to calculate() (used to compute velocity)
046  private double m_prevError;
047
048  // The sum of the errors for use in the integral calc
049  private double m_totalError;
050
051  // The error that is considered at setpoint.
052  private double m_positionTolerance = 0.05;
053  private double m_velocityTolerance = Double.POSITIVE_INFINITY;
054
055  private double m_setpoint;
056  private double m_measurement;
057
058  private boolean m_haveMeasurement;
059  private boolean m_haveSetpoint;
060
061  /**
062   * Allocates a PIDController with the given constants for kp, ki, and kd and a default period of
063   * 0.02 seconds.
064   *
065   * @param kp The proportional coefficient.
066   * @param ki The integral coefficient.
067   * @param kd The derivative coefficient.
068   */
069  public PIDController(double kp, double ki, double kd) {
070    this(kp, ki, kd, 0.02);
071  }
072
073  /**
074   * Allocates a PIDController with the given constants for kp, ki, and kd.
075   *
076   * @param kp The proportional coefficient.
077   * @param ki The integral coefficient.
078   * @param kd The derivative coefficient.
079   * @param period The period between controller updates in seconds. Must be non-zero and positive.
080   */
081  public PIDController(double kp, double ki, double kd, double period) {
082    m_kp = kp;
083    m_ki = ki;
084    m_kd = kd;
085
086    if (period <= 0) {
087      throw new IllegalArgumentException("Controller period must be a non-zero positive number!");
088    }
089    m_period = period;
090
091    instances++;
092    SendableRegistry.addLW(this, "PIDController", instances);
093
094    MathSharedStore.reportUsage(MathUsageId.kController_PIDController2, instances);
095  }
096
097  @Override
098  public void close() {
099    SendableRegistry.remove(this);
100  }
101
102  /**
103   * Sets the PID Controller gain parameters.
104   *
105   * <p>Set the proportional, integral, and differential coefficients.
106   *
107   * @param kp The proportional coefficient.
108   * @param ki The integral coefficient.
109   * @param kd The derivative coefficient.
110   */
111  public void setPID(double kp, double ki, double kd) {
112    m_kp = kp;
113    m_ki = ki;
114    m_kd = kd;
115  }
116
117  /**
118   * Sets the Proportional coefficient of the PID controller gain.
119   *
120   * @param kp proportional coefficient
121   */
122  public void setP(double kp) {
123    m_kp = kp;
124  }
125
126  /**
127   * Sets the Integral coefficient of the PID controller gain.
128   *
129   * @param ki integral coefficient
130   */
131  public void setI(double ki) {
132    m_ki = ki;
133  }
134
135  /**
136   * Sets the Differential coefficient of the PID controller gain.
137   *
138   * @param kd differential coefficient
139   */
140  public void setD(double kd) {
141    m_kd = kd;
142  }
143
144  /**
145   * Get the Proportional coefficient.
146   *
147   * @return proportional coefficient
148   */
149  public double getP() {
150    return m_kp;
151  }
152
153  /**
154   * Get the Integral coefficient.
155   *
156   * @return integral coefficient
157   */
158  public double getI() {
159    return m_ki;
160  }
161
162  /**
163   * Get the Differential coefficient.
164   *
165   * @return differential coefficient
166   */
167  public double getD() {
168    return m_kd;
169  }
170
171  /**
172   * Returns the period of this controller.
173   *
174   * @return the period of the controller.
175   */
176  public double getPeriod() {
177    return m_period;
178  }
179
180  /**
181   * Returns the position tolerance of this controller.
182   *
183   * @return the position tolerance of the controller.
184   */
185  public double getPositionTolerance() {
186    return m_positionTolerance;
187  }
188
189  /**
190   * Returns the velocity tolerance of this controller.
191   *
192   * @return the velocity tolerance of the controller.
193   */
194  public double getVelocityTolerance() {
195    return m_velocityTolerance;
196  }
197
198  /**
199   * Sets the setpoint for the PIDController.
200   *
201   * @param setpoint The desired setpoint.
202   */
203  public void setSetpoint(double setpoint) {
204    m_setpoint = setpoint;
205    m_haveSetpoint = true;
206
207    if (m_continuous) {
208      double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
209      m_positionError = MathUtil.inputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
210    } else {
211      m_positionError = m_setpoint - m_measurement;
212    }
213
214    m_velocityError = (m_positionError - m_prevError) / m_period;
215  }
216
217  /**
218   * Returns the current setpoint of the PIDController.
219   *
220   * @return The current setpoint.
221   */
222  public double getSetpoint() {
223    return m_setpoint;
224  }
225
226  /**
227   * Returns true if the error is within the tolerance of the setpoint.
228   *
229   * <p>This will return false until at least one input value has been computed.
230   *
231   * @return Whether the error is within the acceptable bounds.
232   */
233  public boolean atSetpoint() {
234    return m_haveMeasurement
235        && m_haveSetpoint
236        && Math.abs(m_positionError) < m_positionTolerance
237        && Math.abs(m_velocityError) < m_velocityTolerance;
238  }
239
240  /**
241   * Enables continuous input.
242   *
243   * <p>Rather then using the max and min input range as constraints, it considers them to be the
244   * same point and automatically calculates the shortest route to the setpoint.
245   *
246   * @param minimumInput The minimum value expected from the input.
247   * @param maximumInput The maximum value expected from the input.
248   */
249  public void enableContinuousInput(double minimumInput, double maximumInput) {
250    m_continuous = true;
251    m_minimumInput = minimumInput;
252    m_maximumInput = maximumInput;
253  }
254
255  /** Disables continuous input. */
256  public void disableContinuousInput() {
257    m_continuous = false;
258  }
259
260  /**
261   * Returns true if continuous input is enabled.
262   *
263   * @return True if continuous input is enabled.
264   */
265  public boolean isContinuousInputEnabled() {
266    return m_continuous;
267  }
268
269  /**
270   * Sets the minimum and maximum values for the integrator.
271   *
272   * <p>When the cap is reached, the integrator value is added to the controller output rather than
273   * the integrator value times the integral gain.
274   *
275   * @param minimumIntegral The minimum value of the integrator.
276   * @param maximumIntegral The maximum value of the integrator.
277   */
278  public void setIntegratorRange(double minimumIntegral, double maximumIntegral) {
279    m_minimumIntegral = minimumIntegral;
280    m_maximumIntegral = maximumIntegral;
281  }
282
283  /**
284   * Sets the error which is considered tolerable for use with atSetpoint().
285   *
286   * @param positionTolerance Position error which is tolerable.
287   */
288  public void setTolerance(double positionTolerance) {
289    setTolerance(positionTolerance, Double.POSITIVE_INFINITY);
290  }
291
292  /**
293   * Sets the error which is considered tolerable for use with atSetpoint().
294   *
295   * @param positionTolerance Position error which is tolerable.
296   * @param velocityTolerance Velocity error which is tolerable.
297   */
298  public void setTolerance(double positionTolerance, double velocityTolerance) {
299    m_positionTolerance = positionTolerance;
300    m_velocityTolerance = velocityTolerance;
301  }
302
303  /**
304   * Returns the difference between the setpoint and the measurement.
305   *
306   * @return The error.
307   */
308  public double getPositionError() {
309    return m_positionError;
310  }
311
312  /**
313   * Returns the velocity error.
314   *
315   * @return The velocity error.
316   */
317  public double getVelocityError() {
318    return m_velocityError;
319  }
320
321  /**
322   * Returns the next output of the PID controller.
323   *
324   * @param measurement The current measurement of the process variable.
325   * @param setpoint The new setpoint of the controller.
326   * @return The next controller output.
327   */
328  public double calculate(double measurement, double setpoint) {
329    m_setpoint = setpoint;
330    m_haveSetpoint = true;
331    return calculate(measurement);
332  }
333
334  /**
335   * Returns the next output of the PID controller.
336   *
337   * @param measurement The current measurement of the process variable.
338   * @return The next controller output.
339   */
340  public double calculate(double measurement) {
341    m_measurement = measurement;
342    m_prevError = m_positionError;
343    m_haveMeasurement = true;
344
345    if (m_continuous) {
346      double errorBound = (m_maximumInput - m_minimumInput) / 2.0;
347      m_positionError = MathUtil.inputModulus(m_setpoint - m_measurement, -errorBound, errorBound);
348    } else {
349      m_positionError = m_setpoint - m_measurement;
350    }
351
352    m_velocityError = (m_positionError - m_prevError) / m_period;
353
354    if (m_ki != 0) {
355      m_totalError =
356          MathUtil.clamp(
357              m_totalError + m_positionError * m_period,
358              m_minimumIntegral / m_ki,
359              m_maximumIntegral / m_ki);
360    }
361
362    return m_kp * m_positionError + m_ki * m_totalError + m_kd * m_velocityError;
363  }
364
365  /** Resets the previous error and the integral term. */
366  public void reset() {
367    m_positionError = 0;
368    m_prevError = 0;
369    m_totalError = 0;
370    m_velocityError = 0;
371    m_haveMeasurement = false;
372  }
373
374  @Override
375  public void initSendable(SendableBuilder builder) {
376    builder.setSmartDashboardType("PIDController");
377    builder.addDoubleProperty("p", this::getP, this::setP);
378    builder.addDoubleProperty("i", this::getI, this::setI);
379    builder.addDoubleProperty("d", this::getD, this::setD);
380    builder.addDoubleProperty("setpoint", this::getSetpoint, this::setSetpoint);
381  }
382}