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}