2023-02-13 17:21:24 -06:00
|
|
|
package swervelib;
|
2023-01-29 21:18:52 -06:00
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
import static edu.wpi.first.units.Units.MetersPerSecond;
|
|
|
|
|
import static edu.wpi.first.units.Units.RadiansPerSecond;
|
|
|
|
|
import static edu.wpi.first.units.Units.RotationsPerSecond;
|
|
|
|
|
|
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-08-29 21:03:58 -05:00
|
|
|
import edu.wpi.first.math.kinematics.SwerveModuleState;
|
2024-12-17 18:49:55 +00:00
|
|
|
import edu.wpi.first.networktables.BooleanPublisher;
|
|
|
|
|
import edu.wpi.first.networktables.DoublePublisher;
|
|
|
|
|
import edu.wpi.first.networktables.NetworkTableInstance;
|
2024-12-09 23:26:04 +00:00
|
|
|
import edu.wpi.first.units.measure.AngularVelocity;
|
|
|
|
|
import edu.wpi.first.units.measure.LinearVelocity;
|
|
|
|
|
import edu.wpi.first.wpilibj.Alert;
|
|
|
|
|
import edu.wpi.first.wpilibj.Alert.AlertType;
|
2023-02-13 14:37:05 -06:00
|
|
|
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
2024-12-09 23:26:04 +00:00
|
|
|
import swervelib.encoders.SparkMaxEncoderSwerve;
|
2023-02-13 17:21:24 -06:00
|
|
|
import swervelib.encoders.SwerveAbsoluteEncoder;
|
2023-03-29 07:24:24 -05:00
|
|
|
import swervelib.math.SwerveMath;
|
2024-12-09 23:26:04 +00:00
|
|
|
import swervelib.motors.SparkMaxBrushedMotorSwerve;
|
|
|
|
|
import swervelib.motors.SparkMaxSwerve;
|
2023-02-13 17:21:24 -06:00
|
|
|
import swervelib.motors.SwerveMotor;
|
2024-02-02 18:55:29 -06:00
|
|
|
import swervelib.parser.Cache;
|
2024-02-28 13:25:59 -06:00
|
|
|
import swervelib.parser.PIDFConfig;
|
2023-02-13 17:21:24 -06:00
|
|
|
import swervelib.parser.SwerveModuleConfiguration;
|
2024-12-09 23:26:04 +00:00
|
|
|
import swervelib.parser.SwerveModulePhysicalCharacteristics;
|
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;
|
2024-02-02 18:55:29 -06:00
|
|
|
/**
|
|
|
|
|
* Absolute encoder position cache.
|
|
|
|
|
*/
|
2024-02-06 16:03:08 -06:00
|
|
|
public final Cache<Double> absolutePositionCache;
|
2024-02-02 18:55:29 -06:00
|
|
|
/**
|
|
|
|
|
* Drive motor position cache.
|
|
|
|
|
*/
|
2024-02-06 16:03:08 -06:00
|
|
|
public final Cache<Double> drivePositionCache;
|
2024-02-02 18:55:29 -06:00
|
|
|
/**
|
|
|
|
|
* Drive motor velocity cache.
|
|
|
|
|
*/
|
2024-02-06 16:03:08 -06:00
|
|
|
public final Cache<Double> driveVelocityCache;
|
2024-08-24 17:27:03 -05:00
|
|
|
/**
|
|
|
|
|
* Module number for kinematics, usually 0 to 3. front left -> front right -> back left -> back right.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
public final int moduleNumber;
|
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;
|
2024-01-17 09:17:39 -06:00
|
|
|
/**
|
|
|
|
|
* An {@link Alert} for if pushing the Absolute Encoder offset to the encoder fails.
|
|
|
|
|
*/
|
|
|
|
|
private final Alert encoderOffsetWarning;
|
|
|
|
|
/**
|
|
|
|
|
* An {@link Alert} for if there is no Absolute Encoder on the module.
|
|
|
|
|
*/
|
|
|
|
|
private final Alert noEncoderWarning;
|
2024-02-12 18:59:40 -06:00
|
|
|
/**
|
2024-12-17 18:49:55 +00:00
|
|
|
* NT4 Raw Absolute Angle publisher for the absolute encoder.
|
2024-02-12 18:59:40 -06:00
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final DoublePublisher rawAbsoluteAnglePublisher;
|
2024-02-12 18:59:40 -06:00
|
|
|
/**
|
2024-12-17 18:49:55 +00:00
|
|
|
* NT4 Adjusted Absolute angle publisher for the absolute encoder.
|
2024-02-12 18:59:40 -06:00
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final DoublePublisher adjAbsoluteAnglePublisher;
|
2024-02-12 18:59:40 -06:00
|
|
|
/**
|
2024-12-17 18:49:55 +00:00
|
|
|
* NT4 Absolute encoder read issue.
|
2024-02-12 18:59:40 -06:00
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final BooleanPublisher absoluteEncoderIssuePublisher;
|
2024-02-12 18:59:40 -06:00
|
|
|
/**
|
2024-12-17 18:49:55 +00:00
|
|
|
* NT4 raw angle motor.
|
2024-02-12 18:59:40 -06:00
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final DoublePublisher rawAnglePublisher;
|
2024-02-12 18:59:40 -06:00
|
|
|
/**
|
2024-12-17 18:49:55 +00:00
|
|
|
* NT4 Raw drive motor.
|
2024-02-12 18:59:40 -06:00
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final DoublePublisher rawDriveEncoderPublisher;
|
2024-06-12 15:10:29 -05:00
|
|
|
/**
|
2024-12-17 18:49:55 +00:00
|
|
|
* NT4 Raw drive motor.
|
2024-06-12 15:10:29 -05:00
|
|
|
*/
|
2024-12-17 18:49:55 +00:00
|
|
|
private final DoublePublisher rawDriveVelocityPublisher;
|
|
|
|
|
/**
|
|
|
|
|
* Speed setpoint publisher for the module motor-controller PID.
|
|
|
|
|
*/
|
|
|
|
|
private final DoublePublisher speedSetpointPublisher;
|
|
|
|
|
/**
|
|
|
|
|
* Angle setpoint publisher for the module motor-controller PID.
|
|
|
|
|
*/
|
|
|
|
|
private final DoublePublisher angleSetpointPublisher;
|
2023-11-09 17:32:48 -06:00
|
|
|
/**
|
2024-12-09 23:26:04 +00:00
|
|
|
* Maximum {@link LinearVelocity} for the drive motor of the swerve module.
|
|
|
|
|
*/
|
|
|
|
|
private LinearVelocity maxDriveVelocity;
|
|
|
|
|
/**
|
|
|
|
|
* Maximum {@link AngularVelocity} for the azimuth/angle motor of the swerve module.
|
2023-11-09 17:32:48 -06:00
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
private AngularVelocity maxAngularVelocity;
|
2024-06-12 15:58:37 -05:00
|
|
|
/**
|
|
|
|
|
* Feedforward for the drive motor during closed loop control.
|
|
|
|
|
*/
|
|
|
|
|
private SimpleMotorFeedforward driveMotorFeedforward;
|
2024-02-28 13:25:59 -06:00
|
|
|
/**
|
|
|
|
|
* Anti-Jitter AKA auto-centering disabled.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
private boolean antiJitterEnabled = true;
|
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
|
|
|
*/
|
2024-01-26 12:45:09 -06:00
|
|
|
private SwerveModuleState lastState;
|
2023-12-12 10:48:54 -06:00
|
|
|
/**
|
|
|
|
|
* Angle offset from the absolute encoder.
|
|
|
|
|
*/
|
2024-01-15 14:37:13 -06:00
|
|
|
private double angleOffset;
|
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-12-12 10:48:54 -06:00
|
|
|
private SwerveModuleSimulation simModule;
|
2023-03-15 21:49:24 -05:00
|
|
|
/**
|
|
|
|
|
* Encoder synchronization queued.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
private boolean synchronizeEncoderQueued = false;
|
2024-10-14 13:22:11 -05:00
|
|
|
/**
|
|
|
|
|
* Encoder, Absolute encoder synchronization enabled.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
private boolean synchronizeEncoderEnabled = false;
|
2024-10-14 13:22:11 -05:00
|
|
|
/**
|
|
|
|
|
* Encoder synchronization deadband in degrees.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
private double synchronizeEncoderDeadband = 3;
|
2023-01-29 21:18:52 -06:00
|
|
|
|
2024-02-02 18:55:29 -06:00
|
|
|
|
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.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
public SwerveModule(int moduleNumber, SwerveModuleConfiguration moduleConfiguration)
|
2023-02-13 14:37:05 -06:00
|
|
|
{
|
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;
|
|
|
|
|
|
|
|
|
|
// Create motors from configuration and reset them to defaults.
|
|
|
|
|
angleMotor = moduleConfiguration.angleMotor;
|
|
|
|
|
driveMotor = moduleConfiguration.driveMotor;
|
|
|
|
|
angleMotor.factoryDefaults();
|
|
|
|
|
driveMotor.factoryDefaults();
|
|
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
// Initialize Feedforwards.
|
|
|
|
|
driveMotorFeedforward = getDefaultFeedforward();
|
|
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
// 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-01-29 21:18:52 -06:00
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
if (SwerveDriveTelemetry.isSimulation)
|
|
|
|
|
{
|
|
|
|
|
simModule = new SwerveModuleSimulation();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-02 18:55:29 -06:00
|
|
|
// Setup the cache for the absolute encoder position.
|
2024-12-09 23:26:04 +00:00
|
|
|
absolutePositionCache = new Cache<>(this::getRawAbsolutePosition, 20);
|
2024-02-02 18:55:29 -06:00
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
// Config angle motor/controller
|
2024-12-09 23:26:04 +00:00
|
|
|
angleMotor.configureIntegratedEncoder(moduleConfiguration.conversionFactors.angle.factor);
|
2023-02-13 14:37:05 -06:00
|
|
|
angleMotor.configurePIDF(moduleConfiguration.anglePIDF);
|
2024-12-09 23:26:04 +00:00
|
|
|
angleMotor.configurePIDWrapping(0, 360);
|
2023-02-13 14:37:05 -06:00
|
|
|
angleMotor.setInverted(moduleConfiguration.angleMotorInverted);
|
|
|
|
|
angleMotor.setMotorBrake(false);
|
2023-01-29 21:18:52 -06:00
|
|
|
|
2024-01-26 11:29:15 -06:00
|
|
|
// Set the position AFTER settings the conversion factor.
|
|
|
|
|
if (absoluteEncoder != null)
|
|
|
|
|
{
|
|
|
|
|
angleMotor.setPosition(getAbsolutePosition());
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
// Config drive motor/controller
|
2024-12-09 23:26:04 +00:00
|
|
|
driveMotor.configureIntegratedEncoder(moduleConfiguration.conversionFactors.drive.factor);
|
2023-02-13 14:37:05 -06:00
|
|
|
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
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
drivePositionCache = new Cache<>(driveMotor::getPosition, 20);
|
|
|
|
|
driveVelocityCache = new Cache<>(driveMotor::getVelocity, 20);
|
2023-02-15 22:18:27 -06:00
|
|
|
|
2024-02-02 18:55:29 -06:00
|
|
|
// Force a cache update on init.
|
|
|
|
|
driveVelocityCache.update();
|
|
|
|
|
drivePositionCache.update();
|
|
|
|
|
absolutePositionCache.update();
|
|
|
|
|
|
|
|
|
|
// Save the current state.
|
2023-03-29 07:24:24 -05:00
|
|
|
lastState = getState();
|
2024-01-17 09:17:39 -06:00
|
|
|
|
|
|
|
|
noEncoderWarning = new Alert("Motors",
|
|
|
|
|
"There is no Absolute Encoder on module #" +
|
|
|
|
|
moduleNumber,
|
2024-12-09 23:26:04 +00:00
|
|
|
AlertType.kWarning);
|
2024-01-17 09:17:39 -06:00
|
|
|
encoderOffsetWarning = new Alert("Motors",
|
|
|
|
|
"Pushing the Absolute Encoder offset to the encoder failed on module #" +
|
|
|
|
|
moduleNumber,
|
2024-12-09 23:26:04 +00:00
|
|
|
AlertType.kWarning);
|
2024-02-12 18:59:40 -06:00
|
|
|
|
2025-01-06 15:44:15 +00:00
|
|
|
rawAbsoluteAnglePublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Raw Absolute Encoder").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
adjAbsoluteAnglePublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Adjusted Absolute Encoder").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
absoluteEncoderIssuePublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getBooleanTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Absolute Encoder Read Issue").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
rawAnglePublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Raw Angle Encoder").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
rawDriveEncoderPublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Raw Drive Encoder").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
rawDriveVelocityPublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Raw Drive Velocity").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
speedSetpointPublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Speed Setpoint").publish();
|
2025-01-06 15:44:15 +00:00
|
|
|
angleSetpointPublisher = NetworkTableInstance.getDefault().getTable("SmartDashboard").getDoubleTopic(
|
2024-12-17 18:49:55 +00:00
|
|
|
"swerve/modules/" + configuration.name + "/Angle Setpoint").publish();
|
2023-01-29 21:18:52 -06:00
|
|
|
}
|
|
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
/**
|
|
|
|
|
* Get the default {@link SimpleMotorFeedforward} for the swerve module drive motor.
|
|
|
|
|
*
|
|
|
|
|
* @return {@link SimpleMotorFeedforward} using motor details.
|
|
|
|
|
*/
|
|
|
|
|
public SimpleMotorFeedforward getDefaultFeedforward()
|
|
|
|
|
{
|
|
|
|
|
double nominalVoltage = driveMotor.getSimMotor().nominalVoltageVolts;
|
|
|
|
|
double maxDriveSpeedMPS = getMaxVelocity().in(MetersPerSecond);
|
|
|
|
|
return SwerveMath.createDriveFeedforward(nominalVoltage,
|
|
|
|
|
maxDriveSpeedMPS,
|
|
|
|
|
configuration.physicalCharacteristics.wheelGripCoefficientOfFriction);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 17:32:48 -06:00
|
|
|
/**
|
|
|
|
|
* Set the voltage compensation for the swerve module motor.
|
|
|
|
|
*
|
|
|
|
|
* @param optimalVoltage Nominal voltage for operation to output to.
|
|
|
|
|
*/
|
|
|
|
|
public void setAngleMotorVoltageCompensation(double optimalVoltage)
|
|
|
|
|
{
|
|
|
|
|
angleMotor.setVoltageCompensation(optimalVoltage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the voltage compensation for the swerve module motor.
|
|
|
|
|
*
|
|
|
|
|
* @param optimalVoltage Nominal voltage for operation to output to.
|
|
|
|
|
*/
|
|
|
|
|
public void setDriveMotorVoltageCompensation(double optimalVoltage)
|
|
|
|
|
{
|
|
|
|
|
driveMotor.setVoltageCompensation(optimalVoltage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2024-10-14 13:22:11 -05:00
|
|
|
if (absoluteEncoder != null && synchronizeEncoderEnabled)
|
2023-02-16 21:21:26 -06:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
|
2024-10-14 13:22:11 -05:00
|
|
|
/**
|
2024-11-06 00:10:07 +00:00
|
|
|
* Enable auto synchronization for encoders during a match. This will only occur when the modules are not moving for a
|
|
|
|
|
* few seconds.
|
|
|
|
|
*
|
|
|
|
|
* @param enabled Enable state
|
2024-10-14 13:22:11 -05:00
|
|
|
* @param deadband Deadband in degrees, default is 3 degrees.
|
|
|
|
|
*/
|
|
|
|
|
public void setEncoderAutoSynchronize(boolean enabled, double deadband)
|
|
|
|
|
{
|
|
|
|
|
synchronizeEncoderEnabled = enabled;
|
|
|
|
|
synchronizeEncoderDeadband = deadband;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-11-06 00:10:07 +00:00
|
|
|
* Enable auto synchronization for encoders during a match. This will only occur when the modules are not moving for a
|
|
|
|
|
* few seconds.
|
|
|
|
|
*
|
2024-10-14 13:22:11 -05:00
|
|
|
* @param enabled Enable state
|
|
|
|
|
*/
|
|
|
|
|
public void setEncoderAutoSynchronize(boolean enabled)
|
|
|
|
|
{
|
|
|
|
|
synchronizeEncoderEnabled = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-28 13:25:59 -06:00
|
|
|
/**
|
|
|
|
|
* Set the antiJitter functionality, if true the modules will NOT auto center. Pushes the offsets to the angle motor
|
|
|
|
|
* controllers as well.
|
|
|
|
|
*
|
|
|
|
|
* @param antiJitter Anti-Jitter state desired.
|
|
|
|
|
*/
|
|
|
|
|
public void setAntiJitter(boolean antiJitter)
|
|
|
|
|
{
|
|
|
|
|
this.antiJitterEnabled = antiJitter;
|
|
|
|
|
if (antiJitter)
|
|
|
|
|
{
|
2024-06-12 15:10:29 -05:00
|
|
|
pushOffsetsToEncoders();
|
2024-02-28 13:25:59 -06:00
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
restoreInternalOffset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the feedforward attributes to the given parameters.
|
|
|
|
|
*
|
|
|
|
|
* @param drive Drive motor feedforward for the module.
|
|
|
|
|
*/
|
|
|
|
|
public void setFeedforward(SimpleMotorFeedforward drive)
|
|
|
|
|
{
|
|
|
|
|
this.driveMotorFeedforward = drive;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 15:58:37 -05:00
|
|
|
/**
|
|
|
|
|
* Get the current drive motor PIDF values.
|
|
|
|
|
*
|
|
|
|
|
* @return {@link PIDFConfig} of the drive motor.
|
|
|
|
|
*/
|
|
|
|
|
public PIDFConfig getDrivePIDF()
|
|
|
|
|
{
|
|
|
|
|
return configuration.velocityPIDF;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-28 13:25:59 -06:00
|
|
|
/**
|
|
|
|
|
* Set the drive PIDF values.
|
|
|
|
|
*
|
|
|
|
|
* @param config {@link PIDFConfig} of that should be set.
|
|
|
|
|
*/
|
|
|
|
|
public void setDrivePIDF(PIDFConfig config)
|
|
|
|
|
{
|
|
|
|
|
configuration.velocityPIDF = config;
|
|
|
|
|
driveMotor.configurePIDF(config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-06-12 15:58:37 -05:00
|
|
|
* Get the current angle/azimuth/steering motor PIDF values.
|
2024-02-28 13:25:59 -06:00
|
|
|
*
|
2024-06-12 15:58:37 -05:00
|
|
|
* @return {@link PIDFConfig} of the angle motor.
|
2024-02-28 13:25:59 -06:00
|
|
|
*/
|
2024-06-12 15:58:37 -05:00
|
|
|
public PIDFConfig getAnglePIDF()
|
2024-02-28 13:25:59 -06:00
|
|
|
{
|
2024-06-12 15:58:37 -05:00
|
|
|
return configuration.anglePIDF;
|
2024-02-28 13:25:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the angle/azimuth/steering motor PID
|
|
|
|
|
*
|
|
|
|
|
* @param config {@link PIDFConfig} of that should be set.
|
|
|
|
|
*/
|
|
|
|
|
public void setAnglePIDF(PIDFConfig config)
|
|
|
|
|
{
|
|
|
|
|
configuration.anglePIDF = config;
|
|
|
|
|
angleMotor.configurePIDF(config);
|
|
|
|
|
}
|
|
|
|
|
|
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-08-29 21:03:58 -05:00
|
|
|
public void setDesiredState(SwerveModuleState desiredState, boolean isOpenLoop, boolean force)
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2024-12-09 23:26:04 +00:00
|
|
|
|
|
|
|
|
desiredState.optimize(Rotation2d.fromDegrees(getAbsolutePosition()));
|
2024-02-17 08:13:53 -06:00
|
|
|
|
|
|
|
|
// If we are forcing the angle
|
2024-02-28 13:25:59 -06:00
|
|
|
if (!force && antiJitterEnabled)
|
2024-02-17 08:13:53 -06:00
|
|
|
{
|
|
|
|
|
// Prevents module rotation if speed is less than 1%
|
2024-12-09 23:26:04 +00:00
|
|
|
SwerveMath.antiJitter(desiredState, lastState, Math.min(maxDriveVelocity.in(MetersPerSecond), 4));
|
2024-02-17 08:13:53 -06:00
|
|
|
}
|
|
|
|
|
|
2024-01-26 12:45:09 -06:00
|
|
|
// Cosine compensation.
|
2024-12-09 23:26:04 +00:00
|
|
|
LinearVelocity nextVelocity = configuration.useCosineCompensator
|
|
|
|
|
? getCosineCompensatedVelocity(desiredState)
|
|
|
|
|
: MetersPerSecond.of(desiredState.speedMetersPerSecond);
|
|
|
|
|
LinearVelocity curVelocity = MetersPerSecond.of(lastState.speedMetersPerSecond);
|
|
|
|
|
desiredState.speedMetersPerSecond = nextVelocity.magnitude();
|
|
|
|
|
|
2025-01-06 15:44:15 +00:00
|
|
|
setDesiredState(desiredState,
|
|
|
|
|
isOpenLoop,
|
|
|
|
|
driveMotorFeedforward.calculateWithVelocities(curVelocity.in(MetersPerSecond),
|
|
|
|
|
nextVelocity.in(MetersPerSecond)));
|
2024-12-09 23:26:04 +00: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>
|
|
|
|
|
*
|
|
|
|
|
* @param desiredState Desired swerve module state.
|
|
|
|
|
* @param isOpenLoop Whether to use open loop (direct percent) or direct velocity control.
|
|
|
|
|
* @param driveFeedforwardVoltage Drive motor controller feedforward as a voltage.
|
|
|
|
|
*/
|
|
|
|
|
public void setDesiredState(SwerveModuleState desiredState, boolean isOpenLoop,
|
|
|
|
|
double driveFeedforwardVoltage)
|
|
|
|
|
{
|
2023-04-08 12:31:07 -05:00
|
|
|
|
2023-02-13 14:37:05 -06:00
|
|
|
if (isOpenLoop)
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2024-12-09 23:26:04 +00:00
|
|
|
double percentOutput = desiredState.speedMetersPerSecond / maxDriveVelocity.in(MetersPerSecond);
|
2025-01-06 15:44:15 +00:00
|
|
|
driveMotor.setVoltage(percentOutput * 12);
|
2023-01-29 21:18:52 -06:00
|
|
|
} else
|
|
|
|
|
{
|
2024-12-09 23:26:04 +00:00
|
|
|
driveMotor.setReference(desiredState.speedMetersPerSecond, driveFeedforwardVoltage);
|
2023-01-29 21:18:52 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-20 19:20:26 -05:00
|
|
|
// Prevent module rotation if angle is the same as the previous angle.
|
2024-01-15 14:37:13 -06:00
|
|
|
// Synchronize encoders if queued and send in the current position as the value from the absolute encoder.
|
2024-10-14 13:22:11 -05:00
|
|
|
if (absoluteEncoder != null && synchronizeEncoderQueued && synchronizeEncoderEnabled)
|
2023-03-20 19:20:26 -05:00
|
|
|
{
|
2024-01-15 14:37:13 -06:00
|
|
|
double absoluteEncoderPosition = getAbsolutePosition();
|
2024-11-06 00:10:07 +00:00
|
|
|
if (Math.abs(angleMotor.getPosition() - absoluteEncoderPosition) >= synchronizeEncoderDeadband)
|
|
|
|
|
{
|
2024-10-14 13:22:11 -05:00
|
|
|
angleMotor.setPosition(absoluteEncoderPosition);
|
|
|
|
|
}
|
2024-01-15 14:37:13 -06:00
|
|
|
angleMotor.setReference(desiredState.angle.getDegrees(), 0, absoluteEncoderPosition);
|
|
|
|
|
synchronizeEncoderQueued = false;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
angleMotor.setReference(desiredState.angle.getDegrees(), 0);
|
2023-03-06 20:55:49 -06:00
|
|
|
}
|
2023-04-08 12:31:07 -05: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
|
|
|
}
|
2024-01-15 14:37:13 -06:00
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
// TODO: Change and move to SwerveDriveTelemetry
|
2024-06-12 15:10:29 -05:00
|
|
|
if (SwerveDriveTelemetry.verbosity.ordinal() >= TelemetryVerbosity.INFO.ordinal())
|
2024-01-26 12:45:09 -06:00
|
|
|
{
|
2024-12-09 23:26:04 +00:00
|
|
|
SwerveDriveTelemetry.desiredStatesObj[moduleNumber] = desiredState;
|
2024-01-26 12:45:09 -06:00
|
|
|
}
|
|
|
|
|
|
2024-01-15 14:37:13 -06:00
|
|
|
if (SwerveDriveTelemetry.verbosity == TelemetryVerbosity.HIGH)
|
|
|
|
|
{
|
2024-12-17 18:49:55 +00:00
|
|
|
speedSetpointPublisher.set(desiredState.speedMetersPerSecond);
|
|
|
|
|
angleSetpointPublisher.set(desiredState.angle.getDegrees());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (moduleNumber == SwerveDriveTelemetry.moduleCount - 1)
|
|
|
|
|
{
|
|
|
|
|
SwerveDriveTelemetry.endCtrlCycle();
|
2024-01-15 14:37:13 -06:00
|
|
|
}
|
2023-01-29 21:18:52 -06:00
|
|
|
}
|
|
|
|
|
|
2024-01-26 12:45:09 -06:00
|
|
|
/**
|
|
|
|
|
* Get the cosine compensated velocity to set the swerve module to.
|
|
|
|
|
*
|
|
|
|
|
* @param desiredState Desired {@link SwerveModuleState} to use.
|
|
|
|
|
* @return Cosine compensated velocity in meters/second.
|
|
|
|
|
*/
|
2024-12-09 23:26:04 +00:00
|
|
|
private LinearVelocity getCosineCompensatedVelocity(SwerveModuleState desiredState)
|
2024-01-26 12:45:09 -06:00
|
|
|
{
|
|
|
|
|
double cosineScalar = 1.0;
|
|
|
|
|
// Taken from the CTRE SwerveModule class.
|
|
|
|
|
// https://api.ctr-electronics.com/phoenix6/release/java/src-html/com/ctre/phoenix6/mechanisms/swerve/SwerveModule.html#line.46
|
|
|
|
|
/* From FRC 900's whitepaper, we add a cosine compensator to the applied drive velocity */
|
|
|
|
|
/* To reduce the "skew" that occurs when changing direction */
|
|
|
|
|
/* If error is close to 0 rotations, we're already there, so apply full power */
|
|
|
|
|
/* If the error is close to 0.25 rotations, then we're 90 degrees, so movement doesn't help us at all */
|
2024-02-06 16:03:08 -06:00
|
|
|
cosineScalar = Rotation2d.fromDegrees(desiredState.angle.getDegrees())
|
2024-02-12 18:59:40 -06:00
|
|
|
.minus(Rotation2d.fromDegrees(getAbsolutePosition()))
|
|
|
|
|
.getCos(); // TODO: Investigate angle modulus by 180.
|
2024-01-26 12:45:09 -06:00
|
|
|
/* Make sure we don't invert our drive, even though we shouldn't ever target over 90 degrees anyway */
|
|
|
|
|
if (cosineScalar < 0.0)
|
|
|
|
|
{
|
2024-02-06 16:03:08 -06:00
|
|
|
cosineScalar = 1;
|
2024-01-26 12:45:09 -06:00
|
|
|
}
|
|
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
return MetersPerSecond.of(desiredState.speedMetersPerSecond).times(cosineScalar);
|
2024-01-26 12:45:09 -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-04-08 13:33:02 -05:00
|
|
|
angleMotor.setReference(angle, 0);
|
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
|
|
|
*/
|
2023-08-29 21:03:58 -05:00
|
|
|
public SwerveModuleState getState()
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2023-02-13 14:37:05 -06:00
|
|
|
double velocity;
|
|
|
|
|
Rotation2d azimuth;
|
2023-02-24 19:11:05 -06:00
|
|
|
if (!SwerveDriveTelemetry.isSimulation)
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2024-02-02 18:55:29 -06:00
|
|
|
velocity = driveVelocityCache.getValue();
|
2023-11-29 17:51:42 -06:00
|
|
|
azimuth = Rotation2d.fromDegrees(getAbsolutePosition());
|
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-08-29 21:03:58 -05:00
|
|
|
return new SwerveModuleState(velocity, azimuth);
|
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
|
|
|
{
|
2024-02-02 18:55:29 -06:00
|
|
|
position = drivePositionCache.getValue();
|
2023-11-29 17:49:06 -06:00
|
|
|
azimuth = Rotation2d.fromDegrees(getAbsolutePosition());
|
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-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()
|
2024-02-02 18:55:29 -06:00
|
|
|
{
|
|
|
|
|
return absolutePositionCache.getValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the absolute position. Falls back to relative position on reading failure.
|
|
|
|
|
*
|
|
|
|
|
* @return Absolute encoder angle in degrees in the range [0, 360).
|
|
|
|
|
*/
|
|
|
|
|
public double getRawAbsolutePosition()
|
2023-01-29 21:18:52 -06:00
|
|
|
{
|
2024-12-09 23:26:04 +00:00
|
|
|
/* During simulation, when no absolute encoders are available, we return the state from the simulation module instead. */
|
|
|
|
|
if (SwerveDriveTelemetry.isSimulation)
|
|
|
|
|
{
|
|
|
|
|
Rotation2d absolutePosition = simModule.getState().angle;
|
|
|
|
|
return absolutePosition.getDegrees();
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
2023-11-09 17:32:48 -06:00
|
|
|
/**
|
|
|
|
|
* Set the conversion factor for the angle/azimuth motor controller.
|
|
|
|
|
*
|
|
|
|
|
* @param conversionFactor Angle motor conversion factor for PID, should be generated from
|
|
|
|
|
* {@link SwerveMath#calculateDegreesPerSteeringRotation(double, double)} or calculated.
|
|
|
|
|
*/
|
|
|
|
|
public void setAngleMotorConversionFactor(double conversionFactor)
|
|
|
|
|
{
|
|
|
|
|
angleMotor.configureIntegratedEncoder(conversionFactor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the conversion factor for the drive motor controller.
|
|
|
|
|
*
|
|
|
|
|
* @param conversionFactor Drive motor conversion factor for PID, should be generated from
|
|
|
|
|
* {@link SwerveMath#calculateMetersPerRotation(double, double, double)} or calculated.
|
|
|
|
|
*/
|
|
|
|
|
public void setDriveMotorConversionFactor(double conversionFactor)
|
|
|
|
|
{
|
|
|
|
|
driveMotor.configureIntegratedEncoder(conversionFactor);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 12:09:37 -06:00
|
|
|
/**
|
|
|
|
|
* Get the {@link SwerveAbsoluteEncoder} for the {@link SwerveModule}.
|
|
|
|
|
*
|
|
|
|
|
* @return {@link SwerveAbsoluteEncoder} for the swerve module.
|
|
|
|
|
*/
|
|
|
|
|
public SwerveAbsoluteEncoder getAbsoluteEncoder()
|
|
|
|
|
{
|
|
|
|
|
return absoluteEncoder;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-20 19:20:26 -05:00
|
|
|
/**
|
|
|
|
|
* 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-11-20 20:34:28 -06:00
|
|
|
|
2023-12-12 10:48:54 -06:00
|
|
|
/**
|
|
|
|
|
* Push absolute encoder offset in the memory of the encoder or controller. Also removes the internal angle offset.
|
|
|
|
|
*/
|
2024-06-12 15:10:29 -05:00
|
|
|
public void pushOffsetsToEncoders()
|
2023-12-12 10:48:54 -06:00
|
|
|
{
|
2024-02-28 13:25:59 -06:00
|
|
|
if (absoluteEncoder != null && angleOffset == configuration.angleOffset)
|
2023-12-12 10:48:54 -06:00
|
|
|
{
|
2024-08-24 17:27:03 -05:00
|
|
|
// If the absolute encoder is attached.
|
2024-12-09 23:26:04 +00:00
|
|
|
if (angleMotor instanceof SparkMaxSwerve || angleMotor instanceof SparkMaxBrushedMotorSwerve)
|
2023-12-12 10:48:54 -06:00
|
|
|
{
|
2024-12-09 23:26:04 +00:00
|
|
|
if (absoluteEncoder instanceof SparkMaxEncoderSwerve)
|
2024-08-24 17:27:03 -05:00
|
|
|
{
|
|
|
|
|
angleMotor.setAbsoluteEncoder(absoluteEncoder);
|
|
|
|
|
if (absoluteEncoder.setAbsoluteEncoderOffset(angleOffset))
|
|
|
|
|
{
|
|
|
|
|
angleOffset = 0;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
angleMotor.setAbsoluteEncoder(null);
|
|
|
|
|
encoderOffsetWarning.set(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-12 10:48:54 -06:00
|
|
|
}
|
2024-08-24 17:27:03 -05:00
|
|
|
|
2023-12-12 10:48:54 -06:00
|
|
|
} else
|
|
|
|
|
{
|
2024-01-17 09:17:39 -06:00
|
|
|
noEncoderWarning.set(true);
|
2023-12-12 10:48:54 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Restore internal offset in YAGSL and either sets absolute encoder offset to 0 or restores old value.
|
|
|
|
|
*/
|
|
|
|
|
public void restoreInternalOffset()
|
|
|
|
|
{
|
2024-08-24 17:31:26 -05:00
|
|
|
angleMotor.setAbsoluteEncoder(null);
|
2023-12-12 10:48:54 -06:00
|
|
|
absoluteEncoder.setAbsoluteEncoderOffset(0);
|
|
|
|
|
angleOffset = configuration.angleOffset;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 17:36:08 -06:00
|
|
|
/**
|
|
|
|
|
* Get if the last Absolute Encoder had a read issue, such as it does not exist.
|
|
|
|
|
*
|
|
|
|
|
* @return If the last Absolute Encoder had a read issue, or absolute encoder does not exist.
|
2023-11-20 20:34:28 -06:00
|
|
|
*/
|
|
|
|
|
public boolean getAbsoluteEncoderReadIssue()
|
|
|
|
|
{
|
2023-12-12 10:48:54 -06:00
|
|
|
if (absoluteEncoder == null)
|
|
|
|
|
{
|
2023-11-29 17:36:08 -06:00
|
|
|
return true;
|
2023-12-12 10:48:54 -06:00
|
|
|
} else
|
|
|
|
|
{
|
2023-11-29 17:36:08 -06:00
|
|
|
return absoluteEncoder.readingError;
|
2023-12-12 10:48:54 -06:00
|
|
|
}
|
2023-11-20 20:34:28 -06:00
|
|
|
}
|
2024-01-15 14:37:13 -06:00
|
|
|
|
2024-12-09 23:26:04 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the maximum module velocity as a {@link LinearVelocity} based on the RPM and gear ratio.
|
|
|
|
|
*
|
|
|
|
|
* @return {@link LinearVelocity} max velocity of the drive wheel.
|
|
|
|
|
*/
|
|
|
|
|
public LinearVelocity getMaxVelocity()
|
|
|
|
|
{
|
|
|
|
|
if (maxDriveVelocity == null)
|
|
|
|
|
{
|
|
|
|
|
maxDriveVelocity = MetersPerSecond.of(
|
|
|
|
|
(RadiansPerSecond.of(driveMotor.getSimMotor().freeSpeedRadPerSec).in(RotationsPerSecond) /
|
|
|
|
|
configuration.conversionFactors.drive.gearRatio) *
|
|
|
|
|
configuration.conversionFactors.drive.diameter);
|
|
|
|
|
}
|
|
|
|
|
return maxDriveVelocity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the maximum module angular velocity as a {@link AngularVelocity} based on the RPM and gear ratio.
|
|
|
|
|
*
|
|
|
|
|
* @return {@link AngularVelocity} max velocity of the angle/azimuth.
|
|
|
|
|
*/
|
|
|
|
|
public AngularVelocity getMaxAngularVelocity()
|
|
|
|
|
{
|
|
|
|
|
if (maxAngularVelocity == null)
|
|
|
|
|
{
|
|
|
|
|
maxAngularVelocity = RotationsPerSecond.of(
|
|
|
|
|
RadiansPerSecond.of(angleMotor.getSimMotor().freeSpeedRadPerSec).in(RotationsPerSecond) *
|
|
|
|
|
configuration.conversionFactors.angle.gearRatio);
|
|
|
|
|
}
|
|
|
|
|
return maxAngularVelocity;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 14:37:13 -06:00
|
|
|
/**
|
|
|
|
|
* Update data sent to {@link SmartDashboard}.
|
|
|
|
|
*/
|
|
|
|
|
public void updateTelemetry()
|
|
|
|
|
{
|
|
|
|
|
if (absoluteEncoder != null)
|
|
|
|
|
{
|
2024-12-17 18:49:55 +00:00
|
|
|
rawAbsoluteAnglePublisher.set(absoluteEncoder.getAbsolutePosition());
|
2024-01-15 14:37:13 -06:00
|
|
|
}
|
2024-12-17 18:49:55 +00:00
|
|
|
rawAnglePublisher.set(angleMotor.getPosition());
|
|
|
|
|
rawDriveEncoderPublisher.set(drivePositionCache.getValue());
|
|
|
|
|
rawDriveVelocityPublisher.set(driveVelocityCache.getValue());
|
|
|
|
|
adjAbsoluteAnglePublisher.set(getAbsolutePosition());
|
|
|
|
|
absoluteEncoderIssuePublisher.set(getAbsoluteEncoderReadIssue());
|
2024-01-15 14:37:13 -06:00
|
|
|
}
|
2024-12-09 23:26:04 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invalidate the {@link Cache} objects used by {@link SwerveModule}.
|
|
|
|
|
*/
|
|
|
|
|
public void invalidateCache()
|
|
|
|
|
{
|
|
|
|
|
absolutePositionCache.update();
|
|
|
|
|
drivePositionCache.update();
|
|
|
|
|
driveVelocityCache.update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Obtains the {@link SwerveModuleSimulation} used in simulation.
|
|
|
|
|
*
|
|
|
|
|
* @return the module simulation, <b>null</b> if this method is called on a real robot
|
|
|
|
|
*/
|
|
|
|
|
public SwerveModuleSimulation getSimModule()
|
|
|
|
|
{
|
|
|
|
|
return simModule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configure the {@link SwerveModule#simModule} with the MapleSim
|
|
|
|
|
* {@link org.ironmaple.simulation.drivesims.SwerveModuleSimulation}
|
|
|
|
|
*
|
2025-01-06 15:44:15 +00:00
|
|
|
* @param swerveModuleSimulation MapleSim {@link org.ironmaple.simulation.drivesims.SwerveModuleSimulation} to
|
|
|
|
|
* configure with.
|
|
|
|
|
* @param physicalCharacteristics {@link SwerveModulePhysicalCharacteristics} that represent the swerve drive.
|
2024-12-09 23:26:04 +00:00
|
|
|
*/
|
|
|
|
|
public void configureModuleSimulation(
|
|
|
|
|
org.ironmaple.simulation.drivesims.SwerveModuleSimulation swerveModuleSimulation,
|
|
|
|
|
SwerveModulePhysicalCharacteristics physicalCharacteristics)
|
|
|
|
|
{
|
|
|
|
|
this.simModule.configureSimModule(swerveModuleSimulation, physicalCharacteristics);
|
|
|
|
|
}
|
2023-02-13 14:37:05 -06:00
|
|
|
}
|