-
Notifications
You must be signed in to change notification settings - Fork 187
/
Copy pathSwerveModule.java
143 lines (118 loc) · 5.66 KB
/
SwerveModule.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package frc.robot;
import edu.wpi.first.math.controller.SimpleMotorFeedforward;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.kinematics.SwerveModulePosition;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.wpilibj.Timer;
import frc.lib.math.Conversions;
import frc.lib.util.CTREModuleState;
import frc.lib.util.SwerveModuleConstants;
import com.ctre.phoenix.ErrorCode;
import com.ctre.phoenix.motorcontrol.ControlMode;
import com.ctre.phoenix.motorcontrol.DemandType;
import com.ctre.phoenix.motorcontrol.can.TalonFX;
import com.ctre.phoenix.sensors.CANCoder;
public class SwerveModule {
public int moduleNumber;
private Rotation2d angleOffset;
private Rotation2d lastAngle;
private TalonFX mAngleMotor;
private TalonFX mDriveMotor;
private CANCoder angleEncoder;
public double CANcoderInitTime = 0.0;
SimpleMotorFeedforward feedforward = new SimpleMotorFeedforward(Constants.Swerve.driveKS, Constants.Swerve.driveKV, Constants.Swerve.driveKA);
public SwerveModule(int moduleNumber, SwerveModuleConstants moduleConstants){
this.moduleNumber = moduleNumber;
this.angleOffset = moduleConstants.angleOffset;
/* Angle Encoder Config */
angleEncoder = new CANCoder(moduleConstants.cancoderID);
configAngleEncoder();
/* Angle Motor Config */
mAngleMotor = new TalonFX(moduleConstants.angleMotorID);
configAngleMotor();
/* Drive Motor Config */
mDriveMotor = new TalonFX(moduleConstants.driveMotorID);
configDriveMotor();
lastAngle = getState().angle;
}
public void setDesiredState(SwerveModuleState desiredState, boolean isOpenLoop){
/* This is a custom optimize function, since default WPILib optimize assumes continuous controller which CTRE and Rev onboard is not */
desiredState = CTREModuleState.optimize(desiredState, getState().angle);
setAngle(desiredState);
setSpeed(desiredState, isOpenLoop);
}
private void setSpeed(SwerveModuleState desiredState, boolean isOpenLoop){
if(isOpenLoop){
double percentOutput = desiredState.speedMetersPerSecond / Constants.Swerve.maxSpeed;
mDriveMotor.set(ControlMode.PercentOutput, percentOutput);
}
else {
double velocity = Conversions.MPSToFalcon(desiredState.speedMetersPerSecond, Constants.Swerve.wheelCircumference, Constants.Swerve.driveGearRatio);
mDriveMotor.set(ControlMode.Velocity, velocity, DemandType.ArbitraryFeedForward, feedforward.calculate(desiredState.speedMetersPerSecond));
}
}
private void setAngle(SwerveModuleState desiredState){
Rotation2d angle = (Math.abs(desiredState.speedMetersPerSecond) <= (Constants.Swerve.maxSpeed * 0.01)) ? lastAngle : desiredState.angle; //Prevent rotating module if speed is less then 1%. Prevents Jittering.
mAngleMotor.set(ControlMode.Position, Conversions.degreesToFalcon(angle.getDegrees(), Constants.Swerve.angleGearRatio));
lastAngle = angle;
}
private Rotation2d getAngle(){
return Rotation2d.fromDegrees(Conversions.falconToDegrees(mAngleMotor.getSelectedSensorPosition(), Constants.Swerve.angleGearRatio));
}
public Rotation2d getCanCoder(){
return Rotation2d.fromDegrees(angleEncoder.getAbsolutePosition());
}
private void waitForCanCoder(){
/*
* Wait for up to 1000 ms for a good CANcoder signal.
*
* This prevents a race condition during program startup
* where we try to synchronize the Falcon encoder to the
* CANcoder before we have received any position signal
* from the CANcoder.
*/
for (int i = 0; i < 100; ++i) {
angleEncoder.getAbsolutePosition();
if (angleEncoder.getLastError() == ErrorCode.OK) {
break;
}
Timer.delay(0.010);
CANcoderInitTime += 10;
}
}
private void resetToAbsolute(){
waitForCanCoder();
double absolutePosition = Conversions.degreesToFalcon(getCanCoder().getDegrees() - angleOffset.getDegrees(), Constants.Swerve.angleGearRatio);
mAngleMotor.setSelectedSensorPosition(absolutePosition);
}
private void configAngleEncoder(){
angleEncoder.configFactoryDefault();
angleEncoder.configAllSettings(Robot.ctreConfigs.swerveCanCoderConfig);
}
private void configAngleMotor(){
mAngleMotor.configFactoryDefault();
mAngleMotor.configAllSettings(Robot.ctreConfigs.swerveAngleFXConfig);
mAngleMotor.setInverted(Constants.Swerve.angleMotorInvert);
mAngleMotor.setNeutralMode(Constants.Swerve.angleNeutralMode);
resetToAbsolute();
}
private void configDriveMotor(){
mDriveMotor.configFactoryDefault();
mDriveMotor.configAllSettings(Robot.ctreConfigs.swerveDriveFXConfig);
mDriveMotor.setInverted(Constants.Swerve.driveMotorInvert);
mDriveMotor.setNeutralMode(Constants.Swerve.driveNeutralMode);
mDriveMotor.setSelectedSensorPosition(0);
}
public SwerveModuleState getState(){
return new SwerveModuleState(
Conversions.falconToMPS(mDriveMotor.getSelectedSensorVelocity(), Constants.Swerve.wheelCircumference, Constants.Swerve.driveGearRatio),
getAngle()
);
}
public SwerveModulePosition getPosition(){
return new SwerveModulePosition(
Conversions.falconToMeters(mDriveMotor.getSelectedSensorPosition(), Constants.Swerve.wheelCircumference, Constants.Swerve.driveGearRatio),
getAngle()
);
}
}