|
6 | 6 |
|
7 | 7 | package org.frc6423.frc2025.subsystems.swerve; |
8 | 8 |
|
| 9 | +import edu.wpi.first.math.controller.PIDController; |
| 10 | +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; |
| 11 | +import edu.wpi.first.math.geometry.Pose2d; |
| 12 | +import edu.wpi.first.math.geometry.Rotation2d; |
| 13 | +import edu.wpi.first.math.geometry.Translation2d; |
| 14 | +import edu.wpi.first.math.kinematics.ChassisSpeeds; |
| 15 | +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; |
| 16 | +import edu.wpi.first.math.kinematics.SwerveModulePosition; |
| 17 | +import edu.wpi.first.math.kinematics.SwerveModuleState; |
| 18 | +import edu.wpi.first.wpilibj.DriverStation; |
| 19 | +import edu.wpi.first.wpilibj.DriverStation.Alliance; |
| 20 | +import edu.wpi.first.wpilibj2.command.Command; |
| 21 | +import edu.wpi.first.wpilibj2.command.Commands; |
9 | 22 | import edu.wpi.first.wpilibj2.command.SubsystemBase; |
10 | 23 | import java.util.Arrays; |
| 24 | +import java.util.function.DoubleSupplier; |
| 25 | + |
| 26 | +import org.frc6423.frc2025.Robot; |
| 27 | +import org.frc6423.frc2025.Constants.KDriveConstants.DriveControlMode; |
| 28 | +import org.frc6423.frc2025.subsystems.swerve.gyro.GyroIO; |
| 29 | +import org.frc6423.frc2025.subsystems.swerve.gyro.GyroIOInputsAutoLogged; |
| 30 | +import org.frc6423.frc2025.subsystems.swerve.gyro.GyroIONavX; |
11 | 31 | import org.frc6423.frc2025.subsystems.swerve.module.Module; |
12 | 32 | import org.frc6423.frc2025.subsystems.swerve.module.ModuleIO; |
| 33 | +import org.littletonrobotics.junction.Logger; |
13 | 34 |
|
14 | 35 | public class SwerveSubsystem extends SubsystemBase { |
15 | 36 |
|
16 | 37 | private final Module[] m_modules; |
17 | 38 |
|
18 | | - public SwerveSubsystem(ModuleIO[] ios) { |
| 39 | + private final GyroIO m_gryo; |
| 40 | + private final GyroIOInputsAutoLogged m_gyroInputs; |
| 41 | + private Rotation2d m_simulationHeading; |
| 42 | + |
| 43 | + private final SwerveDriveKinematics m_swerveKinematics; |
| 44 | + private final SwerveDrivePoseEstimator m_swerveOdometry; |
| 45 | + private ChassisSpeeds m_setpointVelocity; |
| 46 | + |
| 47 | + private final PIDController m_rotationalVelocityFeedback; |
| 48 | + |
| 49 | + public SwerveSubsystem(ModuleIO[] ios, Translation2d[] locs) { |
| 50 | + m_gryo = new GyroIONavX(); |
| 51 | + m_gyroInputs = new GyroIOInputsAutoLogged(); |
| 52 | + m_simulationHeading = new Rotation2d(); |
19 | 53 | m_modules = new Module[ios.length]; |
| 54 | + for (int i = 0; i < ios.length; i++) { |
| 55 | + m_modules[i] = new Module(ios[i], i); |
| 56 | + } |
20 | 57 |
|
21 | | - Arrays.stream(ios).forEach((io) -> m_modules[0] = new Module(io, 0)); |
| 58 | + m_swerveKinematics = new SwerveDriveKinematics(locs); |
| 59 | + m_swerveOdometry = |
| 60 | + new SwerveDrivePoseEstimator( |
| 61 | + m_swerveKinematics, new Rotation2d(), getModulePoses(), new Pose2d()); |
| 62 | + |
| 63 | + m_rotationalVelocityFeedback = new PIDController( |
| 64 | + 0, |
| 65 | + 0, |
| 66 | + 0); // ! |
22 | 67 | } |
23 | 68 |
|
24 | 69 | @Override |
25 | | - public void periodic() {} |
| 70 | + public void periodic() { |
| 71 | + // gyro update |
| 72 | + if (Robot.isReal()) m_gryo.updateInputs(m_gyroInputs); |
| 73 | + else m_simulationHeading.rotateBy(Rotation2d.fromRadians(getSetpointVelocity().omegaRadiansPerSecond)); |
| 74 | + |
| 75 | + Arrays.stream(m_modules).forEach(Module::periodic); // Update each module |
| 76 | + |
| 77 | + Logger.processInputs("Swerve/Gyro", m_gyroInputs); |
| 78 | + Logger.recordOutput("Swerve/ModuleStates", getModuleStates()); |
| 79 | + Logger.recordOutput("Swerve/DesiredVel", m_setpointVelocity); |
| 80 | + Logger.recordOutput("Swerve/Velocity", getVelocity()); |
| 81 | + } |
| 82 | + |
| 83 | + /** |
| 84 | + * Drives Robot based on input velocities |
| 85 | + * |
| 86 | + * @param velXMetersPerSec X translation velocity supplier |
| 87 | + * @param velYMetersPerSec Y translation velocity supplier |
| 88 | + * @param velOmegaRadsPerSec Omega rotation velocity supplier |
| 89 | + * @return {@link Command} |
| 90 | + */ |
| 91 | + public Command drive(DoubleSupplier velXMetersPerSec, DoubleSupplier velYMetersPerSec, DoubleSupplier velOmegaRadsPerSec) { |
| 92 | + return Commands.run( |
| 93 | + () -> setSetpointVelocities( |
| 94 | + ChassisSpeeds.fromFieldRelativeSpeeds( |
| 95 | + velXMetersPerSec.getAsDouble(), |
| 96 | + velYMetersPerSec.getAsDouble(), |
| 97 | + velOmegaRadsPerSec.getAsDouble(), |
| 98 | + DriverStation.getAlliance().get() == Alliance.Blue |
| 99 | + ? getHeading() |
| 100 | + : getHeading().minus(Rotation2d.fromRotations(0.5))), |
| 101 | + DriveControlMode.OPENLOOP)); |
| 102 | + } |
| 103 | + |
| 104 | + /** |
| 105 | + * Drives Robot based on input velocities and desired heading |
| 106 | + * |
| 107 | + * @param velXMetersPerSec X translation velocity supplier |
| 108 | + * @param velYMetersPerSec Y translation velocity supplier |
| 109 | + * @param desiredHeading Desired robot orientation |
| 110 | + * @return {@link Command} |
| 111 | + */ |
| 112 | + public Command drive(DoubleSupplier velXMetersPerSec, DoubleSupplier velYMetersPerSec, Rotation2d desiredHeading) { |
| 113 | + return Commands.run( |
| 114 | + () -> setSetpointVelocities( |
| 115 | + ChassisSpeeds.fromFieldRelativeSpeeds( |
| 116 | + velXMetersPerSec.getAsDouble(), |
| 117 | + velYMetersPerSec.getAsDouble(), |
| 118 | + m_rotationalVelocityFeedback.calculate(getHeading().getRadians(), desiredHeading.getRadians()), |
| 119 | + getHeading()), |
| 120 | + DriveControlMode.OPENLOOP)); |
| 121 | + } |
| 122 | + |
| 123 | + /** Set setpoint velocities */ |
| 124 | + public void setSetpointVelocities(ChassisSpeeds desiredSpeeds, DriveControlMode controlMode) { |
| 125 | + m_setpointVelocity = ChassisSpeeds.fromFieldRelativeSpeeds(desiredSpeeds, getHeading()); // converts to field relative speeds |
| 126 | + setModuleStates( |
| 127 | + m_swerveKinematics.toSwerveModuleStates( |
| 128 | + ChassisSpeeds.discretize(m_setpointVelocity, 0.02)), |
| 129 | + controlMode |
| 130 | + ); |
| 131 | + } |
| 132 | + |
| 133 | + /** Set swerve module setpoints */ |
| 134 | + public void setModuleStates(SwerveModuleState[] states, DriveControlMode mode) { |
| 135 | + Arrays.stream(m_modules) |
| 136 | + .forEach((m) -> m.setDesiredSate(states[m.getIndex()], mode)); |
| 137 | + } |
| 138 | + |
| 139 | + /** Returns desired chassis velocities */ // ! |
| 140 | + public ChassisSpeeds getSetpointVelocity() { |
| 141 | + return new ChassisSpeeds(); |
| 142 | + } |
| 143 | + |
| 144 | + /** Get current velocity */ |
| 145 | + public ChassisSpeeds getVelocity() { |
| 146 | + return ChassisSpeeds.fromRobotRelativeSpeeds(m_swerveKinematics.toChassisSpeeds(getModuleStates()), getHeading()); |
| 147 | + } |
| 148 | + |
| 149 | + /** Get current heading */ |
| 150 | + public Rotation2d getHeading() { |
| 151 | + return Robot.isReal() ? m_gyroInputs.orientation.toRotation2d() : m_simulationHeading; |
| 152 | + } |
| 153 | + |
| 154 | + /** Get Swerve Module Positions */ |
| 155 | + public SwerveModulePosition[] getModulePoses() { |
| 156 | + return Arrays.stream(m_modules) |
| 157 | + .map(Module::getCurrentPose) |
| 158 | + .toArray(SwerveModulePosition[]::new); |
| 159 | + } |
| 160 | + |
| 161 | + /** Get Swerve Module States */ |
| 162 | + public SwerveModuleState[] getModuleStates() { |
| 163 | + return Arrays.stream(m_modules) |
| 164 | + .map(Module::getCurrentState) |
| 165 | + .toArray(SwerveModuleState[]::new); |
| 166 | + } |
26 | 167 | } |
0 commit comments