blob: c8c1897f66f4d72947c68d889a138559966807f8 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "RobotDrive.h"
9
10#include <algorithm>
11#include <cmath>
12
13#include "GenericHID.h"
14#include "HAL/HAL.h"
15#include "Joystick.h"
16#include "Talon.h"
17#include "Utility.h"
18#include "WPIErrors.h"
19
20using namespace frc;
21
22const int RobotDrive::kMaxNumberOfMotors;
23
24static auto make_shared_nodelete(SpeedController* ptr) {
25 return std::shared_ptr<SpeedController>(ptr, NullDeleter<SpeedController>());
26}
27
28/*
29 * Driving functions
30 * These functions provide an interface to multiple motors that is used for C
31 * programming.
32 * The Drive(speed, direction) function is the main part of the set that makes
33 * it easy to set speeds and direction independently in one call.
34 */
35
36/**
37 * Common function to initialize all the robot drive constructors.
38 *
39 * Create a motor safety object (the real reason for the common code) and
40 * initialize all the motor assignments. The default timeout is set for the
41 * robot drive.
42 */
43void RobotDrive::InitRobotDrive() {
44 m_safetyHelper = std::make_unique<MotorSafetyHelper>(this);
45 m_safetyHelper->SetSafetyEnabled(true);
46}
47
48/**
49 * Constructor for RobotDrive with 2 motors specified with channel numbers.
50 *
51 * Set up parameters for a two wheel drive system where the
52 * left and right motor pwm channels are specified in the call.
53 * This call assumes Talons for controlling the motors.
54 *
55 * @param leftMotorChannel The PWM channel number that drives the left motor.
56 * 0-9 are on-board, 10-19 are on the MXP port
57 * @param rightMotorChannel The PWM channel number that drives the right motor.
58 * 0-9 are on-board, 10-19 are on the MXP port
59 */
60RobotDrive::RobotDrive(int leftMotorChannel, int rightMotorChannel) {
61 InitRobotDrive();
62 m_rearLeftMotor = std::make_shared<Talon>(leftMotorChannel);
63 m_rearRightMotor = std::make_shared<Talon>(rightMotorChannel);
64 SetLeftRightMotorOutputs(0.0, 0.0);
65}
66
67/**
68 * Constructor for RobotDrive with 4 motors specified with channel numbers.
69 *
70 * Set up parameters for a four wheel drive system where all four motor
71 * pwm channels are specified in the call.
72 * This call assumes Talons for controlling the motors.
73 *
74 * @param frontLeftMotor Front left motor channel number. 0-9 are on-board,
75 * 10-19 are on the MXP port
76 * @param rearLeftMotor Rear Left motor channel number. 0-9 are on-board,
77 * 10-19 are on the MXP port
78 * @param frontRightMotor Front right motor channel number. 0-9 are on-board,
79 * 10-19 are on the MXP port
80 * @param rearRightMotor Rear Right motor channel number. 0-9 are on-board,
81 * 10-19 are on the MXP port
82 */
83RobotDrive::RobotDrive(int frontLeftMotor, int rearLeftMotor,
84 int frontRightMotor, int rearRightMotor) {
85 InitRobotDrive();
86 m_rearLeftMotor = std::make_shared<Talon>(rearLeftMotor);
87 m_rearRightMotor = std::make_shared<Talon>(rearRightMotor);
88 m_frontLeftMotor = std::make_shared<Talon>(frontLeftMotor);
89 m_frontRightMotor = std::make_shared<Talon>(frontRightMotor);
90 SetLeftRightMotorOutputs(0.0, 0.0);
91}
92
93/**
94 * Constructor for RobotDrive with 2 motors specified as SpeedController
95 * objects.
96 *
97 * The SpeedController version of the constructor enables programs to use the
98 * RobotDrive classes with subclasses of the SpeedController objects, for
99 * example, versions with ramping or reshaping of the curve to suit motor bias
100 * or deadband elimination.
101 *
102 * @param leftMotor The left SpeedController object used to drive the robot.
103 * @param rightMotor The right SpeedController object used to drive the robot.
104 */
105RobotDrive::RobotDrive(SpeedController* leftMotor,
106 SpeedController* rightMotor) {
107 InitRobotDrive();
108 if (leftMotor == nullptr || rightMotor == nullptr) {
109 wpi_setWPIError(NullParameter);
110 m_rearLeftMotor = m_rearRightMotor = nullptr;
111 return;
112 }
113 m_rearLeftMotor = make_shared_nodelete(leftMotor);
114 m_rearRightMotor = make_shared_nodelete(rightMotor);
115}
116
117// TODO: Change to rvalue references & move syntax.
118RobotDrive::RobotDrive(SpeedController& leftMotor,
119 SpeedController& rightMotor) {
120 InitRobotDrive();
121 m_rearLeftMotor = make_shared_nodelete(&leftMotor);
122 m_rearRightMotor = make_shared_nodelete(&rightMotor);
123}
124
125RobotDrive::RobotDrive(std::shared_ptr<SpeedController> leftMotor,
126 std::shared_ptr<SpeedController> rightMotor) {
127 InitRobotDrive();
128 if (leftMotor == nullptr || rightMotor == nullptr) {
129 wpi_setWPIError(NullParameter);
130 m_rearLeftMotor = m_rearRightMotor = nullptr;
131 return;
132 }
133 m_rearLeftMotor = leftMotor;
134 m_rearRightMotor = rightMotor;
135}
136
137/**
138 * Constructor for RobotDrive with 4 motors specified as SpeedController
139 * objects.
140 *
141 * Speed controller input version of RobotDrive (see previous comments).
142 *
143 * @param rearLeftMotor The back left SpeedController object used to drive
144 * the robot.
145 * @param frontLeftMotor The front left SpeedController object used to drive
146 * the robot.
147 * @param rearRightMotor The back right SpeedController object used to drive
148 * the robot.
149 * @param frontRightMotor The front right SpeedController object used to drive
150 * the robot.
151 */
152RobotDrive::RobotDrive(SpeedController* frontLeftMotor,
153 SpeedController* rearLeftMotor,
154 SpeedController* frontRightMotor,
155 SpeedController* rearRightMotor) {
156 InitRobotDrive();
157 if (frontLeftMotor == nullptr || rearLeftMotor == nullptr ||
158 frontRightMotor == nullptr || rearRightMotor == nullptr) {
159 wpi_setWPIError(NullParameter);
160 return;
161 }
162 m_frontLeftMotor = make_shared_nodelete(frontLeftMotor);
163 m_rearLeftMotor = make_shared_nodelete(rearLeftMotor);
164 m_frontRightMotor = make_shared_nodelete(frontRightMotor);
165 m_rearRightMotor = make_shared_nodelete(rearRightMotor);
166}
167
168RobotDrive::RobotDrive(SpeedController& frontLeftMotor,
169 SpeedController& rearLeftMotor,
170 SpeedController& frontRightMotor,
171 SpeedController& rearRightMotor) {
172 InitRobotDrive();
173 m_frontLeftMotor = make_shared_nodelete(&frontLeftMotor);
174 m_rearLeftMotor = make_shared_nodelete(&rearLeftMotor);
175 m_frontRightMotor = make_shared_nodelete(&frontRightMotor);
176 m_rearRightMotor = make_shared_nodelete(&rearRightMotor);
177}
178
179RobotDrive::RobotDrive(std::shared_ptr<SpeedController> frontLeftMotor,
180 std::shared_ptr<SpeedController> rearLeftMotor,
181 std::shared_ptr<SpeedController> frontRightMotor,
182 std::shared_ptr<SpeedController> rearRightMotor) {
183 InitRobotDrive();
184 if (frontLeftMotor == nullptr || rearLeftMotor == nullptr ||
185 frontRightMotor == nullptr || rearRightMotor == nullptr) {
186 wpi_setWPIError(NullParameter);
187 return;
188 }
189 m_frontLeftMotor = frontLeftMotor;
190 m_rearLeftMotor = rearLeftMotor;
191 m_frontRightMotor = frontRightMotor;
192 m_rearRightMotor = rearRightMotor;
193}
194
195/**
196 * Drive the motors at "outputMagnitude" and "curve".
197 * Both outputMagnitude and curve are -1.0 to +1.0 values, where 0.0 represents
198 * stopped and not turning. curve < 0 will turn left and curve > 0 will turn
199 * right.
200 *
201 * The algorithm for steering provides a constant turn radius for any normal
202 * speed range, both forward and backward. Increasing m_sensitivity causes
203 * sharper turns for fixed values of curve.
204 *
205 * This function will most likely be used in an autonomous routine.
206 *
207 * @param outputMagnitude The speed setting for the outside wheel in a turn,
208 * forward or backwards, +1 to -1.
209 * @param curve The rate of turn, constant for different forward
210 * speeds. Set curve < 0 for left turn or curve > 0 for
211 * right turn.
212 *
213 * Set curve = e^(-r/w) to get a turn radius r for wheelbase w of your robot.
214 * Conversely, turn radius r = -ln(curve)*w for a given value of curve and
215 * wheelbase w.
216 */
217void RobotDrive::Drive(double outputMagnitude, double curve) {
218 double leftOutput, rightOutput;
219 static bool reported = false;
220 if (!reported) {
221 HAL_Report(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(),
222 HALUsageReporting::kRobotDrive_ArcadeRatioCurve);
223 reported = true;
224 }
225
226 if (curve < 0) {
227 double value = std::log(-curve);
228 double ratio = (value - m_sensitivity) / (value + m_sensitivity);
229 if (ratio == 0) ratio = .0000000001;
230 leftOutput = outputMagnitude / ratio;
231 rightOutput = outputMagnitude;
232 } else if (curve > 0) {
233 double value = std::log(curve);
234 double ratio = (value - m_sensitivity) / (value + m_sensitivity);
235 if (ratio == 0) ratio = .0000000001;
236 leftOutput = outputMagnitude;
237 rightOutput = outputMagnitude / ratio;
238 } else {
239 leftOutput = outputMagnitude;
240 rightOutput = outputMagnitude;
241 }
242 SetLeftRightMotorOutputs(leftOutput, rightOutput);
243}
244
245/**
246 * Provide tank steering using the stored robot configuration.
247 *
248 * Drive the robot using two joystick inputs. The Y-axis will be selected from
249 * each Joystick object.
250 *
251 * @param leftStick The joystick to control the left side of the robot.
252 * @param rightStick The joystick to control the right side of the robot.
253 */
254void RobotDrive::TankDrive(GenericHID* leftStick, GenericHID* rightStick,
255 bool squaredInputs) {
256 if (leftStick == nullptr || rightStick == nullptr) {
257 wpi_setWPIError(NullParameter);
258 return;
259 }
260 TankDrive(leftStick->GetY(), rightStick->GetY(), squaredInputs);
261}
262
263void RobotDrive::TankDrive(GenericHID& leftStick, GenericHID& rightStick,
264 bool squaredInputs) {
265 TankDrive(leftStick.GetY(), rightStick.GetY(), squaredInputs);
266}
267
268/**
269 * Provide tank steering using the stored robot configuration.
270 *
271 * This function lets you pick the axis to be used on each Joystick object for
272 * the left and right sides of the robot.
273 *
274 * @param leftStick The Joystick object to use for the left side of the robot.
275 * @param leftAxis The axis to select on the left side Joystick object.
276 * @param rightStick The Joystick object to use for the right side of the
277 * robot.
278 * @param rightAxis The axis to select on the right side Joystick object.
279 */
280void RobotDrive::TankDrive(GenericHID* leftStick, int leftAxis,
281 GenericHID* rightStick, int rightAxis,
282 bool squaredInputs) {
283 if (leftStick == nullptr || rightStick == nullptr) {
284 wpi_setWPIError(NullParameter);
285 return;
286 }
287 TankDrive(leftStick->GetRawAxis(leftAxis), rightStick->GetRawAxis(rightAxis),
288 squaredInputs);
289}
290
291void RobotDrive::TankDrive(GenericHID& leftStick, int leftAxis,
292 GenericHID& rightStick, int rightAxis,
293 bool squaredInputs) {
294 TankDrive(leftStick.GetRawAxis(leftAxis), rightStick.GetRawAxis(rightAxis),
295 squaredInputs);
296}
297
298/**
299 * Provide tank steering using the stored robot configuration.
300 *
301 * This function lets you directly provide joystick values from any source.
302 *
303 * @param leftValue The value of the left stick.
304 * @param rightValue The value of the right stick.
305 */
306void RobotDrive::TankDrive(double leftValue, double rightValue,
307 bool squaredInputs) {
308 static bool reported = false;
309 if (!reported) {
310 HAL_Report(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(),
311 HALUsageReporting::kRobotDrive_Tank);
312 reported = true;
313 }
314
315 // square the inputs (while preserving the sign) to increase fine control
316 // while permitting full power
317 leftValue = Limit(leftValue);
318 rightValue = Limit(rightValue);
319 if (squaredInputs) {
320 if (leftValue >= 0.0) {
321 leftValue = (leftValue * leftValue);
322 } else {
323 leftValue = -(leftValue * leftValue);
324 }
325 if (rightValue >= 0.0) {
326 rightValue = (rightValue * rightValue);
327 } else {
328 rightValue = -(rightValue * rightValue);
329 }
330 }
331
332 SetLeftRightMotorOutputs(leftValue, rightValue);
333}
334
335/**
336 * Arcade drive implements single stick driving.
337 *
338 * Given a single Joystick, the class assumes the Y axis for the move value and
339 * the X axis for the rotate value.
340 * (Should add more information here regarding the way that arcade drive works.)
341 *
342 * @param stick The joystick to use for Arcade single-stick driving.
343 * The Y-axis will be selected for forwards/backwards and
344 * the X-axis will be selected for rotation rate.
345 * @param squaredInputs If true, the sensitivity will be increased for small
346 * values
347 */
348void RobotDrive::ArcadeDrive(GenericHID* stick, bool squaredInputs) {
349 // simply call the full-featured ArcadeDrive with the appropriate values
350 ArcadeDrive(stick->GetY(), stick->GetX(), squaredInputs);
351}
352
353/**
354 * Arcade drive implements single stick driving.
355 *
356 * Given a single Joystick, the class assumes the Y axis for the move value and
357 * the X axis for the rotate value.
358 * (Should add more information here regarding the way that arcade drive works.)
359 *
360 * @param stick The joystick to use for Arcade single-stick driving.
361 * The Y-axis will be selected for forwards/backwards and
362 * the X-axis will be selected for rotation rate.
363 * @param squaredInputs If true, the sensitivity will be increased for small
364 * values
365 */
366void RobotDrive::ArcadeDrive(GenericHID& stick, bool 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.
373 *
374 * Given two joystick instances and two axis, compute the values to send to
375 * either two or four motors.
376 *
377 * @param moveStick The Joystick object that represents the
378 * forward/backward direction
379 * @param moveAxis The axis on the moveStick object to use for
380 * forwards/backwards (typically Y_AXIS)
381 * @param rotateStick The Joystick object that represents the rotation value
382 * @param rotateAxis The axis on the rotation object to use for the rotate
383 * right/left (typically X_AXIS)
384 * @param squaredInputs Setting this parameter to true increases the
385 * sensitivity at lower speeds
386 */
387void RobotDrive::ArcadeDrive(GenericHID* moveStick, int moveAxis,
388 GenericHID* rotateStick, int rotateAxis,
389 bool squaredInputs) {
390 double moveValue = moveStick->GetRawAxis(moveAxis);
391 double rotateValue = rotateStick->GetRawAxis(rotateAxis);
392
393 ArcadeDrive(moveValue, rotateValue, squaredInputs);
394}
395
396/**
397 * Arcade drive implements single stick driving.
398 *
399 * Given two joystick instances and two axis, compute the values to send to
400 * either two or four motors.
401 *
402 * @param moveStick The Joystick object that represents the
403 * forward/backward direction
404 * @param moveAxis The axis on the moveStick object to use for
405 * forwards/backwards (typically Y_AXIS)
406 * @param rotateStick The Joystick object that represents the rotation value
407 * @param rotateAxis The axis on the rotation object to use for the rotate
408 * right/left (typically X_AXIS)
409 * @param squaredInputs Setting this parameter to true increases the
410 * sensitivity at lower speeds
411 */
412void RobotDrive::ArcadeDrive(GenericHID& moveStick, int moveAxis,
413 GenericHID& rotateStick, int rotateAxis,
414 bool squaredInputs) {
415 double moveValue = moveStick.GetRawAxis(moveAxis);
416 double rotateValue = rotateStick.GetRawAxis(rotateAxis);
417
418 ArcadeDrive(moveValue, rotateValue, squaredInputs);
419}
420
421/**
422 * Arcade drive implements single stick driving.
423 *
424 * This function lets you directly provide joystick values from any source.
425 *
426 * @param moveValue The value to use for fowards/backwards
427 * @param rotateValue The value to use for the rotate right/left
428 * @param squaredInputs If set, increases the sensitivity at low speeds
429 */
430void RobotDrive::ArcadeDrive(double moveValue, double rotateValue,
431 bool squaredInputs) {
432 static bool reported = false;
433 if (!reported) {
434 HAL_Report(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(),
435 HALUsageReporting::kRobotDrive_ArcadeStandard);
436 reported = true;
437 }
438
439 // local variables to hold the computed PWM values for the motors
440 double leftMotorOutput;
441 double rightMotorOutput;
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 leftMotorOutput = moveValue - rotateValue;
464 rightMotorOutput = std::max(moveValue, rotateValue);
465 } else {
466 leftMotorOutput = std::max(moveValue, -rotateValue);
467 rightMotorOutput = moveValue + rotateValue;
468 }
469 } else {
470 if (rotateValue > 0.0) {
471 leftMotorOutput = -std::max(-moveValue, rotateValue);
472 rightMotorOutput = moveValue + rotateValue;
473 } else {
474 leftMotorOutput = moveValue - rotateValue;
475 rightMotorOutput = -std::max(-moveValue, -rotateValue);
476 }
477 }
478 SetLeftRightMotorOutputs(leftMotorOutput, rightMotorOutput);
479}
480
481/**
482 * Drive method for Mecanum wheeled robots.
483 *
484 * A method for driving with Mecanum wheeled robots. There are 4 wheels
485 * on the robot, arranged so that the front and back wheels are toed in 45
486 * degrees.
487 * When looking at the wheels from the top, the roller axles should form an X
488 * across the robot.
489 *
490 * This is designed to be directly driven by joystick axes.
491 *
492 * @param x The speed that the robot should drive in the X direction.
493 * [-1.0..1.0]
494 * @param y The speed that the robot should drive in the Y direction.
495 * This input is inverted to match the forward == -1.0 that
496 * joysticks produce. [-1.0..1.0]
497 * @param rotation The rate of rotation for the robot that is completely
498 * independent of the translation. [-1.0..1.0]
499 * @param gyroAngle The current angle reading from the gyro. Use this to
500 * implement field-oriented controls.
501 */
502void RobotDrive::MecanumDrive_Cartesian(double x, double y, double rotation,
503 double gyroAngle) {
504 static bool reported = false;
505 if (!reported) {
506 HAL_Report(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(),
507 HALUsageReporting::kRobotDrive_MecanumCartesian);
508 reported = true;
509 }
510
511 double xIn = x;
512 double yIn = y;
513 // Negate y for the joystick.
514 yIn = -yIn;
515 // Compenstate for gyro angle.
516 RotateVector(xIn, yIn, gyroAngle);
517
518 double wheelSpeeds[kMaxNumberOfMotors];
519 wheelSpeeds[kFrontLeftMotor] = xIn + yIn + rotation;
520 wheelSpeeds[kFrontRightMotor] = -xIn + yIn - rotation;
521 wheelSpeeds[kRearLeftMotor] = -xIn + yIn + rotation;
522 wheelSpeeds[kRearRightMotor] = xIn + yIn - rotation;
523
524 Normalize(wheelSpeeds);
525
526 m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput);
527 m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput);
528 m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput);
529 m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput);
530
531 m_safetyHelper->Feed();
532}
533
534/**
535 * Drive method for Mecanum wheeled robots.
536 *
537 * A method for driving with Mecanum wheeled robots. There are 4 wheels
538 * on the robot, arranged so that the front and back wheels are toed in 45
539 * degrees.
540 * When looking at the wheels from the top, the roller axles should form an X
541 * across the robot.
542 *
543 * @param magnitude The speed that the robot should drive in a given direction.
544 * [-1.0..1.0]
545 * @param direction The direction the robot should drive in degrees. The
546 * direction and maginitute are independent of the rotation
547 * rate.
548 * @param rotation The rate of rotation for the robot that is completely
549 * independent of the magnitute or direction. [-1.0..1.0]
550 */
551void RobotDrive::MecanumDrive_Polar(double magnitude, double direction,
552 double rotation) {
553 static bool reported = false;
554 if (!reported) {
555 HAL_Report(HALUsageReporting::kResourceType_RobotDrive, GetNumMotors(),
556 HALUsageReporting::kRobotDrive_MecanumPolar);
557 reported = true;
558 }
559
560 // Normalized for full power along the Cartesian axes.
561 magnitude = Limit(magnitude) * std::sqrt(2.0);
562 // The rollers are at 45 degree angles.
563 double dirInRad = (direction + 45.0) * 3.14159 / 180.0;
564 double cosD = std::cos(dirInRad);
565 double sinD = std::sin(dirInRad);
566
567 double wheelSpeeds[kMaxNumberOfMotors];
568 wheelSpeeds[kFrontLeftMotor] = sinD * magnitude + rotation;
569 wheelSpeeds[kFrontRightMotor] = cosD * magnitude - rotation;
570 wheelSpeeds[kRearLeftMotor] = cosD * magnitude + rotation;
571 wheelSpeeds[kRearRightMotor] = sinD * magnitude - rotation;
572
573 Normalize(wheelSpeeds);
574
575 m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput);
576 m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput);
577 m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput);
578 m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput);
579
580 m_safetyHelper->Feed();
581}
582
583/**
584 * Holonomic Drive method for Mecanum wheeled robots.
585 *
586 * This is an alias to MecanumDrive_Polar() for backward compatability
587 *
588 * @param magnitude The speed that the robot should drive in a given direction.
589 * [-1.0..1.0]
590 * @param direction The direction the robot should drive. The direction and
591 * magnitude are independent of the rotation rate.
592 * @param rotation The rate of rotation for the robot that is completely
593 * independent of the magnitude or direction. [-1.0..1.0]
594 */
595void RobotDrive::HolonomicDrive(double magnitude, double direction,
596 double rotation) {
597 MecanumDrive_Polar(magnitude, direction, rotation);
598}
599
600/**
601 * Set the speed of the right and left motors.
602 *
603 * This is used once an appropriate drive setup function is called such as
604 * TwoWheelDrive(). The motors are set to "leftOutput" and "rightOutput"
605 * and includes flipping the direction of one side for opposing motors.
606 *
607 * @param leftOutput The speed to send to the left side of the robot.
608 * @param rightOutput The speed to send to the right side of the robot.
609 */
610void RobotDrive::SetLeftRightMotorOutputs(double leftOutput,
611 double rightOutput) {
612 wpi_assert(m_rearLeftMotor != nullptr && m_rearRightMotor != nullptr);
613
614 if (m_frontLeftMotor != nullptr)
615 m_frontLeftMotor->Set(Limit(leftOutput) * m_maxOutput);
616 m_rearLeftMotor->Set(Limit(leftOutput) * m_maxOutput);
617
618 if (m_frontRightMotor != nullptr)
619 m_frontRightMotor->Set(-Limit(rightOutput) * m_maxOutput);
620 m_rearRightMotor->Set(-Limit(rightOutput) * m_maxOutput);
621
622 m_safetyHelper->Feed();
623}
624
625/**
626 * Limit motor values to the -1.0 to +1.0 range.
627 */
628double RobotDrive::Limit(double num) {
629 if (num > 1.0) {
630 return 1.0;
631 }
632 if (num < -1.0) {
633 return -1.0;
634 }
635 return num;
636}
637
638/**
639 * Normalize all wheel speeds if the magnitude of any wheel is greater than 1.0.
640 */
641void RobotDrive::Normalize(double* wheelSpeeds) {
642 double maxMagnitude = std::fabs(wheelSpeeds[0]);
643 int i;
644 for (i = 1; i < kMaxNumberOfMotors; i++) {
645 double temp = std::fabs(wheelSpeeds[i]);
646 if (maxMagnitude < temp) maxMagnitude = temp;
647 }
648 if (maxMagnitude > 1.0) {
649 for (i = 0; i < kMaxNumberOfMotors; i++) {
650 wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude;
651 }
652 }
653}
654
655/**
656 * Rotate a vector in Cartesian space.
657 */
658void RobotDrive::RotateVector(double& x, double& y, double angle) {
659 double cosA = std::cos(angle * (3.14159 / 180.0));
660 double sinA = std::sin(angle * (3.14159 / 180.0));
661 double xOut = x * cosA - y * sinA;
662 double yOut = x * sinA + y * cosA;
663 x = xOut;
664 y = yOut;
665}
666
667/*
668 * Invert a motor direction.
669 *
670 * This is used when a motor should run in the opposite direction as the drive
671 * code would normally run it. Motors that are direct drive would be inverted,
672 * the Drive code assumes that the motors are geared with one reversal.
673 *
674 * @param motor The motor index to invert.
675 * @param isInverted True if the motor should be inverted when operated.
676 */
677void RobotDrive::SetInvertedMotor(MotorType motor, bool isInverted) {
678 if (motor < 0 || motor > 3) {
679 wpi_setWPIError(InvalidMotorIndex);
680 return;
681 }
682 switch (motor) {
683 case kFrontLeftMotor:
684 m_frontLeftMotor->SetInverted(isInverted);
685 break;
686 case kFrontRightMotor:
687 m_frontRightMotor->SetInverted(isInverted);
688 break;
689 case kRearLeftMotor:
690 m_rearLeftMotor->SetInverted(isInverted);
691 break;
692 case kRearRightMotor:
693 m_rearRightMotor->SetInverted(isInverted);
694 break;
695 }
696}
697
698/**
699 * Set the turning sensitivity.
700 *
701 * This only impacts the Drive() entry-point.
702 *
703 * @param sensitivity Effectively sets the turning sensitivity (or turn radius
704 * for a given value)
705 */
706void RobotDrive::SetSensitivity(double sensitivity) {
707 m_sensitivity = sensitivity;
708}
709
710/**
711 * Configure the scaling factor for using RobotDrive with motor controllers in a
712 * mode other than PercentVbus.
713 *
714 * @param maxOutput Multiplied with the output percentage computed by the drive
715 * functions.
716 */
717void RobotDrive::SetMaxOutput(double maxOutput) { m_maxOutput = maxOutput; }
718
719void RobotDrive::SetExpiration(double timeout) {
720 m_safetyHelper->SetExpiration(timeout);
721}
722
723double RobotDrive::GetExpiration() const {
724 return m_safetyHelper->GetExpiration();
725}
726
727bool RobotDrive::IsAlive() const { return m_safetyHelper->IsAlive(); }
728
729bool RobotDrive::IsSafetyEnabled() const {
730 return m_safetyHelper->IsSafetyEnabled();
731}
732
733void RobotDrive::SetSafetyEnabled(bool enabled) {
734 m_safetyHelper->SetSafetyEnabled(enabled);
735}
736
737void RobotDrive::GetDescription(std::ostringstream& desc) const {
738 desc << "RobotDrive";
739}
740
741void RobotDrive::StopMotor() {
742 if (m_frontLeftMotor != nullptr) m_frontLeftMotor->StopMotor();
743 if (m_frontRightMotor != nullptr) m_frontRightMotor->StopMotor();
744 if (m_rearLeftMotor != nullptr) m_rearLeftMotor->StopMotor();
745 if (m_rearRightMotor != nullptr) m_rearRightMotor->StopMotor();
746 m_safetyHelper->Feed();
747}