|
| 1 | +package frc.robot.subsystems; |
| 2 | + |
| 3 | +import static frc.robot.Constants.*; |
| 4 | +import static frc.robot.utils.MotorUtils.*; |
| 5 | + |
| 6 | +import java.util.concurrent.locks.LockSupport; |
| 7 | + |
| 8 | +import com.ctre.phoenix.motorcontrol.ControlMode; |
| 9 | +import com.ctre.phoenix.motorcontrol.can.TalonFX; |
| 10 | +import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; |
| 11 | +import com.kauailabs.navx.frc.AHRS; |
| 12 | + |
| 13 | +import edu.wpi.first.math.controller.PIDController; |
| 14 | +import edu.wpi.first.math.geometry.Pose2d; |
| 15 | +import edu.wpi.first.math.geometry.Rotation2d; |
| 16 | +import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; |
| 17 | +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; |
| 18 | +import edu.wpi.first.wpilibj.BuiltInAccelerometer; |
| 19 | +import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; |
| 20 | +import edu.wpi.first.wpilibj2.command.SubsystemBase; |
| 21 | +import frc.robot.Constants; |
| 22 | + |
| 23 | +/** |
| 24 | + * Used by DriveTrain command to move robot Calculates output for each side of |
| 25 | + * the drivetrain |
| 26 | + */ |
| 27 | +@SuppressWarnings("unused") |
| 28 | +public class DriveTrain extends SubsystemBase implements AutoCloseable { |
| 29 | + private WPI_TalonFX leftLeader; |
| 30 | + private WPI_TalonFX leftFollower; |
| 31 | + |
| 32 | + private WPI_TalonFX rightLeader; |
| 33 | + private WPI_TalonFX rightFollower; |
| 34 | + // private AHRS gyro = new AHRS(); |
| 35 | + private final DifferentialDriveOdometry m_odometry; |
| 36 | + private PIDController controller; |
| 37 | + |
| 38 | + private BuiltInAccelerometer accelerometer; |
| 39 | + private double previousRightPercentOutput; |
| 40 | + |
| 41 | + private double previousLeftPercentOutput; |
| 42 | + |
| 43 | + /** |
| 44 | + * Initializing drive train and talonmFX settings |
| 45 | + */ |
| 46 | + public DriveTrain() { |
| 47 | + leftLeader = initWPITalonFX(DRIVE_LEFT_LEADER_ID); |
| 48 | + rightLeader = initWPITalonFX(DRIVE_RIGHT_LEADER_ID); |
| 49 | + leftFollower = initWPITalonFX(DRIVE_LEFT_FOLLOWER_ID); |
| 50 | + rightFollower = initWPITalonFX(DRIVE_RIGHT_FOLLOWER_ID); |
| 51 | + |
| 52 | + accelerometer = new BuiltInAccelerometer(); |
| 53 | + // leftLeader.config_kP(0, .5); |
| 54 | + // rightLeader.config_kP(0, .5); |
| 55 | + |
| 56 | + leftFollower.follow(leftLeader); |
| 57 | + rightFollower.follow(rightLeader); |
| 58 | + |
| 59 | + m_odometry = new DifferentialDriveOdometry(new Rotation2d(), 0, 0); |
| 60 | + |
| 61 | + // leftLeader.config_kP(0, .25); |
| 62 | + // leftLeader.config_kI(0, 0); |
| 63 | + // leftLeader.config_kD(0, 0); |
| 64 | + // leftLeader.setControlFramePeriod(0, 20); |
| 65 | + // leftLeader.configClosedloopRamp(1); |
| 66 | + |
| 67 | + // rightLeader.config_kP(0, .25); |
| 68 | + // rightLeader.config_kI(0, 0); |
| 69 | + // rightLeader.config_kD(0, 0); |
| 70 | + // rightLeader.setControlFramePeriod(0, 20); |
| 71 | + // rightLeader.configClosedloopRamp(1); |
| 72 | + // gyro.reset(); |
| 73 | + previousRightPercentOutput = 0; |
| 74 | + previousLeftPercentOutput = 0; |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * Sets the talonmFX speeds for the given speed and rotation |
| 79 | + * |
| 80 | + * @param speed speed from a joystick input |
| 81 | + * @param rotation rotation from joystick triggers |
| 82 | + */ |
| 83 | + public void arcadeDrive(double speed, double rotation) { |
| 84 | + double maxInput = Math.copySign(Math.max(Math.abs(speed), Math.abs(rotation)), speed); |
| 85 | + double leftMotorOutput; |
| 86 | + double rightMotorOutput; |
| 87 | + |
| 88 | + // speed is -1 to 1, rotation is also -1 to 1 |
| 89 | + if (speed >= 0) { |
| 90 | + if (rotation >= 0) { |
| 91 | + leftMotorOutput = maxInput; |
| 92 | + rightMotorOutput = speed - rotation; |
| 93 | + } else { |
| 94 | + leftMotorOutput = speed + rotation; |
| 95 | + rightMotorOutput = maxInput; |
| 96 | + } |
| 97 | + } else { |
| 98 | + if (rotation >= 0) { |
| 99 | + leftMotorOutput = speed + rotation; |
| 100 | + rightMotorOutput = maxInput; |
| 101 | + } else { |
| 102 | + leftMotorOutput = maxInput; |
| 103 | + rightMotorOutput = speed - rotation; |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + leftMotorOutput = limitAcceleration(leftMotorOutput, previousLeftPercentOutput); |
| 108 | + rightMotorOutput = limitAcceleration(rightMotorOutput, previousRightPercentOutput); |
| 109 | + |
| 110 | + System.out.printf("left: %.02f, right : %.02f\n", leftMotorOutput, rightMotorOutput); |
| 111 | + |
| 112 | + leftLeader.set(-leftMotorOutput); |
| 113 | + rightLeader.set(rightMotorOutput); |
| 114 | + |
| 115 | + previousLeftPercentOutput = leftMotorOutput; |
| 116 | + previousRightPercentOutput = rightMotorOutput; |
| 117 | + } |
| 118 | + |
| 119 | + /** |
| 120 | + * limits acceleration for the robot, should prevent rocking does not prevent |
| 121 | + * rapid decceleration |
| 122 | + * |
| 123 | + * @param currentTargetPercentOutput target output for motor that user inputed |
| 124 | + * @param previousPercentOutput previous outputed target for motor |
| 125 | + * @return a target motor value |
| 126 | + */ |
| 127 | + public double limitAcceleration( |
| 128 | + double currentTargetPercentOutput, |
| 129 | + double previousPercentOutput) { |
| 130 | + // increment |
| 131 | + final double INCR = Constants.TIME_PER_LOOP / Constants.TIME_TO_FULL_SPEED; |
| 132 | + // change that the user wants |
| 133 | + double error = currentTargetPercentOutput - previousPercentOutput; |
| 134 | + |
| 135 | + // target is going towards 0 |
| 136 | + boolean isDecel = Math.abs(currentTargetPercentOutput) < .05; |
| 137 | + |
| 138 | + if (isDecel) |
| 139 | + return currentTargetPercentOutput; |
| 140 | + |
| 141 | + // divide that change over a period of time |
| 142 | + // if the change in acceleration is too large positively, accelerate slower |
| 143 | + if (error > INCR) |
| 144 | + return previousPercentOutput + INCR; |
| 145 | + |
| 146 | + // the change in acceleration is too large negatively, accelerate to the |
| 147 | + // negative direction slower |
| 148 | + if (error < -INCR) |
| 149 | + return previousPercentOutput - INCR; |
| 150 | + |
| 151 | + return currentTargetPercentOutput; |
| 152 | + } |
| 153 | + |
| 154 | + /** |
| 155 | + * Test the lead motors and folowing motors test to see if initialization |
| 156 | + * process for setting 'following' is correct |
| 157 | + * |
| 158 | + * @param left left talonmFX output |
| 159 | + * @param right right talonmFX output |
| 160 | + */ |
| 161 | + public void testDrive(double left, double right) { |
| 162 | + leftLeader.set(ControlMode.PercentOutput, left); |
| 163 | + rightLeader.set(ControlMode.PercentOutput, right); |
| 164 | + } |
| 165 | + |
| 166 | + /** |
| 167 | + * testing method for eaching individual talonmFX only works if constructor does |
| 168 | + * not set follow |
| 169 | + * |
| 170 | + * @param leftFront left front talonmFX output |
| 171 | + * @param rightFront right front talonmFX output |
| 172 | + * @param leftFollow left follow talonmFX output |
| 173 | + * @param rightFollow right followe talonmFX output |
| 174 | + */ |
| 175 | + public void testMotors( |
| 176 | + double leftFront, |
| 177 | + double rightFront, |
| 178 | + double leftFollow, |
| 179 | + double rightFollow) { |
| 180 | + leftLeader.set(ControlMode.PercentOutput, leftFront); |
| 181 | + rightLeader.set(ControlMode.PercentOutput, rightFront); |
| 182 | + |
| 183 | + // experimental |
| 184 | + leftFollower.follow(leftFollower); |
| 185 | + rightFollower.follow(rightFollower); |
| 186 | + |
| 187 | + leftFollower.set(ControlMode.PercentOutput, leftFollow); |
| 188 | + rightFollower.set(ControlMode.PercentOutput, rightFollow); |
| 189 | + |
| 190 | + // experimental |
| 191 | + leftFollower.follow(leftLeader); |
| 192 | + rightFollower.follow(rightLeader); |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * take encoder ticks displacement of left wheels and convert into meters |
| 197 | + * |
| 198 | + * @return displacement of left wheels in meters |
| 199 | + */ |
| 200 | + public double getLeftDistance() { |
| 201 | + double encoderTicks = leftLeader.getSelectedSensorPosition(); |
| 202 | + double distance = encoderTicks / 2048 / 5.88 * Constants.WHEEL_CIRCUMFERENCE; |
| 203 | + return distance; |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * Take encoder ticks displacement of right wheels and convert into meters |
| 208 | + * |
| 209 | + * @return displacement of right wheels in meters |
| 210 | + */ |
| 211 | + public double getRightDistance() { |
| 212 | + double encoderTicks = rightLeader.getSelectedSensorPosition(); |
| 213 | + double distance = encoderTicks / 2048 / 5.88 * Constants.WHEEL_CIRCUMFERENCE; |
| 214 | + return distance; |
| 215 | + } |
| 216 | + |
| 217 | + int disp, vel; |
| 218 | + |
| 219 | + @Override |
| 220 | + public void periodic() { |
| 221 | + // SmartDashboard.putNumber("Acceleration in x", accelerometer.getX()); |
| 222 | + |
| 223 | + // sketchy distance |
| 224 | + vel += accelerometer.getX(); |
| 225 | + disp += vel; |
| 226 | + |
| 227 | + m_odometry.update(new Rotation2d(), getLeftDistance(), getRightDistance()); |
| 228 | + |
| 229 | + // SmartDashboard.putNumber("Movement in x", m_odometry.getPoseMeters().getX()); |
| 230 | + } |
| 231 | + |
| 232 | + public Pose2d getPose() { |
| 233 | + return m_odometry.getPoseMeters(); |
| 234 | + } |
| 235 | + |
| 236 | + public DifferentialDriveWheelSpeeds getWheelSpeeds() { |
| 237 | + // encoder ticks per 100 ms |
| 238 | + // rotations per second |
| 239 | + // meters per second |
| 240 | + double leftSpeed = leftLeader.getSelectedSensorVelocity() |
| 241 | + * (10.0 / 2048 / 5.88) // dm -> m, et -> rot, gear ratio |
| 242 | + * Constants.WHEEL_CIRCUMFERENCE; |
| 243 | + // SmartDashboard.putNumber("leftSpeed", leftSpeed); |
| 244 | + double rightSpeed = rightLeader.getSelectedSensorVelocity() |
| 245 | + * (10.0 / 2048 / 5.88) |
| 246 | + * Constants.WHEEL_CIRCUMFERENCE; |
| 247 | + // SmartDashboard.putNumber("rightSpeed", rightSpeed); |
| 248 | + |
| 249 | + return new DifferentialDriveWheelSpeeds(leftSpeed, rightSpeed); |
| 250 | + } |
| 251 | + |
| 252 | + public void resetOdometry(Pose2d pose) { |
| 253 | + resetEncoders(); |
| 254 | + m_odometry.resetPosition(new Rotation2d(), 0, 0, pose); |
| 255 | + } |
| 256 | + |
| 257 | + public void resetEncoders() { |
| 258 | + leftLeader.setSelectedSensorPosition(0); |
| 259 | + rightLeader.setSelectedSensorPosition(0); |
| 260 | + } |
| 261 | + |
| 262 | + public void tankDriveVolts(double lVolts, double rVolts) { |
| 263 | + leftLeader.set(ControlMode.PercentOutput, lVolts / 12.0 * (46.0 / 48.0)); |
| 264 | + rightLeader.set(ControlMode.PercentOutput, rVolts / 12.0); |
| 265 | + // SmartDashboard.putNumber("lVolts", lVolts); |
| 266 | + // SmartDashboard.putNumber("rVolts", rVolts); |
| 267 | + } |
| 268 | + |
| 269 | + /** |
| 270 | + * two params : speed, rotation use those two params to generate target state of |
| 271 | + * robot, which means we need to know |
| 272 | + * the speed forward and rotational speed. Then compare those two speeds to the |
| 273 | + * current speed of the robot, and find |
| 274 | + * the difference between the current speed between the target speed Adjust the |
| 275 | + * target speed based on the current |
| 276 | + * speed and the difference. Basically arcadeDrive. |
| 277 | + */ |
| 278 | + public void adjustedArcadeDrive(double speed, double rotation) { |
| 279 | + double maxInput = Math.copySign(Math.max(Math.abs(speed), Math.abs(rotation)), speed); |
| 280 | + double leftMotorOutput; |
| 281 | + double rightMotorOutput; |
| 282 | + if (speed >= 0) { |
| 283 | + if (rotation >= 0) { |
| 284 | + leftMotorOutput = speed; |
| 285 | + rightMotorOutput = speed - rotation; |
| 286 | + } else { |
| 287 | + leftMotorOutput = speed + rotation; |
| 288 | + rightMotorOutput = maxInput; |
| 289 | + } |
| 290 | + } else { |
| 291 | + if (rotation >= 0) { |
| 292 | + leftMotorOutput = speed + rotation; |
| 293 | + rightMotorOutput = maxInput; |
| 294 | + } else { |
| 295 | + leftMotorOutput = maxInput; |
| 296 | + rightMotorOutput = speed - rotation; |
| 297 | + } |
| 298 | + } |
| 299 | + DifferentialDriveWheelSpeeds currentSpeed = getWheelSpeeds(); |
| 300 | + |
| 301 | + // Somehow the flipped output for the right motor is assined to the left motor. |
| 302 | + // Somehow it woirks in the original arcadeDrive. |
| 303 | + double desiredLeftOutput = rightMotorOutput; |
| 304 | + double desiredRightOutput = -leftMotorOutput; |
| 305 | + // System.out.println("current: " + lspeed + " " + rspeed); |
| 306 | + // System.out.println("desired: " + desiredLeftOutput + " " + |
| 307 | + // desiredRightOutput); |
| 308 | + // P(ID?) Constants |
| 309 | + final double P = .5; |
| 310 | + // normalize speeds to [-1,1] |
| 311 | + double curLSpeed = currentSpeed.leftMetersPerSecond / Constants.MAX_SPEED_MPS; |
| 312 | + double curRSpeed = currentSpeed.rightMetersPerSecond / Constants.MAX_SPEED_MPS; |
| 313 | + // get error |
| 314 | + double leftErr = desiredLeftOutput - curLSpeed;// curLSpeed - desiredLeftOutput; |
| 315 | + double rightErr = desiredRightOutput - curRSpeed;// curRSpeed - desiredRightOutput; |
| 316 | + // got proportional offset |
| 317 | + double leftDiff = leftErr * P; |
| 318 | + double rightDiff = rightErr * P; |
| 319 | + |
| 320 | + // get final speeds; |
| 321 | + double leftSpeed = curLSpeed + leftDiff; |
| 322 | + double rightSpeed = curRSpeed + rightDiff; |
| 323 | + System.out.printf("left: %f , right : %f\n", leftSpeed, rightSpeed); |
| 324 | + leftLeader.set(leftSpeed); |
| 325 | + rightLeader.set(rightSpeed); |
| 326 | + } |
| 327 | + |
| 328 | + @Override |
| 329 | + public void close() throws Exception { |
| 330 | + leftLeader.close(); |
| 331 | + rightLeader.close(); |
| 332 | + leftFollower.close(); |
| 333 | + rightFollower.close(); |
| 334 | + } |
| 335 | +} |
0 commit comments