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