001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. 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.smartdashboard.SendableBuilder; 016 017/** 018 * This class defines a {@link Command} which interacts heavily with a PID loop. 019 * 020 * <p> It provides some convenience methods to run an internal {@link PIDController} . It will also 021 * start and stop said {@link PIDController} when the {@link PIDCommand} is first initialized and 022 * ended/interrupted. </p> 023 */ 024public abstract class PIDCommand extends Command implements Sendable { 025 /** 026 * The internal {@link PIDController}. 027 */ 028 private final PIDController m_controller; 029 /** 030 * An output which calls {@link PIDCommand#usePIDOutput(double)}. 031 */ 032 private final PIDOutput m_output = this::usePIDOutput; 033 /** 034 * A source which calls {@link PIDCommand#returnPIDInput()}. 035 */ 036 private final PIDSource m_source = new PIDSource() { 037 public void setPIDSourceType(PIDSourceType pidSource) { 038 } 039 040 public PIDSourceType getPIDSourceType() { 041 return PIDSourceType.kDisplacement; 042 } 043 044 public double pidGet() { 045 return returnPIDInput(); 046 } 047 }; 048 049 /** 050 * Instantiates a {@link PIDCommand} that will use the given p, i and d values. 051 * 052 * @param name the name of the command 053 * @param p the proportional value 054 * @param i the integral value 055 * @param d the derivative value 056 */ 057 @SuppressWarnings("ParameterName") 058 public PIDCommand(String name, double p, double i, double d) { 059 super(name); 060 m_controller = new PIDController(p, i, d, m_source, m_output); 061 } 062 063 /** 064 * Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will also space 065 * the time between PID loop calculations to be equal to the given period. 066 * 067 * @param name the name 068 * @param p the proportional value 069 * @param i the integral value 070 * @param d the derivative value 071 * @param period the time (in seconds) between calculations 072 */ 073 @SuppressWarnings("ParameterName") 074 public PIDCommand(String name, double p, double i, double d, double period) { 075 super(name); 076 m_controller = new PIDController(p, i, d, m_source, m_output, period); 077 } 078 079 /** 080 * Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will use the 081 * class name as its name. 082 * 083 * @param p the proportional value 084 * @param i the integral value 085 * @param d the derivative value 086 */ 087 @SuppressWarnings("ParameterName") 088 public PIDCommand(double p, double i, double d) { 089 m_controller = new PIDController(p, i, d, m_source, m_output); 090 } 091 092 /** 093 * Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will use the 094 * class name as its name.. It will also space the time between PID loop calculations to be equal 095 * to the given period. 096 * 097 * @param p the proportional value 098 * @param i the integral value 099 * @param d the derivative value 100 * @param period the time (in seconds) between calculations 101 */ 102 @SuppressWarnings("ParameterName") 103 public PIDCommand(double p, double i, double d, double period) { 104 m_controller = new PIDController(p, i, d, m_source, m_output, period); 105 } 106 107 /** 108 * Returns the {@link PIDController} used by this {@link PIDCommand}. Use this if you would like 109 * to fine tune the pid loop. 110 * 111 * @return the {@link PIDController} used by this {@link PIDCommand} 112 */ 113 protected PIDController getPIDController() { 114 return m_controller; 115 } 116 117 @Override 118 @SuppressWarnings("MethodName") 119 void _initialize() { 120 m_controller.enable(); 121 } 122 123 @Override 124 @SuppressWarnings("MethodName") 125 void _end() { 126 m_controller.disable(); 127 } 128 129 @Override 130 @SuppressWarnings("MethodName") 131 void _interrupted() { 132 _end(); 133 } 134 135 /** 136 * Adds the given value to the setpoint. If {@link PIDCommand#setInputRange(double, double) 137 * setInputRange(...)} was used, then the bounds will still be honored by this method. 138 * 139 * @param deltaSetpoint the change in the setpoint 140 */ 141 public void setSetpointRelative(double deltaSetpoint) { 142 setSetpoint(getSetpoint() + deltaSetpoint); 143 } 144 145 /** 146 * Sets the setpoint to the given value. If {@link PIDCommand#setInputRange(double, double) 147 * setInputRange(...)} was called, then the given setpoint will be trimmed to fit within the 148 * range. 149 * 150 * @param setpoint the new setpoint 151 */ 152 protected void setSetpoint(double setpoint) { 153 m_controller.setSetpoint(setpoint); 154 } 155 156 /** 157 * Returns the setpoint. 158 * 159 * @return the setpoint 160 */ 161 protected double getSetpoint() { 162 return m_controller.getSetpoint(); 163 } 164 165 /** 166 * Returns the current position. 167 * 168 * @return the current position 169 */ 170 protected double getPosition() { 171 return returnPIDInput(); 172 } 173 174 /** 175 * Sets the maximum and minimum values expected from the input and setpoint. 176 * 177 * @param minimumInput the minimum value expected from the input and setpoint 178 * @param maximumInput the maximum value expected from the input and setpoint 179 */ 180 protected void setInputRange(double minimumInput, double maximumInput) { 181 m_controller.setInputRange(minimumInput, maximumInput); 182 } 183 184 /** 185 * Returns the input for the pid loop. 186 * 187 * <p>It returns the input for the pid loop, so if this command was based off of a gyro, then it 188 * should return the angle of the gyro. 189 * 190 * <p>All subclasses of {@link PIDCommand} must override this method. 191 * 192 * <p>This method will be called in a different thread then the {@link Scheduler} thread. 193 * 194 * @return the value the pid loop should use as input 195 */ 196 protected abstract double returnPIDInput(); 197 198 /** 199 * Uses the value that the pid loop calculated. The calculated value is the "output" parameter. 200 * This method is a good time to set motor values, maybe something along the lines of 201 * <code>driveline.tankDrive(output, -output)</code> 202 * 203 * <p>All subclasses of {@link PIDCommand} must override this method. 204 * 205 * <p>This method will be called in a different thread then the {@link Scheduler} thread. 206 * 207 * @param output the value the pid loop calculated 208 */ 209 protected abstract void usePIDOutput(double output); 210 211 @Override 212 public void initSendable(SendableBuilder builder) { 213 m_controller.initSendable(builder); 214 super.initSendable(builder); 215 builder.setSmartDashboardType("PIDCommand"); 216 } 217}