Files
YAGSL/swervelib/SwerveModule.java

301 lines
9.3 KiB
Java
Raw Normal View History

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;
import swervelib.simulation.SwerveModuleSimulation;
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;
/**
* Last velocity set for the swerve module.
*/
public double lastVelocity;
2023-01-29 21:18:52 -06:00
/**
* Simulated swerve module.
2023-01-29 21:18:52 -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)
{
// 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;
if (absoluteEncoder != null)
{
absoluteEncoder.factoryDefault();
absoluteEncoder.configure(moduleConfiguration.absoluteEncoderInverted);
2023-02-22 20:50:16 -06:00
angleMotor.setPosition(getAbsolutePosition() - angleOffset);
}
2023-01-29 21:18:52 -06:00
2023-02-13 14:37:05 -06:00
// Config angle motor/controller
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
if (SwerveDriveTelemetry.isSimulation)
2023-01-29 21:18:52 -06:00
{
simModule = new SwerveModuleSimulation();
2023-01-29 21:18:52 -06:00
}
lastAngle = getState().angle.getDegrees();
2023-03-06 21:19:01 -06:00
lastVelocity = getState().speedMetersPerSecond;
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
{
if (absoluteEncoder != null)
{
angleMotor.setPosition(getAbsolutePosition() - angleOffset);
}
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.
* @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
*/
public void setDesiredState(SwerveModuleState2 desiredState, boolean isOpenLoop, boolean force)
2023-01-29 21:18:52 -06:00
{
SwerveModuleState simpleState =
new SwerveModuleState(desiredState.speedMetersPerSecond, desiredState.angle);
2023-02-13 14:37:05 -06:00
simpleState = SwerveModuleState.optimize(simpleState, getState().angle);
desiredState =
new SwerveModuleState2(
simpleState.speedMetersPerSecond, simpleState.angle, desiredState.omegaRadPerSecond);
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;
if (velocity != lastVelocity)
{
driveMotor.setReference(velocity, feedforward.calculate(velocity));
}
lastVelocity = velocity;
2023-01-29 21:18:52 -06:00
}
double angle = desiredState.angle.getDegrees();
// If we are forcing the angle
if (force)
{
angleMotor.setReference(angle, Math.toDegrees(desiredState.omegaRadPerSecond) * configuration.angleKV);
} else
{
// Prevents module rotation if speed is less than 1%
angle = Math.abs(desiredState.speedMetersPerSecond) <= (configuration.maxSpeed * 0.01) ? lastAngle : angle;
// Prevent module rotation if angle is the same as the previous angle.
if (angle != lastAngle)
{
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
if (SwerveDriveTelemetry.isSimulation)
2023-02-13 14:37:05 -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
{
angleMotor.setReference(angle, configuration.angleKV);
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;
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());
omega = Math.toRadians(angleMotor.getVelocity());
2023-02-13 14:37:05 -06:00
} else
2023-01-29 21:18:52 -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
}
/**
* 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;
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
{
return simModule.getPosition();
2023-01-29 21:18:52 -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
}
/**
* 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
*/
public double getAbsolutePosition()
2023-01-29 21:18:52 -06:00
{
if (absoluteEncoder != null)
{
double angle = absoluteEncoder.getAbsolutePosition();
if (absoluteEncoder.readingError)
{
angle = getRelativePosition();
}
return angle;
}
return getRelativePosition();
2023-01-29 21:18:52 -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
*/
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
}