From a2e7a6ef73b665c4871c67f116dd877bc5a33e3c Mon Sep 17 00:00:00 2001 From: IRoot Date: Thu, 9 Jan 2025 21:16:04 -0500 Subject: [PATCH 01/13] created coral subsystem --- .../frc/robot/constants/CoralConstants.java | 79 ++++++++++++++++- .../frc/robot/subsystems/coral/CoralIO.java | 26 +++++- .../frc/robot/subsystems/coral/CoralIOFX.java | 78 +++++++++++++++++ .../subsystems/coral/CoralSubsystem.java | 84 ++++++++++++++++++- 4 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 src/main/java/frc/robot/subsystems/coral/CoralIOFX.java diff --git a/src/main/java/frc/robot/constants/CoralConstants.java b/src/main/java/frc/robot/constants/CoralConstants.java index 3ac015bb..5fe9aeac 100644 --- a/src/main/java/frc/robot/constants/CoralConstants.java +++ b/src/main/java/frc/robot/constants/CoralConstants.java @@ -1,3 +1,80 @@ package frc.robot.constants; -public class CoralConstants {} +import static edu.wpi.first.units.Units.DegreesPerSecond; + +import com.ctre.phoenix6.configs.CurrentLimitsConfigs; +import com.ctre.phoenix6.configs.HardwareLimitSwitchConfigs; +import com.ctre.phoenix6.configs.MotionMagicConfigs; +import com.ctre.phoenix6.configs.MotorOutputConfigs; +import com.ctre.phoenix6.configs.Slot0Configs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.signals.ForwardLimitSourceValue; +import com.ctre.phoenix6.signals.ForwardLimitTypeValue; +import com.ctre.phoenix6.signals.GravityTypeValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.ctre.phoenix6.signals.ReverseLimitSourceValue; +import com.ctre.phoenix6.signals.ReverseLimitTypeValue; +import edu.wpi.first.units.measure.AngularVelocity; + +public class CoralConstants { + public static int kCoralFxId = 0; + + public static final AngularVelocity kCloseEnough = DegreesPerSecond.of(5); + + // Coral Talon FX Config + public static TalonFXConfiguration getFXConfig() { + TalonFXConfiguration fxConfig = new TalonFXConfiguration(); + + CurrentLimitsConfigs current = + new CurrentLimitsConfigs() + .withStatorCurrentLimit(10) + .withStatorCurrentLimitEnable(false) + .withStatorCurrentLimit(20) + .withSupplyCurrentLimit(10) + .withSupplyCurrentLowerLimit(8) + .withSupplyCurrentLowerTime(0.02) + .withSupplyCurrentLimitEnable(true); + fxConfig.CurrentLimits = current; + + HardwareLimitSwitchConfigs hwLimit = + new HardwareLimitSwitchConfigs() + .withForwardLimitAutosetPositionEnable(false) + .withForwardLimitEnable(false) + .withForwardLimitType(ForwardLimitTypeValue.NormallyOpen) + .withForwardLimitSource(ForwardLimitSourceValue.LimitSwitchPin) + .withReverseLimitAutosetPositionEnable(false) + .withReverseLimitEnable(false) + .withReverseLimitType(ReverseLimitTypeValue.NormallyOpen) + .withReverseLimitSource(ReverseLimitSourceValue.LimitSwitchPin); + fxConfig.HardwareLimitSwitch = hwLimit; + + Slot0Configs slot0 = + new Slot0Configs() + .withKP(0) + .withKI(0) + .withKD(0) + .withGravityType(GravityTypeValue.Elevator_Static) + .withKG(0) + .withKS(0) + .withKV(0) + .withKA(0); + fxConfig.Slot0 = slot0; + + MotionMagicConfigs motionMagic = + new MotionMagicConfigs() + .withMotionMagicAcceleration(0) + .withMotionMagicCruiseVelocity(0) + .withMotionMagicExpo_kA(0) + .withMotionMagicExpo_kV(0) + .withMotionMagicJerk(0); + fxConfig.MotionMagic = motionMagic; + + MotorOutputConfigs motorOut = + new MotorOutputConfigs() + .withDutyCycleNeutralDeadband(0.01) + .withNeutralMode(NeutralModeValue.Coast); + fxConfig.MotorOutput = motorOut; + + return fxConfig; + } +} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIO.java b/src/main/java/frc/robot/subsystems/coral/CoralIO.java index ebd16b79..4631c0b5 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIO.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIO.java @@ -1,3 +1,27 @@ package frc.robot.subsystems.coral; -public class CoralIO {} +import static edu.wpi.first.units.Units.RotationsPerSecond; + +import edu.wpi.first.units.measure.AngularVelocity; +import org.littletonrobotics.junction.AutoLog; +import org.strykeforce.telemetry.TelemetryService; + +public interface CoralIO { + + @AutoLog + public static class CoralIOInputs { + public AngularVelocity velocity = RotationsPerSecond.of(0.0); + public boolean isFwdLimitSwitchClosed = false; + public boolean isRevLimitSwitchClosed = false; + } + + public default void setVelocity(AngularVelocity velocity) {} + + public default void enableFwdLimitSwitch(boolean enabled) {} + + public default void enableRevLimitSwitch(boolean enabled) {} + + public default void updateInputs(CoralIOInputs inputs) {} + + public default void registerWith(TelemetryService telemetryService) {} +} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java new file mode 100644 index 00000000..2d9b318f --- /dev/null +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -0,0 +1,78 @@ +package frc.robot.subsystems.coral; + +import static edu.wpi.first.units.Units.DegreesPerSecond; + +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.controls.MotionMagicVelocityDutyCycle; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.ForwardLimitValue; +import com.ctre.phoenix6.signals.ReverseLimitValue; +import edu.wpi.first.units.measure.AngularVelocity; +import frc.robot.constants.CoralConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.strykeforce.telemetry.TelemetryService; + +public class CoralIOFX implements CoralIO { + // private objects + private Logger logger; + private TalonFX talonFx; + + // FX Access objects + TalonFXConfigurator configurator; + private MotionMagicVelocityDutyCycle velocityRequest = + new MotionMagicVelocityDutyCycle(0).withEnableFOC(false).withSlot(0); + StatusSignal currVelocity; + StatusSignal fwdLimitSwitch; + StatusSignal revLimitSwitch; + + public CoralIOFX() { + logger = LoggerFactory.getLogger(this.getClass()); + talonFx = new TalonFX(CoralConstants.kCoralFxId); + + // Config controller + configurator = talonFx.getConfigurator(); + configurator.apply(new TalonFXConfiguration()); // Factory default motor controller + configurator.apply(CoralConstants.getFXConfig()); + + // Attach status signals + currVelocity = talonFx.getVelocity(); + fwdLimitSwitch = talonFx.getForwardLimit(); + revLimitSwitch = talonFx.getReverseLimit(); + } + + @Override + public void setVelocity(AngularVelocity velocity) { + logger.info("Setting velocity to {} degrees per second", velocity.in(DegreesPerSecond)); + + talonFx.setControl(velocityRequest.withVelocity(velocity)); + } + + @Override + public void enableFwdLimitSwitch(boolean enabled) { + talonFx + .getConfigurator() + .apply(CoralConstants.getFXConfig().HardwareLimitSwitch.withForwardLimitEnable(enabled)); + } + + @Override + public void enableRevLimitSwitch(boolean enabled) { + talonFx + .getConfigurator() + .apply(CoralConstants.getFXConfig().HardwareLimitSwitch.withReverseLimitEnable(enabled)); + } + + @Override + public void updateInputs(CoralIOInputs inputs) { + inputs.velocity = currVelocity.refresh().getValue(); + inputs.isFwdLimitSwitchClosed = fwdLimitSwitch.refresh().getValue().value == 1; + inputs.isRevLimitSwitchClosed = revLimitSwitch.refresh().getValue().value == 0; + } + + @Override + public void registerWith(TelemetryService telemetryService) { + telemetryService.register(talonFx, true); + } +} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index f87d1e21..f20ae99c 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -1,3 +1,85 @@ package frc.robot.subsystems.coral; -public class CoralSubsystem {} +import static edu.wpi.first.units.Units.RotationsPerSecond; + +import edu.wpi.first.units.measure.AngularVelocity; +import frc.robot.constants.CoralConstants; +import frc.robot.standards.ClosedLoopSpeedSubsystem; +import java.util.Set; +import org.littletonrobotics.junction.Logger; +import org.strykeforce.telemetry.TelemetryService; +import org.strykeforce.telemetry.measurable.MeasurableSubsystem; +import org.strykeforce.telemetry.measurable.Measure; + +public class CoralSubsystem extends MeasurableSubsystem implements ClosedLoopSpeedSubsystem { + private final CoralIO io; + private final CoralIOInputsAutoLogged inputs = new CoralIOInputsAutoLogged(); + private AngularVelocity setpoint = RotationsPerSecond.of(0.0); + private CoralState curState = CoralState.INIT; + + public CoralSubsystem(CoralIO io) { + this.io = io; + } + + public CoralState getState() { + return curState; + } + + @Override + public AngularVelocity getSpeed() { + return inputs.velocity; + } + + @Override + public void setSpeed(AngularVelocity speed) { + setpoint = speed; + io.setVelocity(speed); + } + + @Override + public boolean atSpeed() { + return setpoint.minus(inputs.velocity).abs(RotationsPerSecond) + <= CoralConstants.kCloseEnough.in(RotationsPerSecond); + } + + public boolean isBeamBroken() { + return false; // FIXME when we get the robot + } + + // Periodic Function + @Override + public void periodic() { + // Read Inputs + io.updateInputs(inputs); + + // State Machine + switch (curState) { + case INIT: + break; + case ZEROED: + break; + default: + break; + } + + // Log Outputs + Logger.recordOutput("Coral/curState", curState.ordinal()); + Logger.recordOutput("Coral/setpoint", setpoint.in(RotationsPerSecond)); + } + + @Override + public void registerWith(TelemetryService telemetryService) { + super.registerWith(telemetryService); + io.registerWith(telemetryService); + } + + @Override + public Set getMeasures() { + return Set.of(new Measure("State", () -> curState.ordinal())); + } + + public enum CoralState { + INIT, + ZEROED + } +} From ce36c1d5188dd3e2bcd6fa5b2ea246504dc255b9 Mon Sep 17 00:00:00 2001 From: IRoot <74614800+imroot07@users.noreply.github.com> Date: Fri, 10 Jan 2025 21:03:56 -0500 Subject: [PATCH 02/13] fixed naming issue --- src/main/java/frc/robot/subsystems/coral/CoralIOFX.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java index 2d9b318f..ff5e0ccd 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -24,7 +24,7 @@ public class CoralIOFX implements CoralIO { TalonFXConfigurator configurator; private MotionMagicVelocityDutyCycle velocityRequest = new MotionMagicVelocityDutyCycle(0).withEnableFOC(false).withSlot(0); - StatusSignal currVelocity; + StatusSignal curVelocity; StatusSignal fwdLimitSwitch; StatusSignal revLimitSwitch; @@ -38,7 +38,7 @@ public CoralIOFX() { configurator.apply(CoralConstants.getFXConfig()); // Attach status signals - currVelocity = talonFx.getVelocity(); + curVelocity = talonFx.getVelocity(); fwdLimitSwitch = talonFx.getForwardLimit(); revLimitSwitch = talonFx.getReverseLimit(); } @@ -66,7 +66,7 @@ public void enableRevLimitSwitch(boolean enabled) { @Override public void updateInputs(CoralIOInputs inputs) { - inputs.velocity = currVelocity.refresh().getValue(); + inputs.velocity = curVelocity.refresh().getValue(); inputs.isFwdLimitSwitchClosed = fwdLimitSwitch.refresh().getValue().value == 1; inputs.isRevLimitSwitchClosed = revLimitSwitch.refresh().getValue().value == 0; } From f49b6e9a483a56d98cee71c9fab42b9fe3e5dd5a Mon Sep 17 00:00:00 2001 From: EpicPigGuy Date: Mon, 13 Jan 2025 18:45:06 -0500 Subject: [PATCH 03/13] idk just an update --- .../frc/robot/subsystems/algae/algaeIOFX.java | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java b/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java index 38550683..c2a2c3f8 100644 --- a/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java +++ b/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java @@ -1,3 +1,59 @@ package frc.robot.subsystems.algae; -public class algaeIOFX {} +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.strykeforce.telemetry.TelemetryService; + +import com.ctre.phoenix.motorcontrol.ControlMode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.controls.MotionMagicDutyCycle; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.ReverseLimitValue; + +import edu.wpi.first.units.measure.AngularVelocity; +import frc.robot.constants.AlgaeConstants; + +/** Implementation of the algaeIO interface using TalonFX motors. */ +public class algaeIOFX implements algaeIO { + private Logger logger; + private final TalonFX talonFX; + private StatusSignal currVelocity; + private StatusSignal reverseLimitSwitch; + + // FX Access objects + TalonFXConfigurator configurator; + private MotionMagicDutyCycle positionRequest = + new MotionMagicDutyCycle(0).withEnableFOC(false).withFeedForward(0).withSlot(0); + + // Constructor to initialize the TalonFX motor controller + public algaeIOFX() { + logger = LoggerFactory.getLogger(this.getClass()); + talonFX = new TalonFX(AlgaeConstants.kFxId); + reverseLimitSwitch = talonFX.getReverseLimit(); + currVelocity = talonFX.getVelocity(); + } + + @Override + public void updateInputs(IOInputs inputs) { + // Update the velocity input in the IOInputs structure + inputs.velocity = currVelocity.refresh().getValue(); + } + + @Override + public void setPosition(double position) { + // In case you want to use the position control in your system + talonFX.setPosition(position); + } + + @Override + public void zero() { + // This can reset the encoder or clear other state as needed + talonFX.clearStickyFaults(); + } + + @Override + public void registerWith(TelemetryService telemetryService) { + telemetryService.register(talonFX, true); + } +} From 813a15f7ebc588406a40fe3f55cdf84c45aae705 Mon Sep 17 00:00:00 2001 From: EpicPigGuy Date: Thu, 16 Jan 2025 18:16:57 -0500 Subject: [PATCH 04/13] Update algaeIO.java --- src/main/java/frc/robot/subsystems/algae/algaeIO.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/frc/robot/subsystems/algae/algaeIO.java b/src/main/java/frc/robot/subsystems/algae/algaeIO.java index a7712178..e691e19c 100644 --- a/src/main/java/frc/robot/subsystems/algae/algaeIO.java +++ b/src/main/java/frc/robot/subsystems/algae/algaeIO.java @@ -4,10 +4,13 @@ import org.littletonrobotics.junction.AutoLog; import org.strykeforce.telemetry.TelemetryService; +import com.ctre.phoenix6.signals.ReverseLimitValue; + public interface algaeIO { @AutoLog public static class IOInputs { public AngularVelocity velocity; + public ReverseLimitValue reverseLimitSwitch; } public default void updateInputs(IOInputs inputs) {} From 37d4467c9412500392e72d7adc57ea80a884714f Mon Sep 17 00:00:00 2001 From: EpicPigGuy Date: Fri, 17 Jan 2025 17:56:32 -0500 Subject: [PATCH 05/13] summary --- .../subsystems/algae/AlgaeSubsystem.java | 57 ++- .../frc/robot/subsystems/algae/algaeIO.java | 2 + .../frc/robot/subsystems/algae/algaeIOFX.java | 13 +- ...-2025.0.0.json => ChoreoLib-2025.0.1.json} | 8 +- vendordeps/DogLog.json | 20 + vendordeps/LumynLabs-2025.0.0.json | 71 +++ vendordeps/PathplannerLib-2025.2.1.json | 38 ++ vendordeps/Phoenix5-5.35.1.json | 171 +++++++ vendordeps/Phoenix6-25.2.0.json | 419 ++++++++++++++++++ vendordeps/PlayingWithFusion-2025.01.04.json | 68 +++ vendordeps/REVLib-2025.0.1.json | 71 +++ vendordeps/ReduxLib-2025.0.0.json | 72 +++ ...ca-2025.0.0.json => Studica-2025.0.1.json} | 14 +- vendordeps/ThriftyLib.json | 20 + vendordeps/URCL.json | 65 +++ vendordeps/maple-sim.json | 26 ++ vendordeps/yagsl-2025.2.1.json | 64 +++ 17 files changed, 1183 insertions(+), 16 deletions(-) rename vendordeps/{ChoreoLib-2025.0.0.json => ChoreoLib-2025.0.1.json} (88%) create mode 100644 vendordeps/DogLog.json create mode 100644 vendordeps/LumynLabs-2025.0.0.json create mode 100644 vendordeps/PathplannerLib-2025.2.1.json create mode 100644 vendordeps/Phoenix5-5.35.1.json create mode 100644 vendordeps/Phoenix6-25.2.0.json create mode 100644 vendordeps/PlayingWithFusion-2025.01.04.json create mode 100644 vendordeps/REVLib-2025.0.1.json create mode 100644 vendordeps/ReduxLib-2025.0.0.json rename vendordeps/{Studica-2025.0.0.json => Studica-2025.0.1.json} (89%) create mode 100644 vendordeps/ThriftyLib.json create mode 100644 vendordeps/URCL.json create mode 100644 vendordeps/maple-sim.json create mode 100644 vendordeps/yagsl-2025.2.1.json diff --git a/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java b/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java index 340af28a..bea51e02 100644 --- a/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java +++ b/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java @@ -1,3 +1,58 @@ package frc.robot.subsystems.algae; -public class AlgaeSubsystem {} +import static edu.wpi.first.units.Units.Rotations; + +import edu.wpi.first.units.measure.Angle; +import frc.robot.constants.AlgaeConstants; +import frc.robot.constants.ExampleConstants; +import frc.robot.standards.ClosedLoopPosSubsystem; +import frc.robot.subsystems.algae.algaeIO.IOInputs; + +import java.util.Set; +import org.littletonrobotics.junction.Logger; +import org.strykeforce.telemetry.TelemetryService; +import org.strykeforce.telemetry.measurable.MeasurableSubsystem; +import org.strykeforce.telemetry.measurable.Measure; + +public class AlgaeSubsystem implements ClosedLoopPosSubsystem { + private final algaeIO io; + private final IOInputsAutoLogged inputs = new IOInputsAutoLogged(); + private Angle setpoint = Rotations.of(0.0); + + public AlgaeSubsystem(algaeIO io) { + this.io = io; + } + + public Angle getPosition() { + return getPosition(); + } + + public void setPosition(Angle position) { + setPosition(position); + setpoint = position; + } + + public boolean isFinished() { + return setpoint.minus(getPosition()).abs(Rotations) <= AlgaeConstants.kCloseEnough.in(Rotations); + } + + // Periodic Function + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(getName(), inputs); + Logger.recordOutput("Algae/setpoint", setpoint.in(Rotations)); + } + + // Grapher + @Override + public void registerWith(TelemetryService telemetryService) { + + super.registerWith(telemetryService); + io.registerWith(telemetryService); + } + + @Override + public Set getMeasures() { + return Set.of(new Measure("State", () -> curState.ordinal())); + } +} \ No newline at end of file diff --git a/src/main/java/frc/robot/subsystems/algae/algaeIO.java b/src/main/java/frc/robot/subsystems/algae/algaeIO.java index e691e19c..52f04533 100644 --- a/src/main/java/frc/robot/subsystems/algae/algaeIO.java +++ b/src/main/java/frc/robot/subsystems/algae/algaeIO.java @@ -1,5 +1,6 @@ package frc.robot.subsystems.algae; +import edu.wpi.first.units.measure.Angle; import edu.wpi.first.units.measure.AngularVelocity; import org.littletonrobotics.junction.AutoLog; import org.strykeforce.telemetry.TelemetryService; @@ -10,6 +11,7 @@ public interface algaeIO { @AutoLog public static class IOInputs { public AngularVelocity velocity; + public Angle location; public ReverseLimitValue reverseLimitSwitch; } diff --git a/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java b/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java index c2a2c3f8..58142983 100644 --- a/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java +++ b/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java @@ -11,6 +11,7 @@ import com.ctre.phoenix6.hardware.TalonFX; import com.ctre.phoenix6.signals.ReverseLimitValue; +import edu.wpi.first.units.measure.Angle; import edu.wpi.first.units.measure.AngularVelocity; import frc.robot.constants.AlgaeConstants; @@ -20,6 +21,7 @@ public class algaeIOFX implements algaeIO { private final TalonFX talonFX; private StatusSignal currVelocity; private StatusSignal reverseLimitSwitch; + private StatusSignal currAngle; // FX Access objects TalonFXConfigurator configurator; @@ -32,24 +34,27 @@ public algaeIOFX() { talonFX = new TalonFX(AlgaeConstants.kFxId); reverseLimitSwitch = talonFX.getReverseLimit(); currVelocity = talonFX.getVelocity(); + currAngle = talonFX.getPosition(); } @Override public void updateInputs(IOInputs inputs) { - // Update the velocity input in the IOInputs structure + // inputs.reverseLimitSwitch = reverseLimitSwitch.refresh().getValue().value == 1; inputs.velocity = currVelocity.refresh().getValue(); + inputs.location = currAngle.refresh().getValue(); } @Override public void setPosition(double position) { - // In case you want to use the position control in your system talonFX.setPosition(position); } + public Angle getPosition() { + return currAngle.getValue(); + } @Override public void zero() { - // This can reset the encoder or clear other state as needed - talonFX.clearStickyFaults(); + // not doing one for now } @Override diff --git a/vendordeps/ChoreoLib-2025.0.0.json b/vendordeps/ChoreoLib-2025.0.1.json similarity index 88% rename from vendordeps/ChoreoLib-2025.0.0.json rename to vendordeps/ChoreoLib-2025.0.1.json index e96f4f9a..705ad168 100644 --- a/vendordeps/ChoreoLib-2025.0.0.json +++ b/vendordeps/ChoreoLib-2025.0.1.json @@ -1,7 +1,7 @@ { - "fileName": "ChoreoLib-2025.0.0.json", + "fileName": "ChoreoLib-2025.0.1.json", "name": "ChoreoLib", - "version": "2025.0.0", + "version": "2025.0.1", "uuid": "b5e23f0a-dac9-4ad2-8dd6-02767c520aca", "frcYear": "2025", "mavenUrls": [ @@ -13,7 +13,7 @@ { "groupId": "choreo", "artifactId": "ChoreoLib-java", - "version": "2025.0.0" + "version": "2025.0.1" }, { "groupId": "com.google.code.gson", @@ -26,7 +26,7 @@ { "groupId": "choreo", "artifactId": "ChoreoLib-cpp", - "version": "2025.0.0", + "version": "2025.0.1", "libName": "ChoreoLib", "headerClassifier": "headers", "sharedLibrary": false, diff --git a/vendordeps/DogLog.json b/vendordeps/DogLog.json new file mode 100644 index 00000000..b410eb1d --- /dev/null +++ b/vendordeps/DogLog.json @@ -0,0 +1,20 @@ +{ + "javaDependencies": [ + { + "groupId": "com.github.jonahsnider", + "artifactId": "doglog", + "version": "2025.3.0" + } + ], + "fileName": "DogLog.json", + "frcYear": "2025", + "jsonUrl": "https://doglog.dev/vendordep.json", + "name": "DogLog", + "jniDependencies": [], + "mavenUrls": [ + "https://jitpack.io" + ], + "cppDependencies": [], + "version": "2025.3.0", + "uuid": "65592ce1-2251-4a31-8e4b-2df20dacebe4" +} \ No newline at end of file diff --git a/vendordeps/LumynLabs-2025.0.0.json b/vendordeps/LumynLabs-2025.0.0.json new file mode 100644 index 00000000..650c5e69 --- /dev/null +++ b/vendordeps/LumynLabs-2025.0.0.json @@ -0,0 +1,71 @@ +{ + "fileName": "LumynLabs-2025.0.0.json", + "name": "LumynLabs", + "version": "2025.0.0", + "frcYear": "2025", + "uuid": "eebd34fc-a6d3-48a2-a286-ac1cec59ab89", + "mavenUrls": [ + "https://packages.lumynlabs.com/repo" + ], + "jsonUrl": "https://packages.lumynlabs.com/LumynLabs.json", + "javaDependencies": [ + { + "groupId": "com.lumynlabs.frc", + "artifactId": "LumynLabs-java", + "version": "2025.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.lumynlabs.frc", + "artifactId": "LumynLabs-driver", + "version": "2025.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.lumynlabs.frc", + "artifactId": "LumynLabs-cpp", + "version": "2025.0.0", + "libName": "Lumyn", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.lumynlabs.frc", + "artifactId": "LumynLabs-driver", + "version": "2025.0.0", + "libName": "LumynDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/PathplannerLib-2025.2.1.json b/vendordeps/PathplannerLib-2025.2.1.json new file mode 100644 index 00000000..71e25f3d --- /dev/null +++ b/vendordeps/PathplannerLib-2025.2.1.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib-2025.2.1.json", + "name": "PathplannerLib", + "version": "2025.2.1", + "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", + "frcYear": "2025", + "mavenUrls": [ + "https://3015rangerrobotics.github.io/pathplannerlib/repo" + ], + "jsonUrl": "https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json", + "javaDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-java", + "version": "2025.2.1" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2025.2.1", + "libName": "PathplannerLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal", + "linuxathena", + "linuxarm32", + "linuxarm64" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/Phoenix5-5.35.1.json b/vendordeps/Phoenix5-5.35.1.json new file mode 100644 index 00000000..69df8b53 --- /dev/null +++ b/vendordeps/Phoenix5-5.35.1.json @@ -0,0 +1,171 @@ +{ + "fileName": "Phoenix5-5.35.1.json", + "name": "CTRE-Phoenix (v5)", + "version": "5.35.1", + "frcYear": "2025", + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-frc2025-latest.json", + "requires": [ + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Phoenix 5 requires low-level libraries from Phoenix 6. Please add the Phoenix 6 vendordep before adding Phoenix 5.", + "offlineFileName": "Phoenix6-frc2025-latest.json", + "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json" + } + ], + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users must use the Phoenix 5 replay vendordep when using the Phoenix 6 replay vendordep.", + "offlineFileName": "Phoenix6-replay-frc2025-latest.json" + }, + { + "uuid": "fbc886a4-2cec-40c0-9835-71086a8cc3df", + "errorMessage": "Users cannot have both the replay and regular Phoenix 5 vendordeps in their robot program.", + "offlineFileName": "Phoenix5-replay-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-java", + "version": "5.35.1" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-java", + "version": "5.35.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-cpp", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-cpp", + "version": "5.35.1", + "libName": "CTRE_Phoenix", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "api-cpp-sim", + "version": "5.35.1", + "libName": "CTRE_PhoenixSim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/vendordeps/Phoenix6-25.2.0.json b/vendordeps/Phoenix6-25.2.0.json new file mode 100644 index 00000000..38747fb6 --- /dev/null +++ b/vendordeps/Phoenix6-25.2.0.json @@ -0,0 +1,419 @@ +{ + "fileName": "Phoenix6-25.2.0.json", + "name": "CTRE-Phoenix (v6)", + "version": "25.2.0", + "frcYear": "2025", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "25.2.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "25.2.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.2.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "25.2.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.2.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.2.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.2.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.2.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.2.0", + "libName": "CTRE_SimCANCoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.2.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.2.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "25.2.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "25.2.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.2.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/vendordeps/PlayingWithFusion-2025.01.04.json b/vendordeps/PlayingWithFusion-2025.01.04.json new file mode 100644 index 00000000..580c1217 --- /dev/null +++ b/vendordeps/PlayingWithFusion-2025.01.04.json @@ -0,0 +1,68 @@ +{ + "fileName": "PlayingWithFusion-2025.01.04.json", + "name": "PlayingWithFusion", + "version": "2025.01.04", + "uuid": "14b8ad04-24df-11ea-978f-2e728ce88125", + "frcYear": "2025", + "jsonUrl": "https://www.playingwithfusion.com/frc/playingwithfusion2025.json", + "mavenUrls": [ + "https://www.playingwithfusion.com/frc/maven/" + ], + "javaDependencies": [ + { + "groupId": "com.playingwithfusion.frc", + "artifactId": "PlayingWithFusion-java", + "version": "2025.01.04" + } + ], + "jniDependencies": [ + { + "groupId": "com.playingwithfusion.frc", + "artifactId": "PlayingWithFusion-driver", + "version": "2025.01.04", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.playingwithfusion.frc", + "artifactId": "PlayingWithFusion-cpp", + "version": "2025.01.04", + "headerClassifier": "headers", + "sharedLibrary": false, + "libName": "PlayingWithFusion", + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "osxuniversal" + ] + }, + { + "groupId": "com.playingwithfusion.frc", + "artifactId": "PlayingWithFusion-driver", + "version": "2025.01.04", + "headerClassifier": "headers", + "sharedLibrary": true, + "libName": "PlayingWithFusionDriver", + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/REVLib-2025.0.1.json b/vendordeps/REVLib-2025.0.1.json new file mode 100644 index 00000000..c9980543 --- /dev/null +++ b/vendordeps/REVLib-2025.0.1.json @@ -0,0 +1,71 @@ +{ + "fileName": "REVLib-2025.0.1.json", + "name": "REVLib", + "version": "2025.0.1", + "frcYear": "2025", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2025.0.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2025.0.1", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.1", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/ReduxLib-2025.0.0.json b/vendordeps/ReduxLib-2025.0.0.json new file mode 100644 index 00000000..f15ff901 --- /dev/null +++ b/vendordeps/ReduxLib-2025.0.0.json @@ -0,0 +1,72 @@ +{ + "fileName": "ReduxLib-2025.0.0.json", + "name": "ReduxLib", + "version": "2025.0.0", + "frcYear": "2025", + "uuid": "151ecca8-670b-4026-8160-cdd2679ef2bd", + "mavenUrls": [ + "https://maven.reduxrobotics.com/" + ], + "jsonUrl": "https://frcsdk.reduxrobotics.com/ReduxLib_2025.json", + "javaDependencies": [ + { + "groupId": "com.reduxrobotics.frc", + "artifactId": "ReduxLib-java", + "version": "2025.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.reduxrobotics.frc", + "artifactId": "ReduxLib-driver", + "version": "2025.0.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena", + "linuxx86-64", + "linuxarm32", + "linuxarm64", + "osxuniversal", + "windowsx86-64" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.reduxrobotics.frc", + "artifactId": "ReduxLib-cpp", + "version": "2025.0.0", + "libName": "ReduxLib", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxx86-64", + "linuxarm32", + "linuxarm64", + "osxuniversal", + "windowsx86-64" + ] + }, + { + "groupId": "com.reduxrobotics.frc", + "artifactId": "ReduxLib-driver", + "version": "2025.0.0", + "libName": "ReduxCore", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxx86-64", + "linuxarm32", + "linuxarm64", + "osxuniversal", + "windowsx86-64" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/Studica-2025.0.0.json b/vendordeps/Studica-2025.0.1.json similarity index 89% rename from vendordeps/Studica-2025.0.0.json rename to vendordeps/Studica-2025.0.1.json index ddb0e44b..5010be04 100644 --- a/vendordeps/Studica-2025.0.0.json +++ b/vendordeps/Studica-2025.0.1.json @@ -1,13 +1,13 @@ { - "fileName": "Studica-2025.0.0.json", + "fileName": "Studica-2025.0.1.json", "name": "Studica", - "version": "2025.0.0", + "version": "2025.0.1", "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", "frcYear": "2025", "mavenUrls": [ "https://dev.studica.com/maven/release/2025/" ], - "jsonUrl": "https://dev.studica.com/releases/2025/Studica-2025.0.0.json", + "jsonUrl": "https://dev.studica.com/releases/2025/Studica-2025.0.1.json", "cppDependencies": [ { "artifactId": "Studica-cpp", @@ -24,7 +24,7 @@ "libName": "Studica", "sharedLibrary": false, "skipInvalidPlatforms": true, - "version": "2025.0.0" + "version": "2025.0.1" }, { "artifactId": "Studica-driver", @@ -41,14 +41,14 @@ "libName": "StudicaDriver", "sharedLibrary": false, "skipInvalidPlatforms": true, - "version": "2025.0.0" + "version": "2025.0.1" } ], "javaDependencies": [ { "artifactId": "Studica-java", "groupId": "com.studica.frc", - "version": "2025.0.0" + "version": "2025.0.1" } ], "jniDependencies": [ @@ -65,7 +65,7 @@ "osxuniversal", "windowsx86-64" ], - "version": "2025.0.0" + "version": "2025.0.1" } ] } \ No newline at end of file diff --git a/vendordeps/ThriftyLib.json b/vendordeps/ThriftyLib.json new file mode 100644 index 00000000..02445068 --- /dev/null +++ b/vendordeps/ThriftyLib.json @@ -0,0 +1,20 @@ +{ + "fileName": "ThriftyLib.json", + "name": "ThriftyLib", + "version": "2025.0.1", + "frcYear": "2025", + "uuid": "60b2694b-9e6e-4026-81ee-6f167946f4b0", + "mavenUrls": [ + "https://docs.home.thethriftybot.com" + ], + "jsonUrl": "https://docs.home.thethriftybot.com/ThriftyLib.json", + "javaDependencies": [ + { + "groupId": "com.thethriftybot.frc", + "artifactId": "ThriftyLib-java", + "version": "2025.0.1" + } + ], + "jniDependencies": [], + "cppDependencies": [] +} \ No newline at end of file diff --git a/vendordeps/URCL.json b/vendordeps/URCL.json new file mode 100644 index 00000000..55e7841a --- /dev/null +++ b/vendordeps/URCL.json @@ -0,0 +1,65 @@ +{ + "fileName": "URCL.json", + "name": "URCL", + "version": "2025.0.0", + "frcYear": "2025", + "uuid": "84246d17-a797-4d1e-bd9f-c59cd8d2477c", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" + ], + "jsonUrl": "https://raw.githubusercontent.com/Mechanical-Advantage/URCL/main/URCL.json", + "javaDependencies": [ + { + "groupId": "org.littletonrobotics.urcl", + "artifactId": "URCL-java", + "version": "2025.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "org.littletonrobotics.urcl", + "artifactId": "URCL-driver", + "version": "2025.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "org.littletonrobotics.urcl", + "artifactId": "URCL-cpp", + "version": "2025.0.0", + "libName": "URCL", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena", + "osxuniversal" + ] + }, + { + "groupId": "org.littletonrobotics.urcl", + "artifactId": "URCL-driver", + "version": "2025.0.0", + "libName": "URCLDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/maple-sim.json b/vendordeps/maple-sim.json new file mode 100644 index 00000000..5369ef8b --- /dev/null +++ b/vendordeps/maple-sim.json @@ -0,0 +1,26 @@ +{ + "fileName": "maple-sim.json", + "name": "maplesim", + "version": "0.3.1", + "frcYear": "2025", + "uuid": "c39481e8-4a63-4a4c-9df6-48d91e4da37b", + "mavenUrls": [ + "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/repos/releases", + "https://repo1.maven.org/maven2" + ], + "jsonUrl": "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/maple-sim.json", + "javaDependencies": [ + { + "groupId": "org.ironmaple", + "artifactId": "maplesim-java", + "version": "0.3.1" + }, + { + "groupId": "org.dyn4j", + "artifactId": "dyn4j", + "version": "5.0.2" + } + ], + "jniDependencies": [], + "cppDependencies": [] +} \ No newline at end of file diff --git a/vendordeps/yagsl-2025.2.1.json b/vendordeps/yagsl-2025.2.1.json new file mode 100644 index 00000000..cb0a7c16 --- /dev/null +++ b/vendordeps/yagsl-2025.2.1.json @@ -0,0 +1,64 @@ +{ + "fileName": "yagsl-2025.2.1.json", + "name": "YAGSL", + "version": "2025.2.1", + "frcYear": "2025", + "uuid": "1ccce5a4-acd2-4d18-bca3-4b8047188400", + "mavenUrls": [ + "https://broncbotz3481.github.io/YAGSL-Lib/yagsl/repos" + ], + "jsonUrl": "https://broncbotz3481.github.io/YAGSL-Lib/yagsl/yagsl.json", + "javaDependencies": [ + { + "groupId": "swervelib", + "artifactId": "YAGSL-java", + "version": "2025.2.1" + } + ], + "requires": [ + { + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "errorMessage": "REVLib is required!", + "offlineFileName": "REVLib-2025.0.0.json", + "onlineUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json" + }, + { + "uuid": "151ecca8-670b-4026-8160-cdd2679ef2bd", + "errorMessage": "ReduxLib is required!", + "offlineFileName": "ReduxLib-2025.0.0.json", + "onlineUrl": "https://frcsdk.reduxrobotics.com/ReduxLib_2025.json" + }, + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Phoenix6 is required!", + "offlineFileName": "Phoenix6-25.1.0.json", + "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json" + }, + { + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "errorMessage": "Phoenix5 is required!", + "offlineFileName": "Phoenix5-5.35.0.json", + "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-frc2025-latest.json" + }, + { + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "errorMessage": "Studica is required!", + "offlineFileName": "Studica-2025.0.0.json", + "onlineUrl": "https://dev.studica.com/releases/2025/Studica-2025.0.0.json" + }, + { + "uuid": "60b2694b-9e6e-4026-81ee-6f167946f4b0", + "errorMessage": "ThriftyLib is required!", + "offlineFileName": "ThriftyLib.json", + "onlineUrl": "https://docs.home.thethriftybot.com/ThriftyLib.json" + }, + { + "uuid": "c39481e8-4a63-4a4c-9df6-48d91e4da37b", + "errorMessage": "maple-sim is required for simulation", + "offlineFileName": "maple-sim.json", + "onlineUrl": "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/maple-sim.json" + } + ], + "jniDependencies": [], + "cppDependencies": [] +} \ No newline at end of file From 677a4c33bc3ef4b5943eb52e91c46b8dc87f275b Mon Sep 17 00:00:00 2001 From: David Shen Date: Thu, 30 Jan 2025 20:02:36 -0500 Subject: [PATCH 06/13] fxs --- build.gradle | 2 +- .../frc/robot/constants/CoralConstants.java | 22 +- .../frc/robot/subsystems/coral/CoralIOFX.java | 35 +- .../subsystems/coral/CoralSubsystem.java | 2 +- vendordeps/DogLog.json | 20 - vendordeps/LumynLabs-2025.0.0.json | 71 --- vendordeps/PathplannerLib-2025.2.1.json | 38 -- vendordeps/Phoenix5-5.35.1.json | 171 ------- ....35.0.json => Phoenix5-replay-5.35.1.json} | 32 +- vendordeps/Phoenix6-25.2.0.json | 419 ------------------ ...5.1.0.json => Phoenix6-replay-25.2.1.json} | 92 ++-- vendordeps/PlayingWithFusion-2025.01.04.json | 68 --- vendordeps/REVLib-2025.0.1.json | 71 --- vendordeps/ReduxLib-2025.0.0.json | 72 --- vendordeps/ThriftyLib.json | 20 - vendordeps/URCL.json | 65 --- vendordeps/maple-sim.json | 26 -- vendordeps/yagsl-2025.2.1.json | 64 --- 18 files changed, 109 insertions(+), 1181 deletions(-) delete mode 100644 vendordeps/DogLog.json delete mode 100644 vendordeps/LumynLabs-2025.0.0.json delete mode 100644 vendordeps/PathplannerLib-2025.2.1.json delete mode 100644 vendordeps/Phoenix5-5.35.1.json rename vendordeps/{Phoenix5-replay-5.35.0.json => Phoenix5-replay-5.35.1.json} (92%) delete mode 100644 vendordeps/Phoenix6-25.2.0.json rename vendordeps/{Phoenix6-replay-25.1.0.json => Phoenix6-replay-25.2.1.json} (86%) delete mode 100644 vendordeps/PlayingWithFusion-2025.01.04.json delete mode 100644 vendordeps/REVLib-2025.0.1.json delete mode 100644 vendordeps/ReduxLib-2025.0.0.json delete mode 100644 vendordeps/ThriftyLib.json delete mode 100644 vendordeps/URCL.json delete mode 100644 vendordeps/maple-sim.json delete mode 100644 vendordeps/yagsl-2025.2.1.json diff --git a/build.gradle b/build.gradle index 53a5548d..a64c9abf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2025.1.1" + id "edu.wpi.first.GradleRIO" version "2025.2.1" id "com.diffplug.spotless" version "6.12.1" id "com.peterabeles.gversion" version "1.10" } diff --git a/src/main/java/frc/robot/constants/CoralConstants.java b/src/main/java/frc/robot/constants/CoralConstants.java index 5fe9aeac..8ca72712 100644 --- a/src/main/java/frc/robot/constants/CoralConstants.java +++ b/src/main/java/frc/robot/constants/CoralConstants.java @@ -1,13 +1,15 @@ package frc.robot.constants; import static edu.wpi.first.units.Units.DegreesPerSecond; +import static edu.wpi.first.units.Units.Rotations; +import static edu.wpi.first.units.Units.RotationsPerSecond; import com.ctre.phoenix6.configs.CurrentLimitsConfigs; import com.ctre.phoenix6.configs.HardwareLimitSwitchConfigs; import com.ctre.phoenix6.configs.MotionMagicConfigs; import com.ctre.phoenix6.configs.MotorOutputConfigs; import com.ctre.phoenix6.configs.Slot0Configs; -import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXSConfiguration; import com.ctre.phoenix6.signals.ForwardLimitSourceValue; import com.ctre.phoenix6.signals.ForwardLimitTypeValue; import com.ctre.phoenix6.signals.GravityTypeValue; @@ -19,11 +21,11 @@ public class CoralConstants { public static int kCoralFxId = 0; - public static final AngularVelocity kCloseEnough = DegreesPerSecond.of(5); + public static final AngularVelocity kCloseEnough = RotationsPerSecond.of(0.1); // Coral Talon FX Config - public static TalonFXConfiguration getFXConfig() { - TalonFXConfiguration fxConfig = new TalonFXConfiguration(); + public static TalonFXSConfiguration getFXConfig() { + TalonFXSConfiguration fxsConfig = new TalonFXSConfiguration(); CurrentLimitsConfigs current = new CurrentLimitsConfigs() @@ -34,7 +36,7 @@ public static TalonFXConfiguration getFXConfig() { .withSupplyCurrentLowerLimit(8) .withSupplyCurrentLowerTime(0.02) .withSupplyCurrentLimitEnable(true); - fxConfig.CurrentLimits = current; + fxsConfig.CurrentLimits = current; HardwareLimitSwitchConfigs hwLimit = new HardwareLimitSwitchConfigs() @@ -46,7 +48,7 @@ public static TalonFXConfiguration getFXConfig() { .withReverseLimitEnable(false) .withReverseLimitType(ReverseLimitTypeValue.NormallyOpen) .withReverseLimitSource(ReverseLimitSourceValue.LimitSwitchPin); - fxConfig.HardwareLimitSwitch = hwLimit; + fxsConfig.HardwareLimitSwitch = hwLimit; Slot0Configs slot0 = new Slot0Configs() @@ -58,7 +60,7 @@ public static TalonFXConfiguration getFXConfig() { .withKS(0) .withKV(0) .withKA(0); - fxConfig.Slot0 = slot0; + fxsConfig.Slot0 = slot0; MotionMagicConfigs motionMagic = new MotionMagicConfigs() @@ -67,14 +69,14 @@ public static TalonFXConfiguration getFXConfig() { .withMotionMagicExpo_kA(0) .withMotionMagicExpo_kV(0) .withMotionMagicJerk(0); - fxConfig.MotionMagic = motionMagic; + fxsConfig.MotionMagic = motionMagic; MotorOutputConfigs motorOut = new MotorOutputConfigs() .withDutyCycleNeutralDeadband(0.01) .withNeutralMode(NeutralModeValue.Coast); - fxConfig.MotorOutput = motorOut; + fxsConfig.MotorOutput = motorOut; - return fxConfig; + return fxsConfig; } } diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java index ff5e0ccd..8d8b2e71 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -2,11 +2,12 @@ import static edu.wpi.first.units.Units.DegreesPerSecond; +import com.ctre.phoenix6.BaseStatusSignal; import com.ctre.phoenix6.StatusSignal; -import com.ctre.phoenix6.configs.TalonFXConfiguration; -import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.configs.TalonFXSConfiguration; +import com.ctre.phoenix6.configs.TalonFXSConfigurator; import com.ctre.phoenix6.controls.MotionMagicVelocityDutyCycle; -import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.hardware.TalonFXS; import com.ctre.phoenix6.signals.ForwardLimitValue; import com.ctre.phoenix6.signals.ReverseLimitValue; import edu.wpi.first.units.measure.AngularVelocity; @@ -16,12 +17,11 @@ import org.strykeforce.telemetry.TelemetryService; public class CoralIOFX implements CoralIO { - // private objects private Logger logger; - private TalonFX talonFx; + private TalonFXS talonFxs; // FX Access objects - TalonFXConfigurator configurator; + TalonFXSConfigurator configurator; private MotionMagicVelocityDutyCycle velocityRequest = new MotionMagicVelocityDutyCycle(0).withEnableFOC(false).withSlot(0); StatusSignal curVelocity; @@ -30,49 +30,50 @@ public class CoralIOFX implements CoralIO { public CoralIOFX() { logger = LoggerFactory.getLogger(this.getClass()); - talonFx = new TalonFX(CoralConstants.kCoralFxId); + talonFxs = new TalonFXS(CoralConstants.kCoralFxId); // Config controller - configurator = talonFx.getConfigurator(); - configurator.apply(new TalonFXConfiguration()); // Factory default motor controller + configurator = talonFxs.getConfigurator(); + configurator.apply(new TalonFXSConfiguration()); // Factory default motor controller configurator.apply(CoralConstants.getFXConfig()); // Attach status signals - curVelocity = talonFx.getVelocity(); - fwdLimitSwitch = talonFx.getForwardLimit(); - revLimitSwitch = talonFx.getReverseLimit(); + curVelocity = talonFxs.getVelocity(); + fwdLimitSwitch = talonFxs.getForwardLimit(); + revLimitSwitch = talonFxs.getReverseLimit(); } @Override public void setVelocity(AngularVelocity velocity) { logger.info("Setting velocity to {} degrees per second", velocity.in(DegreesPerSecond)); - talonFx.setControl(velocityRequest.withVelocity(velocity)); + talonFxs.setControl(velocityRequest.withVelocity(velocity)); } @Override public void enableFwdLimitSwitch(boolean enabled) { - talonFx + talonFxs .getConfigurator() .apply(CoralConstants.getFXConfig().HardwareLimitSwitch.withForwardLimitEnable(enabled)); } @Override public void enableRevLimitSwitch(boolean enabled) { - talonFx + talonFxs .getConfigurator() .apply(CoralConstants.getFXConfig().HardwareLimitSwitch.withReverseLimitEnable(enabled)); } @Override public void updateInputs(CoralIOInputs inputs) { - inputs.velocity = curVelocity.refresh().getValue(); + BaseStatusSignal.refreshAll(curVelocity, fwdLimitSwitch, revLimitSwitch); + inputs.velocity = curVelocity.getValue(); inputs.isFwdLimitSwitchClosed = fwdLimitSwitch.refresh().getValue().value == 1; inputs.isRevLimitSwitchClosed = revLimitSwitch.refresh().getValue().value == 0; } @Override public void registerWith(TelemetryService telemetryService) { - telemetryService.register(talonFx, true); + telemetryService.register(talonFxs, true); } } diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index f20ae99c..1f4c1e59 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -51,7 +51,7 @@ public boolean isBeamBroken() { public void periodic() { // Read Inputs io.updateInputs(inputs); - + // State Machine switch (curState) { case INIT: diff --git a/vendordeps/DogLog.json b/vendordeps/DogLog.json deleted file mode 100644 index b410eb1d..00000000 --- a/vendordeps/DogLog.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "javaDependencies": [ - { - "groupId": "com.github.jonahsnider", - "artifactId": "doglog", - "version": "2025.3.0" - } - ], - "fileName": "DogLog.json", - "frcYear": "2025", - "jsonUrl": "https://doglog.dev/vendordep.json", - "name": "DogLog", - "jniDependencies": [], - "mavenUrls": [ - "https://jitpack.io" - ], - "cppDependencies": [], - "version": "2025.3.0", - "uuid": "65592ce1-2251-4a31-8e4b-2df20dacebe4" -} \ No newline at end of file diff --git a/vendordeps/LumynLabs-2025.0.0.json b/vendordeps/LumynLabs-2025.0.0.json deleted file mode 100644 index 650c5e69..00000000 --- a/vendordeps/LumynLabs-2025.0.0.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "fileName": "LumynLabs-2025.0.0.json", - "name": "LumynLabs", - "version": "2025.0.0", - "frcYear": "2025", - "uuid": "eebd34fc-a6d3-48a2-a286-ac1cec59ab89", - "mavenUrls": [ - "https://packages.lumynlabs.com/repo" - ], - "jsonUrl": "https://packages.lumynlabs.com/LumynLabs.json", - "javaDependencies": [ - { - "groupId": "com.lumynlabs.frc", - "artifactId": "LumynLabs-java", - "version": "2025.0.0" - } - ], - "jniDependencies": [ - { - "groupId": "com.lumynlabs.frc", - "artifactId": "LumynLabs-driver", - "version": "2025.0.0", - "skipInvalidPlatforms": true, - "isJar": false, - "validPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - } - ], - "cppDependencies": [ - { - "groupId": "com.lumynlabs.frc", - "artifactId": "LumynLabs-cpp", - "version": "2025.0.0", - "libName": "Lumyn", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - }, - { - "groupId": "com.lumynlabs.frc", - "artifactId": "LumynLabs-driver", - "version": "2025.0.0", - "libName": "LumynDriver", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/PathplannerLib-2025.2.1.json b/vendordeps/PathplannerLib-2025.2.1.json deleted file mode 100644 index 71e25f3d..00000000 --- a/vendordeps/PathplannerLib-2025.2.1.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "fileName": "PathplannerLib-2025.2.1.json", - "name": "PathplannerLib", - "version": "2025.2.1", - "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", - "frcYear": "2025", - "mavenUrls": [ - "https://3015rangerrobotics.github.io/pathplannerlib/repo" - ], - "jsonUrl": "https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json", - "javaDependencies": [ - { - "groupId": "com.pathplanner.lib", - "artifactId": "PathplannerLib-java", - "version": "2025.2.1" - } - ], - "jniDependencies": [], - "cppDependencies": [ - { - "groupId": "com.pathplanner.lib", - "artifactId": "PathplannerLib-cpp", - "version": "2025.2.1", - "libName": "PathplannerLib", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "osxuniversal", - "linuxathena", - "linuxarm32", - "linuxarm64" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/Phoenix5-5.35.1.json b/vendordeps/Phoenix5-5.35.1.json deleted file mode 100644 index 69df8b53..00000000 --- a/vendordeps/Phoenix5-5.35.1.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "fileName": "Phoenix5-5.35.1.json", - "name": "CTRE-Phoenix (v5)", - "version": "5.35.1", - "frcYear": "2025", - "uuid": "ab676553-b602-441f-a38d-f1296eff6537", - "mavenUrls": [ - "https://maven.ctr-electronics.com/release/" - ], - "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-frc2025-latest.json", - "requires": [ - { - "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", - "errorMessage": "Phoenix 5 requires low-level libraries from Phoenix 6. Please add the Phoenix 6 vendordep before adding Phoenix 5.", - "offlineFileName": "Phoenix6-frc2025-latest.json", - "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json" - } - ], - "conflictsWith": [ - { - "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", - "errorMessage": "Users must use the Phoenix 5 replay vendordep when using the Phoenix 6 replay vendordep.", - "offlineFileName": "Phoenix6-replay-frc2025-latest.json" - }, - { - "uuid": "fbc886a4-2cec-40c0-9835-71086a8cc3df", - "errorMessage": "Users cannot have both the replay and regular Phoenix 5 vendordeps in their robot program.", - "offlineFileName": "Phoenix5-replay-frc2025-latest.json" - } - ], - "javaDependencies": [ - { - "groupId": "com.ctre.phoenix", - "artifactId": "api-java", - "version": "5.35.1" - }, - { - "groupId": "com.ctre.phoenix", - "artifactId": "wpiapi-java", - "version": "5.35.1" - } - ], - "jniDependencies": [ - { - "groupId": "com.ctre.phoenix", - "artifactId": "cci", - "version": "5.35.1", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix.sim", - "artifactId": "cci-sim", - "version": "5.35.1", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - } - ], - "cppDependencies": [ - { - "groupId": "com.ctre.phoenix", - "artifactId": "wpiapi-cpp", - "version": "5.35.1", - "libName": "CTRE_Phoenix_WPI", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix", - "artifactId": "api-cpp", - "version": "5.35.1", - "libName": "CTRE_Phoenix", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix", - "artifactId": "cci", - "version": "5.35.1", - "libName": "CTRE_PhoenixCCI", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix.sim", - "artifactId": "wpiapi-cpp-sim", - "version": "5.35.1", - "libName": "CTRE_Phoenix_WPISim", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix.sim", - "artifactId": "api-cpp-sim", - "version": "5.35.1", - "libName": "CTRE_PhoenixSim", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix.sim", - "artifactId": "cci-sim", - "version": "5.35.1", - "libName": "CTRE_PhoenixCCISim", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - } - ] -} \ No newline at end of file diff --git a/vendordeps/Phoenix5-replay-5.35.0.json b/vendordeps/Phoenix5-replay-5.35.1.json similarity index 92% rename from vendordeps/Phoenix5-replay-5.35.0.json rename to vendordeps/Phoenix5-replay-5.35.1.json index 89afeef4..bdf2f445 100644 --- a/vendordeps/Phoenix5-replay-5.35.0.json +++ b/vendordeps/Phoenix5-replay-5.35.1.json @@ -1,7 +1,7 @@ { - "fileName": "Phoenix5-replay-5.35.0.json", + "fileName": "Phoenix5-replay-5.35.1.json", "name": "CTRE-Phoenix (v5)", - "version": "5.35.0", + "version": "5.35.1", "frcYear": "2025", "uuid": "fbc886a4-2cec-40c0-9835-71086a8cc3df", "mavenUrls": [ @@ -32,19 +32,19 @@ { "groupId": "com.ctre.phoenix", "artifactId": "api-java", - "version": "5.35.0" + "version": "5.35.1" }, { "groupId": "com.ctre.phoenix", "artifactId": "wpiapi-java", - "version": "5.35.0" + "version": "5.35.1" } ], "jniDependencies": [ { "groupId": "com.ctre.phoenix", "artifactId": "cci", - "version": "5.35.0", + "version": "5.35.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -55,7 +55,7 @@ { "groupId": "com.ctre.phoenix.replay", "artifactId": "cci-replay", - "version": "5.35.0", + "version": "5.35.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -69,7 +69,7 @@ { "groupId": "com.ctre.phoenix.sim", "artifactId": "cci-sim", - "version": "5.35.0", + "version": "5.35.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -85,7 +85,7 @@ { "groupId": "com.ctre.phoenix", "artifactId": "wpiapi-cpp", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_Phoenix_WPI", "headerClassifier": "headers", "sharedLibrary": true, @@ -98,7 +98,7 @@ { "groupId": "com.ctre.phoenix", "artifactId": "api-cpp", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_Phoenix", "headerClassifier": "headers", "sharedLibrary": true, @@ -111,7 +111,7 @@ { "groupId": "com.ctre.phoenix", "artifactId": "cci", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_PhoenixCCI", "headerClassifier": "headers", "sharedLibrary": true, @@ -124,7 +124,7 @@ { "groupId": "com.ctre.phoenix.replay", "artifactId": "wpiapi-cpp-replay", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_Phoenix_WPIReplay", "headerClassifier": "headers", "sharedLibrary": true, @@ -140,7 +140,7 @@ { "groupId": "com.ctre.phoenix.replay", "artifactId": "api-cpp-replay", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_PhoenixReplay", "headerClassifier": "headers", "sharedLibrary": true, @@ -156,7 +156,7 @@ { "groupId": "com.ctre.phoenix.replay", "artifactId": "cci-replay", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_PhoenixCCIReplay", "headerClassifier": "headers", "sharedLibrary": true, @@ -172,7 +172,7 @@ { "groupId": "com.ctre.phoenix.sim", "artifactId": "wpiapi-cpp-sim", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_Phoenix_WPISim", "headerClassifier": "headers", "sharedLibrary": true, @@ -188,7 +188,7 @@ { "groupId": "com.ctre.phoenix.sim", "artifactId": "api-cpp-sim", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_PhoenixSim", "headerClassifier": "headers", "sharedLibrary": true, @@ -204,7 +204,7 @@ { "groupId": "com.ctre.phoenix.sim", "artifactId": "cci-sim", - "version": "5.35.0", + "version": "5.35.1", "libName": "CTRE_PhoenixCCISim", "headerClassifier": "headers", "sharedLibrary": true, diff --git a/vendordeps/Phoenix6-25.2.0.json b/vendordeps/Phoenix6-25.2.0.json deleted file mode 100644 index 38747fb6..00000000 --- a/vendordeps/Phoenix6-25.2.0.json +++ /dev/null @@ -1,419 +0,0 @@ -{ - "fileName": "Phoenix6-25.2.0.json", - "name": "CTRE-Phoenix (v6)", - "version": "25.2.0", - "frcYear": "2025", - "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", - "mavenUrls": [ - "https://maven.ctr-electronics.com/release/" - ], - "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json", - "conflictsWith": [ - { - "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", - "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", - "offlineFileName": "Phoenix6-replay-frc2025-latest.json" - } - ], - "javaDependencies": [ - { - "groupId": "com.ctre.phoenix6", - "artifactId": "wpiapi-java", - "version": "25.2.0" - } - ], - "jniDependencies": [ - { - "groupId": "com.ctre.phoenix6", - "artifactId": "api-cpp", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix6", - "artifactId": "tools", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "api-cpp-sim", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "tools-sim", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simTalonSRX", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simVictorSPX", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simPigeonIMU", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simCANCoder", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProTalonFX", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProTalonFXS", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProCANcoder", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProPigeon2", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProCANrange", - "version": "25.2.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - } - ], - "cppDependencies": [ - { - "groupId": "com.ctre.phoenix6", - "artifactId": "wpiapi-cpp", - "version": "25.2.0", - "libName": "CTRE_Phoenix6_WPI", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix6", - "artifactId": "tools", - "version": "25.2.0", - "libName": "CTRE_PhoenixTools", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "linuxathena" - ], - "simMode": "hwsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "wpiapi-cpp-sim", - "version": "25.2.0", - "libName": "CTRE_Phoenix6_WPISim", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "tools-sim", - "version": "25.2.0", - "libName": "CTRE_PhoenixTools_Sim", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simTalonSRX", - "version": "25.2.0", - "libName": "CTRE_SimTalonSRX", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simVictorSPX", - "version": "25.2.0", - "libName": "CTRE_SimVictorSPX", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simPigeonIMU", - "version": "25.2.0", - "libName": "CTRE_SimPigeonIMU", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simCANCoder", - "version": "25.2.0", - "libName": "CTRE_SimCANCoder", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProTalonFX", - "version": "25.2.0", - "libName": "CTRE_SimProTalonFX", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProTalonFXS", - "version": "25.2.0", - "libName": "CTRE_SimProTalonFXS", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProCANcoder", - "version": "25.2.0", - "libName": "CTRE_SimProCANcoder", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProPigeon2", - "version": "25.2.0", - "libName": "CTRE_SimProPigeon2", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - }, - { - "groupId": "com.ctre.phoenix6.sim", - "artifactId": "simProCANrange", - "version": "25.2.0", - "libName": "CTRE_SimProCANrange", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxarm64", - "osxuniversal" - ], - "simMode": "swsim" - } - ] -} \ No newline at end of file diff --git a/vendordeps/Phoenix6-replay-25.1.0.json b/vendordeps/Phoenix6-replay-25.2.1.json similarity index 86% rename from vendordeps/Phoenix6-replay-25.1.0.json rename to vendordeps/Phoenix6-replay-25.2.1.json index 2a777349..77acc5ad 100644 --- a/vendordeps/Phoenix6-replay-25.1.0.json +++ b/vendordeps/Phoenix6-replay-25.2.1.json @@ -1,7 +1,7 @@ { - "fileName": "Phoenix6-replay-25.1.0.json", + "fileName": "Phoenix6-replay-25.2.1.json", "name": "CTRE-Phoenix (v6) Replay", - "version": "25.1.0", + "version": "25.2.1", "frcYear": "2025", "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", "mavenUrls": [ @@ -19,14 +19,14 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-java", - "version": "25.1.0" + "version": "25.2.1" } ], "jniDependencies": [ { "groupId": "com.ctre.phoenix6", "artifactId": "api-cpp", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -37,7 +37,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -48,7 +48,7 @@ { "groupId": "com.ctre.phoenix6.replay", "artifactId": "api-cpp-replay", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -62,7 +62,7 @@ { "groupId": "com.ctre.phoenix6.replay", "artifactId": "tools-replay", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -76,7 +76,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "api-cpp-sim", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -90,7 +90,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -104,7 +104,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -118,7 +118,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -132,7 +132,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -146,7 +146,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -160,7 +160,21 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "25.1.0", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -174,7 +188,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -188,7 +202,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -202,7 +216,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANrange", - "version": "25.1.0", + "version": "25.2.1", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -218,7 +232,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-cpp", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_Phoenix6_WPI", "headerClassifier": "headers", "sharedLibrary": true, @@ -231,7 +245,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", "sharedLibrary": true, @@ -244,7 +258,7 @@ { "groupId": "com.ctre.phoenix6.replay", "artifactId": "wpiapi-cpp-replay", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_Phoenix6_WPIReplay", "headerClassifier": "headers", "sharedLibrary": true, @@ -260,7 +274,7 @@ { "groupId": "com.ctre.phoenix6.replay", "artifactId": "tools-replay", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_PhoenixTools_Replay", "headerClassifier": "headers", "sharedLibrary": true, @@ -276,7 +290,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "wpiapi-cpp-sim", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_Phoenix6_WPISim", "headerClassifier": "headers", "sharedLibrary": true, @@ -292,7 +306,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_PhoenixTools_Sim", "headerClassifier": "headers", "sharedLibrary": true, @@ -308,7 +322,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimTalonSRX", "headerClassifier": "headers", "sharedLibrary": true, @@ -324,7 +338,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimVictorSPX", "headerClassifier": "headers", "sharedLibrary": true, @@ -340,7 +354,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimPigeonIMU", "headerClassifier": "headers", "sharedLibrary": true, @@ -356,7 +370,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimCANCoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -372,7 +386,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimProTalonFX", "headerClassifier": "headers", "sharedLibrary": true, @@ -385,10 +399,26 @@ ], "simMode": "swsim" }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.2.1", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimProCANcoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -404,7 +434,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", "sharedLibrary": true, @@ -420,7 +450,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANrange", - "version": "25.1.0", + "version": "25.2.1", "libName": "CTRE_SimProCANrange", "headerClassifier": "headers", "sharedLibrary": true, diff --git a/vendordeps/PlayingWithFusion-2025.01.04.json b/vendordeps/PlayingWithFusion-2025.01.04.json deleted file mode 100644 index 580c1217..00000000 --- a/vendordeps/PlayingWithFusion-2025.01.04.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "fileName": "PlayingWithFusion-2025.01.04.json", - "name": "PlayingWithFusion", - "version": "2025.01.04", - "uuid": "14b8ad04-24df-11ea-978f-2e728ce88125", - "frcYear": "2025", - "jsonUrl": "https://www.playingwithfusion.com/frc/playingwithfusion2025.json", - "mavenUrls": [ - "https://www.playingwithfusion.com/frc/maven/" - ], - "javaDependencies": [ - { - "groupId": "com.playingwithfusion.frc", - "artifactId": "PlayingWithFusion-java", - "version": "2025.01.04" - } - ], - "jniDependencies": [ - { - "groupId": "com.playingwithfusion.frc", - "artifactId": "PlayingWithFusion-driver", - "version": "2025.01.04", - "skipInvalidPlatforms": true, - "isJar": false, - "validPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "osxuniversal" - ] - } - ], - "cppDependencies": [ - { - "groupId": "com.playingwithfusion.frc", - "artifactId": "PlayingWithFusion-cpp", - "version": "2025.01.04", - "headerClassifier": "headers", - "sharedLibrary": false, - "libName": "PlayingWithFusion", - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "osxuniversal" - ] - }, - { - "groupId": "com.playingwithfusion.frc", - "artifactId": "PlayingWithFusion-driver", - "version": "2025.01.04", - "headerClassifier": "headers", - "sharedLibrary": true, - "libName": "PlayingWithFusionDriver", - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "osxuniversal" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/REVLib-2025.0.1.json b/vendordeps/REVLib-2025.0.1.json deleted file mode 100644 index c9980543..00000000 --- a/vendordeps/REVLib-2025.0.1.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "fileName": "REVLib-2025.0.1.json", - "name": "REVLib", - "version": "2025.0.1", - "frcYear": "2025", - "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", - "mavenUrls": [ - "https://maven.revrobotics.com/" - ], - "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json", - "javaDependencies": [ - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-java", - "version": "2025.0.1" - } - ], - "jniDependencies": [ - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-driver", - "version": "2025.0.1", - "skipInvalidPlatforms": true, - "isJar": false, - "validPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - } - ], - "cppDependencies": [ - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-cpp", - "version": "2025.0.1", - "libName": "REVLib", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - }, - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-driver", - "version": "2025.0.1", - "libName": "REVLibDriver", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/ReduxLib-2025.0.0.json b/vendordeps/ReduxLib-2025.0.0.json deleted file mode 100644 index f15ff901..00000000 --- a/vendordeps/ReduxLib-2025.0.0.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "fileName": "ReduxLib-2025.0.0.json", - "name": "ReduxLib", - "version": "2025.0.0", - "frcYear": "2025", - "uuid": "151ecca8-670b-4026-8160-cdd2679ef2bd", - "mavenUrls": [ - "https://maven.reduxrobotics.com/" - ], - "jsonUrl": "https://frcsdk.reduxrobotics.com/ReduxLib_2025.json", - "javaDependencies": [ - { - "groupId": "com.reduxrobotics.frc", - "artifactId": "ReduxLib-java", - "version": "2025.0.0" - } - ], - "jniDependencies": [ - { - "groupId": "com.reduxrobotics.frc", - "artifactId": "ReduxLib-driver", - "version": "2025.0.0", - "isJar": false, - "skipInvalidPlatforms": true, - "validPlatforms": [ - "linuxathena", - "linuxx86-64", - "linuxarm32", - "linuxarm64", - "osxuniversal", - "windowsx86-64" - ] - } - ], - "cppDependencies": [ - { - "groupId": "com.reduxrobotics.frc", - "artifactId": "ReduxLib-cpp", - "version": "2025.0.0", - "libName": "ReduxLib", - "headerClassifier": "headers", - "sourcesClassifier": "sources", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "linuxathena", - "linuxx86-64", - "linuxarm32", - "linuxarm64", - "osxuniversal", - "windowsx86-64" - ] - }, - { - "groupId": "com.reduxrobotics.frc", - "artifactId": "ReduxLib-driver", - "version": "2025.0.0", - "libName": "ReduxCore", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "linuxathena", - "linuxx86-64", - "linuxarm32", - "linuxarm64", - "osxuniversal", - "windowsx86-64" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/ThriftyLib.json b/vendordeps/ThriftyLib.json deleted file mode 100644 index 02445068..00000000 --- a/vendordeps/ThriftyLib.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "fileName": "ThriftyLib.json", - "name": "ThriftyLib", - "version": "2025.0.1", - "frcYear": "2025", - "uuid": "60b2694b-9e6e-4026-81ee-6f167946f4b0", - "mavenUrls": [ - "https://docs.home.thethriftybot.com" - ], - "jsonUrl": "https://docs.home.thethriftybot.com/ThriftyLib.json", - "javaDependencies": [ - { - "groupId": "com.thethriftybot.frc", - "artifactId": "ThriftyLib-java", - "version": "2025.0.1" - } - ], - "jniDependencies": [], - "cppDependencies": [] -} \ No newline at end of file diff --git a/vendordeps/URCL.json b/vendordeps/URCL.json deleted file mode 100644 index 55e7841a..00000000 --- a/vendordeps/URCL.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "fileName": "URCL.json", - "name": "URCL", - "version": "2025.0.0", - "frcYear": "2025", - "uuid": "84246d17-a797-4d1e-bd9f-c59cd8d2477c", - "mavenUrls": [ - "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" - ], - "jsonUrl": "https://raw.githubusercontent.com/Mechanical-Advantage/URCL/main/URCL.json", - "javaDependencies": [ - { - "groupId": "org.littletonrobotics.urcl", - "artifactId": "URCL-java", - "version": "2025.0.0" - } - ], - "jniDependencies": [ - { - "groupId": "org.littletonrobotics.urcl", - "artifactId": "URCL-driver", - "version": "2025.0.0", - "skipInvalidPlatforms": true, - "isJar": false, - "validPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxathena", - "osxuniversal" - ] - } - ], - "cppDependencies": [ - { - "groupId": "org.littletonrobotics.urcl", - "artifactId": "URCL-cpp", - "version": "2025.0.0", - "libName": "URCL", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxathena", - "osxuniversal" - ] - }, - { - "groupId": "org.littletonrobotics.urcl", - "artifactId": "URCL-driver", - "version": "2025.0.0", - "libName": "URCLDriver", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxx86-64", - "linuxathena", - "osxuniversal" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/maple-sim.json b/vendordeps/maple-sim.json deleted file mode 100644 index 5369ef8b..00000000 --- a/vendordeps/maple-sim.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "fileName": "maple-sim.json", - "name": "maplesim", - "version": "0.3.1", - "frcYear": "2025", - "uuid": "c39481e8-4a63-4a4c-9df6-48d91e4da37b", - "mavenUrls": [ - "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/repos/releases", - "https://repo1.maven.org/maven2" - ], - "jsonUrl": "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/maple-sim.json", - "javaDependencies": [ - { - "groupId": "org.ironmaple", - "artifactId": "maplesim-java", - "version": "0.3.1" - }, - { - "groupId": "org.dyn4j", - "artifactId": "dyn4j", - "version": "5.0.2" - } - ], - "jniDependencies": [], - "cppDependencies": [] -} \ No newline at end of file diff --git a/vendordeps/yagsl-2025.2.1.json b/vendordeps/yagsl-2025.2.1.json deleted file mode 100644 index cb0a7c16..00000000 --- a/vendordeps/yagsl-2025.2.1.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "fileName": "yagsl-2025.2.1.json", - "name": "YAGSL", - "version": "2025.2.1", - "frcYear": "2025", - "uuid": "1ccce5a4-acd2-4d18-bca3-4b8047188400", - "mavenUrls": [ - "https://broncbotz3481.github.io/YAGSL-Lib/yagsl/repos" - ], - "jsonUrl": "https://broncbotz3481.github.io/YAGSL-Lib/yagsl/yagsl.json", - "javaDependencies": [ - { - "groupId": "swervelib", - "artifactId": "YAGSL-java", - "version": "2025.2.1" - } - ], - "requires": [ - { - "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", - "errorMessage": "REVLib is required!", - "offlineFileName": "REVLib-2025.0.0.json", - "onlineUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json" - }, - { - "uuid": "151ecca8-670b-4026-8160-cdd2679ef2bd", - "errorMessage": "ReduxLib is required!", - "offlineFileName": "ReduxLib-2025.0.0.json", - "onlineUrl": "https://frcsdk.reduxrobotics.com/ReduxLib_2025.json" - }, - { - "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", - "errorMessage": "Phoenix6 is required!", - "offlineFileName": "Phoenix6-25.1.0.json", - "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json" - }, - { - "uuid": "ab676553-b602-441f-a38d-f1296eff6537", - "errorMessage": "Phoenix5 is required!", - "offlineFileName": "Phoenix5-5.35.0.json", - "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-frc2025-latest.json" - }, - { - "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", - "errorMessage": "Studica is required!", - "offlineFileName": "Studica-2025.0.0.json", - "onlineUrl": "https://dev.studica.com/releases/2025/Studica-2025.0.0.json" - }, - { - "uuid": "60b2694b-9e6e-4026-81ee-6f167946f4b0", - "errorMessage": "ThriftyLib is required!", - "offlineFileName": "ThriftyLib.json", - "onlineUrl": "https://docs.home.thethriftybot.com/ThriftyLib.json" - }, - { - "uuid": "c39481e8-4a63-4a4c-9df6-48d91e4da37b", - "errorMessage": "maple-sim is required for simulation", - "offlineFileName": "maple-sim.json", - "onlineUrl": "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/maple-sim.json" - } - ], - "jniDependencies": [], - "cppDependencies": [] -} \ No newline at end of file From b617c6d4dee528ee013755980b7285dacf4f6cbe Mon Sep 17 00:00:00 2001 From: David Shen Date: Thu, 30 Jan 2025 20:48:47 -0500 Subject: [PATCH 07/13] coral states, fix venderdeps, no algae --- .vscode/launch.json | 12 +- .vscode/settings.json | 3 +- README.md | 134 ++++-- build.gradle | 2 +- docs/reefscape-RobotState-Light.png | Bin 0 -> 282667 bytes docs/reefscape-states.drawio | 454 ++++++++++++++++++ src/main/deploy/choreo/2025-project.chor | 53 +- src/main/deploy/choreo/New Path.traj | 28 ++ .../commands/auton/AutoCommandInterface.java | 5 + .../commands/drive/DriveAutonCommand.java | 106 ++++ .../frc/robot/constants/BiscuitConstants.java | 102 +++- .../frc/robot/constants/CoralConstants.java | 6 +- .../frc/robot/constants/DriveConstants.java | 150 +++++- .../frc/robot/constants/RobotConstants.java | 4 +- .../robot/constants/RobotStateConstants.java | 4 +- .../robot/constants/TagServoingConstants.java | 5 + .../frc/robot/constants/VisionConstants.java | 100 ++++ .../subsystems/algae/AlgaeSubsystem.java | 57 +-- .../frc/robot/subsystems/algae/algaeIO.java | 3 +- .../frc/robot/subsystems/algae/algaeIOFX.java | 98 ++-- .../robot/subsystems/biscuit/BiscuitIO.java | 27 +- .../robot/subsystems/biscuit/BiscuitIOFX.java | 82 +++- .../subsystems/biscuit/BiscuitSubsystem.java | 61 ++- .../frc/robot/subsystems/coral/CoralIOFX.java | 35 +- .../subsystems/coral/CoralSubsystem.java | 66 ++- .../subsystems/drive/DriveSubsystem.java | 383 ++++++++++++++- .../frc/robot/subsystems/drive/Swerve.java | 270 ++++++++++- .../frc/robot/subsystems/drive/SwerveIO.java | 97 +++- .../subsystems/pathHandler/PathHandler.java | 261 +++++++++- vendordeps/AdvantageKit.json | 11 +- vendordeps/ChoreoLib-2025.0.2.json | 44 ++ vendordeps/thirdcoast.json | 4 +- 32 files changed, 2451 insertions(+), 216 deletions(-) create mode 100644 docs/reefscape-RobotState-Light.png create mode 100644 docs/reefscape-states.drawio create mode 100644 src/main/deploy/choreo/New Path.traj create mode 100644 src/main/java/frc/robot/commands/auton/AutoCommandInterface.java create mode 100644 src/main/java/frc/robot/commands/drive/DriveAutonCommand.java create mode 100644 src/main/java/frc/robot/constants/TagServoingConstants.java create mode 100644 src/main/java/frc/robot/constants/VisionConstants.java create mode 100644 vendordeps/ChoreoLib-2025.0.2.json diff --git a/.vscode/launch.json b/.vscode/launch.json index c9c9713d..18414213 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,18 +4,24 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - + { + "type": "java", + "name": "Main", + "request": "launch", + "mainClass": "frc.robot.Main", + "projectName": "reefscape" + }, { "type": "wpilib", "name": "WPILib Desktop Debug", "request": "launch", - "desktop": true, + "desktop": true }, { "type": "wpilib", "name": "WPILib roboRIO Debug", "request": "launch", - "desktop": false, + "desktop": false } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 612cdd0d..9a299ae5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -56,5 +56,6 @@ "edu.wpi.first.math.proto.*", "edu.wpi.first.math.**.proto.*", "edu.wpi.first.math.**.struct.*", - ] + ], + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable" } diff --git a/README.md b/README.md index 3deb2bc1..4b683297 100644 --- a/README.md +++ b/README.md @@ -9,49 +9,96 @@ ### Operator Controller +## State Diagrams + +### Robot State +![robotState](docs/reefscape-RobotState-Light.png) + ## CAN Bus -| Subsystem | Type | Talon | ID | Comp PDP | Proto PDP | Motor | Breaker | -| --------- | ---- | -------------- | --- | -------- | --------- | ------ | ------- | -| Drive | FXS | azimuth | 0 | | | Minion | | -| Drive | FXS | azimuth | 1 | | | Minion | | -| Drive | FXS | azimuth | 2 | | | Minion | | -| Drive | FXS | azimuth | 3 | | | Minion | | -| Drive | FX | drive | 10 | | | kraken | | -| Drive | FX | drive | 11 | | | kraken | | -| Drive | FX | drive | 12 | | | kraken | | -| Drive | FX | drive | 13 | | | kraken | | -| Elevator | FX | elevatorMain | 20 | | | | | -| Elevator | FX | elevatorFollow | 21 | | | | | -| Biscuit | FX | biscuit | 25 | | | | | -| Algae | FX | rollers | 30 | | | | | -| Coral | FX | wheels | 35 | | | | | +| Subsystem | Type | Talon | ID | CAN BUS | Comp PDP | Proto PDP | Motor | Breaker | +| --------- | -------- | ------------------------- | --- | ------- | -------- | --------- | ----- | ------- | +| Drive | FXS | azimuth | 0 | FD | | | Minion | | +| Drive | FXS | azimuth | 1 | FD | | | Minion | | +| Drive | FXS | azimuth | 2 | FD | | | Minion | | +| Drive | FXS | azimuth | 3 | FD | | | Minion | | +| Drive | FX | drive | 10 | FD | | | kraken | | +| Drive | FX | drive | 11 | FD | | | kraken | | +| Drive | FX | drive | 12 | FD | | | kraken | | +| Drive | FX | drive | 13 | FD | | | kraken | | +| Elevator | FX | elevatorMain | 20 | rio | | 21 | kraken | | +| Elevator | FX | elevatorFollow | 21 | rio | | 20 | kraken | | +| Biscuit | FXS | biscuit | 25 | rio | | | Minion | | +| Algae | FXS | rollers | 30 | rio | | | Minion | | +| Coral | FXS | wheels | 35 | rio | | | Minion | | +| Funnel | FXS | rollers | 40 | rio | | | Minion | | +| Climb | FX | rollers | 45 | rio | | | Minion | | +| Climb | FX | pivot | 46 | rio | | | kraken | | +| Climb | CANcoder | CANCoder | 47 | ri0 | | | n/a | | +| - | - | rio | - | both | | 12 | | | +| - | - | radio | - | - | | | | | +| - | - | custom circuit (pi power) | - | - | | | | | + +## VRM +| Device | Voltage | Current | +| --------------- | ------- | ------- | +| Pigeon 2 | 12 | 2 | +| Ethernet Switch | 12 | 2 | +| | | | + +## Beam Breaks +| Subsystem | Talon | ID | Fwd/Rev | Purpose | +| --------- | ------- | --- | ------- | ------------------ | +| Funnel | rollers | 40 | | Coral Presence | +| Coral | wheels | 30 | | Coral partially in | +| Coral | wheels | 30 | | Coral fully in | ## Roborio -| Subsystem | Interface | Device | -| --------- | --------- | ------ | -| | USB | | +| Subsystem | Interface | Device | +| --------- | --------- | -------- | +| n/a | USB | CANivore | ## DIO -| Subsystem | name | ID | -| --------- | -------------- | --- | -| | | 0 | -| | | 1 | -| | | 2 | -| | | 3 | -| | | 4 | -| | | 5 | -| | | 6 | -| | | 7 | -| | | 8 | -| | | 9 | +| Subsystem | name | ID | +| ---------- | --------- | --- | +| BattMon | Batt V | 0 | +| BattMon | Batt I | 1 | +| BattMon | PDP V | 2 | +| BattMon | Breaker T | 3 | +| AutoSwitch | switch | 4 | +| AutoSwitch | switch | 5 | +| AutoSwitch | switch | 6 | +| AutoSwitch | switch | 7 | +| AutoSwitch | switch | 8 | +| AutoSwitch | switch | 9 | + +## MXP +| Subsystem | name | ID | +| --------- | --------- | --- | +| TagServo | wallSense | 10 | +| | | 11 | +| | | 12 | +| | | 13 | +| | | 14 | +| | | 15 | +| | | 16 | +| | | 17 | +| | | 18 | +| | | 19 | +| | | 20 | +| | | 21 | +| | | 22 | +| | | 23 | +| | | 24 | +| | | 25 | + ## PWM | Subsystem | name | ID | | --------- | ------------ | --- | -| | | 0 | +| LED | lights | 0 | | | | 1 | | | | 2 | | | | 3 | @@ -62,8 +109,25 @@ | | | 8 | | | | 9 | -## MXP -| Subsystem | name | ID | -| --------- | ------ | -- | -| | | | +## Analog +| Subsystem | name | ID | +| --------- | ------ | --- | +| Elevator | height | 0 | +| | | 1 | +| | | 2 | +| | | 3 | +| | | 4 | +| | | 5 | +| | | 6 | +| | | 7 | +| | | 8 | +| | | 9 | +## Cameras +| Camera | IP Address | Type | +| ----------- | ----------- | ------- | +| Left Servo | 10.27.67.XX | USB 3.0 | +| Right Servo | 10.27.67.YY | USB 3.0 | +| Upper Left | 10.27.67.XX | USB 2.0 | +| Upper Right | 10.27.67.YY | USB 2.0 | +| Rear | 10.27.67.ZZ | USB 2.0 | diff --git a/build.gradle b/build.gradle index a64c9abf..3677b87b 100644 --- a/build.gradle +++ b/build.gradle @@ -81,7 +81,7 @@ dependencies { implementation 'ch.qos.logback:logback-classic:1.3.5' //logging implementation("com.opencsv:opencsv:5.6") - implementation 'org.strykeforce:wallEYE:24.2.0' + implementation 'org.strykeforce:wallEYE:25.0.0' implementation 'net.jafama:jafama:2.3.2' //fastMath } diff --git a/docs/reefscape-RobotState-Light.png b/docs/reefscape-RobotState-Light.png new file mode 100644 index 0000000000000000000000000000000000000000..799dab4817347f08a44457052914122c17a92a61 GIT binary patch literal 282667 zcmeEP2RxPg|1VKSMX5BUJ!J39QZ%fP5|Lz&V{^<@s+*#F8!EG@n@}k#n=6&92HE6t z$aakE-{(2!c{qv4ujSVN|9jn>=RD8zU7yeU^ZtzQ_xz!xAUlnIKK+CV6Q*t5vPpTu zgee&lCeXa1oeWp*sm{3$|4g)2mfbiZEMsBygbBR~Hk)_bm^tVhnd(hg!6!}rYXvX2 zAdF>yu6!D^>l5laOUtQTsOlVFw!^DJ3#)9mz!6BgPVtgS5TElXa(N}ejfM_ z4-cmZuh1Uy{W=DE=ID#$agIi&raCKlWdu06;Z>`58JQoz*;~V(3aaoQ9&WhIw-Nq= zF9i9?e>w>AuYoHYEG$g*cIoZkZe)Wz&nLvmCkU5UZ{MOSFSmkM8vZslGSP#7Wc74S zaL6MEtaR)-jc{-~FE1xICm&prHnhT-!A&weJp6D;K!{I(lUG;-z8M.gvEFeg7; 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

>oXf;DM?a6y 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%0AG<=spLC6!bVFai3QSoHw%F$ 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≫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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/deploy/choreo/2025-project.chor b/src/main/deploy/choreo/2025-project.chor index b24176f8..1b20df9d 100644 --- a/src/main/deploy/choreo/2025-project.chor +++ b/src/main/deploy/choreo/2025-project.chor @@ -4,27 +4,42 @@ "type":"Swerve", "variables":{ "expressions":{}, - "poses":{} + "poses":{ + "startpos":{ + "x":{ + "exp":"7.1008875 m", + "val":7.1008875 + }, + "y":{ + "exp":"5.0756788 m", + "val":5.0756788 + }, + "heading":{ + "exp":"180 deg", + "val":3.141592653589793 + } + } + } }, "config":{ "frontLeft":{ "x":{ - "exp":"11 in", - "val":0.2794 + "exp":"12.125 in", + "val":0.307975 }, "y":{ - "exp":"11 in", - "val":0.2794 + "exp":"12.125 in", + "val":0.307975 } }, "backLeft":{ "x":{ - "exp":"-11 in", - "val":-0.2794 + "exp":"-12.125 in", + "val":-0.307975 }, "y":{ - "exp":"11 in", - "val":0.2794 + "exp":"12.125 in", + "val":0.307975 } }, "mass":{ @@ -40,8 +55,8 @@ "val":6.5 }, "radius":{ - "exp":"2 in", - "val":0.0508 + "exp":"1.75 in", + "val":0.04445 }, "vmax":{ "exp":"6000 RPM", @@ -52,21 +67,21 @@ "val":1.2 }, "cof":{ - "exp":"1.5", - "val":1.5 + "exp":"1.1", + "val":1.1 }, "bumper":{ "front":{ - "exp":"16 in", - "val":0.4064 + "exp":"17.9375 in", + "val":0.4556125 }, "side":{ - "exp":"16 in", - "val":0.4064 + "exp":"17.9375 in", + "val":0.4556125 }, "back":{ - "exp":"16 in", - "val":0.4064 + "exp":"17.9375 in", + "val":0.4556125 } }, "differentialTrackWidth":{ diff --git a/src/main/deploy/choreo/New Path.traj b/src/main/deploy/choreo/New Path.traj new file mode 100644 index 00000000..7b2e9ed1 --- /dev/null +++ b/src/main/deploy/choreo/New Path.traj @@ -0,0 +1,28 @@ +{ + "name":"New Path", + "version":1, + "snapshot":{ + "waypoints":[], + "constraints":[], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"7.1008875 m", "val":7.1008875}, "y":{"exp":"5.0756788 m", "val":5.0756788}, "heading":{"exp":"180 deg", "val":3.141592653589793}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"17.548 m", "val":17.548}, "h":{"exp":"8.052 m", "val":8.052}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "sampleType":null, + "waypoints":[], + "samples":[], + "splits":[] + }, + "events":[] +} diff --git a/src/main/java/frc/robot/commands/auton/AutoCommandInterface.java b/src/main/java/frc/robot/commands/auton/AutoCommandInterface.java new file mode 100644 index 00000000..7a81bf6b --- /dev/null +++ b/src/main/java/frc/robot/commands/auton/AutoCommandInterface.java @@ -0,0 +1,5 @@ +package frc.robot.commands.auton; + +public interface AutoCommandInterface { + public void reassignAlliance(); +} diff --git a/src/main/java/frc/robot/commands/drive/DriveAutonCommand.java b/src/main/java/frc/robot/commands/drive/DriveAutonCommand.java new file mode 100644 index 00000000..114e18d8 --- /dev/null +++ b/src/main/java/frc/robot/commands/drive/DriveAutonCommand.java @@ -0,0 +1,106 @@ +package frc.robot.commands.drive; + +import choreo.Choreo; +import choreo.trajectory.SwerveSample; +import choreo.trajectory.Trajectory; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.commands.auton.AutoCommandInterface; +import frc.robot.subsystems.drive.DriveSubsystem; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DriveAutonCommand extends Command implements AutoCommandInterface { + private final DriveSubsystem driveSubsystem; + private Trajectory trajectory; + private final Timer timer = new Timer(); + private static final Logger logger = LoggerFactory.getLogger(DriveAutonCommand.class); + private boolean isTherePath = false; + private String trajectoryName; + private boolean mirrorTrajectory = false; + + private boolean resetOdometry; + private boolean lastPath; + + public DriveAutonCommand( + DriveSubsystem driveSubsystem, + String trajectoryName, + boolean lastPath, + boolean resetOdometry) { + + addRequirements(driveSubsystem); + this.driveSubsystem = driveSubsystem; + this.resetOdometry = resetOdometry; + this.lastPath = lastPath; + this.trajectoryName = trajectoryName; + Optional> tempTrajectory = Choreo.loadTrajectory(trajectoryName); + if (tempTrajectory.isPresent()) { + trajectory = tempTrajectory.get(); + isTherePath = true; + } else { + logger.error("Trajectory {} not found", trajectoryName); + isTherePath = false; + } + timer.start(); + } + + @Override + public void reassignAlliance() { + mirrorTrajectory = driveSubsystem.shouldFlip(); + } + + @Override + public void initialize() { + if (isTherePath) { + driveSubsystem.setAutoDebugMsg("Initialize " + trajectoryName); + Pose2d initialPose = new Pose2d(); + initialPose = trajectory.getInitialPose(mirrorTrajectory).get(); + if (resetOdometry) { + driveSubsystem.resetOdometry(initialPose); + } + driveSubsystem.setEnableHolo(true); + // driveSubsystem.recordAutoTrajectory(trajectory); + driveSubsystem.resetHolonomicController(); + driveSubsystem.grapherTrajectoryActive(true); + timer.reset(); + logger.info("Begin Trajectory: {}", trajectoryName); + SwerveSample desiredState = trajectory.sampleAt(timer.get(), mirrorTrajectory).get(); + driveSubsystem.calculateController(desiredState); + } + } + + @Override + public void execute() { + if (isTherePath) { + SwerveSample desiredState = trajectory.sampleAt(timer.get(), mirrorTrajectory).get(); + driveSubsystem.calculateController(desiredState); + } + } + + @Override + public boolean isFinished() { + if (!isTherePath) { + return false; + } + return timer.hasElapsed(trajectory.getTotalTime()); + } + + @Override + public void end(boolean interrupted) { + driveSubsystem.setEnableHolo(false); + // driveSubsystem.recordAutoTrajectory(null); + + if (!interrupted && !lastPath) { + driveSubsystem.calculateController( + trajectory.sampleAt(trajectory.getTotalTime(), mirrorTrajectory).get()); + } else { + driveSubsystem.drive(0, 0, 0); + } + + driveSubsystem.grapherTrajectoryActive(false); + logger.info("End Trajectory {}: {}", trajectoryName, timer.get()); + driveSubsystem.setAutoDebugMsg("End " + trajectoryName); + } +} diff --git a/src/main/java/frc/robot/constants/BiscuitConstants.java b/src/main/java/frc/robot/constants/BiscuitConstants.java index 4d46333e..e714e54a 100644 --- a/src/main/java/frc/robot/constants/BiscuitConstants.java +++ b/src/main/java/frc/robot/constants/BiscuitConstants.java @@ -1,3 +1,103 @@ package frc.robot.constants; -public class BiscuitConstants {} +import static edu.wpi.first.units.Units.Rotations; + +import com.ctre.phoenix6.configs.CurrentLimitsConfigs; +import com.ctre.phoenix6.configs.HardwareLimitSwitchConfigs; +import com.ctre.phoenix6.configs.MotionMagicConfigs; +import com.ctre.phoenix6.configs.MotorOutputConfigs; +import com.ctre.phoenix6.configs.Slot0Configs; +import com.ctre.phoenix6.configs.SoftwareLimitSwitchConfigs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.VoltageConfigs; +import com.ctre.phoenix6.signals.ForwardLimitSourceValue; +import com.ctre.phoenix6.signals.ForwardLimitTypeValue; +import com.ctre.phoenix6.signals.GravityTypeValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.ctre.phoenix6.signals.ReverseLimitSourceValue; +import com.ctre.phoenix6.signals.ReverseLimitTypeValue; +import edu.wpi.first.units.measure.Angle; + +public class BiscuitConstants { + // These are all wrong right now because we don't have any actual info + + public static Angle kZero = Rotations.of(42); // Will need to be experimentally determined + public static int talonID = 3; + public static double kCloseEnough = 2137473647; // This is a little out of wack. + public static final Angle kMaxFwd = Rotations.of(100); + public static final Angle kMaxRev = Rotations.of(-100); + // public static Angle Level1 = ; + + // Disables the TalonFX by setting it's voltage to zero. Not very shocking. + public static VoltageConfigs disableTalon() { + VoltageConfigs voltage = + new VoltageConfigs().withPeakForwardVoltage(0.0).withPeakReverseVoltage(0.0); + getFXConfig().Voltage = voltage; + return voltage; + } + + // I copied and pasted this because I'm lazy + public static TalonFXConfiguration getFXConfig() { + TalonFXConfiguration fxConfig = new TalonFXConfiguration(); + + CurrentLimitsConfigs current = + new CurrentLimitsConfigs() + .withStatorCurrentLimit(10) + .withStatorCurrentLimitEnable(false) + .withStatorCurrentLimit(20) + .withSupplyCurrentLimit(10) + .withSupplyCurrentLowerLimit(8) + .withSupplyCurrentLowerTime(0.02) + .withSupplyCurrentLimitEnable(true); + fxConfig.CurrentLimits = current; + + HardwareLimitSwitchConfigs hwLimit = + new HardwareLimitSwitchConfigs() + .withForwardLimitAutosetPositionEnable(false) + .withForwardLimitEnable(false) + .withForwardLimitType(ForwardLimitTypeValue.NormallyOpen) + .withForwardLimitSource(ForwardLimitSourceValue.LimitSwitchPin) + .withReverseLimitAutosetPositionEnable(false) + .withReverseLimitEnable(false) + .withReverseLimitType(ReverseLimitTypeValue.NormallyOpen) + .withReverseLimitSource(ReverseLimitSourceValue.LimitSwitchPin); + fxConfig.HardwareLimitSwitch = hwLimit; + + SoftwareLimitSwitchConfigs swLimit = + new SoftwareLimitSwitchConfigs() + .withForwardSoftLimitEnable(true) + .withForwardSoftLimitThreshold(kMaxFwd) + .withReverseSoftLimitEnable(true) + .withReverseSoftLimitThreshold(kMaxRev); + fxConfig.SoftwareLimitSwitch = swLimit; + + Slot0Configs slot0 = + new Slot0Configs() + .withKP(0) + .withKI(0) + .withKD(0) + .withGravityType(GravityTypeValue.Elevator_Static) + .withKG(0) + .withKS(0) + .withKV(0) + .withKA(0); + fxConfig.Slot0 = slot0; + + MotionMagicConfigs motionMagic = + new MotionMagicConfigs() + .withMotionMagicAcceleration(0) + .withMotionMagicCruiseVelocity(0) + .withMotionMagicExpo_kA(0) + .withMotionMagicExpo_kV(0) + .withMotionMagicJerk(0); + fxConfig.MotionMagic = motionMagic; + + MotorOutputConfigs motorOut = + new MotorOutputConfigs() + .withDutyCycleNeutralDeadband(0.01) + .withNeutralMode(NeutralModeValue.Coast); + fxConfig.MotorOutput = motorOut; + + return fxConfig; + } +} diff --git a/src/main/java/frc/robot/constants/CoralConstants.java b/src/main/java/frc/robot/constants/CoralConstants.java index 8ca72712..b7640be2 100644 --- a/src/main/java/frc/robot/constants/CoralConstants.java +++ b/src/main/java/frc/robot/constants/CoralConstants.java @@ -1,7 +1,5 @@ package frc.robot.constants; -import static edu.wpi.first.units.Units.DegreesPerSecond; -import static edu.wpi.first.units.Units.Rotations; import static edu.wpi.first.units.Units.RotationsPerSecond; import com.ctre.phoenix6.configs.CurrentLimitsConfigs; @@ -23,6 +21,10 @@ public class CoralConstants { public static final AngularVelocity kCloseEnough = RotationsPerSecond.of(0.1); + public static final AngularVelocity kIntakingSpeed = null; + + public static final AngularVelocity kEjectingSpeed = null; + // Coral Talon FX Config public static TalonFXSConfiguration getFXConfig() { TalonFXSConfiguration fxsConfig = new TalonFXSConfiguration(); diff --git a/src/main/java/frc/robot/constants/DriveConstants.java b/src/main/java/frc/robot/constants/DriveConstants.java index a390c4c3..f917539f 100644 --- a/src/main/java/frc/robot/constants/DriveConstants.java +++ b/src/main/java/frc/robot/constants/DriveConstants.java @@ -1,3 +1,151 @@ package frc.robot.constants; -public class DriveConstants {} +import com.ctre.phoenix.motorcontrol.FeedbackDevice; +import com.ctre.phoenix.motorcontrol.LimitSwitchSource; +import com.ctre.phoenix.motorcontrol.can.TalonSRXConfiguration; +import com.ctre.phoenix.sensors.SensorVelocityMeasPeriod; +import com.ctre.phoenix6.configs.CurrentLimitsConfigs; +import com.ctre.phoenix6.configs.MotorOutputConfigs; +import com.ctre.phoenix6.configs.Pigeon2Configuration; +import com.ctre.phoenix6.configs.Slot0Configs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.geometry.Translation2d; + +public class DriveConstants { + public static final double kDriveGearRatio = 6.5; + public static final double kWheelDiameterInches = 4.0; + public static final double kMaxSpeedMetersPerSecond = 12.0; + public static final double kSpeedStillThreshold = 0.1; // meters per second + public static final double kGyroRateStillThreshold = 10.0; // 25 5 degrees per second + public static final double kGyroDifferentThreshold = 5.0; // 5 degrees + public static final int kGyroDifferentCount = 3; + + public static final double kRobotLength = 22.0; + public static final double kRobotWidth = 22.0; + public static final double kFieldMaxX = 690.0; + + public static final double kPOmega = 4.5; + public static final double kIOmega = 0.0; + public static final double kDOmega = 0.0; + public static final double kMaxVelOmega = + (kMaxSpeedMetersPerSecond / Math.hypot(kRobotWidth / 2.0, kRobotLength / 2.0)) / 2.0; + public static final double kMaxAccelOmega = 5.0; + + public static final double kPHolonomic = 3.0; // was 3 + public static final double kIHolonomic = 0.0000; + public static final double kDHolonomic = 0.00; // kPHolonomic/100 + + public static Translation2d[] getWheelLocationMeters() { + final double x = kRobotLength / 2.0; // front-back, was ROBOT_LENGTH + final double y = kRobotWidth / 2.0; // left-right, was ROBOT_WIDTH + Translation2d[] locs = new Translation2d[4]; + locs[0] = new Translation2d(x, y); // left front + locs[1] = new Translation2d(x, -y); // right front + locs[2] = new Translation2d(-x, y); // left rear + locs[3] = new Translation2d(-x, -y); // right rear + return locs; + } + + // temp stuff + public static final int kTempAvgCount = 25; + public static final double kTripTemp = 1300; + public static final double kRecoverTemp = 1290; + public static final double kNotifyTemp = 1295; + + public static TalonSRXConfiguration + getAzimuthTalonConfig() { // will be changed to a TalonFXConfiguration + // constructor sets encoder to Quad/CTRE_MagEncoder_Relative + TalonSRXConfiguration azimuthConfig = new TalonSRXConfiguration(); + + azimuthConfig.primaryPID.selectedFeedbackCoefficient = 1.0; + azimuthConfig.auxiliaryPID.selectedFeedbackSensor = FeedbackDevice.None; + + azimuthConfig.forwardLimitSwitchSource = LimitSwitchSource.Deactivated; + azimuthConfig.reverseLimitSwitchSource = LimitSwitchSource.Deactivated; + + azimuthConfig.continuousCurrentLimit = 10; + azimuthConfig.peakCurrentDuration = 0; + azimuthConfig.peakCurrentLimit = 0; + + azimuthConfig.slot0.kP = 15.0; + azimuthConfig.slot0.kI = 0.0; + azimuthConfig.slot0.kD = 150.0; + azimuthConfig.slot0.kF = 1.0; + azimuthConfig.slot0.integralZone = 0; + azimuthConfig.slot0.allowableClosedloopError = 0; + azimuthConfig.slot0.maxIntegralAccumulator = 0; + + azimuthConfig.motionCruiseVelocity = 800; + azimuthConfig.motionAcceleration = 10_000; + azimuthConfig.velocityMeasurementWindow = 64; + azimuthConfig.velocityMeasurementPeriod = SensorVelocityMeasPeriod.Period_100Ms; + azimuthConfig.voltageCompSaturation = 12; + azimuthConfig.voltageMeasurementFilter = 32; + azimuthConfig.neutralDeadband = 0.04; + return azimuthConfig; + } + + public static TalonFXConfiguration getDriveTalonConfig() { + TalonFXConfiguration driveConfig = new TalonFXConfiguration(); + + CurrentLimitsConfigs currentConfig = new CurrentLimitsConfigs(); + currentConfig.SupplyCurrentLimit = 60; + + currentConfig.StatorCurrentLimit = 140; + + currentConfig.SupplyCurrentLimitEnable = true; + currentConfig.StatorCurrentLimitEnable = true; + + driveConfig.CurrentLimits = currentConfig; + + Slot0Configs slot0Config = new Slot0Configs(); + slot0Config.kP = 0.5; // 0.16 using phoenix 6 migrate + slot0Config.kI = 0.5; // 0.0002 using phoenix 6 migrate + slot0Config.kD = 0.0; + slot0Config.kV = 0.12; // 0.047 using phoenix 6 migrate + driveConfig.Slot0 = slot0Config; + + MotorOutputConfigs motorConfigs = new MotorOutputConfigs(); + motorConfigs.DutyCycleNeutralDeadband = 0.01; + motorConfigs.NeutralMode = NeutralModeValue.Brake; + driveConfig.MotorOutput = motorConfigs; + + return driveConfig; + } + + public static final int kPigeonCanID = 1000; // fix later + + public static Pigeon2Configuration getPigeon2Configuration() { + Pigeon2Configuration config = new Pigeon2Configuration(); + + config.MountPose.MountPoseYaw = -90.0; + config.MountPose.MountPoseRoll = 0.0; + config.MountPose.MountPosePitch = 0.0; + + config.GyroTrim.GyroScalarX = 0.0; + config.GyroTrim.GyroScalarY = 0.0; + config.GyroTrim.GyroScalarZ = -2.12; + + return config; + } + + public static CurrentLimitsConfigs getSafeDriveLimits() { + CurrentLimitsConfigs currentConfig = new CurrentLimitsConfigs(); + currentConfig.SupplyCurrentLimit = 30; + currentConfig.SupplyCurrentLimitEnable = true; + currentConfig.StatorCurrentLimitEnable = false; + return currentConfig; + } + + public static CurrentLimitsConfigs getNormDriveLimits() { + CurrentLimitsConfigs currentConfig = new CurrentLimitsConfigs(); + currentConfig.SupplyCurrentLimit = 30; + + currentConfig.StatorCurrentLimit = 140; + + currentConfig.SupplyCurrentLimitEnable = true; + currentConfig.StatorCurrentLimitEnable = true; + return currentConfig; + } +} diff --git a/src/main/java/frc/robot/constants/RobotConstants.java b/src/main/java/frc/robot/constants/RobotConstants.java index 823dd676..fe48e79c 100644 --- a/src/main/java/frc/robot/constants/RobotConstants.java +++ b/src/main/java/frc/robot/constants/RobotConstants.java @@ -1,3 +1,5 @@ package frc.robot.constants; -public class RobotConstants {} +public class RobotConstants { + public static final int kTalonConfigTimeout = 10; // ms +} diff --git a/src/main/java/frc/robot/constants/RobotStateConstants.java b/src/main/java/frc/robot/constants/RobotStateConstants.java index 5da79b20..c434fc49 100644 --- a/src/main/java/frc/robot/constants/RobotStateConstants.java +++ b/src/main/java/frc/robot/constants/RobotStateConstants.java @@ -1,3 +1,5 @@ package frc.robot.constants; -public class RobotStateConstants {} +public class RobotStateConstants { + public static final double[] kNodeAngles = {0.0, 60.0, 120.0, 180.0, -120.0, -60.0}; +} diff --git a/src/main/java/frc/robot/constants/TagServoingConstants.java b/src/main/java/frc/robot/constants/TagServoingConstants.java new file mode 100644 index 00000000..840eea2d --- /dev/null +++ b/src/main/java/frc/robot/constants/TagServoingConstants.java @@ -0,0 +1,5 @@ +package frc.robot.constants; + +public class TagServoingConstants { + public static final double kAngleCloseEnough = 3.0; +} diff --git a/src/main/java/frc/robot/constants/VisionConstants.java b/src/main/java/frc/robot/constants/VisionConstants.java new file mode 100644 index 00000000..d1a8cfd8 --- /dev/null +++ b/src/main/java/frc/robot/constants/VisionConstants.java @@ -0,0 +1,100 @@ +package frc.robot.constants; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.util.Units; + +public final class VisionConstants { + public static final double kMaxTimeNoVision = 20; + public static final double kTimeToResetWheelCount = 5; + public static final int kResultsForWheels = 5; + public static final double kTimeToDecayDev = 10; + public static final double kStdDevDecayCoeff = -0.005; + public static final double kMinStdDev = 0.01; + public static final double kMaxAmbig = 1.0; + public static final int kMaxTimesOffWheels = 5; + public static final double kBumperPixelLine = 87; // 100 + + // public static final double kThetaStdDevUsed = Units.degreesToRadians(0.02); + // public static final double kThetaStdDevRejected = Units.degreesToRadians(360); + // public static final double kThetaStdThres = 0.2; + + // Velocity Filter + public static final double kLinearCoeffOnVelFilter = 0.1; + public static final double kOffsetOnVelFilter = 0.10; + public static final double kSquaredCoeffOnVelFilter = 0.1; + + public static Matrix kStateStdDevs = VecBuilder.fill(0.1, 0.1, Units.degreesToRadians(0)); + + public static final double kTimeStampOffset = 0.0; + + // StdDev scaling + public static final double singleTagCoeff = 25.0 / 100.0; + public static final double multiTagCoeff = 18.0 / 100.0; + public static final double baseNumber = Math.E; + public static final double powerNumber = 4.0; + public static final double FOV45MultiTagCoeff = 16.0 / 100.0; + public static final double FOV45powerNumber = 4.5; + public static final double FOV45SinlgeTagCoeff = 21.0 / 100.0; + public static final double FOV58MJPGMultiTagCoeff = 16.0 / 100.0; + public static final double FOV58MJPGPowerNumber = 3.5; + public static final double FOV58MJPGSingleTagCoeff = 21.0 / 100.0; + public static final double FOV58YUYVMultiTagCoeff = 17.0 / 100.0; + public static final double FOV58YUYVPowerNumber = 4.0; + public static final double FOV58YUYVSingleTagCoeff = 22.0 / 100.0; + + // Constants for cameras + public static final int kNumCams = 4; + + // Names + public static final String kCam1Name = "Shooter"; + public static final String kCam2Name = "Intake"; + public static final String kCam3Name = "AngledShooterLeft"; + public static final String kCam4Name = "AngledShooterRight"; + + public static final String kPi1Name = "Shooter"; + public static final String kPi2Name = "Intake"; + public static final String kPi3Name = "AngledShooters"; + + // Indexs + public static final int kCam1Idx = 0; + public static final int kCam2Idx = 0; + public static final int kCam3Idx = 0; + public static final int kCam4Idx = 1; + + public static final double kLoopTime = 0.02; + public static final int kCircularBufferSize = 1000; + // Poses + public static final Pose3d kCam1Pose = + new Pose3d( + new Translation3d(-0.27, 0.055, 0.20), + new Rotation3d(0, Units.degreesToRadians(20.0), Units.degreesToRadians(180.0))); + public static final Pose3d kCam2Pose = + new Pose3d( + new Translation3d(-0.21, -0.31, 0.44), + new Rotation3d(0, Units.degreesToRadians(20.0), Units.degreesToRadians(0.0))); + public static final Pose3d kCam3Pose = + new Pose3d( + new Translation3d(-0.23, 0.33, 0.56), + new Rotation3d(0, Units.degreesToRadians(20.0), Units.degreesToRadians(-132.0))); + public static final Pose3d kCam4Pose = + new Pose3d( + new Translation3d(-0.22, -0.335, 0.50), + new Rotation3d(0, Units.degreesToRadians(20.0), Units.degreesToRadians(138.0))); + + // Increase these numbers to trust sensor readings from encoders and gyros less. This matrix is + // in the form [theta], with units in radians. + public static Matrix kLocalMeasurementStdDevs = + VecBuilder.fill(Units.degreesToRadians(0.01)); + + // Increase these numbers to trust global measurements from vision less. This matrix is in the + // form [x, y, theta]áµ€, with units in meters and radians. + // Vision Odometry Standard devs + public static Matrix kVisionMeasurementStdDevs = + VecBuilder.fill(0.05, 0.05, Units.degreesToRadians(360)); +} diff --git a/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java b/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java index bea51e02..340af28a 100644 --- a/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java +++ b/src/main/java/frc/robot/subsystems/algae/AlgaeSubsystem.java @@ -1,58 +1,3 @@ package frc.robot.subsystems.algae; -import static edu.wpi.first.units.Units.Rotations; - -import edu.wpi.first.units.measure.Angle; -import frc.robot.constants.AlgaeConstants; -import frc.robot.constants.ExampleConstants; -import frc.robot.standards.ClosedLoopPosSubsystem; -import frc.robot.subsystems.algae.algaeIO.IOInputs; - -import java.util.Set; -import org.littletonrobotics.junction.Logger; -import org.strykeforce.telemetry.TelemetryService; -import org.strykeforce.telemetry.measurable.MeasurableSubsystem; -import org.strykeforce.telemetry.measurable.Measure; - -public class AlgaeSubsystem implements ClosedLoopPosSubsystem { - private final algaeIO io; - private final IOInputsAutoLogged inputs = new IOInputsAutoLogged(); - private Angle setpoint = Rotations.of(0.0); - - public AlgaeSubsystem(algaeIO io) { - this.io = io; - } - - public Angle getPosition() { - return getPosition(); - } - - public void setPosition(Angle position) { - setPosition(position); - setpoint = position; - } - - public boolean isFinished() { - return setpoint.minus(getPosition()).abs(Rotations) <= AlgaeConstants.kCloseEnough.in(Rotations); - } - - // Periodic Function - public void periodic() { - io.updateInputs(inputs); - Logger.processInputs(getName(), inputs); - Logger.recordOutput("Algae/setpoint", setpoint.in(Rotations)); - } - - // Grapher - @Override - public void registerWith(TelemetryService telemetryService) { - - super.registerWith(telemetryService); - io.registerWith(telemetryService); - } - - @Override - public Set getMeasures() { - return Set.of(new Measure("State", () -> curState.ordinal())); - } -} \ No newline at end of file +public class AlgaeSubsystem {} diff --git a/src/main/java/frc/robot/subsystems/algae/algaeIO.java b/src/main/java/frc/robot/subsystems/algae/algaeIO.java index 52f04533..3fb9d43f 100644 --- a/src/main/java/frc/robot/subsystems/algae/algaeIO.java +++ b/src/main/java/frc/robot/subsystems/algae/algaeIO.java @@ -1,12 +1,11 @@ package frc.robot.subsystems.algae; +import com.ctre.phoenix6.signals.ReverseLimitValue; import edu.wpi.first.units.measure.Angle; import edu.wpi.first.units.measure.AngularVelocity; import org.littletonrobotics.junction.AutoLog; import org.strykeforce.telemetry.TelemetryService; -import com.ctre.phoenix6.signals.ReverseLimitValue; - public interface algaeIO { @AutoLog public static class IOInputs { diff --git a/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java b/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java index 58142983..3f28d28a 100644 --- a/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java +++ b/src/main/java/frc/robot/subsystems/algae/algaeIOFX.java @@ -1,64 +1,62 @@ package frc.robot.subsystems.algae; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.strykeforce.telemetry.TelemetryService; - -import com.ctre.phoenix.motorcontrol.ControlMode; import com.ctre.phoenix6.StatusSignal; import com.ctre.phoenix6.configs.TalonFXConfigurator; import com.ctre.phoenix6.controls.MotionMagicDutyCycle; import com.ctre.phoenix6.hardware.TalonFX; import com.ctre.phoenix6.signals.ReverseLimitValue; - import edu.wpi.first.units.measure.Angle; import edu.wpi.first.units.measure.AngularVelocity; import frc.robot.constants.AlgaeConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.strykeforce.telemetry.TelemetryService; /** Implementation of the algaeIO interface using TalonFX motors. */ public class algaeIOFX implements algaeIO { - private Logger logger; - private final TalonFX talonFX; - private StatusSignal currVelocity; - private StatusSignal reverseLimitSwitch; - private StatusSignal currAngle; - - // FX Access objects - TalonFXConfigurator configurator; - private MotionMagicDutyCycle positionRequest = - new MotionMagicDutyCycle(0).withEnableFOC(false).withFeedForward(0).withSlot(0); - - // Constructor to initialize the TalonFX motor controller - public algaeIOFX() { - logger = LoggerFactory.getLogger(this.getClass()); - talonFX = new TalonFX(AlgaeConstants.kFxId); - reverseLimitSwitch = talonFX.getReverseLimit(); - currVelocity = talonFX.getVelocity(); - currAngle = talonFX.getPosition(); - } - - @Override - public void updateInputs(IOInputs inputs) { - // inputs.reverseLimitSwitch = reverseLimitSwitch.refresh().getValue().value == 1; - inputs.velocity = currVelocity.refresh().getValue(); - inputs.location = currAngle.refresh().getValue(); - } - - @Override - public void setPosition(double position) { - talonFX.setPosition(position); - } - public Angle getPosition() { - return currAngle.getValue(); - } - - @Override - public void zero() { - // not doing one for now - } - - @Override - public void registerWith(TelemetryService telemetryService) { - telemetryService.register(talonFX, true); - } + private Logger logger; + private final TalonFX talonFX; + private StatusSignal currVelocity; + private StatusSignal reverseLimitSwitch; + private StatusSignal currAngle; + + // FX Access objects + TalonFXConfigurator configurator; + private MotionMagicDutyCycle positionRequest = + new MotionMagicDutyCycle(0).withEnableFOC(false).withFeedForward(0).withSlot(0); + + // Constructor to initialize the TalonFX motor controller + public algaeIOFX() { + logger = LoggerFactory.getLogger(this.getClass()); + talonFX = new TalonFX(AlgaeConstants.kFxId); + reverseLimitSwitch = talonFX.getReverseLimit(); + currVelocity = talonFX.getVelocity(); + currAngle = talonFX.getPosition(); + } + + @Override + public void updateInputs(IOInputs inputs) { + // inputs.reverseLimitSwitch = reverseLimitSwitch.refresh().getValue().value == 1; + inputs.velocity = currVelocity.refresh().getValue(); + inputs.location = currAngle.refresh().getValue(); + } + + @Override + public void setPosition(double position) { + talonFX.setPosition(position); + } + + public Angle getPosition() { + return currAngle.getValue(); + } + + @Override + public void zero() { + // not doing one for now + } + + @Override + public void registerWith(TelemetryService telemetryService) { + telemetryService.register(talonFX, true); + } } diff --git a/src/main/java/frc/robot/subsystems/biscuit/BiscuitIO.java b/src/main/java/frc/robot/subsystems/biscuit/BiscuitIO.java index 45a776f2..02d8743e 100644 --- a/src/main/java/frc/robot/subsystems/biscuit/BiscuitIO.java +++ b/src/main/java/frc/robot/subsystems/biscuit/BiscuitIO.java @@ -1,3 +1,28 @@ package frc.robot.subsystems.biscuit; -public class BiscuitIO {} +import static edu.wpi.first.units.Units.Rotations; +import static edu.wpi.first.units.Units.RotationsPerSecond; + +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import org.littletonrobotics.junction.AutoLog; +import org.strykeforce.telemetry.TelemetryService; + +public interface BiscuitIO { + + @AutoLog + public class BiscuitIOInputs { + public Angle position = Rotations.of(0); + public AngularVelocity velocity = RotationsPerSecond.of(0); + public boolean fwdLimitSwitchOpen = false; + public boolean didZero; + } + + public default void setPosition(Angle position) {} + + public default void updateInputs(BiscuitIOInputs inputs) {} + + public default void registerWith(TelemetryService telemetry) {} + + public default void zero() {} +} diff --git a/src/main/java/frc/robot/subsystems/biscuit/BiscuitIOFX.java b/src/main/java/frc/robot/subsystems/biscuit/BiscuitIOFX.java index 2c5a726e..e519e701 100644 --- a/src/main/java/frc/robot/subsystems/biscuit/BiscuitIOFX.java +++ b/src/main/java/frc/robot/subsystems/biscuit/BiscuitIOFX.java @@ -1,3 +1,83 @@ package frc.robot.subsystems.biscuit; -public class BiscuitIOFX {} +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.controls.MotionMagicDutyCycle; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.ForwardLimitTypeValue; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.Alert; +import edu.wpi.first.wpilibj.Alert.AlertType; +import frc.robot.constants.BiscuitConstants; +import frc.robot.subsystems.biscuit.BiscuitIO.BiscuitIOInputs; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.strykeforce.telemetry.TelemetryService; + +public class BiscuitIOFX implements BiscuitIO { + + private Logger logger; + private TalonFX talon; + + private final Angle sensorInitial; + private StatusSignal position; + private StatusSignal velocity; + private StatusSignal fwdLimitSwitch; + private boolean didZero; + private boolean fwdLimitSwitchOpen; + private Angle offset; + private Alert rangeAlert = new Alert("Biscuit overextended! Shuting down!", AlertType.kError); + + TalonFXConfigurator configurator; + private MotionMagicDutyCycle positionRequest = + new MotionMagicDutyCycle(0).withEnableFOC(false).withFeedForward(0); + + public BiscuitIOFX() { + // Logger initialization with class name + logger = LoggerFactory.getLogger(this.getClass()); + // Moter initialization with ID from constants + talon = new TalonFX(BiscuitConstants.talonID); + // Set the starting encoder position + sensorInitial = talon.getPosition().getValue(); + + // Reset and configure moter settings + configurator = talon.getConfigurator(); + configurator.apply(new TalonFXConfiguration()); + configurator.apply(BiscuitConstants.getFXConfig()); + // Set our variables + velocity = talon.getVelocity(); + position = talon.getPosition(); + } + + public void setPosition(Angle position) { + talon.setControl(positionRequest.withPosition(position)); + } + + public void updateInputs(BiscuitIOInputs inputs) { + BaseStatusSignal.refreshAll(velocity, position, fwdLimitSwitch); + inputs.velocity = velocity.getValue(); + inputs.position = position.getValue(); + inputs.fwdLimitSwitchOpen = fwdLimitSwitch.getValueAsDouble() == 1; + inputs.didZero = didZero; + } + + public void registerWith(TelemetryService telemetry) { + telemetry.register(talon, true); + } + + public void zero() { + didZero = false; + if (fwdLimitSwitchOpen == true) { + Angle pos = position.getValue(); + offset = BiscuitConstants.kZero.minus(pos); + didZero = true; + } else { + rangeAlert.set(true); + logger.error("Biscuit overextended! Shutting down movement!"); + configurator.apply(BiscuitConstants.disableTalon()); + } + } +} diff --git a/src/main/java/frc/robot/subsystems/biscuit/BiscuitSubsystem.java b/src/main/java/frc/robot/subsystems/biscuit/BiscuitSubsystem.java index 213d3f21..d6e69c31 100644 --- a/src/main/java/frc/robot/subsystems/biscuit/BiscuitSubsystem.java +++ b/src/main/java/frc/robot/subsystems/biscuit/BiscuitSubsystem.java @@ -1,3 +1,62 @@ +// Blessed by the great tech-priests of the Adeptus Mechanicus + package frc.robot.subsystems.biscuit; -public class BiscuitSubsystem {} +import static edu.wpi.first.units.Units.Rotations; + +import edu.wpi.first.units.measure.*; +import frc.robot.constants.BiscuitConstants; +import java.util.Set; +import org.littletonrobotics.junction.Logger; +import org.strykeforce.telemetry.TelemetryService; +import org.strykeforce.telemetry.measurable.MeasurableSubsystem; +import org.strykeforce.telemetry.measurable.Measure; + +public class BiscuitSubsystem extends MeasurableSubsystem { + + private BiscuitIO io; + private BiscuitIOInputsAutoLogged inputs = new BiscuitIOInputsAutoLogged(); + private Angle setPoint; + + public void setPosition(Angle position) { + io.setPosition(position); + setPoint = position; + } + + public Angle getPosition(Angle position) { + return inputs.position; + } + + public AngularVelocity getVelocity(AngularVelocity velocity) { + return inputs.velocity; + } + + @Override + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(getName(), inputs); + Logger.recordOutput("Biscuit setPoint", setPoint); + Logger.recordOutput("Is Biscuit Finished", isFinished() ? 1.0 : 0.0); + } + + @Override + public void registerWith(TelemetryService telemetry) { + super.registerWith(telemetry); + io.registerWith(telemetry); + } + + public boolean isFinished() { + return setPoint.minus(inputs.position).abs(Rotations) <= BiscuitConstants.kCloseEnough; + } + + @Override + public Set getMeasures() { + return Set.of( + new Measure("Is Biscuit Finished", () -> isFinished() ? 1.0 : 0.0), + new Measure("Biscuit Set Point", () -> setPoint.in(Rotations))); + } + + public void zero() { + io.zero(); + } +} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java index 8d8b2e71..0c20dace 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -1,8 +1,7 @@ package frc.robot.subsystems.coral; -import static edu.wpi.first.units.Units.DegreesPerSecond; +import static edu.wpi.first.units.Units.*; -import com.ctre.phoenix6.BaseStatusSignal; import com.ctre.phoenix6.StatusSignal; import com.ctre.phoenix6.configs.TalonFXSConfiguration; import com.ctre.phoenix6.configs.TalonFXSConfigurator; @@ -17,8 +16,9 @@ import org.strykeforce.telemetry.TelemetryService; public class CoralIOFX implements CoralIO { + // private objects private Logger logger; - private TalonFXS talonFxs; + private TalonFXS talonFx; // FX Access objects TalonFXSConfigurator configurator; @@ -30,50 +30,51 @@ public class CoralIOFX implements CoralIO { public CoralIOFX() { logger = LoggerFactory.getLogger(this.getClass()); - talonFxs = new TalonFXS(CoralConstants.kCoralFxId); + talonFx = new TalonFXS(CoralConstants.kCoralFxId); // Config controller - configurator = talonFxs.getConfigurator(); + configurator = talonFx.getConfigurator(); configurator.apply(new TalonFXSConfiguration()); // Factory default motor controller configurator.apply(CoralConstants.getFXConfig()); // Attach status signals - curVelocity = talonFxs.getVelocity(); - fwdLimitSwitch = talonFxs.getForwardLimit(); - revLimitSwitch = talonFxs.getReverseLimit(); + curVelocity = talonFx.getVelocity(); + fwdLimitSwitch = talonFx.getForwardLimit(); + revLimitSwitch = talonFx.getReverseLimit(); } @Override public void setVelocity(AngularVelocity velocity) { - logger.info("Setting velocity to {} degrees per second", velocity.in(DegreesPerSecond)); + logger.info("Setting velocity to {} rots per second", velocity.in(RotationsPerSecond)); - talonFxs.setControl(velocityRequest.withVelocity(velocity)); + talonFx.setControl(velocityRequest.withVelocity(velocity)); } @Override public void enableFwdLimitSwitch(boolean enabled) { - talonFxs + talonFx .getConfigurator() .apply(CoralConstants.getFXConfig().HardwareLimitSwitch.withForwardLimitEnable(enabled)); } @Override public void enableRevLimitSwitch(boolean enabled) { - talonFxs + talonFx .getConfigurator() .apply(CoralConstants.getFXConfig().HardwareLimitSwitch.withReverseLimitEnable(enabled)); } @Override public void updateInputs(CoralIOInputs inputs) { - BaseStatusSignal.refreshAll(curVelocity, fwdLimitSwitch, revLimitSwitch); - inputs.velocity = curVelocity.getValue(); - inputs.isFwdLimitSwitchClosed = fwdLimitSwitch.refresh().getValue().value == 1; - inputs.isRevLimitSwitchClosed = revLimitSwitch.refresh().getValue().value == 0; + inputs.velocity = curVelocity.refresh().getValue(); + inputs.isFwdLimitSwitchClosed = + fwdLimitSwitch.refresh().getValue().value == 1; // FIXME check right value + inputs.isRevLimitSwitchClosed = + revLimitSwitch.refresh().getValue().value == 0; // FIXME check right value } @Override public void registerWith(TelemetryService telemetryService) { - telemetryService.register(talonFxs, true); + telemetryService.register(talonFx, true); } } diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index 1f4c1e59..f7c2c27f 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -7,6 +7,7 @@ import frc.robot.standards.ClosedLoopSpeedSubsystem; import java.util.Set; import org.littletonrobotics.junction.Logger; +import org.slf4j.LoggerFactory; import org.strykeforce.telemetry.TelemetryService; import org.strykeforce.telemetry.measurable.MeasurableSubsystem; import org.strykeforce.telemetry.measurable.Measure; @@ -15,7 +16,9 @@ public class CoralSubsystem extends MeasurableSubsystem implements ClosedLoopSpe private final CoralIO io; private final CoralIOInputsAutoLogged inputs = new CoralIOInputsAutoLogged(); private AngularVelocity setpoint = RotationsPerSecond.of(0.0); - private CoralState curState = CoralState.INIT; + private CoralState curState; + ; + private org.slf4j.Logger logger = LoggerFactory.getLogger(CoralSubsystem.class); public CoralSubsystem(CoralIO io) { this.io = io; @@ -30,6 +33,11 @@ public AngularVelocity getSpeed() { return inputs.velocity; } + public void setState(CoralState state) { + logger.info("{} -> {}", curState, state); + this.curState = state; + } + @Override public void setSpeed(AngularVelocity speed) { setpoint = speed; @@ -42,8 +50,24 @@ public boolean atSpeed() { <= CoralConstants.kCloseEnough.in(RotationsPerSecond); } - public boolean isBeamBroken() { - return false; // FIXME when we get the robot + public boolean isEnterBeamBroken() { + return inputs.isFwdLimitSwitchClosed; // FIXME correct one? + } + + public boolean isExitBeamBroken() { + return inputs.isRevLimitSwitchClosed; // FIXME correct one? + } + + public void intake() { + io.enableRevLimitSwitch(true); + setSpeed(CoralConstants.kIntakingSpeed); + setState(CoralState.INTAKING); + } + + public void eject() { + io.enableRevLimitSwitch(false); + setSpeed(CoralConstants.kEjectingSpeed); + setState(CoralState.EJECTING); } // Periodic Function @@ -51,15 +75,29 @@ public boolean isBeamBroken() { public void periodic() { // Read Inputs io.updateInputs(inputs); - + Logger.processInputs(getName(), inputs); + // State Machine switch (curState) { - case INIT: - break; - case ZEROED: - break; - default: - break; + case IDLE -> {} + case INTAKING -> { + if (isEnterBeamBroken()) { + setState(CoralState.CORAL_LOADING); + } + } + case CORAL_LOADING -> { + if (isExitBeamBroken()) { + setState(CoralState.HAS_CORAL); + } + } + case HAS_CORAL -> {} + case EJECTING -> { + if (!isExitBeamBroken()) { + setState(CoralState.EMPTY); + } + } + case EMPTY -> {} + default -> {} } // Log Outputs @@ -79,7 +117,11 @@ public Set getMeasures() { } public enum CoralState { - INIT, - ZEROED + IDLE, + HAS_CORAL, + CORAL_LOADING, + INTAKING, + EJECTING, + EMPTY } } diff --git a/src/main/java/frc/robot/subsystems/drive/DriveSubsystem.java b/src/main/java/frc/robot/subsystems/drive/DriveSubsystem.java index 79728e76..79282da4 100644 --- a/src/main/java/frc/robot/subsystems/drive/DriveSubsystem.java +++ b/src/main/java/frc/robot/subsystems/drive/DriveSubsystem.java @@ -1,3 +1,384 @@ package frc.robot.subsystems.drive; -public class DriveSubsystem {} +import choreo.trajectory.SwerveSample; +import choreo.trajectory.Trajectory; +import edu.wpi.first.math.controller.HolonomicDriveController; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import frc.robot.constants.DriveConstants; +import java.util.Set; +import java.util.function.BooleanSupplier; +import net.jafama.FastMath; +import org.littletonrobotics.junction.Logger; +import org.slf4j.LoggerFactory; +import org.strykeforce.telemetry.measurable.MeasurableSubsystem; +import org.strykeforce.telemetry.measurable.Measure; + +public class DriveSubsystem extends MeasurableSubsystem { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(DriveSubsystem.class); + private final SwerveIO io; + private SwerveIOInputsAutoLogged inputs = new SwerveIOInputsAutoLogged(); + private final HolonomicDriveController holonomicController; + + private final ProfiledPIDController omegaController; + private final PIDController xController; + private final PIDController yController; + + public DriveStates currDriveState = DriveStates.IDLE; + private ChassisSpeeds holoContOutput = new ChassisSpeeds(); + private SwerveSample holoContInput = + new SwerveSample( + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, new double[4], new double[4]); + private Trajectory autoTrajectory; + private double trajectoryActive = 0.0; + + private int gyroDifferentCount = 0; + + public DriveSubsystem(SwerveIO io) { + org.littletonrobotics.junction.Logger.recordOutput("Swerve/YVelSpeed", 0.0); + org.littletonrobotics.junction.Logger.recordOutput("Swerve/UsingDeadEye", false); + org.littletonrobotics.junction.Logger.recordOutput("Swerve/Auto Drive Info", "Nothing"); + + this.io = io; + // Setup Holonomic Controller + omegaController = + new ProfiledPIDController( + DriveConstants.kPOmega, + DriveConstants.kIOmega, + DriveConstants.kDOmega, + new TrapezoidProfile.Constraints( + DriveConstants.kMaxVelOmega, DriveConstants.kMaxAccelOmega)); + omegaController.enableContinuousInput(Math.toRadians(-180), Math.toRadians(180)); + + xController = + new PIDController( + DriveConstants.kPHolonomic, DriveConstants.kIHolonomic, DriveConstants.kDHolonomic); + // xController.setIntegratorRange(DriveConstants.kIMin, DriveConstants.kIMax); + yController = + new PIDController( + DriveConstants.kPHolonomic, DriveConstants.kIHolonomic, DriveConstants.kDHolonomic); + // yController.setIntegratorRange(DriveConstants.kIMin, DriveConstants.kIMax); + holonomicController = new HolonomicDriveController(xController, yController, omegaController); + // Disabling the holonomic controller makes the robot directly follow the + // trajectory output (no + // closing the loop on x,y,theta errors) + holonomicController.setEnabled(true); + } + + // Open-Loop Swerve Movements + public void drive(double vXmps, double vYmps, double vOmegaRadps) { + io.drive(vXmps, vYmps, vOmegaRadps, true); + } + + public void setAzimuthVel(double vel) { + io.setAzimuthVel(vel); + } + + // Closed-Loop (Velocity Controlled) Swerve Movement + public void move(double vXmps, double vYmps, double vOmegaRadps, boolean isFieldOriented) { + org.littletonrobotics.junction.Logger.recordOutput("Swerve/Move X", vXmps); + org.littletonrobotics.junction.Logger.recordOutput("Swerve/Move Y", vYmps); + org.littletonrobotics.junction.Logger.recordOutput("Swerve/Move Omega", vOmegaRadps); + io.move(vXmps, vYmps, vOmegaRadps, isFieldOriented); + } + + public void recordYVel(double val) { + org.littletonrobotics.junction.Logger.recordOutput("Swerve/YVelSpeed", val); + } + + // Choreo Holonomic Controller + public void calculateController(SwerveSample desiredState) { + holoContInput = desiredState; + double xFF = desiredState.vx; + double yFF = desiredState.vy; + double rotationFF = desiredState.heading; + + Pose2d pose = inputs.poseMeters; + double xFeedback = xController.calculate(pose.getX(), desiredState.x); + double yFeedback = yController.calculate(pose.getY(), desiredState.y); + double rotationFeedback = + omegaController.calculate(pose.getRotation().getRadians(), desiredState.heading); + + holoContOutput.vxMetersPerSecond = xFF + xFeedback; + holoContOutput.vyMetersPerSecond = yFF + yFeedback; + holoContOutput.omegaRadiansPerSecond = rotationFF + rotationFeedback; + + io.move( + holoContOutput.vxMetersPerSecond, + holoContOutput.vyMetersPerSecond, + holoContOutput.omegaRadiansPerSecond, + false); + } + + // Choreo Holonomic Controller + public void calculateControllerServo(SwerveSample desiredState, double desiredRobotRelvY) { + holoContInput = desiredState; + double xFF = desiredState.vx; + double rotationFF = desiredState.heading; + + Pose2d pose = inputs.poseMeters; + double xFeedback = xController.calculate(pose.getX(), desiredState.x); + double rotationFeedback = + omegaController.calculate(pose.getRotation().getRadians(), desiredState.heading); + + holoContOutput.vxMetersPerSecond = xFF + xFeedback; + holoContOutput.vyMetersPerSecond = desiredRobotRelvY; + holoContOutput.omegaRadiansPerSecond = rotationFF + rotationFeedback; + + io.move( + holoContOutput.vxMetersPerSecond, + holoContOutput.vyMetersPerSecond, + holoContOutput.omegaRadiansPerSecond, + false); + } + + public void resetOdometry(Pose2d pose) { + io.resetOdometry(pose); + logger.info("reset odometry with: {}", pose); + } + + public void recordAutoTrajectory(Trajectory traj) { + autoTrajectory = traj; + } + + public void setAutoDebugMsg(String msg) { + org.littletonrobotics.junction.Logger.recordOutput("Swerve/Auto Drive Info", msg); + } + + public Trajectory getAutoTrajectory() { + if (autoTrajectory != null) { + return autoTrajectory; + } else { + return null; + } + } + + public void resetHolonomicController(double yaw) { + xController.reset(); + yController.reset(); + omegaController.reset(yaw); + holonomicController.getThetaController().reset(yaw); + logger.info("Holonomic Controller Reset: {}", yaw); + } + + public void resetHolonomicController() { + xController.reset(); + yController.reset(); + omegaController.reset(inputs.gyroRotation2d.getRadians()); + holonomicController.getThetaController().reset(inputs.gyroRotation2d.getRadians()); + logger.info("Holonomic Controller Reset: {}", inputs.gyroRotation2d.getRadians()); + } + + public void resetOmegaController() { + omegaController.reset(inputs.gyroRotation2d.getRadians()); + } + + // Getters/Setters + public Pose2d getPoseMeters() { + return inputs.poseMeters; + } + + public Rotation2d getGyroRotation2d() { + return inputs.gyroRotation2d; + } + + public ChassisSpeeds getFieldRelSpeed() { + return inputs.fieldRelSpeed; + } + + public void setDriveState(DriveStates state) { + currDriveState = state; + } + + public BooleanSupplier getAzimuth1FwdLimitSupplier() { + return io.getAzimuth1FwdLimitSwitch(); + } + + public boolean isDriveStill() { + // logger.info( + // "Timestamp Before FieldRel: {}", + // org.littletonrobotics.junction.Logger.getRealTimestamp() / 1000); + ChassisSpeeds cs = inputs.fieldRelSpeed; + double vX = cs.vxMetersPerSecond; + double vY = cs.vyMetersPerSecond; + // logger.info( + // "Timestamp After FieldRel: {}", + // org.littletonrobotics.junction.Logger.getRealTimestamp() / 1000); + + // Take fieldRel Speed and get the magnitude of the vector + double wheelSpeed = FastMath.hypot(vX, vY); + + boolean velStill = Math.abs(wheelSpeed) <= DriveConstants.kSpeedStillThreshold; + boolean gyroStill = isGyroStill(); + + return velStill && gyroStill; + } + + public boolean isGyroStill() { + return Math.abs(inputs.gyroRate) <= DriveConstants.kGyroRateStillThreshold; + } + + public void setGyroOffset(Rotation2d rotation) { + io.setBothGyroOffset(apply(rotation)); + } + + public void setEnableHolo(boolean enabled) { + holonomicController.setEnabled(enabled); + logger.info("Holonomic Controller Enabled: {}", enabled); + } + + public void teleResetGyro() { + logger.info("Driver Joystick: Reset Gyro"); + // double gyroResetDegs = robotStateSubsystem.getAllianceColor() == Alliance.Blue ? 0.0 : 180.0; + double gyroResetDegs = 0.0; // TODO change this to the above once we have RobotStateSubsystem + io.setBothGyroOffset(Rotation2d.fromDegrees(gyroResetDegs)); + io.resetGyro(); + io.resetOdometry( + new Pose2d(inputs.poseMeters.getTranslation(), Rotation2d.fromDegrees(gyroResetDegs))); + } + + // Make whether a trajectory is currently active obvious on grapher + public void grapherTrajectoryActive(Boolean active) { + if (active) trajectoryActive = 1.0; + else trajectoryActive = 0.0; + } + + // Field flipping stuff + public boolean shouldFlip() { + // return robotStateSubsystem.getAllianceColor() == Alliance.Red; + return true; // TODO change this to the above once we have RobotStateSubsystem + } + + public Translation2d apply(Translation2d translation) { + if (shouldFlip()) { + return new Translation2d(DriveConstants.kFieldMaxX - translation.getX(), translation.getY()); + } else { + return translation; + } + } + + public Pose2d apply(Pose2d pose) { + if (shouldFlip()) { + return new Pose2d( + DriveConstants.kFieldMaxX - pose.getX(), + pose.getY(), + new Rotation2d(-pose.getRotation().getCos(), pose.getRotation().getSin())); + } else { + return pose; + } + } + + public Rotation2d apply(Rotation2d rotation) { + logger.info( + "initial target yaw: {}, cos: {}, sin: {}", rotation, rotation.getCos(), rotation.getSin()); + if (shouldFlip()) { + return new Rotation2d(-rotation.getCos(), rotation.getSin()); + } else return rotation; + } + + public double apply(double x) { + logger.info("initial x: {}", x); + if (shouldFlip()) { + return DriveConstants.kFieldMaxX - x; + } else return x; + } + + // Control Methods + public void lockZero() { + Rotation2d rot = Rotation2d.fromDegrees(0.0); + io.setSwerveModuleAngles(rot, rot, rot, rot); + } + + public void toSafeHold() { + setDriveState(DriveStates.SAFE_HOLD); + io.configDriveCurrents(DriveConstants.getSafeDriveLimits()); + } + + public void toIdle() { + setDriveState(DriveStates.IDLE); + io.configDriveCurrents(DriveConstants.getNormDriveLimits()); + } + + public PIDController getxController() { + return xController; + } + + public PIDController getyController() { + return yController; + } + + public ProfiledPIDController getomegaController() { + return omegaController; + } + + public PIDController getomegaControllerNonProfiled() { + return new PIDController( + omegaController.getP(), omegaController.getI(), omegaController.getD()); + } + + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(getName(), inputs); + if (Math.abs(inputs.gyroRotation2d.getDegrees() - inputs.navxRotation2d.getDegrees()) + > DriveConstants.kGyroDifferentThreshold) { + gyroDifferentCount++; + } else { + gyroDifferentCount = 0; + } + if (gyroDifferentCount > DriveConstants.kGyroDifferentCount && isDriveStill()) { + io.setPigeonGyroOffset(inputs.navxRotation2d); + gyroDifferentCount = 0; + } + + switch (currDriveState) { + case IDLE: + break; + case SAFE: + break; + case SAFE_HOLD: + break; + default: + break; + } + } + + public enum DriveStates { + IDLE, + SAFE, + SAFE_HOLD + } + + @Override + public Set getMeasures() { + return Set.of( + new Measure("State", () -> currDriveState.ordinal()), + new Measure("Gyro roll", () -> inputs.gyroRoll), + new Measure("Gyro pitch", () -> inputs.gyroPitch), + new Measure("Gyro Rotation2D(deg)", () -> inputs.gyroRotation2d.getDegrees()), + new Measure("Odometry X", () -> inputs.poseMeters.getX()), + new Measure("Odometry Y", () -> inputs.poseMeters.getY()), + new Measure("Odometry Rotation2D(deg)", () -> inputs.poseMeters.getRotation().getDegrees()), + new Measure("Trajectory Vel X", () -> holoContInput.vx), + new Measure("Trajectory Vel Y", () -> holoContInput.vy), + new Measure("Trajectory Accel X", () -> holoContInput.ax), + new Measure("Trajectory Accel Y", () -> holoContInput.ay), + new Measure("Trajectory X", () -> holoContInput.x), + new Measure("Trajectory Y", () -> holoContInput.y), + new Measure("Trajectory Rotation2D(deg)", () -> holoContInput.heading), + new Measure("Holonomic Cont Vx", () -> holoContOutput.vxMetersPerSecond), + new Measure("Holonomic Cont Vy", () -> holoContOutput.vyMetersPerSecond), + new Measure("Holonomic Cont Vomega", () -> holoContOutput.omegaRadiansPerSecond), + new Measure( + "Holo Controller Omega Err", + () -> holonomicController.getThetaController().getPositionError()), + new Measure( + "Holo Controller Omega Setpoint", + () -> holonomicController.getThetaController().getSetpoint().position), + new Measure("Trajectory Active", () -> trajectoryActive)); + } +} diff --git a/src/main/java/frc/robot/subsystems/drive/Swerve.java b/src/main/java/frc/robot/subsystems/drive/Swerve.java index bd271064..a68aa4ea 100644 --- a/src/main/java/frc/robot/subsystems/drive/Swerve.java +++ b/src/main/java/frc/robot/subsystems/drive/Swerve.java @@ -1,3 +1,271 @@ package frc.robot.subsystems.drive; -public class Swerve {} +import com.ctre.phoenix.motorcontrol.NeutralMode; +import com.ctre.phoenix.motorcontrol.TalonSRXControlMode; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import com.ctre.phoenix6.configs.CurrentLimitsConfigs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import frc.robot.constants.DriveConstants; +import frc.robot.constants.RobotConstants; +import frc.robot.constants.VisionConstants; +import java.util.function.BooleanSupplier; +import net.jafama.FastMath; +import org.strykeforce.gyro.SF_AHRS; +import org.strykeforce.gyro.SF_PIGEON2; +import org.strykeforce.healthcheck.Checkable; +import org.strykeforce.healthcheck.HealthCheck; +import org.strykeforce.swerve.OdometryStrategy; +import org.strykeforce.swerve.PoseEstimatorOdometryStrategy; +import org.strykeforce.swerve.SwerveDrive; +import org.strykeforce.swerve.SwerveModule; +import org.strykeforce.swerve.V6TalonSwerveModule; +import org.strykeforce.swerve.V6TalonSwerveModule.ClosedLoopUnits; +import org.strykeforce.telemetry.TelemetryService; + +public class Swerve implements SwerveIO, Checkable { + @HealthCheck private final SwerveDrive swerveDrive; + + // Grapher stuff + private PoseEstimatorOdometryStrategy odometryStrategy; + + private SF_PIGEON2 pigeon; + private SF_AHRS navx; + private Rotation2d navxOffset = new Rotation2d(); + + private TalonFXConfigurator configurator; + + private BooleanSupplier azimuth1FwdLimitSupplier = () -> false; + + private TalonSRX[] azimuths = new TalonSRX[4]; + private TalonFX[] drives = new TalonFX[4]; + + private V6TalonSwerveModule[] swerveModules; + private SwerveDriveKinematics kinematics; + private double fieldY = 0.0; + private double fieldX = 0.0; + + public Swerve() { + + var moduleBuilder = + new V6TalonSwerveModule.V6Builder() + .driveGearRatio(DriveConstants.kDriveGearRatio) + .wheelDiameterInches(DriveConstants.kWheelDiameterInches) + .driveMaximumMetersPerSecond(DriveConstants.kMaxSpeedMetersPerSecond) + .latencyCompensation(true); + + swerveModules = new V6TalonSwerveModule[4]; + Translation2d[] wheelLocations = DriveConstants.getWheelLocationMeters(); + + for (int i = 0; i < 4; i++) { + var azimuthTalon = new TalonSRX(i); + azimuths[i] = azimuthTalon; + azimuthTalon.configFactoryDefault(RobotConstants.kTalonConfigTimeout); + azimuthTalon.configAllSettings( + DriveConstants.getAzimuthTalonConfig(), RobotConstants.kTalonConfigTimeout); + azimuthTalon.enableCurrentLimit(true); + azimuthTalon.enableVoltageCompensation(true); + azimuthTalon.setNeutralMode(NeutralMode.Coast); + + var driveTalon = new TalonFX(i + 10); + drives[i] = driveTalon; + configurator = driveTalon.getConfigurator(); + configurator.apply(new TalonFXConfiguration()); // factory default + configurator.apply(DriveConstants.getDriveTalonConfig()); + driveTalon.getSupplyVoltage().setUpdateFrequency(100); + driveTalon.getSupplyCurrent().setUpdateFrequency(100); + driveTalon.getClosedLoopReference().setUpdateFrequency(200); + + swerveModules[i] = + moduleBuilder + .azimuthTalon(azimuthTalon) + .driveTalon(driveTalon) + .wheelLocationMeters(wheelLocations[i]) + .closedLoopUnits(ClosedLoopUnits.VOLTAGE) + .build(); + swerveModules[i].loadAndSetAzimuthZeroReference(); + } + + pigeon = new SF_PIGEON2(DriveConstants.kPigeonCanID, "*"); + pigeon.applyConfig(DriveConstants.getPigeon2Configuration()); + navx = new SF_AHRS(); + swerveDrive = new SwerveDrive(false, 0.02, pigeon, swerveModules); + swerveDrive.resetGyro(); + swerveDrive.setGyroOffset(Rotation2d.fromDegrees(0)); + + kinematics = swerveDrive.getKinematics(); + + odometryStrategy = + new PoseEstimatorOdometryStrategy( + swerveDrive.getHeading(), + new Pose2d(), + swerveDrive.getKinematics(), + VisionConstants.kStateStdDevs, + VisionConstants.kLocalMeasurementStdDevs, + VisionConstants.kVisionMeasurementStdDevs, + getSwerveModulePositions()); + + swerveDrive.setOdometry(odometryStrategy); + } + + // Getters/Setter + @Override + public String getName() { + return "Swerve"; + } + + private SwerveModule[] getSwerveModules() { + return swerveDrive.getSwerveModules(); + } + + public void setSwerveModuleAngles(Rotation2d FL, Rotation2d FR, Rotation2d BL, Rotation2d BR) { + swerveModules[0].setAzimuthRotation2d(FL); + swerveModules[1].setAzimuthRotation2d(FR); + swerveModules[2].setAzimuthRotation2d(BL); + swerveModules[3].setAzimuthRotation2d(BR); + } + + private SwerveModulePosition[] getSwerveModulePositions() { + SwerveModule[] swerveModules = getSwerveModules(); + SwerveModulePosition[] temp = {null, null, null, null}; + for (int i = 0; i < 4; ++i) { + temp[i] = swerveModules[i].getPosition(); + } + return temp; + } + + public SwerveModuleState[] getSwerveModuleStates() { + V6TalonSwerveModule[] swerveModules = (V6TalonSwerveModule[]) swerveDrive.getSwerveModules(); + SwerveModuleState[] swerveModuleStates = new SwerveModuleState[4]; + for (int i = 0; i < 4; i++) { + swerveModuleStates[i] = swerveModules[i].getState(); + } + return swerveModuleStates; + } + + private ChassisSpeeds getRobotRelSpeed() { + SwerveDriveKinematics kinematics = swerveDrive.getKinematics(); + SwerveModule[] swerveModules = swerveDrive.getSwerveModules(); + SwerveModuleState[] swerveModuleStates = new SwerveModuleState[4]; + for (int i = 0; i < 4; ++i) { + swerveModuleStates[i] = swerveModules[i].getState(); + } + return kinematics.toChassisSpeeds(swerveModuleStates); + } + + private ChassisSpeeds getFieldRelSpeed() { + // SwerveDriveKinematics kinematics = swerveDrive.getKinematics(); + // SwerveModule[] swerveModules = swerveDrive.getSwerveModules(); + SwerveModuleState[] swerveModuleStates = new SwerveModuleState[4]; + for (int i = 0; i < 4; ++i) { + swerveModuleStates[i] = swerveModules[i].getState(); + } + ChassisSpeeds roboRelSpeed = kinematics.toChassisSpeeds(swerveModuleStates); + + Rotation2d heading = swerveDrive.getHeading().unaryMinus(); + fieldX = + roboRelSpeed.vxMetersPerSecond * heading.getCos() + + roboRelSpeed.vyMetersPerSecond * heading.getSin(); + fieldY = + -roboRelSpeed.vxMetersPerSecond * heading.getSin() + + roboRelSpeed.vyMetersPerSecond * heading.getCos(); + + return new ChassisSpeeds(fieldX, fieldY, roboRelSpeed.omegaRadiansPerSecond); + } + + public void setOdometry(OdometryStrategy Odom) { + swerveDrive.setOdometry(Odom); + } + + public void setPigeonGyroOffset(Rotation2d rotation) { + swerveDrive.setGyroOffset(rotation); + } + + public void setBothGyroOffset(Rotation2d rotation) { + swerveDrive.setGyroOffset(rotation); + navxOffset = rotation; + } + + public void resetGyro() { + swerveDrive.resetGyro(); + navx.reset(); + } + + public void updateSwerve() { + swerveDrive.periodic(); + } + + public void resetOdometry(Pose2d pose) { + swerveDrive.resetOdometry(pose); + navx.reset(); + } + + public void addVisionMeasurement(Pose2d pose, double timestamp) { + odometryStrategy.addVisionMeasurement(pose, timestamp); + } + + public void addVisionMeasurement(Pose2d pose2d, double timestamp, Matrix stdDevs) { + odometryStrategy.addVisionMeasurement(pose2d, timestamp, stdDevs); + } + + public void drive(double vXmps, double vYmps, double vOmegaRadps, boolean isFieldOriented) { + swerveDrive.drive(vXmps, vYmps, vOmegaRadps, isFieldOriented); + } + + public void move(double vXmps, double vYmps, double vOmegaRadps, boolean isFieldOriented) { + swerveDrive.move(vXmps, vYmps, vOmegaRadps, isFieldOriented); + } + + public void setAzimuthVel(double vel) { + for (int i = 0; i < 4; i++) { + azimuths[i].set(TalonSRXControlMode.PercentOutput, vel); + } + } + + public void configDriveCurrents(CurrentLimitsConfigs config) { + for (int i = 0; i < 4; i++) { + drives[i].getConfigurator().apply(config); + } + } + + @Override + public void updateInputs(SwerveIOInputs inputs) { + inputs.odometryX = swerveDrive.getPoseMeters().getX(); + inputs.odometryY = swerveDrive.getPoseMeters().getY(); + inputs.odometryRotation2D = swerveDrive.getPoseMeters().getRotation().getDegrees(); + inputs.gyroRotation2d = swerveDrive.getHeading(); + inputs.navxRotation2d = navx.getRotation2d().rotateBy(navxOffset); + inputs.normalizedGyroRotation = + FastMath.toDegrees(FastMath.normalizeZeroTwoPi(swerveDrive.getHeading().getRadians())); + inputs.gyroPitch = pigeon.getPitch(); + inputs.gyroRoll = pigeon.getRoll(); + inputs.gyroRate = swerveDrive.getGyroRate(); + inputs.isConnected = pigeon.getPigeon2().getUpTime().hasUpdated(); + inputs.poseMeters = swerveDrive.getPoseMeters(); + inputs.pigeonTemp = pigeon.getPigeon2().getTemperature().getValueAsDouble(); + for (int i = 0; i < 4; ++i) { + inputs.azimuthVels[i] = azimuths[i].getSelectedSensorVelocity(); + inputs.azimuthCurrent[i] = azimuths[i].getSupplyCurrent(); + } + inputs.fieldRelSpeed = getFieldRelSpeed(); + inputs.fieldY = fieldY; + inputs.fieldX = fieldX; + } + + @Override + public void registerWith(TelemetryService telemetryService) { + swerveDrive.registerWith(telemetryService); + pigeon.registerWith(telemetryService); + } +} diff --git a/src/main/java/frc/robot/subsystems/drive/SwerveIO.java b/src/main/java/frc/robot/subsystems/drive/SwerveIO.java index 66439ffa..e4cef1fe 100644 --- a/src/main/java/frc/robot/subsystems/drive/SwerveIO.java +++ b/src/main/java/frc/robot/subsystems/drive/SwerveIO.java @@ -1,3 +1,98 @@ package frc.robot.subsystems.drive; -public class SwerveIO {} +import com.ctre.phoenix6.configs.CurrentLimitsConfigs; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import java.util.function.BooleanSupplier; +import org.littletonrobotics.junction.AutoLog; +import org.strykeforce.swerve.OdometryStrategy; +import org.strykeforce.swerve.SwerveModule; +import org.strykeforce.telemetry.TelemetryService; + +public interface SwerveIO { + + @AutoLog + public static class SwerveIOInputs { + public double odometryX = 0.0; + public double odometryY = 0.0; + public double odometryRotation2D = 0.0; + public Rotation2d gyroRotation2d = new Rotation2d(); + public Rotation2d navxRotation2d = new Rotation2d(); + public double normalizedGyroRotation = 0.0; + public double gyroPitch = 0.0; + public double gyroRoll = 0.0; + public double gyroRate = 0.0; + public boolean isConnected = false; + public Pose2d poseMeters = new Pose2d(); + public double pigeonTemp = 0; + public double fieldX = 0; + public double fieldY = 0; + public ChassisSpeeds fieldRelSpeed = new ChassisSpeeds(); + public double[] azimuthVels = {0, 0, 0, 0}; + public double[] azimuthCurrent = {0, 0, 0, 0}; + } + + private SwerveModule[] getSwerveModules() { + return null; + } + + public default void setSwerveModuleAngles( + Rotation2d FL, Rotation2d FR, Rotation2d BL, Rotation2d BR) {} + + private SwerveModulePosition[] getSwerveModulePositions() { + return null; + } + + private SwerveModuleState[] getSwerveModuleStates() { + return null; + } + + private ChassisSpeeds getRobotRelSpeed() { + return null; + } + + public default SwerveDriveKinematics getKinematics() { + return null; + } + + public default void setOdometry(OdometryStrategy Odom) {} + + public default void setPigeonGyroOffset(Rotation2d rotation) {} + + public default void setBothGyroOffset(Rotation2d rotation) {} + + public default void resetGyro() {} + + public default void updateSwerve() {} + + public default void resetOdometry(Pose2d pose) {} + + public default void addVisionMeasurement(Pose2d pose, double timestamp) {} + + public default void addVisionMeasurement(Pose2d pose, double timestamp, Matrix stdDevs) {} + + public default void drive( + double vXmps, double vYmps, double vOmegaRadps, boolean isFieldOriented) {} + + public default void move( + double vXmps, double vYmps, double vOmegaRadps, boolean isFieldOriented) {} + + public default void setAzimuthVel(double vel) {} + + public default void updateInputs(SwerveIOInputs inputs) {} + + public default void registerWith(TelemetryService telemetryService) {} + + public default void configDriveCurrents(CurrentLimitsConfigs config) {} + + public default BooleanSupplier getAzimuth1FwdLimitSwitch() { + return () -> false; + } +} diff --git a/src/main/java/frc/robot/subsystems/pathHandler/PathHandler.java b/src/main/java/frc/robot/subsystems/pathHandler/PathHandler.java index dda12674..1721e1ac 100644 --- a/src/main/java/frc/robot/subsystems/pathHandler/PathHandler.java +++ b/src/main/java/frc/robot/subsystems/pathHandler/PathHandler.java @@ -1,3 +1,262 @@ package frc.robot.subsystems.pathHandler; -public class PathHandler {} +import choreo.Choreo; +import choreo.trajectory.SwerveSample; +import choreo.trajectory.Trajectory; +import edu.wpi.first.wpilibj.Timer; +import frc.robot.constants.RobotStateConstants; +import frc.robot.constants.TagServoingConstants; +import frc.robot.subsystems.drive.DriveSubsystem; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import net.jafama.FastMath; +import org.strykeforce.telemetry.measurable.MeasurableSubsystem; +import org.strykeforce.telemetry.measurable.Measure; + +public class PathHandler extends MeasurableSubsystem { + DriveSubsystem driveSubsystem; + + private PathStates currState = PathStates.DONE; + private boolean isHandling = false; + private List NodeNames; + private List NodeLevels; + private Timer timer = new Timer(); + private List> fetchPaths; + private List> placePaths; + private String[][] pathNames = new String[12][2]; + private Trajectory currPath; + private String currPathString; + private boolean runningPath = false; + private boolean isServoing = false; + private boolean mirrorTrajectory = false; + private Character startNode = 'a'; + + PathHandler(DriveSubsystem driveSubsystem) { + this.driveSubsystem = driveSubsystem; + reassignAlliance(); + } + + PathHandler( + DriveSubsystem driveSubsystem, + String[][] pathNames, + List NodeNames, + List NodeLevels, + Character startNode) { + this.driveSubsystem = driveSubsystem; + this.pathNames = pathNames; + this.NodeNames = NodeNames; + this.NodeLevels = NodeLevels; + this.startNode = startNode; + } + + public void setPathNames(String[][] pathNames) { + if (!isHandling) { + this.pathNames = pathNames; + } + } + + public void setNodeNames(List NodeNames) { + if (!isHandling) { + this.NodeNames = NodeNames; + } + } + + public void setNodeLevels(List NodeLevels) { + if (!isHandling) { + this.NodeLevels = NodeLevels; + } + } + + public void setStartNode(Character startNode) { + if (!isHandling) { + this.startNode = startNode; + } + } + + public void startPathHandler() { + NodeNames.add(0, startNode); + isHandling = true; + currState = PathStates.DRIVE_FETCH; + } + + public void reassignAlliance() { + mirrorTrajectory = driveSubsystem.shouldFlip(); + for (int i = 0; i < 12; i++) { + Optional> temp = Choreo.loadTrajectory(pathNames[i][0]); + fetchPaths.add(temp.get()); + temp = Choreo.loadTrajectory(pathNames[i][1]); + placePaths.add(temp.get()); + } + } + + private void startPath(Trajectory path) { + if (isHandling && path != null) { + driveSubsystem.setAutoDebugMsg("Start " + currPathString); + currPath = path; + runningPath = true; + timer.stop(); + timer.reset(); + timer.start(); + driveSubsystem.calculateController(currPath.getInitialSample(mirrorTrajectory).get()); + if (currState == PathStates.PLACE) { + currState = PathStates.DRIVE_FETCH; + } else if (currState == PathStates.FETCH) { + currState = PathStates.DRIVE_PLACE; + } + } + } + + private void drivePath() { + if (isHandling && runningPath && currPath != null) { + driveSubsystem.calculateController(currPath.sampleAt(timer.get(), mirrorTrajectory).get()); + if (timer.hasElapsed(currPath.getTotalTime())) { + driveSubsystem.setAutoDebugMsg("End " + currPathString); + runningPath = false; + timer.stop(); + timer.reset(); + driveSubsystem.calculateController(currPath.getFinalSample(mirrorTrajectory).get()); + if (currState == PathStates.DRIVE_FETCH) { + currState = PathStates.FETCH; + } else if (currState == PathStates.DRIVE_PLACE) { + currState = PathStates.PLACE; + } + } else if (shouldTransitionToServoing()) { + currState = PathStates.DRIVE_PLACE_SERVO; + isServoing = true; + } + } + } + + private void drivePathServo() { + if (isHandling && runningPath && currPath != null && isServoing) { + driveSubsystem.calculateControllerServo( + currPath.sampleAt(timer.get(), mirrorTrajectory).get(), + 0.0); // TODO: use tag servoing here when ready + if (timer.hasElapsed(currPath.getTotalTime())) { + driveSubsystem.setAutoDebugMsg("End " + currPathString); + runningPath = false; + timer.stop(); + timer.reset(); + driveSubsystem.calculateControllerServo( + currPath.getFinalSample(mirrorTrajectory).get(), 0.0); + if (currState == PathStates.DRIVE_FETCH) { + currState = PathStates.FETCH; + } else if (currState == PathStates.DRIVE_PLACE_SERVO) { + isServoing = false; + // RobotStateSubsystem.PlacePiece(); + currState = PathStates.PLACE; + } + } + } + } + + private Trajectory nextPath() { + if (NodeNames.size() > 0) { + if (currState == PathStates.DRIVE_FETCH) { + currPathString = pathNames[NodeNames.get(0) - 'a'][0]; + return fetchPaths.get(NodeNames.get(0) - 'a'); + } else if (currState == PathStates.DRIVE_PLACE) { + currPathString = pathNames[NodeNames.get(0) - 'a'][1]; + return placePaths.get(NodeNames.get(0) - 'a'); + } + } else { + killPathHandler(); + } + return null; + } + + private void advanceNodes() { + if (NodeNames.size() > 0) { + NodeNames.remove(0); + } + } + + public void killPathHandler() { + isHandling = false; + currState = PathStates.DONE; + runningPath = false; + timer.stop(); + timer.reset(); + } + + public void killPathHandlerAfterPath() { + NodeNames.clear(); + } + + private boolean shouldTransitionToServoing() { + boolean isCloseEnough = false; + double preNormalizedAngle = + FastMath.toRadians( + RobotStateConstants.kNodeAngles[FastMath.floorToInt((NodeNames.get(0) - 'a') / 2)]); + double goal = + mirrorTrajectory + ? FastMath.normalizeMinusPiPi(preNormalizedAngle + Math.PI) + : preNormalizedAngle; + double pos = driveSubsystem.getGyroRotation2d().getRadians(); + if (goal < -Math.PI / 2 || goal > Math.PI / 2) { + isCloseEnough = + FastMath.abs(FastMath.normalizeZeroTwoPi(pos) - FastMath.normalizeZeroTwoPi(goal)) + < FastMath.toRadians(TagServoingConstants.kAngleCloseEnough); + } else { + isCloseEnough = + FastMath.abs(pos - goal) < FastMath.toRadians(TagServoingConstants.kAngleCloseEnough); + } + return currState == PathStates.DRIVE_PLACE + && timer.hasElapsed(currPath.getTotalTime() - 1.0) + && isCloseEnough; + } + + public void periodic() { + switch (currState) { + case DRIVE_FETCH: + if (!runningPath) { + startPath(nextPath()); + } + drivePath(); + break; + case FETCH: + // if (robotStateSubsystem.hasPiece) { + advanceNodes(); + currState = PathStates.DRIVE_PLACE; + // } + break; + case DRIVE_PLACE: + if (!runningPath) { + startPath(nextPath()); + } + drivePath(); + break; + case DRIVE_PLACE_SERVO: + if (runningPath && isServoing) { + drivePathServo(); + } + break; + case PLACE: + // if (RobotStateSubsystem.State != RobotStateSubsystem.hasPiece) { + currState = PathStates.DRIVE_FETCH; + // } + break; + case DONE: + isHandling = false; + break; + default: + break; + } + } + + @Override + public Set getMeasures() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getMeasures'"); + } + + public enum PathStates { + FETCH, + DRIVE_FETCH, + PLACE, + DRIVE_PLACE, + DRIVE_PLACE_SERVO, + DONE + } +} diff --git a/vendordeps/AdvantageKit.json b/vendordeps/AdvantageKit.json index 1fa7c033..03df051a 100644 --- a/vendordeps/AdvantageKit.json +++ b/vendordeps/AdvantageKit.json @@ -1,7 +1,7 @@ { "fileName": "AdvantageKit.json", "name": "AdvantageKit", - "version": "4.0.0", + "version": "4.1.0", "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", "frcYear": "2025", "mavenUrls": [ @@ -12,21 +12,22 @@ { "groupId": "org.littletonrobotics.akit", "artifactId": "akit-java", - "version": "4.0.0" + "version": "4.1.0" } ], "jniDependencies": [ { "groupId": "org.littletonrobotics.akit", "artifactId": "akit-wpilibio", - "version": "4.0.0", + "version": "4.1.0", "skipInvalidPlatforms": false, "isJar": false, "validPlatforms": [ "linuxathena", - "windowsx86-64", "linuxx86-64", - "osxuniversal" + "linuxarm64", + "osxuniversal", + "windowsx86-64" ] } ], diff --git a/vendordeps/ChoreoLib-2025.0.2.json b/vendordeps/ChoreoLib-2025.0.2.json new file mode 100644 index 00000000..95f610a5 --- /dev/null +++ b/vendordeps/ChoreoLib-2025.0.2.json @@ -0,0 +1,44 @@ +{ + "fileName": "ChoreoLib-2025.0.2.json", + "name": "ChoreoLib", + "version": "2025.0.2", + "uuid": "b5e23f0a-dac9-4ad2-8dd6-02767c520aca", + "frcYear": "2025", + "mavenUrls": [ + "https://lib.choreo.autos/dep", + "https://repo1.maven.org/maven2" + ], + "jsonUrl": "https://lib.choreo.autos/dep/ChoreoLib2025.json", + "javaDependencies": [ + { + "groupId": "choreo", + "artifactId": "ChoreoLib-java", + "version": "2025.0.2" + }, + { + "groupId": "com.google.code.gson", + "artifactId": "gson", + "version": "2.11.0" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "choreo", + "artifactId": "ChoreoLib-cpp", + "version": "2025.0.2", + "libName": "ChoreoLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal", + "linuxathena", + "linuxarm32", + "linuxarm64" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/thirdcoast.json b/vendordeps/thirdcoast.json index e252cff0..d142a4ca 100644 --- a/vendordeps/thirdcoast.json +++ b/vendordeps/thirdcoast.json @@ -1,7 +1,7 @@ { "fileName": "thirdcoast.json", "name": "StrykeForce-ThirdCoast", - "version": "25.0.0", + "version": "25.0.1", "uuid": "13c4f4b5-a1c0-4670-8f8d-b7b03628c0d3", "frcYear": "2025", "mavenUrls": [ @@ -12,7 +12,7 @@ { "groupId": "org.strykeforce", "artifactId": "thirdcoast", - "version": "25.0.0" + "version": "25.0.1" } ], "jniDependencies": [], From 2bc0fbb62543ee00c0a753a5dcd4f5e733221bfa Mon Sep 17 00:00:00 2001 From: David Shen Date: Thu, 30 Jan 2025 21:04:31 -0500 Subject: [PATCH 08/13] refresh io --- .../java/frc/robot/subsystems/coral/CoralIOFX.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java index 0c20dace..894fbefa 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -1,7 +1,8 @@ package frc.robot.subsystems.coral; -import static edu.wpi.first.units.Units.*; +import static edu.wpi.first.units.Units.RotationsPerSecond; +import com.ctre.phoenix6.BaseStatusSignal; import com.ctre.phoenix6.StatusSignal; import com.ctre.phoenix6.configs.TalonFXSConfiguration; import com.ctre.phoenix6.configs.TalonFXSConfigurator; @@ -66,11 +67,10 @@ public void enableRevLimitSwitch(boolean enabled) { @Override public void updateInputs(CoralIOInputs inputs) { - inputs.velocity = curVelocity.refresh().getValue(); - inputs.isFwdLimitSwitchClosed = - fwdLimitSwitch.refresh().getValue().value == 1; // FIXME check right value - inputs.isRevLimitSwitchClosed = - revLimitSwitch.refresh().getValue().value == 0; // FIXME check right value + BaseStatusSignal.refreshAll(curVelocity, fwdLimitSwitch, revLimitSwitch); + inputs.velocity = curVelocity.getValue(); + inputs.isFwdLimitSwitchClosed = fwdLimitSwitch.getValue().value == 1; // FIXME check right value + inputs.isRevLimitSwitchClosed = revLimitSwitch.getValue().value == 0; // FIXME check right value } @Override From e6f3a140d04ad12c9545c329d0d5e4f0c8e37603 Mon Sep 17 00:00:00 2001 From: David Shen Date: Sat, 1 Feb 2025 13:36:28 -0500 Subject: [PATCH 09/13] typo --- src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index f7c2c27f..ed0c5ebd 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -17,7 +17,6 @@ public class CoralSubsystem extends MeasurableSubsystem implements ClosedLoopSpe private final CoralIOInputsAutoLogged inputs = new CoralIOInputsAutoLogged(); private AngularVelocity setpoint = RotationsPerSecond.of(0.0); private CoralState curState; - ; private org.slf4j.Logger logger = LoggerFactory.getLogger(CoralSubsystem.class); public CoralSubsystem(CoralIO io) { From c0b530ff89134df406bfbe11638fe140b33cfc3a Mon Sep 17 00:00:00 2001 From: David Shen Date: Sat, 1 Feb 2025 14:12:51 -0500 Subject: [PATCH 10/13] speeds --- src/main/java/frc/robot/constants/CoralConstants.java | 8 +++----- src/main/java/frc/robot/subsystems/coral/CoralIO.java | 4 ++-- src/main/java/frc/robot/subsystems/coral/CoralIOFX.java | 4 ++-- .../java/frc/robot/subsystems/coral/CoralSubsystem.java | 6 +++--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/frc/robot/constants/CoralConstants.java b/src/main/java/frc/robot/constants/CoralConstants.java index b7640be2..c1ffe4ad 100644 --- a/src/main/java/frc/robot/constants/CoralConstants.java +++ b/src/main/java/frc/robot/constants/CoralConstants.java @@ -21,9 +21,8 @@ public class CoralConstants { public static final AngularVelocity kCloseEnough = RotationsPerSecond.of(0.1); - public static final AngularVelocity kIntakingSpeed = null; - - public static final AngularVelocity kEjectingSpeed = null; + public static final AngularVelocity kIntakingSpeed = RotationsPerSecond.of(-1); + public static final AngularVelocity kEjectingSpeed = RotationsPerSecond.of(1); // Coral Talon FX Config public static TalonFXSConfiguration getFXConfig() { @@ -33,7 +32,6 @@ public static TalonFXSConfiguration getFXConfig() { new CurrentLimitsConfigs() .withStatorCurrentLimit(10) .withStatorCurrentLimitEnable(false) - .withStatorCurrentLimit(20) .withSupplyCurrentLimit(10) .withSupplyCurrentLowerLimit(8) .withSupplyCurrentLowerTime(0.02) @@ -47,7 +45,7 @@ public static TalonFXSConfiguration getFXConfig() { .withForwardLimitType(ForwardLimitTypeValue.NormallyOpen) .withForwardLimitSource(ForwardLimitSourceValue.LimitSwitchPin) .withReverseLimitAutosetPositionEnable(false) - .withReverseLimitEnable(false) + .withReverseLimitEnable(true) .withReverseLimitType(ReverseLimitTypeValue.NormallyOpen) .withReverseLimitSource(ReverseLimitSourceValue.LimitSwitchPin); fxsConfig.HardwareLimitSwitch = hwLimit; diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIO.java b/src/main/java/frc/robot/subsystems/coral/CoralIO.java index 4631c0b5..e223b4dd 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIO.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIO.java @@ -11,8 +11,8 @@ public interface CoralIO { @AutoLog public static class CoralIOInputs { public AngularVelocity velocity = RotationsPerSecond.of(0.0); - public boolean isFwdLimitSwitchClosed = false; - public boolean isRevLimitSwitchClosed = false; + public boolean isFwdBeamBroken = false; + public boolean isRevBeamBroken = false; } public default void setVelocity(AngularVelocity velocity) {} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java index 894fbefa..1d91dbd8 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -69,8 +69,8 @@ public void enableRevLimitSwitch(boolean enabled) { public void updateInputs(CoralIOInputs inputs) { BaseStatusSignal.refreshAll(curVelocity, fwdLimitSwitch, revLimitSwitch); inputs.velocity = curVelocity.getValue(); - inputs.isFwdLimitSwitchClosed = fwdLimitSwitch.getValue().value == 1; // FIXME check right value - inputs.isRevLimitSwitchClosed = revLimitSwitch.getValue().value == 0; // FIXME check right value + inputs.isFwdBeamBroken = fwdLimitSwitch.getValue().value == 0; // FIXME check right value + inputs.isRevBeamBroken = revLimitSwitch.getValue().value == 0; // FIXME check right value } @Override diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index ed0c5ebd..65aff140 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -16,7 +16,7 @@ public class CoralSubsystem extends MeasurableSubsystem implements ClosedLoopSpe private final CoralIO io; private final CoralIOInputsAutoLogged inputs = new CoralIOInputsAutoLogged(); private AngularVelocity setpoint = RotationsPerSecond.of(0.0); - private CoralState curState; + private CoralState curState = CoralState.IDLE; private org.slf4j.Logger logger = LoggerFactory.getLogger(CoralSubsystem.class); public CoralSubsystem(CoralIO io) { @@ -50,11 +50,11 @@ public boolean atSpeed() { } public boolean isEnterBeamBroken() { - return inputs.isFwdLimitSwitchClosed; // FIXME correct one? + return inputs.isFwdBeamBroken; } public boolean isExitBeamBroken() { - return inputs.isRevLimitSwitchClosed; // FIXME correct one? + return inputs.isRevBeamBroken; } public void intake() { From 387ae3151983538a936d48cbbdad1115574b8f80 Mon Sep 17 00:00:00 2001 From: David Shen Date: Sat, 1 Feb 2025 14:35:35 -0500 Subject: [PATCH 11/13] typo --- src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index 65aff140..78fdf92c 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -100,7 +100,7 @@ public void periodic() { } // Log Outputs - Logger.recordOutput("Coral/curState", curState.ordinal()); + Logger.recordOutput("Coral/curState", curState); Logger.recordOutput("Coral/setpoint", setpoint.in(RotationsPerSecond)); } From 153b539e7518f99b2a94700dcb5d3359aa60dd2c Mon Sep 17 00:00:00 2001 From: David Shen Date: Sat, 1 Feb 2025 15:15:49 -0500 Subject: [PATCH 12/13] open loop ready --- src/main/java/frc/robot/RobotContainer.java | 22 +++++++++++++++++++ .../commands/coral/OpenLoopCoralCommand.java | 19 ++++++++++++++++ .../frc/robot/subsystems/coral/CoralIO.java | 2 ++ .../frc/robot/subsystems/coral/CoralIOFX.java | 15 +++++++++---- .../subsystems/coral/CoralSubsystem.java | 4 ++++ 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/main/java/frc/robot/commands/coral/OpenLoopCoralCommand.java diff --git a/src/main/java/frc/robot/RobotContainer.java b/src/main/java/frc/robot/RobotContainer.java index de2c9d09..c8a2d5b3 100644 --- a/src/main/java/frc/robot/RobotContainer.java +++ b/src/main/java/frc/robot/RobotContainer.java @@ -4,16 +4,38 @@ package frc.robot; +import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.button.JoystickButton; +import frc.robot.commands.coral.OpenLoopCoralCommand; +import frc.robot.subsystems.coral.CoralIO; +import frc.robot.subsystems.coral.CoralIOFX; +import frc.robot.subsystems.coral.CoralSubsystem; public class RobotContainer { + private final CoralSubsystem coralSubsystem; + private final CoralIO coralIO; + + private final XboxController xboxController = new XboxController(1); + public RobotContainer() { + coralIO = new CoralIOFX(); + coralSubsystem = new CoralSubsystem(coralIO); + configureBindings(); + configureOperatorBindings(); } private void configureBindings() {} + private void configureOperatorBindings() { + new JoystickButton(xboxController, XboxController.Button.kA.value) + .onTrue(new OpenLoopCoralCommand(coralSubsystem, 0.5)); + new JoystickButton(xboxController, XboxController.Button.kB.value) + .onTrue(new OpenLoopCoralCommand(coralSubsystem, -0.5)); + } + public Command getAutonomousCommand() { return Commands.print("No autonomous command configured"); } diff --git a/src/main/java/frc/robot/commands/coral/OpenLoopCoralCommand.java b/src/main/java/frc/robot/commands/coral/OpenLoopCoralCommand.java new file mode 100644 index 00000000..a736540c --- /dev/null +++ b/src/main/java/frc/robot/commands/coral/OpenLoopCoralCommand.java @@ -0,0 +1,19 @@ +package frc.robot.commands.coral; + +import edu.wpi.first.wpilibj2.command.InstantCommand; +import frc.robot.subsystems.coral.CoralSubsystem; + +public class OpenLoopCoralCommand extends InstantCommand { + private CoralSubsystem coralSubsystem; + private double pct; + + public OpenLoopCoralCommand(CoralSubsystem coralSubsystem, double pct) { + this.coralSubsystem = coralSubsystem; + this.pct = pct; + } + + @Override + public void initialize() { + coralSubsystem.setPct(pct); + } +} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIO.java b/src/main/java/frc/robot/subsystems/coral/CoralIO.java index e223b4dd..0f5a36fc 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIO.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIO.java @@ -17,6 +17,8 @@ public static class CoralIOInputs { public default void setVelocity(AngularVelocity velocity) {} + public default void setPct(double percentOutput) {} + public default void enableFwdLimitSwitch(boolean enabled) {} public default void enableRevLimitSwitch(boolean enabled) {} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java index 1d91dbd8..ae9ef0e2 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralIOFX.java @@ -6,6 +6,7 @@ import com.ctre.phoenix6.StatusSignal; import com.ctre.phoenix6.configs.TalonFXSConfiguration; import com.ctre.phoenix6.configs.TalonFXSConfigurator; +import com.ctre.phoenix6.controls.DutyCycleOut; import com.ctre.phoenix6.controls.MotionMagicVelocityDutyCycle; import com.ctre.phoenix6.hardware.TalonFXS; import com.ctre.phoenix6.signals.ForwardLimitValue; @@ -22,12 +23,13 @@ public class CoralIOFX implements CoralIO { private TalonFXS talonFx; // FX Access objects - TalonFXSConfigurator configurator; + private TalonFXSConfigurator configurator; private MotionMagicVelocityDutyCycle velocityRequest = new MotionMagicVelocityDutyCycle(0).withEnableFOC(false).withSlot(0); - StatusSignal curVelocity; - StatusSignal fwdLimitSwitch; - StatusSignal revLimitSwitch; + private DutyCycleOut dutyCycleRequest = new DutyCycleOut(0).withEnableFOC(false); + private StatusSignal curVelocity; + private StatusSignal fwdLimitSwitch; + private StatusSignal revLimitSwitch; public CoralIOFX() { logger = LoggerFactory.getLogger(this.getClass()); @@ -51,6 +53,11 @@ public void setVelocity(AngularVelocity velocity) { talonFx.setControl(velocityRequest.withVelocity(velocity)); } + @Override + public void setPct(double percentOutput) { + talonFx.setControl(dutyCycleRequest.withOutput(percentOutput)); + } + @Override public void enableFwdLimitSwitch(boolean enabled) { talonFx diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index 78fdf92c..86035ba8 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -43,6 +43,10 @@ public void setSpeed(AngularVelocity speed) { io.setVelocity(speed); } + public void setPct(double percentOutput) { + io.setPct(percentOutput); + } + @Override public boolean atSpeed() { return setpoint.minus(inputs.velocity).abs(RotationsPerSecond) From 52f009efa59e9d6f067f16e82359ec418ba7d916 Mon Sep 17 00:00:00 2001 From: mwitcpalek Date: Sat, 1 Feb 2025 16:04:23 -0500 Subject: [PATCH 13/13] tweak buttons --- src/main/java/frc/robot/RobotContainer.java | 16 +++++++++++++--- .../coral/EnableEjectBeamCommand.java | 19 +++++++++++++++++++ .../subsystems/coral/CoralSubsystem.java | 4 ++++ 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/main/java/frc/robot/commands/coral/EnableEjectBeamCommand.java diff --git a/src/main/java/frc/robot/RobotContainer.java b/src/main/java/frc/robot/RobotContainer.java index 62493d9c..245e9951 100644 --- a/src/main/java/frc/robot/RobotContainer.java +++ b/src/main/java/frc/robot/RobotContainer.java @@ -9,6 +9,7 @@ import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.button.JoystickButton; +import frc.robot.commands.coral.EnableEjectBeamCommand; import frc.robot.commands.coral.OpenLoopCoralCommand; import frc.robot.commands.drive.DriveTeleopCommand; import frc.robot.controllers.FlyskyJoystick; @@ -58,10 +59,19 @@ private void configureDriverBindings() { } private void configureOperatorBindings() { - new JoystickButton(xboxController, XboxController.Button.kA.value) - .onTrue(new OpenLoopCoralCommand(coralSubsystem, 0.5)); + // Stop Coral new JoystickButton(xboxController, XboxController.Button.kB.value) - .onTrue(new OpenLoopCoralCommand(coralSubsystem, -0.5)); + .onTrue(new OpenLoopCoralCommand(coralSubsystem, 0)); + + // Intake Coral + new JoystickButton(xboxController, XboxController.Button.kY.value) + .onTrue(new OpenLoopCoralCommand(coralSubsystem, -0.5)) + .onTrue(new EnableEjectBeamCommand(false, coralSubsystem)); + + // Eject Coral + new JoystickButton(xboxController, XboxController.Button.kA.value) + .onTrue(new OpenLoopCoralCommand(coralSubsystem, -0.5)) + .onTrue(new EnableEjectBeamCommand(true, coralSubsystem)); } public Command getAutonomousCommand() { diff --git a/src/main/java/frc/robot/commands/coral/EnableEjectBeamCommand.java b/src/main/java/frc/robot/commands/coral/EnableEjectBeamCommand.java new file mode 100644 index 00000000..97bb8ab4 --- /dev/null +++ b/src/main/java/frc/robot/commands/coral/EnableEjectBeamCommand.java @@ -0,0 +1,19 @@ +package frc.robot.commands.coral; + +import edu.wpi.first.wpilibj2.command.InstantCommand; +import frc.robot.subsystems.coral.CoralSubsystem; + +public class EnableEjectBeamCommand extends InstantCommand { + private CoralSubsystem coralSubsystem; + private Boolean enable; + + public EnableEjectBeamCommand(Boolean enable, CoralSubsystem coralSubsystem) { + this.coralSubsystem = coralSubsystem; + this.enable = enable; + } + + @Override + public void initialize() { + coralSubsystem.enableEjectBeam(enable); + } +} diff --git a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java index 86035ba8..af24f3d3 100644 --- a/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java +++ b/src/main/java/frc/robot/subsystems/coral/CoralSubsystem.java @@ -47,6 +47,10 @@ public void setPct(double percentOutput) { io.setPct(percentOutput); } + public void enableEjectBeam(boolean enable) { + io.enableRevLimitSwitch(enable); + } + @Override public boolean atSpeed() { return setpoint.minus(inputs.velocity).abs(RotationsPerSecond)