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 ;
2025-01-08 18:32:02 -07:00
import java.util.Optional ;
2025-01-16 19:41:08 -07:00
import java.util.function.DoubleSupplier ;
2025-01-08 18:32:02 -07:00
import com.ctre.phoenix6.Utils ;
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 ;
import com.ctre.phoenix6.swerve.SwerveRequest ;
2025-01-08 18:32:02 -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 ;
2025-01-08 18:32:02 -07:00
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 ;
2025-01-10 21:36:30 -07:00
import frc4388.robot.Constants.VisionConstants ;
2025-01-04 16:09:10 -07:00
import frc4388.utility.Status ;
import frc4388.utility.Subsystem ;
import frc4388.utility.Status.ReportLevel ;
2025-01-15 16:49:32 -07:00
import edu.wpi.first.wpilibj.DriverStation ;
2025-01-04 16:09:10 -07:00
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-04 16:09:10 -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 ;
public double autoSpeedAdjust = SwerveDriveConstants . MAX_SPEED_MEETERS_PER_SEC * 0 . 25 ; // cap auto performance to 25%
2025-01-04 16:09:10 -07:00
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 ) {
// 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-16 19:41:08 -07:00
( ) - > {
var pose = swerveDriveTrain . samplePoseAt ( Utils . getCurrentTimeSeconds ( ) ) . orElse ( new Pose2d ( ) ) ;
2025-01-17 16:59:32 -07:00
// pose = new Pose2d(pose.getX(), pose.getY(), pose.getRotation().times(-1));
2025-01-16 19:41:08 -07:00
return pose ; //getRotation().times(-1)
} , // Robot pose supplier
2025-01-15 16:31:53 -07:00
swerveDriveTrain : : resetPose , // Method to reset odometry (will be called if your auto has a starting 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
var alliance = DriverStation . getAlliance ( ) ;
if ( alliance . isPresent ( ) ) {
return alliance . get ( ) = = DriverStation . Alliance . Red ;
}
return false ;
} ,
this // Reference to this subsystem to set requirements
2025-01-15 16:49:32 -07:00
) ;
2025-01-15 16:31:53 -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);
// }
public void driveWithInput ( Translation2d leftStick , Translation2d rightStick , boolean fieldRelative ) {
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
2025-01-07 08:38:21 -07:00
2025-01-15 16:31:53 -07:00
if ( rightStick . getNorm ( ) < 0 . 05 & & leftStick . getNorm ( ) < 0 . 05 ) //if no imput
return ; // don't bother doing swerve drive math and return early.
2025-01-07 08:38:21 -07:00
2025-01-15 16:31:53 -07:00
leftStick = leftStick . rotateBy ( Rotation2d . fromDegrees ( SwerveDriveConstants . FORWARD_OFFSET ) ) ;
2025-01-07 08:38:21 -07:00
2025-01-04 16:09:10 -07:00
if ( fieldRelative ) {
2025-01-06 21:34:40 -07:00
swerveDriveTrain . setControl ( new SwerveRequest . FieldCentric ( )
2025-01-17 20:39:20 -07:00
. withVelocityX ( leftStick . getX ( ) * speedAdjust )
. withVelocityY ( - leftStick . getY ( ) * speedAdjust )
2025-01-08 18:32:02 -07:00
. withRotationalRate ( rightStick . getX ( ) * rotSpeedAdjust )
2025-01-06 21:34:40 -07:00
) ;
// double rot = 0;
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
// ! drift correction
// dependant on if the new odomitry system acounts for rotational drift, this may not be needed.
// if (rightStick.getNorm() > 0.05) {
// rotTarget = swerveDriveTrain.getRotation3d().toRotation2d().getDegrees();
// swerveDriveTrain.setControl(new SwerveRequest.FieldCentric()
// .withVelocityX(leftStick.getX()*speedAdjust)
// .withVelocityY(leftStick.getY()*speedAdjust)
// .withRotationalRate(rightStick.getY()*rotSpeedAdjust)
// );
2025-01-06 21:34:40 -07:00
2025-01-15 16:31:53 -07:00
// // SmartDashboard.putBoolean("drift correction", false);
// stopped = false;
// } else if(leftStick.getNorm() > 0.05) {
// if (!stopped) {
// stopModules();
// stopped = true;
// }
// // SmartDashboard.putBoolean("drift correction", true);
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
// // 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 ( )
2025-01-16 19:41:08 -07:00
. withVelocityX ( leftStick . getX ( ) * speedAdjust )
. withVelocityY ( - leftStick . getY ( ) * speedAdjust )
2025-01-15 16:31:53 -07:00
. withRotationalRate ( rightStick . getX ( ) * rotSpeedAdjust )
) ;
}
2025-01-04 16:09:10 -07:00
}
2025-01-15 16:31:53 -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
2025-01-07 13:45:41 -07:00
2025-01-15 16:31:53 -07:00
if ( rightStick . getNorm ( ) < 0 . 05 & & leftStick . getNorm ( ) < 0 . 05 ) //if no imput
return ; // don't bother doing swerve drive math and return early.
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
leftStick . rotateBy ( Rotation2d . fromDegrees ( SwerveDriveConstants . FORWARD_OFFSET ) ) ;
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
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-15 16:31:53 -07:00
public boolean rotateToTarget ( double angle ) {
swerveDriveTrain . setControl ( new SwerveRequest . FieldCentricFacingAngle ( )
. withVelocityX ( 0 )
. withVelocityY ( 0 )
. withTargetDirection ( Rotation2d . fromDegrees ( angle ) )
) ;
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
if ( Math . abs ( angle - getGyroAngle ( ) ) < 5 . 0 ) {
return true ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
return false ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void driveWithInputRotation ( Translation2d leftStick , Rotation2d rot ) {
// if (leftStick.getNorm() < 0.05 && stopped == false) // if no imput and the swerve drive is still going:
// stopModules(); // stop the swerve
2025-01-11 15:35:24 -07:00
2025-01-15 16:31:53 -07:00
// if (leftStick.getNorm() < 0.05) //if no imput
// return; // don't bother doing swerve drive math and return early.
2025-01-11 15:35:24 -07:00
2025-01-15 16:31:53 -07:00
leftStick = leftStick . rotateBy ( Rotation2d . fromDegrees ( SwerveDriveConstants . FORWARD_OFFSET ) ) ;
2025-01-11 15:35:24 -07:00
2025-01-15 16:31:53 -07:00
swerveDriveTrain . setControl ( new SwerveRequest . FieldCentricFacingAngle ( )
. withVelocityX ( leftStick . getX ( ) * - speedAdjust )
. withVelocityY ( leftStick . getY ( ) * speedAdjust )
. withTargetDirection ( rot )
) ;
// double
}
2025-01-11 15:35:24 -07:00
2025-01-15 16:31:53 -07:00
public double getGyroAngle ( ) {
return swerveDriveTrain . getRotation3d ( ) . getAngle ( ) ;
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 ( ) {
swerveDriveTrain . tareEverything ( ) ;
}
2025-01-04 16:09:10 -07:00
public void stopModules ( ) {
2025-01-15 16:32:13 -07:00
swerveDriveTrain . setControl ( new SwerveRequest . SwerveDriveBrake ( ) ) ;
2025-01-04 16:09:10 -07:00
}
2025-01-15 16:31:53 -07:00
@Override
public void periodic ( ) {
// This method will be called once per scheduler run\
SmartDashboard . putNumber ( " Gyro " , getGyroAngle ( ) ) ;
SmartDashboard . putNumber ( " RotTartget " , rotTarget ) ;
2025-01-08 18:32:02 -07:00
2025-01-15 16:31:53 -07:00
double time = Vision . getTime ( ) ;
2025-01-09 19:40:37 -07:00
2025-01-15 16:31:53 -07:00
vision . setLastOdomPose ( swerveDriveTrain . samplePoseAt ( time ) ) ;
2025-01-09 19:40:37 -07:00
2025-01-15 16:31:53 -07:00
if ( vision . isTag ( ) ) {
swerveDriveTrain . addVisionMeasurement ( vision . getPose2d ( ) , time ) ;
}
2025-01-08 18:32:02 -07:00
2025-01-15 16:31:53 -07:00
// if(e.isPresent())
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
private void reset_index ( ) {
gear_index = SwerveDriveConstants . STARTING_GEAR ; // however we wish to initialize the gear (What gear does the robot start in?)
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftDown ( ) {
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-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftUp ( ) {
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-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setPercentOutput ( double speed ) {
speedAdjust = SwerveDriveConstants . MAX_SPEED_MEETERS_PER_SEC * speed ;
gear_index = - 1 ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setToSlow ( ) {
setPercentOutput ( SwerveDriveConstants . SLOW_SPEED ) ;
gear_index = 0 ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setToFast ( ) {
setPercentOutput ( SwerveDriveConstants . FAST_SPEED ) ;
gear_index = 1 ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void setToTurbo ( ) {
setPercentOutput ( SwerveDriveConstants . TURBO_SPEED ) ;
gear_index = 2 ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftUpRot ( ) {
rotSpeedAdjust = SwerveDriveConstants . ROTATION_SPEED ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
public void shiftDownRot ( ) {
rotSpeedAdjust = SwerveDriveConstants . MIN_ROT_SPEED ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
@Override
public String getSubsystemName ( ) {
return " Swerve Drive Controller " ;
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
ShuffleboardLayout subsystemLayout = Shuffleboard . getTab ( " Subsystems " )
. 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
. 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
. 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 ( ) {
sbGyro . setDouble ( getGyroAngle ( ) ) ;
sbShiftState . setDouble ( this . speedAdjust ) ;
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
//TODO: Add more status things
}
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
@Override
public Status diagnosticStatus ( ) {
Status status = new Status ( ) ;
2025-01-04 16:09:10 -07:00
2025-01-15 16:31:53 -07:00
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-15 16:31:53 -07:00
return status ;
}
2025-01-04 16:09:10 -07:00
}