Guest User

Swerve Code

a guest
Feb 26th, 2026
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.00 KB | None | 0 0
  1. // Copyright (c) 2021-2026 Littleton Robotics
  2. // http://github.com/Mechanical-Advantage
  3. //
  4. // Use of this source code is governed by a BSD
  5. // license that can be found in the LICENSE file
  6. // at the root directory of this project.
  7.  
  8. package frc.robot.subsystems.Drive;
  9.  
  10. import static frc.robot.subsystems.Drive.DriveConstants.*;
  11. import static frc.robot.util.SparkUtil.*;
  12.  
  13. import com.ctre.phoenix6.BaseStatusSignal;
  14. import com.ctre.phoenix6.StatusSignal;
  15. import com.ctre.phoenix6.hardware.CANcoder;
  16. import com.revrobotics.PersistMode;
  17. import com.revrobotics.RelativeEncoder;
  18. import com.revrobotics.ResetMode;
  19. import com.revrobotics.spark.ClosedLoopSlot;
  20. import com.revrobotics.spark.FeedbackSensor;
  21. import com.revrobotics.spark.SparkBase;
  22. import com.revrobotics.spark.SparkBase.ControlType;
  23. import com.revrobotics.spark.SparkClosedLoopController;
  24. import com.revrobotics.spark.SparkClosedLoopController.ArbFFUnits;
  25. import com.revrobotics.spark.SparkFlex;
  26. import com.revrobotics.spark.SparkLowLevel.MotorType;
  27. import com.revrobotics.spark.SparkMax;
  28. import com.revrobotics.spark.config.SparkBaseConfig.IdleMode;
  29. import com.revrobotics.spark.config.SparkFlexConfig;
  30. import com.revrobotics.spark.config.SparkMaxConfig;
  31. import edu.wpi.first.math.MathUtil;
  32. import edu.wpi.first.math.controller.ProfiledPIDController;
  33. import edu.wpi.first.math.filter.Debouncer;
  34. import edu.wpi.first.math.geometry.Rotation2d;
  35. import edu.wpi.first.math.trajectory.TrapezoidProfile;
  36. import edu.wpi.first.math.util.Units;
  37. import edu.wpi.first.units.measure.Angle;
  38. import java.util.Queue;
  39. import java.util.function.DoubleSupplier;
  40. import org.littletonrobotics.junction.AutoLogOutputManager;
  41.  
  42. /**
  43. * Module IO implementation for Spark Flex drive motor controller, Spark Max turn motor controller,
  44. * and duty cycle absolute encoder.
  45. */
  46. public class ModuleIOSpark implements ModuleIO {
  47. private final Rotation2d zeroRotation;
  48.  
  49. // Hardware objects
  50. private final SparkBase driveSpark;
  51. private final SparkBase turnSpark;
  52. private final RelativeEncoder driveEncoder;
  53.  
  54. private final CANcoder turnCAN;
  55. private final StatusSignal<Angle> absPosSig;
  56.  
  57. private final ProfiledPIDController turnMotorPIDController =
  58. new ProfiledPIDController(0.25, 0, 0.00025, new TrapezoidProfile.Constraints(502, 1190));
  59.  
  60. // Closed loop controllers
  61. private final SparkClosedLoopController driveController;
  62.  
  63. // Queue inputs from odometry thread
  64. private final Queue<Double> timestampQueue;
  65. private final Queue<Double> drivePositionQueue;
  66. private final Queue<Double> turnPositionQueue;
  67.  
  68. // Connection debouncers
  69. private final Debouncer driveConnectedDebounce =
  70. new Debouncer(0.5, Debouncer.DebounceType.kFalling);
  71. private final Debouncer turnConnectedDebounce =
  72. new Debouncer(0.5, Debouncer.DebounceType.kFalling);
  73.  
  74. public ModuleIOSpark(int module) {
  75. zeroRotation =
  76. switch (module) {
  77. case 0 -> frontLeftZeroRotation;
  78. case 1 -> frontRightZeroRotation;
  79. case 2 -> backLeftZeroRotation;
  80. case 3 -> backRightZeroRotation;
  81. default -> new Rotation2d();
  82. };
  83. driveSpark =
  84. new SparkFlex(
  85. switch (module) {
  86. case 0 -> frontLeftDriveCanId;
  87. case 1 -> frontRightDriveCanId;
  88. case 2 -> backLeftDriveCanId;
  89. case 3 -> backRightDriveCanId;
  90. default -> 0;
  91. },
  92. MotorType.kBrushless);
  93. turnSpark =
  94. new SparkMax(
  95. switch (module) {
  96. case 0 -> frontLeftTurnCanId;
  97. case 1 -> frontRightTurnCanId;
  98. case 2 -> backLeftTurnCanId;
  99. case 3 -> backRightTurnCanId;
  100. default -> 0;
  101. },
  102. MotorType.kBrushless);
  103. turnCAN =
  104. new CANcoder(
  105. switch (module) {
  106. case 0 -> frontLeftCANcoderID;
  107. case 1 -> frontRightCANcoderID;
  108. case 2 -> backLeftCANcoderID;
  109. case 3 -> backRightCANcoderID;
  110. default -> 0;
  111. });
  112. driveEncoder = driveSpark.getEncoder();
  113. driveController = driveSpark.getClosedLoopController();
  114.  
  115. absPosSig = turnCAN.getAbsolutePosition();
  116.  
  117. BaseStatusSignal.setUpdateFrequencyForAll(100.0, absPosSig);
  118. turnCAN.optimizeBusUtilization();
  119.  
  120. // Configure drive motor
  121. var driveConfig = new SparkFlexConfig();
  122. driveConfig
  123. .idleMode(IdleMode.kBrake)
  124. .smartCurrentLimit(driveMotorCurrentLimit)
  125. .voltageCompensation(12.0);
  126. driveConfig
  127. .encoder
  128. .positionConversionFactor(driveEncoderPositionFactor)
  129. .velocityConversionFactor(driveEncoderVelocityFactor)
  130. .uvwMeasurementPeriod(10)
  131. .uvwAverageDepth(2);
  132. driveConfig
  133. .closedLoop
  134. .feedbackSensor(FeedbackSensor.kPrimaryEncoder)
  135. .pid(driveKp, 0.0, driveKd);
  136. driveConfig
  137. .signals
  138. .primaryEncoderPositionAlwaysOn(true)
  139. .primaryEncoderPositionPeriodMs((int) (1000.0 / odometryFrequency))
  140. .primaryEncoderVelocityAlwaysOn(true)
  141. .primaryEncoderVelocityPeriodMs(20)
  142. .appliedOutputPeriodMs(20)
  143. .busVoltagePeriodMs(20)
  144. .outputCurrentPeriodMs(20);
  145. tryUntilOk(
  146. driveSpark,
  147. 5,
  148. () ->
  149. driveSpark.configure(
  150. driveConfig, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters));
  151. tryUntilOk(driveSpark, 5, () -> driveEncoder.setPosition(0.0));
  152.  
  153. var turnConfig = new SparkMaxConfig();
  154. switch (module) {
  155. case 0 -> turnConfig
  156. .inverted(!turnInverted)
  157. .idleMode(IdleMode.kBrake)
  158. .smartCurrentLimit(turnMotorCurrentLimit)
  159. .voltageCompensation(12.0);
  160. case 1 -> turnConfig
  161. .inverted(!turnInverted)
  162. .idleMode(IdleMode.kBrake)
  163. .smartCurrentLimit(turnMotorCurrentLimit)
  164. .voltageCompensation(12.0);
  165. case 2 -> turnConfig
  166. .inverted(!turnInverted)
  167. .idleMode(IdleMode.kBrake)
  168. .smartCurrentLimit(turnMotorCurrentLimit)
  169. .voltageCompensation(12.0);
  170. case 3 -> turnConfig
  171. .inverted(!turnInverted)
  172. .idleMode(IdleMode.kBrake)
  173. .smartCurrentLimit(turnMotorCurrentLimit)
  174. .voltageCompensation(12.0);
  175. }
  176.  
  177. // turnConfig
  178. // .absoluteEncoder
  179. // .inverted(turnEncoderInverted)
  180. // .positionConversionFactor(turnEncoderPositionFactor)
  181. // .velocityConversionFactor(turnEncoderVelocityFactor)
  182. // .averageDepth(2);
  183. // turnConfig
  184. // .closedLoop
  185. // .feedbackSensor(FeedbackSensor.kAbsoluteEncoder)
  186. // .positionWrappingEnabled(true)
  187. // .positionWrappingInputRange(turnPIDMinInput, turnPIDMaxInput)
  188. // .pid(turnKp, 0.0, turnKd);
  189. turnConfig
  190. .signals
  191. // .absoluteEncoderPositionAlwaysOn(true)
  192. // .absoluteEncoderPositionPeriodMs((int) (1000.0 / odometryFrequency))
  193. // .absoluteEncoderVelocityAlwaysOn(true)
  194. // .absoluteEncoderVelocityPeriodMs(20)
  195. .appliedOutputPeriodMs(20)
  196. .busVoltagePeriodMs(20)
  197. .outputCurrentPeriodMs(20);
  198. tryUntilOk(
  199. turnSpark,
  200. 5,
  201. () ->
  202. turnSpark.configure(
  203. turnConfig, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters));
  204.  
  205. turnMotorPIDController.enableContinuousInput(0, 2 * 3.14159);
  206.  
  207. // Create odometry queues
  208. timestampQueue = SparkOdometryThread.getInstance().makeTimestampQueue();
  209. drivePositionQueue =
  210. SparkOdometryThread.getInstance().registerSignal(driveSpark, driveEncoder::getPosition);
  211. turnPositionQueue =
  212. SparkOdometryThread.getInstance().registerSignal(turnSpark, this::getMeasuredAngleRad);
  213. }
  214.  
  215. private double getMeasuredAngleRad() {
  216. absPosSig.refresh();
  217. double rot = absPosSig.getValueAsDouble();
  218. double rad = Units.rotationsToRadians(rot);
  219. return MathUtil.inputModulus(rad, 0.0, 2.0 * Math.PI);
  220. }
  221.  
  222. @Override
  223. public void updateInputs(ModuleIOInputs inputs) {
  224. // Update drive inputs
  225. sparkStickyFault = false;
  226. ifOk(driveSpark, driveEncoder::getPosition, (value) -> inputs.drivePositionRad = value);
  227. ifOk(driveSpark, driveEncoder::getVelocity, (value) -> inputs.driveVelocityRadPerSec = value);
  228. ifOk(
  229. driveSpark,
  230. new DoubleSupplier[] {driveSpark::getAppliedOutput, driveSpark::getBusVoltage},
  231. (values) -> inputs.driveAppliedVolts = values[0] * values[1]);
  232. ifOk(driveSpark, driveSpark::getOutputCurrent, (value) -> inputs.driveCurrentAmps = value);
  233. inputs.driveConnected = driveConnectedDebounce.calculate(!sparkStickyFault);
  234.  
  235. // Update turn inputs
  236. sparkStickyFault = false;
  237. ifOk(
  238. turnSpark,
  239. this::getMeasuredAngleRad,
  240. (value) -> inputs.turnPosition = new Rotation2d(value).minus(zeroRotation));
  241. ifOk(turnSpark, this::getMeasuredAngleRad, (value) -> inputs.turnVelocityRadPerSec = value);
  242. ifOk(
  243. turnSpark,
  244. new DoubleSupplier[] {turnSpark::getAppliedOutput, turnSpark::getBusVoltage},
  245. (values) -> inputs.turnAppliedVolts = values[0] * values[1]);
  246. ifOk(turnSpark, turnSpark::getOutputCurrent, (value) -> inputs.turnCurrentAmps = value);
  247. inputs.turnConnected = turnConnectedDebounce.calculate(!sparkStickyFault);
  248.  
  249. // Update odometry inputs
  250. inputs.odometryTimestamps =
  251. timestampQueue.stream().mapToDouble((Double value) -> value).toArray();
  252. inputs.odometryDrivePositionsRad =
  253. drivePositionQueue.stream().mapToDouble((Double value) -> value).toArray();
  254. inputs.odometryTurnPositions =
  255. turnPositionQueue.stream()
  256. .map((Double value) -> new Rotation2d(value).minus(zeroRotation))
  257. .toArray(Rotation2d[]::new);
  258. timestampQueue.clear();
  259. drivePositionQueue.clear();
  260. turnPositionQueue.clear();
  261. }
  262.  
  263. @Override
  264. public void setDriveOpenLoop(double output) {
  265. driveSpark.setVoltage(output);
  266. }
  267.  
  268. @Override
  269. public void setTurnOpenLoop(double output) {
  270. turnSpark.setVoltage(output);
  271. // AutoLogOutputManager.addObject(output);
  272. }
  273.  
  274. @Override
  275. public void setDriveVelocity(double velocityRadPerSec) {
  276. double ffVolts = driveKs * Math.signum(velocityRadPerSec) + driveKv * velocityRadPerSec;
  277. driveController.setSetpoint(
  278. velocityRadPerSec,
  279. ControlType.kVelocity,
  280. ClosedLoopSlot.kSlot0,
  281. ffVolts,
  282. ArbFFUnits.kVoltage);
  283. }
  284.  
  285. @Override
  286. public void setTurnPosition(Rotation2d rotation) {
  287. double maxOutput = 1.0;
  288.  
  289. double meas = getMeasuredAngleRad();
  290. double goal =
  291. MathUtil.inputModulus(rotation.plus(zeroRotation).getRadians(), 0.0, 2.0 * Math.PI);
  292. double out = turnMotorPIDController.calculate(meas, goal);
  293.  
  294. double capped = MathUtil.clamp(out, -maxOutput, maxOutput);
  295. turnSpark.set(-capped);
  296.  
  297. var sp = turnMotorPIDController.getSetpoint();
  298.  
  299. AutoLogOutputManager.addObject(sp);
  300. }
  301. }
Advertisement
Add Comment
Please, Sign In to add comment