Skip to content

Instantly share code, notes, and snippets.

@gcschmit
Created February 28, 2024 03:54
Show Gist options
  • Save gcschmit/6918edb630c8d26f893b0a6b3a3a734c to your computer and use it in GitHub Desktop.
Save gcschmit/6918edb630c8d26f893b0a6b3a3a734c to your computer and use it in GitHub Desktop.
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package frc.robot.subsystems.drivetrain;
import static frc.robot.subsystems.drivetrain.DrivetrainConstants.*;
import com.pathplanner.lib.auto.AutoBuilder;
import com.pathplanner.lib.util.HolonomicPathFollowerConfig;
import com.pathplanner.lib.util.PIDConstants;
import com.pathplanner.lib.util.ReplanningConfig;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.geometry.Twist2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
import edu.wpi.first.math.kinematics.SwerveModulePosition;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.DriverStation.Alliance;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.InstantCommand;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc.lib.team3061.gyro.GyroIO;
import frc.lib.team3061.gyro.GyroIOInputsAutoLogged;
import frc.lib.team3061.swerve.SwerveModule;
import frc.lib.team3061.util.RobotOdometry;
import frc.lib.team6328.util.TunableNumber;
import org.littletonrobotics.junction.Logger;
/**
* This subsystem models the robot's drivetrain mechanism. It consists of a four MK4 swerve modules,
* each with two motors and an encoder. It also consists of a Pigeon which is used to measure the
* robot's rotation.
*/
public class Drivetrain extends SubsystemBase {
private final GyroIO gyroIO;
private final GyroIOInputsAutoLogged gyroInputs = new GyroIOInputsAutoLogged();
private final TunableNumber autoDriveKp =
new TunableNumber("AutoDrive/DriveKp", AUTO_DRIVE_P_CONTROLLER);
private final TunableNumber autoDriveKi =
new TunableNumber("AutoDrive/DriveKi", AUTO_DRIVE_I_CONTROLLER);
private final TunableNumber autoDriveKd =
new TunableNumber("AutoDrive/DriveKd", AUTO_DRIVE_D_CONTROLLER);
private final TunableNumber autoTurnKp =
new TunableNumber("AutoDrive/TurnKp", AUTO_TURN_P_CONTROLLER);
private final TunableNumber autoTurnKi =
new TunableNumber("AutoDrive/TurnKi", AUTO_TURN_I_CONTROLLER);
private final TunableNumber autoTurnKd =
new TunableNumber("AutoDrive/TurnKd", AUTO_TURN_D_CONTROLLER);
private final PIDController autoXController =
new PIDController(autoDriveKp.get(), autoDriveKi.get(), autoDriveKd.get());
private final PIDController autoYController =
new PIDController(autoDriveKp.get(), autoDriveKi.get(), autoDriveKd.get());
private final PIDController autoThetaController =
new PIDController(autoTurnKp.get(), autoTurnKi.get(), autoTurnKd.get());
private final SwerveModule[] swerveModules = new SwerveModule[4]; // FL, FR, BL, BR
// some of this code is from the SDS example code
private Translation2d centerGravity;
private final SwerveModulePosition[] swerveModulePositions =
new SwerveModulePosition[] {
new SwerveModulePosition(),
new SwerveModulePosition(),
new SwerveModulePosition(),
new SwerveModulePosition()
};
private final SwerveModulePosition[] prevSwerveModulePositions =
new SwerveModulePosition[] {
new SwerveModulePosition(),
new SwerveModulePosition(),
new SwerveModulePosition(),
new SwerveModulePosition()
};
private Pose2d estimatedPoseWithoutGyro = new Pose2d();
private boolean isFieldRelative;
private double gyroOffset;
private ChassisSpeeds chassisSpeeds;
private static final String SUBSYSTEM_NAME = "Drivetrain";
private static final boolean TESTING = false;
private static final boolean DEBUGGING = false;
private final SwerveDrivePoseEstimator poseEstimator;
private boolean brakeMode;
private DriveMode driveMode = DriveMode.NORMAL;
private double characterizationVoltage = 0.0;
private DriverStation.Alliance alliance = DriverStation.Alliance.Blue;
/** Constructs a new DrivetrainSubsystem object. */
public Drivetrain(
GyroIO gyroIO,
SwerveModule flModule,
SwerveModule frModule,
SwerveModule blModule,
SwerveModule brModule) {
this.gyroIO = gyroIO;
this.swerveModules[0] = flModule;
this.swerveModules[1] = frModule;
this.swerveModules[2] = blModule;
this.swerveModules[3] = brModule;
this.autoThetaController.enableContinuousInput(-Math.PI, Math.PI);
this.centerGravity = new Translation2d(); // default to (0,0)
this.zeroGyroscope();
this.isFieldRelative = false;
this.gyroOffset = 0;
this.chassisSpeeds = new ChassisSpeeds(0.0, 0.0, 0.0);
this.poseEstimator = RobotOdometry.getInstance().getPoseEstimator();
ShuffleboardTab tabMain = Shuffleboard.getTab("MAIN");
tabMain.addNumber("Gyroscope Angle", () -> getRotation().getDegrees());
tabMain.addBoolean("X-Stance On?", this::isXstance);
tabMain.addBoolean("Field-Relative Enabled?", () -> this.isFieldRelative);
AutoBuilder.configureHolonomic(
this::getPose, // Robot pose supplier
this::resetPose, // Method to reset odometry (will be called if your auto has a starting
// pose)
this::getRobotRelativeSpeeds, // ChassisSpeeds supplier. MUST BE ROBOT RELATIVE
this::driveRobotRelative, // Method that will drive the robot given ROBOT RELATIVE
// ChassisSpeeds
new HolonomicPathFollowerConfig( // HolonomicPathFollowerConfig, this should likely live in
// your Constants class
new PIDConstants(
DrivetrainConstants.AUTO_DRIVE_P_CONTROLLER,
DrivetrainConstants.AUTO_DRIVE_I_CONTROLLER,
DrivetrainConstants.AUTO_DRIVE_D_CONTROLLER), // Translation PID constants
new PIDConstants(
DrivetrainConstants.AUTO_TURN_P_CONTROLLER,
DrivetrainConstants.AUTO_TURN_I_CONTROLLER,
DrivetrainConstants.AUTO_TURN_D_CONTROLLER), // Rotation PID constants
DrivetrainConstants.AUTO_MAX_SPEED_METERS_PER_SECOND, // Max module speed, in m/s
new Translation2d(
DrivetrainConstants.WHEELBASE_METERS,
DrivetrainConstants.TRACKWIDTH_METERS)
.getNorm(), // Drive base radius in meters. Distance from robot center to furthest
// module.
new ReplanningConfig() // Default path replanning config. See the API for the options
// here
),
this::shouldFlipAutoPath,
this // Reference to this subsystem to set requirements
);
if (DEBUGGING) {
ShuffleboardTab tab = Shuffleboard.getTab(SUBSYSTEM_NAME);
tab.add(SUBSYSTEM_NAME, this);
tab.addNumber("vx", this::getVelocityX);
tab.addNumber("vy", this::getVelocityY);
tab.addNumber("Pose Est X", () -> poseEstimator.getEstimatedPosition().getX());
tab.addNumber("Pose Est Y", () -> poseEstimator.getEstimatedPosition().getY());
tab.addNumber(
"Pose Est Rot", () -> poseEstimator.getEstimatedPosition().getRotation().getDegrees());
tab.addNumber("CoG X", () -> this.centerGravity.getX());
tab.addNumber("CoG Y", () -> this.centerGravity.getY());
}
if (TESTING) {
ShuffleboardTab tab = Shuffleboard.getTab(SUBSYSTEM_NAME);
tab.add("Enable XStance", new InstantCommand(this::enableXstance));
tab.add("Disable XStance", new InstantCommand(this::disableXstance));
}
}
public ChassisSpeeds getRobotRelativeSpeeds() {
return KINEMATICS.toChassisSpeeds(
swerveModules[0].getState(),
swerveModules[1].getState(),
swerveModules[2].getState(),
swerveModules[3].getState());
}
public void driveRobotRelative(ChassisSpeeds chassisSpeeds) {
SwerveModuleState[] swerveModuleStates =
KINEMATICS.toSwerveModuleStates(chassisSpeeds, centerGravity);
SwerveDriveKinematics.desaturateWheelSpeeds(
swerveModuleStates, MAX_VELOCITY_METERS_PER_SECOND);
for (SwerveModule swerveModule : swerveModules) {
swerveModule.setDesiredState(
swerveModuleStates[swerveModule.getModuleNumber()], false, false);
}
}
/**
* Zeroes the gyroscope. This sets the current rotation of the robot to zero degrees. This method
* is intended to be invoked only when the alignment beteween the robot's rotation and the gyro is
* sufficiently different to make field-relative driving difficult. The robot needs to be
* positioned facing away from the driver, ideally aligned to a field wall before this method is
* invoked.
*/
public void zeroGyroscope() {
setGyroOffset(0.0);
}
public void autoGyroscope() {
setGyroOffset(180);
}
/**
* Returns the rotation of the robot. Zero degrees is facing away from the driver station; CCW is
* positive. This method should always be invoked instead of obtaining the yaw directly from the
* Pigeon as the local offset needs to be added. If the gyro is not connected, the rotation from
* the estimated pose is returned.
*
* @return the rotation of the robot
*/
private Rotation2d getRotation() {
if (gyroInputs.connected) {
return Rotation2d.fromDegrees(gyroInputs.positionDeg + this.gyroOffset);
} else {
return estimatedPoseWithoutGyro.getRotation();
}
}
/**
* Sets the rotation of the robot to the specified value. This method should only be invoked when
* the rotation of the robot is known (e.g., at the start of an autonomous path). Zero degrees is
* facing away from the driver station; CCW is positive.
*
* @param expectedYaw the rotation of the robot (in degrees)
*/
public void setGyroOffset(double expectedYaw) {
// There is a delay between setting the yaw on the Pigeon and that change
// taking effect. As a result, it is recommended to never set the yaw and
// adjust the local offset instead.
if (gyroInputs.connected) {
this.gyroOffset = expectedYaw - gyroInputs.positionDeg;
} else {
this.gyroOffset = 0;
this.estimatedPoseWithoutGyro =
new Pose2d(
estimatedPoseWithoutGyro.getX(),
estimatedPoseWithoutGyro.getY(),
Rotation2d.fromDegrees(expectedYaw));
}
}
/**
* Returns the pose of the robot (e.g., x and y position of the robot on the field and the robot's
* rotation). The origin of the field to the lower left corner (i.e., the corner of the field to
* the driver's right). Zero degrees is away from the driver and increases in the CCW direction.
*
* @return the pose of the robot
*/
public Pose2d getPose() {
return poseEstimator.getEstimatedPosition();
}
/**
* Sets the odometry of the robot to the specified PathPlanner state. This method should only be
* invoked when the rotation of the robot is known (e.g., at the start of an autonomous path). The
* origin of the field to the lower left corner (i.e., the corner of the field to the driver's
* right). Zero degrees is away from the driver and increases in the CCW direction.
*
* @param state the specified PathPlanner state to which is set the odometry //*****************
* NEED TO CHECK ON THIS
*/
// public void resetOdometry(PathPlannerState state) {
public void resetPose(Pose2d pose) {
this.resetPose(pose);
} // setGyroOffset(state.holonomicRotation.getDegrees());
// for (int i = 0; i < 4; i++) {
// swerveModulePositions[i] = swerveModules[i].getPosition();
// }
// estimatedPoseWithoutGyro =
// new Pose2d(state.poseMeters.getTranslation(), state.holonomicRotation);
// poseEstimator.resetPosition(
// this.getRotation(),
// swerveModulePositions,
// new Pose2d(state.poseMeters.getTranslation(), state.holonomicRotation));
// }
/**
* Controls the drivetrain to move the robot with the desired velocities in the x, y, and
* rotational directions. The velocities may be specified from either the robot's frame of
* reference of the field's frame of reference. In the robot's frame of reference, the positive x
* direction is forward; the positive y direction, left; position rotation, CCW. In the field
* frame of reference, the origin of the field to the lower left corner (i.e., the corner of the
* field to the driver's right). Zero degrees is away from the driver and increases in the CCW
* direction.
*
* <p>If the drive mode is XSTANCE, the robot will ignore the specified velocities and turn the
* swerve modules into the x-stance orientation.
*
* <p>If the drive mode is CHARACTERIZATION, the robot will ignore the specified velocities and
* run the characterization routine.
*
* @param translationXSupplier the desired velocity in the x direction (m/s)
* @param translationYSupplier the desired velocity in the y direction (m/s)
* @param rotationSupplier the desired rotational velcoity (rad/s)
*/
public void drive(double xVelocity, double yVelocity, double rotationalVelocity) {
switch (driveMode) {
case NORMAL:
if (isFieldRelative) {
chassisSpeeds =
ChassisSpeeds.fromFieldRelativeSpeeds(
xVelocity, yVelocity, rotationalVelocity, getRotation());
} else {
chassisSpeeds = new ChassisSpeeds(xVelocity, yVelocity, rotationalVelocity);
}
Logger.recordOutput("Drivetrain/chassisSpeedVx", chassisSpeeds.vxMetersPerSecond);
Logger.recordOutput("Drivetrain/chassisSpeedVy", chassisSpeeds.vyMetersPerSecond);
Logger.recordOutput("Drivetrain/chassisSpeedVo", chassisSpeeds.omegaRadiansPerSecond);
SwerveModuleState[] swerveModuleStates =
KINEMATICS.toSwerveModuleStates(chassisSpeeds, centerGravity);
SwerveDriveKinematics.desaturateWheelSpeeds(
swerveModuleStates, MAX_VELOCITY_METERS_PER_SECOND);
for (SwerveModule swerveModule : swerveModules) {
swerveModule.setDesiredState(
swerveModuleStates[swerveModule.getModuleNumber()], true, false);
}
break;
case CHARACTERIZATION:
// In characterization mode, drive at the specified voltage (and turn to zero
// degrees)
for (SwerveModule swerveModule : swerveModules) {
swerveModule.setVoltageForCharacterization(characterizationVoltage);
}
break;
case X:
this.setXStance();
break;
}
}
/**
* Stops the motion of the robot. Since the motors are in break mode, the robot will stop soon
* after this method is invoked.
*/
public void stop() {
chassisSpeeds = new ChassisSpeeds(0.0, 0.0, 0.0);
SwerveModuleState[] states = KINEMATICS.toSwerveModuleStates(chassisSpeeds, centerGravity);
setSwerveModuleStates(states);
}
/**
* This method is invoked each iteration of the scheduler. Typically, when using a command-based
* model, subsystems don't override the periodic method. However, the drivetrain needs to
* continually update the odometry of the robot, update and log the gyro and swerve module inputs,
* update brake mode, and update the tunable values.
*/
@Override
public void periodic() {
// update and log gyro inputs
gyroIO.updateInputs(gyroInputs);
Logger.processInputs("Drive/Gyro", gyroInputs);
SmartDashboard.putNumber("pitch", gyroInputs.pitch);
// update and log the swerve moudles inputs
for (SwerveModule swerveModule : swerveModules) {
swerveModule.updateAndProcessInputs();
}
// update estimated poses
SwerveModuleState[] states = new SwerveModuleState[4];
for (int i = 0; i < 4; i++) {
states[i] = swerveModules[i].getState();
swerveModulePositions[i] = swerveModules[i].getPosition();
}
// if the gyro is not connected, use the swerve module positions to estimate the
// robot's
// rotation
if (!gyroInputs.connected) {
SwerveModulePosition[] moduleDeltas = new SwerveModulePosition[4];
for (int index = 0; index < moduleDeltas.length; index++) {
SwerveModulePosition current = swerveModulePositions[index];
SwerveModulePosition previous = prevSwerveModulePositions[index];
moduleDeltas[index] =
new SwerveModulePosition(
current.distanceMeters - previous.distanceMeters, current.angle);
previous.distanceMeters = current.distanceMeters;
}
Twist2d twist = KINEMATICS.toTwist2d(moduleDeltas);
estimatedPoseWithoutGyro = estimatedPoseWithoutGyro.exp(twist);
}
poseEstimator.updateWithTime(
Timer.getFPGATimestamp(), this.getRotation(), swerveModulePositions);
// update the brake mode based on the robot's velocity and state
// (enabled/disabled)
updateBrakeMode();
// update tunables
if (autoDriveKp.hasChanged() || autoDriveKi.hasChanged() || autoDriveKd.hasChanged()) {
autoXController.setPID(autoDriveKp.get(), autoDriveKi.get(), autoDriveKd.get());
autoYController.setPID(autoDriveKp.get(), autoDriveKi.get(), autoDriveKd.get());
}
if (autoTurnKp.hasChanged() || autoTurnKi.hasChanged() || autoTurnKd.hasChanged()) {
autoThetaController.setPID(autoTurnKp.get(), autoTurnKi.get(), autoTurnKd.get());
}
// log poses, 3D geometry, and swerve module states, gyro offset
Pose2d poseEstimatorPose = poseEstimator.getEstimatedPosition();
Logger.recordOutput("Odometry/RobotNoGyro", estimatedPoseWithoutGyro);
Logger.recordOutput("Odometry/Robot", poseEstimatorPose);
Logger.recordOutput("3DField", new Pose3d(poseEstimatorPose));
Logger.recordOutput("SwerveModuleStates", states);
Logger.recordOutput(SUBSYSTEM_NAME + "/gyroOffset", this.gyroOffset);
}
/**
* If the robot is enabled and brake mode is not enabled, enable it. If the robot is disabled, has
* stopped moving, and brake mode is enabled, disable it.
*/
private void updateBrakeMode() {
if (DriverStation.isEnabled() && !brakeMode) {
brakeMode = true;
setBrakeMode(true);
} else {
boolean stillMoving = false;
for (SwerveModule mod : swerveModules) {
if (Math.abs(mod.getState().speedMetersPerSecond) > MAX_COAST_VELOCITY_METERS_PER_SECOND) {
stillMoving = true;
}
}
if (brakeMode && !stillMoving) {
brakeMode = false;
setBrakeMode(false);
}
}
}
private void setBrakeMode(boolean enable) {
for (SwerveModule mod : swerveModules) {
mod.setAngleBrakeMode(enable);
mod.setDriveBrakeMode(enable);
}
}
public void setBrakeOn() {
for (SwerveModule mod : swerveModules) {
mod.setAngleBrakeMode(true);
mod.setDriveBrakeMode(true);
}
}
public void setBrakeOff() {
for (SwerveModule mod : swerveModules) {
mod.setAngleBrakeMode(false);
mod.setDriveBrakeMode(false);
}
}
/**
* Sets each of the swerve modules based on the specified corresponding swerve module state.
* Incorporates the configured feedforward when setting each swerve module. The order of the
* states in the array must be front left, front right, back left, back right.
*
* <p>This method is invoked by the FollowPath autonomous command.
*
* @param states the specified swerve module state for each swerve module
*/
public void setSwerveModuleStates(SwerveModuleState[] states) {
SwerveDriveKinematics.desaturateWheelSpeeds(states, MAX_VELOCITY_METERS_PER_SECOND);
for (SwerveModule swerveModule : swerveModules) {
swerveModule.setDesiredState(states[swerveModule.getModuleNumber()], false, false);
}
}
/**
* Returns true if field relative mode is enabled
*
* @return true if field relative mode is enabled
*/
public boolean getFieldRelative() {
return isFieldRelative;
}
/**
* Enables field-relative mode. When enabled, the joystick inputs specify the velocity of the
* robot in the frame of reference of the field.
*/
public void enableFieldRelative() {
this.isFieldRelative = true;
}
/**
* Disables field-relative mode. When disabled, the joystick inputs specify the velocity of the
* robot in the frame of reference of the robot.
*/
public void disableFieldRelative() {
this.isFieldRelative = false;
}
/**
* Sets the swerve modules in the x-stance orientation. In this orientation the wheels are aligned
* to make an 'X'. This makes it more difficult for other robots to push the robot, which is
* useful when shooting.
*/
public void setXStance() {
chassisSpeeds = new ChassisSpeeds(0.0, 0.0, 0.0);
SwerveModuleState[] states = KINEMATICS.toSwerveModuleStates(chassisSpeeds, centerGravity);
states[0].angle = new Rotation2d(Math.PI / 2 - Math.atan(TRACKWIDTH_METERS / WHEELBASE_METERS));
states[1].angle = new Rotation2d(Math.PI / 2 + Math.atan(TRACKWIDTH_METERS / WHEELBASE_METERS));
states[2].angle = new Rotation2d(Math.PI / 2 + Math.atan(TRACKWIDTH_METERS / WHEELBASE_METERS));
states[3].angle =
new Rotation2d(3.0 / 2.0 * Math.PI - Math.atan(TRACKWIDTH_METERS / WHEELBASE_METERS));
for (SwerveModule swerveModule : swerveModules) {
swerveModule.setDesiredState(states[swerveModule.getModuleNumber()], true, true);
}
}
/**
* Sets the robot's center of gravity about which it will rotate. The origin is at the center of
* the robot. The positive x direction is forward; the positive y direction, left.
*
* @param x the x coordinate of the robot's center of gravity (in meters)
* @param y the y coordinate of the robot's center of gravity (in meters)
*/
public void setCenterGrav(double x, double y) {
this.centerGravity = new Translation2d(x, y);
}
/** Resets the robot's center of gravity about which it will rotate to the center of the robot. */
public void resetCenterGrav() {
setCenterGrav(0.0, 0.0);
}
/**
* Returns the desired velocity of the drivetrain in the x direction (units of m/s)
*
* @return the desired velocity of the drivetrain in the x direction (units of m/s)
*/
public double getVelocityX() {
return chassisSpeeds.vxMetersPerSecond;
}
/**
* Returns the desired velocity of the drivetrain in the y direction (units of m/s)
*
* @return the desired velocity of the drivetrain in the y direction (units of m/s)
*/
public double getVelocityY() {
return chassisSpeeds.vyMetersPerSecond;
}
/**
* Puts the drivetrain into the x-stance orientation. In this orientation the wheels are aligned
* to make an 'X'. This makes it more difficult for other robots to push the robot, which is
* useful when shooting. The robot cannot be driven until x-stance is disabled.
*/
public void enableXstance() {
this.driveMode = DriveMode.X;
this.setXStance();
}
/** Disables the x-stance, allowing the robot to be driven. */
public void disableXstance() {
this.driveMode = DriveMode.NORMAL;
}
/**
* Returns true if the robot is in the x-stance orientation.
*
* @return true if the robot is in the x-stance orientation
*/
public boolean isXstance() {
return this.driveMode == DriveMode.X;
}
/**
* Returns the PID controller used to control the robot's x position during autonomous.
*
* @return the PID controller used to control the robot's x position during autonomous
*/
public PIDController getAutoXController() {
return autoXController;
}
/**
* Returns the PID controller used to control the robot's y position during autonomous.
*
* @return the PID controller used to control the robot's y position during autonomous
*/
public PIDController getAutoYController() {
return autoYController;
}
/**
* Returns the PID controller used to control the robot's rotation during autonomous.
*
* @return the PID controller used to control the robot's rotation during autonomous
*/
public PIDController getAutoThetaController() {
return autoThetaController;
}
/** Runs forwards at the commanded voltage. */
public void runCharacterizationVolts(double volts) {
driveMode = DriveMode.CHARACTERIZATION;
characterizationVoltage = volts;
// invoke drive which will set the characterization voltage to each module
drive(0, 0, 0);
}
/** Returns the average drive velocity in meters/sec. */
public double getCharacterizationVelocity() {
double driveVelocityAverage = 0.0;
for (SwerveModule swerveModule : swerveModules) {
driveVelocityAverage += swerveModule.getState().speedMetersPerSecond;
}
return driveVelocityAverage / 4.0;
}
private enum DriveMode {
NORMAL,
X,
CHARACTERIZATION
}
/**
* This method should be invoked once the alliance color is known. Refer to the RobotContainer's
* checkAllianceColor method for best practices on when to check the alliance's color. The
* alliance color is needed when running auto paths as those paths are always defined for
* blue-alliance robots and need to be flipped for red-alliance robots.
*
* @param newAlliance the new alliance color
*/
public void updateAlliance(DriverStation.Alliance newAlliance) {
this.alliance = newAlliance;
}
/**
* Returns true if the auto path, which is always defined for a blue alliance robot, should be
* flipped to the red alliance side of the field.
*
* @return true if the auto path should be flipped to the red alliance side of the field
*/
public boolean shouldFlipAutoPath() {
return this.alliance == Alliance.Red;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment