2023-02-13 17:21:24 -06:00
package swervelib ;
2023-01-29 21:18:52 -06:00
import edu.wpi.first.math.controller.SimpleMotorFeedforward ;
import edu.wpi.first.math.geometry.Rotation2d ;
import edu.wpi.first.math.kinematics.SwerveModulePosition ;
2023-03-29 07:24:24 -05:00
import edu.wpi.first.math.util.Units ;
2023-02-13 14:37:05 -06:00
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard ;
2023-02-13 17:21:24 -06:00
import swervelib.encoders.SwerveAbsoluteEncoder ;
2023-03-29 07:24:24 -05:00
import swervelib.math.SwerveMath ;
2023-02-13 17:21:24 -06:00
import swervelib.math.SwerveModuleState2 ;
import swervelib.motors.SwerveMotor ;
import swervelib.parser.SwerveModuleConfiguration ;
2023-02-15 22:18:27 -06:00
import swervelib.simulation.SwerveModuleSimulation ;
2023-02-24 19:11:05 -06:00
import swervelib.telemetry.SwerveDriveTelemetry ;
import swervelib.telemetry.SwerveDriveTelemetry.TelemetryVerbosity ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* The Swerve Module class which represents and controls Swerve Modules for the swerve drive .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
public class SwerveModule
2023-01-29 21:18:52 -06:00
{
/ * *
2023-02-13 14:37:05 -06:00
* Swerve module configuration options .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
public final SwerveModuleConfiguration configuration ;
2023-02-01 16:48:31 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* Angle offset from the absolute encoder .
2023-02-01 16:48:31 -06:00
* /
2023-02-13 14:37:05 -06:00
private final double angleOffset ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* Swerve Motors .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
private final SwerveMotor angleMotor , driveMotor ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* Absolute encoder for swerve drive .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
private final SwerveAbsoluteEncoder absoluteEncoder ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* Module number for kinematics , usually 0 to 3 . front left - > front right - > back left - > back right .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
public int moduleNumber ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* Feedforward for drive motor during closed loop control .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
public SimpleMotorFeedforward feedforward ;
2023-01-29 21:18:52 -06:00
/ * *
2023-03-29 07:24:24 -05:00
* Last swerve module state applied .
2023-01-29 21:18:52 -06:00
* /
2023-03-29 07:24:24 -05:00
public SwerveModuleState2 lastState ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-15 22:18:27 -06:00
* Simulated swerve module .
2023-01-29 21:18:52 -06:00
* /
2023-02-15 22:18:27 -06:00
private SwerveModuleSimulation simModule ;
2023-03-15 21:49:24 -05:00
/ * *
* Encoder synchronization queued .
* /
private boolean synchronizeEncoderQueued = false ;
2023-01-29 21:18:52 -06:00
/ * *
2023-02-13 14:37:05 -06:00
* Construct the swerve module and initialize the swerve module motors and absolute encoder .
2023-01-29 21:18:52 -06:00
*
2023-02-13 14:37:05 -06:00
* @param moduleNumber Module number for kinematics .
* @param moduleConfiguration Module constants containing CAN ID ' s and offsets .
* /
public SwerveModule ( int moduleNumber , SwerveModuleConfiguration moduleConfiguration )
{
2023-02-20 20:59:31 -06:00
// angle = 0;
// speed = 0;
// omega = 0;
// fakePos = 0;
2023-02-13 14:37:05 -06:00
this . moduleNumber = moduleNumber ;
configuration = moduleConfiguration ;
angleOffset = moduleConfiguration . angleOffset ;
// Initialize Feedforward for drive motor.
feedforward = configuration . createDriveFeedforward ( ) ;
// Create motors from configuration and reset them to defaults.
angleMotor = moduleConfiguration . angleMotor ;
driveMotor = moduleConfiguration . driveMotor ;
angleMotor . factoryDefaults ( ) ;
driveMotor . factoryDefaults ( ) ;
// Configure voltage comp, current limit, and ramp rate.
angleMotor . setVoltageCompensation ( configuration . physicalCharacteristics . optimalVoltage ) ;
driveMotor . setVoltageCompensation ( configuration . physicalCharacteristics . optimalVoltage ) ;
angleMotor . setCurrentLimit ( configuration . physicalCharacteristics . angleMotorCurrentLimit ) ;
driveMotor . setCurrentLimit ( configuration . physicalCharacteristics . driveMotorCurrentLimit ) ;
angleMotor . setLoopRampRate ( configuration . physicalCharacteristics . angleMotorRampRate ) ;
driveMotor . setLoopRampRate ( configuration . physicalCharacteristics . driveMotorRampRate ) ;
// Config angle encoders
absoluteEncoder = moduleConfiguration . absoluteEncoder ;
2023-02-16 21:21:26 -06:00
if ( absoluteEncoder ! = null )
{
absoluteEncoder . factoryDefault ( ) ;
absoluteEncoder . configure ( moduleConfiguration . absoluteEncoderInverted ) ;
2023-03-15 21:49:24 -05:00
angleMotor . setPosition ( getAbsolutePosition ( ) ) ;
2023-02-16 21:21:26 -06:00
}
2023-01-29 21:18:52 -06:00
2023-02-13 14:37:05 -06:00
// Config angle motor/controller
2023-02-21 22:38:14 -06:00
angleMotor . configureIntegratedEncoder (
2023-02-22 20:50:16 -06:00
moduleConfiguration . getPositionEncoderConversion ( false ) ) ;
2023-02-13 14:37:05 -06:00
angleMotor . configurePIDF ( moduleConfiguration . anglePIDF ) ;
angleMotor . configurePIDWrapping ( - 180 , 180 ) ;
angleMotor . setInverted ( moduleConfiguration . angleMotorInverted ) ;
angleMotor . setMotorBrake ( false ) ;
2023-01-29 21:18:52 -06:00
2023-02-13 14:37:05 -06:00
// Config drive motor/controller
driveMotor . configureIntegratedEncoder ( moduleConfiguration . getPositionEncoderConversion ( true ) ) ;
driveMotor . configurePIDF ( moduleConfiguration . velocityPIDF ) ;
driveMotor . setInverted ( moduleConfiguration . driveMotorInverted ) ;
driveMotor . setMotorBrake ( true ) ;
2023-01-29 21:18:52 -06:00
2023-02-13 14:37:05 -06:00
driveMotor . burnFlash ( ) ;
angleMotor . burnFlash ( ) ;
2023-01-29 21:18:52 -06:00
2023-02-24 19:11:05 -06:00
if ( SwerveDriveTelemetry . isSimulation )
2023-01-29 21:18:52 -06:00
{
2023-02-15 22:18:27 -06:00
simModule = new SwerveModuleSimulation ( ) ;
2023-01-29 21:18:52 -06:00
}
2023-02-15 22:18:27 -06:00
2023-03-29 07:24:24 -05:00
lastState = getState ( ) ;
2023-01-29 21:18:52 -06:00
}
/ * *
2023-03-15 21:49:24 -05:00
* Queue synchronization of the integrated angle encoder with the absolute encoder .
2023-01-29 21:18:52 -06:00
* /
2023-03-15 21:49:24 -05:00
public void queueSynchronizeEncoders ( )
2023-01-29 21:18:52 -06:00
{
2023-02-16 21:21:26 -06:00
if ( absoluteEncoder ! = null )
{
2023-03-15 21:49:24 -05:00
synchronizeEncoderQueued = true ;
2023-02-16 21:21:26 -06:00
}
2023-01-29 21:18:52 -06:00
}
/ * *
2023-03-15 21:49:24 -05:00
* Set the desired state of the swerve module . < br / > < b > WARNING : If you are not using one of the functions from
* { @link SwerveDrive } you may screw up { @link SwerveDrive # kinematics } < / b >
2023-02-13 14:37:05 -06:00
*
* @param desiredState Desired swerve module state .
* @param isOpenLoop Whether to use open loop ( direct percent ) or direct velocity control .
2023-03-09 21:48:47 -06:00
* @param force Disables optimizations that prevent movement in the angle motor and forces the desired state
* onto the swerve module .
2023-01-29 21:18:52 -06:00
* /
2023-03-09 21:48:47 -06:00
public void setDesiredState ( SwerveModuleState2 desiredState , boolean isOpenLoop , boolean force )
2023-01-29 21:18:52 -06:00
{
2023-03-29 07:24:24 -05:00
// SwerveModuleState simpleState =
// new SwerveModuleState(desiredState.speedMetersPerSecond, desiredState.angle);
// simpleState = SwerveModuleState.optimize(simpleState, getState().angle);
// desiredState =
// new SwerveModuleState2(
// simpleState.speedMetersPerSecond, simpleState.angle, desiredState.omegaRadPerSecond);
// Taken from https://github.com/pittsfordrobotics/REVSwerve2023/blob/a13156d573b6390c2130edb741cc7381c4d31583/src/main/java/com/team3181/frc2023/subsystems/swerve/Swerve.java#L101
desiredState = SwerveMath . optimize ( desiredState , getState ( ) . angle ,
Units . radiansToDegrees ( lastState . omegaRadPerSecond * configuration . angleKV ) *
0 . 065 ) ; // I am unsure of what the 0.065 represents
2023-02-13 14:37:05 -06:00
if ( isOpenLoop )
2023-01-29 21:18:52 -06:00
{
2023-02-13 14:37:05 -06:00
double percentOutput = desiredState . speedMetersPerSecond / configuration . maxSpeed ;
driveMotor . set ( percentOutput ) ;
2023-01-29 21:18:52 -06:00
} else
{
2023-03-29 07:24:24 -05:00
if ( desiredState . speedMetersPerSecond ! = lastState . speedMetersPerSecond )
2023-03-06 20:55:49 -06:00
{
2023-03-29 07:24:24 -05:00
double velocity = desiredState . speedMetersPerSecond ;
2023-03-06 20:55:49 -06:00
driveMotor . setReference ( velocity , feedforward . calculate ( velocity ) ) ;
}
2023-01-29 21:18:52 -06:00
}
2023-03-09 21:48:47 -06:00
// If we are forcing the angle
2023-03-20 19:20:26 -05:00
if ( ! force )
2023-03-06 20:55:49 -06:00
{
2023-03-09 21:48:47 -06:00
// Prevents module rotation if speed is less than 1%
2023-03-29 07:24:24 -05:00
SwerveMath . antiJitter ( desiredState , lastState , configuration . maxSpeed ) ;
2023-03-30 23:28:07 -05:00
} else
{
desiredState . omegaRadPerSecond = 0 ;
2023-03-21 22:18:24 -05:00
}
2023-03-21 22:36:46 -05:00
if ( SwerveDriveTelemetry . verbosity = = TelemetryVerbosity . HIGH )
{
SmartDashboard . putNumber (
" Optimized " + moduleNumber + " Speed Setpoint: " , desiredState . speedMetersPerSecond ) ;
SmartDashboard . putNumber (
2023-03-29 07:24:24 -05:00
" Optimized " + moduleNumber + " Angle Setpoint: " , desiredState . angle . getDegrees ( ) ) ;
2023-03-21 22:36:46 -05:00
SmartDashboard . putNumber (
" Module " + moduleNumber + " Omega: " , Math . toDegrees ( desiredState . omegaRadPerSecond ) ) ;
}
2023-03-20 19:20:26 -05:00
// Prevent module rotation if angle is the same as the previous angle.
2023-03-29 07:24:24 -05:00
if ( desiredState . angle ! = lastState . angle | | synchronizeEncoderQueued )
2023-03-20 19:20:26 -05:00
{
// Synchronize encoders if queued and send in the current position as the value from the absolute encoder.
2023-03-30 23:28:07 -05:00
double feedforward = Math . toDegrees ( desiredState . omegaRadPerSecond ) * configuration . angleKV ;
System . out . println ( ( float ) ( Math . toDegrees ( 0 ) * configuration . angleKV ) ) ;
2023-03-20 19:20:26 -05:00
if ( absoluteEncoder ! = null & & synchronizeEncoderQueued )
{
double absoluteEncoderPosition = getAbsolutePosition ( ) ;
angleMotor . setPosition ( absoluteEncoderPosition ) ;
2023-03-29 07:24:24 -05:00
angleMotor . setReference ( desiredState . angle . getDegrees ( ) ,
2023-03-30 23:28:07 -05:00
feedforward ,
2023-03-20 19:20:26 -05:00
absoluteEncoderPosition ) ;
synchronizeEncoderQueued = false ;
} else
2023-03-09 21:48:47 -06:00
{
2023-03-29 07:24:24 -05:00
angleMotor . setReference ( desiredState . angle . getDegrees ( ) ,
2023-03-30 23:28:07 -05:00
feedforward ) ;
2023-03-09 21:48:47 -06:00
}
2023-03-06 20:55:49 -06:00
}
2023-03-29 07:24:24 -05:00
lastState = desiredState ;
2023-01-29 21:18:52 -06:00
2023-02-24 19:11:05 -06:00
if ( SwerveDriveTelemetry . isSimulation )
2023-02-13 14:37:05 -06:00
{
2023-02-15 22:18:27 -06:00
simModule . updateStateAndPosition ( desiredState ) ;
2023-02-13 14:37:05 -06:00
}
2023-01-29 21:18:52 -06:00
}
/ * *
2023-02-13 14:37:05 -06:00
* Set the angle for the module .
2023-01-29 21:18:52 -06:00
*
* @param angle Angle in degrees .
* /
2023-02-13 14:37:05 -06:00
public void setAngle ( double angle )
2023-01-29 21:18:52 -06:00
{
2023-02-21 22:38:14 -06:00
angleMotor . setReference ( angle , configuration . angleKV ) ;
2023-03-29 07:24:24 -05:00
lastState . angle = Rotation2d . fromDegrees ( angle ) ;
2023-01-29 21:18:52 -06:00
}
/ * *
2023-02-13 14:37:05 -06:00
* Get the Swerve Module state .
2023-01-29 21:18:52 -06:00
*
2023-02-13 14:37:05 -06:00
* @return Current SwerveModule state .
2023-01-29 21:18:52 -06:00
* /
public SwerveModuleState2 getState ( )
{
2023-02-13 14:37:05 -06:00
double velocity ;
Rotation2d azimuth ;
double omega ;
2023-02-24 19:11:05 -06:00
if ( ! SwerveDriveTelemetry . isSimulation )
2023-01-29 21:18:52 -06:00
{
2023-02-13 14:37:05 -06:00
velocity = driveMotor . getVelocity ( ) ;
azimuth = Rotation2d . fromDegrees ( angleMotor . getPosition ( ) ) ;
2023-02-14 22:37:45 -06:00
omega = Math . toRadians ( angleMotor . getVelocity ( ) ) ;
2023-02-13 14:37:05 -06:00
} else
2023-01-29 21:18:52 -06:00
{
2023-02-15 22:18:27 -06:00
return simModule . getState ( ) ;
2023-01-29 21:18:52 -06:00
}
2023-02-13 14:37:05 -06:00
return new SwerveModuleState2 ( velocity , azimuth , omega ) ;
2023-01-29 21:18:52 -06:00
}
2023-02-14 22:03:02 -06:00
/ * *
* Get the position of the swerve module .
*
* @return { @link SwerveModulePosition } of the swerve module .
* /
2023-02-13 14:37:05 -06:00
public SwerveModulePosition getPosition ( )
2023-01-29 21:18:52 -06:00
{
2023-02-13 14:37:05 -06:00
double position ;
Rotation2d azimuth ;
2023-02-24 19:11:05 -06:00
if ( ! SwerveDriveTelemetry . isSimulation )
2023-01-29 21:18:52 -06:00
{
2023-02-13 14:37:05 -06:00
position = driveMotor . getPosition ( ) ;
azimuth = Rotation2d . fromDegrees ( angleMotor . getPosition ( ) ) ;
2023-01-29 21:18:52 -06:00
} else
{
2023-02-15 22:18:27 -06:00
return simModule . getPosition ( ) ;
2023-01-29 21:18:52 -06:00
}
2023-02-24 19:11:05 -06:00
if ( SwerveDriveTelemetry . verbosity = = TelemetryVerbosity . HIGH )
{
SmartDashboard . putNumber ( " Module " + moduleNumber + " Angle " , azimuth . getDegrees ( ) ) ;
}
2023-02-13 14:37:05 -06:00
return new SwerveModulePosition ( position , azimuth ) ;
2023-01-29 21:18:52 -06:00
}
/ * *
2023-02-21 22:38:14 -06:00
* Get the absolute position . Falls back to relative position on reading failure .
2023-01-29 21:18:52 -06:00
*
2023-03-15 21:49:24 -05:00
* @return Absolute encoder angle in degrees in the range [ 0 , 360 ) .
2023-01-29 21:18:52 -06:00
* /
2023-02-14 22:58:34 -06:00
public double getAbsolutePosition ( )
2023-01-29 21:18:52 -06:00
{
2023-03-15 21:49:24 -05:00
double angle ;
2023-02-21 22:38:14 -06:00
if ( absoluteEncoder ! = null )
2023-02-20 20:59:31 -06:00
{
2023-03-15 21:49:24 -05:00
angle = absoluteEncoder . getAbsolutePosition ( ) - angleOffset ;
2023-02-21 22:38:14 -06:00
if ( absoluteEncoder . readingError )
{
angle = getRelativePosition ( ) ;
}
2023-03-15 21:49:24 -05:00
} else
{
angle = getRelativePosition ( ) ;
}
angle % = 360 ;
if ( angle < 0 . 0 )
{
angle + = 360 ;
2023-02-20 20:59:31 -06:00
}
2023-03-15 21:49:24 -05:00
return angle ;
2023-01-29 21:18:52 -06:00
}
/ * *
2023-02-14 22:58:34 -06:00
* Get the relative angle in degrees .
2023-01-29 21:18:52 -06:00
*
2023-02-13 14:37:05 -06:00
* @return Angle in degrees .
2023-01-29 21:18:52 -06:00
* /
2023-02-14 22:58:34 -06:00
public double getRelativePosition ( )
2023-01-29 21:18:52 -06:00
{
2023-02-13 14:37:05 -06:00
return angleMotor . getPosition ( ) ;
2023-01-29 21:18:52 -06:00
}
/ * *
2023-02-13 14:37:05 -06:00
* Set the brake mode .
2023-01-29 21:18:52 -06:00
*
2023-02-13 14:37:05 -06:00
* @param brake Set the brake mode .
2023-01-29 21:18:52 -06:00
* /
2023-02-13 14:37:05 -06:00
public void setMotorBrake ( boolean brake )
2023-01-29 21:18:52 -06:00
{
2023-02-13 14:37:05 -06:00
driveMotor . setMotorBrake ( brake ) ;
2023-01-29 21:18:52 -06:00
}
2023-03-20 19:20:26 -05:00
/ * *
* Get the angle { @link SwerveMotor } for the { @link SwerveModule } .
*
* @return { @link SwerveMotor } for the angle / steering motor of the module .
* /
public SwerveMotor getAngleMotor ( )
{
return angleMotor ;
}
/ * *
* Get the drive { @link SwerveMotor } for the { @link SwerveModule } .
*
* @return { @link SwerveMotor } for the drive motor of the module .
* /
public SwerveMotor getDriveMotor ( )
{
return driveMotor ;
}
/ * *
* Fetch the { @link SwerveModuleConfiguration } for the { @link SwerveModule } with the parsed configurations .
*
* @return { @link SwerveModuleConfiguration } for the { @link SwerveModule } .
* /
public SwerveModuleConfiguration getConfiguration ( )
{
return configuration ;
}
2023-02-13 14:37:05 -06:00
}