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}