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-02-13 14:37:05 -06:00
|
|
|
import edu.wpi.first.math.kinematics.SwerveModuleState;
|
|
|
|
|
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
2023-02-13 17:21:24 -06:00
|
|
|
import swervelib.encoders.SwerveAbsoluteEncoder;
|
|
|
|
|
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-02-13 14:37:05 -06:00
|
|
|
* Last angle set for the swerve module.
|
2023-01-29 21:18:52 -06:00
|
|
|
*/
|
2023-02-13 14:37:05 -06:00
|
|
|
public double lastAngle;
|
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-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-02-22 20:50:16 -06:00
|
|
|
angleMotor.setPosition(getAbsolutePosition() - angleOffset);
|
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
|
|
|
|
|
|
|
|
lastAngle = getState().angle.getDegrees();
|
2023-01-29 21:18:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-02-13 14:37:05 -06:00
|
|
|
* Synchronize the integrated angle encoder with the absolute encoder.
|
2023-01-29 21:18:52 -06:00
|
|
|
*/
|
2023-02-13 14:37:05 -06:00
|
|
|
public void synchronizeEncoders()
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2023-02-16 21:21:26 -06:00
|
|
|
if (absoluteEncoder != null)
|
|
|
|
|
{
|
2023-02-20 20:59:31 -06:00
|
|
|
angleMotor.setPosition(getAbsolutePosition() - angleOffset);
|
2023-02-16 21:21:26 -06:00
|
|
|
}
|
2023-01-29 21:18:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-02-13 14:37:05 -06:00
|
|
|
* Set the desired state of the swerve module.
|
|
|
|
|
*
|
|
|
|
|
* @param desiredState Desired swerve module state.
|
|
|
|
|
* @param isOpenLoop Whether to use open loop (direct percent) or direct velocity control.
|
2023-01-29 21:18:52 -06:00
|
|
|
*/
|
2023-02-13 14:37:05 -06:00
|
|
|
public void setDesiredState(SwerveModuleState2 desiredState, boolean isOpenLoop)
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2023-02-20 20:59:31 -06:00
|
|
|
SwerveModuleState simpleState =
|
|
|
|
|
new SwerveModuleState(desiredState.speedMetersPerSecond, desiredState.angle);
|
2023-02-13 14:37:05 -06:00
|
|
|
simpleState = SwerveModuleState.optimize(simpleState, getState().angle);
|
2023-02-20 20:59:31 -06:00
|
|
|
desiredState =
|
|
|
|
|
new SwerveModuleState2(
|
|
|
|
|
simpleState.speedMetersPerSecond, simpleState.angle, desiredState.omegaRadPerSecond);
|
2023-02-24 19:11:05 -06:00
|
|
|
if (SwerveDriveTelemetry.verbosity == TelemetryVerbosity.HIGH)
|
|
|
|
|
{
|
|
|
|
|
SmartDashboard.putNumber(
|
|
|
|
|
"Optimized " + moduleNumber + " Speed Setpoint: ", desiredState.speedMetersPerSecond);
|
|
|
|
|
SmartDashboard.putNumber(
|
|
|
|
|
"Optimized " + moduleNumber + " Angle Setpoint: ", desiredState.angle.getDegrees());
|
|
|
|
|
SmartDashboard.putNumber(
|
|
|
|
|
"Module " + moduleNumber + " Omega: ", Math.toDegrees(desiredState.omegaRadPerSecond));
|
|
|
|
|
}
|
2023-01-29 21:18:52 -06:00
|
|
|
|
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-02-13 14:37:05 -06:00
|
|
|
double velocity = desiredState.speedMetersPerSecond;
|
|
|
|
|
driveMotor.setReference(velocity, feedforward.calculate(velocity));
|
2023-01-29 21:18:52 -06:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
// Prevents module rotation if speed is less than 1%
|
2023-02-20 20:59:31 -06:00
|
|
|
double angle =
|
|
|
|
|
(Math.abs(desiredState.speedMetersPerSecond) <= (configuration.maxSpeed * 0.01)
|
|
|
|
|
? lastAngle
|
|
|
|
|
: desiredState.angle.getDegrees());
|
|
|
|
|
angleMotor.setReference(
|
|
|
|
|
angle, Math.toDegrees(desiredState.omegaRadPerSecond) * configuration.angleKV);
|
2023-02-13 14:37:05 -06:00
|
|
|
lastAngle = angle;
|
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-02-15 22:18:27 -06:00
|
|
|
lastAngle = 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-02-13 14:37:05 -06:00
|
|
|
* @return Absolute encoder angle in degrees.
|
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-02-21 22:38:14 -06:00
|
|
|
if (absoluteEncoder != null)
|
2023-02-20 20:59:31 -06:00
|
|
|
{
|
2023-02-21 22:38:14 -06:00
|
|
|
double angle = absoluteEncoder.getAbsolutePosition();
|
|
|
|
|
if (absoluteEncoder.readingError)
|
|
|
|
|
{
|
|
|
|
|
angle = getRelativePosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return angle;
|
2023-02-20 20:59:31 -06:00
|
|
|
}
|
|
|
|
|
|
2023-02-21 22:38:14 -06:00
|
|
|
return getRelativePosition();
|
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-02-13 14:37:05 -06:00
|
|
|
}
|