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; 009 010import edu.wpi.first.wpilibj.hal.FRCNetComm.tInstances; 011import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 012import edu.wpi.first.wpilibj.hal.HAL; 013 014import static java.util.Objects.requireNonNull; 015 016/** 017 * Utility class for handling Robot drive based on a definition of the motor configuration. The 018 * robot drive class handles basic driving for a robot. Currently, 2 and 4 motor tank and mecanum 019 * drive trains are supported. In the future other drive types like swerve might be implemented. 020 * Motor channel numbers are supplied on creation of the class. Those are used for either the drive 021 * function (intended for hand created drive code, such as autonomous) or with the Tank/Arcade 022 * functions intended to be used for Operator Control driving. 023 */ 024public class RobotDrive implements MotorSafety { 025 026 protected MotorSafetyHelper m_safetyHelper; 027 028 /** 029 * The location of a motor on the robot for the purpose of driving. 030 */ 031 public enum MotorType { 032 kFrontLeft(0), kFrontRight(1), kRearLeft(2), kRearRight(3); 033 034 @SuppressWarnings("MemberName") 035 public final int value; 036 037 private MotorType(int value) { 038 this.value = value; 039 } 040 } 041 042 public static final double kDefaultExpirationTime = 0.1; 043 public static final double kDefaultSensitivity = 0.5; 044 public static final double kDefaultMaxOutput = 1.0; 045 protected static final int kMaxNumberOfMotors = 4; 046 protected double m_sensitivity; 047 protected double m_maxOutput; 048 protected SpeedController m_frontLeftMotor; 049 protected SpeedController m_frontRightMotor; 050 protected SpeedController m_rearLeftMotor; 051 protected SpeedController m_rearRightMotor; 052 protected boolean m_allocatedSpeedControllers; 053 protected static boolean kArcadeRatioCurve_Reported = false; 054 protected static boolean kTank_Reported = false; 055 protected static boolean kArcadeStandard_Reported = false; 056 protected static boolean kMecanumCartesian_Reported = false; 057 protected static boolean kMecanumPolar_Reported = false; 058 059 /** 060 * Constructor for RobotDrive with 2 motors specified with channel numbers. Set up parameters for 061 * a two wheel drive system where the left and right motor pwm channels are specified in the call. 062 * This call assumes Talons for controlling the motors. 063 * 064 * @param leftMotorChannel The PWM channel number that drives the left motor. 065 * @param rightMotorChannel The PWM channel number that drives the right motor. 066 */ 067 public RobotDrive(final int leftMotorChannel, final int rightMotorChannel) { 068 m_sensitivity = kDefaultSensitivity; 069 m_maxOutput = kDefaultMaxOutput; 070 m_frontLeftMotor = null; 071 m_rearLeftMotor = new Talon(leftMotorChannel); 072 m_frontRightMotor = null; 073 m_rearRightMotor = new Talon(rightMotorChannel); 074 m_allocatedSpeedControllers = true; 075 setupMotorSafety(); 076 drive(0, 0); 077 } 078 079 /** 080 * Constructor for RobotDrive with 4 motors specified with channel numbers. Set up parameters for 081 * a four wheel drive system where all four motor pwm channels are specified in the call. This 082 * call assumes Talons for controlling the motors. 083 * 084 * @param frontLeftMotor Front left motor channel number 085 * @param rearLeftMotor Rear Left motor channel number 086 * @param frontRightMotor Front right motor channel number 087 * @param rearRightMotor Rear Right motor channel number 088 */ 089 public RobotDrive(final int frontLeftMotor, final int rearLeftMotor, final int frontRightMotor, 090 final int rearRightMotor) { 091 m_sensitivity = kDefaultSensitivity; 092 m_maxOutput = kDefaultMaxOutput; 093 m_rearLeftMotor = new Talon(rearLeftMotor); 094 m_rearRightMotor = new Talon(rearRightMotor); 095 m_frontLeftMotor = new Talon(frontLeftMotor); 096 m_frontRightMotor = new Talon(frontRightMotor); 097 m_allocatedSpeedControllers = true; 098 setupMotorSafety(); 099 drive(0, 0); 100 } 101 102 /** 103 * Constructor for RobotDrive with 2 motors specified as SpeedController objects. The 104 * SpeedController version of the constructor enables programs to use the RobotDrive classes with 105 * subclasses of the SpeedController objects, for example, versions with ramping or reshaping of 106 * the curve to suit motor bias or dead-band elimination. 107 * 108 * @param leftMotor The left SpeedController object used to drive the robot. 109 * @param rightMotor the right SpeedController object used to drive the robot. 110 */ 111 public RobotDrive(SpeedController leftMotor, SpeedController rightMotor) { 112 if (leftMotor == null || rightMotor == null) { 113 m_rearLeftMotor = m_rearRightMotor = null; 114 throw new NullPointerException("Null motor provided"); 115 } 116 m_frontLeftMotor = null; 117 m_rearLeftMotor = leftMotor; 118 m_frontRightMotor = null; 119 m_rearRightMotor = rightMotor; 120 m_sensitivity = kDefaultSensitivity; 121 m_maxOutput = kDefaultMaxOutput; 122 m_allocatedSpeedControllers = false; 123 setupMotorSafety(); 124 drive(0, 0); 125 } 126 127 /** 128 * Constructor for RobotDrive with 4 motors specified as SpeedController objects. Speed controller 129 * input version of RobotDrive (see previous comments). 130 * 131 * @param rearLeftMotor The back left SpeedController object used to drive the robot. 132 * @param frontLeftMotor The front left SpeedController object used to drive the robot 133 * @param rearRightMotor The back right SpeedController object used to drive the robot. 134 * @param frontRightMotor The front right SpeedController object used to drive the robot. 135 */ 136 public RobotDrive(SpeedController frontLeftMotor, SpeedController rearLeftMotor, 137 SpeedController frontRightMotor, SpeedController rearRightMotor) { 138 m_frontLeftMotor = requireNonNull(frontLeftMotor, "frontLeftMotor cannot be null"); 139 m_rearLeftMotor = requireNonNull(rearLeftMotor, "rearLeftMotor cannot be null"); 140 m_frontRightMotor = requireNonNull(frontRightMotor, "frontRightMotor cannot be null"); 141 m_rearRightMotor = requireNonNull(rearRightMotor, "rearRightMotor cannot be null"); 142 m_sensitivity = kDefaultSensitivity; 143 m_maxOutput = kDefaultMaxOutput; 144 m_allocatedSpeedControllers = false; 145 setupMotorSafety(); 146 drive(0, 0); 147 } 148 149 /** 150 * Drive the motors at "outputMagnitude" and "curve". Both outputMagnitude and curve are -1.0 to 151 * +1.0 values, where 0.0 represents stopped and not turning. {@literal curve < 0 will turn left 152 * and curve > 0} will turn right. 153 * 154 * <p>The algorithm for steering provides a constant turn radius for any normal speed range, both 155 * forward and backward. Increasing sensitivity causes sharper turns for fixed values of curve. 156 * 157 * <p>This function will most likely be used in an autonomous routine. 158 * 159 * @param outputMagnitude The speed setting for the outside wheel in a turn, forward or backwards, 160 * +1 to -1. 161 * @param curve The rate of turn, constant for different forward speeds. Set {@literal 162 * curve < 0 for left turn or curve > 0 for right turn.} Set curve = 163 * e^(-r/w) to get a turn radius r for wheelbase w of your robot. 164 * Conversely, turn radius r = -ln(curve)*w for a given value of curve and 165 * wheelbase w. 166 */ 167 public void drive(double outputMagnitude, double curve) { 168 final double leftOutput; 169 final double rightOutput; 170 171 if (!kArcadeRatioCurve_Reported) { 172 HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(), 173 tInstances.kRobotDrive_ArcadeRatioCurve); 174 kArcadeRatioCurve_Reported = true; 175 } 176 if (curve < 0) { 177 double value = Math.log(-curve); 178 double ratio = (value - m_sensitivity) / (value + m_sensitivity); 179 if (ratio == 0) { 180 ratio = .0000000001; 181 } 182 leftOutput = outputMagnitude / ratio; 183 rightOutput = outputMagnitude; 184 } else if (curve > 0) { 185 double value = Math.log(curve); 186 double ratio = (value - m_sensitivity) / (value + m_sensitivity); 187 if (ratio == 0) { 188 ratio = .0000000001; 189 } 190 leftOutput = outputMagnitude; 191 rightOutput = outputMagnitude / ratio; 192 } else { 193 leftOutput = outputMagnitude; 194 rightOutput = outputMagnitude; 195 } 196 setLeftRightMotorOutputs(leftOutput, rightOutput); 197 } 198 199 /** 200 * Provide tank steering using the stored robot configuration. drive the robot using two joystick 201 * inputs. The Y-axis will be selected from each Joystick object. 202 * 203 * @param leftStick The joystick to control the left side of the robot. 204 * @param rightStick The joystick to control the right side of the robot. 205 */ 206 public void tankDrive(GenericHID leftStick, GenericHID rightStick) { 207 if (leftStick == null || rightStick == null) { 208 throw new NullPointerException("Null HID provided"); 209 } 210 tankDrive(leftStick.getY(), rightStick.getY(), true); 211 } 212 213 /** 214 * Provide tank steering using the stored robot configuration. drive the robot using two joystick 215 * inputs. The Y-axis will be selected from each Joystick object. 216 * 217 * @param leftStick The joystick to control the left side of the robot. 218 * @param rightStick The joystick to control the right side of the robot. 219 * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds 220 */ 221 public void tankDrive(GenericHID leftStick, GenericHID rightStick, boolean squaredInputs) { 222 if (leftStick == null || rightStick == null) { 223 throw new NullPointerException("Null HID provided"); 224 } 225 tankDrive(leftStick.getY(), rightStick.getY(), squaredInputs); 226 } 227 228 /** 229 * Provide tank steering using the stored robot configuration. This function lets you pick the 230 * axis to be used on each Joystick object for the left and right sides of the robot. 231 * 232 * @param leftStick The Joystick object to use for the left side of the robot. 233 * @param leftAxis The axis to select on the left side Joystick object. 234 * @param rightStick The Joystick object to use for the right side of the robot. 235 * @param rightAxis The axis to select on the right side Joystick object. 236 */ 237 public void tankDrive(GenericHID leftStick, final int leftAxis, GenericHID rightStick, 238 final int rightAxis) { 239 if (leftStick == null || rightStick == null) { 240 throw new NullPointerException("Null HID provided"); 241 } 242 tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), true); 243 } 244 245 /** 246 * Provide tank steering using the stored robot configuration. This function lets you pick the 247 * axis to be used on each Joystick object for the left and right sides of the robot. 248 * 249 * @param leftStick The Joystick object to use for the left side of the robot. 250 * @param leftAxis The axis to select on the left side Joystick object. 251 * @param rightStick The Joystick object to use for the right side of the robot. 252 * @param rightAxis The axis to select on the right side Joystick object. 253 * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds 254 */ 255 public void tankDrive(GenericHID leftStick, final int leftAxis, GenericHID rightStick, 256 final int rightAxis, boolean squaredInputs) { 257 if (leftStick == null || rightStick == null) { 258 throw new NullPointerException("Null HID provided"); 259 } 260 tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), squaredInputs); 261 } 262 263 /** 264 * Provide tank steering using the stored robot configuration. This function lets you directly 265 * provide joystick values from any source. 266 * 267 * @param leftValue The value of the left stick. 268 * @param rightValue The value of the right stick. 269 * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds 270 */ 271 public void tankDrive(double leftValue, double rightValue, boolean squaredInputs) { 272 273 if (!kTank_Reported) { 274 HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(), 275 tInstances.kRobotDrive_Tank); 276 kTank_Reported = true; 277 } 278 279 // square the inputs (while preserving the sign) to increase fine control 280 // while permitting full power 281 leftValue = limit(leftValue); 282 rightValue = limit(rightValue); 283 if (squaredInputs) { 284 if (leftValue >= 0.0) { 285 leftValue = leftValue * leftValue; 286 } else { 287 leftValue = -(leftValue * leftValue); 288 } 289 if (rightValue >= 0.0) { 290 rightValue = rightValue * rightValue; 291 } else { 292 rightValue = -(rightValue * rightValue); 293 } 294 } 295 setLeftRightMotorOutputs(leftValue, rightValue); 296 } 297 298 /** 299 * Provide tank steering using the stored robot configuration. This function lets you directly 300 * provide joystick values from any source. 301 * 302 * @param leftValue The value of the left stick. 303 * @param rightValue The value of the right stick. 304 */ 305 public void tankDrive(double leftValue, double rightValue) { 306 tankDrive(leftValue, rightValue, true); 307 } 308 309 /** 310 * Arcade drive implements single stick driving. Given a single Joystick, the class assumes the Y 311 * axis for the move value and the X axis for the rotate value. (Should add more information here 312 * regarding the way that arcade drive works.) 313 * 314 * @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be 315 * selected for forwards/backwards and the X-axis will be selected for 316 * rotation rate. 317 * @param squaredInputs If true, the sensitivity will be decreased for small values 318 */ 319 public void arcadeDrive(GenericHID stick, boolean squaredInputs) { 320 // simply call the full-featured arcadeDrive with the appropriate values 321 arcadeDrive(stick.getY(), stick.getX(), squaredInputs); 322 } 323 324 /** 325 * Arcade drive implements single stick driving. Given a single Joystick, the class assumes the Y 326 * axis for the move value and the X axis for the rotate value. (Should add more information here 327 * regarding the way that arcade drive works.) 328 * 329 * @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be selected 330 * for forwards/backwards and the X-axis will be selected for rotation rate. 331 */ 332 public void arcadeDrive(GenericHID stick) { 333 arcadeDrive(stick, true); 334 } 335 336 /** 337 * Arcade drive implements single stick driving. Given two joystick instances and two axis, 338 * compute the values to send to either two or four motors. 339 * 340 * @param moveStick The Joystick object that represents the forward/backward direction 341 * @param moveAxis The axis on the moveStick object to use for forwards/backwards (typically 342 * Y_AXIS) 343 * @param rotateStick The Joystick object that represents the rotation value 344 * @param rotateAxis The axis on the rotation object to use for the rotate right/left 345 * (typically X_AXIS) 346 * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds 347 */ 348 public void arcadeDrive(GenericHID moveStick, final int moveAxis, GenericHID rotateStick, 349 final int rotateAxis, boolean squaredInputs) { 350 double moveValue = moveStick.getRawAxis(moveAxis); 351 double rotateValue = rotateStick.getRawAxis(rotateAxis); 352 353 arcadeDrive(moveValue, rotateValue, squaredInputs); 354 } 355 356 /** 357 * Arcade drive implements single stick driving. Given two joystick instances and two axis, 358 * compute the values to send to either two or four motors. 359 * 360 * @param moveStick The Joystick object that represents the forward/backward direction 361 * @param moveAxis The axis on the moveStick object to use for forwards/backwards (typically 362 * Y_AXIS) 363 * @param rotateStick The Joystick object that represents the rotation value 364 * @param rotateAxis The axis on the rotation object to use for the rotate right/left (typically 365 * X_AXIS) 366 */ 367 public void arcadeDrive(GenericHID moveStick, final int moveAxis, GenericHID rotateStick, 368 final int rotateAxis) { 369 arcadeDrive(moveStick, moveAxis, rotateStick, rotateAxis, true); 370 } 371 372 /** 373 * Arcade drive implements single stick driving. This function lets you directly provide 374 * joystick values from any source. 375 * 376 * @param moveValue The value to use for forwards/backwards 377 * @param rotateValue The value to use for the rotate right/left 378 * @param squaredInputs If set, decreases the sensitivity at low speeds 379 */ 380 public void arcadeDrive(double moveValue, double rotateValue, boolean squaredInputs) { 381 // local variables to hold the computed PWM values for the motors 382 if (!kArcadeStandard_Reported) { 383 HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(), 384 tInstances.kRobotDrive_ArcadeStandard); 385 kArcadeStandard_Reported = true; 386 } 387 388 double leftMotorSpeed; 389 double rightMotorSpeed; 390 391 moveValue = limit(moveValue); 392 rotateValue = limit(rotateValue); 393 394 if (squaredInputs) { 395 // square the inputs (while preserving the sign) to increase fine control 396 // while permitting full power 397 if (moveValue >= 0.0) { 398 moveValue = moveValue * moveValue; 399 } else { 400 moveValue = -(moveValue * moveValue); 401 } 402 if (rotateValue >= 0.0) { 403 rotateValue = rotateValue * rotateValue; 404 } else { 405 rotateValue = -(rotateValue * rotateValue); 406 } 407 } 408 409 if (moveValue > 0.0) { 410 if (rotateValue > 0.0) { 411 leftMotorSpeed = moveValue - rotateValue; 412 rightMotorSpeed = Math.max(moveValue, rotateValue); 413 } else { 414 leftMotorSpeed = Math.max(moveValue, -rotateValue); 415 rightMotorSpeed = moveValue + rotateValue; 416 } 417 } else { 418 if (rotateValue > 0.0) { 419 leftMotorSpeed = -Math.max(-moveValue, rotateValue); 420 rightMotorSpeed = moveValue + rotateValue; 421 } else { 422 leftMotorSpeed = moveValue - rotateValue; 423 rightMotorSpeed = -Math.max(-moveValue, -rotateValue); 424 } 425 } 426 427 setLeftRightMotorOutputs(leftMotorSpeed, rightMotorSpeed); 428 } 429 430 /** 431 * Arcade drive implements single stick driving. This function lets you directly provide 432 * joystick values from any source. 433 * 434 * @param moveValue The value to use for fowards/backwards 435 * @param rotateValue The value to use for the rotate right/left 436 */ 437 public void arcadeDrive(double moveValue, double rotateValue) { 438 arcadeDrive(moveValue, rotateValue, true); 439 } 440 441 /** 442 * Drive method for Mecanum wheeled robots. 443 * 444 * <p>A method for driving with Mecanum wheeled robots. There are 4 wheels on the robot, arranged 445 * so that the front and back wheels are toed in 45 degrees. When looking at the wheels from the 446 * top, the roller axles should form an X across the robot. 447 * 448 * <p>This is designed to be directly driven by joystick axes. 449 * 450 * @param x The speed that the robot should drive in the X direction. [-1.0..1.0] 451 * @param y The speed that the robot should drive in the Y direction. This input is 452 * inverted to match the forward == -1.0 that joysticks produce. [-1.0..1.0] 453 * @param rotation The rate of rotation for the robot that is completely independent of the 454 * translation. [-1.0..1.0] 455 * @param gyroAngle The current angle reading from the gyro. Use this to implement field-oriented 456 * controls. 457 */ 458 @SuppressWarnings("ParameterName") 459 public void mecanumDrive_Cartesian(double x, double y, double rotation, double gyroAngle) { 460 if (!kMecanumCartesian_Reported) { 461 HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(), 462 tInstances.kRobotDrive_MecanumCartesian); 463 kMecanumCartesian_Reported = true; 464 } 465 @SuppressWarnings("LocalVariableName") 466 double xIn = x; 467 @SuppressWarnings("LocalVariableName") 468 double yIn = y; 469 // Negate y for the joystick. 470 yIn = -yIn; 471 // Compenstate for gyro angle. 472 double[] rotated = rotateVector(xIn, yIn, gyroAngle); 473 xIn = rotated[0]; 474 yIn = rotated[1]; 475 476 double[] wheelSpeeds = new double[kMaxNumberOfMotors]; 477 wheelSpeeds[MotorType.kFrontLeft.value] = xIn + yIn + rotation; 478 wheelSpeeds[MotorType.kFrontRight.value] = -xIn + yIn - rotation; 479 wheelSpeeds[MotorType.kRearLeft.value] = -xIn + yIn + rotation; 480 wheelSpeeds[MotorType.kRearRight.value] = xIn + yIn - rotation; 481 482 normalize(wheelSpeeds); 483 m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft.value] * m_maxOutput); 484 m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight.value] * m_maxOutput); 485 m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft.value] * m_maxOutput); 486 m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight.value] * m_maxOutput); 487 488 if (m_safetyHelper != null) { 489 m_safetyHelper.feed(); 490 } 491 } 492 493 /** 494 * Drive method for Mecanum wheeled robots. 495 * 496 * <p>A method for driving with Mecanum wheeled robots. There are 4 wheels on the robot, arranged 497 * so that the front and back wheels are toed in 45 degrees. When looking at the wheels from the 498 * top, the roller axles should form an X across the robot. 499 * 500 * @param magnitude The speed that the robot should drive in a given direction. 501 * @param direction The direction the robot should drive in degrees. The direction and maginitute 502 * are independent of the rotation rate. 503 * @param rotation The rate of rotation for the robot that is completely independent of the 504 * magnitute or direction. [-1.0..1.0] 505 */ 506 public void mecanumDrive_Polar(double magnitude, double direction, double rotation) { 507 if (!kMecanumPolar_Reported) { 508 HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(), 509 tInstances.kRobotDrive_MecanumPolar); 510 kMecanumPolar_Reported = true; 511 } 512 // Normalized for full power along the Cartesian axes. 513 magnitude = limit(magnitude) * Math.sqrt(2.0); 514 // The rollers are at 45 degree angles. 515 double dirInRad = (direction + 45.0) * 3.14159 / 180.0; 516 double cosD = Math.cos(dirInRad); 517 double sinD = Math.sin(dirInRad); 518 519 double[] wheelSpeeds = new double[kMaxNumberOfMotors]; 520 wheelSpeeds[MotorType.kFrontLeft.value] = (sinD * magnitude + rotation); 521 wheelSpeeds[MotorType.kFrontRight.value] = (cosD * magnitude - rotation); 522 wheelSpeeds[MotorType.kRearLeft.value] = (cosD * magnitude + rotation); 523 wheelSpeeds[MotorType.kRearRight.value] = (sinD * magnitude - rotation); 524 525 normalize(wheelSpeeds); 526 527 m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft.value] * m_maxOutput); 528 m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight.value] * m_maxOutput); 529 m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft.value] * m_maxOutput); 530 m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight.value] * m_maxOutput); 531 532 if (m_safetyHelper != null) { 533 m_safetyHelper.feed(); 534 } 535 } 536 537 /** 538 * Holonomic Drive method for Mecanum wheeled robots. 539 * 540 * <p>This is an alias to mecanumDrive_Polar() for backward compatability 541 * 542 * @param magnitude The speed that the robot should drive in a given direction. [-1.0..1.0] 543 * @param direction The direction the robot should drive. The direction and maginitute are 544 * independent of the rotation rate. 545 * @param rotation The rate of rotation for the robot that is completely independent of the 546 * magnitute or direction. [-1.0..1.0] 547 */ 548 void holonomicDrive(double magnitude, double direction, double rotation) { 549 mecanumDrive_Polar(magnitude, direction, rotation); 550 } 551 552 /** 553 * Set the speed of the right and left motors. This is used once an appropriate drive setup 554 * function is called such as twoWheelDrive(). The motors are set to "leftSpeed" and 555 * "rightSpeed" and includes flipping the direction of one side for opposing motors. 556 * 557 * @param leftOutput The speed to send to the left side of the robot. 558 * @param rightOutput The speed to send to the right side of the robot. 559 */ 560 public void setLeftRightMotorOutputs(double leftOutput, double rightOutput) { 561 if (m_rearLeftMotor == null || m_rearRightMotor == null) { 562 throw new NullPointerException("Null motor provided"); 563 } 564 565 if (m_frontLeftMotor != null) { 566 m_frontLeftMotor.set(limit(leftOutput) * m_maxOutput); 567 } 568 m_rearLeftMotor.set(limit(leftOutput) * m_maxOutput); 569 570 if (m_frontRightMotor != null) { 571 m_frontRightMotor.set(-limit(rightOutput) * m_maxOutput); 572 } 573 m_rearRightMotor.set(-limit(rightOutput) * m_maxOutput); 574 575 if (m_safetyHelper != null) { 576 m_safetyHelper.feed(); 577 } 578 } 579 580 /** 581 * Limit motor values to the -1.0 to +1.0 range. 582 */ 583 protected static double limit(double num) { 584 if (num > 1.0) { 585 return 1.0; 586 } 587 if (num < -1.0) { 588 return -1.0; 589 } 590 return num; 591 } 592 593 /** 594 * Normalize all wheel speeds if the magnitude of any wheel is greater than 1.0. 595 */ 596 protected static void normalize(double[] wheelSpeeds) { 597 double maxMagnitude = Math.abs(wheelSpeeds[0]); 598 for (int i = 1; i < kMaxNumberOfMotors; i++) { 599 double temp = Math.abs(wheelSpeeds[i]); 600 if (maxMagnitude < temp) { 601 maxMagnitude = temp; 602 } 603 } 604 if (maxMagnitude > 1.0) { 605 for (int i = 0; i < kMaxNumberOfMotors; i++) { 606 wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude; 607 } 608 } 609 } 610 611 /** 612 * Rotate a vector in Cartesian space. 613 */ 614 @SuppressWarnings("ParameterName") 615 protected static double[] rotateVector(double x, double y, double angle) { 616 double cosA = Math.cos(angle * (3.14159 / 180.0)); 617 double sinA = Math.sin(angle * (3.14159 / 180.0)); 618 double[] out = new double[2]; 619 out[0] = x * cosA - y * sinA; 620 out[1] = x * sinA + y * cosA; 621 return out; 622 } 623 624 /** 625 * Invert a motor direction. This is used when a motor should run in the opposite direction as the 626 * drive code would normally run it. Motors that are direct drive would be inverted, the drive 627 * code assumes that the motors are geared with one reversal. 628 * 629 * @param motor The motor index to invert. 630 * @param isInverted True if the motor should be inverted when operated. 631 */ 632 public void setInvertedMotor(MotorType motor, boolean isInverted) { 633 switch (motor) { 634 case kFrontLeft: 635 m_frontLeftMotor.setInverted(isInverted); 636 break; 637 case kFrontRight: 638 m_frontRightMotor.setInverted(isInverted); 639 break; 640 case kRearLeft: 641 m_rearLeftMotor.setInverted(isInverted); 642 break; 643 case kRearRight: 644 m_rearRightMotor.setInverted(isInverted); 645 break; 646 default: 647 throw new IllegalArgumentException("Illegal motor type: " + motor); 648 } 649 } 650 651 /** 652 * Set the turning sensitivity. 653 * 654 * <p>This only impacts the drive() entry-point. 655 * 656 * @param sensitivity Effectively sets the turning sensitivity (or turn radius for a given value) 657 */ 658 public void setSensitivity(double sensitivity) { 659 m_sensitivity = sensitivity; 660 } 661 662 /** 663 * Configure the scaling factor for using RobotDrive with motor controllers in a mode other than 664 * PercentVbus. 665 * 666 * @param maxOutput Multiplied with the output percentage computed by the drive functions. 667 */ 668 public void setMaxOutput(double maxOutput) { 669 m_maxOutput = maxOutput; 670 } 671 672 /** 673 * Free the speed controllers if they were allocated locally. 674 */ 675 public void free() { 676 if (m_allocatedSpeedControllers) { 677 if (m_frontLeftMotor != null) { 678 ((PWM) m_frontLeftMotor).free(); 679 } 680 if (m_frontRightMotor != null) { 681 ((PWM) m_frontRightMotor).free(); 682 } 683 if (m_rearLeftMotor != null) { 684 ((PWM) m_rearLeftMotor).free(); 685 } 686 if (m_rearRightMotor != null) { 687 ((PWM) m_rearRightMotor).free(); 688 } 689 } 690 } 691 692 @Override 693 public void setExpiration(double timeout) { 694 m_safetyHelper.setExpiration(timeout); 695 } 696 697 @Override 698 public double getExpiration() { 699 return m_safetyHelper.getExpiration(); 700 } 701 702 @Override 703 public boolean isAlive() { 704 return m_safetyHelper.isAlive(); 705 } 706 707 @Override 708 public boolean isSafetyEnabled() { 709 return m_safetyHelper.isSafetyEnabled(); 710 } 711 712 @Override 713 public void setSafetyEnabled(boolean enabled) { 714 m_safetyHelper.setSafetyEnabled(enabled); 715 } 716 717 @Override 718 public String getDescription() { 719 return "Robot Drive"; 720 } 721 722 @Override 723 public void stopMotor() { 724 if (m_frontLeftMotor != null) { 725 m_frontLeftMotor.stopMotor(); 726 } 727 if (m_frontRightMotor != null) { 728 m_frontRightMotor.stopMotor(); 729 } 730 if (m_rearLeftMotor != null) { 731 m_rearLeftMotor.stopMotor(); 732 } 733 if (m_rearRightMotor != null) { 734 m_rearRightMotor.stopMotor(); 735 } 736 if (m_safetyHelper != null) { 737 m_safetyHelper.feed(); 738 } 739 } 740 741 private void setupMotorSafety() { 742 m_safetyHelper = new MotorSafetyHelper(this); 743 m_safetyHelper.setExpiration(kDefaultExpirationTime); 744 m_safetyHelper.setSafetyEnabled(true); 745 } 746 747 protected int getNumMotors() { 748 int motors = 0; 749 if (m_frontLeftMotor != null) { 750 motors++; 751 } 752 if (m_frontRightMotor != null) { 753 motors++; 754 } 755 if (m_rearLeftMotor != null) { 756 motors++; 757 } 758 if (m_rearRightMotor != null) { 759 motors++; 760 } 761 return motors; 762 } 763}