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