Files
2025RidgeScape/src/main/java/frc4388/robot/subsystems/SwerveDrive.java
T

419 lines
17 KiB
Java
Raw Normal View History

2025-01-04 16:09:10 -07:00
// 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 frc4388.robot.subsystems;
import java.util.Optional;
2025-01-16 19:41:08 -07:00
import java.util.function.DoubleSupplier;
import com.ctre.phoenix6.Utils;
2025-02-01 15:52:06 -07:00
import com.ctre.phoenix6.configs.TalonFXConfiguration;
2025-01-06 21:34:40 -07:00
import com.ctre.phoenix6.hardware.CANcoder;
import com.ctre.phoenix6.hardware.TalonFX;
import com.ctre.phoenix6.swerve.SwerveDrivetrain;
2025-02-01 15:52:06 -07:00
import com.ctre.phoenix6.swerve.SwerveModule;
2025-01-06 21:34:40 -07:00
import com.ctre.phoenix6.swerve.SwerveRequest;
2025-01-30 19:28:46 -07:00
import com.ctre.phoenix6.swerve.SwerveRequest.ForwardPerspectiveValue;
2025-01-06 21:34:40 -07:00
import edu.wpi.first.math.geometry.Pose2d;
2025-01-04 16:09:10 -07:00
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.SwerveModuleState;
2025-01-05 11:25:37 -07:00
import edu.wpi.first.networktables.GenericEntry;
import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts;
import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets;
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout;
import edu.wpi.first.wpilibj.smartdashboard.Field2d;
2025-01-04 16:09:10 -07:00
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
// import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.Constants.VisionConstants;
2025-02-04 19:44:24 -07:00
import frc4388.robot.commands.GotoLastApril;
import frc4388.robot.commands.LidarAlign;
2025-01-04 16:09:10 -07:00
import frc4388.utility.Status;
import frc4388.utility.Subsystem;
2025-02-24 17:59:44 -07:00
import frc4388.utility.TimesNegativeOne;
2025-01-04 16:09:10 -07:00
import frc4388.utility.Status.ReportLevel;
2025-01-15 16:31:53 -07:00
import com.pathplanner.lib.controllers.PPHolonomicDriveController;
import com.pathplanner.lib.config.PIDConstants;
import com.pathplanner.lib.auto.AutoBuilder;
import com.pathplanner.lib.commands.PathPlannerAuto;
import com.pathplanner.lib.config.RobotConfig;
2025-01-04 16:09:10 -07:00
public class SwerveDrive extends Subsystem {
2025-01-15 16:31:53 -07:00
private SwerveDrivetrain<TalonFX, TalonFX, CANcoder> swerveDriveTrain;
2025-01-09 12:57:04 -07:00
2025-01-15 16:31:53 -07:00
private Vision vision;
2025-01-18 11:36:26 -07:00
2025-01-15 16:31:53 -07:00
private int gear_index = SwerveDriveConstants.STARTING_GEAR;
private boolean stopped = false;
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public double speedAdjust = SwerveDriveConstants.MAX_SPEED_MEETERS_PER_SEC * SwerveDriveConstants.GEARS[gear_index];
public double rotSpeedAdjust = SwerveDriveConstants.MAX_ROT_SPEED;
2025-01-18 11:36:26 -07:00
public double autoSpeedAdjust = SwerveDriveConstants.MAX_SPEED_MEETERS_PER_SEC * 0.25; // cap auto performance to
// 25%
2025-01-23 19:32:12 -07:00
public Pose2d initalPose2d = null;
2025-01-15 16:31:53 -07:00
public double rotTarget = 0.0;
public Rotation2d orientRotTarget = new Rotation2d();
public ChassisSpeeds chassisSpeeds = new ChassisSpeeds();
/** Creates a new SwerveDrive. */
public SwerveDrive(SwerveDrivetrain<TalonFX, TalonFX, CANcoder> swerveDriveTrain, Vision vision) {
2025-01-18 11:36:26 -07:00
// public SwerveDrive(SwerveDrivetrain<TalonFX, TalonFX, CANcoder>
// swerveDriveTrain) {
super();
this.swerveDriveTrain = swerveDriveTrain;
this.vision = vision;
RobotConfig config;
try {
config = RobotConfig.fromGUISettings();
} catch (Exception e) {
// Handle exception as needed
config = null;
}
2025-01-16 19:41:08 -07:00
// DoubleSupplier a = () -> 1.d;
2025-01-15 16:31:53 -07:00
AutoBuilder.configure(
2025-01-18 11:36:26 -07:00
() -> {
2025-01-23 19:32:12 -07:00
return swerveDriveTrain.samplePoseAt(Utils.getCurrentTimeSeconds()).orElse(initalPose2d);
2025-01-18 11:36:26 -07:00
}, // Robot pose supplier
2025-01-23 19:32:12 -07:00
this::setOdoPose, // Method to reset odometry (will be called if your auto has a starting
2025-01-18 11:36:26 -07:00
// pose)
() -> swerveDriveTrain.getState().Speeds, // ChassisSpeeds supplier. MUST BE ROBOT RELATIVE
(speeds, feedforwards) -> swerveDriveTrain.setControl(new SwerveRequest.ApplyRobotSpeeds()
.withSpeeds(speeds)), // Method that will drive the robot given ROBOT RELATIVE ChassisSpeeds.
// Also optionally outputs individual module feedforwards
new PPHolonomicDriveController( // PPHolonomicController is the built in path following controller for
// holonomic drive trains
new PIDConstants(5.0, 0.0, 0.0), // Translation PID constants
new PIDConstants(5.0, 0.0, 0.0) // Rotation PID constants
),
config, // The robot configuration
() -> {
// Boolean supplier that controls when the path will be mirrored for the red
// alliance
// This will flip the path being followed to the red side of the field.
// THE ORIGIN WILL REMAIN ON THE BLUE SIDE
2025-02-24 17:59:44 -07:00
// var alliance = DriverStation.getAlliance();
// if (alliance.isPresent()) {
// return alliance.get() == DriverStation.Alliance.Red;
// }
return TimesNegativeOne.isRed;
2025-01-18 11:36:26 -07:00
},
this // Reference to this subsystem to set requirements
);
}
2025-01-23 19:32:12 -07:00
public void setOdoPose(Pose2d pose) {
initalPose2d = pose;
swerveDriveTrain.resetPose(pose);
}
2025-01-18 11:36:26 -07:00
// public void oneModuleTest(SwerveModule module, Translation2d leftStick,
// Translation2d rightStick){
// // double ang = Math.atan2(rightStick.getY(), rightStick.getX());
// // rightStick.getAngle()
// double speed = Math.sqrt(Math.pow(leftStick.getX(), 2) +
// Math.pow(leftStick.getY(), 2));
// // System.out.println(ang);
// // module.go(ang);
// // Rotation2d rot = Rotation2d.fromRadians(ang);
// Rotation2d rot = new Rotation2d(rightStick.getX(), rightStick.getY());
// SwerveModuleState state = new SwerveModuleState(speed, rot);
// module.setDesiredState(state);
2025-01-15 16:31:53 -07:00
// }
public void driveWithInput(Translation2d leftStick, Translation2d rightStick, boolean fieldRelative) {
2025-01-18 11:36:26 -07:00
if (rightStick.getNorm() < 0.05 && leftStick.getNorm() < 0.05 && stopped == false) // if no imput and the swerve drive is still going:
stopModules(); // stop the swerve
if (rightStick.getNorm() < 0.05 && leftStick.getNorm() < 0.05) // if no imput
return; // don't bother doing swerve drive math and return early.
leftStick = leftStick.rotateBy(Rotation2d.fromDegrees(SwerveDriveConstants.FORWARD_OFFSET));
2025-02-24 17:59:44 -07:00
leftStick = TimesNegativeOne.invert(leftStick, TimesNegativeOne.XAxis, TimesNegativeOne.YAxis);
leftStick = TimesNegativeOne.invert(rightStick, TimesNegativeOne.RotAxis);
2025-02-24 17:10:12 -07:00
2025-01-18 11:36:26 -07:00
stopped = false;
if (fieldRelative) {
// ! drift correction
if (rightStick.getNorm() > 0.05 || !SwerveDriveConstants.DRIFT_CORRECTION_ENABLED) {
rotTarget = swerveDriveTrain.samplePoseAt(Utils.getCurrentTimeSeconds()).orElse(new Pose2d()).getRotation().getDegrees();
swerveDriveTrain.setControl(new SwerveRequest.FieldCentric()
.withVelocityX(leftStick.getX() * speedAdjust)
.withVelocityY(leftStick.getY() * speedAdjust)
.withRotationalRate(rightStick.getX() * rotSpeedAdjust));
2025-01-30 19:28:46 -07:00
// .withForwardPerspective(ForwardPerspectiveValue.OperatorPerspective));
2025-01-18 11:36:26 -07:00
SmartDashboard.putBoolean("drift correction", false);
} else {
var ctrl = new SwerveRequest.FieldCentricFacingAngle()
.withVelocityX(leftStick.getX() * speedAdjust)
.withVelocityY(leftStick.getY() * speedAdjust)
.withTargetDirection(Rotation2d.fromDegrees(rotTarget));
ctrl.HeadingController.setPID(
SwerveDriveConstants.PIDConstants.DRIFT_CORRECTION_GAINS.kP,
SwerveDriveConstants.PIDConstants.DRIFT_CORRECTION_GAINS.kI,
SwerveDriveConstants.PIDConstants.DRIFT_CORRECTION_GAINS.kD
);
swerveDriveTrain.setControl(ctrl);
SmartDashboard.putBoolean("drift correction", true);
}
// // SmartDashboard.putBoolean("drift correction", true);
// // rot = ((rotTarget - gyro.getAngle()) / 360) *
// SwerveDriveConstants.ROT_CORRECTION_SPEED;
// }
// // Use the left joystick to set speed. Apply a cubic curve and the set max
// speed.
// Translation2d speed = leftStick.times(leftStick.getNorm() * speedAdjust);
// // Translation2d cubedSpeed = new Translation2d(Math.pow(speed.getX(), 3.00),
// Math.pow(speed.getY(), 3.00));
// // Convert field-relative speeds to robot-relative speeds.
// // chassisSpeeds = chassisSpeeds.
// chassisSpeeds = ChassisSpeeds.fromFieldRelativeSpeeds(-1 * speed.getX(), -1 *
// speed.getY(), (-1 * rightStick.getX() * rotSpeedAdjust) - rot_correction,
// gyro.getRotation2d().times(-1));
} else { // Create robot-relative speeds.
swerveDriveTrain.setControl(new SwerveRequest.RobotCentric()
.withVelocityX(leftStick.getX() * speedAdjust)
.withVelocityY(-leftStick.getY() * speedAdjust)
.withRotationalRate(rightStick.getX() * rotSpeedAdjust));
}
2025-01-04 16:09:10 -07:00
}
2025-01-18 11:36:26 -07:00
public void driveWithInputOrientation(Translation2d leftStick, Translation2d rightStick) { // there is no practical
// reason to have a robot
// relitive version of
// this, and no pre
// provided version
if (rightStick.getNorm() < 0.05 && leftStick.getNorm() < 0.05 && stopped == false) // if no imput and the swerve
// drive is still going:
stopModules(); // stop the swerve
if (rightStick.getNorm() < 0.05 && leftStick.getNorm() < 0.05) // if no imput
return; // don't bother doing swerve drive math and return early.
leftStick.rotateBy(Rotation2d.fromDegrees(SwerveDriveConstants.FORWARD_OFFSET));
swerveDriveTrain.setControl(new SwerveRequest.FieldCentricFacingAngle()
.withVelocityX(leftStick.getX() * speedAdjust)
.withVelocityY(leftStick.getY() * speedAdjust)
.withTargetDirection(rightStick.getAngle()));
2025-01-04 16:09:10 -07:00
}
2025-01-29 17:06:55 -07:00
public void driveRelativeLockedAngle(Translation2d leftStick, Rotation2d heading) {
leftStick = leftStick.rotateBy(heading);
var ctrl = new SwerveRequest.FieldCentricFacingAngle()
.withVelocityX(leftStick.getX() * speedAdjust)
.withVelocityY(leftStick.getY() * speedAdjust)
.withTargetDirection(Rotation2d.fromDegrees(rotTarget));
ctrl.HeadingController.setPID(
SwerveDriveConstants.PIDConstants.RELATIVE_LOCKED_ANGLE_GAINS.kP,
SwerveDriveConstants.PIDConstants.RELATIVE_LOCKED_ANGLE_GAINS.kI,
SwerveDriveConstants.PIDConstants.RELATIVE_LOCKED_ANGLE_GAINS.kD
);
swerveDriveTrain.setControl(ctrl);
}
2025-02-01 15:52:06 -07:00
public void setLimits(double limitInAmps) {
for (SwerveModule<TalonFX, TalonFX, CANcoder> module : swerveDriveTrain.getModules()) {
var talonFXConfigurator = module.getDriveMotor().getConfigurator();
var talonFXConfigs = new TalonFXConfiguration();
talonFXConfigurator.refresh(talonFXConfigs);
talonFXConfigs.CurrentLimits.StatorCurrentLimit = limitInAmps;
talonFXConfigs.CurrentLimits.SupplyCurrentLimit = limitInAmps+10;
talonFXConfigurator.apply(talonFXConfigs);
}
}
public void activateLuigiMode() {
setLimits(20);
}
public void deactivateLuigiMode() {
setLimits(SwerveDriveConstants.Configurations.SLIP_CURRENT);
}
2025-01-15 16:31:53 -07:00
public boolean rotateToTarget(double angle) {
2025-01-18 11:36:26 -07:00
swerveDriveTrain.setControl(new SwerveRequest.FieldCentricFacingAngle()
.withVelocityX(0)
.withVelocityY(0)
.withTargetDirection(Rotation2d.fromDegrees(angle)));
2025-01-04 16:09:10 -07:00
2025-01-18 11:36:26 -07:00
if (Math.abs(angle - getGyroAngle()) < 5.0) {
return true;
}
2025-01-04 16:09:10 -07:00
2025-01-18 11:36:26 -07:00
return false;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void driveWithInputRotation(Translation2d leftStick, Rotation2d rot) {
2025-01-18 11:36:26 -07:00
// if (leftStick.getNorm() < 0.05 && stopped == false) // if no imput and the
// swerve drive is still going:
// stopModules(); // stop the swerve
// if (leftStick.getNorm() < 0.05) //if no imput
// return; // don't bother doing swerve drive math and return early.
leftStick = leftStick.rotateBy(Rotation2d.fromDegrees(SwerveDriveConstants.FORWARD_OFFSET));
swerveDriveTrain.setControl(new SwerveRequest.FieldCentricFacingAngle()
.withVelocityX(leftStick.getX() * -speedAdjust)
.withVelocityY(leftStick.getY() * speedAdjust)
.withTargetDirection(rot));
// double
2025-01-15 16:31:53 -07:00
}
2025-01-11 15:35:24 -07:00
2025-01-15 16:31:53 -07:00
public double getGyroAngle() {
2025-02-01 15:52:06 -07:00
return getPose2d().getRotation().getRadians();
}
public Pose2d getPose2d() {
return swerveDriveTrain.samplePoseAt(Vision.getTime()).orElse(initalPose2d);
2025-01-09 19:40:37 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void resetGyro() {
2025-01-18 11:36:26 -07:00
swerveDriveTrain.tareEverything();
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-29 17:06:55 -07:00
public void softStop() {
stopped = true;
swerveDriveTrain.setControl(new SwerveRequest.FieldCentric()
.withVelocityX(0)
.withVelocityY(0)
.withRotationalRate(0)
); // stop the modules without breaking
}
2025-01-18 11:36:26 -07:00
public void stopModules() {
2025-02-21 16:54:18 -07:00
// stopped = true;
// swerveDriveTrain.setControl(new SwerveRequest.SwerveDriveBrake());
softStop();
2025-01-18 11:36:26 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
@Override
public void periodic() {
2025-01-18 11:36:26 -07:00
// This method will be called once per scheduler run\
2025-02-01 15:52:06 -07:00
SmartDashboard.putNumber("Gyro", (getGyroAngle() * 180) / Math.PI);
2025-01-18 11:36:26 -07:00
SmartDashboard.putNumber("RotTartget", rotTarget);
2025-01-18 11:36:26 -07:00
double time = Vision.getTime();
2025-02-04 19:44:24 -07:00
2025-01-09 19:40:37 -07:00
2025-01-18 11:36:26 -07:00
vision.setLastOdomPose(swerveDriveTrain.samplePoseAt(time));
2025-01-09 19:40:37 -07:00
2025-01-18 11:36:26 -07:00
if (vision.isTag()) {
swerveDriveTrain.addVisionMeasurement(vision.getPose2d(), time);
}
2025-01-18 11:36:26 -07:00
// if(e.isPresent())
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
private void reset_index() {
2025-01-18 11:36:26 -07:00
gear_index = SwerveDriveConstants.STARTING_GEAR; // however we wish to initialize the gear (What gear does the
// robot start in?)
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftDown() {
2025-01-18 11:36:26 -07:00
if (gear_index == -1 || gear_index >= SwerveDriveConstants.GEARS.length)
reset_index(); // If outof bounds: reset index
int i = gear_index - 1;
if (i == -1)
i = 0;
setPercentOutput(SwerveDriveConstants.GEARS[i]);
gear_index = i;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftUp() {
2025-01-18 11:36:26 -07:00
if (gear_index == -1 || gear_index >= SwerveDriveConstants.GEARS.length)
reset_index(); // If outof bounds: reset index
int i = gear_index + 1;
if (i == SwerveDriveConstants.GEARS.length)
i = SwerveDriveConstants.GEARS.length - 1;
setPercentOutput(SwerveDriveConstants.GEARS[i]);
gear_index = i;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setPercentOutput(double speed) {
2025-01-18 11:36:26 -07:00
speedAdjust = SwerveDriveConstants.MAX_SPEED_MEETERS_PER_SEC * speed;
gear_index = -1;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setToSlow() {
2025-01-18 11:36:26 -07:00
setPercentOutput(SwerveDriveConstants.SLOW_SPEED);
gear_index = 0;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setToFast() {
2025-01-18 11:36:26 -07:00
setPercentOutput(SwerveDriveConstants.FAST_SPEED);
gear_index = 1;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setToTurbo() {
2025-01-18 11:36:26 -07:00
setPercentOutput(SwerveDriveConstants.TURBO_SPEED);
gear_index = 2;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftUpRot() {
2025-01-18 11:36:26 -07:00
rotSpeedAdjust = SwerveDriveConstants.ROTATION_SPEED;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftDownRot() {
2025-01-18 11:36:26 -07:00
rotSpeedAdjust = SwerveDriveConstants.MIN_ROT_SPEED;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
@Override
public String getSubsystemName() {
2025-01-18 11:36:26 -07:00
return "Swerve Drive Controller";
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
ShuffleboardLayout subsystemLayout = Shuffleboard.getTab("Subsystems")
2025-01-18 11:36:26 -07:00
.getLayout(getSubsystemName(), BuiltInLayouts.kList)
.withSize(2, 2);
2025-01-05 11:25:37 -07:00
2025-01-15 16:31:53 -07:00
GenericEntry sbGyro = subsystemLayout
2025-01-18 11:36:26 -07:00
.add("Gyro angle", 0)
.withWidget(BuiltInWidgets.kGyro)
.getEntry();
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
GenericEntry sbShiftState = subsystemLayout
2025-01-18 11:36:26 -07:00
.add("Shift State", 0)
.withWidget(BuiltInWidgets.kNumberBar)
.getEntry();
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
@Override
public void queryStatus() {
2025-01-18 11:36:26 -07:00
sbGyro.setDouble(getGyroAngle());
sbShiftState.setDouble(this.speedAdjust);
// TODO: Add more status things
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
@Override
public Status diagnosticStatus() {
2025-01-18 11:36:26 -07:00
Status status = new Status();
status.addReport(ReportLevel.ERROR,
"Don't know how to diganose new CTRE swerve systems. please check under the CAN(t) section for more detailed information about the swerves there.");
2025-01-04 16:09:10 -07:00
2025-01-18 11:36:26 -07:00
return status;
2025-01-15 16:31:53 -07:00
}
2025-01-04 16:09:10 -07:00
}