diff --git a/src/main/java/frc/robot/Constants.java b/src/main/java/frc/robot/Constants.java
index a82210e..73cfcf2 100644
--- a/src/main/java/frc/robot/Constants.java
+++ b/src/main/java/frc/robot/Constants.java
@@ -22,16 +22,22 @@
* constants are needed, to reduce verbosity.
*/
public final class Constants {
- public static final Mode currentMode = Mode.REAL;
+ public static final Mode currentMode = Mode.REAL;
- public static enum Mode {
- /** Running on a real robot. */
- REAL,
+ public enum Mode {
+ /**
+ * Running on a real robot.
+ */
+ REAL,
- /** Running a physics simulator. */
- SIM,
+ /**
+ * Running a physics simulator.
+ */
+ SIM,
- /** Replaying from a log file. */
- REPLAY
- }
+ /**
+ * Replaying from a log file.
+ */
+ REPLAY
+ }
}
diff --git a/src/main/java/frc/robot/Main.java b/src/main/java/frc/robot/Main.java
index e8af1c1..a5858bc 100644
--- a/src/main/java/frc/robot/Main.java
+++ b/src/main/java/frc/robot/Main.java
@@ -21,14 +21,15 @@
* call.
*/
public final class Main {
- private Main() {}
+ private Main() {
+ }
- /**
- * Main initialization function. Do not perform any initialization here.
- *
- *
If you change your main robot class, change the parameter type.
- */
- public static void main(String... args) {
- RobotBase.startRobot(Robot::new);
- }
+ /**
+ * Main initialization function. Do not perform any initialization here.
+ *
+ *
If you change your main robot class, change the parameter type.
+ */
+ public static void main(String... args) {
+ RobotBase.startRobot(Robot::new);
+ }
}
diff --git a/src/main/java/frc/robot/Robot.java b/src/main/java/frc/robot/Robot.java
index ea47d35..87f975c 100644
--- a/src/main/java/frc/robot/Robot.java
+++ b/src/main/java/frc/robot/Robot.java
@@ -29,132 +29,161 @@
* project.
*/
public class Robot extends LoggedRobot {
- private Command autonomousCommand;
- private RobotContainer robotContainer;
-
- /**
- * This function is run when the robot is first started up and should be used for any
- * initialization code.
- */
- @Override
- public void robotInit() {
- // Record metadata
- Logger.recordMetadata("ProjectName", BuildConstants.MAVEN_NAME);
- Logger.recordMetadata("BuildDate", BuildConstants.BUILD_DATE);
- Logger.recordMetadata("GitSHA", BuildConstants.GIT_SHA);
- Logger.recordMetadata("GitDate", BuildConstants.GIT_DATE);
- Logger.recordMetadata("GitBranch", BuildConstants.GIT_BRANCH);
- switch (BuildConstants.DIRTY) {
- case 0:
- Logger.recordMetadata("GitDirty", "All changes committed");
- break;
- case 1:
- Logger.recordMetadata("GitDirty", "Uncomitted changes");
- break;
- default:
- Logger.recordMetadata("GitDirty", "Unknown");
- break;
+ private Command autonomousCommand;
+ private RobotContainer robotContainer;
+
+ /**
+ * This function is run when the robot is first started up and should be used for any
+ * initialization code.
+ */
+ @Override
+ public void robotInit() {
+ // Record metadata
+ Logger.recordMetadata("ProjectName", BuildConstants.MAVEN_NAME);
+ Logger.recordMetadata("BuildDate", BuildConstants.BUILD_DATE);
+ Logger.recordMetadata("GitSHA", BuildConstants.GIT_SHA);
+ Logger.recordMetadata("GitDate", BuildConstants.GIT_DATE);
+ Logger.recordMetadata("GitBranch", BuildConstants.GIT_BRANCH);
+ switch (BuildConstants.DIRTY) {
+ case 0:
+ Logger.recordMetadata("GitDirty", "All changes committed");
+ break;
+ case 1:
+ Logger.recordMetadata("GitDirty", "Uncomitted changes");
+ break;
+ default:
+ Logger.recordMetadata("GitDirty", "Unknown");
+ break;
+ }
+
+ // Set up data receivers & replay source
+ switch (Constants.currentMode) {
+ case REAL:
+ // Running on a real robot, log to a USB stick ("/U/logs")
+ Logger.addDataReceiver(new WPILOGWriter());
+ Logger.addDataReceiver(new NT4Publisher());
+ break;
+
+ case SIM:
+ // Running a physics simulator, log to NT
+ Logger.addDataReceiver(new NT4Publisher());
+ break;
+
+ case REPLAY:
+ // Replaying a log, set up replay source
+ setUseTiming(false); // Run as fast as possible
+ String logPath = LogFileUtil.findReplayLog();
+ Logger.setReplaySource(new WPILOGReader(logPath));
+ Logger.addDataReceiver(new WPILOGWriter(LogFileUtil.addPathSuffix(logPath, "_sim")));
+ break;
+ }
+
+ // See http://bit.ly/3YIzFZ6 for more information on timestamps in AdvantageKit.
+ // Logger.disableDeterministicTimestamps()
+
+ // Start AdvantageKit logger
+ Logger.start();
+
+ // Instantiate our RobotContainer. This will perform all our button bindings,
+ // and put our autonomous chooser on the dashboard.
+ robotContainer = new RobotContainer();
}
- // Set up data receivers & replay source
- switch (Constants.currentMode) {
- case REAL:
- // Running on a real robot, log to a USB stick ("/U/logs")
- Logger.addDataReceiver(new WPILOGWriter());
- Logger.addDataReceiver(new NT4Publisher());
- break;
-
- case SIM:
- // Running a physics simulator, log to NT
- Logger.addDataReceiver(new NT4Publisher());
- break;
-
- case REPLAY:
- // Replaying a log, set up replay source
- setUseTiming(false); // Run as fast as possible
- String logPath = LogFileUtil.findReplayLog();
- Logger.setReplaySource(new WPILOGReader(logPath));
- Logger.addDataReceiver(new WPILOGWriter(LogFileUtil.addPathSuffix(logPath, "_sim")));
- break;
+ /**
+ * This function is called periodically during all modes.
+ */
+ @Override
+ public void robotPeriodic() {
+ // Runs the Scheduler. This is responsible for polling buttons, adding
+ // newly-scheduled commands, running already-scheduled commands, removing
+ // finished or interrupted commands, and running subsystem periodic() methods.
+ // This must be called from the robot's periodic block in order for anything in
+ // the Command-based framework to work.
+ CommandScheduler.getInstance().run();
}
- // See http://bit.ly/3YIzFZ6 for more information on timestamps in AdvantageKit.
- // Logger.disableDeterministicTimestamps()
-
- // Start AdvantageKit logger
- Logger.start();
-
- // Instantiate our RobotContainer. This will perform all our button bindings,
- // and put our autonomous chooser on the dashboard.
- robotContainer = new RobotContainer();
- }
-
- /** This function is called periodically during all modes. */
- @Override
- public void robotPeriodic() {
- // Runs the Scheduler. This is responsible for polling buttons, adding
- // newly-scheduled commands, running already-scheduled commands, removing
- // finished or interrupted commands, and running subsystem periodic() methods.
- // This must be called from the robot's periodic block in order for anything in
- // the Command-based framework to work.
- CommandScheduler.getInstance().run();
- }
-
- /** This function is called once when the robot is disabled. */
- @Override
- public void disabledInit() {}
-
- /** This function is called periodically when disabled. */
- @Override
- public void disabledPeriodic() {}
-
- /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */
- @Override
- public void autonomousInit() {
- autonomousCommand = robotContainer.getAutonomousCommand();
-
- // schedule the autonomous command (example)
- if (autonomousCommand != null) {
- autonomousCommand.schedule();
+ /**
+ * This function is called once when the robot is disabled.
+ */
+ @Override
+ public void disabledInit() {
}
- }
-
- /** This function is called periodically during autonomous. */
- @Override
- public void autonomousPeriodic() {}
-
- /** This function is called once when teleop is enabled. */
- @Override
- public void teleopInit() {
- // This makes sure that the autonomous stops running when
- // teleop starts running. If you want the autonomous to
- // continue until interrupted by another command, remove
- // this line or comment it out.
- if (autonomousCommand != null) {
- autonomousCommand.cancel();
+
+ /**
+ * This function is called periodically when disabled.
+ */
+ @Override
+ public void disabledPeriodic() {
+ }
+
+ /**
+ * This autonomous runs the autonomous command selected by your {@link RobotContainer} class.
+ */
+ @Override
+ public void autonomousInit() {
+ autonomousCommand = robotContainer.getAutonomousCommand();
+
+ // schedule the autonomous command (example)
+ if (autonomousCommand != null) {
+ autonomousCommand.schedule();
+ }
+ }
+
+ /**
+ * This function is called periodically during autonomous.
+ */
+ @Override
+ public void autonomousPeriodic() {
+ }
+
+ /**
+ * This function is called once when teleop is enabled.
+ */
+ @Override
+ public void teleopInit() {
+ // This makes sure that the autonomous stops running when
+ // teleop starts running. If you want the autonomous to
+ // continue until interrupted by another command, remove
+ // this line or comment it out.
+ if (autonomousCommand != null) {
+ autonomousCommand.cancel();
+ }
+ }
+
+ /**
+ * This function is called periodically during operator control.
+ */
+ @Override
+ public void teleopPeriodic() {
+ }
+
+ /**
+ * This function is called once when test mode is enabled.
+ */
+ @Override
+ public void testInit() {
+ // Cancels all running commands at the start of test mode.
+ CommandScheduler.getInstance().cancelAll();
+ }
+
+ /**
+ * This function is called periodically during test mode.
+ */
+ @Override
+ public void testPeriodic() {
+ }
+
+ /**
+ * This function is called once when the robot is first started up.
+ */
+ @Override
+ public void simulationInit() {
+ }
+
+ /**
+ * This function is called periodically whilst in simulation.
+ */
+ @Override
+ public void simulationPeriodic() {
}
- }
-
- /** This function is called periodically during operator control. */
- @Override
- public void teleopPeriodic() {}
-
- /** This function is called once when test mode is enabled. */
- @Override
- public void testInit() {
- // Cancels all running commands at the start of test mode.
- CommandScheduler.getInstance().cancelAll();
- }
-
- /** This function is called periodically during test mode. */
- @Override
- public void testPeriodic() {}
-
- /** This function is called once when the robot is first started up. */
- @Override
- public void simulationInit() {}
-
- /** This function is called periodically whilst in simulation. */
- @Override
- public void simulationPeriodic() {}
}
diff --git a/src/main/java/frc/robot/RobotContainer.java b/src/main/java/frc/robot/RobotContainer.java
index 9f92af4..2515880 100644
--- a/src/main/java/frc/robot/RobotContainer.java
+++ b/src/main/java/frc/robot/RobotContainer.java
@@ -44,135 +44,144 @@
* subsystems, commands, and button mappings) should be declared here.
*/
public class RobotContainer {
- // Subsystems
- private final Drive drive;
- private final Flywheel flywheel;
+ // Subsystems
+ private final Drive drive;
+ private final Flywheel flywheel;
- // Controller
- private final CommandXboxController controller = new CommandXboxController(0);
+ // Controller
+ private final CommandXboxController controller = new CommandXboxController(0);
- // Dashboard inputs
- private final LoggedDashboardChooser autoChooser;
- private final LoggedDashboardNumber flywheelSpeedInput =
- new LoggedDashboardNumber("Flywheel Speed", 1500.0);
+ // Dashboard inputs
+ private final LoggedDashboardChooser autoChooser;
+ private final LoggedDashboardNumber flywheelSpeedInput =
+ new LoggedDashboardNumber("Flywheel Speed", 1500.0);
- /** The container for the robot. Contains subsystems, OI devices, and commands. */
- public RobotContainer() {
- switch (Constants.currentMode) {
- case REAL:
- // Real robot, instantiate hardware IO implementations
- drive =
- new Drive(
- new GyroIOPigeon2(false),
- new ModuleIOSparkMax(0),
- new ModuleIOSparkMax(1),
- new ModuleIOSparkMax(2),
- new ModuleIOSparkMax(3));
- flywheel = new Flywheel(new FlywheelIOSparkMax());
- // drive = new Drive(
- // new GyroIOPigeon2(true),
- // new ModuleIOTalonFX(0),
- // new ModuleIOTalonFX(1),
- // new ModuleIOTalonFX(2),
- // new ModuleIOTalonFX(3));
- // flywheel = new Flywheel(new FlywheelIOTalonFX());
- break;
+ /**
+ * The container for the robot. Contains subsystems, OI devices, and commands.
+ */
+ public RobotContainer() {
+ switch (Constants.currentMode) {
+ case REAL:
+ // Real robot, instantiate hardware IO implementations
+ drive =
+ new Drive(
+ new GyroIOPigeon2(false),
+ new ModuleIOSparkMax(0),
+ new ModuleIOSparkMax(1),
+ new ModuleIOSparkMax(2),
+ new ModuleIOSparkMax(3));
+ flywheel = new Flywheel(new FlywheelIOSparkMax());
+ // drive = new Drive(
+ // new GyroIOPigeon2(true),
+ // new ModuleIOTalonFX(0),
+ // new ModuleIOTalonFX(1),
+ // new ModuleIOTalonFX(2),
+ // new ModuleIOTalonFX(3));
+ // flywheel = new Flywheel(new FlywheelIOTalonFX());
+ break;
- case SIM:
- // Sim robot, instantiate physics sim IO implementations
- drive =
- new Drive(
- new GyroIO() {},
- new ModuleIOSim(),
- new ModuleIOSim(),
- new ModuleIOSim(),
- new ModuleIOSim());
- flywheel = new Flywheel(new FlywheelIOSim());
- break;
+ case SIM:
+ // Sim robot, instantiate physics sim IO implementations
+ drive =
+ new Drive(
+ new GyroIO() {
+ },
+ new ModuleIOSim(),
+ new ModuleIOSim(),
+ new ModuleIOSim(),
+ new ModuleIOSim());
+ flywheel = new Flywheel(new FlywheelIOSim());
+ break;
- default:
- // Replayed robot, disable IO implementations
- drive =
- new Drive(
- new GyroIO() {},
- new ModuleIO() {},
- new ModuleIO() {},
- new ModuleIO() {},
- new ModuleIO() {});
- flywheel = new Flywheel(new FlywheelIO() {});
- break;
- }
+ default:
+ // Replayed robot, disable IO implementations
+ drive =
+ new Drive(
+ new GyroIO() {
+ },
+ new ModuleIO() {
+ },
+ new ModuleIO() {
+ },
+ new ModuleIO() {
+ },
+ new ModuleIO() {
+ });
+ flywheel = new Flywheel(new FlywheelIO() {
+ });
+ break;
+ }
- // Set up auto routines
- NamedCommands.registerCommand(
- "Run Flywheel",
- Commands.startEnd(
- () -> flywheel.runVelocity(flywheelSpeedInput.get()), flywheel::stop, flywheel)
- .withTimeout(5.0));
- autoChooser = new LoggedDashboardChooser<>("Auto Choices", AutoBuilder.buildAutoChooser());
+ // Set up auto routines
+ NamedCommands.registerCommand(
+ "Run Flywheel",
+ Commands.startEnd(
+ () -> flywheel.runVelocity(flywheelSpeedInput.get()), flywheel::stop, flywheel)
+ .withTimeout(5.0));
+ autoChooser = new LoggedDashboardChooser<>("Auto Choices", AutoBuilder.buildAutoChooser());
- // Set up SysId routines
- autoChooser.addOption(
- "Drive SysId (Quasistatic Forward)",
- drive.sysIdQuasistatic(SysIdRoutine.Direction.kForward));
- autoChooser.addOption(
- "Drive SysId (Quasistatic Reverse)",
- drive.sysIdQuasistatic(SysIdRoutine.Direction.kReverse));
- autoChooser.addOption(
- "Drive SysId (Dynamic Forward)", drive.sysIdDynamic(SysIdRoutine.Direction.kForward));
- autoChooser.addOption(
- "Drive SysId (Dynamic Reverse)", drive.sysIdDynamic(SysIdRoutine.Direction.kReverse));
- autoChooser.addOption(
- "Flywheel SysId (Quasistatic Forward)",
- flywheel.sysIdQuasistatic(SysIdRoutine.Direction.kForward));
- autoChooser.addOption(
- "Flywheel SysId (Quasistatic Reverse)",
- flywheel.sysIdQuasistatic(SysIdRoutine.Direction.kReverse));
- autoChooser.addOption(
- "Flywheel SysId (Dynamic Forward)", flywheel.sysIdDynamic(SysIdRoutine.Direction.kForward));
- autoChooser.addOption(
- "Flywheel SysId (Dynamic Reverse)", flywheel.sysIdDynamic(SysIdRoutine.Direction.kReverse));
+ // Set up SysId routines
+ autoChooser.addOption(
+ "Drive SysId (Quasistatic Forward)",
+ drive.sysIdQuasistatic(SysIdRoutine.Direction.kForward));
+ autoChooser.addOption(
+ "Drive SysId (Quasistatic Reverse)",
+ drive.sysIdQuasistatic(SysIdRoutine.Direction.kReverse));
+ autoChooser.addOption(
+ "Drive SysId (Dynamic Forward)", drive.sysIdDynamic(SysIdRoutine.Direction.kForward));
+ autoChooser.addOption(
+ "Drive SysId (Dynamic Reverse)", drive.sysIdDynamic(SysIdRoutine.Direction.kReverse));
+ autoChooser.addOption(
+ "Flywheel SysId (Quasistatic Forward)",
+ flywheel.sysIdQuasistatic(SysIdRoutine.Direction.kForward));
+ autoChooser.addOption(
+ "Flywheel SysId (Quasistatic Reverse)",
+ flywheel.sysIdQuasistatic(SysIdRoutine.Direction.kReverse));
+ autoChooser.addOption(
+ "Flywheel SysId (Dynamic Forward)", flywheel.sysIdDynamic(SysIdRoutine.Direction.kForward));
+ autoChooser.addOption(
+ "Flywheel SysId (Dynamic Reverse)", flywheel.sysIdDynamic(SysIdRoutine.Direction.kReverse));
- // Configure the button bindings
- configureButtonBindings();
- }
+ // Configure the button bindings
+ configureButtonBindings();
+ }
- /**
- * Use this method to define your button->command mappings. Buttons can be created by
- * instantiating a {@link GenericHID} or one of its subclasses ({@link
- * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a {@link
- * edu.wpi.first.wpilibj2.command.button.JoystickButton}.
- */
- private void configureButtonBindings() {
- drive.setDefaultCommand(
- DriveCommands.joystickDrive(
- drive,
- () -> -controller.getLeftY(),
- () -> -controller.getLeftX(),
- () -> -controller.getRightX()));
- controller.x().onTrue(Commands.runOnce(drive::stopWithX, drive));
- controller
- .b()
- .onTrue(
- Commands.runOnce(
- () ->
- drive.setPose(
- new Pose2d(drive.getPose().getTranslation(), new Rotation2d())),
- drive)
- .ignoringDisable(true));
- controller
- .a()
- .whileTrue(
- Commands.startEnd(
- () -> flywheel.runVelocity(flywheelSpeedInput.get()), flywheel::stop, flywheel));
- }
+ /**
+ * Use this method to define your button->command mappings. Buttons can be created by
+ * instantiating a {@link GenericHID} or one of its subclasses ({@link
+ * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a {@link
+ * edu.wpi.first.wpilibj2.command.button.JoystickButton}.
+ */
+ private void configureButtonBindings() {
+ drive.setDefaultCommand(
+ DriveCommands.joystickDrive(
+ drive,
+ () -> -controller.getLeftY(),
+ () -> -controller.getLeftX(),
+ () -> -controller.getRightX()));
+ controller.x().onTrue(Commands.runOnce(drive::stopWithX, drive));
+ controller
+ .b()
+ .onTrue(
+ Commands.runOnce(
+ () ->
+ drive.setPose(
+ new Pose2d(drive.getPose().getTranslation(), new Rotation2d())),
+ drive)
+ .ignoringDisable(true));
+ controller
+ .a()
+ .whileTrue(
+ Commands.startEnd(
+ () -> flywheel.runVelocity(flywheelSpeedInput.get()), flywheel::stop, flywheel));
+ }
- /**
- * Use this to pass the autonomous command to the main {@link Robot} class.
- *
- * @return the command to run in autonomous
- */
- public Command getAutonomousCommand() {
- return autoChooser.get();
- }
+ /**
+ * Use this to pass the autonomous command to the main {@link Robot} class.
+ *
+ * @return the command to run in autonomous
+ */
+ public Command getAutonomousCommand() {
+ return autoChooser.get();
+ }
}
diff --git a/src/main/java/frc/robot/commands/DriveCommands.java b/src/main/java/frc/robot/commands/DriveCommands.java
index fc8f596..330d5e4 100644
--- a/src/main/java/frc/robot/commands/DriveCommands.java
+++ b/src/main/java/frc/robot/commands/DriveCommands.java
@@ -24,54 +24,56 @@
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.Commands;
import frc.robot.subsystems.drive.Drive;
+
import java.util.function.DoubleSupplier;
public class DriveCommands {
- private static final double DEADBAND = 0.1;
+ private static final double DEADBAND = 0.1;
- private DriveCommands() {}
+ private DriveCommands() {
+ }
- /**
- * Field relative drive command using two joysticks (controlling linear and angular velocities).
- */
- public static Command joystickDrive(
- Drive drive,
- DoubleSupplier xSupplier,
- DoubleSupplier ySupplier,
- DoubleSupplier omegaSupplier) {
- return Commands.run(
- () -> {
- // Apply deadband
- double linearMagnitude =
- MathUtil.applyDeadband(
- Math.hypot(xSupplier.getAsDouble(), ySupplier.getAsDouble()), DEADBAND);
- Rotation2d linearDirection =
- new Rotation2d(xSupplier.getAsDouble(), ySupplier.getAsDouble());
- double omega = MathUtil.applyDeadband(omegaSupplier.getAsDouble(), DEADBAND);
+ /**
+ * Field relative drive command using two joysticks (controlling linear and angular velocities).
+ */
+ public static Command joystickDrive(
+ Drive drive,
+ DoubleSupplier xSupplier,
+ DoubleSupplier ySupplier,
+ DoubleSupplier omegaSupplier) {
+ return Commands.run(
+ () -> {
+ // Apply deadband
+ double linearMagnitude =
+ MathUtil.applyDeadband(
+ Math.hypot(xSupplier.getAsDouble(), ySupplier.getAsDouble()), DEADBAND);
+ Rotation2d linearDirection =
+ new Rotation2d(xSupplier.getAsDouble(), ySupplier.getAsDouble());
+ double omega = MathUtil.applyDeadband(omegaSupplier.getAsDouble(), DEADBAND);
- // Square values
- linearMagnitude = linearMagnitude * linearMagnitude;
- omega = Math.copySign(omega * omega, omega);
+ // Square values
+ linearMagnitude = linearMagnitude * linearMagnitude;
+ omega = Math.copySign(omega * omega, omega);
- // Calcaulate new linear velocity
- Translation2d linearVelocity =
- new Pose2d(new Translation2d(), linearDirection)
- .transformBy(new Transform2d(linearMagnitude, 0.0, new Rotation2d()))
- .getTranslation();
+ // Calcaulate new linear velocity
+ Translation2d linearVelocity =
+ new Pose2d(new Translation2d(), linearDirection)
+ .transformBy(new Transform2d(linearMagnitude, 0.0, new Rotation2d()))
+ .getTranslation();
- // Convert to field relative speeds & send command
- boolean isFlipped =
- DriverStation.getAlliance().isPresent()
- && DriverStation.getAlliance().get() == Alliance.Red;
- drive.runVelocity(
- ChassisSpeeds.fromFieldRelativeSpeeds(
- linearVelocity.getX() * drive.getMaxLinearSpeedMetersPerSec(),
- linearVelocity.getY() * drive.getMaxLinearSpeedMetersPerSec(),
- omega * drive.getMaxAngularSpeedRadPerSec(),
- isFlipped
- ? drive.getRotation().plus(new Rotation2d(Math.PI))
- : drive.getRotation()));
- },
- drive);
- }
+ // Convert to field relative speeds & send command
+ boolean isFlipped =
+ DriverStation.getAlliance().isPresent()
+ && DriverStation.getAlliance().get() == Alliance.Red;
+ drive.runVelocity(
+ ChassisSpeeds.fromFieldRelativeSpeeds(
+ linearVelocity.getX() * drive.getMaxLinearSpeedMetersPerSec(),
+ linearVelocity.getY() * drive.getMaxLinearSpeedMetersPerSec(),
+ omega * drive.getMaxAngularSpeedRadPerSec(),
+ isFlipped
+ ? drive.getRotation().plus(new Rotation2d(Math.PI))
+ : drive.getRotation()));
+ },
+ drive);
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/Drive.java b/src/main/java/frc/robot/subsystems/drive/Drive.java
index 51d02c0..288d22e 100644
--- a/src/main/java/frc/robot/subsystems/drive/Drive.java
+++ b/src/main/java/frc/robot/subsystems/drive/Drive.java
@@ -36,264 +36,288 @@
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
import frc.robot.util.LocalADStarAK;
+
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+
import org.littletonrobotics.junction.AutoLogOutput;
import org.littletonrobotics.junction.Logger;
public class Drive extends SubsystemBase {
- private static final double MAX_LINEAR_SPEED = Units.feetToMeters(14.5);
- private static final double TRACK_WIDTH_X = Units.inchesToMeters(25.0);
- private static final double TRACK_WIDTH_Y = Units.inchesToMeters(25.0);
- private static final double DRIVE_BASE_RADIUS =
- Math.hypot(TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0);
- private static final double MAX_ANGULAR_SPEED = MAX_LINEAR_SPEED / DRIVE_BASE_RADIUS;
-
- static final Lock odometryLock = new ReentrantLock();
- private final GyroIO gyroIO;
- private final GyroIOInputsAutoLogged gyroInputs = new GyroIOInputsAutoLogged();
- private final Module[] modules = new Module[4]; // FL, FR, BL, BR
- private final SysIdRoutine sysId;
-
- private SwerveDriveKinematics kinematics = new SwerveDriveKinematics(getModuleTranslations());
- private Rotation2d rawGyroRotation = new Rotation2d();
- private SwerveModulePosition[] lastModulePositions = // For delta tracking
- new SwerveModulePosition[] {
- new SwerveModulePosition(),
- new SwerveModulePosition(),
- new SwerveModulePosition(),
- new SwerveModulePosition()
- };
- private SwerveDrivePoseEstimator poseEstimator =
- new SwerveDrivePoseEstimator(kinematics, rawGyroRotation, lastModulePositions, new Pose2d());
-
- public Drive(
- GyroIO gyroIO,
- ModuleIO flModuleIO,
- ModuleIO frModuleIO,
- ModuleIO blModuleIO,
- ModuleIO brModuleIO) {
- this.gyroIO = gyroIO;
- modules[0] = new Module(flModuleIO, 0);
- modules[1] = new Module(frModuleIO, 1);
- modules[2] = new Module(blModuleIO, 2);
- modules[3] = new Module(brModuleIO, 3);
-
- // Start threads (no-op for each if no signals have been created)
- PhoenixOdometryThread.getInstance().start();
- SparkMaxOdometryThread.getInstance().start();
-
- // Configure AutoBuilder for PathPlanner
- AutoBuilder.configureHolonomic(
- this::getPose,
- this::setPose,
- () -> kinematics.toChassisSpeeds(getModuleStates()),
- this::runVelocity,
- new HolonomicPathFollowerConfig(
- MAX_LINEAR_SPEED, DRIVE_BASE_RADIUS, new ReplanningConfig()),
- () ->
- DriverStation.getAlliance().isPresent()
- && DriverStation.getAlliance().get() == Alliance.Red,
- this);
- Pathfinding.setPathfinder(new LocalADStarAK());
- PathPlannerLogging.setLogActivePathCallback(
- (activePath) -> {
- Logger.recordOutput(
- "Odometry/Trajectory", activePath.toArray(new Pose2d[activePath.size()]));
- });
- PathPlannerLogging.setLogTargetPoseCallback(
- (targetPose) -> {
- Logger.recordOutput("Odometry/TrajectorySetpoint", targetPose);
- });
-
- // Configure SysId
- sysId =
- new SysIdRoutine(
- new SysIdRoutine.Config(
- null,
- null,
- null,
- (state) -> Logger.recordOutput("Drive/SysIdState", state.toString())),
- new SysIdRoutine.Mechanism(
- (voltage) -> {
- for (int i = 0; i < 4; i++) {
- modules[i].runCharacterization(voltage.in(Volts));
- }
- },
- null,
- this));
- }
-
- public void periodic() {
- odometryLock.lock(); // Prevents odometry updates while reading data
- gyroIO.updateInputs(gyroInputs);
- for (var module : modules) {
- module.updateInputs();
+ private static final double MAX_LINEAR_SPEED = Units.feetToMeters(14.5);
+ private static final double TRACK_WIDTH_X = Units.inchesToMeters(25.0);
+ private static final double TRACK_WIDTH_Y = Units.inchesToMeters(25.0);
+ private static final double DRIVE_BASE_RADIUS =
+ Math.hypot(TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0);
+ private static final double MAX_ANGULAR_SPEED = MAX_LINEAR_SPEED / DRIVE_BASE_RADIUS;
+
+ static final Lock odometryLock = new ReentrantLock();
+ private final GyroIO gyroIO;
+ private final GyroIOInputsAutoLogged gyroInputs = new GyroIOInputsAutoLogged();
+ private final Module[] modules = new Module[4]; // FL, FR, BL, BR
+ private final SysIdRoutine sysId;
+
+ private SwerveDriveKinematics kinematics = new SwerveDriveKinematics(getModuleTranslations());
+ private Rotation2d rawGyroRotation = new Rotation2d();
+ private SwerveModulePosition[] lastModulePositions = // For delta tracking
+ new SwerveModulePosition[]{
+ new SwerveModulePosition(),
+ new SwerveModulePosition(),
+ new SwerveModulePosition(),
+ new SwerveModulePosition()
+ };
+ private SwerveDrivePoseEstimator poseEstimator =
+ new SwerveDrivePoseEstimator(kinematics, rawGyroRotation, lastModulePositions, new Pose2d());
+
+ public Drive(
+ GyroIO gyroIO,
+ ModuleIO flModuleIO,
+ ModuleIO frModuleIO,
+ ModuleIO blModuleIO,
+ ModuleIO brModuleIO) {
+ this.gyroIO = gyroIO;
+ modules[0] = new Module(flModuleIO, 0);
+ modules[1] = new Module(frModuleIO, 1);
+ modules[2] = new Module(blModuleIO, 2);
+ modules[3] = new Module(brModuleIO, 3);
+
+ // Start threads (no-op for each if no signals have been created)
+ PhoenixOdometryThread.getInstance().start();
+ SparkMaxOdometryThread.getInstance().start();
+
+ // Configure AutoBuilder for PathPlanner
+ AutoBuilder.configureHolonomic(
+ this::getPose,
+ this::setPose,
+ () -> kinematics.toChassisSpeeds(getModuleStates()),
+ this::runVelocity,
+ new HolonomicPathFollowerConfig(
+ MAX_LINEAR_SPEED, DRIVE_BASE_RADIUS, new ReplanningConfig()),
+ () ->
+ DriverStation.getAlliance().isPresent()
+ && DriverStation.getAlliance().get() == Alliance.Red,
+ this);
+ Pathfinding.setPathfinder(new LocalADStarAK());
+ PathPlannerLogging.setLogActivePathCallback(
+ (activePath) -> {
+ Logger.recordOutput(
+ "Odometry/Trajectory", activePath.toArray(new Pose2d[activePath.size()]));
+ });
+ PathPlannerLogging.setLogTargetPoseCallback(
+ (targetPose) -> {
+ Logger.recordOutput("Odometry/TrajectorySetpoint", targetPose);
+ });
+
+ // Configure SysId
+ sysId =
+ new SysIdRoutine(
+ new SysIdRoutine.Config(
+ null,
+ null,
+ null,
+ (state) -> Logger.recordOutput("Drive/SysIdState", state.toString())),
+ new SysIdRoutine.Mechanism(
+ (voltage) -> {
+ for (int i = 0; i < 4; i++) {
+ modules[i].runCharacterization(voltage.in(Volts));
+ }
+ },
+ null,
+ this));
}
- odometryLock.unlock();
- Logger.processInputs("Drive/Gyro", gyroInputs);
- for (var module : modules) {
- module.periodic();
+
+ public void periodic() {
+ odometryLock.lock(); // Prevents odometry updates while reading data
+ gyroIO.updateInputs(gyroInputs);
+ for (var module : modules) {
+ module.updateInputs();
+ }
+ odometryLock.unlock();
+ Logger.processInputs("Drive/Gyro", gyroInputs);
+ for (var module : modules) {
+ module.periodic();
+ }
+
+ // Stop moving when disabled
+ if (DriverStation.isDisabled()) {
+ for (var module : modules) {
+ module.stop();
+ }
+ }
+ // Log empty setpoint states when disabled
+ if (DriverStation.isDisabled()) {
+ Logger.recordOutput("SwerveStates/Setpoints", new SwerveModuleState[]{});
+ Logger.recordOutput("SwerveStates/SetpointsOptimized", new SwerveModuleState[]{});
+ }
+
+ // Update odometry
+ double[] sampleTimestamps =
+ modules[0].getOdometryTimestamps(); // All signals are sampled together
+ int sampleCount = sampleTimestamps.length;
+ for (int i = 0; i < sampleCount; i++) {
+ // Read wheel positions and deltas from each module
+ SwerveModulePosition[] modulePositions = new SwerveModulePosition[4];
+ SwerveModulePosition[] moduleDeltas = new SwerveModulePosition[4];
+ for (int moduleIndex = 0; moduleIndex < 4; moduleIndex++) {
+ modulePositions[moduleIndex] = modules[moduleIndex].getOdometryPositions()[i];
+ moduleDeltas[moduleIndex] =
+ new SwerveModulePosition(
+ modulePositions[moduleIndex].distanceMeters
+ - lastModulePositions[moduleIndex].distanceMeters,
+ modulePositions[moduleIndex].angle);
+ lastModulePositions[moduleIndex] = modulePositions[moduleIndex];
+ }
+
+ // Update gyro angle
+ if (gyroInputs.connected) {
+ // Use the real gyro angle
+ rawGyroRotation = gyroInputs.odometryYawPositions[i];
+ } else {
+ // Use the angle delta from the kinematics and module deltas
+ Twist2d twist = kinematics.toTwist2d(moduleDeltas);
+ rawGyroRotation = rawGyroRotation.plus(new Rotation2d(twist.dtheta));
+ }
+
+ // Apply update
+ poseEstimator.updateWithTime(sampleTimestamps[i], rawGyroRotation, modulePositions);
+ }
}
- // Stop moving when disabled
- if (DriverStation.isDisabled()) {
- for (var module : modules) {
- module.stop();
- }
+ /**
+ * Runs the drive at the desired velocity.
+ *
+ * @param speeds Speeds in meters/sec
+ */
+ public void runVelocity(ChassisSpeeds speeds) {
+ // Calculate module setpoints
+ ChassisSpeeds discreteSpeeds = ChassisSpeeds.discretize(speeds, 0.02);
+ SwerveModuleState[] setpointStates = kinematics.toSwerveModuleStates(discreteSpeeds);
+ SwerveDriveKinematics.desaturateWheelSpeeds(setpointStates, MAX_LINEAR_SPEED);
+
+ // Send setpoints to modules
+ SwerveModuleState[] optimizedSetpointStates = new SwerveModuleState[4];
+ for (int i = 0; i < 4; i++) {
+ // The module returns the optimized state, useful for logging
+ optimizedSetpointStates[i] = modules[i].runSetpoint(setpointStates[i]);
+ }
+
+ // Log setpoint states
+ Logger.recordOutput("SwerveStates/Setpoints", setpointStates);
+ Logger.recordOutput("SwerveStates/SetpointsOptimized", optimizedSetpointStates);
}
- // Log empty setpoint states when disabled
- if (DriverStation.isDisabled()) {
- Logger.recordOutput("SwerveStates/Setpoints", new SwerveModuleState[] {});
- Logger.recordOutput("SwerveStates/SetpointsOptimized", new SwerveModuleState[] {});
+
+ /**
+ * Stops the drive.
+ */
+ public void stop() {
+ runVelocity(new ChassisSpeeds());
}
- // Update odometry
- double[] sampleTimestamps =
- modules[0].getOdometryTimestamps(); // All signals are sampled together
- int sampleCount = sampleTimestamps.length;
- for (int i = 0; i < sampleCount; i++) {
- // Read wheel positions and deltas from each module
- SwerveModulePosition[] modulePositions = new SwerveModulePosition[4];
- SwerveModulePosition[] moduleDeltas = new SwerveModulePosition[4];
- for (int moduleIndex = 0; moduleIndex < 4; moduleIndex++) {
- modulePositions[moduleIndex] = modules[moduleIndex].getOdometryPositions()[i];
- moduleDeltas[moduleIndex] =
- new SwerveModulePosition(
- modulePositions[moduleIndex].distanceMeters
- - lastModulePositions[moduleIndex].distanceMeters,
- modulePositions[moduleIndex].angle);
- lastModulePositions[moduleIndex] = modulePositions[moduleIndex];
- }
-
- // Update gyro angle
- if (gyroInputs.connected) {
- // Use the real gyro angle
- rawGyroRotation = gyroInputs.odometryYawPositions[i];
- } else {
- // Use the angle delta from the kinematics and module deltas
- Twist2d twist = kinematics.toTwist2d(moduleDeltas);
- rawGyroRotation = rawGyroRotation.plus(new Rotation2d(twist.dtheta));
- }
-
- // Apply update
- poseEstimator.updateWithTime(sampleTimestamps[i], rawGyroRotation, modulePositions);
+ /**
+ * Stops the drive and turns the modules to an X arrangement to resist movement. The modules will
+ * return to their normal orientations the next time a nonzero velocity is requested.
+ */
+ public void stopWithX() {
+ Rotation2d[] headings = new Rotation2d[4];
+ for (int i = 0; i < 4; i++) {
+ headings[i] = getModuleTranslations()[i].getAngle();
+ }
+ kinematics.resetHeadings(headings);
+ stop();
}
- }
-
- /**
- * Runs the drive at the desired velocity.
- *
- * @param speeds Speeds in meters/sec
- */
- public void runVelocity(ChassisSpeeds speeds) {
- // Calculate module setpoints
- ChassisSpeeds discreteSpeeds = ChassisSpeeds.discretize(speeds, 0.02);
- SwerveModuleState[] setpointStates = kinematics.toSwerveModuleStates(discreteSpeeds);
- SwerveDriveKinematics.desaturateWheelSpeeds(setpointStates, MAX_LINEAR_SPEED);
-
- // Send setpoints to modules
- SwerveModuleState[] optimizedSetpointStates = new SwerveModuleState[4];
- for (int i = 0; i < 4; i++) {
- // The module returns the optimized state, useful for logging
- optimizedSetpointStates[i] = modules[i].runSetpoint(setpointStates[i]);
+
+ /**
+ * Returns a command to run a quasistatic test in the specified direction.
+ */
+ public Command sysIdQuasistatic(SysIdRoutine.Direction direction) {
+ return sysId.quasistatic(direction);
}
- // Log setpoint states
- Logger.recordOutput("SwerveStates/Setpoints", setpointStates);
- Logger.recordOutput("SwerveStates/SetpointsOptimized", optimizedSetpointStates);
- }
-
- /** Stops the drive. */
- public void stop() {
- runVelocity(new ChassisSpeeds());
- }
-
- /**
- * Stops the drive and turns the modules to an X arrangement to resist movement. The modules will
- * return to their normal orientations the next time a nonzero velocity is requested.
- */
- public void stopWithX() {
- Rotation2d[] headings = new Rotation2d[4];
- for (int i = 0; i < 4; i++) {
- headings[i] = getModuleTranslations()[i].getAngle();
+ /**
+ * Returns a command to run a dynamic test in the specified direction.
+ */
+ public Command sysIdDynamic(SysIdRoutine.Direction direction) {
+ return sysId.dynamic(direction);
}
- kinematics.resetHeadings(headings);
- stop();
- }
-
- /** Returns a command to run a quasistatic test in the specified direction. */
- public Command sysIdQuasistatic(SysIdRoutine.Direction direction) {
- return sysId.quasistatic(direction);
- }
-
- /** Returns a command to run a dynamic test in the specified direction. */
- public Command sysIdDynamic(SysIdRoutine.Direction direction) {
- return sysId.dynamic(direction);
- }
-
- /** Returns the module states (turn angles and drive velocities) for all of the modules. */
- @AutoLogOutput(key = "SwerveStates/Measured")
- private SwerveModuleState[] getModuleStates() {
- SwerveModuleState[] states = new SwerveModuleState[4];
- for (int i = 0; i < 4; i++) {
- states[i] = modules[i].getState();
+
+ /**
+ * Returns the module states (turn angles and drive velocities) for all of the modules.
+ */
+ @AutoLogOutput(key = "SwerveStates/Measured")
+ private SwerveModuleState[] getModuleStates() {
+ SwerveModuleState[] states = new SwerveModuleState[4];
+ for (int i = 0; i < 4; i++) {
+ states[i] = modules[i].getState();
+ }
+ return states;
+ }
+
+ /**
+ * Returns the module positions (turn angles and drive positions) for all of the modules.
+ */
+ private SwerveModulePosition[] getModulePositions() {
+ SwerveModulePosition[] states = new SwerveModulePosition[4];
+ for (int i = 0; i < 4; i++) {
+ states[i] = modules[i].getPosition();
+ }
+ return states;
+ }
+
+ /**
+ * Returns the current odometry pose.
+ */
+ @AutoLogOutput(key = "Odometry/Robot")
+ public Pose2d getPose() {
+ return poseEstimator.getEstimatedPosition();
+ }
+
+ /**
+ * Returns the current odometry rotation.
+ */
+ public Rotation2d getRotation() {
+ return getPose().getRotation();
+ }
+
+ /**
+ * Resets the current odometry pose.
+ */
+ public void setPose(Pose2d pose) {
+ poseEstimator.resetPosition(rawGyroRotation, getModulePositions(), pose);
+ }
+
+ /**
+ * Adds a vision measurement to the pose estimator.
+ *
+ * @param visionPose The pose of the robot as measured by the vision camera.
+ * @param timestamp The timestamp of the vision measurement in seconds.
+ */
+ public void addVisionMeasurement(Pose2d visionPose, double timestamp) {
+ poseEstimator.addVisionMeasurement(visionPose, timestamp);
+ }
+
+ /**
+ * Returns the maximum linear speed in meters per sec.
+ */
+ public double getMaxLinearSpeedMetersPerSec() {
+ return MAX_LINEAR_SPEED;
+ }
+
+ /**
+ * Returns the maximum angular speed in radians per sec.
+ */
+ public double getMaxAngularSpeedRadPerSec() {
+ return MAX_ANGULAR_SPEED;
}
- return states;
- }
-
- /** Returns the module positions (turn angles and drive positions) for all of the modules. */
- private SwerveModulePosition[] getModulePositions() {
- SwerveModulePosition[] states = new SwerveModulePosition[4];
- for (int i = 0; i < 4; i++) {
- states[i] = modules[i].getPosition();
+
+ /**
+ * Returns an array of module translations.
+ */
+ public static Translation2d[] getModuleTranslations() {
+ return new Translation2d[]{
+ new Translation2d(TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0),
+ new Translation2d(TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0),
+ new Translation2d(-TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0),
+ new Translation2d(-TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0)
+ };
}
- return states;
- }
-
- /** Returns the current odometry pose. */
- @AutoLogOutput(key = "Odometry/Robot")
- public Pose2d getPose() {
- return poseEstimator.getEstimatedPosition();
- }
-
- /** Returns the current odometry rotation. */
- public Rotation2d getRotation() {
- return getPose().getRotation();
- }
-
- /** Resets the current odometry pose. */
- public void setPose(Pose2d pose) {
- poseEstimator.resetPosition(rawGyroRotation, getModulePositions(), pose);
- }
-
- /**
- * Adds a vision measurement to the pose estimator.
- *
- * @param visionPose The pose of the robot as measured by the vision camera.
- * @param timestamp The timestamp of the vision measurement in seconds.
- */
- public void addVisionMeasurement(Pose2d visionPose, double timestamp) {
- poseEstimator.addVisionMeasurement(visionPose, timestamp);
- }
-
- /** Returns the maximum linear speed in meters per sec. */
- public double getMaxLinearSpeedMetersPerSec() {
- return MAX_LINEAR_SPEED;
- }
-
- /** Returns the maximum angular speed in radians per sec. */
- public double getMaxAngularSpeedRadPerSec() {
- return MAX_ANGULAR_SPEED;
- }
-
- /** Returns an array of module translations. */
- public static Translation2d[] getModuleTranslations() {
- return new Translation2d[] {
- new Translation2d(TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0),
- new Translation2d(TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0),
- new Translation2d(-TRACK_WIDTH_X / 2.0, TRACK_WIDTH_Y / 2.0),
- new Translation2d(-TRACK_WIDTH_X / 2.0, -TRACK_WIDTH_Y / 2.0)
- };
- }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/GyroIO.java b/src/main/java/frc/robot/subsystems/drive/GyroIO.java
index 18ce6fd..9e17e75 100644
--- a/src/main/java/frc/robot/subsystems/drive/GyroIO.java
+++ b/src/main/java/frc/robot/subsystems/drive/GyroIO.java
@@ -17,14 +17,15 @@
import org.littletonrobotics.junction.AutoLog;
public interface GyroIO {
- @AutoLog
- public static class GyroIOInputs {
- public boolean connected = false;
- public Rotation2d yawPosition = new Rotation2d();
- public double[] odometryYawTimestamps = new double[] {};
- public Rotation2d[] odometryYawPositions = new Rotation2d[] {};
- public double yawVelocityRadPerSec = 0.0;
- }
+ @AutoLog
+ public static class GyroIOInputs {
+ public boolean connected = false;
+ public Rotation2d yawPosition = new Rotation2d();
+ public double[] odometryYawTimestamps = new double[]{};
+ public Rotation2d[] odometryYawPositions = new Rotation2d[]{};
+ public double yawVelocityRadPerSec = 0.0;
+ }
- public default void updateInputs(GyroIOInputs inputs) {}
+ public default void updateInputs(GyroIOInputs inputs) {
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/GyroIOPigeon2.java b/src/main/java/frc/robot/subsystems/drive/GyroIOPigeon2.java
index 01ccef2..51460e6 100644
--- a/src/main/java/frc/robot/subsystems/drive/GyroIOPigeon2.java
+++ b/src/main/java/frc/robot/subsystems/drive/GyroIOPigeon2.java
@@ -20,56 +20,59 @@
import com.ctre.phoenix6.hardware.Pigeon2;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.util.Units;
+
import java.util.OptionalDouble;
import java.util.Queue;
-/** IO implementation for Pigeon2 */
+/**
+ * IO implementation for Pigeon2
+ */
public class GyroIOPigeon2 implements GyroIO {
- private final Pigeon2 pigeon = new Pigeon2(20);
- private final StatusSignal yaw = pigeon.getYaw();
- private final Queue yawPositionQueue;
- private final Queue yawTimestampQueue;
- private final StatusSignal yawVelocity = pigeon.getAngularVelocityZWorld();
+ private final Pigeon2 pigeon = new Pigeon2(20);
+ private final StatusSignal yaw = pigeon.getYaw();
+ private final Queue yawPositionQueue;
+ private final Queue yawTimestampQueue;
+ private final StatusSignal yawVelocity = pigeon.getAngularVelocityZWorld();
- public GyroIOPigeon2(boolean phoenixDrive) {
- pigeon.getConfigurator().apply(new Pigeon2Configuration());
- pigeon.getConfigurator().setYaw(0.0);
- yaw.setUpdateFrequency(Module.ODOMETRY_FREQUENCY);
- yawVelocity.setUpdateFrequency(100.0);
- pigeon.optimizeBusUtilization();
- if (phoenixDrive) {
- yawTimestampQueue = PhoenixOdometryThread.getInstance().makeTimestampQueue();
- yawPositionQueue =
- PhoenixOdometryThread.getInstance().registerSignal(pigeon, pigeon.getYaw());
- } else {
- yawTimestampQueue = SparkMaxOdometryThread.getInstance().makeTimestampQueue();
- yawPositionQueue =
- SparkMaxOdometryThread.getInstance()
- .registerSignal(
- () -> {
- boolean valid = yaw.refresh().getStatus().isOK();
- if (valid) {
- return OptionalDouble.of(yaw.getValueAsDouble());
- } else {
- return OptionalDouble.empty();
- }
- });
+ public GyroIOPigeon2(boolean phoenixDrive) {
+ pigeon.getConfigurator().apply(new Pigeon2Configuration());
+ pigeon.getConfigurator().setYaw(0.0);
+ yaw.setUpdateFrequency(Module.ODOMETRY_FREQUENCY);
+ yawVelocity.setUpdateFrequency(100.0);
+ pigeon.optimizeBusUtilization();
+ if (phoenixDrive) {
+ yawTimestampQueue = PhoenixOdometryThread.getInstance().makeTimestampQueue();
+ yawPositionQueue =
+ PhoenixOdometryThread.getInstance().registerSignal(pigeon, pigeon.getYaw());
+ } else {
+ yawTimestampQueue = SparkMaxOdometryThread.getInstance().makeTimestampQueue();
+ yawPositionQueue =
+ SparkMaxOdometryThread.getInstance()
+ .registerSignal(
+ () -> {
+ boolean valid = yaw.refresh().getStatus().isOK();
+ if (valid) {
+ return OptionalDouble.of(yaw.getValueAsDouble());
+ } else {
+ return OptionalDouble.empty();
+ }
+ });
+ }
}
- }
- @Override
- public void updateInputs(GyroIOInputs inputs) {
- inputs.connected = BaseStatusSignal.refreshAll(yaw, yawVelocity).equals(StatusCode.OK);
- inputs.yawPosition = Rotation2d.fromDegrees(yaw.getValueAsDouble());
- inputs.yawVelocityRadPerSec = Units.degreesToRadians(yawVelocity.getValueAsDouble());
+ @Override
+ public void updateInputs(GyroIOInputs inputs) {
+ inputs.connected = BaseStatusSignal.refreshAll(yaw, yawVelocity).equals(StatusCode.OK);
+ inputs.yawPosition = Rotation2d.fromDegrees(yaw.getValueAsDouble());
+ inputs.yawVelocityRadPerSec = Units.degreesToRadians(yawVelocity.getValueAsDouble());
- inputs.odometryYawTimestamps =
- yawTimestampQueue.stream().mapToDouble((Double value) -> value).toArray();
- inputs.odometryYawPositions =
- yawPositionQueue.stream()
- .map((Double value) -> Rotation2d.fromDegrees(value))
- .toArray(Rotation2d[]::new);
- yawTimestampQueue.clear();
- yawPositionQueue.clear();
- }
+ inputs.odometryYawTimestamps =
+ yawTimestampQueue.stream().mapToDouble((Double value) -> value).toArray();
+ inputs.odometryYawPositions =
+ yawPositionQueue.stream()
+ .map((Double value) -> Rotation2d.fromDegrees(value))
+ .toArray(Rotation2d[]::new);
+ yawTimestampQueue.clear();
+ yawPositionQueue.clear();
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/Module.java b/src/main/java/frc/robot/subsystems/drive/Module.java
index 65016c4..e83c694 100644
--- a/src/main/java/frc/robot/subsystems/drive/Module.java
+++ b/src/main/java/frc/robot/subsystems/drive/Module.java
@@ -23,182 +23,206 @@
import org.littletonrobotics.junction.Logger;
public class Module {
- private static final double WHEEL_RADIUS = Units.inchesToMeters(2.0);
- static final double ODOMETRY_FREQUENCY = 250.0;
-
- private final ModuleIO io;
- private final ModuleIOInputsAutoLogged inputs = new ModuleIOInputsAutoLogged();
- private final int index;
-
- private final SimpleMotorFeedforward driveFeedforward;
- private final PIDController driveFeedback;
- private final PIDController turnFeedback;
- private Rotation2d angleSetpoint = null; // Setpoint for closed loop control, null for open loop
- private Double speedSetpoint = null; // Setpoint for closed loop control, null for open loop
- private Rotation2d turnRelativeOffset = null; // Relative + Offset = Absolute
- private SwerveModulePosition[] odometryPositions = new SwerveModulePosition[] {};
-
- public Module(ModuleIO io, int index) {
- this.io = io;
- this.index = index;
-
- // Switch constants based on mode (the physics simulator is treated as a
- // separate robot with different tuning)
- switch (Constants.currentMode) {
- case REAL:
- case REPLAY:
- driveFeedforward = new SimpleMotorFeedforward(0.1, 0.13);
- driveFeedback = new PIDController(0.05, 0.0, 0.0);
- turnFeedback = new PIDController(7.0, 0.0, 0.0);
- break;
- case SIM:
- driveFeedforward = new SimpleMotorFeedforward(0.0, 0.13);
- driveFeedback = new PIDController(0.1, 0.0, 0.0);
- turnFeedback = new PIDController(10.0, 0.0, 0.0);
- break;
- default:
- driveFeedforward = new SimpleMotorFeedforward(0.0, 0.0);
- driveFeedback = new PIDController(0.0, 0.0, 0.0);
- turnFeedback = new PIDController(0.0, 0.0, 0.0);
- break;
+ private static final double WHEEL_RADIUS = Units.inchesToMeters(2.0);
+ static final double ODOMETRY_FREQUENCY = 250.0;
+
+ private final ModuleIO io;
+ private final ModuleIOInputsAutoLogged inputs = new ModuleIOInputsAutoLogged();
+ private final int index;
+
+ private final SimpleMotorFeedforward driveFeedforward;
+ private final PIDController driveFeedback;
+ private final PIDController turnFeedback;
+ private Rotation2d angleSetpoint = null; // Setpoint for closed loop control, null for open loop
+ private Double speedSetpoint = null; // Setpoint for closed loop control, null for open loop
+ private Rotation2d turnRelativeOffset = null; // Relative + Offset = Absolute
+ private SwerveModulePosition[] odometryPositions = new SwerveModulePosition[]{};
+
+ public Module(ModuleIO io, int index) {
+ this.io = io;
+ this.index = index;
+
+ // Switch constants based on mode (the physics simulator is treated as a
+ // separate robot with different tuning)
+ switch (Constants.currentMode) {
+ case REAL:
+ case REPLAY:
+ driveFeedforward = new SimpleMotorFeedforward(0.1, 0.13);
+ driveFeedback = new PIDController(0.05, 0.0, 0.0);
+ turnFeedback = new PIDController(7.0, 0.0, 0.0);
+ break;
+ case SIM:
+ driveFeedforward = new SimpleMotorFeedforward(0.0, 0.13);
+ driveFeedback = new PIDController(0.1, 0.0, 0.0);
+ turnFeedback = new PIDController(10.0, 0.0, 0.0);
+ break;
+ default:
+ driveFeedforward = new SimpleMotorFeedforward(0.0, 0.0);
+ driveFeedback = new PIDController(0.0, 0.0, 0.0);
+ turnFeedback = new PIDController(0.0, 0.0, 0.0);
+ break;
+ }
+
+ turnFeedback.enableContinuousInput(-Math.PI, Math.PI);
+ setBrakeMode(true);
}
- turnFeedback.enableContinuousInput(-Math.PI, Math.PI);
- setBrakeMode(true);
- }
-
- /**
- * Update inputs without running the rest of the periodic logic. This is useful since these
- * updates need to be properly thread-locked.
- */
- public void updateInputs() {
- io.updateInputs(inputs);
- }
-
- public void periodic() {
- Logger.processInputs("Drive/Module" + Integer.toString(index), inputs);
-
- // On first cycle, reset relative turn encoder
- // Wait until absolute angle is nonzero in case it wasn't initialized yet
- if (turnRelativeOffset == null && inputs.turnAbsolutePosition.getRadians() != 0.0) {
- turnRelativeOffset = inputs.turnAbsolutePosition.minus(inputs.turnPosition);
+ /**
+ * Update inputs without running the rest of the periodic logic. This is useful since these
+ * updates need to be properly thread-locked.
+ */
+ public void updateInputs() {
+ io.updateInputs(inputs);
}
- // Run closed loop turn control
- if (angleSetpoint != null) {
- io.setTurnVoltage(
- turnFeedback.calculate(getAngle().getRadians(), angleSetpoint.getRadians()));
-
- // Run closed loop drive control
- // Only allowed if closed loop turn control is running
- if (speedSetpoint != null) {
- // Scale velocity based on turn error
- //
- // When the error is 90°, the velocity setpoint should be 0. As the wheel turns
- // towards the setpoint, its velocity should increase. This is achieved by
- // taking the component of the velocity in the direction of the setpoint.
- double adjustSpeedSetpoint = speedSetpoint * Math.cos(turnFeedback.getPositionError());
-
- // Run drive controller
- double velocityRadPerSec = adjustSpeedSetpoint / WHEEL_RADIUS;
- io.setDriveVoltage(
- driveFeedforward.calculate(velocityRadPerSec)
- + driveFeedback.calculate(inputs.driveVelocityRadPerSec, velocityRadPerSec));
- }
+ public void periodic() {
+ Logger.processInputs("Drive/Module" + Integer.toString(index), inputs);
+
+ // On first cycle, reset relative turn encoder
+ // Wait until absolute angle is nonzero in case it wasn't initialized yet
+ if (turnRelativeOffset == null && inputs.turnAbsolutePosition.getRadians() != 0.0) {
+ turnRelativeOffset = inputs.turnAbsolutePosition.minus(inputs.turnPosition);
+ }
+
+ // Run closed loop turn control
+ if (angleSetpoint != null) {
+ io.setTurnVoltage(
+ turnFeedback.calculate(getAngle().getRadians(), angleSetpoint.getRadians()));
+
+ // Run closed loop drive control
+ // Only allowed if closed loop turn control is running
+ if (speedSetpoint != null) {
+ // Scale velocity based on turn error
+ //
+ // When the error is 90°, the velocity setpoint should be 0. As the wheel turns
+ // towards the setpoint, its velocity should increase. This is achieved by
+ // taking the component of the velocity in the direction of the setpoint.
+ double adjustSpeedSetpoint = speedSetpoint * Math.cos(turnFeedback.getPositionError());
+
+ // Run drive controller
+ double velocityRadPerSec = adjustSpeedSetpoint / WHEEL_RADIUS;
+ io.setDriveVoltage(
+ driveFeedforward.calculate(velocityRadPerSec)
+ + driveFeedback.calculate(inputs.driveVelocityRadPerSec, velocityRadPerSec));
+ }
+ }
+
+ // Calculate positions for odometry
+ int sampleCount = inputs.odometryTimestamps.length; // All signals are sampled together
+ odometryPositions = new SwerveModulePosition[sampleCount];
+ for (int i = 0; i < sampleCount; i++) {
+ double positionMeters = inputs.odometryDrivePositionsRad[i] * WHEEL_RADIUS;
+ Rotation2d angle =
+ inputs.odometryTurnPositions[i].plus(
+ turnRelativeOffset != null ? turnRelativeOffset : new Rotation2d());
+ odometryPositions[i] = new SwerveModulePosition(positionMeters, angle);
+ }
}
- // Calculate positions for odometry
- int sampleCount = inputs.odometryTimestamps.length; // All signals are sampled together
- odometryPositions = new SwerveModulePosition[sampleCount];
- for (int i = 0; i < sampleCount; i++) {
- double positionMeters = inputs.odometryDrivePositionsRad[i] * WHEEL_RADIUS;
- Rotation2d angle =
- inputs.odometryTurnPositions[i].plus(
- turnRelativeOffset != null ? turnRelativeOffset : new Rotation2d());
- odometryPositions[i] = new SwerveModulePosition(positionMeters, angle);
+ /**
+ * Runs the module with the specified setpoint state. Returns the optimized state.
+ */
+ public SwerveModuleState runSetpoint(SwerveModuleState state) {
+ // Optimize state based on current angle
+ // Controllers run in "periodic" when the setpoint is not null
+ var optimizedState = SwerveModuleState.optimize(state, getAngle());
+
+ // Update setpoints, controllers run in "periodic"
+ angleSetpoint = optimizedState.angle;
+ speedSetpoint = optimizedState.speedMetersPerSecond;
+
+ return optimizedState;
}
- }
-
- /** Runs the module with the specified setpoint state. Returns the optimized state. */
- public SwerveModuleState runSetpoint(SwerveModuleState state) {
- // Optimize state based on current angle
- // Controllers run in "periodic" when the setpoint is not null
- var optimizedState = SwerveModuleState.optimize(state, getAngle());
-
- // Update setpoints, controllers run in "periodic"
- angleSetpoint = optimizedState.angle;
- speedSetpoint = optimizedState.speedMetersPerSecond;
-
- return optimizedState;
- }
-
- /** Runs the module with the specified voltage while controlling to zero degrees. */
- public void runCharacterization(double volts) {
- // Closed loop turn control
- angleSetpoint = new Rotation2d();
-
- // Open loop drive control
- io.setDriveVoltage(volts);
- speedSetpoint = null;
- }
-
- /** Disables all outputs to motors. */
- public void stop() {
- io.setTurnVoltage(0.0);
- io.setDriveVoltage(0.0);
-
- // Disable closed loop control for turn and drive
- angleSetpoint = null;
- speedSetpoint = null;
- }
-
- /** Sets whether brake mode is enabled. */
- public void setBrakeMode(boolean enabled) {
- io.setDriveBrakeMode(enabled);
- io.setTurnBrakeMode(enabled);
- }
-
- /** Returns the current turn angle of the module. */
- public Rotation2d getAngle() {
- if (turnRelativeOffset == null) {
- return new Rotation2d();
- } else {
- return inputs.turnPosition.plus(turnRelativeOffset);
+
+ /**
+ * Runs the module with the specified voltage while controlling to zero degrees.
+ */
+ public void runCharacterization(double volts) {
+ // Closed loop turn control
+ angleSetpoint = new Rotation2d();
+
+ // Open loop drive control
+ io.setDriveVoltage(volts);
+ speedSetpoint = null;
+ }
+
+ /**
+ * Disables all outputs to motors.
+ */
+ public void stop() {
+ io.setTurnVoltage(0.0);
+ io.setDriveVoltage(0.0);
+
+ // Disable closed loop control for turn and drive
+ angleSetpoint = null;
+ speedSetpoint = null;
+ }
+
+ /**
+ * Sets whether brake mode is enabled.
+ */
+ public void setBrakeMode(boolean enabled) {
+ io.setDriveBrakeMode(enabled);
+ io.setTurnBrakeMode(enabled);
+ }
+
+ /**
+ * Returns the current turn angle of the module.
+ */
+ public Rotation2d getAngle() {
+ if (turnRelativeOffset == null) {
+ return new Rotation2d();
+ } else {
+ return inputs.turnPosition.plus(turnRelativeOffset);
+ }
+ }
+
+ /**
+ * Returns the current drive position of the module in meters.
+ */
+ public double getPositionMeters() {
+ return inputs.drivePositionRad * WHEEL_RADIUS;
+ }
+
+ /**
+ * Returns the current drive velocity of the module in meters per second.
+ */
+ public double getVelocityMetersPerSec() {
+ return inputs.driveVelocityRadPerSec * WHEEL_RADIUS;
+ }
+
+ /**
+ * Returns the module position (turn angle and drive position).
+ */
+ public SwerveModulePosition getPosition() {
+ return new SwerveModulePosition(getPositionMeters(), getAngle());
+ }
+
+ /**
+ * Returns the module state (turn angle and drive velocity).
+ */
+ public SwerveModuleState getState() {
+ return new SwerveModuleState(getVelocityMetersPerSec(), getAngle());
+ }
+
+ /**
+ * Returns the module positions received this cycle.
+ */
+ public SwerveModulePosition[] getOdometryPositions() {
+ return odometryPositions;
+ }
+
+ /**
+ * Returns the timestamps of the samples received this cycle.
+ */
+ public double[] getOdometryTimestamps() {
+ return inputs.odometryTimestamps;
+ }
+
+ /**
+ * Returns the drive velocity in radians/sec.
+ */
+ public double getCharacterizationVelocity() {
+ return inputs.driveVelocityRadPerSec;
}
- }
-
- /** Returns the current drive position of the module in meters. */
- public double getPositionMeters() {
- return inputs.drivePositionRad * WHEEL_RADIUS;
- }
-
- /** Returns the current drive velocity of the module in meters per second. */
- public double getVelocityMetersPerSec() {
- return inputs.driveVelocityRadPerSec * WHEEL_RADIUS;
- }
-
- /** Returns the module position (turn angle and drive position). */
- public SwerveModulePosition getPosition() {
- return new SwerveModulePosition(getPositionMeters(), getAngle());
- }
-
- /** Returns the module state (turn angle and drive velocity). */
- public SwerveModuleState getState() {
- return new SwerveModuleState(getVelocityMetersPerSec(), getAngle());
- }
-
- /** Returns the module positions received this cycle. */
- public SwerveModulePosition[] getOdometryPositions() {
- return odometryPositions;
- }
-
- /** Returns the timestamps of the samples received this cycle. */
- public double[] getOdometryTimestamps() {
- return inputs.odometryTimestamps;
- }
-
- /** Returns the drive velocity in radians/sec. */
- public double getCharacterizationVelocity() {
- return inputs.driveVelocityRadPerSec;
- }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/ModuleIO.java b/src/main/java/frc/robot/subsystems/drive/ModuleIO.java
index 200afa3..b511cd2 100644
--- a/src/main/java/frc/robot/subsystems/drive/ModuleIO.java
+++ b/src/main/java/frc/robot/subsystems/drive/ModuleIO.java
@@ -17,36 +17,51 @@
import org.littletonrobotics.junction.AutoLog;
public interface ModuleIO {
- @AutoLog
- public static class ModuleIOInputs {
- public double drivePositionRad = 0.0;
- public double driveVelocityRadPerSec = 0.0;
- public double driveAppliedVolts = 0.0;
- public double[] driveCurrentAmps = new double[] {};
-
- public Rotation2d turnAbsolutePosition = new Rotation2d();
- public Rotation2d turnPosition = new Rotation2d();
- public double turnVelocityRadPerSec = 0.0;
- public double turnAppliedVolts = 0.0;
- public double[] turnCurrentAmps = new double[] {};
-
- public double[] odometryTimestamps = new double[] {};
- public double[] odometryDrivePositionsRad = new double[] {};
- public Rotation2d[] odometryTurnPositions = new Rotation2d[] {};
- }
-
- /** Updates the set of loggable inputs. */
- public default void updateInputs(ModuleIOInputs inputs) {}
-
- /** Run the drive motor at the specified voltage. */
- public default void setDriveVoltage(double volts) {}
-
- /** Run the turn motor at the specified voltage. */
- public default void setTurnVoltage(double volts) {}
-
- /** Enable or disable brake mode on the drive motor. */
- public default void setDriveBrakeMode(boolean enable) {}
-
- /** Enable or disable brake mode on the turn motor. */
- public default void setTurnBrakeMode(boolean enable) {}
+ @AutoLog
+ public static class ModuleIOInputs {
+ public double drivePositionRad = 0.0;
+ public double driveVelocityRadPerSec = 0.0;
+ public double driveAppliedVolts = 0.0;
+ public double[] driveCurrentAmps = new double[]{};
+
+ public Rotation2d turnAbsolutePosition = new Rotation2d();
+ public Rotation2d turnPosition = new Rotation2d();
+ public double turnVelocityRadPerSec = 0.0;
+ public double turnAppliedVolts = 0.0;
+ public double[] turnCurrentAmps = new double[]{};
+
+ public double[] odometryTimestamps = new double[]{};
+ public double[] odometryDrivePositionsRad = new double[]{};
+ public Rotation2d[] odometryTurnPositions = new Rotation2d[]{};
+ }
+
+ /**
+ * Updates the set of loggable inputs.
+ */
+ public default void updateInputs(ModuleIOInputs inputs) {
+ }
+
+ /**
+ * Run the drive motor at the specified voltage.
+ */
+ public default void setDriveVoltage(double volts) {
+ }
+
+ /**
+ * Run the turn motor at the specified voltage.
+ */
+ public default void setTurnVoltage(double volts) {
+ }
+
+ /**
+ * Enable or disable brake mode on the drive motor.
+ */
+ public default void setDriveBrakeMode(boolean enable) {
+ }
+
+ /**
+ * Enable or disable brake mode on the turn motor.
+ */
+ public default void setTurnBrakeMode(boolean enable) {
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/ModuleIOSim.java b/src/main/java/frc/robot/subsystems/drive/ModuleIOSim.java
index f2ffe48..9d96000 100644
--- a/src/main/java/frc/robot/subsystems/drive/ModuleIOSim.java
+++ b/src/main/java/frc/robot/subsystems/drive/ModuleIOSim.java
@@ -27,46 +27,46 @@
* approximation for the behavior of the module.
*/
public class ModuleIOSim implements ModuleIO {
- private static final double LOOP_PERIOD_SECS = 0.02;
+ private static final double LOOP_PERIOD_SECS = 0.02;
- private DCMotorSim driveSim = new DCMotorSim(DCMotor.getNEO(1), 6.75, 0.025);
- private DCMotorSim turnSim = new DCMotorSim(DCMotor.getNEO(1), 150.0 / 7.0, 0.004);
+ private DCMotorSim driveSim = new DCMotorSim(DCMotor.getNEO(1), 6.75, 0.025);
+ private DCMotorSim turnSim = new DCMotorSim(DCMotor.getNEO(1), 150.0 / 7.0, 0.004);
- private final Rotation2d turnAbsoluteInitPosition = new Rotation2d(Math.random() * 2.0 * Math.PI);
- private double driveAppliedVolts = 0.0;
- private double turnAppliedVolts = 0.0;
+ private final Rotation2d turnAbsoluteInitPosition = new Rotation2d(Math.random() * 2.0 * Math.PI);
+ private double driveAppliedVolts = 0.0;
+ private double turnAppliedVolts = 0.0;
- @Override
- public void updateInputs(ModuleIOInputs inputs) {
- driveSim.update(LOOP_PERIOD_SECS);
- turnSim.update(LOOP_PERIOD_SECS);
+ @Override
+ public void updateInputs(ModuleIOInputs inputs) {
+ driveSim.update(LOOP_PERIOD_SECS);
+ turnSim.update(LOOP_PERIOD_SECS);
- inputs.drivePositionRad = driveSim.getAngularPositionRad();
- inputs.driveVelocityRadPerSec = driveSim.getAngularVelocityRadPerSec();
- inputs.driveAppliedVolts = driveAppliedVolts;
- inputs.driveCurrentAmps = new double[] {Math.abs(driveSim.getCurrentDrawAmps())};
+ inputs.drivePositionRad = driveSim.getAngularPositionRad();
+ inputs.driveVelocityRadPerSec = driveSim.getAngularVelocityRadPerSec();
+ inputs.driveAppliedVolts = driveAppliedVolts;
+ inputs.driveCurrentAmps = new double[]{Math.abs(driveSim.getCurrentDrawAmps())};
- inputs.turnAbsolutePosition =
- new Rotation2d(turnSim.getAngularPositionRad()).plus(turnAbsoluteInitPosition);
- inputs.turnPosition = new Rotation2d(turnSim.getAngularPositionRad());
- inputs.turnVelocityRadPerSec = turnSim.getAngularVelocityRadPerSec();
- inputs.turnAppliedVolts = turnAppliedVolts;
- inputs.turnCurrentAmps = new double[] {Math.abs(turnSim.getCurrentDrawAmps())};
+ inputs.turnAbsolutePosition =
+ new Rotation2d(turnSim.getAngularPositionRad()).plus(turnAbsoluteInitPosition);
+ inputs.turnPosition = new Rotation2d(turnSim.getAngularPositionRad());
+ inputs.turnVelocityRadPerSec = turnSim.getAngularVelocityRadPerSec();
+ inputs.turnAppliedVolts = turnAppliedVolts;
+ inputs.turnCurrentAmps = new double[]{Math.abs(turnSim.getCurrentDrawAmps())};
- inputs.odometryTimestamps = new double[] {Timer.getFPGATimestamp()};
- inputs.odometryDrivePositionsRad = new double[] {inputs.drivePositionRad};
- inputs.odometryTurnPositions = new Rotation2d[] {inputs.turnPosition};
- }
+ inputs.odometryTimestamps = new double[]{Timer.getFPGATimestamp()};
+ inputs.odometryDrivePositionsRad = new double[]{inputs.drivePositionRad};
+ inputs.odometryTurnPositions = new Rotation2d[]{inputs.turnPosition};
+ }
- @Override
- public void setDriveVoltage(double volts) {
- driveAppliedVolts = MathUtil.clamp(volts, -12.0, 12.0);
- driveSim.setInputVoltage(driveAppliedVolts);
- }
+ @Override
+ public void setDriveVoltage(double volts) {
+ driveAppliedVolts = MathUtil.clamp(volts, -12.0, 12.0);
+ driveSim.setInputVoltage(driveAppliedVolts);
+ }
- @Override
- public void setTurnVoltage(double volts) {
- turnAppliedVolts = MathUtil.clamp(volts, -12.0, 12.0);
- turnSim.setInputVoltage(turnAppliedVolts);
- }
+ @Override
+ public void setTurnVoltage(double volts) {
+ turnAppliedVolts = MathUtil.clamp(volts, -12.0, 12.0);
+ turnSim.setInputVoltage(turnAppliedVolts);
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/ModuleIOSparkMax.java b/src/main/java/frc/robot/subsystems/drive/ModuleIOSparkMax.java
index 781ec70..3e5c541 100644
--- a/src/main/java/frc/robot/subsystems/drive/ModuleIOSparkMax.java
+++ b/src/main/java/frc/robot/subsystems/drive/ModuleIOSparkMax.java
@@ -23,6 +23,7 @@
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.AnalogInput;
import edu.wpi.first.wpilibj.RobotController;
+
import java.util.OptionalDouble;
import java.util.Queue;
@@ -39,164 +40,164 @@
* "/Drive/ModuleX/TurnAbsolutePositionRad"
*/
public class ModuleIOSparkMax implements ModuleIO {
- // Gear ratios for SDS MK4i L2, adjust as necessary
- private static final double DRIVE_GEAR_RATIO = (50.0 / 14.0) * (17.0 / 27.0) * (45.0 / 15.0);
- private static final double TURN_GEAR_RATIO = 150.0 / 7.0;
-
- private final CANSparkMax driveSparkMax;
- private final CANSparkMax turnSparkMax;
-
- private final RelativeEncoder driveEncoder;
- private final RelativeEncoder turnRelativeEncoder;
- private final AnalogInput turnAbsoluteEncoder;
- private final Queue timestampQueue;
- private final Queue drivePositionQueue;
- private final Queue turnPositionQueue;
-
- private final boolean isTurnMotorInverted = true;
- private final Rotation2d absoluteEncoderOffset;
-
- public ModuleIOSparkMax(int index) {
- switch (index) {
- case 0:
- driveSparkMax = new CANSparkMax(1, MotorType.kBrushless);
- turnSparkMax = new CANSparkMax(2, MotorType.kBrushless);
- turnAbsoluteEncoder = new AnalogInput(0);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- case 1:
- driveSparkMax = new CANSparkMax(3, MotorType.kBrushless);
- turnSparkMax = new CANSparkMax(4, MotorType.kBrushless);
- turnAbsoluteEncoder = new AnalogInput(1);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- case 2:
- driveSparkMax = new CANSparkMax(5, MotorType.kBrushless);
- turnSparkMax = new CANSparkMax(6, MotorType.kBrushless);
- turnAbsoluteEncoder = new AnalogInput(2);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- case 3:
- driveSparkMax = new CANSparkMax(7, MotorType.kBrushless);
- turnSparkMax = new CANSparkMax(8, MotorType.kBrushless);
- turnAbsoluteEncoder = new AnalogInput(3);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- default:
- throw new RuntimeException("Invalid module index");
+ // Gear ratios for SDS MK4i L2, adjust as necessary
+ private static final double DRIVE_GEAR_RATIO = (50.0 / 14.0) * (17.0 / 27.0) * (45.0 / 15.0);
+ private static final double TURN_GEAR_RATIO = 150.0 / 7.0;
+
+ private final CANSparkMax driveSparkMax;
+ private final CANSparkMax turnSparkMax;
+
+ private final RelativeEncoder driveEncoder;
+ private final RelativeEncoder turnRelativeEncoder;
+ private final AnalogInput turnAbsoluteEncoder;
+ private final Queue timestampQueue;
+ private final Queue drivePositionQueue;
+ private final Queue turnPositionQueue;
+
+ private final boolean isTurnMotorInverted = true;
+ private final Rotation2d absoluteEncoderOffset;
+
+ public ModuleIOSparkMax(int index) {
+ switch (index) {
+ case 0:
+ driveSparkMax = new CANSparkMax(1, MotorType.kBrushless);
+ turnSparkMax = new CANSparkMax(2, MotorType.kBrushless);
+ turnAbsoluteEncoder = new AnalogInput(0);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ case 1:
+ driveSparkMax = new CANSparkMax(3, MotorType.kBrushless);
+ turnSparkMax = new CANSparkMax(4, MotorType.kBrushless);
+ turnAbsoluteEncoder = new AnalogInput(1);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ case 2:
+ driveSparkMax = new CANSparkMax(5, MotorType.kBrushless);
+ turnSparkMax = new CANSparkMax(6, MotorType.kBrushless);
+ turnAbsoluteEncoder = new AnalogInput(2);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ case 3:
+ driveSparkMax = new CANSparkMax(7, MotorType.kBrushless);
+ turnSparkMax = new CANSparkMax(8, MotorType.kBrushless);
+ turnAbsoluteEncoder = new AnalogInput(3);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ default:
+ throw new RuntimeException("Invalid module index");
+ }
+
+ driveSparkMax.restoreFactoryDefaults();
+ turnSparkMax.restoreFactoryDefaults();
+
+ driveSparkMax.setCANTimeout(250);
+ turnSparkMax.setCANTimeout(250);
+
+ driveEncoder = driveSparkMax.getEncoder();
+ turnRelativeEncoder = turnSparkMax.getEncoder();
+
+ turnSparkMax.setInverted(isTurnMotorInverted);
+ driveSparkMax.setSmartCurrentLimit(40);
+ turnSparkMax.setSmartCurrentLimit(30);
+ driveSparkMax.enableVoltageCompensation(12.0);
+ turnSparkMax.enableVoltageCompensation(12.0);
+
+ driveEncoder.setPosition(0.0);
+ driveEncoder.setMeasurementPeriod(10);
+ driveEncoder.setAverageDepth(2);
+
+ turnRelativeEncoder.setPosition(0.0);
+ turnRelativeEncoder.setMeasurementPeriod(10);
+ turnRelativeEncoder.setAverageDepth(2);
+
+ driveSparkMax.setCANTimeout(0);
+ turnSparkMax.setCANTimeout(0);
+
+ driveSparkMax.setPeriodicFramePeriod(
+ PeriodicFrame.kStatus2, (int) (1000.0 / Module.ODOMETRY_FREQUENCY));
+ turnSparkMax.setPeriodicFramePeriod(
+ PeriodicFrame.kStatus2, (int) (1000.0 / Module.ODOMETRY_FREQUENCY));
+ timestampQueue = SparkMaxOdometryThread.getInstance().makeTimestampQueue();
+ drivePositionQueue =
+ SparkMaxOdometryThread.getInstance()
+ .registerSignal(
+ () -> {
+ double value = driveEncoder.getPosition();
+ if (driveSparkMax.getLastError() == REVLibError.kOk) {
+ return OptionalDouble.of(value);
+ } else {
+ return OptionalDouble.empty();
+ }
+ });
+ turnPositionQueue =
+ SparkMaxOdometryThread.getInstance()
+ .registerSignal(
+ () -> {
+ double value = turnRelativeEncoder.getPosition();
+ if (turnSparkMax.getLastError() == REVLibError.kOk) {
+ return OptionalDouble.of(value);
+ } else {
+ return OptionalDouble.empty();
+ }
+ });
+
+ driveSparkMax.burnFlash();
+ turnSparkMax.burnFlash();
+ }
+
+ @Override
+ public void updateInputs(ModuleIOInputs inputs) {
+ inputs.drivePositionRad =
+ Units.rotationsToRadians(driveEncoder.getPosition()) / DRIVE_GEAR_RATIO;
+ inputs.driveVelocityRadPerSec =
+ Units.rotationsPerMinuteToRadiansPerSecond(driveEncoder.getVelocity()) / DRIVE_GEAR_RATIO;
+ inputs.driveAppliedVolts = driveSparkMax.getAppliedOutput() * driveSparkMax.getBusVoltage();
+ inputs.driveCurrentAmps = new double[]{driveSparkMax.getOutputCurrent()};
+
+ inputs.turnAbsolutePosition =
+ new Rotation2d(
+ turnAbsoluteEncoder.getVoltage() / RobotController.getVoltage5V() * 2.0 * Math.PI)
+ .minus(absoluteEncoderOffset);
+ inputs.turnPosition =
+ Rotation2d.fromRotations(turnRelativeEncoder.getPosition() / TURN_GEAR_RATIO);
+ inputs.turnVelocityRadPerSec =
+ Units.rotationsPerMinuteToRadiansPerSecond(turnRelativeEncoder.getVelocity())
+ / TURN_GEAR_RATIO;
+ inputs.turnAppliedVolts = turnSparkMax.getAppliedOutput() * turnSparkMax.getBusVoltage();
+ inputs.turnCurrentAmps = new double[]{turnSparkMax.getOutputCurrent()};
+
+ inputs.odometryTimestamps =
+ timestampQueue.stream().mapToDouble((Double value) -> value).toArray();
+ inputs.odometryDrivePositionsRad =
+ drivePositionQueue.stream()
+ .mapToDouble((Double value) -> Units.rotationsToRadians(value) / DRIVE_GEAR_RATIO)
+ .toArray();
+ inputs.odometryTurnPositions =
+ turnPositionQueue.stream()
+ .map((Double value) -> Rotation2d.fromRotations(value / TURN_GEAR_RATIO))
+ .toArray(Rotation2d[]::new);
+ timestampQueue.clear();
+ drivePositionQueue.clear();
+ turnPositionQueue.clear();
+ }
+
+ @Override
+ public void setDriveVoltage(double volts) {
+ driveSparkMax.setVoltage(volts);
}
- driveSparkMax.restoreFactoryDefaults();
- turnSparkMax.restoreFactoryDefaults();
-
- driveSparkMax.setCANTimeout(250);
- turnSparkMax.setCANTimeout(250);
-
- driveEncoder = driveSparkMax.getEncoder();
- turnRelativeEncoder = turnSparkMax.getEncoder();
-
- turnSparkMax.setInverted(isTurnMotorInverted);
- driveSparkMax.setSmartCurrentLimit(40);
- turnSparkMax.setSmartCurrentLimit(30);
- driveSparkMax.enableVoltageCompensation(12.0);
- turnSparkMax.enableVoltageCompensation(12.0);
-
- driveEncoder.setPosition(0.0);
- driveEncoder.setMeasurementPeriod(10);
- driveEncoder.setAverageDepth(2);
-
- turnRelativeEncoder.setPosition(0.0);
- turnRelativeEncoder.setMeasurementPeriod(10);
- turnRelativeEncoder.setAverageDepth(2);
-
- driveSparkMax.setCANTimeout(0);
- turnSparkMax.setCANTimeout(0);
-
- driveSparkMax.setPeriodicFramePeriod(
- PeriodicFrame.kStatus2, (int) (1000.0 / Module.ODOMETRY_FREQUENCY));
- turnSparkMax.setPeriodicFramePeriod(
- PeriodicFrame.kStatus2, (int) (1000.0 / Module.ODOMETRY_FREQUENCY));
- timestampQueue = SparkMaxOdometryThread.getInstance().makeTimestampQueue();
- drivePositionQueue =
- SparkMaxOdometryThread.getInstance()
- .registerSignal(
- () -> {
- double value = driveEncoder.getPosition();
- if (driveSparkMax.getLastError() == REVLibError.kOk) {
- return OptionalDouble.of(value);
- } else {
- return OptionalDouble.empty();
- }
- });
- turnPositionQueue =
- SparkMaxOdometryThread.getInstance()
- .registerSignal(
- () -> {
- double value = turnRelativeEncoder.getPosition();
- if (turnSparkMax.getLastError() == REVLibError.kOk) {
- return OptionalDouble.of(value);
- } else {
- return OptionalDouble.empty();
- }
- });
-
- driveSparkMax.burnFlash();
- turnSparkMax.burnFlash();
- }
-
- @Override
- public void updateInputs(ModuleIOInputs inputs) {
- inputs.drivePositionRad =
- Units.rotationsToRadians(driveEncoder.getPosition()) / DRIVE_GEAR_RATIO;
- inputs.driveVelocityRadPerSec =
- Units.rotationsPerMinuteToRadiansPerSecond(driveEncoder.getVelocity()) / DRIVE_GEAR_RATIO;
- inputs.driveAppliedVolts = driveSparkMax.getAppliedOutput() * driveSparkMax.getBusVoltage();
- inputs.driveCurrentAmps = new double[] {driveSparkMax.getOutputCurrent()};
-
- inputs.turnAbsolutePosition =
- new Rotation2d(
- turnAbsoluteEncoder.getVoltage() / RobotController.getVoltage5V() * 2.0 * Math.PI)
- .minus(absoluteEncoderOffset);
- inputs.turnPosition =
- Rotation2d.fromRotations(turnRelativeEncoder.getPosition() / TURN_GEAR_RATIO);
- inputs.turnVelocityRadPerSec =
- Units.rotationsPerMinuteToRadiansPerSecond(turnRelativeEncoder.getVelocity())
- / TURN_GEAR_RATIO;
- inputs.turnAppliedVolts = turnSparkMax.getAppliedOutput() * turnSparkMax.getBusVoltage();
- inputs.turnCurrentAmps = new double[] {turnSparkMax.getOutputCurrent()};
-
- inputs.odometryTimestamps =
- timestampQueue.stream().mapToDouble((Double value) -> value).toArray();
- inputs.odometryDrivePositionsRad =
- drivePositionQueue.stream()
- .mapToDouble((Double value) -> Units.rotationsToRadians(value) / DRIVE_GEAR_RATIO)
- .toArray();
- inputs.odometryTurnPositions =
- turnPositionQueue.stream()
- .map((Double value) -> Rotation2d.fromRotations(value / TURN_GEAR_RATIO))
- .toArray(Rotation2d[]::new);
- timestampQueue.clear();
- drivePositionQueue.clear();
- turnPositionQueue.clear();
- }
-
- @Override
- public void setDriveVoltage(double volts) {
- driveSparkMax.setVoltage(volts);
- }
-
- @Override
- public void setTurnVoltage(double volts) {
- turnSparkMax.setVoltage(volts);
- }
-
- @Override
- public void setDriveBrakeMode(boolean enable) {
- driveSparkMax.setIdleMode(enable ? IdleMode.kBrake : IdleMode.kCoast);
- }
-
- @Override
- public void setTurnBrakeMode(boolean enable) {
- turnSparkMax.setIdleMode(enable ? IdleMode.kBrake : IdleMode.kCoast);
- }
+ @Override
+ public void setTurnVoltage(double volts) {
+ turnSparkMax.setVoltage(volts);
+ }
+
+ @Override
+ public void setDriveBrakeMode(boolean enable) {
+ driveSparkMax.setIdleMode(enable ? IdleMode.kBrake : IdleMode.kCoast);
+ }
+
+ @Override
+ public void setTurnBrakeMode(boolean enable) {
+ turnSparkMax.setIdleMode(enable ? IdleMode.kBrake : IdleMode.kCoast);
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/ModuleIOTalonFX.java b/src/main/java/frc/robot/subsystems/drive/ModuleIOTalonFX.java
index b41202e..9dfb717 100644
--- a/src/main/java/frc/robot/subsystems/drive/ModuleIOTalonFX.java
+++ b/src/main/java/frc/robot/subsystems/drive/ModuleIOTalonFX.java
@@ -25,6 +25,7 @@
import com.ctre.phoenix6.signals.NeutralModeValue;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.util.Units;
+
import java.util.Queue;
/**
@@ -40,179 +41,179 @@
* "/Drive/ModuleX/TurnAbsolutePositionRad"
*/
public class ModuleIOTalonFX implements ModuleIO {
- private final TalonFX driveTalon;
- private final TalonFX turnTalon;
- private final CANcoder cancoder;
-
- private final Queue timestampQueue;
-
- private final StatusSignal drivePosition;
- private final Queue drivePositionQueue;
- private final StatusSignal driveVelocity;
- private final StatusSignal driveAppliedVolts;
- private final StatusSignal driveCurrent;
-
- private final StatusSignal turnAbsolutePosition;
- private final StatusSignal turnPosition;
- private final Queue turnPositionQueue;
- private final StatusSignal turnVelocity;
- private final StatusSignal turnAppliedVolts;
- private final StatusSignal turnCurrent;
-
- // Gear ratios for SDS MK4i L2, adjust as necessary
- private final double DRIVE_GEAR_RATIO = (50.0 / 14.0) * (17.0 / 27.0) * (45.0 / 15.0);
- private final double TURN_GEAR_RATIO = 150.0 / 7.0;
-
- private final boolean isTurnMotorInverted = true;
- private final Rotation2d absoluteEncoderOffset;
-
- public ModuleIOTalonFX(int index) {
- switch (index) {
- case 0:
- driveTalon = new TalonFX(0);
- turnTalon = new TalonFX(1);
- cancoder = new CANcoder(2);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- case 1:
- driveTalon = new TalonFX(3);
- turnTalon = new TalonFX(4);
- cancoder = new CANcoder(5);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- case 2:
- driveTalon = new TalonFX(6);
- turnTalon = new TalonFX(7);
- cancoder = new CANcoder(8);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- case 3:
- driveTalon = new TalonFX(9);
- turnTalon = new TalonFX(10);
- cancoder = new CANcoder(11);
- absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
- break;
- default:
- throw new RuntimeException("Invalid module index");
+ private final TalonFX driveTalon;
+ private final TalonFX turnTalon;
+ private final CANcoder cancoder;
+
+ private final Queue timestampQueue;
+
+ private final StatusSignal drivePosition;
+ private final Queue drivePositionQueue;
+ private final StatusSignal driveVelocity;
+ private final StatusSignal driveAppliedVolts;
+ private final StatusSignal driveCurrent;
+
+ private final StatusSignal turnAbsolutePosition;
+ private final StatusSignal turnPosition;
+ private final Queue turnPositionQueue;
+ private final StatusSignal turnVelocity;
+ private final StatusSignal turnAppliedVolts;
+ private final StatusSignal turnCurrent;
+
+ // Gear ratios for SDS MK4i L2, adjust as necessary
+ private final double DRIVE_GEAR_RATIO = (50.0 / 14.0) * (17.0 / 27.0) * (45.0 / 15.0);
+ private final double TURN_GEAR_RATIO = 150.0 / 7.0;
+
+ private final boolean isTurnMotorInverted = true;
+ private final Rotation2d absoluteEncoderOffset;
+
+ public ModuleIOTalonFX(int index) {
+ switch (index) {
+ case 0:
+ driveTalon = new TalonFX(0);
+ turnTalon = new TalonFX(1);
+ cancoder = new CANcoder(2);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ case 1:
+ driveTalon = new TalonFX(3);
+ turnTalon = new TalonFX(4);
+ cancoder = new CANcoder(5);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ case 2:
+ driveTalon = new TalonFX(6);
+ turnTalon = new TalonFX(7);
+ cancoder = new CANcoder(8);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ case 3:
+ driveTalon = new TalonFX(9);
+ turnTalon = new TalonFX(10);
+ cancoder = new CANcoder(11);
+ absoluteEncoderOffset = new Rotation2d(0.0); // MUST BE CALIBRATED
+ break;
+ default:
+ throw new RuntimeException("Invalid module index");
+ }
+
+ var driveConfig = new TalonFXConfiguration();
+ driveConfig.CurrentLimits.SupplyCurrentLimit = 40.0;
+ driveConfig.CurrentLimits.SupplyCurrentLimitEnable = true;
+ driveTalon.getConfigurator().apply(driveConfig);
+ setDriveBrakeMode(true);
+
+ var turnConfig = new TalonFXConfiguration();
+ turnConfig.CurrentLimits.SupplyCurrentLimit = 30.0;
+ turnConfig.CurrentLimits.SupplyCurrentLimitEnable = true;
+ turnTalon.getConfigurator().apply(turnConfig);
+ setTurnBrakeMode(true);
+
+ cancoder.getConfigurator().apply(new CANcoderConfiguration());
+
+ timestampQueue = PhoenixOdometryThread.getInstance().makeTimestampQueue();
+
+ drivePosition = driveTalon.getPosition();
+ drivePositionQueue =
+ PhoenixOdometryThread.getInstance().registerSignal(driveTalon, driveTalon.getPosition());
+ driveVelocity = driveTalon.getVelocity();
+ driveAppliedVolts = driveTalon.getMotorVoltage();
+ driveCurrent = driveTalon.getSupplyCurrent();
+
+ turnAbsolutePosition = cancoder.getAbsolutePosition();
+ turnPosition = turnTalon.getPosition();
+ turnPositionQueue =
+ PhoenixOdometryThread.getInstance().registerSignal(turnTalon, turnTalon.getPosition());
+ turnVelocity = turnTalon.getVelocity();
+ turnAppliedVolts = turnTalon.getMotorVoltage();
+ turnCurrent = turnTalon.getSupplyCurrent();
+
+ BaseStatusSignal.setUpdateFrequencyForAll(
+ Module.ODOMETRY_FREQUENCY, drivePosition, turnPosition);
+ BaseStatusSignal.setUpdateFrequencyForAll(
+ 50.0,
+ driveVelocity,
+ driveAppliedVolts,
+ driveCurrent,
+ turnAbsolutePosition,
+ turnVelocity,
+ turnAppliedVolts,
+ turnCurrent);
+ driveTalon.optimizeBusUtilization();
+ turnTalon.optimizeBusUtilization();
+ }
+
+ @Override
+ public void updateInputs(ModuleIOInputs inputs) {
+ BaseStatusSignal.refreshAll(
+ drivePosition,
+ driveVelocity,
+ driveAppliedVolts,
+ driveCurrent,
+ turnAbsolutePosition,
+ turnPosition,
+ turnVelocity,
+ turnAppliedVolts,
+ turnCurrent);
+
+ inputs.drivePositionRad =
+ Units.rotationsToRadians(drivePosition.getValueAsDouble()) / DRIVE_GEAR_RATIO;
+ inputs.driveVelocityRadPerSec =
+ Units.rotationsToRadians(driveVelocity.getValueAsDouble()) / DRIVE_GEAR_RATIO;
+ inputs.driveAppliedVolts = driveAppliedVolts.getValueAsDouble();
+ inputs.driveCurrentAmps = new double[]{driveCurrent.getValueAsDouble()};
+
+ inputs.turnAbsolutePosition =
+ Rotation2d.fromRotations(turnAbsolutePosition.getValueAsDouble())
+ .minus(absoluteEncoderOffset);
+ inputs.turnPosition =
+ Rotation2d.fromRotations(turnPosition.getValueAsDouble() / TURN_GEAR_RATIO);
+ inputs.turnVelocityRadPerSec =
+ Units.rotationsToRadians(turnVelocity.getValueAsDouble()) / TURN_GEAR_RATIO;
+ inputs.turnAppliedVolts = turnAppliedVolts.getValueAsDouble();
+ inputs.turnCurrentAmps = new double[]{turnCurrent.getValueAsDouble()};
+
+ inputs.odometryTimestamps =
+ timestampQueue.stream().mapToDouble((Double value) -> value).toArray();
+ inputs.odometryDrivePositionsRad =
+ drivePositionQueue.stream()
+ .mapToDouble((Double value) -> Units.rotationsToRadians(value) / DRIVE_GEAR_RATIO)
+ .toArray();
+ inputs.odometryTurnPositions =
+ turnPositionQueue.stream()
+ .map((Double value) -> Rotation2d.fromRotations(value / TURN_GEAR_RATIO))
+ .toArray(Rotation2d[]::new);
+ timestampQueue.clear();
+ drivePositionQueue.clear();
+ turnPositionQueue.clear();
+ }
+
+ @Override
+ public void setDriveVoltage(double volts) {
+ driveTalon.setControl(new VoltageOut(volts));
}
- var driveConfig = new TalonFXConfiguration();
- driveConfig.CurrentLimits.SupplyCurrentLimit = 40.0;
- driveConfig.CurrentLimits.SupplyCurrentLimitEnable = true;
- driveTalon.getConfigurator().apply(driveConfig);
- setDriveBrakeMode(true);
-
- var turnConfig = new TalonFXConfiguration();
- turnConfig.CurrentLimits.SupplyCurrentLimit = 30.0;
- turnConfig.CurrentLimits.SupplyCurrentLimitEnable = true;
- turnTalon.getConfigurator().apply(turnConfig);
- setTurnBrakeMode(true);
-
- cancoder.getConfigurator().apply(new CANcoderConfiguration());
-
- timestampQueue = PhoenixOdometryThread.getInstance().makeTimestampQueue();
-
- drivePosition = driveTalon.getPosition();
- drivePositionQueue =
- PhoenixOdometryThread.getInstance().registerSignal(driveTalon, driveTalon.getPosition());
- driveVelocity = driveTalon.getVelocity();
- driveAppliedVolts = driveTalon.getMotorVoltage();
- driveCurrent = driveTalon.getSupplyCurrent();
-
- turnAbsolutePosition = cancoder.getAbsolutePosition();
- turnPosition = turnTalon.getPosition();
- turnPositionQueue =
- PhoenixOdometryThread.getInstance().registerSignal(turnTalon, turnTalon.getPosition());
- turnVelocity = turnTalon.getVelocity();
- turnAppliedVolts = turnTalon.getMotorVoltage();
- turnCurrent = turnTalon.getSupplyCurrent();
-
- BaseStatusSignal.setUpdateFrequencyForAll(
- Module.ODOMETRY_FREQUENCY, drivePosition, turnPosition);
- BaseStatusSignal.setUpdateFrequencyForAll(
- 50.0,
- driveVelocity,
- driveAppliedVolts,
- driveCurrent,
- turnAbsolutePosition,
- turnVelocity,
- turnAppliedVolts,
- turnCurrent);
- driveTalon.optimizeBusUtilization();
- turnTalon.optimizeBusUtilization();
- }
-
- @Override
- public void updateInputs(ModuleIOInputs inputs) {
- BaseStatusSignal.refreshAll(
- drivePosition,
- driveVelocity,
- driveAppliedVolts,
- driveCurrent,
- turnAbsolutePosition,
- turnPosition,
- turnVelocity,
- turnAppliedVolts,
- turnCurrent);
-
- inputs.drivePositionRad =
- Units.rotationsToRadians(drivePosition.getValueAsDouble()) / DRIVE_GEAR_RATIO;
- inputs.driveVelocityRadPerSec =
- Units.rotationsToRadians(driveVelocity.getValueAsDouble()) / DRIVE_GEAR_RATIO;
- inputs.driveAppliedVolts = driveAppliedVolts.getValueAsDouble();
- inputs.driveCurrentAmps = new double[] {driveCurrent.getValueAsDouble()};
-
- inputs.turnAbsolutePosition =
- Rotation2d.fromRotations(turnAbsolutePosition.getValueAsDouble())
- .minus(absoluteEncoderOffset);
- inputs.turnPosition =
- Rotation2d.fromRotations(turnPosition.getValueAsDouble() / TURN_GEAR_RATIO);
- inputs.turnVelocityRadPerSec =
- Units.rotationsToRadians(turnVelocity.getValueAsDouble()) / TURN_GEAR_RATIO;
- inputs.turnAppliedVolts = turnAppliedVolts.getValueAsDouble();
- inputs.turnCurrentAmps = new double[] {turnCurrent.getValueAsDouble()};
-
- inputs.odometryTimestamps =
- timestampQueue.stream().mapToDouble((Double value) -> value).toArray();
- inputs.odometryDrivePositionsRad =
- drivePositionQueue.stream()
- .mapToDouble((Double value) -> Units.rotationsToRadians(value) / DRIVE_GEAR_RATIO)
- .toArray();
- inputs.odometryTurnPositions =
- turnPositionQueue.stream()
- .map((Double value) -> Rotation2d.fromRotations(value / TURN_GEAR_RATIO))
- .toArray(Rotation2d[]::new);
- timestampQueue.clear();
- drivePositionQueue.clear();
- turnPositionQueue.clear();
- }
-
- @Override
- public void setDriveVoltage(double volts) {
- driveTalon.setControl(new VoltageOut(volts));
- }
-
- @Override
- public void setTurnVoltage(double volts) {
- turnTalon.setControl(new VoltageOut(volts));
- }
-
- @Override
- public void setDriveBrakeMode(boolean enable) {
- var config = new MotorOutputConfigs();
- config.Inverted = InvertedValue.CounterClockwise_Positive;
- config.NeutralMode = enable ? NeutralModeValue.Brake : NeutralModeValue.Coast;
- driveTalon.getConfigurator().apply(config);
- }
-
- @Override
- public void setTurnBrakeMode(boolean enable) {
- var config = new MotorOutputConfigs();
- config.Inverted =
- isTurnMotorInverted
- ? InvertedValue.Clockwise_Positive
- : InvertedValue.CounterClockwise_Positive;
- config.NeutralMode = enable ? NeutralModeValue.Brake : NeutralModeValue.Coast;
- turnTalon.getConfigurator().apply(config);
- }
+ @Override
+ public void setTurnVoltage(double volts) {
+ turnTalon.setControl(new VoltageOut(volts));
+ }
+
+ @Override
+ public void setDriveBrakeMode(boolean enable) {
+ var config = new MotorOutputConfigs();
+ config.Inverted = InvertedValue.CounterClockwise_Positive;
+ config.NeutralMode = enable ? NeutralModeValue.Brake : NeutralModeValue.Coast;
+ driveTalon.getConfigurator().apply(config);
+ }
+
+ @Override
+ public void setTurnBrakeMode(boolean enable) {
+ var config = new MotorOutputConfigs();
+ config.Inverted =
+ isTurnMotorInverted
+ ? InvertedValue.Clockwise_Positive
+ : InvertedValue.CounterClockwise_Positive;
+ config.NeutralMode = enable ? NeutralModeValue.Brake : NeutralModeValue.Coast;
+ turnTalon.getConfigurator().apply(config);
+ }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/PhoenixOdometryThread.java b/src/main/java/frc/robot/subsystems/drive/PhoenixOdometryThread.java
index b7531fa..7597afc 100644
--- a/src/main/java/frc/robot/subsystems/drive/PhoenixOdometryThread.java
+++ b/src/main/java/frc/robot/subsystems/drive/PhoenixOdometryThread.java
@@ -17,12 +17,14 @@
import com.ctre.phoenix6.CANBus;
import com.ctre.phoenix6.StatusSignal;
import com.ctre.phoenix6.hardware.ParentDevice;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+
import org.littletonrobotics.junction.Logger;
/**
@@ -34,105 +36,105 @@
* time synchronization.
*/
public class PhoenixOdometryThread extends Thread {
- private final Lock signalsLock =
- new ReentrantLock(); // Prevents conflicts when registering signals
- private BaseStatusSignal[] signals = new BaseStatusSignal[0];
- private final List> queues = new ArrayList<>();
- private final List> timestampQueues = new ArrayList<>();
- private boolean isCANFD = false;
+ private final Lock signalsLock =
+ new ReentrantLock(); // Prevents conflicts when registering signals
+ private BaseStatusSignal[] signals = new BaseStatusSignal[0];
+ private final List> queues = new ArrayList<>();
+ private final List> timestampQueues = new ArrayList<>();
+ private boolean isCANFD = false;
- private static PhoenixOdometryThread instance = null;
+ private static PhoenixOdometryThread instance = null;
- public static PhoenixOdometryThread getInstance() {
- if (instance == null) {
- instance = new PhoenixOdometryThread();
+ public static PhoenixOdometryThread getInstance() {
+ if (instance == null) {
+ instance = new PhoenixOdometryThread();
+ }
+ return instance;
}
- return instance;
- }
- private PhoenixOdometryThread() {
- setName("PhoenixOdometryThread");
- setDaemon(true);
- }
-
- @Override
- public void start() {
- if (timestampQueues.size() > 0) {
- super.start();
+ private PhoenixOdometryThread() {
+ setName("PhoenixOdometryThread");
+ setDaemon(true);
}
- }
- public Queue registerSignal(ParentDevice device, StatusSignal signal) {
- Queue queue = new ArrayBlockingQueue<>(20);
- signalsLock.lock();
- Drive.odometryLock.lock();
- try {
- isCANFD = CANBus.isNetworkFD(device.getNetwork());
- BaseStatusSignal[] newSignals = new BaseStatusSignal[signals.length + 1];
- System.arraycopy(signals, 0, newSignals, 0, signals.length);
- newSignals[signals.length] = signal;
- signals = newSignals;
- queues.add(queue);
- } finally {
- signalsLock.unlock();
- Drive.odometryLock.unlock();
+ @Override
+ public void start() {
+ if (timestampQueues.size() > 0) {
+ super.start();
+ }
}
- return queue;
- }
- public Queue makeTimestampQueue() {
- Queue queue = new ArrayBlockingQueue<>(20);
- Drive.odometryLock.lock();
- try {
- timestampQueues.add(queue);
- } finally {
- Drive.odometryLock.unlock();
+ public Queue registerSignal(ParentDevice device, StatusSignal signal) {
+ Queue queue = new ArrayBlockingQueue<>(20);
+ signalsLock.lock();
+ Drive.odometryLock.lock();
+ try {
+ isCANFD = CANBus.isNetworkFD(device.getNetwork());
+ BaseStatusSignal[] newSignals = new BaseStatusSignal[signals.length + 1];
+ System.arraycopy(signals, 0, newSignals, 0, signals.length);
+ newSignals[signals.length] = signal;
+ signals = newSignals;
+ queues.add(queue);
+ } finally {
+ signalsLock.unlock();
+ Drive.odometryLock.unlock();
+ }
+ return queue;
}
- return queue;
- }
- @Override
- public void run() {
- while (true) {
- // Wait for updates from all signals
- signalsLock.lock();
- try {
- if (isCANFD) {
- BaseStatusSignal.waitForAll(2.0 / Module.ODOMETRY_FREQUENCY, signals);
- } else {
- // "waitForAll" does not support blocking on multiple
- // signals with a bus that is not CAN FD, regardless
- // of Pro licensing. No reasoning for this behavior
- // is provided by the documentation.
- Thread.sleep((long) (1000.0 / Module.ODOMETRY_FREQUENCY));
- if (signals.length > 0) BaseStatusSignal.refreshAll(signals);
+ public Queue makeTimestampQueue() {
+ Queue queue = new ArrayBlockingQueue<>(20);
+ Drive.odometryLock.lock();
+ try {
+ timestampQueues.add(queue);
+ } finally {
+ Drive.odometryLock.unlock();
}
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- signalsLock.unlock();
- }
+ return queue;
+ }
- // Save new data to queues
- Drive.odometryLock.lock();
- try {
- double timestamp = Logger.getRealTimestamp() / 1e6;
- double totalLatency = 0.0;
- for (BaseStatusSignal signal : signals) {
- totalLatency += signal.getTimestamp().getLatency();
- }
- if (signals.length > 0) {
- timestamp -= totalLatency / signals.length;
- }
- for (int i = 0; i < signals.length; i++) {
- queues.get(i).offer(signals[i].getValueAsDouble());
- }
- for (int i = 0; i < timestampQueues.size(); i++) {
- timestampQueues.get(i).offer(timestamp);
+ @Override
+ public void run() {
+ while (true) {
+ // Wait for updates from all signals
+ signalsLock.lock();
+ try {
+ if (isCANFD) {
+ BaseStatusSignal.waitForAll(2.0 / Module.ODOMETRY_FREQUENCY, signals);
+ } else {
+ // "waitForAll" does not support blocking on multiple
+ // signals with a bus that is not CAN FD, regardless
+ // of Pro licensing. No reasoning for this behavior
+ // is provided by the documentation.
+ Thread.sleep((long) (1000.0 / Module.ODOMETRY_FREQUENCY));
+ if (signals.length > 0) BaseStatusSignal.refreshAll(signals);
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ signalsLock.unlock();
+ }
+
+ // Save new data to queues
+ Drive.odometryLock.lock();
+ try {
+ double timestamp = Logger.getRealTimestamp() / 1e6;
+ double totalLatency = 0.0;
+ for (BaseStatusSignal signal : signals) {
+ totalLatency += signal.getTimestamp().getLatency();
+ }
+ if (signals.length > 0) {
+ timestamp -= totalLatency / signals.length;
+ }
+ for (int i = 0; i < signals.length; i++) {
+ queues.get(i).offer(signals[i].getValueAsDouble());
+ }
+ for (int i = 0; i < timestampQueues.size(); i++) {
+ timestampQueues.get(i).offer(timestamp);
+ }
+ } finally {
+ Drive.odometryLock.unlock();
+ }
}
- } finally {
- Drive.odometryLock.unlock();
- }
}
- }
}
diff --git a/src/main/java/frc/robot/subsystems/drive/SparkMaxOdometryThread.java b/src/main/java/frc/robot/subsystems/drive/SparkMaxOdometryThread.java
index 15db266..05f9ebc 100644
--- a/src/main/java/frc/robot/subsystems/drive/SparkMaxOdometryThread.java
+++ b/src/main/java/frc/robot/subsystems/drive/SparkMaxOdometryThread.java
@@ -14,12 +14,14 @@
package frc.robot.subsystems.drive;
import edu.wpi.first.wpilibj.Notifier;
+
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalDouble;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.function.Supplier;
+
import org.littletonrobotics.junction.Logger;
/**
@@ -29,79 +31,79 @@
* blocking thread. A Notifier thread is used to gather samples with consistent timing.
*/
public class SparkMaxOdometryThread {
- private List> signals = new ArrayList<>();
- private List> queues = new ArrayList<>();
- private List> timestampQueues = new ArrayList<>();
+ private List> signals = new ArrayList<>();
+ private List> queues = new ArrayList<>();
+ private List> timestampQueues = new ArrayList<>();
- private final Notifier notifier;
- private static SparkMaxOdometryThread instance = null;
+ private final Notifier notifier;
+ private static SparkMaxOdometryThread instance = null;
- public static SparkMaxOdometryThread getInstance() {
- if (instance == null) {
- instance = new SparkMaxOdometryThread();
+ public static SparkMaxOdometryThread getInstance() {
+ if (instance == null) {
+ instance = new SparkMaxOdometryThread();
+ }
+ return instance;
}
- return instance;
- }
- private SparkMaxOdometryThread() {
- notifier = new Notifier(this::periodic);
- notifier.setName("SparkMaxOdometryThread");
- }
-
- public void start() {
- if (timestampQueues.size() > 0) {
- notifier.startPeriodic(1.0 / Module.ODOMETRY_FREQUENCY);
+ private SparkMaxOdometryThread() {
+ notifier = new Notifier(this::periodic);
+ notifier.setName("SparkMaxOdometryThread");
}
- }
- public Queue registerSignal(Supplier signal) {
- Queue queue = new ArrayBlockingQueue<>(20);
- Drive.odometryLock.lock();
- try {
- signals.add(signal);
- queues.add(queue);
- } finally {
- Drive.odometryLock.unlock();
+ public void start() {
+ if (timestampQueues.size() > 0) {
+ notifier.startPeriodic(1.0 / Module.ODOMETRY_FREQUENCY);
+ }
}
- return queue;
- }
- public Queue makeTimestampQueue() {
- Queue queue = new ArrayBlockingQueue<>(20);
- Drive.odometryLock.lock();
- try {
- timestampQueues.add(queue);
- } finally {
- Drive.odometryLock.unlock();
+ public Queue registerSignal(Supplier signal) {
+ Queue queue = new ArrayBlockingQueue<>(20);
+ Drive.odometryLock.lock();
+ try {
+ signals.add(signal);
+ queues.add(queue);
+ } finally {
+ Drive.odometryLock.unlock();
+ }
+ return queue;
}
- return queue;
- }
- private void periodic() {
- Drive.odometryLock.lock();
- double timestamp = Logger.getRealTimestamp() / 1e6;
- try {
- double[] values = new double[signals.size()];
- boolean isValid = true;
- for (int i = 0; i < signals.size(); i++) {
- OptionalDouble value = signals.get(i).get();
- if (value.isPresent()) {
- values[i] = value.getAsDouble();
- } else {
- isValid = false;
- break;
- }
- }
- if (isValid) {
- for (int i = 0; i < queues.size(); i++) {
- queues.get(i).offer(values[i]);
+ public Queue makeTimestampQueue() {
+ Queue queue = new ArrayBlockingQueue<>(20);
+ Drive.odometryLock.lock();
+ try {
+ timestampQueues.add(queue);
+ } finally {
+ Drive.odometryLock.unlock();
}
- for (int i = 0; i < timestampQueues.size(); i++) {
- timestampQueues.get(i).offer(timestamp);
+ return queue;
+ }
+
+ private void periodic() {
+ Drive.odometryLock.lock();
+ double timestamp = Logger.getRealTimestamp() / 1e6;
+ try {
+ double[] values = new double[signals.size()];
+ boolean isValid = true;
+ for (int i = 0; i < signals.size(); i++) {
+ OptionalDouble value = signals.get(i).get();
+ if (value.isPresent()) {
+ values[i] = value.getAsDouble();
+ } else {
+ isValid = false;
+ break;
+ }
+ }
+ if (isValid) {
+ for (int i = 0; i < queues.size(); i++) {
+ queues.get(i).offer(values[i]);
+ }
+ for (int i = 0; i < timestampQueues.size(); i++) {
+ timestampQueues.get(i).offer(timestamp);
+ }
+ }
+ } finally {
+ Drive.odometryLock.unlock();
}
- }
- } finally {
- Drive.odometryLock.unlock();
}
- }
}
diff --git a/src/main/java/frc/robot/subsystems/flywheel/Flywheel.java b/src/main/java/frc/robot/subsystems/flywheel/Flywheel.java
deleted file mode 100644
index 6056b24..0000000
--- a/src/main/java/frc/robot/subsystems/flywheel/Flywheel.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2021-2024 FRC 6328
-// http://github.com/Mechanical-Advantage
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// version 3 as published by the Free Software Foundation or
-// available in the root directory of this project.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-package frc.robot.subsystems.flywheel;
-
-import static edu.wpi.first.units.Units.*;
-
-import edu.wpi.first.math.controller.SimpleMotorFeedforward;
-import edu.wpi.first.math.util.Units;
-import edu.wpi.first.wpilibj2.command.Command;
-import edu.wpi.first.wpilibj2.command.SubsystemBase;
-import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
-import frc.robot.Constants;
-import org.littletonrobotics.junction.AutoLogOutput;
-import org.littletonrobotics.junction.Logger;
-
-public class Flywheel extends SubsystemBase {
- private final FlywheelIO io;
- private final FlywheelIOInputsAutoLogged inputs = new FlywheelIOInputsAutoLogged();
- private final SimpleMotorFeedforward ffModel;
- private final SysIdRoutine sysId;
-
- /** Creates a new Flywheel. */
- public Flywheel(FlywheelIO io) {
- this.io = io;
-
- // Switch constants based on mode (the physics simulator is treated as a
- // separate robot with different tuning)
- switch (Constants.currentMode) {
- case REAL:
- case REPLAY:
- ffModel = new SimpleMotorFeedforward(0.1, 0.05);
- io.configurePID(1.0, 0.0, 0.0);
- break;
- case SIM:
- ffModel = new SimpleMotorFeedforward(0.0, 0.03);
- io.configurePID(0.5, 0.0, 0.0);
- break;
- default:
- ffModel = new SimpleMotorFeedforward(0.0, 0.0);
- break;
- }
-
- // Configure SysId
- sysId =
- new SysIdRoutine(
- new SysIdRoutine.Config(
- null,
- null,
- null,
- (state) -> Logger.recordOutput("Flywheel/SysIdState", state.toString())),
- new SysIdRoutine.Mechanism((voltage) -> runVolts(voltage.in(Volts)), null, this));
- }
-
- @Override
- public void periodic() {
- io.updateInputs(inputs);
- Logger.processInputs("Flywheel", inputs);
- }
-
- /** Run open loop at the specified voltage. */
- public void runVolts(double volts) {
- io.setVoltage(volts);
- }
-
- /** Run closed loop at the specified velocity. */
- public void runVelocity(double velocityRPM) {
- var velocityRadPerSec = Units.rotationsPerMinuteToRadiansPerSecond(velocityRPM);
- io.setVelocity(velocityRadPerSec, ffModel.calculate(velocityRadPerSec));
-
- // Log flywheel setpoint
- Logger.recordOutput("Flywheel/SetpointRPM", velocityRPM);
- }
-
- /** Stops the flywheel. */
- public void stop() {
- io.stop();
- }
-
- /** Returns a command to run a quasistatic test in the specified direction. */
- public Command sysIdQuasistatic(SysIdRoutine.Direction direction) {
- return sysId.quasistatic(direction);
- }
-
- /** Returns a command to run a dynamic test in the specified direction. */
- public Command sysIdDynamic(SysIdRoutine.Direction direction) {
- return sysId.dynamic(direction);
- }
-
- /** Returns the current velocity in RPM. */
- @AutoLogOutput
- public double getVelocityRPM() {
- return Units.radiansPerSecondToRotationsPerMinute(inputs.velocityRadPerSec);
- }
-
- /** Returns the current velocity in radians per second. */
- public double getCharacterizationVelocity() {
- return inputs.velocityRadPerSec;
- }
-}
diff --git a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIO.java b/src/main/java/frc/robot/subsystems/flywheel/FlywheelIO.java
deleted file mode 100644
index 98f7624..0000000
--- a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIO.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2021-2024 FRC 6328
-// http://github.com/Mechanical-Advantage
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// version 3 as published by the Free Software Foundation or
-// available in the root directory of this project.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-package frc.robot.subsystems.flywheel;
-
-import org.littletonrobotics.junction.AutoLog;
-
-public interface FlywheelIO {
- @AutoLog
- public static class FlywheelIOInputs {
- public double positionRad = 0.0;
- public double velocityRadPerSec = 0.0;
- public double appliedVolts = 0.0;
- public double[] currentAmps = new double[] {};
- }
-
- /** Updates the set of loggable inputs. */
- public default void updateInputs(FlywheelIOInputs inputs) {}
-
- /** Run open loop at the specified voltage. */
- public default void setVoltage(double volts) {}
-
- /** Run closed loop at the specified velocity. */
- public default void setVelocity(double velocityRadPerSec, double ffVolts) {}
-
- /** Stop in open loop. */
- public default void stop() {}
-
- /** Set velocity PID constants. */
- public default void configurePID(double kP, double kI, double kD) {}
-}
diff --git a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOSim.java b/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOSim.java
deleted file mode 100644
index 32ffa6f..0000000
--- a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOSim.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2021-2024 FRC 6328
-// http://github.com/Mechanical-Advantage
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// version 3 as published by the Free Software Foundation or
-// available in the root directory of this project.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-package frc.robot.subsystems.flywheel;
-
-import edu.wpi.first.math.MathUtil;
-import edu.wpi.first.math.controller.PIDController;
-import edu.wpi.first.math.system.plant.DCMotor;
-import edu.wpi.first.wpilibj.simulation.FlywheelSim;
-
-public class FlywheelIOSim implements FlywheelIO {
- private FlywheelSim sim = new FlywheelSim(DCMotor.getNEO(1), 1.5, 0.004);
- private PIDController pid = new PIDController(0.0, 0.0, 0.0);
-
- private boolean closedLoop = false;
- private double ffVolts = 0.0;
- private double appliedVolts = 0.0;
-
- @Override
- public void updateInputs(FlywheelIOInputs inputs) {
- if (closedLoop) {
- appliedVolts =
- MathUtil.clamp(pid.calculate(sim.getAngularVelocityRadPerSec()) + ffVolts, -12.0, 12.0);
- sim.setInputVoltage(appliedVolts);
- }
-
- sim.update(0.02);
-
- inputs.positionRad = 0.0;
- inputs.velocityRadPerSec = sim.getAngularVelocityRadPerSec();
- inputs.appliedVolts = appliedVolts;
- inputs.currentAmps = new double[] {sim.getCurrentDrawAmps()};
- }
-
- @Override
- public void setVoltage(double volts) {
- closedLoop = false;
- appliedVolts = volts;
- sim.setInputVoltage(volts);
- }
-
- @Override
- public void setVelocity(double velocityRadPerSec, double ffVolts) {
- closedLoop = true;
- pid.setSetpoint(velocityRadPerSec);
- this.ffVolts = ffVolts;
- }
-
- @Override
- public void stop() {
- setVoltage(0.0);
- }
-
- @Override
- public void configurePID(double kP, double kI, double kD) {
- pid.setPID(kP, kI, kD);
- }
-}
diff --git a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOSparkMax.java b/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOSparkMax.java
deleted file mode 100644
index 787f369..0000000
--- a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOSparkMax.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2021-2024 FRC 6328
-// http://github.com/Mechanical-Advantage
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// version 3 as published by the Free Software Foundation or
-// available in the root directory of this project.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-package frc.robot.subsystems.flywheel;
-
-import com.revrobotics.CANSparkBase.ControlType;
-import com.revrobotics.CANSparkLowLevel.MotorType;
-import com.revrobotics.CANSparkMax;
-import com.revrobotics.RelativeEncoder;
-import com.revrobotics.SparkPIDController;
-import com.revrobotics.SparkPIDController.ArbFFUnits;
-import edu.wpi.first.math.util.Units;
-
-/**
- * NOTE: To use the Spark Flex / NEO Vortex, replace all instances of "CANSparkMax" with
- * "CANSparkFlex".
- */
-public class FlywheelIOSparkMax implements FlywheelIO {
- private static final double GEAR_RATIO = 1.5;
-
- private final CANSparkMax leader = new CANSparkMax(0, MotorType.kBrushless);
- private final CANSparkMax follower = new CANSparkMax(1, MotorType.kBrushless);
- private final RelativeEncoder encoder = leader.getEncoder();
- private final SparkPIDController pid = leader.getPIDController();
-
- public FlywheelIOSparkMax() {
- leader.restoreFactoryDefaults();
- follower.restoreFactoryDefaults();
-
- leader.setCANTimeout(250);
- follower.setCANTimeout(250);
-
- leader.setInverted(false);
- follower.follow(leader, false);
-
- leader.enableVoltageCompensation(12.0);
- leader.setSmartCurrentLimit(30);
-
- leader.burnFlash();
- follower.burnFlash();
- }
-
- @Override
- public void updateInputs(FlywheelIOInputs inputs) {
- inputs.positionRad = Units.rotationsToRadians(encoder.getPosition() / GEAR_RATIO);
- inputs.velocityRadPerSec =
- Units.rotationsPerMinuteToRadiansPerSecond(encoder.getVelocity() / GEAR_RATIO);
- inputs.appliedVolts = leader.getAppliedOutput() * leader.getBusVoltage();
- inputs.currentAmps = new double[] {leader.getOutputCurrent(), follower.getOutputCurrent()};
- }
-
- @Override
- public void setVoltage(double volts) {
- leader.setVoltage(volts);
- }
-
- @Override
- public void setVelocity(double velocityRadPerSec, double ffVolts) {
- pid.setReference(
- Units.radiansPerSecondToRotationsPerMinute(velocityRadPerSec) * GEAR_RATIO,
- ControlType.kVelocity,
- 0,
- ffVolts,
- ArbFFUnits.kVoltage);
- }
-
- @Override
- public void stop() {
- leader.stopMotor();
- }
-
- @Override
- public void configurePID(double kP, double kI, double kD) {
- pid.setP(kP, 0);
- pid.setI(kI, 0);
- pid.setD(kD, 0);
- pid.setFF(0, 0);
- }
-}
diff --git a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOTalonFX.java b/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOTalonFX.java
deleted file mode 100644
index 3f53512..0000000
--- a/src/main/java/frc/robot/subsystems/flywheel/FlywheelIOTalonFX.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2021-2024 FRC 6328
-// http://github.com/Mechanical-Advantage
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// version 3 as published by the Free Software Foundation or
-// available in the root directory of this project.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-package frc.robot.subsystems.flywheel;
-
-import com.ctre.phoenix6.BaseStatusSignal;
-import com.ctre.phoenix6.StatusSignal;
-import com.ctre.phoenix6.configs.Slot0Configs;
-import com.ctre.phoenix6.configs.TalonFXConfiguration;
-import com.ctre.phoenix6.controls.Follower;
-import com.ctre.phoenix6.controls.VelocityVoltage;
-import com.ctre.phoenix6.controls.VoltageOut;
-import com.ctre.phoenix6.hardware.TalonFX;
-import com.ctre.phoenix6.signals.NeutralModeValue;
-import edu.wpi.first.math.util.Units;
-
-public class FlywheelIOTalonFX implements FlywheelIO {
- private static final double GEAR_RATIO = 1.5;
-
- private final TalonFX leader = new TalonFX(0);
- private final TalonFX follower = new TalonFX(1);
-
- private final StatusSignal leaderPosition = leader.getPosition();
- private final StatusSignal leaderVelocity = leader.getVelocity();
- private final StatusSignal leaderAppliedVolts = leader.getMotorVoltage();
- private final StatusSignal leaderCurrent = leader.getSupplyCurrent();
- private final StatusSignal followerCurrent = follower.getSupplyCurrent();
-
- public FlywheelIOTalonFX() {
- var config = new TalonFXConfiguration();
- config.CurrentLimits.SupplyCurrentLimit = 30.0;
- config.CurrentLimits.SupplyCurrentLimitEnable = true;
- config.MotorOutput.NeutralMode = NeutralModeValue.Coast;
- leader.getConfigurator().apply(config);
- follower.getConfigurator().apply(config);
- follower.setControl(new Follower(leader.getDeviceID(), false));
-
- BaseStatusSignal.setUpdateFrequencyForAll(
- 50.0, leaderPosition, leaderVelocity, leaderAppliedVolts, leaderCurrent, followerCurrent);
- leader.optimizeBusUtilization();
- follower.optimizeBusUtilization();
- }
-
- @Override
- public void updateInputs(FlywheelIOInputs inputs) {
- BaseStatusSignal.refreshAll(
- leaderPosition, leaderVelocity, leaderAppliedVolts, leaderCurrent, followerCurrent);
- inputs.positionRad = Units.rotationsToRadians(leaderPosition.getValueAsDouble()) / GEAR_RATIO;
- inputs.velocityRadPerSec =
- Units.rotationsToRadians(leaderVelocity.getValueAsDouble()) / GEAR_RATIO;
- inputs.appliedVolts = leaderAppliedVolts.getValueAsDouble();
- inputs.currentAmps =
- new double[] {leaderCurrent.getValueAsDouble(), followerCurrent.getValueAsDouble()};
- }
-
- @Override
- public void setVoltage(double volts) {
- leader.setControl(new VoltageOut(volts));
- }
-
- @Override
- public void setVelocity(double velocityRadPerSec, double ffVolts) {
- leader.setControl(
- new VelocityVoltage(
- Units.radiansToRotations(velocityRadPerSec),
- 0.0,
- true,
- ffVolts,
- 0,
- false,
- false,
- false));
- }
-
- @Override
- public void stop() {
- leader.stopMotor();
- }
-
- @Override
- public void configurePID(double kP, double kI, double kD) {
- var config = new Slot0Configs();
- config.kP = kP;
- config.kI = kI;
- config.kD = kD;
- leader.getConfigurator().apply(config);
- }
-}
diff --git a/src/main/java/frc/robot/util/LocalADStarAK.java b/src/main/java/frc/robot/util/LocalADStarAK.java
index 5309b85..10a7fe0 100644
--- a/src/main/java/frc/robot/util/LocalADStarAK.java
+++ b/src/main/java/frc/robot/util/LocalADStarAK.java
@@ -8,9 +8,11 @@
import com.pathplanner.lib.pathfinding.Pathfinder;
import edu.wpi.first.math.Pair;
import edu.wpi.first.math.geometry.Translation2d;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import org.littletonrobotics.junction.LogTable;
import org.littletonrobotics.junction.Logger;
import org.littletonrobotics.junction.inputs.LoggableInputs;
@@ -19,134 +21,135 @@
// https://gist.github.com/mjansen4857/a8024b55eb427184dbd10ae8923bd57d
public class LocalADStarAK implements Pathfinder {
- private final ADStarIO io = new ADStarIO();
-
- /**
- * Get if a new path has been calculated since the last time a path was retrieved
- *
- * @return True if a new path is available
- */
- @Override
- public boolean isNewPathAvailable() {
- if (!Logger.hasReplaySource()) {
- io.updateIsNewPathAvailable();
- }
-
- Logger.processInputs("LocalADStarAK", io);
-
- return io.isNewPathAvailable;
- }
-
- /**
- * Get the most recently calculated path
- *
- * @param constraints The path constraints to use when creating the path
- * @param goalEndState The goal end state to use when creating the path
- * @return The PathPlannerPath created from the points calculated by the pathfinder
- */
- @Override
- public PathPlannerPath getCurrentPath(PathConstraints constraints, GoalEndState goalEndState) {
- if (!Logger.hasReplaySource()) {
- io.updateCurrentPathPoints(constraints, goalEndState);
- }
+ private final ADStarIO io = new ADStarIO();
- Logger.processInputs("LocalADStarAK", io);
+ /**
+ * Get if a new path has been calculated since the last time a path was retrieved
+ *
+ * @return True if a new path is available
+ */
+ @Override
+ public boolean isNewPathAvailable() {
+ if (!Logger.hasReplaySource()) {
+ io.updateIsNewPathAvailable();
+ }
- if (io.currentPathPoints.isEmpty()) {
- return null;
- }
+ Logger.processInputs("LocalADStarAK", io);
- return PathPlannerPath.fromPathPoints(io.currentPathPoints, constraints, goalEndState);
- }
-
- /**
- * Set the start position to pathfind from
- *
- * @param startPosition Start position on the field. If this is within an obstacle it will be
- * moved to the nearest non-obstacle node.
- */
- @Override
- public void setStartPosition(Translation2d startPosition) {
- if (!Logger.hasReplaySource()) {
- io.adStar.setStartPosition(startPosition);
- }
- }
-
- /**
- * Set the goal position to pathfind to
- *
- * @param goalPosition Goal position on the field. f this is within an obstacle it will be moved
- * to the nearest non-obstacle node.
- */
- @Override
- public void setGoalPosition(Translation2d goalPosition) {
- if (!Logger.hasReplaySource()) {
- io.adStar.setGoalPosition(goalPosition);
- }
- }
-
- /**
- * Set the dynamic obstacles that should be avoided while pathfinding.
- *
- * @param obs A List of Translation2d pairs representing obstacles. Each Translation2d represents
- * opposite corners of a bounding box.
- * @param currentRobotPos The current position of the robot. This is needed to change the start
- * position of the path to properly avoid obstacles
- */
- @Override
- public void setDynamicObstacles(
- List> obs, Translation2d currentRobotPos) {
- if (!Logger.hasReplaySource()) {
- io.adStar.setDynamicObstacles(obs, currentRobotPos);
+ return io.isNewPathAvailable;
}
- }
-
- private static class ADStarIO implements LoggableInputs {
- public LocalADStar adStar = new LocalADStar();
- public boolean isNewPathAvailable = false;
- public List currentPathPoints = Collections.emptyList();
+ /**
+ * Get the most recently calculated path
+ *
+ * @param constraints The path constraints to use when creating the path
+ * @param goalEndState The goal end state to use when creating the path
+ * @return The PathPlannerPath created from the points calculated by the pathfinder
+ */
@Override
- public void toLog(LogTable table) {
- table.put("IsNewPathAvailable", isNewPathAvailable);
-
- double[] pointsLogged = new double[currentPathPoints.size() * 2];
- int idx = 0;
- for (PathPoint point : currentPathPoints) {
- pointsLogged[idx] = point.position.getX();
- pointsLogged[idx + 1] = point.position.getY();
- idx += 2;
- }
-
- table.put("CurrentPathPoints", pointsLogged);
- }
+ public PathPlannerPath getCurrentPath(PathConstraints constraints, GoalEndState goalEndState) {
+ if (!Logger.hasReplaySource()) {
+ io.updateCurrentPathPoints(constraints, goalEndState);
+ }
- @Override
- public void fromLog(LogTable table) {
- isNewPathAvailable = table.get("IsNewPathAvailable", false);
+ Logger.processInputs("LocalADStarAK", io);
- double[] pointsLogged = table.get("CurrentPathPoints", new double[0]);
+ if (io.currentPathPoints.isEmpty()) {
+ return null;
+ }
- List pathPoints = new ArrayList<>();
- for (int i = 0; i < pointsLogged.length; i += 2) {
- pathPoints.add(new PathPoint(new Translation2d(pointsLogged[i], pointsLogged[i + 1]), null));
- }
+ return PathPlannerPath.fromPathPoints(io.currentPathPoints, constraints, goalEndState);
+ }
- currentPathPoints = pathPoints;
+ /**
+ * Set the start position to pathfind from
+ *
+ * @param startPosition Start position on the field. If this is within an obstacle it will be
+ * moved to the nearest non-obstacle node.
+ */
+ @Override
+ public void setStartPosition(Translation2d startPosition) {
+ if (!Logger.hasReplaySource()) {
+ io.adStar.setStartPosition(startPosition);
+ }
}
- public void updateIsNewPathAvailable() {
- isNewPathAvailable = adStar.isNewPathAvailable();
+ /**
+ * Set the goal position to pathfind to
+ *
+ * @param goalPosition Goal position on the field. f this is within an obstacle it will be moved
+ * to the nearest non-obstacle node.
+ */
+ @Override
+ public void setGoalPosition(Translation2d goalPosition) {
+ if (!Logger.hasReplaySource()) {
+ io.adStar.setGoalPosition(goalPosition);
+ }
}
- public void updateCurrentPathPoints(PathConstraints constraints, GoalEndState goalEndState) {
- PathPlannerPath currentPath = adStar.getCurrentPath(constraints, goalEndState);
+ /**
+ * Set the dynamic obstacles that should be avoided while pathfinding.
+ *
+ * @param obs A List of Translation2d pairs representing obstacles. Each Translation2d represents
+ * opposite corners of a bounding box.
+ * @param currentRobotPos The current position of the robot. This is needed to change the start
+ * position of the path to properly avoid obstacles
+ */
+ @Override
+ public void setDynamicObstacles(
+ List> obs, Translation2d currentRobotPos) {
+ if (!Logger.hasReplaySource()) {
+ io.adStar.setDynamicObstacles(obs, currentRobotPos);
+ }
+ }
- if (currentPath != null) {
- currentPathPoints = currentPath.getAllPathPoints();
- } else {
- currentPathPoints = Collections.emptyList();
- }
+ private static class ADStarIO implements LoggableInputs {
+ public LocalADStar adStar = new LocalADStar();
+ public boolean isNewPathAvailable = false;
+ public List currentPathPoints = Collections.emptyList();
+
+ @Override
+ public void toLog(LogTable table) {
+ table.put("IsNewPathAvailable", isNewPathAvailable);
+
+ double[] pointsLogged = new double[currentPathPoints.size() * 2];
+ int idx = 0;
+ for (PathPoint point : currentPathPoints) {
+ pointsLogged[idx] = point.position.getX();
+ pointsLogged[idx + 1] = point.position.getY();
+ idx += 2;
+ }
+
+ table.put("CurrentPathPoints", pointsLogged);
+ }
+
+ @Override
+ public void fromLog(LogTable table) {
+ isNewPathAvailable = table.get("IsNewPathAvailable", false);
+
+ double[] pointsLogged = table.get("CurrentPathPoints", new double[0]);
+
+ List pathPoints = new ArrayList<>();
+ for (int i = 0; i < pointsLogged.length; i += 2) {
+ pathPoints.add(
+ new PathPoint(new Translation2d(pointsLogged[i], pointsLogged[i + 1]), null));
+ }
+
+ currentPathPoints = pathPoints;
+ }
+
+ public void updateIsNewPathAvailable() {
+ isNewPathAvailable = adStar.isNewPathAvailable();
+ }
+
+ public void updateCurrentPathPoints(PathConstraints constraints, GoalEndState goalEndState) {
+ PathPlannerPath currentPath = adStar.getCurrentPath(constraints, goalEndState);
+
+ if (currentPath != null) {
+ currentPathPoints = currentPath.getAllPathPoints();
+ } else {
+ currentPathPoints = Collections.emptyList();
+ }
+ }
}
- }
}
diff --git a/vendordeps/AdvantageKit.json b/vendordeps/AdvantageKit.json
index 5fda88d..1f57c3b 100644
--- a/vendordeps/AdvantageKit.json
+++ b/vendordeps/AdvantageKit.json
@@ -1,42 +1,42 @@
{
- "fileName": "AdvantageKit.json",
- "name": "AdvantageKit",
- "version": "3.2.1",
- "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003",
- "frcYear": "2024",
- "mavenUrls": [],
- "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json",
- "javaDependencies": [
- {
- "groupId": "org.littletonrobotics.akit.junction",
- "artifactId": "wpilib-shim",
- "version": "3.2.1"
- },
- {
- "groupId": "org.littletonrobotics.akit.junction",
- "artifactId": "junction-core",
- "version": "3.2.1"
- },
- {
- "groupId": "org.littletonrobotics.akit.conduit",
- "artifactId": "conduit-api",
- "version": "3.2.1"
- }
- ],
- "jniDependencies": [
- {
- "groupId": "org.littletonrobotics.akit.conduit",
- "artifactId": "conduit-wpilibio",
- "version": "3.2.1",
- "skipInvalidPlatforms": false,
- "isJar": false,
- "validPlatforms": [
- "linuxathena",
- "windowsx86-64",
- "linuxx86-64",
- "osxuniversal"
- ]
- }
- ],
- "cppDependencies": []
-}
\ No newline at end of file
+ "fileName": "AdvantageKit.json",
+ "name": "AdvantageKit",
+ "version": "3.2.1",
+ "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003",
+ "frcYear": "2024",
+ "mavenUrls": [],
+ "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json",
+ "javaDependencies": [
+ {
+ "groupId": "org.littletonrobotics.akit.junction",
+ "artifactId": "wpilib-shim",
+ "version": "3.2.1"
+ },
+ {
+ "groupId": "org.littletonrobotics.akit.junction",
+ "artifactId": "junction-core",
+ "version": "3.2.1"
+ },
+ {
+ "groupId": "org.littletonrobotics.akit.conduit",
+ "artifactId": "conduit-api",
+ "version": "3.2.1"
+ }
+ ],
+ "jniDependencies": [
+ {
+ "groupId": "org.littletonrobotics.akit.conduit",
+ "artifactId": "conduit-wpilibio",
+ "version": "3.2.1",
+ "skipInvalidPlatforms": false,
+ "isJar": false,
+ "validPlatforms": [
+ "linuxathena",
+ "windowsx86-64",
+ "linuxx86-64",
+ "osxuniversal"
+ ]
+ }
+ ],
+ "cppDependencies": []
+}