Merge pull request #15 from FairportRobotics/feature/swervedrive

TalonFX Swerve Drive Support
This commit is contained in:
Tyler Wilcox 2026-02-01 17:41:05 -05:00 committed by GitHub
commit fae46eb9b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 862 additions and 18 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>RoboLib</name> <name>potential-octo-eureka-RoboLib</name>
<comment>Project RoboLib created by Buildship.</comment> <comment>Project RoboLib created by Buildship.</comment>
<projects> <projects>
</projects> </projects>

View File

@ -1,5 +1,5 @@
# Potential Octo Eureka(POE) # RoboLib
The official library for Team 578 R3 - Red Raider Robotics The official library for Team 578 R3 - Fairport Robotics
# Including in FRC Project # Including in FRC Project
To include this library in a Java FRC Robot project. To include this library in a Java FRC Robot project.

5
gradle.properties Normal file
View File

@ -0,0 +1,5 @@
wpiVersion = 2026.2.1
ctreVersion = 26.1.0
advantageKitVersion = 4.0.0

20
lib/.classpath Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="src/main/java">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="build/generated/sources/annotationProcessor/java/main"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

34
lib/.project Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>RoboLib-lib</name>
<comment>Project lib created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>1743377880685</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@ -25,14 +25,14 @@ repositories {
mavenLocal() mavenLocal()
gradlePluginPortal() gradlePluginPortal()
maven {url "https://maven.photonvision.org/repository/snapshots"}
maven {url "https://maven.photonvision.org/repository/internal"}
maven {url "https://maven.ctr-electronics.com/release/"} maven {url "https://maven.ctr-electronics.com/release/"}
maven {url "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/"}
} }
dependencies { dependencies {
// Use JUnit Jupiter for testing. // Use JUnit Jupiter for testing.
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// WPI Lib // WPI Lib
implementation "edu.wpi.first.cscore:cscore-java:$wpiVersion" implementation "edu.wpi.first.cscore:cscore-java:$wpiVersion"
@ -48,6 +48,9 @@ dependencies {
// CTRE Phoenix 6 // CTRE Phoenix 6
implementation "com.ctre.phoenix6:wpiapi-java:${ctreVersion}" implementation "com.ctre.phoenix6:wpiapi-java:${ctreVersion}"
// AdvantageKit
implementation "org.littletonrobotics.akit:akit-java:${advantageKitVersion}"
} }
tasks.named('test') { tasks.named('test') {

View File

@ -1,11 +0,0 @@
package org.fairportrobotics.frc.robolib.DriveSystems.SwerveDrive;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
public class SwerveDriveSubsystem extends SubsystemBase{
public SwerveDriveSubsystem(){
}
}

View File

@ -0,0 +1,344 @@
package org.fairportrobotics.frc.robolib.drivesystems.swerve;
import java.util.ArrayList;
import com.ctre.phoenix6.CANBus;
import edu.wpi.first.math.geometry.Translation2d;
public class SwerveBuilder {
private ArrayList<SwerveModule> modules = new ArrayList<SwerveModule>();
private CANBus canBusObj;
private double maxAngularVelRadiansSecond = 1.0;
private double maxLinearVelMetersSecond = Math.PI;
private int pigeonId;
public SwerveBuilder() {
}
/**
* Set the CAN ID of the Pigeon IMU
* @param pigeonId CAN Bus ID
* @return itself
*/
public SwerveBuilder withPigeonId(int pigeonId) {
this.pigeonId = pigeonId;
return this;
}
/**
* Set the CAN Bus name
* @param canBusname Name of the CAN Bus
* @return itself
*/
public SwerveBuilder withCanbusName(String canBusname) {
this.canBusObj = new CANBus(canBusname);
return this;
}
/**
* Add a SwerveModule
*
* Use the inner class SwerveModuleBuilder to construct a SwerveModule
* @param module
* @return itself
*/
public SwerveBuilder withSwerveModule(SwerveModule module) {
this.modules.add(module);
return this;
}
/**
* Set the max linear velocity in meters per second
*
* @param maxVel Max linear velocity in meters per second
* @return itself
*/
public SwerveBuilder withMaxLinearVelocity(double maxVel) {
this.maxLinearVelMetersSecond = maxVel;
return this;
}
/**
* Set the max angular velocity in radians per second
*
* @param maxVel Max angular velocity in radians per second
* @return itself
*/
public SwerveBuilder withMaxAngularVelocity(double maxVel) {
this.maxAngularVelRadiansSecond = maxVel;
return this;
}
/**
* Build the configured instance of a SwerveDriveSystem
* @return instance of SwerveDriveSystem
*/
public SwerveDriveSystem build() {
return new SwerveDriveSystem(pigeonId, canBusObj, maxLinearVelMetersSecond, maxAngularVelRadiansSecond,
modules.toArray(size -> new SwerveModule[size]));
}
public class SwerveModuleBuilder {
private int driveMotorId;
private double driveKP;
private double driveKI;
private double driveKD;
private double driveKV;
private boolean driveInverted = false;
private int steerMotorId;
private double steerKP;
private double steerKI;
private double steerKD;
private double steerKS;
private double steerKV;
private double steerKA;
private int steerEncoderId;
private double steerOffset;
private Translation2d moduleLocation;
private double gearRatio;
private double wheelDiameterInMeters;
private String moduleName;
public SwerveModuleBuilder() {
}
/**
* Set the drive motor CAN Bus ID
* @param driveMotorId ID of the drive motor
* @return itself
*/
public SwerveModuleBuilder withDriveMotorId(int driveMotorId) {
this.driveMotorId = driveMotorId;
return this;
}
/**
* Set the drive motors closed loop proportional gain value.
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
*
* @param driveKP Proportional gain in Volts
* @return itself
*/
public SwerveModuleBuilder withDriveKP(double driveKP) {
this.driveKP = driveKP;
return this;
}
/**
* Set the drive motors closed loop integral gain value.
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
*
* @param driveKP Integral gain in Volts
* @return itself
*/
public SwerveModuleBuilder withDriveKI(double driveKI) {
this.driveKI = driveKI;
return this;
}
/**
* Set the drive motors closed loop derrivative gain value.
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
*
* @param driveKP Derrivative gain in Volts
* @return itself
*/
public SwerveModuleBuilder withDriveKD(double driveKD) {
this.driveKD = driveKD;
return this;
}
/**
* Set the drive motors closed loop feed forward velocity gain.
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
* @param driveKV velocity gain in Volts
* @return itself
*/
public SwerveModuleBuilder withDriveKV(double driveKV) {
this.driveKV = driveKV;
return this;
}
/**
* Set the drive motors direction to be inverted.
* <br><br>
* When inverted positive values will be Counter Clockwise
*
* @return itself
*/
public SwerveModuleBuilder withDriveInverted(){
this.driveInverted = true;
return this;
}
/**
* Set the steer motors CAN Bus ID
* @param steerMotorId ID of steer motor
* @return itself
*/
public SwerveModuleBuilder withSteerMotorId(int steerMotorId) {
this.steerMotorId = steerMotorId;
return this;
}
/**
* Set the steer motors closed loop proportional gain value
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
* @param steerKP Proportional gain in Volts
* @return itself
*/
public SwerveModuleBuilder withSteerKP(double steerKP) {
this.steerKP = steerKP;
return this;
}
/**
* Set the steer motors closed loop integral gain value
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
* @param steerKP Integral gain in Volts
* @return itself
*/
public SwerveModuleBuilder withSteerKI(double steerKI) {
this.steerKI = steerKI;
return this;
}
/**
* Set the steer motors closed loop derrivative gain value
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
* @param steerKP Derrivative gain in Volts
* @return itself
*/
public SwerveModuleBuilder withSteerKD(double steerKD) {
this.steerKD = steerKD;
return this;
}
/**
* Set the steer motors closed loop feed forward static gain.
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
* @param steerKS Static gain in Volts
* @return itself
*/
public SwerveModuleBuilder withSteerKS(double steerKS) {
this.steerKS = steerKS;
return this;
}
/**
* Set the steer motors closed loop feed forward velocity gain.
* <br><br>
* Refer here for more info: https://v6.docs.ctr-electronics.com/en/latest/docs/api-reference/device-specific/talonfx/basic-pid-control.html
* @param steerKV velocity gain in Volts
* @return itself
*/
public SwerveModuleBuilder withSteerKV(double steerKV) {
this.steerKV = steerKV;
return this;
}
/**
* Set the steer encoders CAN Bus ID
* @param steerEncoderId ID of steer encoder
* @return itself
*/
public SwerveModuleBuilder withSteerEncoderId(int steerEncoderId) {
this.steerEncoderId = steerEncoderId;
return this;
}
/**
* Set the offset of the steer encoder.
* <br><br>
* This value can be found from Pheonix tuner.
* <br><br>
* Ensure the configured offset is 0.0 and then set this to
* be the Absolute Position when the wheel is properly aligned
* @param offsetRotations offset in the range of [-0.5, 0.5)
* @return itself
*/
public SwerveModuleBuilder withSteerOffset(double offsetRotations) {
this.steerOffset = offsetRotations;
return this;
}
/**
* Set the swerve modules location relative to the center of the robot.
* <br><br>
* Refer to https://docs.wpilib.org/en/stable/docs/software/basic-programming/coordinate-system.html
* for how the cordinate system works.
* <br><br>
* The location is in meters from the center of the robot.
*
* @param moduleLocation location of the swerve module
* @return
*/
public SwerveModuleBuilder withModuleLocation(Translation2d moduleLocation) {
this.moduleLocation = moduleLocation;
return this;
}
/**
* Set the gear ratio of the drive motor to the drive wheel.
* <br><br>
* This ratio is the number of rotations the motor has to make for one(1) rotation
* of the drive wheel.
* @param gearRatio the gear ratio
* @return itself
*/
public SwerveModuleBuilder withGearRatio(double gearRatio) {
this.gearRatio = gearRatio;
return this;
}
/**
* Set the diameter of the drive wheel in meters.
* @param wheelDiameterInMeters diameter of the drive wheel
* @return itself
*/
public SwerveModuleBuilder withWheelDiameter(double wheelDiameterInMeters) {
this.wheelDiameterInMeters = wheelDiameterInMeters;
return this;
}
/**
* Set the friendly name of this module. This name is used in logging
* @param moduleName name of the module
* @return itself
*/
public SwerveModuleBuilder withModuleName(String moduleName) {
this.moduleName = moduleName;
return this;
}
/**
* Build the configured instance of a SwerveModule
* @return a SwerveModule object
*/
public SwerveModule build() {
return new SwerveModule(driveMotorId, driveKP, driveKI, driveKD, driveKV, driveInverted, steerMotorId, steerKP, steerKI,
steerKD, steerKS, steerKV, steerKA,
steerEncoderId, steerOffset, canBusObj, moduleLocation, gearRatio, wheelDiameterInMeters,
moduleName);
}
}
}

View File

@ -0,0 +1,209 @@
package org.fairportrobotics.frc.robolib.drivesystems.swerve;
import java.util.Arrays;
import com.ctre.phoenix6.CANBus;
import com.ctre.phoenix6.Utils;
import com.ctre.phoenix6.hardware.Pigeon2;
import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator3d;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
import edu.wpi.first.math.kinematics.SwerveModulePosition;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.units.measure.Angle;
import edu.wpi.first.wpilibj2.command.Subsystem;
public class SwerveDriveSystem implements Subsystem{
private SwerveDriveKinematics driveKiniematics;
private ChassisSpeeds chassisSpeeds;
private Translation2d centerOfRotation;
private SwerveDrivePoseEstimator3d poseEstimator;
private SwerveModule[] modules;
/**
* The max linear speed of the robot in meters per second
*/
private double MAX_LINEAR_SPEED = 3.9;
/**
* The max angular rotation speed in radians per second
*/
private double MAX_ANGULAR_SPEED = Math.PI * 2;
private Pigeon2 gyro;
/**
* Only used during simulation.
* This variable tracks when the last simulation loop started
* to calculate how long it's been since the last simulation loop
*/
private double mLastSimTime = 0;
/**
* An implementation of Swerve drive.
*
* You should not directly instantiate this class.
* You should use SwerveBuilder to create an instance.
* @param pigeonId
* @param canBus
* @param maxLinearVelMetersSecond
* @param maxAngularVelRadiansSecond
* @param modules
*/
public SwerveDriveSystem(int pigeonId, CANBus canBus, double maxLinearVelMetersSecond, double maxAngularVelRadiansSecond, SwerveModule... modules){
MAX_LINEAR_SPEED = maxLinearVelMetersSecond;
MAX_ANGULAR_SPEED = maxAngularVelRadiansSecond;
this.modules = modules;
driveKiniematics = new SwerveDriveKinematics(
Arrays.stream(modules).map((m) -> m.getModuleLocation()).toArray(size -> new Translation2d[size])
);
gyro = new Pigeon2(pigeonId, canBus);
poseEstimator = new SwerveDrivePoseEstimator3d(driveKiniematics, new Rotation3d(Rotation2d.fromRotations(getCurrentYaw().magnitude())), getModulePositions(), Pose3d.kZero);
chassisSpeeds = new ChassisSpeeds();
}
@Override
public void periodic() {
SwerveModuleState[] moduleStates = driveKiniematics.toSwerveModuleStates(chassisSpeeds, centerOfRotation);
for(int i=0; i<modules.length;i++){
moduleStates[i].optimize(modules[i].getSteerRotations());
}
poseEstimator.updateWithTime(Utils.getCurrentTimeSeconds(), new Rotation3d(Rotation2d.fromRotations(getCurrentYaw().magnitude())), getModulePositions());
for(int i=0;i<modules.length;i++){
modules[i].setRequestedModuleState(moduleStates[i]);
modules[i].periodic();
}
}
@Override
public void simulationPeriodic() {
double deltaTimeSecond = (mLastSimTime - Utils.getCurrentTimeSeconds());
if(mLastSimTime != 0){
Pose3d oldPose = getRobotPose();
poseEstimator.resetPose(new Pose3d(oldPose.getX() + (chassisSpeeds.vxMetersPerSecond * (deltaTimeSecond)), oldPose.getY() + chassisSpeeds.vyMetersPerSecond * (deltaTimeSecond), 0, oldPose.getRotation().plus(new Rotation3d(Rotation2d.fromRadians(chassisSpeeds.omegaRadiansPerSecond * (deltaTimeSecond))))));
}
mLastSimTime = Utils.getCurrentTimeSeconds();
}
/**
* Get the location of the swerve modules
* The order of the array is the same order from when they were defined
* @return an array of Translation2d
*/
public Translation2d[] getModuleLocations(){
return Arrays.stream(modules).map((SwerveModule m) -> m.getModuleLocation()).toArray(Translation2d[]::new);
}
/**
* Get the requested state of the swerve modules.
* The order of the array is the same order from when they were defined
*
* @return an array of SwerveModuleState
*/
public SwerveModuleState[] getRequestedModuleStates(){
return Arrays.stream(modules).map((SwerveModule m) -> m.getRequestedModuleState()).toArray(SwerveModuleState[]::new);
}
/**
* Get the actual state of the swerve modules.
* The order of the array is the same order from when they were defined
* @return an array of SwerveModuleState
*/
public SwerveModuleState[] getActualModuleStates(){
return Arrays.stream(modules).map((SwerveModule m) -> m.getActualModuleState()).toArray(SwerveModuleState[]::new);
}
/**
* Get an array of the SwerveModules configured for this system
* @return Array of SwerveModules
*/
public SwerveModule[] getModules(){
return Arrays.stream(modules).toArray(SwerveModule[]::new);
}
/**
* Get the current yaw angle from the Gyro
* @return The current yaw as an Angle
*/
private Angle getCurrentYaw(){
return gyro.getYaw().getValue();
}
/**
* Get the instance of the gyro
* @return
*/
public Pigeon2 getGyro(){
return gyro;
}
/**
* Set robot speed
* @param chassisSpeeds
* @param centerOfRotation
*/
private void setChassisSpeed(ChassisSpeeds chassisSpeeds, Translation2d centerOfRotation){
this.chassisSpeeds = chassisSpeeds;
this.centerOfRotation = centerOfRotation;
}
/**
* Set robot speed relative to field
* @param x Forward robot speed in meters per second
* @param y Left robot speed in meters per second
* @param rot CCW robot speed in radians per second
*/
public void setChassisSpeedsFromJoystickFieldRelative(double x, double y, double rot){
this.setChassisSpeed(ChassisSpeeds.fromFieldRelativeSpeeds(
MAX_LINEAR_SPEED * x,
MAX_LINEAR_SPEED * y,
MAX_ANGULAR_SPEED * rot,
Rotation2d.fromDegrees(getCurrentYaw().magnitude())
), new Translation2d());
}
/**
* Set robot speed relative to robot
* @param x Forward robot speed in meters per second
* @param y Left robot speed in meters per second
* @param rot CCW robot speed in radians per second
*/
public void setChassisSpeedsFromJoystickRobotRelative(double x, double y, double rot){
this.setChassisSpeed(ChassisSpeeds.fromRobotRelativeSpeeds(
MAX_LINEAR_SPEED * x,
MAX_LINEAR_SPEED * y,
MAX_ANGULAR_SPEED * rot,
Rotation2d.fromDegrees(getCurrentYaw().magnitude())
), new Translation2d());
}
/**
* Get the current field location of the robot
* @return The current pose as a Pose3d
*/
public Pose3d getRobotPose(){
return poseEstimator.getEstimatedPosition();
}
private SwerveModulePosition[] getModulePositions(){
return Arrays.stream(modules).map((SwerveModule m) -> m.getModulePosition()).toArray(SwerveModulePosition[]::new);
}
}

View File

@ -0,0 +1,240 @@
package org.fairportrobotics.frc.robolib.drivesystems.swerve;
import com.ctre.phoenix6.CANBus;
import com.ctre.phoenix6.configs.CANcoderConfiguration;
import com.ctre.phoenix6.configs.ClosedLoopGeneralConfigs;
import com.ctre.phoenix6.configs.CurrentLimitsConfigs;
import com.ctre.phoenix6.configs.FeedbackConfigs;
import com.ctre.phoenix6.configs.MagnetSensorConfigs;
import com.ctre.phoenix6.configs.MotionMagicConfigs;
import com.ctre.phoenix6.configs.MotorOutputConfigs;
import com.ctre.phoenix6.configs.Slot0Configs;
import com.ctre.phoenix6.configs.TalonFXConfiguration;
import com.ctre.phoenix6.controls.PositionVoltage;
import com.ctre.phoenix6.controls.VelocityVoltage;
import com.ctre.phoenix6.hardware.CANcoder;
import com.ctre.phoenix6.hardware.TalonFX;
import com.ctre.phoenix6.signals.FeedbackSensorSourceValue;
import com.ctre.phoenix6.signals.InvertedValue;
import com.ctre.phoenix6.signals.SensorDirectionValue;
import com.ctre.phoenix6.signals.StaticFeedforwardSignValue;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.kinematics.SwerveModulePosition;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
public class SwerveModule {
private final int CAN_UPDATE_FREQUENCY = 50;
private final double STEER_MAX_CURRENT = 60;
private final double DRIVE_MAX_CURRENT = 80;
private final double gearRatio;
private final double wheelDiameterInMeters;
private SwerveModuleState requestedModuleState;
private TalonFX driveMotor;
private VelocityVoltage driveRequest = new VelocityVoltage(0);
private TalonFX steerMotor;
private PositionVoltage steerPosRequest = new PositionVoltage(0);
private CANcoder steerEncoder;
private String moduleName;
private Translation2d moduleLocation;
public SwerveModule(
int driveMotorId,
double driveKP,
double driveKI,
double driveKD,
double driveKV,
boolean driveInverted,
int steerMotorId,
double steerKP,
double steerKI,
double steerKD,
double steerKS,
double steerKV,
double steerKA,
int steerEncoderId,
double steerOffset,
CANBus canBusObj,
Translation2d moduleLocation,
double gearRatio,
double wheelDiameterInMeters,
String moduleName
){
steerEncoder = new CANcoder(steerEncoderId, canBusObj);
steerEncoder.getAbsolutePosition().setUpdateFrequency(CAN_UPDATE_FREQUENCY);
steerEncoder.optimizeBusUtilization();
steerEncoder.getConfigurator().apply(generateCanCoderConfiguration(steerOffset));
driveMotor = new TalonFX(driveMotorId, canBusObj);
driveMotor.getVelocity().setUpdateFrequency(CAN_UPDATE_FREQUENCY);
driveMotor.optimizeBusUtilization();
driveMotor.getConfigurator().apply(generateDriveTalonConfiguration(driveKP, driveKI, driveKD, driveKV, driveInverted));
steerMotor = new TalonFX(steerMotorId, canBusObj);
steerMotor.optimizeBusUtilization();
steerMotor.getConfigurator().apply(generateSteerTalonConfiguration(steerKP, steerKI, steerKD, steerKS, steerKV, steerKA));
// steerMotor.setNeutralMode(NeutralModeValue.Brake);
this.moduleLocation = moduleLocation;
this.gearRatio = gearRatio;
this.wheelDiameterInMeters = wheelDiameterInMeters;
this.moduleName = moduleName;
}
public void setSteerRotations(Rotation2d rotations){
steerMotor.setControl(steerPosRequest.withPosition(rotations.getRotations()));
}
public Rotation2d getSteerRotations(){
return new Rotation2d(steerEncoder.getAbsolutePosition().getValue());
}
public void setDriveSpeed(double speed){
// Convert wheel speed to rotor speed
// (speed / Circumfrence of wheel) * gear ratio
double rotorVelocity = (speed / (Math.PI * wheelDiameterInMeters)) * gearRatio;
driveMotor.setControl(driveRequest.withSlot(0).withVelocity(rotorVelocity));
}
/**
* Get drive motor speed in rotations per second
* @return
*/
public double getDriveMotorSpeed(){
return driveMotor.getVelocity().refresh().getValueAsDouble();
}
/**
* Get drive wheel speed in meters per second
* @return
*/
public double getDriveWheelSpeed(){
return (driveMotor.getVelocity().refresh().getValueAsDouble() / gearRatio) * (Math.PI * wheelDiameterInMeters);
}
public Translation2d getModuleLocation(){
return moduleLocation;
}
public void setRequestedModuleState(SwerveModuleState state){
this.setSteerRotations(state.angle);
this.setDriveSpeed(state.speedMetersPerSecond);
requestedModuleState = state;
}
public TalonFX getSteerMotor(){
return steerMotor;
}
public TalonFX getDriveMotor(){
return driveMotor;
}
public CANcoder getSteerEncoder(){
return steerEncoder;
}
public SwerveModuleState getRequestedModuleState(){
return requestedModuleState;
}
public SwerveModuleState getActualModuleState() {
return new SwerveModuleState(getDriveWheelSpeed(), getSteerRotations());
}
public SwerveModulePosition getModulePosition(){
return new SwerveModulePosition(this.getModuleDistance(), getSteerRotations());
}
public void periodic(){
SmartDashboard.putNumber(moduleName + " steerPow", steerMotor.getClosedLoopOutput().getValueAsDouble());
SmartDashboard.putNumber(moduleName + " steer error", steerMotor.getClosedLoopError().getValueAsDouble());
SmartDashboard.putNumber(moduleName + " steer setpoint", steerMotor.getClosedLoopReference().getValueAsDouble());
SmartDashboard.putNumber(moduleName + " steerPos", getSteerRotations().getRotations());
}
public String getModuleName(){
return moduleName;
}
private double getModuleDistance(){
double distance = (this.driveMotor.getPosition(true).getValueAsDouble() * gearRatio) * (Math.PI * wheelDiameterInMeters);
return distance;
}
private TalonFXConfiguration generateDriveTalonConfiguration(double kP, double kI, double kD, double kV, boolean driveInverted){
return new TalonFXConfiguration().withSlot0(
new Slot0Configs()
.withKP(kP)
.withKI(kI)
.withKD(kD)
.withKV(kV)
)
.withMotorOutput(
new MotorOutputConfigs()
.withInverted(driveInverted ? InvertedValue.Clockwise_Positive : InvertedValue.CounterClockwise_Positive)
).withCurrentLimits(
new CurrentLimitsConfigs()
.withStatorCurrentLimitEnable(true)
.withStatorCurrentLimit(DRIVE_MAX_CURRENT)
);
}
private TalonFXConfiguration generateSteerTalonConfiguration(double kP, double kI, double kD, double kS, double kV, double kA){
return new TalonFXConfiguration().withSlot0(
new Slot0Configs()
.withKP(kP)
.withKI(kI)
.withKD(kD)
.withKS(kS)
.withKV(kV)
.withKA(kA)
.withStaticFeedforwardSign(StaticFeedforwardSignValue.UseClosedLoopSign)
).withMotionMagic(
new MotionMagicConfigs()
.withMotionMagicCruiseVelocity(160)
.withMotionMagicAcceleration(160)
.withMotionMagicJerk(1600)
).withFeedback(
new FeedbackConfigs()
.withFeedbackRemoteSensorID(steerEncoder.getDeviceID())
.withFeedbackSensorSource(FeedbackSensorSourceValue.RemoteCANcoder)
.withSensorToMechanismRatio(1.0)
.withRotorToSensorRatio(12.8)
).withClosedLoopGeneral(
new ClosedLoopGeneralConfigs()
.withContinuousWrap(true)
).withMotorOutput(
new MotorOutputConfigs()
.withInverted(InvertedValue.Clockwise_Positive)
).withCurrentLimits(
new CurrentLimitsConfigs()
.withStatorCurrentLimitEnable(true)
.withStatorCurrentLimit(STEER_MAX_CURRENT)
);
}
private CANcoderConfiguration generateCanCoderConfiguration(double steerOffset){
return new CANcoderConfiguration().withMagnetSensor(
new MagnetSensorConfigs()
.withMagnetOffset(-steerOffset)
.withSensorDirection(SensorDirectionValue.CounterClockwise_Positive)
.withAbsoluteSensorDiscontinuityPoint(0.5)
);
}
}

View File

@ -1,7 +1,7 @@
/* /*
* This Java source file was generated by the Gradle 'init' task. * This Java source file was generated by the Gradle 'init' task.
*/ */
package com.fairportrobotics.frc.robolib; package org.fairportrobotics.frc.robolib;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;