|
26 | 26 | import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
|
27 | 27 | import edu.wpi.first.math.kinematics.SwerveModulePosition;
|
28 | 28 | import edu.wpi.first.math.kinematics.SwerveModuleState;
|
29 |
| -import edu.wpi.first.math.util.Units; |
30 | 29 | import edu.wpi.first.wpilibj.DriverStation;
|
31 | 30 | import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
32 | 31 | import frc.robot.util.LocalADStarAK;
|
|
39 | 38 | public class Drive extends SubsystemBase {
|
40 | 39 |
|
41 | 40 |
|
42 |
| - private final GyroIO gyroIO; |
43 |
| - private final GyroIOInputsAutoLogged gyroInputs = new GyroIOInputsAutoLogged(); |
44 |
| - private final Module[] modules = new Module[4]; // FL, FR, BL, BR |
45 |
| - |
46 |
| - private SwerveDriveKinematics kinematics = new SwerveDriveKinematics(getModuleTranslations()); |
47 |
| - private Pose2d pose = new Pose2d(); |
48 |
| - private Rotation2d lastGyroRotation = new Rotation2d(); |
49 |
| - private boolean forceModuleAngleChange = false; |
50 |
| - |
51 |
| - public Drive( |
52 |
| - GyroIO gyroIO, |
53 |
| - ModuleIO flModuleIO, |
54 |
| - ModuleIO frModuleIO, |
55 |
| - ModuleIO blModuleIO, |
56 |
| - ModuleIO brModuleIO) { |
57 |
| - this.gyroIO = gyroIO; |
58 |
| - modules[0] = new Module(flModuleIO, 0); |
59 |
| - modules[1] = new Module(frModuleIO, 1); |
60 |
| - modules[2] = new Module(blModuleIO, 2); |
61 |
| - modules[3] = new Module(brModuleIO, 3); |
62 |
| - |
63 |
| - // Configure AutoBuilder for PathPlanner |
64 |
| - AutoBuilder.configureHolonomic( |
65 |
| - this::getPose, |
66 |
| - this::setPose, |
67 |
| - () -> kinematics.toChassisSpeeds(getModuleStates()), |
68 |
| - this::runVelocity, |
69 |
| - new HolonomicPathFollowerConfig( |
70 |
| - MAX_LINEAR_SPEED, DRIVE_BASE_RADIUS, new ReplanningConfig()), |
71 |
| - this); |
72 |
| - Pathfinding.setPathfinder(new LocalADStarAK()); |
73 |
| - PathPlannerLogging.setLogActivePathCallback( |
74 |
| - (activePath) -> { |
75 |
| - Logger.recordOutput( |
76 |
| - "Odometry/Trajectory", activePath.toArray(new Pose2d[activePath.size()])); |
77 |
| - }); |
78 |
| - PathPlannerLogging.setLogTargetPoseCallback( |
79 |
| - (targetPose) -> { |
80 |
| - Logger.recordOutput("Odometry/TrajectorySetpoint", targetPose); |
81 |
| - }); |
82 |
| - } |
83 |
| - |
84 |
| - public void periodic() { |
85 |
| - gyroIO.updateInputs(gyroInputs); |
86 |
| - Logger.processInputs("Drive/Gyro", gyroInputs); |
87 |
| - for (var module : modules) { |
88 |
| - module.periodic(); |
| 41 | + private final GyroIO gyroIO; |
| 42 | + private final GyroIOInputsAutoLogged gyroInputs = new GyroIOInputsAutoLogged(); |
| 43 | + private final Module[] modules = new Module[4]; // FL, FR, BL, BR |
| 44 | + |
| 45 | + private SwerveDriveKinematics kinematics = new SwerveDriveKinematics(getModuleTranslations()); |
| 46 | + private Pose2d pose = new Pose2d(); |
| 47 | + private Rotation2d lastGyroRotation = new Rotation2d(); |
| 48 | + private boolean forceModuleAngleChange = false; |
| 49 | + |
| 50 | + public Drive( |
| 51 | + GyroIO gyroIO, |
| 52 | + ModuleIO flModuleIO, |
| 53 | + ModuleIO frModuleIO, |
| 54 | + ModuleIO blModuleIO, |
| 55 | + ModuleIO brModuleIO) { |
| 56 | + this.gyroIO = gyroIO; |
| 57 | + modules[0] = new Module(flModuleIO, 0); |
| 58 | + modules[1] = new Module(frModuleIO, 1); |
| 59 | + modules[2] = new Module(blModuleIO, 2); |
| 60 | + modules[3] = new Module(brModuleIO, 3); |
| 61 | + |
| 62 | + // Configure AutoBuilder for PathPlanner |
| 63 | + AutoBuilder.configureHolonomic( |
| 64 | + this::getPose, |
| 65 | + this::setPose, |
| 66 | + () -> kinematics.toChassisSpeeds(getModuleStates()), |
| 67 | + this::runVelocity, |
| 68 | + new HolonomicPathFollowerConfig(MAX_LINEAR_SPEED, DRIVE_BASE_RADIUS, new ReplanningConfig()), |
| 69 | + () -> DriverStation.getAlliance().isPresent() && DriverStation.getAlliance().get() == DriverStation.Alliance.Red, |
| 70 | + this); |
| 71 | + Pathfinding.setPathfinder(new LocalADStarAK()); |
| 72 | + PathPlannerLogging.setLogActivePathCallback( |
| 73 | + (activePath) -> { |
| 74 | + Logger.recordOutput( |
| 75 | + "Odometry/Trajectory", activePath.toArray(new Pose2d[activePath.size()])); |
| 76 | + }); |
| 77 | + PathPlannerLogging.setLogTargetPoseCallback( |
| 78 | + (targetPose) -> { |
| 79 | + Logger.recordOutput("Odometry/TrajectorySetpoint", targetPose); |
| 80 | + }); |
89 | 81 | }
|
90 | 82 |
|
91 |
| - // Stop moving when disabled |
92 |
| - if (DriverStation.isDisabled()) { |
93 |
| - for (var module : modules) { |
94 |
| - module.stop(); |
95 |
| - } |
| 83 | + public void periodic() { |
| 84 | + gyroIO.updateInputs(gyroInputs); |
| 85 | + Logger.processInputs("Drive/Gyro", gyroInputs); |
| 86 | + for (var module : modules) { |
| 87 | + module.periodic(); |
| 88 | + } |
| 89 | + |
| 90 | + // Stop moving when disabled |
| 91 | + if (DriverStation.isDisabled()) { |
| 92 | + for (var module : modules) { |
| 93 | + module.stop(); |
| 94 | + } |
| 95 | + } |
| 96 | + // Log empty setpoint states when disabled |
| 97 | + if (DriverStation.isDisabled()) { |
| 98 | + Logger.recordOutput("SwerveStates/Setpoints", new SwerveModuleState[]{}); |
| 99 | + Logger.recordOutput("SwerveStates/SetpointsOptimized", new SwerveModuleState[]{}); |
| 100 | + } |
| 101 | + |
| 102 | + // Update odometry |
| 103 | + SwerveModulePosition[] wheelDeltas = new SwerveModulePosition[4]; |
| 104 | + for (int i = 0; i < 4; i++) { |
| 105 | + wheelDeltas[i] = modules[i].getPositionDelta(); |
| 106 | + } |
| 107 | + // The twist represents the motion of the robot since the last |
| 108 | + // loop cycle in x, y, and theta based only on the modules, |
| 109 | + // without the gyro. The gyro is always disconnected in simulation. |
| 110 | + var twist = kinematics.toTwist2d(wheelDeltas); |
| 111 | + if (gyroInputs.connected) { |
| 112 | + // If the gyro is connected, replace the theta component of the twist |
| 113 | + // with the change in angle since the last loop cycle. |
| 114 | + twist = |
| 115 | + new Twist2d( |
| 116 | + twist.dx, twist.dy, gyroInputs.yawPosition.minus(lastGyroRotation).getRadians()); |
| 117 | + lastGyroRotation = gyroInputs.yawPosition; |
| 118 | + } |
| 119 | + // Apply the twist (change since last loop cycle) to the current pose |
| 120 | + pose = pose.exp(twist); |
96 | 121 | }
|
97 |
| - // Log empty setpoint states when disabled |
98 |
| - if (DriverStation.isDisabled()) { |
99 |
| - Logger.recordOutput("SwerveStates/Setpoints", new SwerveModuleState[] {}); |
100 |
| - Logger.recordOutput("SwerveStates/SetpointsOptimized", new SwerveModuleState[] {}); |
| 122 | + |
| 123 | + /** |
| 124 | + * Runs the drive at the desired velocity. |
| 125 | + * |
| 126 | + * @param speeds Speeds in meters/sec |
| 127 | + */ |
| 128 | + public void runVelocity(ChassisSpeeds speeds) { |
| 129 | + // Calculate module setpoints |
| 130 | + ChassisSpeeds discreteSpeeds = ChassisSpeeds.discretize(speeds, 0.02); |
| 131 | + SwerveModuleState[] setpointStates = kinematics.toSwerveModuleStates(discreteSpeeds); |
| 132 | + SwerveDriveKinematics.desaturateWheelSpeeds(setpointStates, MAX_LINEAR_SPEED); |
| 133 | + |
| 134 | + // Send setpoints to modules |
| 135 | + SwerveModuleState[] optimizedSetpointStates = new SwerveModuleState[4]; |
| 136 | + for (int i = 0; i < 4; i++) { |
| 137 | + // The module returns the optimized state, useful for logging |
| 138 | + optimizedSetpointStates[i] = modules[i].runSetpoint(setpointStates[i], forceModuleAngleChange); |
| 139 | + } |
| 140 | + // Log setpoint states |
| 141 | + Logger.recordOutput("SwerveStates/Setpoints", setpointStates); |
| 142 | + Logger.recordOutput("SwerveStates/SetpointsOptimized", optimizedSetpointStates); |
| 143 | + } |
| 144 | + |
| 145 | + /** |
| 146 | + * Stops the drive. |
| 147 | + */ |
| 148 | + public void stop() { |
| 149 | + runVelocity(new ChassisSpeeds()); |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * Stops the drive and turns the modules to an X arrangement to resist movement. The modules will |
| 154 | + * return to their normal orientations the next time a nonzero velocity is requested. |
| 155 | + */ |
| 156 | + public void stopWithX() { |
| 157 | + Rotation2d[] headings = new Rotation2d[4]; |
| 158 | + for (int i = 0; i < 4; i++) { |
| 159 | + headings[i] = getModuleTranslations()[i].getAngle(); |
| 160 | + } |
| 161 | + forceModuleAngleChange = true; |
| 162 | + kinematics.resetHeadings(headings); |
| 163 | + stop(); |
| 164 | + forceModuleAngleChange = false; |
| 165 | + } |
| 166 | + |
| 167 | + /** |
| 168 | + * Runs forwards at the commanded voltage. |
| 169 | + */ |
| 170 | + public void runCharacterizationVolts(double volts) { |
| 171 | + for (int i = 0; i < 4; i++) { |
| 172 | + modules[i].runCharacterization(volts); |
| 173 | + } |
101 | 174 | }
|
102 | 175 |
|
103 |
| - // Update odometry |
104 |
| - SwerveModulePosition[] wheelDeltas = new SwerveModulePosition[4]; |
105 |
| - for (int i = 0; i < 4; i++) { |
106 |
| - wheelDeltas[i] = modules[i].getPositionDelta(); |
| 176 | + /** |
| 177 | + * Returns the average drive velocity in radians/sec. |
| 178 | + */ |
| 179 | + public double getCharacterizationVelocity() { |
| 180 | + double driveVelocityAverage = 0.0; |
| 181 | + for (var module : modules) { |
| 182 | + driveVelocityAverage += module.getCharacterizationVelocity(); |
| 183 | + } |
| 184 | + return driveVelocityAverage / 4.0; |
107 | 185 | }
|
108 |
| - // The twist represents the motion of the robot since the last |
109 |
| - // loop cycle in x, y, and theta based only on the modules, |
110 |
| - // without the gyro. The gyro is always disconnected in simulation. |
111 |
| - var twist = kinematics.toTwist2d(wheelDeltas); |
112 |
| - if (gyroInputs.connected) { |
113 |
| - // If the gyro is connected, replace the theta component of the twist |
114 |
| - // with the change in angle since the last loop cycle. |
115 |
| - twist = |
116 |
| - new Twist2d( |
117 |
| - twist.dx, twist.dy, gyroInputs.yawPosition.minus(lastGyroRotation).getRadians()); |
118 |
| - lastGyroRotation = gyroInputs.yawPosition; |
| 186 | + |
| 187 | + /** |
| 188 | + * Returns the module states (turn angles and drive velocities) for all of the modules. |
| 189 | + */ |
| 190 | + @AutoLogOutput(key = "SwerveStates/Measured") |
| 191 | + private SwerveModuleState[] getModuleStates() { |
| 192 | + SwerveModuleState[] states = new SwerveModuleState[4]; |
| 193 | + for (int i = 0; i < 4; i++) { |
| 194 | + states[i] = modules[i].getState(); |
| 195 | + } |
| 196 | + return states; |
119 | 197 | }
|
120 |
| - // Apply the twist (change since last loop cycle) to the current pose |
121 |
| - pose = pose.exp(twist); |
122 |
| - } |
123 |
| - |
124 |
| - /** |
125 |
| - * Runs the drive at the desired velocity. |
126 |
| - * |
127 |
| - * @param speeds Speeds in meters/sec |
128 |
| - */ |
129 |
| - public void runVelocity(ChassisSpeeds speeds) { |
130 |
| - // Calculate module setpoints |
131 |
| - ChassisSpeeds discreteSpeeds = ChassisSpeeds.discretize(speeds, 0.02); |
132 |
| - SwerveModuleState[] setpointStates = kinematics.toSwerveModuleStates(discreteSpeeds); |
133 |
| - SwerveDriveKinematics.desaturateWheelSpeeds(setpointStates, MAX_LINEAR_SPEED); |
134 |
| - |
135 |
| - // Send setpoints to modules |
136 |
| - SwerveModuleState[] optimizedSetpointStates = new SwerveModuleState[4]; |
137 |
| - for (int i = 0; i < 4; i++) { |
138 |
| - // The module returns the optimized state, useful for logging |
139 |
| - optimizedSetpointStates[i] = modules[i].runSetpoint(setpointStates[i], forceModuleAngleChange); |
| 198 | + |
| 199 | + /** |
| 200 | + * Returns the current odometry pose. |
| 201 | + */ |
| 202 | + @AutoLogOutput(key = "Odometry/Robot") |
| 203 | + public Pose2d getPose() { |
| 204 | + return pose; |
140 | 205 | }
|
141 |
| - // Log setpoint states |
142 |
| - Logger.recordOutput("SwerveStates/Setpoints", setpointStates); |
143 |
| - Logger.recordOutput("SwerveStates/SetpointsOptimized", optimizedSetpointStates); |
144 |
| - } |
145 |
| - |
146 |
| - /** Stops the drive. */ |
147 |
| - public void stop() { |
148 |
| - runVelocity(new ChassisSpeeds()); |
149 |
| - } |
150 |
| - |
151 |
| - /** |
152 |
| - * Stops the drive and turns the modules to an X arrangement to resist movement. The modules will |
153 |
| - * return to their normal orientations the next time a nonzero velocity is requested. |
154 |
| - */ |
155 |
| - public void stopWithX() { |
156 |
| - Rotation2d[] headings = new Rotation2d[4]; |
157 |
| - for (int i = 0; i < 4; i++) { |
158 |
| - headings[i] = getModuleTranslations()[i].getAngle(); |
| 206 | + |
| 207 | + /** |
| 208 | + * Returns the current odometry rotation. |
| 209 | + */ |
| 210 | + public Rotation2d getRotation() { |
| 211 | + return pose.getRotation(); |
159 | 212 | }
|
160 |
| - forceModuleAngleChange = true; |
161 |
| - kinematics.resetHeadings(headings); |
162 |
| - stop(); |
163 |
| - forceModuleAngleChange = false; |
164 |
| - } |
165 |
| - |
166 |
| - /** Runs forwards at the commanded voltage. */ |
167 |
| - public void runCharacterizationVolts(double volts) { |
168 |
| - for (int i = 0; i < 4; i++) { |
169 |
| - modules[i].runCharacterization(volts); |
| 213 | + |
| 214 | + /** |
| 215 | + * Resets the current odometry pose. |
| 216 | + */ |
| 217 | + public void setPose(Pose2d pose) { |
| 218 | + this.pose = pose; |
170 | 219 | }
|
171 |
| - } |
172 | 220 |
|
173 |
| - /** Returns the average drive velocity in radians/sec. */ |
174 |
| - public double getCharacterizationVelocity() { |
175 |
| - double driveVelocityAverage = 0.0; |
176 |
| - for (var module : modules) { |
177 |
| - driveVelocityAverage += module.getCharacterizationVelocity(); |
| 221 | + /** |
| 222 | + * Returns the maximum linear speed in meters per sec. |
| 223 | + */ |
| 224 | + public double getMaxLinearSpeedMetersPerSec() { |
| 225 | + return MAX_LINEAR_SPEED; |
178 | 226 | }
|
179 |
| - return driveVelocityAverage / 4.0; |
180 |
| - } |
181 |
| - |
182 |
| - /** Returns the module states (turn angles and drive velocities) for all of the modules. */ |
183 |
| - @AutoLogOutput(key = "SwerveStates/Measured") |
184 |
| - private SwerveModuleState[] getModuleStates() { |
185 |
| - SwerveModuleState[] states = new SwerveModuleState[4]; |
186 |
| - for (int i = 0; i < 4; i++) { |
187 |
| - states[i] = modules[i].getState(); |
| 227 | + |
| 228 | + /** |
| 229 | + * Returns the maximum angular speed in radians per sec. |
| 230 | + */ |
| 231 | + public double getMaxAngularSpeedRadPerSec() { |
| 232 | + return MAX_ANGULAR_SPEED; |
| 233 | + } |
| 234 | + |
| 235 | + /** |
| 236 | + * Returns an array of module translations. |
| 237 | + */ |
| 238 | + public static Translation2d[] getModuleTranslations() { |
| 239 | + return new Translation2d[]{ |
| 240 | + new Translation2d(TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0), |
| 241 | + new Translation2d(TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0), |
| 242 | + new Translation2d(-TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0), |
| 243 | + new Translation2d(-TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0) |
| 244 | + }; |
188 | 245 | }
|
189 |
| - return states; |
190 |
| - } |
191 |
| - |
192 |
| - /** Returns the current odometry pose. */ |
193 |
| - @AutoLogOutput(key = "Odometry/Robot") |
194 |
| - public Pose2d getPose() { |
195 |
| - return pose; |
196 |
| - } |
197 |
| - |
198 |
| - /** Returns the current odometry rotation. */ |
199 |
| - public Rotation2d getRotation() { |
200 |
| - return pose.getRotation(); |
201 |
| - } |
202 |
| - |
203 |
| - /** Resets the current odometry pose. */ |
204 |
| - public void setPose(Pose2d pose) { |
205 |
| - this.pose = pose; |
206 |
| - } |
207 |
| - |
208 |
| - /** Returns the maximum linear speed in meters per sec. */ |
209 |
| - public double getMaxLinearSpeedMetersPerSec() { |
210 |
| - return MAX_LINEAR_SPEED; |
211 |
| - } |
212 |
| - |
213 |
| - /** Returns the maximum angular speed in radians per sec. */ |
214 |
| - public double getMaxAngularSpeedRadPerSec() { |
215 |
| - return MAX_ANGULAR_SPEED; |
216 |
| - } |
217 |
| - |
218 |
| - /** Returns an array of module translations. */ |
219 |
| - public static Translation2d[] getModuleTranslations() { |
220 |
| - return new Translation2d[] { |
221 |
| - new Translation2d(TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0), |
222 |
| - new Translation2d(TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0), |
223 |
| - new Translation2d(-TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0), |
224 |
| - new Translation2d(-TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0) |
225 |
| - }; |
226 |
| - } |
227 | 246 | }
|
0 commit comments