44
55package frc .robot .subsystems ;
66
7+ import static edu .wpi .first .units .Units .MetersPerSecondPerSecond ;
8+ import static edu .wpi .first .units .Units .RadiansPerSecondPerSecond ;
9+
710import com .studica .frc .AHRS ;
811import com .studica .frc .AHRS .NavXComType ;
912
1013import edu .wpi .first .math .MathUtil ;
1114import edu .wpi .first .math .VecBuilder ;
15+ import edu .wpi .first .math .Vector ;
1216import edu .wpi .first .math .controller .PIDController ;
1317import edu .wpi .first .math .estimator .SwerveDrivePoseEstimator ;
18+ import edu .wpi .first .math .filter .SlewRateLimiter ;
1419import edu .wpi .first .math .geometry .Pose2d ;
1520import edu .wpi .first .math .geometry .Rotation2d ;
1621import edu .wpi .first .math .kinematics .ChassisSpeeds ;
1722import edu .wpi .first .math .kinematics .SwerveDriveKinematics ;
1823import edu .wpi .first .math .kinematics .SwerveModulePosition ;
1924import edu .wpi .first .math .kinematics .SwerveModuleState ;
25+ import edu .wpi .first .math .numbers .N2 ;
26+ import edu .wpi .first .units .measure .AngularAcceleration ;
27+ import edu .wpi .first .units .measure .LinearAcceleration ;
2028import edu .wpi .first .wpilibj .Timer ;
2129import edu .wpi .first .wpilibj .smartdashboard .Field2d ;
2230import edu .wpi .first .wpilibj .smartdashboard .SmartDashboard ;
@@ -73,6 +81,13 @@ public class DriveSubsystem extends SubsystemBase {
7381
7482 private final Field2d m_field = new Field2d ();
7583
84+ // meters per second (per second)
85+ private Vector <N2 > m_prevTranSpeed ;
86+ private double m_maxLinearAcceleration ;
87+
88+ // radians per second
89+ private SlewRateLimiter m_rotationRateFilter ;
90+
7691 /** Creates a new DriveSubsystem. */
7792 public DriveSubsystem () {
7893 this .zeroHeading ();
@@ -97,12 +112,17 @@ public DriveSubsystem() {
97112 LimelightHelpers .SetIMUMode (VisionConstants .kLimelightName , VisionConstants .kIMUMode );
98113 }
99114
115+ m_prevTranSpeed = VecBuilder .fill (0 , 0 );
116+ m_maxLinearAcceleration = DriveConstants .kMaxLinearAcceleration .in (MetersPerSecondPerSecond );
117+
118+ m_rotationRateFilter = new SlewRateLimiter (DriveConstants .kMaxAngularAcceleration .in (RadiansPerSecondPerSecond ));
119+
100120 m_desiredStates = DriveConstants .kDriveKinematics .toSwerveModuleStates (new ChassisSpeeds ());
101121 }
102122
103- @ Override
104- public void periodic () {
105- // This method will be called once per scheduler run
123+ @ Override
124+ public void periodic () {
125+ // This method will be called once per scheduler run
106126
107127 m_swerveModulePositions = new SwerveModulePosition [] {
108128 m_frontLeft .getPosition (),
@@ -213,20 +233,72 @@ public void drive(double xSpeed, double ySpeed, double rotation, boolean fieldRe
213233 calculatedRotation = m_headingCorrectionPID .calculate (currentAngle );
214234 }
215235
216- // Depending on whether the robot is being driven in field relative, calculate
217- // the desired states for each of the modules
218- m_desiredStates = DriveConstants .kDriveKinematics .toSwerveModuleStates (
219- fieldRelative
220- ? ChassisSpeeds .fromFieldRelativeSpeeds (xSpeed , ySpeed , calculatedRotation ,
221- Robot .isReal () ? m_gyro .getRotation2d () : new Rotation2d (m_gyroAngle ))
222- : new ChassisSpeeds (xSpeed , ySpeed , calculatedRotation ));
223- }
236+ // Depending on whether the robot is being driven in field relative, calculate
237+ // the desired states for each of the modules
238+ m_desiredStates = DriveConstants .kDriveKinematics .toSwerveModuleStates (
239+ fieldRelative
240+ ? ChassisSpeeds .fromFieldRelativeSpeeds (xSpeed , ySpeed , calculatedRotation ,
241+ Robot .isReal () ? m_gyro .getRotation2d () : new Rotation2d (m_gyroAngle ))
242+ : new ChassisSpeeds (xSpeed , ySpeed , calculatedRotation ));
224243
225- /**
226- * Resets the odometry to the specified pose.
227- *
228- * @param pose The pose to which to set the odometry.
229- */
244+ SwerveDriveKinematics .desaturateWheelSpeeds (
245+ m_desiredStates , DriveConstants .kMaxSpeedMetersPerSecond );
246+
247+ // acceleration limits
248+
249+ // convert to chassis speeds for calculations
250+ ChassisSpeeds originalSpeeds = DriveConstants .kDriveKinematics .toChassisSpeeds (m_desiredStates );
251+
252+ // slew rate limiter only works in one dimension so it can't be used for
253+ // translation
254+
255+ // calculate speed vector delta magnitude
256+ final Vector <N2 > dv = VecBuilder
257+ .fill (originalSpeeds .vxMetersPerSecond , originalSpeeds .vyMetersPerSecond )
258+ .minus (m_prevTranSpeed );
259+
260+ final double dvMagnitude = dv .norm ();
261+
262+ // calculate maximum speed vector detla magnitude
263+ final double maxdvMagnitude = m_maxLinearAcceleration * Robot .kDefaultPeriod ;
264+
265+ // if dv exceeds maxdv, then add maxdv to the old velocity in the same direction as dv
266+ if (dvMagnitude > maxdvMagnitude ) {
267+ // calculate a vector with direction of dv (normalized) and magnitude maxdv
268+ final Vector <N2 > dvLimited = dv .times (maxdvMagnitude /dvMagnitude );
269+
270+ // calculate new velocity vector
271+ final Vector <N2 > newV = m_prevTranSpeed .plus (dvLimited );
272+
273+ // correct chassis speeds
274+ originalSpeeds .vxMetersPerSecond = newV .get (0 );
275+ originalSpeeds .vyMetersPerSecond = newV .get (1 );
276+
277+ // inverse kinematics back to module states
278+ m_desiredStates = DriveConstants .kDriveKinematics .toSwerveModuleStates (originalSpeeds );
279+ }
280+
281+ // recalculate chassis speed for rotation limits
282+ originalSpeeds = DriveConstants .kDriveKinematics .toChassisSpeeds (m_desiredStates );
283+
284+ // calculate new rotation speed
285+ final double limitedOmega = m_rotationRateFilter .calculate (originalSpeeds .omegaRadiansPerSecond );
286+
287+ // scale all modules to limit rotation
288+ final double rotationScalar = originalSpeeds .omegaRadiansPerSecond == 0 ? 1
289+ : limitedOmega / originalSpeeds .omegaRadiansPerSecond ;
290+ for (SwerveModuleState state : m_desiredStates ) {
291+ state .speedMetersPerSecond *= rotationScalar ;
292+ }
293+
294+ m_prevTranSpeed = VecBuilder .fill (originalSpeeds .vxMetersPerSecond , originalSpeeds .vyMetersPerSecond );
295+ }
296+
297+ /**
298+ * Resets the odometry to the specified pose.
299+ *
300+ * @param pose The pose to which to set the odometry.
301+ */
230302 public void resetOdometry (Pose2d pose ) {
231303 m_poseEstimator .resetPosition (
232304 Robot .isReal () ? m_gyro .getRotation2d () : new Rotation2d (m_gyroAngle ),
@@ -249,18 +321,31 @@ public void addVisionMeasurement(Pose2d pose, double timestamp) {
249321 m_poseEstimator .addVisionMeasurement (pose , timestamp );
250322 }
251323
252- /** Sets the module states every 10ms (100Hz), faster than the regular periodic loop */
253- public void fastPeriodic () {
254- SwerveDriveKinematics .desaturateWheelSpeeds (
255- m_desiredStates , DriveConstants .kMaxSpeedMetersPerSecond );
256- m_frontLeft .setDesiredState (m_desiredStates [0 ]);
257- m_frontRight .setDesiredState (m_desiredStates [1 ]);
258- m_rearLeft .setDesiredState (m_desiredStates [2 ]);
259- m_rearRight .setDesiredState (m_desiredStates [3 ]);
260-
261- // Takes the integral of the rotation speed to find the current angle for the
262- // simulator
263- m_gyroAngle += DriveConstants .kDriveKinematics .toChassisSpeeds (m_desiredStates ).omegaRadiansPerSecond
264- * Constants .kFastPeriodicPeriod ;
324+ /**
325+ * Changes the acceleration limits
326+ * @param linearAccelerationLimit translation acceleration limit
327+ * @param angularAccelerationLimit rotation acceleration limit
328+ */
329+ public void setAccelerationLimits (LinearAcceleration linearAccelerationLimit ,
330+ AngularAcceleration angularAccelerationLimit ) {
331+ m_maxLinearAcceleration = linearAccelerationLimit .in (MetersPerSecondPerSecond );
332+ m_rotationRateFilter = new SlewRateLimiter (angularAccelerationLimit .in (RadiansPerSecondPerSecond ),
333+ angularAccelerationLimit .in (RadiansPerSecondPerSecond ), m_rotationRateFilter .lastValue ());
265334 }
335+
336+ /**
337+ * Sets the module states every 10ms (100Hz), faster than the regular periodic
338+ * loop
339+ */
340+ public void fastPeriodic () {
341+ m_frontLeft .setDesiredState (m_desiredStates [0 ]);
342+ m_frontRight .setDesiredState (m_desiredStates [1 ]);
343+ m_rearLeft .setDesiredState (m_desiredStates [2 ]);
344+ m_rearRight .setDesiredState (m_desiredStates [3 ]);
345+
346+ // Takes the integral of the rotation speed to find the current angle for the
347+ // simulator
348+ m_gyroAngle += DriveConstants .kDriveKinematics .toChassisSpeeds (m_desiredStates ).omegaRadiansPerSecond
349+ * Constants .kFastPeriodicPeriod ;
350+ }
266351}
0 commit comments