Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
700 lines (620 sloc) 24.1 KB
/*
* Titan Robotics Framework Library
* Copyright (c) 2015 Titan Robotics Club (http://www.titanrobotics.net)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package hallib;
import trclib.TrcDbgTrace;
import trclib.TrcUtil;
/**
* This class implements a robot drive base that supports 2-motor or 4-motor
* drive trains. It supports tank drive, arcade drive, mecanum drive and swerve
* drive. This is a port from the WPILib RobotDrive class and extended with
* addition features.
*/
public class HalRobotDrive
{
public static class MotorType
{
public final int value;
private static final int kFrontLeft_val = 0;
private static final int kFrontRight_val = 1;
private static final int kRearLeft_val = 2;
private static final int kRearRight_val = 3;
public static final MotorType kFrontLeft = new MotorType(kFrontLeft_val);
public static final MotorType kFrontRight = new MotorType(kFrontRight_val);
public static final MotorType kRearLeft = new MotorType(kRearLeft_val);
public static final MotorType kRearRight = new MotorType(kRearRight_val);
private MotorType(int value)
{
this.value = value;
} //MotorType
} //class MotorType
private static final String moduleName = "HalRobotDrive";
private static final boolean debugEnabled = false;
private TrcDbgTrace dbgTrace = null;
public static double kDefaultSensitivity = 0.5;
public static double kDefaultMaxOutput = 1.0;
private static double MOTOR_MAX_VALUE = 1.0;
private static double MOTOR_MIN_VALUE = -1.0;
private static int MAX_NUM_MOTORS = 4;
private double sensitivity;
private double maxOutput;
private int numMotors;
private HalMotorController frontLeftMotor;
private HalMotorController frontRightMotor;
private HalMotorController rearLeftMotor;
private HalMotorController rearRightMotor;
/**
* The method initializes this instance object and is called by different
* constructors.
*
* @param frontLeftMotor specifies the left front motor controller object.
* @param rearLeftMotor specifies the left rear motor controller object.
* @param frontRightMotor specifies the right front motor controller object.
* @param rearRightMotor specifies the right rear motor controller object.
*/
private void robotDriveInit(
HalMotorController frontLeftMotor,
HalMotorController rearLeftMotor,
HalMotorController frontRightMotor,
HalMotorController rearRightMotor)
{
if (debugEnabled)
{
dbgTrace = new TrcDbgTrace(
moduleName,
false,
TrcDbgTrace.TraceLevel.API,
TrcDbgTrace.MsgLevel.INFO);
}
sensitivity = kDefaultSensitivity;
maxOutput = kDefaultMaxOutput;
numMotors = 0;
this.frontLeftMotor = frontLeftMotor;
if (frontLeftMotor != null) numMotors++;
this.rearLeftMotor = rearLeftMotor;
if (rearLeftMotor != null) numMotors++;
this.frontRightMotor = frontRightMotor;
if (frontRightMotor != null) numMotors++;
this.rearRightMotor = rearRightMotor;
if (rearRightMotor != null) numMotors++;
stopMotor();
} //robotDriveInit
/**
* Constructor: Create an instance of the object with 4 motors.
*
* @param frontLeftMotor specifies the left front motor controller object.
* @param rearLeftMotor specifies the left rear motor controller object.
* @param frontRightMotor specifies the right front motor controller object.
* @param rearRightMotor specifies the right rear motor controller object.
*/
public HalRobotDrive(
HalMotorController frontLeftMotor,
HalMotorController rearLeftMotor,
HalMotorController frontRightMotor,
HalMotorController rearRightMotor)
{
if (frontLeftMotor == null || rearLeftMotor == null ||
frontRightMotor == null || rearRightMotor == null)
{
this.frontLeftMotor = this.rearLeftMotor
= this.frontRightMotor
= this.rearRightMotor = null;
throw new NullPointerException("Null motor provided");
}
robotDriveInit(frontLeftMotor, rearLeftMotor, frontRightMotor, rearRightMotor);
} //HalRobotDrive
/**
* Constructor: Create an instance of the object with 2 motors.
*
* @param leftMotor specifies the left motor controller object.
* @param rightMotor specifies the right motor controller object.
*/
public HalRobotDrive(HalMotorController leftMotor, HalMotorController rightMotor)
{
if (leftMotor == null || rightMotor == null)
{
this.rearLeftMotor = this.rearRightMotor = null;
throw new NullPointerException("Null motor provided");
}
robotDriveInit(null, leftMotor, null, rightMotor);
} //HalRobotDrive
/**
* This method drives the motors with the given magnitude and curve values.
*
* @param magnitude specifies the magnitude value.
* @param curve specifies the curve value.
* @param inverted specifies true to invert control (i.e. robot front becomes robot back).
*/
public void drive(double magnitude, double curve, boolean inverted)
{
final String funcName = "drive";
double leftOutput;
double rightOutput;
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"mag=%f,curve=%f,inverted=%s",
magnitude, curve, Boolean.toString(inverted));
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
if (curve < 0.0)
{
double value = Math.log(-curve);
double ratio = (value - sensitivity)/(value + sensitivity);
if (ratio == 0.0)
{
ratio = 0.0000000001;
}
leftOutput = magnitude/ratio;
rightOutput = magnitude;
}
else if (curve > 0.0)
{
double value = Math.log(curve);
double ratio = (value - sensitivity)/(value + sensitivity);
if (ratio == 0.0)
{
ratio = 0.0000000001;
}
leftOutput = magnitude;
rightOutput = magnitude/ratio;
}
else
{
leftOutput = magnitude;
rightOutput = magnitude;
}
tankDrive(leftOutput, rightOutput, inverted);
} //drive
/**
* This method drives the motors with the given magnitude and curve values.
*
* @param magnitude specifies the magnitude value.
* @param curve specifies the curve value.
*/
public void drive(double magnitude, double curve)
{
drive(magnitude, curve, false);
} //drive
/**
* This method stops all the motors.
*/
public void stopMotor()
{
final String funcName = "stopMotor";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API);
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
if (frontLeftMotor != null) frontLeftMotor.setPower(0.0);
if (frontRightMotor != null) frontRightMotor.setPower(0.0);
if (rearLeftMotor != null) rearLeftMotor.setPower(0.0);
if (rearRightMotor != null) rearRightMotor.setPower(0.0);
} //stopMotor
/**
* This method sets the sensitivity for the drive() method.
*
* @param sensitivity specifies the sensitivity value.
*/
public void setSensitivity(double sensitivity)
{
final String funcName = "setSensitivity";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"sensitivity=%f", sensitivity);
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
this.sensitivity = sensitivity;
} //setSensitivity
/**
* This method sets the maximum output value of the motor.
*
* @param maxOutput specifies the maximum output value.
*/
public void setMaxOutput(double maxOutput)
{
final String funcName = "setMaxOutput";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API, "maxOutput=%f", maxOutput);
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
this.maxOutput = maxOutput;
} //setMaxOutput
/**
* This method returns the number of motors in the drive train.
*
* @return number of motors.
*/
public int getNumMotors()
{
final String funcName = "getNumMotors";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API);
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API, "=%d", numMotors);
}
return numMotors;
} //getNumMotors
/**
* This method inverts direction of a given motor in the drive train.
*
* @param motorType specifies the motor in the drive train.
* @param isInverted specifies true if inverting motor direction.
*/
public void setInvertedMotor(MotorType motorType, boolean isInverted)
{
final String funcName = "setInvertedMotor";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"type=%s,inverted=%s",
motorType.toString(), Boolean.toString(isInverted));
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
switch (motorType.value)
{
case MotorType.kFrontLeft_val:
if (frontLeftMotor != null)
{
frontLeftMotor.setInverted(isInverted);
}
break;
case MotorType.kFrontRight_val:
if (frontRightMotor != null)
{
frontRightMotor.setInverted(isInverted);
}
break;
case MotorType.kRearLeft_val:
if (rearLeftMotor != null)
{
rearLeftMotor.setInverted(isInverted);
}
break;
case MotorType.kRearRight_val:
if (rearRightMotor != null)
{
rearRightMotor.setInverted(isInverted);
}
break;
}
} //setInvertedMotor
/**
* This method implements tank drive where leftPower controls the left motors
* and right power controls the right motors.
*
* @param leftPower specifies left power value.
* @param rightPower specifies right power value.
* @param inverted specifies true to invert control (i.e. robot front becomes robot back).
*/
public void tankDrive(double leftPower, double rightPower, boolean inverted)
{
final String funcName = "tankDrive";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"leftPower=%f,rightPower=%f,inverted=%s",
leftPower, rightPower, Boolean.toString(inverted));
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
leftPower = TrcUtil.limit(leftPower);
rightPower = TrcUtil.limit(rightPower);
if (inverted)
{
double swap = leftPower;
leftPower = -rightPower;
rightPower = -swap;
}
if (frontLeftMotor != null)
{
frontLeftMotor.setPower(leftPower);
}
if (frontRightMotor != null)
{
frontRightMotor.setPower(rightPower);
}
if (rearLeftMotor != null)
{
rearLeftMotor.setPower(leftPower);
}
if (rearRightMotor != null)
{
rearRightMotor.setPower(rightPower);
}
} //tankDrive
/**
* This method implements tank drive where leftPower controls the left motors
* and right power controls the right motors.
*
* @param leftPower specifies left power value.
* @param rightPower specifies right power value.
*/
public void tankDrive(double leftPower, double rightPower)
{
tankDrive(leftPower, rightPower, false);
} //tankDrive
/**
* This method implements arcade drive where drivePower controls how fast
* the robot goes in the y-axis and turnPower controls how fast it will
* turn.
*
* @param drivePower specifies the drive power value.
* @param turnPower specifies the turn power value.
* @param inverted specifies true to invert control (i.e. robot front becomes robot back).
*/
public void arcadeDrive(double drivePower, double turnPower, boolean inverted)
{
final String funcName = "arcadeDrive";
double leftPower;
double rightPower;
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"drivePower=%f,turnPower=%f,inverted=%s",
drivePower, turnPower, Boolean.toString(inverted));
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
drivePower = TrcUtil.limit(drivePower);
turnPower = TrcUtil.limit(turnPower);
if (drivePower + turnPower > MOTOR_MAX_VALUE)
{
//
// Forward right:
// left = drive + turn - (drive + turn - MOTOR_MAX_VALUE)
// right = drive - turn - (drive + turn - MOTOR_MAX_VALUE)
//
leftPower = MOTOR_MAX_VALUE;
rightPower = -2*turnPower + MOTOR_MAX_VALUE;
}
else if (drivePower - turnPower > MOTOR_MAX_VALUE)
{
//
// Forward left:
// left = drive + turn - (drive - turn - MOTOR_MAX_VALUE)
// right = drive - turn - (drive - turn - MOTOR_MAX_VALUE)
//
leftPower = 2*turnPower + MOTOR_MAX_VALUE;
rightPower = MOTOR_MAX_VALUE;
}
else if (drivePower + turnPower < MOTOR_MIN_VALUE)
{
//
// Backward left:
// left = drive + turn - (drive + turn - MOTOR_MIN_VALUE)
// right = drive - turn - (drive + turn - MOTOR_MIN_VALUE)
//
leftPower = MOTOR_MIN_VALUE;
rightPower = -2*turnPower + MOTOR_MIN_VALUE;
}
else if (drivePower - turnPower < MOTOR_MIN_VALUE)
{
//
// Backward right:
// left = drive + turn - (drive - turn - MOTOR_MIN_VALUE)
// right = drive - turn - (drive - turn - MOTOR_MIN_VALUE)
//
leftPower = 2*turnPower + MOTOR_MIN_VALUE;
rightPower = MOTOR_MIN_VALUE;
}
else
{
leftPower = drivePower + turnPower;
rightPower = drivePower - turnPower;
}
tankDrive(leftPower, rightPower, inverted);
} //arcadeDrive
/**
* This method implements arcade drive where drivePower controls how fast
* the robot goes in the y-axis and turnPower controls how fast it will
* turn.
*
* @param drivePower specifies the drive power value.
* @param turnPower specifies the turn power value.
*/
public void arcadeDrive(double drivePower, double turnPower)
{
arcadeDrive(drivePower, turnPower, false);
} //arcadeDrive
/**
* This method implements mecanum drive where x controls how fast the robot will
* go in the x direction, and y controls how fast the robot will go in the y direction.
* Rotation controls how fast the robot rotates and gyroAngle specifies the heading
* the robot should maintain.
* @param x specifies the x power.
* @param y specifies the y power.
* @param rotation specifies the rotating power.
* @param inverted specifies true to invert control (i.e. robot front becomes robot back).
* @param gyroAngle specifies the gyro angle to maintain.
*/
public void mecanumDrive_Cartesian(double x, double y, double rotation,
boolean inverted, double gyroAngle)
{
final String funcName = "mecanumDrive_Cartesian";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"x=%f,y=%f,rot=%f,inverted=%s,angle=%f",
x, y, rotation, Boolean.toString(inverted), gyroAngle);
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
if (numMotors != MAX_NUM_MOTORS)
{
throw new IllegalArgumentException("Mecanum drive requires 4 motors");
}
x = TrcUtil.limit(x);
y = TrcUtil.limit(y);
rotation = TrcUtil.limit(rotation);
if (inverted)
{
x = -x;
y = -y;
}
double cosA = Math.cos(Math.toRadians(gyroAngle));
double sinA = Math.sin(Math.toRadians(gyroAngle));
x = x*cosA - y*sinA;
y = x*sinA + y*cosA;
double wheelSpeeds[] = new double[MAX_NUM_MOTORS];
wheelSpeeds[MotorType.kFrontLeft_val] = x + y + rotation;
wheelSpeeds[MotorType.kFrontRight_val] = -x + y - rotation;
wheelSpeeds[MotorType.kRearLeft_val] = -x + y + rotation;
wheelSpeeds[MotorType.kRearRight_val] = x + y - rotation;
normalize(wheelSpeeds);
if (frontLeftMotor != null)
{
frontLeftMotor.setPower(wheelSpeeds[MotorType.kFrontLeft_val]);
}
if (frontRightMotor != null)
{
frontRightMotor.setPower(wheelSpeeds[MotorType.kFrontRight_val]);
}
if (rearLeftMotor != null)
{
rearLeftMotor.setPower(wheelSpeeds[MotorType.kRearLeft_val]);
}
if (rearRightMotor != null)
{
rearRightMotor.setPower(wheelSpeeds[MotorType.kRearRight_val]);
}
} //mecanumDrive_Cartesian
/**
* This method implements mecanum drive where x controls how fast the robot will
* go in the x direction, and y controls how fast the robot will go in the y direction.
* Rotation controls how fast the robot rotates and gyroAngle specifies the heading
* the robot should maintain.
* @param x specifies the x power.
* @param y specifies the y power.
* @param rotation specifies the rotating power.
* @param inverted specifies true to invert control (i.e. robot front becomes robot back).
*/
public void mecanumDrive_Cartesian(double x, double y, double rotation, boolean inverted)
{
mecanumDrive_Cartesian(x, y, rotation, inverted, 0.0);
} //mecanumDrive_Cartesian
/**
* This method implements mecanum drive where x controls how fast the robot will
* go in the x direction, and y controls how fast the robot will go in the y direction.
* Rotation controls how fast the robot rotates.
*
* @param x specifies the x power.
* @param y specifies the y power.
* @param rotation specifies the rotating power.
*/
public void mecanumDrive_Cartesian(double x, double y, double rotation)
{
mecanumDrive_Cartesian(x, y, rotation, false, 0.0);
} //mecanumDrive_Cartesian
/**
* This method implements mecanum drive where magnitude controls how fast the robot
* will go in the given direction and how fast it will robote.
*
* @param magnitude specifies the magnitude combining x and y axes.
* @param direction specifies the direction in degrees.
* @param rotation specifies the rotation power.
* @param inverted specifies true to invert control (i.e. robot front becomes robot back).
*/
public void mecanumDrive_Polar(double magnitude, double direction, double rotation,
boolean inverted)
{
final String funcName = "mecanumDrive_Polar";
if (debugEnabled)
{
dbgTrace.traceEnter(funcName, TrcDbgTrace.TraceLevel.API,
"mag=%f,dir=%f,rot=%f,inverted=%s",
magnitude, direction, rotation, Boolean.toString(inverted));
dbgTrace.traceExit(funcName, TrcDbgTrace.TraceLevel.API);
}
if (numMotors != MAX_NUM_MOTORS)
{
throw new IllegalArgumentException("Mecanum drive requires 4 motors");
}
magnitude = TrcUtil.limit(magnitude)*Math.sqrt(2.0);
if (inverted)
{
direction += 180.0;
direction %= 360.0;
}
double dirInRad = Math.toRadians(direction + 45.0);
double cosD = Math.cos(dirInRad);
double sinD = Math.sin(dirInRad);
double wheelSpeeds[] = new double[MAX_NUM_MOTORS];
wheelSpeeds[MotorType.kFrontLeft_val] = (sinD*magnitude + rotation);
wheelSpeeds[MotorType.kFrontRight_val] = (cosD*magnitude - rotation);
wheelSpeeds[MotorType.kRearLeft_val] = (cosD*magnitude + rotation);
wheelSpeeds[MotorType.kRearRight_val] = (sinD*magnitude - rotation);
normalize(wheelSpeeds);
if (frontLeftMotor != null)
{
frontLeftMotor.setPower(wheelSpeeds[MotorType.kFrontLeft_val]);
}
if (frontRightMotor != null)
{
frontRightMotor.setPower(wheelSpeeds[MotorType.kFrontRight_val]);
}
if (rearLeftMotor != null)
{
rearLeftMotor.setPower(wheelSpeeds[MotorType.kRearLeft_val]);
}
if (rearRightMotor != null)
{
rearRightMotor.setPower(wheelSpeeds[MotorType.kRearRight_val]);
}
} //mecanumDrive_Polar
/**
* This method implements mecanum drive where magnitude controls how fast the robot
* will go in the given direction and how fast it will robote.
*
* @param magnitude specifies the magnitude combining x and y axes.
* @param direction specifies the direction in degrees.
* @param rotation specifies the rotation power.
*/
public void mecanumDrive_Polar(double magnitude, double direction, double rotation)
{
mecanumDrive_Polar(magnitude, direction, rotation, false);
} //mecanumDrive_Polar
/**
* This method normalizes the power to the four wheels for mecanum drive.
*
* @param wheelSpeeds specifies the wheel speed of all four wheels.
*/
private void normalize(double[] wheelSpeeds)
{
double maxMagnitude = Math.abs(wheelSpeeds[0]);
for (int i = 1; i < wheelSpeeds.length; i++)
{
double magnitude = Math.abs(wheelSpeeds[i]);
if (magnitude > maxMagnitude)
{
maxMagnitude = magnitude;
}
}
if (maxMagnitude > 1.0)
{
for (int i = 0; i < wheelSpeeds.length; i++)
{
wheelSpeeds[i] /= maxMagnitude;
}
}
} //normalize
} //HalRobotDrive