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