Our team decided to experiment with different drive wheels, so we wrote code to call the RobotDrive class’s different drive methods such as tankDrive and mecanumDrive. First we had mecanum wheels and it drove fine. Then we switched to regular wheels and change the code to call tankDrive instead. To our surprise, the right wheels were driving in opposite direction of the left wheels meaning when both left and right joysticks are pushed forward, the left wheels go forward but the right wheels go backward. Now, we do understand the left and the right wheel motors are mounted mirror imaged so we have set the right side of the wheels “inverted” with rightFrontMotor.setInverted(true) and rightRearMotor.setInverted(true). We did that and have code to spin each wheel independently to prove that all are driving “forward” when doing a xxxxMotor.set(0.5). As a result, mecanum drive is fine (actually more about that later). But when calling tankDrive, the robot just turns because the right wheels are spinning backwards. I finally downloaded the source code of WPILibJ and found the following code snippet.
public void setLeftRightMotorOutputs(double leftOutput, double rightOutput) {
if (m_rearLeftMotor == null || m_rearRightMotor == null) {
throw new NullPointerException("Null motor provided");
}
if (m_frontLeftMotor != null) {
m_frontLeftMotor.set(limit(leftOutput) * m_maxOutput, m_syncGroup);
}
m_rearLeftMotor.set(limit(leftOutput) * m_maxOutput, m_syncGroup);
if (m_frontRightMotor != null) {
m_frontRightMotor.set(-limit(rightOutput) * m_maxOutput, m_syncGroup); // <<<<<<
}
m_rearRightMotor.set(-limit(rightOutput) * m_maxOutput, m_syncGroup); // <<<<<<
if (this.m_syncGroup != 0) {
CANJaguar.updateSyncGroup(m_syncGroup);
}
if (m_safetyHelper != null)
m_safetyHelper.feed();
}
Notice the two right motors are negated (lines marked with <<<<<<).
I don’t understand why the code is doing that. Since the motor direction is already corrected by previous calls to setInverted (or you may also call the setInvertedMotor method, they are doing the same thing), the code should not do any more negation anywhere. In fact, upon examining all the methods in this class, I have concluded that it has a lot of unnecessary negations scattered around. For example, mecanumDrive has “negate Y for joystick” in the code but not for tankDrive and arcadeDrive. For arcadeDrive and tankDrive, the caller has to do the Y negation. I actually think it should not negate Y for joystick because this decision belongs to the caller and depending on what kind of joystick you use. Joysticks, which are designed for airplane, so pushing forward means the plane is going down and therefore gives you a negative value versus xbox gamepad, for example, will give you positive number when pushing forward. So RobotDrive should not be in the business of negating for me. Also, since pushing the joystick to the right will give you a positive number on the X-axis, you would expect a positive “rotateValue” on arcadeDrive would make the robot to turn right but NO, wpilib makes it to turn left. I suspect nobody complains about it because people would just stick with one type of drive (either arcade, tank or mecanum but not switching between them) and they would just call setMotorInverted appropriately or negate the x/y axes of the joystick to compensate for it. But in my opinion, WPILib is plainly wrong. And of course, we did compensate for it by wrapping the RobotDrive class with our own class and correcting all these inconsistencies. But I would really like somebody to review the WPI RobotDrive code and tell me if I read this correctly. Maybe I am missing something.
BTW, since FTC switches to java this year, we have ported the equivalent of RobotDrive code to FTC so our library is actually shared between FTC and FRC. Our port removed all the inconsistencies in RobotDrive.