What is Field Centric Drive?
If you imagine driving a robot normally, as long as the robot is facing forward relative to you, it drives normally. But if you turn the robot, instead of going forward relative to you, it goes forward relative to the robot. This can be bad since during comp it's difficult to keep track of the robot heading and everything else going on around you.
FCD (Field Centric Drive) makes it so the driver has one less thing to worry about during comp and makes it easier to drive. The controls always correspond to the field orientation, not the robot orientation.
The Code
To use the FCD code, create a new .java file named FieldCentricDriveNavX and paste the following:
package org.firstinspires.ftc.teamcode;
import com.qualcomm.hardware.kauailabs.NavxMicroNavigationSensor;
import com.qualcomm.robotcore.eventloop.opmode.OpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.DcMotorEx;
import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder;
import org.firstinspires.ftc.robotcore.external.navigation.AxesReference;
import static com.qualcomm.robotcore.hardware.DcMotorSimple.Direction.REVERSE;
@TeleOp(name="FieldCentricDriveNavX")
public class FieldCentricDriveNavX extends OpMode {
// NavX variable — receives data about robot direction
NavxMicroNavigationSensor navx;
// Motors
DcMotorEx frontLeft;
DcMotorEx backLeft;
DcMotorEx frontRight;
DcMotorEx backRight;
// Joystick variables
double lJoyY;
double lJoyX;
double rJoyX;
// Joystick direction using atan2. Not strictly needed
// but nice for debugging
public static double joyDir(double joyX, double joyY) {
return Math.atan2(joyY, joyX);
}
// Runs once when init is pressed
public void init() {
// Setting config names
navx = hardwareMap.get(
NavxMicroNavigationSensor.class, "imu");
frontLeft = hardwareMap.get(
DcMotorEx.class, "LeftFront");
backLeft = hardwareMap.get(
DcMotorEx.class, "LeftBack");
backRight = hardwareMap.get(
DcMotorEx.class, "RightBack");
frontRight = hardwareMap.get(
DcMotorEx.class, "RightFront");
// Reverse motors so robot actually moves forward
frontRight.setDirection(REVERSE);
backRight.setDirection(REVERSE);
// Brake when no power applied (prevents drifting)
frontRight.setZeroPowerBehavior(
DcMotor.ZeroPowerBehavior.BRAKE);
frontLeft.setZeroPowerBehavior(
DcMotor.ZeroPowerBehavior.BRAKE);
backRight.setZeroPowerBehavior(
DcMotor.ZeroPowerBehavior.BRAKE);
backLeft.setZeroPowerBehavior(
DcMotor.ZeroPowerBehavior.BRAKE);
}
public void loop() {
// Get yaw (rotation) in radians
double yaw = navx.getAngularOrientation(
AxesReference.INTRINSIC,
AxesOrder.XYZ,
AngleUnit.RADIANS
).thirdAngle;
// Read joystick values
lJoyY = gamepad1.left_stick_y;
lJoyX = gamepad1.left_stick_x;
rJoyX = gamepad1.right_stick_x;
// Counteract robot rotation with math
double robotRotY =
lJoyX * Math.sin(-yaw)
+ lJoyY * Math.cos(-yaw);
double robotRotX =
-lJoyY * Math.sin(-yaw)
+ lJoyX * Math.cos(-yaw);
// Keep power ratios even if values exceed -1 to 1
double motorPower = Math.max(
Math.abs(robotRotY)
+ Math.abs(robotRotX)
+ Math.abs(rJoyX), 1);
// Set motor powers
frontLeft.setPower(
(robotRotY + robotRotX + rJoyX) / motorPower);
backLeft.setPower(
(robotRotY - robotRotX + rJoyX) / motorPower);
frontRight.setPower(
(robotRotY - robotRotX - rJoyX) / motorPower);
backRight.setPower(
(robotRotY + robotRotX - rJoyX) / motorPower);
// Telemetry for debugging
telemetry.addData("Joy Dir: ",
Math.toDegrees(joyDir(lJoyY, lJoyX)));
telemetry.addData("Robot Dir: ",
Math.toDegrees(yaw));
telemetry.addData("Joystick Y: ", lJoyY);
telemetry.addData("Joystick X: ", lJoyX);
telemetry.addData("---", "---");
telemetry.addData("Front Left: ",
(robotRotY + robotRotX + rJoyX) / motorPower);
telemetry.addData("Front Right: ",
(robotRotY - robotRotX - rJoyX) / motorPower);
telemetry.addData("Back Left: ",
(robotRotY - robotRotX + rJoyX) / motorPower);
telemetry.addData("Back Right: ",
(robotRotY + robotRotX - rJoyX) / motorPower);
telemetry.update();
}
}