Skip to content

Commit 0101c22

Browse files
authored
Merge branch 'advantagekit-swerve' into advantagekit-swerve-adjustments-#1
2 parents de46fd2 + d206967 commit 0101c22

File tree

5 files changed

+119
-46
lines changed

5 files changed

+119
-46
lines changed

src/main/java/frc/robot/subsystems/drive/DriveConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import edu.wpi.first.math.system.plant.DCMotor;
1818

1919
public class DriveConstants {
20-
public static final double maxSpeedMetersPerSec = 9.72;
20+
public static final double maxSpeedMetersPerSec = 4.5;
2121
public static final double odometryFrequency = 100.0; // Hz
2222
public static final double trackWidth = 69.5; // w metry
2323
public static final double wheelBase = 69.5; // w metry

src/main/java/frc/robot/subsystems/drive/ModuleIOSpark.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static frc.robot.subsystems.drive.DriveConstants.*;
1313
import static frc.robot.util.SparkUtil.*;
1414

15+
import com.revrobotics.AbsoluteEncoder;
1516
import com.revrobotics.PersistMode;
1617
import com.revrobotics.RelativeEncoder;
1718
import com.revrobotics.ResetMode;
@@ -26,9 +27,10 @@
2627
import com.revrobotics.spark.config.SparkBaseConfig.IdleMode;
2728
import com.revrobotics.spark.config.SparkMaxConfig;
2829
import edu.wpi.first.math.MathUtil;
30+
import edu.wpi.first.math.controller.PIDController;
2931
import edu.wpi.first.math.filter.Debouncer;
3032
import edu.wpi.first.math.geometry.Rotation2d;
31-
import edu.wpi.first.wpilibj.AnalogEncoder;
33+
import frc.robot.util.AbsoluteAnalogEncoder;
3234
import java.util.Queue;
3335
import java.util.function.DoubleSupplier;
3436

@@ -50,6 +52,8 @@ public class ModuleIOSpark implements ModuleIO {
5052
// Closed loop controllers
5153
private final SparkClosedLoopController driveController;
5254
private final SparkClosedLoopController turnController;
55+
// RIO-side PID for turn control (we use the RoboRIO analog absolute encoder)
56+
private final PIDController turnPid;
5357

5458
// Queue inputs from odometry thread
5559
private final Queue<Double> timestampQueue;
@@ -107,6 +111,12 @@ public ModuleIOSpark(int module) {
107111
driveController = driveSpark.getClosedLoopController();
108112
turnController = turnSpark.getClosedLoopController();
109113

114+
// RIO-side PID controller for the turn motor. We use the RoboRIO-mounted
115+
// analog absolute encoder (turnEncoder) as the measurement and command
116+
// voltages to the SPARK via setVoltage(...).
117+
turnPid = new PIDController(turnKp, 0.0, turnKd);
118+
turnPid.enableContinuousInput(turnPIDMinInput, turnPIDMaxInput);
119+
110120
// Configure drive motor
111121
var driveConfig = new SparkMaxConfig();
112122
driveConfig
@@ -157,8 +167,7 @@ public ModuleIOSpark(int module) {
157167
.closedLoop
158168
.feedbackSensor(FeedbackSensor.kPrimaryEncoder)
159169
.positionWrappingEnabled(true)
160-
.positionWrappingInputRange(turnPIDMinInput, turnPIDMaxInput)
161-
.pid(turnKp, 0.0, turnKd);
170+
.positionWrappingInputRange(turnPIDMinInput, turnPIDMaxInput);
162171
turnConfig
163172
.signals
164173
.primaryEncoderPositionAlwaysOn(true)
@@ -183,6 +192,10 @@ public ModuleIOSpark(int module) {
183192
timestampQueue = SparkOdometryThread.getInstance().makeTimestampQueue();
184193
drivePositionQueue =
185194
SparkOdometryThread.getInstance().registerSignal(driveSpark, driveEncoder::getPosition);
195+
// The turn encoder is an external RoboRIO-mounted analog encoder (not a Spark signal).
196+
// Register it as a generic DoubleSupplier so the odometry thread samples it directly
197+
// instead of going through the Spark-specific sampling/error path which can drop
198+
// or mis-time samples for a non-Spark sensor and produce jitter in AdvantageScope.
186199
turnPositionQueue =
187200
SparkOdometryThread.getInstance().registerSignal(turnSpark, turnEncoder::getPosition);
188201
//// MODIFIED
@@ -213,7 +226,8 @@ public void updateInputs(ModuleIOInputs inputs) {
213226
new DoubleSupplier[] {turnSpark::getAppliedOutput, turnSpark::getBusVoltage},
214227
(values) -> inputs.turnAppliedVolts = values[0] * values[1]);
215228
ifOk(turnSpark, turnSpark::getOutputCurrent, (value) -> inputs.turnCurrentAmps = value);
216-
inputs.turnConnected = turnConnectedDebounce.calculate(!sparkStickyFault);
229+
// Use the analog-read success as our connected indicator for turn.
230+
inputs.turnConnected = turnConnectedDebounce.calculate(turnReadOk);
217231

218232
// Update odometry inputs
219233
inputs.odometryTimestamps =
@@ -255,6 +269,14 @@ public void setTurnPosition(Rotation2d rotation) {
255269
double setpoint =
256270
MathUtil.inputModulus(
257271
rotation.plus(zeroRotation).getRadians(), turnPIDMinInput, turnPIDMaxInput);
258-
turnController.setSetpoint(setpoint, ControlType.kPosition);
272+
273+
// Read the RoboRIO-mounted analog absolute encoder and compute PID output
274+
double current = turnEncoder.getPosition();
275+
double volts = turnPid.calculate(current, setpoint);
276+
277+
// Clamp to allowable voltage range
278+
volts = MathUtil.clamp(volts, -12.0, 12.0);
279+
280+
turnSpark.setVoltage(volts);
259281
}
260282
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package frc.robot.util;
2+
3+
import static edu.wpi.first.units.Units.Degrees;
4+
import static edu.wpi.first.units.Units.Microseconds;
5+
import static edu.wpi.first.units.Units.Radian;
6+
import static edu.wpi.first.units.Units.Seconds;
7+
8+
import com.revrobotics.AbsoluteEncoder;
9+
import edu.wpi.first.math.MathUtil;
10+
import edu.wpi.first.wpilibj.AnalogEncoder;
11+
import org.littletonrobotics.junction.Logger;
12+
13+
public class AbsoluteAnalogEncoder implements AbsoluteEncoder {
14+
AnalogEncoder encoder;
15+
16+
double lastReading;
17+
double cachedVelocity;
18+
long lastTimestamp;
19+
20+
public AbsoluteAnalogEncoder(int channel) {
21+
encoder = new AnalogEncoder(channel);
22+
23+
lastTimestamp = Logger.getTimestamp();
24+
lastReading = getPosition();
25+
}
26+
27+
public double getRaw() {
28+
return encoder.get();
29+
}
30+
31+
public double getPosition() {
32+
return Radian.convertFrom(this.getRaw() * 360, Degrees);
33+
}
34+
35+
public double getVelocity() {
36+
return calculateVelocity(getPosition(), Logger.getTimestamp());
37+
}
38+
39+
public double calculateVelocity(double currentPositionRad, long currentTimestampMicros) {
40+
double deltaRad = MathUtil.angleModulus(currentPositionRad - lastReading);
41+
42+
double deltaTimeSec = Seconds.convertFrom(currentTimestampMicros - lastTimestamp, Microseconds);
43+
44+
double velocityRadPerSec = deltaRad / deltaTimeSec;
45+
46+
lastReading = currentPositionRad;
47+
lastTimestamp = currentTimestampMicros;
48+
49+
return velocityRadPerSec;
50+
}
51+
}

0 commit comments

Comments
 (0)