55package com .frcteam3255 .components .swerve ;
66
77import java .util .HashMap ;
8+ import java .util .List ;
89import java .util .function .BooleanSupplier ;
910
11+ import com .ctre .phoenix6 .configs .CANcoderConfiguration ;
12+ import com .ctre .phoenix6 .configs .TalonFXConfiguration ;
1013import com .ctre .phoenix6 .hardware .Pigeon2 ;
11- import com .ctre .phoenix6 .signals .InvertedValue ;
12- import com .ctre .phoenix6 .signals .NeutralModeValue ;
13- import com .ctre .phoenix6 .signals .SensorDirectionValue ;
1414import com .pathplanner .lib .auto .AutoBuilder ;
1515import com .pathplanner .lib .config .PIDConstants ;
1616import com .pathplanner .lib .config .RobotConfig ;
1717import com .pathplanner .lib .controllers .PPHolonomicDriveController ;
1818import com .pathplanner .lib .trajectory .PathPlannerTrajectory ;
1919
20+ import edu .wpi .first .math .MathUtil ;
2021import edu .wpi .first .math .Matrix ;
22+ import edu .wpi .first .math .controller .HolonomicDriveController ;
2123import edu .wpi .first .math .estimator .SwerveDrivePoseEstimator ;
2224import edu .wpi .first .math .geometry .Pose2d ;
2325import edu .wpi .first .math .geometry .Rotation2d ;
2931import edu .wpi .first .math .numbers .N1 ;
3032import edu .wpi .first .math .numbers .N3 ;
3133import edu .wpi .first .units .Units ;
34+ import edu .wpi .first .units .measure .Angle ;
35+ import edu .wpi .first .units .measure .AngularVelocity ;
36+ import edu .wpi .first .units .measure .Distance ;
37+ import edu .wpi .first .units .measure .LinearVelocity ;
3238import edu .wpi .first .wpilibj .Timer ;
3339import edu .wpi .first .wpilibj .smartdashboard .Field2d ;
3440import edu .wpi .first .wpilibj2 .command .Command ;
@@ -58,6 +64,10 @@ public class SN_SuperSwerve extends SubsystemBase {
5864 public double timeFromLastUpdate = 0 ;
5965 public double lastSimTime = Timer .getFPGATimestamp ();
6066 public Field2d field ;
67+ public TalonFXConfiguration driveConfig , steerConfig ;
68+ public CANcoderConfiguration cancoderConfig ;
69+ public HolonomicDriveController teleopAutoDriveController ;
70+ public AngularVelocity turnSpeed ;
6171
6272 /**
6373 * <p>
@@ -84,22 +94,21 @@ public class SN_SuperSwerve extends SubsystemBase {
8494 * Physically measured distance (center to center) between the Front
8595 * and Back wheels in meters
8696 * @param CANBusName
87- * The name of the CANBus that all of the swerve components are on
97+ * The name of the CANBus that the Pigeon is on.
8898 * @param pigeonCANId
89- * The CAN id of the Pigeon. The Pigeon MUST be on the same CANBus as
90- * the modules
99+ * The CAN id of the Pigeon.
91100 * @param minimumSteerPercent
92101 * The minimum PercentOutput required to make the steer motor move
93- * @param driveInversion
94- * The direction that is positive for drive motors
95- * @param steerInversion
96- * The direction that is positive for steer motors
97- * @param cancoderInversion
98- * The direction that is positive for Cancoders
99- * @param driveNeutralMode
100- * The behavior of every drive motor when set to neutral-output
101- * @param steerNeutralMode
102- * The behavior of every steer motor when set to neutral-output
102+ * @param driveConfig
103+ * The configuration for each drive motor. Make sure you set the
104+ * inversion, neutral mode, and sensor to mechanism ratio!
105+ * @param steerConfig
106+ * The configuration for each steer motor. Make sure you set the
107+ * inversion, neutral mode, sensor to mechanism ratio, and continuous
108+ * wrap!
109+ * @param cancoderConfig
110+ * The configuration for each CANCoder. Make sure you set the sensor
111+ * direction!
103112 * @param stateStdDevs
104113 * Standard deviations of the pose estimate (x position in meters, y
105114 * position in meters, and heading in radians). Increase these
@@ -114,6 +123,11 @@ public class SN_SuperSwerve extends SubsystemBase {
114123 * @param autoSteerPID
115124 * The rotational PID constants applied to the entire Drivetrain
116125 * during autonomous in order to reach the correct pose
126+ * @param teleopAutoDriveController
127+ * The PID controller used to control rotational snapping and auto
128+ * driving during Teleoperated.
129+ * @param turnSpeed
130+ * The turning speed of the robot.
117131 * @param robotConfig
118132 * The robot configuration used by PathPlanner.
119133 * @param autoFlipPaths
@@ -126,10 +140,10 @@ public class SN_SuperSwerve extends SubsystemBase {
126140 */
127141 public SN_SuperSwerve (SN_SwerveConstants swerveConstants , SN_SwerveModule [] modules , double wheelbase ,
128142 double trackWidth , String CANBusName , int pigeonCANId , double minimumSteerPercent ,
129- InvertedValue driveInversion , InvertedValue steerInversion , SensorDirectionValue cancoderInversion ,
130- NeutralModeValue driveNeutralMode , NeutralModeValue steerNeutralMode , Matrix <N3 , N1 > stateStdDevs ,
131- Matrix < N3 , N1 > visionStdDevs , PIDConstants autoDrivePID , PIDConstants autoSteerPID , RobotConfig robotConfig ,
132- BooleanSupplier autoFlipPaths , boolean isSimulation ) {
143+ TalonFXConfiguration driveConfig , TalonFXConfiguration steerConfig , CANcoderConfiguration cancoderConfig ,
144+ Matrix < N3 , N1 > stateStdDevs , Matrix <N3 , N1 > visionStdDevs , PIDConstants autoDrivePID ,
145+ PIDConstants autoSteerPID , HolonomicDriveController teleopAutoDriveController , AngularVelocity turnSpeed ,
146+ RobotConfig robotConfig , BooleanSupplier autoFlipPaths , boolean isSimulation ) {
133147
134148 isFieldRelative = true ;
135149 field = new Field2d ();
@@ -148,22 +162,15 @@ public SN_SuperSwerve(SN_SwerveConstants swerveConstants, SN_SwerveModule[] modu
148162 this .autoSteerPID = autoSteerPID ;
149163 this .isSimulation = isSimulation ;
150164 this .autoFlipPaths = autoFlipPaths ;
165+ this .teleopAutoDriveController = teleopAutoDriveController ;
166+ this .turnSpeed = turnSpeed ;
151167
152168 SN_SwerveModule .isSimulation = isSimulation ;
153169 SN_SwerveModule .wheelCircumference = swerveConstants .wheelCircumference ;
154170 SN_SwerveModule .maxModuleSpeedMeters = swerveConstants .maxSpeedMeters ;
155- SN_SwerveModule .driveGearRatio = swerveConstants .driveGearRatio ;
156- SN_SwerveModule .steerGearRatio = swerveConstants .steerGearRatio ;
157171
158172 SN_SwerveModule .minimumSteerSpeedPercent = minimumSteerPercent ;
159173
160- SN_SwerveModule .driveInversion = driveInversion ;
161- SN_SwerveModule .driveNeutralMode = driveNeutralMode ;
162-
163- SN_SwerveModule .steerInversion = steerInversion ;
164- SN_SwerveModule .steerNeutralMode = steerNeutralMode ;
165- SN_SwerveModule .cancoderInversion = cancoderInversion ;
166-
167174 pigeon = new Pigeon2 (pigeonCANId , CANBusName );
168175
169176 // The absolute encoders need time to initialize
@@ -176,6 +183,9 @@ public SN_SuperSwerve(SN_SwerveConstants swerveConstants, SN_SwerveModule[] modu
176183 }
177184
178185 public void configure () {
186+ SN_SwerveModule .driveConfiguration = driveConfig ;
187+ SN_SwerveModule .steerConfiguration = steerConfig ;
188+ SN_SwerveModule .cancoderConfiguration = cancoderConfig ;
179189 for (SN_SwerveModule mod : modules ) {
180190 mod .configure ();
181191 }
@@ -297,6 +307,22 @@ public void drive(Translation2d translation, double rotation, boolean isOpenLoop
297307 setModuleStates (desiredModuleStates , isOpenLoop );
298308 }
299309
310+ /**
311+ * Drive the drivetrain with pre-calculated ChassisSpeeds
312+ *
313+ * @param chassisSpeeds
314+ * Desired ChassisSpeeds
315+ * @param isOpenLoop
316+ * Are the modules being set based on open loop or closed loop
317+ * control
318+ *
319+ */
320+ public void drive (ChassisSpeeds chassisSpeeds , boolean isOpenLoop ) {
321+ SwerveModuleState [] desiredModuleStates = swerveKinematics
322+ .toSwerveModuleStates (ChassisSpeeds .discretize (chassisSpeeds , timeFromLastUpdate ));
323+ setModuleStates (desiredModuleStates , isOpenLoop );
324+ }
325+
300326 /**
301327 * Drive the drivetrain in autonomous. Autonomous driving is always closed loop.
302328 *
@@ -305,9 +331,7 @@ public void drive(Translation2d translation, double rotation, boolean isOpenLoop
305331 *
306332 */
307333 public void driveAutonomous (ChassisSpeeds chassisSpeeds ) {
308- SwerveModuleState [] desiredModuleStates = swerveKinematics
309- .toSwerveModuleStates (ChassisSpeeds .discretize (chassisSpeeds , timeFromLastUpdate ));
310- setModuleStates (desiredModuleStates , false );
334+ drive (chassisSpeeds , false );
311335 }
312336
313337 /**
@@ -398,7 +422,7 @@ private Rotation2d getGyroRotation() {
398422 * per Second
399423 */
400424 public double getGyroRate () {
401- return pigeon .getRate ();
425+ return pigeon .getAngularVelocityZWorld (). getValueAsDouble ();
402426 }
403427
404428 /**
@@ -442,6 +466,123 @@ public void updateTimer() {
442466 lastSimTime = Timer .getFPGATimestamp ();
443467 }
444468
469+ /**
470+ * Returns the rotational velocity calculated with PID control to reach the
471+ * given rotation. This must be called every loop until you reach the given
472+ * rotation.
473+ *
474+ * @param desiredYaw
475+ * The desired yaw to rotate to
476+ * @return The desired velocity needed to rotate to that position.
477+ */
478+ public AngularVelocity getVelocityToRotate (Rotation2d desiredYaw ) {
479+ double yawSetpoint = teleopAutoDriveController .getThetaController ().calculate (getRotation ().getRadians (),
480+ desiredYaw .getRadians ());
481+
482+ // limit the PID output to our maximum rotational speed
483+ yawSetpoint = MathUtil .clamp (yawSetpoint , -turnSpeed .in (Units .RadiansPerSecond ),
484+ turnSpeed .in (Units .RadiansPerSecond ));
485+
486+ return Units .RadiansPerSecond .of (yawSetpoint );
487+ }
488+
489+ // I would write "field relative" or "robot relative" somewhere here but I
490+ // genuinely don't know if its robot relative (i think it is?)
491+ /**
492+ * Aligns the drivetrain rotationally while still allowing for translational
493+ * inputs from the driver.
494+ *
495+ * @param isRedAlliance
496+ * If we are a red robot this match
497+ * @param desiredTarget
498+ * The desired rotation to reach
499+ * @param xVelocity
500+ * The manual translational velocity on the X axis
501+ * @param yVelocity
502+ * The manual translational velocity on the Y axis
503+ * @param isOpenLoop
504+ * If we are driving using OpenLoop control
505+ */
506+ public void rotationalAlign (boolean isRedAlliance , Pose2d desiredTarget , LinearVelocity xVelocity ,
507+ LinearVelocity yVelocity , boolean isOpenLoop ) {
508+ int redAllianceMultiplier = isRedAlliance ? -1 : 1 ;
509+ // Rotational-only auto-align
510+ drive (new Translation2d (xVelocity .times (redAllianceMultiplier ).in (Units .MetersPerSecond ),
511+ yVelocity .times (redAllianceMultiplier ).in (Units .MetersPerSecond )),
512+ getVelocityToRotate (desiredTarget .getRotation ()).in (Units .RadiansPerSecond ), isOpenLoop );
513+ }
514+
515+ /**
516+ *
517+ * Automatically drives to a pose during the teleoperated period. If an axis is
518+ * locked, it will override the automatically calculated input with the driver's
519+ * input.
520+ *
521+ * @param isRedAlliance
522+ * If we are a red robot this match
523+ * @param desiredTarget
524+ * The desired pose to drive to
525+ * @param xVelocity
526+ * The manual translational velocity on the X axis
527+ *
528+ * @param yVelocity
529+ * The manual translational velocity on the Y axis
530+ * @param isOpenLoop
531+ * If we are driving using OpenLoop control
532+ * @param lockX
533+ * If manual velocities should be used on the X axis
534+ * @param lockY
535+ * If manual velocities should be used on the Y axis
536+ */
537+ public void autoAlign (boolean isRedAlliance , Pose2d desiredTarget , LinearVelocity xVelocity ,
538+ LinearVelocity yVelocity , boolean isOpenLoop , boolean lockX , boolean lockY ) {
539+
540+ int redAllianceMultiplier = isRedAlliance ? -1 : 1 ;
541+ double manualXVelocity = xVelocity .times (redAllianceMultiplier ).in (Units .MetersPerSecond );
542+ double manualYVelocity = yVelocity .times (redAllianceMultiplier ).in (Units .MetersPerSecond );
543+ // Full auto-align
544+ ChassisSpeeds automatedDTVelocity = teleopAutoDriveController .calculate (getPose (), desiredTarget , 0 ,
545+ desiredTarget .getRotation ());
546+
547+ if (lockX ) {
548+ automatedDTVelocity .vxMetersPerSecond = manualXVelocity ;
549+ }
550+ if (lockY ) {
551+ automatedDTVelocity .vyMetersPerSecond = manualYVelocity ;
552+ }
553+ drive (automatedDTVelocity , isOpenLoop );
554+ }
555+
556+ public boolean isAtRotation (Rotation2d desiredRotation , Angle tolerance ) {
557+ return (getRotation ().getMeasure ().compareTo (desiredRotation .getMeasure ().minus (tolerance )) > 0 )
558+ && getRotation ().getMeasure ().compareTo (desiredRotation .getMeasure ().plus (tolerance )) < 0 ;
559+ }
560+
561+ public boolean isAtPosition (Pose2d desiredPose2d , Distance tolerance ) {
562+ return Units .Meters .of (getPose ().getTranslation ().getDistance (desiredPose2d .getTranslation ())).lte (tolerance );
563+ }
564+ /**
565+ * Calculate which pose from an array has the closest rotation to the robot's
566+ * current pose. If multiple poses have the same rotation, the last one in the
567+ * list will be returned.
568+ *
569+ * @param poses
570+ * The list of poses to check
571+ * @return The last pose in the list with the closest rotation
572+ */
573+ public Pose2d getClosestPoseByRotation (List <Pose2d > poses ) {
574+ Pose2d closestPose = poses .get (0 );
575+ double smallestDifference = Math .abs (getRotation ().minus (closestPose .getRotation ()).getRadians ());
576+ for (Pose2d pose : poses ) {
577+ double difference = Math .abs (getRotation ().minus (pose .getRotation ()).getRadians ());
578+ if (difference < smallestDifference ) {
579+ smallestDifference = difference ;
580+ closestPose = pose ;
581+ }
582+ }
583+ return closestPose ;
584+ }
585+
445586 @ Override
446587 public void periodic () {
447588 updateTimer ();
0 commit comments