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/*----------------------------------------------------------------------------*/ 007package edu.wpi.first.wpilibj; 008 009import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType; 010import edu.wpi.first.wpilibj.communication.UsageReporting; 011import edu.wpi.first.wpilibj.livewindow.LiveWindow; 012import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 013import edu.wpi.first.wpilibj.tables.ITable; 014import edu.wpi.first.wpilibj.util.BoundaryException; 015 016/** 017 * Use a rate gyro to return the robots heading relative to a starting position. 018 * The Gyro class tracks the robots heading based on the starting position. As 019 * the robot rotates the new heading is computed by integrating the rate of 020 * rotation returned by the sensor. When the class is instantiated, it does a 021 * short calibration routine where it samples the gyro while at rest to 022 * determine the default offset. This is subtracted from each sample to 023 * determine the heading. 024 */ 025public class Gyro extends SensorBase implements PIDSource, LiveWindowSendable { 026 027 static final int kOversampleBits = 10; 028 static final int kAverageBits = 0; 029 static final double kSamplesPerSecond = 50.0; 030 static final double kCalibrationSampleTime = 5.0; 031 static final double kDefaultVoltsPerDegreePerSecond = 0.007; 032 protected AnalogInput m_analog; 033 double m_voltsPerDegreePerSecond; 034 double m_offset; 035 int m_center; 036 boolean m_channelAllocated = false; 037 AccumulatorResult result; 038 private PIDSourceParameter m_pidSource; 039 040 /** 041 * Initialize the gyro. Calibrate the gyro by running for a number of 042 * samples and computing the center value. Then use the center 043 * value as the Accumulator center value for subsequent measurements. It's 044 * important to make sure that the robot is not moving while the centering 045 * calculations are in progress, this is typically done when the robot is 046 * first turned on while it's sitting at rest before the competition starts. 047 */ 048 public void initGyro() { 049 result = new AccumulatorResult(); 050 if (m_analog == null) { 051 System.out.println("Null m_analog"); 052 } 053 m_voltsPerDegreePerSecond = kDefaultVoltsPerDegreePerSecond; 054 m_analog.setAverageBits(kAverageBits); 055 m_analog.setOversampleBits(kOversampleBits); 056 double sampleRate = kSamplesPerSecond 057 * (1 << (kAverageBits + kOversampleBits)); 058 AnalogInput.setGlobalSampleRate(sampleRate); 059 Timer.delay(1.0); 060 061 m_analog.initAccumulator(); 062 m_analog.resetAccumulator(); 063 064 Timer.delay(kCalibrationSampleTime); 065 066 m_analog.getAccumulatorOutput(result); 067 068 m_center = (int) ((double) result.value / (double) result.count + .5); 069 070 m_offset = ((double) result.value / (double) result.count) 071 - m_center; 072 073 m_analog.setAccumulatorCenter(m_center); 074 m_analog.resetAccumulator(); 075 076 setDeadband(0.0); 077 078 setPIDSourceParameter(PIDSourceParameter.kAngle); 079 080 UsageReporting.report(tResourceType.kResourceType_Gyro, m_analog.getChannel()); 081 LiveWindow.addSensor("Gyro", m_analog.getChannel(), this); 082 } 083 084 /** 085 * Gyro constructor using the channel number 086 * 087 * @param channel 088 * The analog channel the gyro is connected to. Gyros can only 089 be used on on-board channels 0-1. 090 */ 091 public Gyro(int channel) { 092 this(new AnalogInput(channel)); 093 m_channelAllocated = true; 094 } 095 096 /** 097 * Gyro constructor with a precreated analog channel object. Use this 098 * constructor when the analog channel needs to be shared. 099 * 100 * @param channel 101 * The AnalogInput object that the gyro is connected to. Gyros 102 can only be used on on-board channels 0-1. 103 */ 104 public Gyro(AnalogInput channel) { 105 m_analog = channel; 106 if (m_analog == null) { 107 throw new NullPointerException("AnalogInput supplied to Gyro constructor is null"); 108 } 109 initGyro(); 110 } 111 112 /** 113 * Reset the gyro. Resets the gyro to a heading of zero. This can be used if 114 * there is significant drift in the gyro and it needs to be recalibrated 115 * after it has been running. 116 */ 117 public void reset() { 118 if (m_analog != null) { 119 m_analog.resetAccumulator(); 120 } 121 } 122 123 /** 124 * Delete (free) the accumulator and the analog components used for the 125 * gyro. 126 */ 127 @Override 128 public void free() { 129 if (m_analog != null && m_channelAllocated) { 130 m_analog.free(); 131 } 132 m_analog = null; 133 } 134 135 /** 136 * Return the actual angle in degrees that the robot is currently facing. 137 * 138 * The angle is based on the current accumulator value corrected by the 139 * oversampling rate, the gyro type and the A/D calibration values. The 140 * angle is continuous, that is it will continue from 360 to 361 degrees. This allows 141 * algorithms that wouldn't want to see a discontinuity in the gyro output 142 * as it sweeps past from 360 to 0 on the second time around. 143 * 144 * @return the current heading of the robot in degrees. This heading is 145 * based on integration of the returned rate from the gyro. 146 */ 147 public double getAngle() { 148 if (m_analog == null) { 149 return 0.0; 150 } else { 151 m_analog.getAccumulatorOutput(result); 152 153 long value = result.value - (long) (result.count * m_offset); 154 155 double scaledValue = value 156 * 1e-9 157 * m_analog.getLSBWeight() 158 * (1 << m_analog.getAverageBits()) 159 / (AnalogInput.getGlobalSampleRate() * m_voltsPerDegreePerSecond); 160 161 return scaledValue; 162 } 163 } 164 165 /** 166 * Return the rate of rotation of the gyro 167 * 168 * The rate is based on the most recent reading of the gyro analog value 169 * 170 * @return the current rate in degrees per second 171 */ 172 public double getRate() { 173 if (m_analog == null) { 174 return 0.0; 175 } else { 176 return (m_analog.getAverageValue() - (m_center + m_offset)) 177 * 1e-9 178 * m_analog.getLSBWeight() 179 / ((1 << m_analog.getOversampleBits()) * m_voltsPerDegreePerSecond); 180 } 181 } 182 183 /** 184 * Set the gyro sensitivity. This takes the number of 185 * volts/degree/second sensitivity of the gyro and uses it in subsequent 186 * calculations to allow the code to work with multiple gyros. This value 187 * is typically found in the gyro datasheet. 188 * 189 * @param voltsPerDegreePerSecond 190 * The sensitivity in Volts/degree/second. 191 */ 192 public void setSensitivity(double voltsPerDegreePerSecond) { 193 m_voltsPerDegreePerSecond = voltsPerDegreePerSecond; 194 } 195 196 /** 197 * Set the size of the neutral zone. Any voltage from the gyro less than 198 * this amount from the center is considered stationary. Setting a 199 * deadband will decrease the amount of drift when the gyro isn't rotating, 200 * but will make it less accurate. 201 * 202 * @param volts The size of the deadband in volts 203 */ 204 void setDeadband(double volts) { 205 int deadband = (int)(volts * 1e9 / m_analog.getLSBWeight() * (1 << m_analog.getOversampleBits())); 206 m_analog.setAccumulatorDeadband(deadband); 207 } 208 209 /** 210 * Set which parameter of the gyro you are using as a process control 211 * variable. The Gyro class supports the rate and angle parameters 212 * 213 * @param pidSource 214 * An enum to select the parameter. 215 */ 216 public void setPIDSourceParameter(PIDSourceParameter pidSource) { 217 BoundaryException.assertWithinBounds(pidSource.value, 1, 2); 218 m_pidSource = pidSource; 219 } 220 221 /** 222 * Get the output of the gyro for use with PIDControllers. 223 * May be the angle or rate depending on the set PIDSourceParameter 224 * 225 * @return the output according to the gyro 226 */ 227 @Override 228 public double pidGet() { 229 switch (m_pidSource.value) { 230 case PIDSourceParameter.kRate_val: 231 return getRate(); 232 case PIDSourceParameter.kAngle_val: 233 return getAngle(); 234 default: 235 return 0.0; 236 } 237 } 238 239 /* 240 * Live Window code, only does anything if live window is activated. 241 */ 242 @Override 243 public String getSmartDashboardType() { 244 return "Gyro"; 245 } 246 247 private ITable m_table; 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override 253 public void initTable(ITable subtable) { 254 m_table = subtable; 255 updateTable(); 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override 262 public ITable getTable() { 263 return m_table; 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 @Override 270 public void updateTable() { 271 if (m_table != null) { 272 m_table.putNumber("Value", getAngle()); 273 } 274 } 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override 280 public void startLiveWindowMode() { 281 } 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override 287 public void stopLiveWindowMode() { 288 } 289}