diff --git a/Assets/2025OffsznIntake.png b/Assets/2025OffsznIntake.png new file mode 100644 index 0000000..0ea5000 Binary files /dev/null and b/Assets/2025OffsznIntake.png differ diff --git a/Assets/AdvantageScopeScreenshot.png b/Assets/AdvantageScopeScreenshot.png new file mode 100644 index 0000000..1df2115 Binary files /dev/null and b/Assets/AdvantageScopeScreenshot.png differ diff --git a/Assets/Ambiguity.jfif b/Assets/Ambiguity.jfif new file mode 100644 index 0000000..dd6ab1f Binary files /dev/null and b/Assets/Ambiguity.jfif differ diff --git a/Assets/Apriltags.jpg b/Assets/Apriltags.jpg new file mode 100644 index 0000000..478f435 Binary files /dev/null and b/Assets/Apriltags.jpg differ diff --git a/Assets/Arducam.webp b/Assets/Arducam.webp new file mode 100644 index 0000000..157dccf Binary files /dev/null and b/Assets/Arducam.webp differ diff --git a/Assets/ChargedUp.jpg b/Assets/ChargedUp.jpg new file mode 100644 index 0000000..e283329 Binary files /dev/null and b/Assets/ChargedUp.jpg differ diff --git a/Assets/Distortion.jpg b/Assets/Distortion.jpg new file mode 100644 index 0000000..fd3e102 Binary files /dev/null and b/Assets/Distortion.jpg differ diff --git a/Assets/Note.jpg b/Assets/Note.jpg new file mode 100644 index 0000000..2bc73af Binary files /dev/null and b/Assets/Note.jpg differ diff --git a/Assets/OrangePi.jfif b/Assets/OrangePi.jfif new file mode 100644 index 0000000..6a9972e Binary files /dev/null and b/Assets/OrangePi.jfif differ diff --git a/Assets/Pose.PNG b/Assets/Pose.PNG new file mode 100644 index 0000000..8399410 Binary files /dev/null and b/Assets/Pose.PNG differ diff --git a/Assets/ShapeDetection.webp b/Assets/ShapeDetection.webp new file mode 100644 index 0000000..547130b Binary files /dev/null and b/Assets/ShapeDetection.webp differ diff --git a/Assets/SimGUI.webp b/Assets/SimGUI.webp new file mode 100644 index 0000000..f353e5f Binary files /dev/null and b/Assets/SimGUI.webp differ diff --git a/Assets/banshee-state-machine.png b/Assets/banshee-state-machine.png new file mode 100644 index 0000000..3bc0a3d Binary files /dev/null and b/Assets/banshee-state-machine.png differ diff --git a/Assets/choreo.png b/Assets/choreo.png new file mode 100644 index 0000000..95916c9 Binary files /dev/null and b/Assets/choreo.png differ diff --git a/Assets/convex.png b/Assets/convex.png new file mode 100644 index 0000000..d4fe042 Binary files /dev/null and b/Assets/convex.png differ diff --git a/Docs/Architecture/AKitStructureReference.md b/Docs/Architecture/AKitStructureReference.md new file mode 100644 index 0000000..4d40ff1 --- /dev/null +++ b/Docs/Architecture/AKitStructureReference.md @@ -0,0 +1,328 @@ +# AdvantageKit Code Structure Reference + +This document contains a quick reference for how we structure our code. +It should be used when planning out the structure of a subsystem. + +## Basic Layout + +```mermaid +flowchart TD + subgraph Subsystem + A[Subsystem] + end + Subsystem-->IO + subgraph IO + direction LR + B[IO]-->E[IOInputs] + end + IO-->IOImpl + subgraph IOImpl + C[IO Implementation Real] + D[IO Implementation Sim] + end +``` + +This diagram shows the basic structure of an AKit Subsystem. +It includes 3 layers: + +### Subsystem + +- The "subsystem" layer is a class which `extends SubsystemBase`. +- This class should contain methods that return Commands for this subsystem. +- This class should contain all higher-level control flow within this mechanism. + - For instance, it could contain a `SwerveDriveOdometry` object to track a drivetrain's position. + - It might contain information about the current target of a mechanism, or whether or not the mechanism has homed its position yet. + - *Generally, this information should all be **"processed" information** that we derive from our IOInputs. +- The `Subsystem` file will contain one `IO` instance and one `IOInputs` instance, conventionally called `io` and `inputs` respectively. + +### IO + +- The "IO" layer defines the interface with our hardware, as well as all the values we will log. +- This includes an `interface` called `SubsystemIO` which defines a set of methods to interact with the hardware, such as `setVoltage` or `getPosition`. +However, it doesn't define *how* to do them, because that's specific to each implementation. +The interface just provides a template. +- This class will also include an `updateInputs` method, which takes in an `IOInputs` object and updates it with the latest sensor data from the mechanism. +- The `IOInputs` object is a class that contains various measurements and sensor data about the mechanism, like current draw, encoder position, and voltage output. +- It is marked with `@AutoLog` which means all the inputs and outputs for the subsystem will be automatically recorded in the log, so we can play it back later. + +### IO Implementations + +- These are classes which `implement` the aforementioned `IO` class. +- This means they will specify how to do all of the methods defined in the `IO` class. +- Generally we will have 2 types of `IOImplementation`, `IOSim` and `IOReal`. + - `IOReal` defines its methods to command real hardware to have real outputs. + - `IOSim` often looks similar to `IOReal`, but will have some behaviour added to fake the behaviour of real world physics. + This can include a physics sim which approximates physical behaviour, setting outputs which we can't sim to a default value (like temperature), or other ways of "faking" real world behaviour. +- `IOImplementation`s will contain the actual objects such as `TalonFX`s (for `IOReal`) or `DCMotorSim`s (for `IOSim`). + +## Intake Example + +A cad render of the intake from our 2025 offseason robot + +This example will cover how the code for an intake such as the one above might be set up. + +```mermaid +flowchart TD + subgraph Subsystem + A[IntakeSubsystem] + end + Subsystem-->IO + subgraph IO + direction LR + B[IntakeIO]-->E[IntakeIOInputs] + end + IO-->IOImpl + subgraph IOImpl + C[IntakeIOReal] + D[IntakeIOSim] + end +``` + +Let's start by defining the methods in the `IntakeIO` interface. +There are two motors on this slapdown intake--one that controls the pivot, and one that controls the rollers. +The intake needs to set its position (extended or retracted), so we will need a `setAngle(Rotation2d angle)` method. +We will also need a way to set the rollers' output, so let's add a `setRollerVoltage(double volts)` method. +For convenience, let's add a method `stop()` that calls `setRollerVoltage()` with a voltage of 0. + +We also need to add our `IntakeIOInputs` to the `IntakeIO` file. +This should contain all of the sensor information we need to know about our intake so that we can debug its behaviour with logs. +Then we can add our logged fields for the motor. + +Here is a list of common logged fields for motors: + +- Velocity (Often but not always in rotations per second) + - This is the main field we care about to see if the motor is moving. +- Current draw (Amps) + - This lets us see if the motor is stalling (trying to move but stuck) as well as how much energy the motor is using. +- Temperature (Celsius) + - If a motor gets too hot it will turn itself off to protect its electronics. + This lets us see if we are having issues related to that. + In addition, motors are less efficient as they heat up. +- Voltage (Voltage) + - This lets us see how much we are commanding our motors to move. +- Position (Rotations or inches, often after a gear reduction) + - This lets us track the position of the motor and its corresponding mechanism. + +We can add all of these values into our `IntakeIOInputs` class. + +Finally, add a `updateInputs(IntakeIOInputs inputs)` method that our `IOImplementation`s can call to record these values. +Our `IntakeIO` file should look something like this now: + +```Java +// Imports go here + +public interface IntakeIO { + @AutoLog + public class IntakeIOInputs { + + // Pivot motor values + public double pivotVelocityRotationsPerSec; + public double pivotCurrentDrawAmps; + public double pivotTemperatureCelsius; + public double pivotVoltage; + public Rotation2d pivotMotorPosition; + + // Roller motor values + public double rollerVelocityRotationsPerSec; + public double rollerCurrentDrawAmps; + public double rollerTemperatureCelsius; + public double rollerVoltage; + } + + // Methods that IOImplementations will implement + public void setAngle(Rotation2d angle); + + public void setRollerVoltage(double volts); + + // Note the use of "default" + // This means that we don't have to re-implement this method in each IOImplementation, because it will default to + // whatever the specific implementation of setVoltage() is + public default void stop() { + setRollerVoltage(0); + } + + public void updateInputs(IntakeIOInputs inputs); +} +``` + +Next, let's write `IntakeIOReal`. +This will contain all of the hardware we want to interact with on the real robot. + +First, we will need to define the hardware we want to use. +In this case, they will be two `TalonFX`s. + +```Java +private final TalonFX pivot = new TalonFX(IntakeSubsystem.PIVOT_MOTOR_ID); +private final TalonFX roller = new TalonFX(IntakeSubsystem.ROLLER_MOTOR_ID); +``` + +Next we will need to implement each of the methods from `IntakeIO`. +Each should `@Override` the template method. +For the sake of brevity, I won't cover that in detail here. *MAKE SUBSYSTEM WALKTHROUGH AND LINK HERE* + +In the end you should have something like: + +```Java +public class IntakeIOReal implements IntakeIO { + private final TalonFX pivot = new TalonFX(IntakeSubsystem.PIVOT_MOTOR_ID); + private final TalonFX roller = new TalonFX(IntakeSubsystem.ROLLER_MOTOR_ID); + + @Override + public void updateInputs(IntakeIOInputs inputs) { + // Note that the exact calls here are just examples, and might not work if copy-pasted + + inputs.pivotVelocityRotationsPerSec = pivot.getVelocity(); + inputs.pivotCurrentDrawAmps = pivot.getStatorCurrent(); + inputs.pivotTemperatureCelsius = pivot.getDeviceTemp(); + inputs.pivotVoltage = pivot.getMotorVoltage(); + inputs.pivotMotorPosition = pivot.getMotorPosition(); + + // Roller motor values + inputs.rollerVelocityRotationsPerSec = roller.getVelocity(); + inputs.rollerCurrentDrawAmps = roller.getStatorCurrent(); + inputs.rollerTemperatureCelsius = roller.getDeviceTemp(); + inputs.rollerVoltage = roller.getMotorVoltage(); + } + + @Override + public void setRollerVoltage(double volts) { + roller.setVoltage(volts); + } + + // Note how we don't need to define stop() because it has a default implementation that does what we want + + @Override + public void setAngle(Rotation2d angle) { + pivot.setAngle(angle); + } +} +``` + +We can make a similar class for `IntakeIOSim`, although instead of getting motor outputs directly we would have to use `motor.getSimState()`. +For more information about that, check the [CTRE docs](https://pro.docs.ctr-electronics.com/en/stable/docs/api-reference/simulation/simulation-intro.html). + +Finally, let's write the `IntakeSubsystem` class. +This class will include an instance of `IntakeIO` and an instance of `IntakeIOInputs`. +It will also contain Command factories to allow the rest of our code to interface with it. + +Add the io and io inputs to the class: + +```Java +// Snip imports + +public class IntakeSubsystem extends SubsystemBase { + private final IntakeIO io; + private final IntakeIOInputsAutoLogged inputs = new IntakeIOInputsAutoLogged(); + + public IntakeSubsystem(IntakeIO io) { + // Pass in either the sim io or real io + this.io = io; + } +} +``` + +Then we can add a few Command factories to control the subsystem: + +```Java +public Command intake(double volts) { + return Commands.run(() -> { + io.setAngle(INTAKE_ANGLE); + io.setRollerVoltage(volts); + }); +} + +public Command stop() { + return Commands.runOnce(() -> { + io.setAngle(RETRACTED_ANGLE); + io.stop(); + }); +} +``` + +Finally, let's add our `periodic()` method to update and log our inputs. + +```Java +@Override +public void periodic() { + io.updateInputs(); + // Make sure to import the "littletonRobotics" Logger, not one of the other ones. + Logger.processInputs("Intake", inputs); +} +``` + +Overall, `IntakeSubsystem` should roughly look like: + +```Java +// Snip imports + +public class IntakeSubsystem extends SubsystemBase { + IntakeIO io; + IntakeIOInputsAutoLogged inputs; + + public IntakeSubsystem(IntakeIO io) { + // Pass in either the sim io or real io + this.io = io; + inputs = new IntakeIOInputsAutoLogged(); + } + + public Command intake(double volts) { + return Commands.run(() -> { + io.setAngle(INTAKE_ANGLE); + io.setRollerVoltage(volts); + }); + } + + public Command stop() { + return Commands.runOnce(() -> { + io.setAngle(RETRACTED_ANGLE); + io.stop(); + }); + } + + @Override + public void periodic() { + io.updateInputs(); + // Make sure to import the "littletonRobotics" Logger, not one of the other ones. + Logger.processInputs("Intake", inputs); + } +} +``` + +### More complex subsystems + +The intake is a very simple to program subsystem, but more complex ones exist. +A swerve drive might have the following set of classes: + +```mermaid +flowchart TD + subgraph Subsystem + A[SwerveSubsystem] + end + + Subsystem--- 4x -->SwerveModuleIO + subgraph SwerveModuleIO + direction LR + B[SwerveModuleIO]-->E[SwerveModuleIOInputs] + end + SwerveModuleIO-->SwerveModuleIOImpl + subgraph SwerveModuleIOImpl + C[SwerveModuleIOReal] + D[SwerveModuleIOSim] + end + + Subsystem--->GyroIO + subgraph GyroIO + direction LR + X[GyroIO]-->GyroIOInputs + end + GyroIO-->GyroIOImpl + subgraph GyroIOImpl + GyroIOReal + GyroIOSim + end +``` + +This means that we will have one `SwerveSubsystem` class. +Within that class, we will have 4 instances of `SwerveModuleIO` and its corresponding `SwerveModuleIOInputs`, one for each module. +We will also have an instance of `GyroIO` and `GyroIOInputs`. +The `SwerveSubsystem` file handles coordinating and combining data from these `IO`s, and each `IO` handles turning the control signals from the `Subsystem` into motion in the hardware (or sim!). diff --git a/Docs/Architecture/CommandBased.md b/Docs/Architecture/CommandBased.md index cf1b57d..484a7c4 100644 --- a/Docs/Architecture/CommandBased.md +++ b/Docs/Architecture/CommandBased.md @@ -2,26 +2,49 @@ ## Command Based is a way of structuring our code to make it easy to do complicated tasks, and write control logic in a concise way -Command based programming revolves around two concepts: Subsystems and Commands. +Command based programming revolves around three concepts: **Subsystems**, **Commands**, and **Triggers**. + A Subsystem is a set of hardware that forms one system on our robot, like the drivetrain, elevator, arm, or intake. Each subsystem contains some associated hardware (motors, pistons, sensors, etc.) They are the "nouns" of our robot, what it is. -Commands are the "verbs" of the robt, or what our robot does. +Each Subsystem is generally made to contain a broad set of hardware that will always operate as a unit. + +Commands are the "verbs" of the robot, or what our robot does. Each Subsystem can be used by one Command at the same time, but Commands may use many Subsystems. -Commands can be composed together, so the `Line Up`, `Extend`, and `Outake` Commands might be put together to make a `Score` Command. +Commands can be composed together, so the `LineUp`, `Extend`, and `Outake` Commands might be put together to make a `Score` Command. Because each Subsystem can only be used by one Command at once, we are safe from multiple pieces of code trying to command the same motor to different speeds, for example. +Subsystems are ways to organize resources that can be used by one Command at a time. + +Some hardware might not be stored in a Subsystem if multiple things can/should use it at the same time safely. +For example, a vision setup can be read from by many things, and might not need to be locked by Commands. +Therefore, it might not be stored in a Subsystem. + +On the other hand, a roller that is driven by a motor can only go at one speed at a time. +Therefore, we would wrap it in a Subsystem so that only one Command can use it at once. + +A Trigger is something which can start a Command. +The classic form of this is a button on the driver's controller. +Another common type is one which checks for when the robot enables. +One non-obvious Trigger we used in 2024 was one which checked when we had detected a game piece in the robot, which we used to flash our LEDs and vibrate the driver controller. +Triggers can be made of any function that returns a boolean which makes them very powerful. +Some large Commands are better represented by several Commands and some Triggers! + +# update with superstructure stuff later + ### Resources +- [WPILib intro to functional programming](https://docs.wpilib.org/en/stable/docs/software/basic-programming/functions-as-data.html). + Read through this article on lambda expressions and functional programming if you haven't already. - [WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/index.html). - Read through these docs until you finish "The Command Scheduler" + Read through these docs until you finish "Organizing Command-Based Robot Projects" OR watch [this video](https://drive.google.com/file/d/1ykFDfXVYk27aHlXYKTAqtj1U2T80Szdj/view?usp=drive_link). - Presentation notes for the video are [here](CommandBasedPresentationNotes.md) - The important segment to remember is: + Presentation notes for the video are [here](CommandBasedPresentationNotes.md). + If you watch the video, it is recommended to also read the [Subsystems](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html), [Binding Commands to Triggers](https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html), and [Organizing Command-Based Robot Projects](https://docs.wpilib.org/en/stable/docs/software/commandbased/organizing-command-based.html#) for addition details on using Command-Based. + + The important segment of all of this to remember is: > Commands represent actions the robot can take. Commands run when scheduled, until they are interrupted or their end condition is met. Commands are very recursively composable: commands can be composed to accomplish more-complicated tasks. See Commands for more info. > > Subsystems represent independently-controlled collections of robot hardware (such as motor controllers, sensors, pneumatic actuators, etc.) that operate together. Subsystems back the resource-management system of command-based: only one command can use a given subsystem at the same time. Subsystems allow users to “hide” the internal complexity of their actual hardware from the rest of their code - this both simplifies the rest of the robot code, and allows changes to the internal details of a subsystem’s hardware without also changing the rest of the robot code. -- [WPILib intro to functional programming](https://docs.wpilib.org/en/stable/docs/software/basic-programming/functions-as-data.html). - Read through this article on lambda expressions and functional programming. ### Examples @@ -35,11 +58,7 @@ Because each Subsystem can only be used by one Command at once, we are safe from ### Notes - We prefer making simple Commands with Command factories, or methods in a subsystem that return a Command. - These methods should be simple interactions like `setTargetExtensionInches()` or `extendIntake()`. + These methods should be simple interactions like `setTargetExtensionMeters()` or `extendIntake()`. Then you can use decorators as described [here](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html) to compose the basic Commands into more complex sequences. - Generally we make these compositions in `RobotContainer` but you can also make single-Subsystem compositions within that Subsystem. + Generally we make these compositions in `Robot` and `Superstructure` but you can also make single-Subsystem compositions within that Subsystem. See our code from previous years for examples of this pattern, or talk to a software lead. -- In our 2023 code we started using a pattern called the `SuperstructureSubsystem`. - This pattern involves creating a Subsystem that has references to most other Subsystems to make Command factory methods with multiple Subsystems. - This pattern doesn't really make sense on inspection though, since the `SuperstructureSubsystem` doesn't actually lock any hardware from multiple-access and you can just make the composed Commands in `RobotContainer` instead. - In the future, just make multiple-Subsystem Command factories in `RobotContainer`. diff --git a/Docs/Architecture/CommandBasedPresentationNotes.md b/Docs/Architecture/CommandBasedPresentationNotes.md index 272b7a6..655cbc4 100644 --- a/Docs/Architecture/CommandBasedPresentationNotes.md +++ b/Docs/Architecture/CommandBasedPresentationNotes.md @@ -18,7 +18,7 @@ Note that some of the code shown in the video differs from the current version o - Motors - Pneumatics - Sensors -- Mutex, prevents multiple things from using the same hardware at the same time +- [Mutex (short for "mutually exclusive")](https://en.wikipedia.org/wiki/Lock_(computer_science)), prevents multiple things from using the same hardware at the same time ### Commands @@ -36,24 +36,24 @@ For example: ```Java // Runs the intake rollers forever -Commands.run(() -> intakeSubsystem.spinRoller(), intakeSubsystem); +Commands.run(intakeSubsystem::spinRoller, intakeSubsystem); // Retracts the intake // Note that the real hardware doesn't move instantly // But we only need to set it once in code -Commands.runOnce(() -> intakeSubsystem.retract(), intakeSubsystem); +Commands.runOnce(intakeSubsystem::retract, intakeSubsystem); ``` -If these Commands are defined in a Subsystem file, we can make them even simpler by calling `run` and `runOnce` on the subsystem itself +Currently, we tend to define these Commands in the Subsystem file, so we can make them even simpler by calling `run` and `runOnce` on the subsystem itself ```Java // Inside IntakeSubsystem.java // Notice how we don't need to define the requirements for these // The subsystem does it implicitly -this.run(() -> spinRoller()); +this.run(this::spinRoller); -this.runOnce(() -> retract()); +this.runOnce(this::retract); ``` Anatomy of a Command declaration: @@ -70,18 +70,18 @@ Anatomy of a Command declaration: ```Java // In RobotContainer.java // When the a button on the controller is pressed, run the rollers on the intake -controller.a().whenPressed(Commands.run(() -> intakeSubsystem.runRollers(), intakeSubsystem)); +controller.a().whenPressed(Commands.run(intakeSubsystem::runRollers, intakeSubsystem)); ``` - This is somewhat wordy - But Commands are objects, so we can pass them around! -- Let's make a method that returns the `RunCommand` instead of having to make it here +- Let's make a method that returns the `Command` instead of having to make it here ```Java // In IntakeSubsystem.java -public CommandBase runRollersCommand() { +public Command runRollersCommand() { // Note implicit requirements - return this.run(() -> runRollers()); + return this.run(this::runRollers); } ``` @@ -113,9 +113,7 @@ intakeSubsystem.extendCommand().andThen(intakeSubsystem.runRollersCommand()) // An example of a more complex group intakeSubsystem.extend().andThen( - intakeSubsystem.runRollers().until(() -> - intakeSubsystem.hasGamePiece() - ), + intakeSubsystem.runRollers().until(intakeSubsystem::hasGamePiece), intakeSubsystem.retract() ) @@ -124,7 +122,7 @@ intakeSubsystem.extend().andThen( elevatorSubsystem.runToScoringHeight() .alongWith( grabberSubsystem.holdGamePiece() - ).until(() -> elevatorSubsystem.isAtScoringHeight()) + ).until(elevatorSubsystem::isAtScoringHeight) .andThen( // Outtake game piece for 1 second grabberSubsystem.outtakeGamePiece().withTimeout(1.0) diff --git a/Docs/Architecture/KitbotExampleWalkthrough.md b/Docs/Architecture/KitbotExampleWalkthrough.md index 1c92830..1cc87e7 100644 --- a/Docs/Architecture/KitbotExampleWalkthrough.md +++ b/Docs/Architecture/KitbotExampleWalkthrough.md @@ -7,16 +7,16 @@ ![AM14U4 or Kitbot Chassis](../../Assets/Kitbot.jpg) The kitbot is a drivetrain which comes in the Kit of Parts provided by FIRST as part of registration. -It is a typical of example of a type of drivetrain known as a differential drive. +It is a typical example of a differential drivetrain. That name comes from the fact that the amount it turns is equal to the difference between the speed of the left and right sides. This type of drive is also known as skid-steer or tank drive. ### Why kitbot? We do not use the kitbot or other differential drive for our robots in competition. -Instead we use a much more versatile swerve drive. -However the code for a swerve drive is much more complex than a tank drive, which provides a good exercise to introduce you to programming FRC robots. -The kitbot has somewhat standard dimensions, gearboxes, wheels, and other physical characteristics so it makes for a consistent mechanism to start with. +Instead, we use a much more versatile swerve drive. +However, the code for a swerve drive is much more complex than a tank drive. +The kitbot has somewhat standard dimensions, gearboxes, wheels, and other physical characteristics, so it makes for a good exercise to introduce you to programming FRC robots. ### So what do we need to program? @@ -26,8 +26,8 @@ Inputs/Outputs: Electronics: -- Each side of the chassis has two Falcon 500 motors, which will work together to power the left and right sides of the chassis. - In code this will look like a total of 4 Talon FX motor controllers, since thats the component we can talk to. +- Each side of the chassis has two Kraken X60 motors, which will work together to power the left and right sides of the chassis. + In code this will look like a total of 4 Talon FX motor controllers, which is the component we can talk to. ## Code Walkthrough @@ -55,14 +55,14 @@ In that folder, right click and then click "create a new class/command" ![A screenshot of the context menu to create a new class/command](../../Assets/KitbotScreenshot3.png) -Select Subsystem and name it DrivetrainSubsystem. This file is the subsystem file for the kitbot drivetrain. -It will contain all of the hardware that the drivetrain uses, in this case TalonFX motor controllers. +Select Subsystem and name it `DrivetrainSubsystem`. +This file is the subsystem file for the kitbot drivetrain. +It will contain all of the hardware that the drivetrain uses, which in this case are TalonFX motor controllers. Other subsystems will contain sensors, solenoids, and more. It will also contain methods that we use to interact with the subsystem from the rest of our code. Our team names subsystem files using the "NameSubsystem" convention. That means that our file should end in Subsystem, so it's clear what it is. -This will become more important later when we add AdvantageKit into our code. The name should also be reasonably short and descriptive. `IntakeSubsystem` is a good name. `GreybotsGrabberSubsystem` is too long, and `CircleThingySubsystem` is not specific. @@ -77,7 +77,7 @@ public class DrivetrainSubsystem extends SubsystemBase {} In this subsystem file we will need to add our hardware. But to have access to the API for our motors, we need to install the CTRE Phoenix library. Use the instructions [here](https://pro.docs.ctr-electronics.com/en/stable/docs/installation/installation-frc.html) to install the API. -Make sure to get the pro or v6 api. +Make sure to get the V6 API. For this project you may use the online installer, but if you want to use your computer to run and setup the robot it is useful to have Tuner X installed. The [api docs](https://pro.docs.ctr-electronics.com/en/stable/docs/api-reference/index.html) for CTRE are also on the same website. @@ -93,24 +93,26 @@ public DrivetrainSubsystem() {} ``` The number being passed into the constructor for the TalonFXs is the ID number of the motor, which we set using Tuner. -Since we don't have real hardware for this example this number is arbitrary, but it's good practice to have a separate configuration file to store these sorts of constants. -**Create a new file in the robot folder (where Robot.java and RobotContainer.java are) called Constants.** -You can use the "create a new class or command" option and select "Empty Class" to speed this up. +Since we don't have real hardware for this example this number is arbitrary, but it's good practice to have these sorts of constants defined at the top of the file. In Constants.java add two `public static final int`s, one for the left motor's ID and one for the right motor. By putting these configuration values all in one place we can easily change them if hardware changes. ```Java -public class Constants { - public static final int drivetrainLeftFalconID = 0; - public static final int drivetrainRightFalconID = 1; +public class DrivetrainSubsystem extends SubsystemBase { + public static final int drivetrainLeftFalconID = 0; + public static final int drivetrainRightFalconID = 1; + + TalonFX leftFalcon = new TalonFX(drivetrainLeftFalconID); + TalonFX rightFalcon = new TalonFX(drivetrainRightFalconID); + //... } ``` - Then change DrivetrainSubsystem to use the new constants. -Next, lets add the `ControlRequest` objects. -In CTREs v6/pro api, we set the output of a motor by passing it a subclass of `ControlRequest` like `VoltageOut` or `PositionDutyCycle`. +Next, let's add the `ControlRequest` objects. +These represent the output of a device, such as a desired voltage, velocity, position, etc. +In CTRE's v6 API, we set the output of a motor by passing it a subclass of `ControlRequest` like `VoltageOut` or `PositionDutyCycle`. Below the Talons, add two `VoltageOut`s. The constructor for these takes in a default voltage, which we can set to 0 to avoid any nasty surprises when we turn the robot on. Make sure you import these classes. @@ -139,58 +141,57 @@ private void setVoltages(double left, double right) { You might notice that this method is private. This is because whenever we want to interact with the hardware of a subsystem we should go through a Command, which guarantees that each piece of hardware is only requested to do one thing at a time. -To do this we need to make a Command factory method, or a method that returns `CommandBase`. +To do this we need to make a Command factory method, or a method that returns a `Command`. ```Java -public CommandBase setVoltagesCommand(DoubleSupplier left, DoubleSupplier right) { - return new RunCommand(() -> this.setVoltages(left.getAsDouble(), right.getAsDouble()), this); +public Command setVoltagesCommand(DoubleSupplier left, DoubleSupplier right) { + return this.run(() -> this.setVoltages(left.getAsDouble(), right.getAsDouble())); } ``` -Notice how instead of passing in `double`s we pass in `DoubleSupplier`s. +Notice how instead of passing in `double`s, we pass in `DoubleSupplier`s. A `DoubleSupplier` is just any function that returns a `double`. -This function can always return the same value, effectively acting like a double, or it can get its value some other way. -This lets us use this command to drive the robot with joysticks, an autonomous controller, or other input. +This function could always return the same value, effectively acting like a double, or it can get its value some other way. +This lets us use this command to drive the robot with joysticks, an autonomous controller, or other input that may change. -In the body of the method we return a `RunCommand`. -`RunCommand` is a subtype of CommandBase, and represents a Command which runs a single function over and over again. +In the body of the method we return `this.run()`, which implicitly creates a `RunCommand`. +`RunCommand` is a subtype of Command, and represents a Command which runs a single function over and over again. This function is defined using lambda syntax, or the `() ->` symbol. On the right of the arrow is a call to our `setVoltages()` method, which calls our `DoubleSupplier`s which each return a value each call of the function, in this case every 20ms as part of the Command loop. We might not want to drive the robot just by setting the left and right wheel speeds, however. -Lets add a method that uses a desired forwards/backwards and turning velocity to set the wheel speeds. +Let's add a method that uses a desired forwards/backwards and turning velocity to set the wheel speeds. We can do this using WPILib's `DifferentialDrive` class. ```Java -public CommandBase setVoltagesArcadeCommand(DoubleSupplier drive, DoubleSupplier steer) { - return new RunCommand(() -> { - var speeds = DifferentialDrive.arcadeDriveIK(drive.getAsDouble(), steer.getAsDouble(), false); - this.setVoltages(speeds.left * 12, speeds.right * 12); - }, this); +public Command setVoltagesArcadeCommand(DoubleSupplier drive, DoubleSupplier steer) { + return this.run(() -> { + var speeds = DifferentialDrive.arcadeDriveIK(drive.getAsDouble(), steer.getAsDouble(), false); + this.setVoltages(speeds.left * 12, speeds.right * 12); + }); } ``` +This method has a very similar structure to the previous method. +The main difference is that instead of having one method in our lambda (`() ->`), we have a code block ({} block). +This means we need to end each line with a ; but can use multiple lines in our Command. This method is named `setVoltagesArcadeCommand` after the arcade drive control it uses, or one input for driving speed and one for turning speed. -The "IK" stands for "Inverse Kinematics", a fancy term for a bit of math to convert from the desired driving and turning speed to the needed wheel speeds. -More generally, IK lets us convert from useful units, like an arms position in space, or a drivetrains speed in each direction, to ones we can directly use, like motor speeds or positions. - Arcade drive isn't the only option we have for "useful units" for a drivetrain. Curvature drive is another example which tries to emulate a car by only turning when forward motion is happening. On a real robot what we use would come down to driver preference. -In the method itself we have a very similar structure to the previous method. -The main difference is that instead of having one method in our lambda (`() ->`), we have a code block ({} block). -This means we need to end each line with a ; but can use multiple lines in our Command. -In this case we call arcadeDriveIK from WPILib's `DifferentialDrive` class and store the result in a variable called `speeds`. +The "IK" stands for "Inverse Kinematics", a fancy term for a bit of math to convert from the desired driving and turning speed to the needed wheel speeds. +More generally, IK lets us convert from useful units, like an arm's position in space or a drivetrain's speed in each direction, to ones we can directly use, like motor speeds or positions. +In this case, we call `arcadeDriveIK` from WPILib's `DifferentialDrive` class and store the result in a variable called `speeds`. -Then we pass speeds to the drive method. -However, theres a catch to these speeds. -Because WPILibs `DifferentialDrive` class doesn't know what kind of motors we run or what voltage our robot is at, it gives us motor speeds in the range -1 to 1, where 1 corresponds to full forward output and -1 corresponds to full reverse output. +Then we pass `speeds` to the drive method. +However, there's a catch to these speeds. +Because WPILib's `DifferentialDrive` class doesn't know what kind of motors we run or what voltage our robot is at, it gives us motor speeds in the range -1 to 1, where 1 corresponds to full forward output and -1 corresponds to full reverse output. We can convert to voltage by multiplying by 12, which our robot should nominally be at. These methods are all well and good on their own, but it would be nice if we had a way to actually use them. -Lets bind them to a joystick. -Go to RobotContainer.java and add a `CommandXboxController` object. +Let's bind them to a joystick. +Go to `RobotContainer.java` and add a `CommandXboxController` object. ```Java // Create a new Xbox controller on port 0 @@ -206,21 +207,21 @@ CommandXboxController controller = new CommandXboxController(0); DrivetrainSubsystem drivetrainSubsystem = new DrivetrainSubsystem(); ``` -Finally we can bind the arcade drive command to the joysticks. -To do this call `setDefaultCommand()` on `drivetrainSubsystem` in RobotContainer's `configureBindings()` method. +Finally, we can bind the arcade drive command to the joysticks. +To do this, call `setDefaultCommand()` on `drivetrainSubsystem` in RobotContainer's `configureBindings()` method. This sets a Command that will be run when no other command is using the drivetrain. Pass in `drivetrainSubsystem.setVoltagesArcadeCommand()` as the default Command. -For the `DoubleSuppliers` to the arcade drive command we can add two lambdas that call `getLeftY()` and `getRightX()` on the controller, which looks at the current joysick values. -This means that each loop this command is run, it will check the current joystick values and turn them into voltages for the wheels. +For the `DoubleSupplier`s to the arcade drive command we can add two lambdas that call `getLeftY()` and `getRightX()` on the controller, which looks at the current joystick values. +This means during each loop that this command is run, it will check the current joystick values and turn them into voltages for the wheels. This line is a little long now, so break it up into a few lines to keep it readable. ```Java drivetrainSubsystem.setDefaultCommand( drivetrainSubsystem.setVoltagesArcadeCommand( - () -> controller.getLeftY(), - () -> controller.getRightX())); + controller::getLeftY, + controller::getRightX)); ``` Using the joystick values directly is perfectly fine, but sometimes our driver wants a different feel to the controls. @@ -231,7 +232,7 @@ One common way is to square the inputs to the drivetrain, which reduces the sens The red line here is if we directly take the input, and the blue line shows the squared input. Another is to add a "deadband" where we wont output any voltage if the input is less than some threshold. -This stops the robot from moving if the controllers don't perfectly read 0 when they aren't pressed. +This stops the robot from moving if the controllers are very close to but don't perfectly read 0 when they aren't pressed. ![A graph showing the difference between an input with and without a deadband](../../Assets/DeadbandVNotGraph.png) @@ -240,11 +241,11 @@ Notice how the graphs overlap outside of the deadband. Also notice the jump in inputs when the deadband stops applying. For an exaggerated one like this it would be an issue, but for a real input the deadband is usually small enough that this jump is not significant. -Lets add a function in RobotContainer to modify the joystick inputs. +Let's add a function in RobotContainer to modify the joystick inputs. Create a function that takes in a double and returns a double called `modifyJoystick`. -Lets start by adding the squaring of the input. -To do this we multiply our input by itself, but in the process we lose the sign of the input. +Let's start by adding the squaring of the input. +To do this, we multiply our input by itself, but in the process we lose the sign of the input. We can use `Math.signum` to add it back in. ```Java diff --git a/Docs/Architecture/Simulation.md b/Docs/Architecture/Simulation.md index 517b7bd..94bac2c 100644 --- a/Docs/Architecture/Simulation.md +++ b/Docs/Architecture/Simulation.md @@ -2,35 +2,67 @@ ## Simulation is the process of running our robot code on a computer other than the robot to test it without being limited by physical hardware -Sometimes simulation includes physics simulation, where we use a mathematical model of a mechanism to predict how it will behave. -Sometimes this will be very accurate. -Sometimes it will make a lot of untrue assumptions about the mechanism. -Both are useful, since we will likely have to retune the mechanism one way or another once we have real hardware. -Deciding how much effort to put into a simulation model is a key part of the intuition you will build as you code more robots. +Generally speaking, simulation depends on sending the inputs we would like to test to a model of a system (mechanism) and analyzing the simulated results. +There are several ways to do this, depending on what you’re simulating. +This could be something small, like simulated readings of an encoder, or something more complex, like a physics simulation of an arm. +Simulation is important for a couple of reasons—we can test something without risking breaking an actual physical part, as well as being able to concurrently develop code as those physical components are being built (which is often the case during the build season). We have a number of tools at our disposal for simulation. -One is WPILibs built in desktop simulation. -This provides a way for us to send joystick inputs from a keyboard and run robot code on a computer. +One is WPILib’s built in desktop simulation. +This handles the process of sending inputs (from joysticks, widgets in the sim dashboard, etc) and running robot code on a computer. This is the backbone of our simulation. -Then we have WPILibs simulation physics classes. -These are built around a state-space model of a class of mechanism, and exist for many common mechanisms. +The sim graphical user interface (GUI) looks like this and can be launched through the WPILib command palette. + +Screenshot of the sim GUI + +From here, you can view some interesting bits of information about your simulated robot, such as whether it's enabled or not, how long it's been up, joysticks, etc. More details are [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/simulation-gui.html#using-the-gui). + +Then we have WPILib’s simulation physics classes. +These are built around a state space model of a class of mechanism and exist for many common mechanisms to predict how they will behave. + +
+Quick digression on state space + +A state is a characteristic of a system at a certain point in time. +A vector is just a way to represent these states. +(If you aren't familiar with vectors/matrices, that's okay!) +For example, a drivetrain system might have the states $`\begin{bmatrix}x\\y\\\theta\end{bmatrix}`$ to describe its position on the field - the x is the x position, y is the y position, and $\theta$ is the rotation of the robot, all in a column vector. +A state-space model is a set of matrix equations that describe how a system changes over time by basically multiplying these state vectors by (scalar) inputs. + +--- +
+ + +Like other simulated systems, the mechanism’s state is updated periodically with simulated inputs. +For example, we can simulate the change in position of an elevator when a certain voltage is applied to its motor using this mathematical model. + +Sometimes these models will be very accurate. +Sometimes they will make a lot of untrue assumptions about the mechanism. +Both are useful, since we will likely have to retune the mechanism one way or another once we have real hardware. +Deciding how much effort to put into a simulation model is a key part of the intuition you will build as you code more robots. We tend to use one or two of these per robot for major mechanisms. -If you want to learn more about this sort of modelling, look at the [control theory for FRC book](https://file.tavsys.net/control/controls-engineering-in-frc.pdf). +If you want to learn more about this sort of modeling, look at the [control theory for FRC book](https://file.tavsys.net/control/controls-engineering-in-frc.pdf). Note that the topics covered by this book are largely far more advanced than high schoolers have the math background for, and it is not required reading. Running a model is useful, but even more useful is being able to visualize the output of the model. -[AdvantageScope](https://github.com/Mechanical-Advantage/AdvantageScope) is a tool developed by team 6328 to visualize data from live robots, simulated robots, and robot logs. +There are several dashboards you can use to do this, but we mainly use AdvantageScope. +[AdvantageScope](https://github.com/Mechanical-Advantage/AdvantageScope) is a tool developed by Team 6328 to visualize data from live robots, simulated robots, and robot logs. It provides a variety of formats to visualize data from line graphs to 3d field models to tables and more. -It is closely integrated with AdvantageKit but does not require it. -The final major tool is being able to swap between simulated inputs and outputs (io) and real io easily and correctly. -This is the real advantage of the AdvantageKit Subsystem -> IO -> IOImplementation structure. -By having a simulation and real IOImplementation for each mechanism we can easily test our code in sim and have minimal work to move it to real hardware. +Screenshot of Advantagescope + +It is closely integrated with AdvantageKit, a logging framework also from Team 6328, but does not require it. + +The final major tool is being able to swap between simulated inputs and outputs (IO) and real IO easily and correctly. +We will cover what this looks like with AdvantageKit [here](AdvantageKit.md), but at a high level, this means that we have both a sim and real version of each mechanism in our code. +This is so we can easily test our code in sim and have minimal work to move it to real hardware. + ### Resources - Read through the [WPILib docs intro to simulation](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-simulation/introduction.html). +- Optional additional reading on [state space modeling](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html) ### Examples @@ -44,4 +76,6 @@ By having a simulation and real IOImplementation for each mechanism we can easil ### Notes - Structuring a codebase to be simulateable is not an easy task. - It is important to be familiar with the tools we have and stick to a clean structure to make it easy to simulate our code. + It is important to be familiar with the tools we have and stick to a clean structure to make it easy to simulate our code. +- Code that has been tested in sim does not necessarily work on the robot. +Be sure to stay safe and prepare for unexpected behavior when testing, especially for the first time. \ No newline at end of file diff --git a/Docs/Architecture/WPILib.md b/Docs/Architecture/WPILib.md index 22f0f0a..22def58 100644 --- a/Docs/Architecture/WPILib.md +++ b/Docs/Architecture/WPILib.md @@ -10,11 +10,11 @@ However, it becomes very clumsy and difficult to interface with these parts dire WPILib is an open source library built for FRC which abstracts away the low level code to allow us to focus on the high-level control logic. The project has a large number of features and is the main building block that our code is built on top of. It also has extensive documentation both for it's own API and for the concepts and math behind many of its features. -Venders (companies that sell us parts) also often have libraries to interface with their parts. +Vendors (companies that sell us parts) also often have libraries to interface with their parts. These include the [Phoenix library](https://v6.docs.ctr-electronics.com/en/stable/) made by CTRE, the manufacturer of the majority of our motor controllers. We also use other libraries for other tasks that WPILib doesn't support. One of these is [Photonvision](../Specifics/Vision.md), a library that processes our camera feeds to turn them into useful target information. -Another is Pathplanner, a library to create and follow paths for the Autonomous portion of a match. +Another is Choreo, a library to follow paths generated in a standalone app for the Autonomous portion of a match. Many of these libraries will have their own articles here as well as their own documentation. ## Resources diff --git a/Docs/General/BasicGit.md b/Docs/General/BasicGit.md index 7a4b1c0..49dee4e 100644 --- a/Docs/General/BasicGit.md +++ b/Docs/General/BasicGit.md @@ -11,10 +11,16 @@ This means that while one person works on code for the autonomous, another could ### Resources +Read one of the following: + - [WPILib Git intro](https://docs.wpilib.org/en/stable/docs/software/basic-programming/git-getting-started.html) - [Github Git intro](https://docs.github.com/en/get-started/using-git/about-git) -- _Record quick intro to git vid/talk at some point, talk to kevin or a software lead until then_ -- [Git install](https://gitforwindows.org/) +- _Record quick intro to git vid/talk at some point, talk to kevin or a software lead until then if you prefer a non-text intro_ + +Install the following: + +- [Windows Git install](https://gitforwindows.org/) +- [Linux/Mac Git install](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) ### Examples @@ -40,7 +46,9 @@ This means that while one person works on code for the autonomous, another could ### Notes -- _We may begin using Github's pull request feature in the future, update with current practices_ +- We use GitHub's pull request (PR) feature to manage branches and merges. +Always make sure to merge to main using a PR. +PR's will be explained further in the Git Workflow docs. - Always commit code that at the very least compiles (you can check this by running the "Build robot code" option in WPILibs command bar) - Commit messages should be short and simple, ~10 words is a good target. If it's too long, use multiple lines in the commit message @@ -54,14 +62,14 @@ This means that while one person works on code for the autonomous, another could ### Names -- Lewy Seiden -- Petro Papahadjopoluos -- Sneha Narayan -- Aliya Vora -- Athena Li -- Jerry Yu -- Nathan Tuan (8/15/24) -- Sam Charlson -- Viviane Oesterer -- Jeffery Tornatore -- Ariel Shusteff +- Lewy +- Petro +- Sneha +- Aliya +- Athena +- Jerry +- Nathan (8/15/24) +- Sam +- Viviane +- Jeffery +- Ariel diff --git a/Docs/General/GitWorkflow.md b/Docs/General/GitWorkflow.md index e6c7bc0..2ca2cf8 100644 --- a/Docs/General/GitWorkflow.md +++ b/Docs/General/GitWorkflow.md @@ -3,20 +3,28 @@ ## Branches Branches are a deviation or split from the main branch that can be adding or removing a specific feature -For example, I can open a branch to work on a new feature. Since I am on my own branch, I am not interfering -with the main branch's commit history, which is supposed to be kept clean. +For example, I can open a branch to work on a new doc page for this training repo. +Since I am on my own branch, I am not interfering with the main branch's commit history, which is supposed to be kept clean. +A "clean" commit history is made up of large, well named commits to make it easy to quickly skim recent changes. +Because I am on my own branch, another student can also work on their own article without fear of interfering with my work. -To create a branch: +To create a branch run: `git branch -m ""` -Then, this works just like the main branch with `pull`, `add`, `commit`, and `push` commands. +Then, this new branch works just like the main branch with `pull`, `add`, `commit`, and `push` commands. +You should see your current branch in blue in the command prompt if you are using git bash. +Otherwise you can use `git status` to check your branch. -Right now, this branch is only local to your computer. To upload this branch to everyone else: -`git push origin ` +Right now, this branch is only local to your computer. +To upload this branch to the remote repository so others can view it: +`git push --set-upstream origin ` -> optional to add the flag -u to the end so you do not need to repeat `git push origin ` everytime you want to push to remote. +Once you have pushed the branch for the first time, it should show up on GitHub and be accessable by others. +You can and should use `git push` without the rest to push any further commits. -## Pull requests and other organization practices are crucial to write maintainable, clean code +## Pull Requests + +Pull requests and other organization practices are crucial to write maintainable, clean code A pull request (PR) is a way to get review on changes before merging them to the main branch. To make a pull request, first you need to have changes to merge. @@ -32,6 +40,9 @@ Click that button and then let a software lead or mentor know that you need revi Issues are a way to track features that we want to add and problems we need to fix in the repository. Issues can be created by going to the issues tab of the repository on github and clicking `New issue`. PRs can be linked to issues to show that an issue has been worked on and automatically resolve the issue when the PR is merged. +While our use of Issues is somewhat sporadic, they are useful for managing work. +If there is a relevant issue for something you are working on, make sure to keep it up to date! +If you have something that you think should be added or fixed, open an issue! ### Resources diff --git a/Docs/Specifics/Choreo.md b/Docs/Specifics/Choreo.md new file mode 100644 index 0000000..0dfb8d7 --- /dev/null +++ b/Docs/Specifics/Choreo.md @@ -0,0 +1,175 @@ +# Choreo + +Choreo is a library that helps us create autonomous paths. +Each path is composed of waypoints (places that we would like the robot to hit along the way) and constraints (limitations of the path or robot). +Choreo allows us to find the most optimal path that gets to all those waypoints, while also staying within those constraints. + +## Choreo GUI + +Choreo screenshot + +This is where we create and save the paths. +It's an independent app from Visual Studio or WPILib. +More extensive documentation can be found [here](https://choreo.autos/), but generally you'll just click on the field to add waypoints (usually at pickup/scoring locations), add any constraints from the top toolbar, and click the Generate Path button to generate a file for the path that we can then use. + +So what happens when you hit Generate? + +## Numerical Optimization +You might have done optimization problems in classes like HMA/precalc or Calculus (it's okay if you haven't), where you're trying to find the minimum/maximum amount of something (say I'm trying to maximize my backyard's area) given some constraint (I only have 100 feet of fence). +Choreo uses the same basic idea of modeling our real life problem (and its constraints) with equations and mathematically finding the best solution. +There are infinite ways to go from point A to point B, but we want to find the fastest way for our robot to do so. +In other words, time is a constraint. +We also need to consider other constraints, like our particular robot's maximum acceleration, velocity, motor torque, etc. + +Choreo uses the Sleipnir solver (which in turn uses CasADi, a numerical optimization software) to calculate the fastest path. +At a high level, the solver takes the waypoints and repeatedly "guesses" better ways to interpolate between those waypoints. +(This is what's happening when the path sort of balloons around after you click the generate button). +If you're interested in learning more about how this works under the hood, check out [Sleipnir](https://github.com/SleipnirGroup/Sleipnir). + +## Why Choreo? + +8033 used to use [PathPlanner](https://pathplanner.dev/), which instead uses [Bezier splines](https://en.wikipedia.org/wiki/Bézier_curve). +However, these splines aren't able to take real-life constraints into account, so there would often be a discrepancy between what was "supposed" to work and what actually happened because the spline would require the robot to do something it couldn't physically do. + +## Choreolib Installation + +First, follow the [instructions](https://choreo.autos/choreolib/getting-started/) to add the Choreo vendordep. + +As of 2025, this is how we set up Choreo (and other auto things) in code. +This may change, so leads should ensure they keep this page up to date. +Check the [docs](https://choreo.autos/) for more info. + +There are 3 major files that concern autos: `Autos.java`, `SwerveSubsystem.java`, and `Robot.java`. + +### `Autos.java` + +This file will contain an `AutoFactory`, which is the class Choreo uses to create auto trajectories. +The constructor of this file will look something like this: + +```Java +public Autos( + SwerveSubsystem swerve + // other subsystems + ) { + this.swerve = swerve; + // other subsystems + factory = + new AutoFactory( + swerve::getPose, // A function that returns the current field-relative Pose2d of the robot + swerve::resetPose, // A function that receives a field-relative Pose2d to reset the robot's odometry to. + swerve.choreoDriveController(), // A function that receives the current SampleType and controls the robot. More info below + true, // If this is true, when on the red alliance, the path will be mirrored to the opposite side, while keeping the same coordinate system origin. + swerve, // The drive Subsystem to require for AutoTrajectory Commands. + (traj, edge) -> { // Log trajectories + if (Robot.ROBOT_TYPE != RobotType.REAL) + Logger.recordOutput( + "Choreo/Active Traj", + DriverStation.getAlliance().isPresent() + && DriverStation.getAlliance().get().equals(Alliance.Blue) + ? traj.getPoses() + : traj.flipped().getPoses()); + }); + } +``` +The rest of the file has methods that return a `Command` corresponding to each auto path. +They'll generally look like this: + +```Java + public Command autoPath() { + final var routine = factory.newRoutine("Auto Path"); // Uses the AutoFactory to create a new routine + final var traj = routine.trajectory("AutoPath"); // Uses that routine to load a new trajectory. This should match what the name of the trajectory is in the Choreo app + routine.active().whileTrue(Commands.sequence(traj.resetOdometry(), traj.cmd())); // When the routine starts, the pose will first reset and then the trajectory will be run + // May include other trigger bindings (scoring, intaking, etc) + return routine.cmd(); // Returns command to run this routine + } +``` +Choreolib relies heavily on `Triggers`, which will not be discussed here. +Refer to the [WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html) for more *or potentially another article here?* + +You'll notice one of the parameters above is the `choreoDriveController()` method. +This is in `SwerveSubsystem`. + +### `SwerveSubsystem.java` + +The `SwerveSample` class is used by Choreo to represent the robot's state (position, velocity, acceleration, and the forces applied to each swerve module) at a point in time along the trajectory. +The `choreoDriveController()` method returns a function that "consumes" (takes as a parameter) a `SwerveSample` and drives to it. +Because different teams might have different ways of doing that driving (for example we've added logging statements), the implementation is left up to teams. + +```Java + +/** + * This function bypasses the command-based framework because Choreolib handles setting + * requirements internally. Do NOT use outside of ChoreoLib + * + * @return a Consumer that runs the drivebase to follow a SwerveSample with PID feedback, sample + * target vel feedforward, and module force feedforward. + */ + @SuppressWarnings("resource") // This is here because otherwise it gets angry about the pid controllers not being closed + public Consumer choreoDriveController() { + // PID controllers for x, y, and theta + final PIDController xController = new PIDController(5.0, 0.0, 0.0); + final PIDController yController = new PIDController(5.0, 0.0, 0.0); + final PIDController thetaController = + new PIDController(constants.getHeadingVelocityKP(), 0.0, 0.0); + thetaController.enableContinuousInput(-Math.PI, Math.PI); + + return (sample) -> { // returns a new function that takes in a sample and then does all the stuff inside the brackets + final var pose = getPose(); // Gets the current pose of the robot on the field + + Logger.recordOutput( + "Choreo/Target Pose", + new Pose2d(sample.x, sample.y, Rotation2d.fromRadians(sample.heading))); + Logger.recordOutput( + "Choreo/Target Speeds Field Relative", + new ChassisSpeeds(sample.vx, sample.vy, sample.omega)); + + var feedback = + new ChassisSpeeds( + xController.calculate(pose.getX(), sample.x), + yController.calculate(pose.getY(), sample.y), + thetaController.calculate(pose.getRotation().getRadians(), sample.heading)); // Calculates how to get from its current pose to the target pose in the sample + var speeds = + ChassisSpeeds.fromFieldRelativeSpeeds( + new ChassisSpeeds(sample.vx, sample.vy, sample.omega).plus(feedback), getRotation()); // Adds what we just calculated ^ to the chassis speeds specified by the sample + + Logger.recordOutput("Choreo/Feedback + FF Target Speeds Robot Relative", speeds); + + // Drives with the chassis speeds we just calculated + this.drive( + speeds, + false, + useModuleForceFF ? sample.moduleForcesX() : new double[4], + useModuleForceFF ? sample.moduleForcesY() : new double[4]); + }; + } +``` + +### `Robot.java` + +We have several different auto options, so picking one is handled in `Robot.java` with the `LoggedDashboardChooser` which puts a dropdown list on the dashboard. +We then add all the methods in `Autos.java` that return our auto paths to that chooser in the `addAutos()` method, which is triggered on startup or by an alliance change. + +```Java +private final Autos autos; +private final LoggedDashboardChooser autoChooser = new LoggedDashboardChooser<>("Autos"); + +public Robot() { + //... + RobotModeTriggers.autonomous() + .whileTrue(Commands.defer(() -> autoChooser.get().asProxy(), Set.of())); // Starts whatever auto command is selected in the chooser once the autonomous period starts + + new Trigger( + () -> { // alliance changing stuff + var allianceChange = !DriverStation.getAlliance().equals(lastAlliance); + lastAlliance = DriverStation.getAlliance(); + return allianceChange && DriverStation.getAlliance().isPresent(); + }) + .onTrue( + Commands.runOnce(() -> addAutos())); +} + +private void addAutos() { + autoChooser.addOption("Auto Path", autos.getAutoPath()); // adds all our auto options to the chooser so they can be selected + //... +} +``` \ No newline at end of file diff --git a/Docs/Specifics/Vision.md b/Docs/Specifics/Vision.md index bfa2054..d420ded 100644 --- a/Docs/Specifics/Vision.md +++ b/Docs/Specifics/Vision.md @@ -2,64 +2,130 @@ ## Vision processing for FRC -**WIP** -When we navigate the world, we have our eyes and vision processing parts of our brains to help us. -This is also the case for our robots—we have a camera connected to a coprocessor that lets us use visual input in our code. +When we navigate the world, we have our eyes and the parts of our brain that process that visual input to help us. +This is also the case for our robots—we have a camera connected to a processor that lets us use visual input in our code. We primarily have two uses for vision: target detection and pose estimation. -The first usually involves some sort of visual fiducial marker (something that can act as a known point of reference that you can use to find your current location) near the target that we can align to and then score which is relatively easy to set up. +The first usually involves some sort of visual fiducial marker (something that can act as a known point of reference to find your current location) near the target that we can align to and then score, which is relatively easy to set up. The second involves a more complicated system of identifying where certain visual fiducial markers are relative to the robot and then using that to generate an estimated pose of the robot on the field. -### Retroreflective tape +### AprilTags -Retroreflective (RR) tape is the shiny stuff you see on roads, safety vests, traffic cones, and more. -For most reflective surfaces, any light hitting it bounces off at an angle, but RR surfaces will reflect the majority of the light back at the source. -This makes it a lot easier to accurately identify targets. -For example, in Charged Up there was RR tape on the cone targets which can be used to align for scoring. +AprilTags are a system of visual tags developed by researchers at the University of Michigan to provide low overhead, high accuracy localization for many different applications. -### AprilTags +![Robots at the University of Michigan with AprilTags](/Assets/Apriltags.jpg) -Apriltags are a system of visual tags developed by researchers at the University of Michigan to provide low overhead, high accuracy localization for many different applications. They look similar to QR codes, but with less pixels. -This trades amount of information in each tag for quick, easy, and robust detection. -In the context of FRC, AprilTags are useful for helping your robot know where it is on the field, so it can align itself to some goal position. -The idea behind this is that when the camera detects an Apriltag, it will calculate the distance, angle, etc. that the robot is from that target and then map that to a diagram that shows where all the Apriltags are on the field and then estimate where the robot is from there. -This is useful because with a reliable pose estimation system we can automate more of the scoring process. +This trades the amount of information in each tag for quick, easy, and robust detection. +There are several different "families" of tags, which have different sizes, shapes, and patterns. +These provide tradeoffs between detail, robustness, and number of potential IDs. +In 2023, the FRC game Charged Up used 16h5, while in 2024 36h11 was used instead. +In each frame in the camera stream, we are looking for these tags. +Through a series of image processing algorithms (removing colors, decimation, identifying regions of relative lightness/darkness), we're able to identify both *which* tag it is (a numerical ID corresponding to the unique pattern of the tag) and *where* the corners of the tag are in the frame. + +In the context of FRC, because AprilTags are placed in various known locations around the field, we can use them in order for the robot to know where it is on the field. +The simplest application of this is single tag detection and 2D alignment. +For example, we might know that when a certain tag is in the middle of the camera's field of vision, our robot's shooter is lined up correctly with the target. +We can then write code that instructs the shooter to release a game piece once that tag has been centered, the drivetrain to adjust so that the tag is centered, etc. + +### Pose Estimation +We can also use AprilTags in a more sophisticated way to perform pose estimation. +Once a tag and its corner locations have been detected, we can compare that to the known pose of that tag on the field and calculate an estimate of the distance, angle, etc. that the camera is from that tag. +We know where the camera is relative to the rest of the robot, so we can then estimate where the robot is on the field. +We'll also combine this estimate with the estimated pose returned by the wheels and gyro of the robot, which is known as odometry. +This is useful because with a reliable pose estimation system, we can automate more of the scoring process. +For example, in 2024 we were able to develop an accurate auto aim system based on the robot's estimated pose. + +An example of pose estimation + +Pose estimation is more accurate when there are multiple tags in frame. +Each tag generates its own estimate, which can then be combined and used or discarded if one is inaccurate. + +How would an estimate be incorrect? +One cause of this is pose ambiguity. + +![Demonstration of pose ambiguity](/Assets/Ambiguity.jfif) + +Humans are typically able to rely on things like depth perception and lighting to determine the difference between something like these two boxes, but it is more difficult for computers. +If we also have pose estimates from other tags that are less ambiguous, we can throw out those (likely) incorrect estimates if they're significantly off. +Having multiple cameras also helps with this, as another camera at a different angle could generate a more accurate pose with the same tag. +Comparing a vision pose against odometry can also rule out incorrect poses. + +There are several different strategies to pick which pose we want to use. +One of these is lowest ambiguity, which finds the pose corresponding to the vision result with the lowest reprojection error (a measurement of the difference between a measured 3D point and its projected 2D point in the camera frame). +This is useful when we only have one tag in frame that we can use to differentiate between these possible poses. +Another is to choose the pose closest to some other known pose, such as the camera's pose or the last calculated pose. +Historically in FRC, it's been unlikely that a robot would be above the ground or outside the field, so this can eliminate such poses that are further than the robot could have traveled in the time since the last measurement. +Lastly, the best way to take advantage of multiple tags is by using the multitag PNP (Perspective-n-Point) strategy. +This solves some equations to get a robot pose based on all the poses that we calculate from each tag and "averages" it out. + +An important step to take before using 3D pose estimation is to correctly calibrate the camera. +All camera lenses have some level of distortion that we need to account for when processing images gathered from them, especially in applications where it's important to accurately calculate where things are in 3D space. + +![Example of distorted chessboard](/Assets/Distortion.jpg) + +It's possible for us to undistort these images—we just need to find the camera-specific numbers that will allow us to perform that math, which we can do by calibrating the camera. +There are a couple ways to do this, but one way is to take a lot of photos with the camera being calibrated of a chessboard pattern (which we know has straight lines and certain measurements) at different angles and positions and send those photos to programs (such as [PhotonVision](https://docs.photonvision.org/en/latest/docs/calibration/calibration.html) or [mrcal](https://mrcal.secretsauce.net/)) that then handle the math for us and return those numbers. + +### Object Detection + +Sometimes we want to be able to detect other things on the field that aren't AprilTags. +In the past, FRC games have included retroreflective tape on various game elements, which is made out of the same shiny material on safety vests and road medians. +This primarily includes game pieces, but can include other robots or more. +One way of accomplishing this is through tuning HSV (hue, saturation, and value, which is a way of defining colors) thresholds to only pick up on a certain color, such as the orange of a Crescendo note. + +![2024 Crescendo note](/Assets/Note.jpg) + +Another way is contour filtering, which looks for the outlines of a shape like a circle or a triangle, such as a cone from Charged Up. + +Example of detecting a triangular object + +A more complicated system is through machine learning models that have been specially trained on the objects we want to detect, such as game pieces or other robots. +Due to the significant processing power that this requires, in FRC this is typically done on the Rockchip Neural Processing Unit (NPU), which is on the Orange Pi 5 and similar coprocessors, or a Google Coral. +While we haven't implemented this, the greater FRC community has developed several models and other resources. +Photonvision has also shipped ML based game piece detection recently, and likely will continue to. ### PhotonVision -Photonvision is an app and library that gets vision data from the robot for a variety of applications. -It makes it a lot easier to interface with the camera and also has plenty of utility methods to get information like distance and position, as well as having some built in pose estimator features and more. +Photonvision is a community, open source library that gets vision data from the robot for a variety of applications. +It simplifies interfacing with the camera and also has plenty of utility methods to get information like distance and position, as well as having some built in pose estimator features and more. ### Limelight Limelight (LL) is a camera and processor system for FRC designed to make vision easy and simple. -We used a Limelight 2+ with Limelight's software in 2022 for RR tape detection, and a LL2+ with Photonvision software in 2023 for Apriltag detection. -The LL2+ was not satisfactory for apriltag detection for us, and we will likely move on to different hardware in the near future. +We used a Limelight 2+ with Limelight's software in 2022 for retroreflective tape detection, and a LL2+ with Photonvision software in 2023 for Apriltag detection. -### Resources +Limelight has recently released the LL3, which possesses more processing power for AprilTags and object detection, and other teams have had success with it. +The LL3G also features a global shutter camera for further improved AprilTag performance. +At time of writing we have not tested either. -- [Photonvision docs](https://docs.photonvision.org/en/latest/index.html) -- [Photonvision repo](https://github.com/PhotonVision/photonvision) -- [WPILib article on vision](https://docs.wpilib.org/en/stable/docs/software/vision-processing/index.html) +The LL2+ was not satisfactory for Apriltag detection for us, so we switched to the... -### Examples +### Orange Pi + Arducam -- [Official photonlib examples](https://github.com/PhotonVision/photonvision/tree/master/photonlib-java-examples) -- [5026 2023 code](https://github.com/Iron-Panthers/FRC-2023/blob/037d52ac93f4e46a2518491acd2e195d429d6dbd/src/main/java/frc/robot/subsystems/VisionSubsystem.java) -- [8033 2023 apriltag code](https://github.com/HighlanderRobotics/Charged-Up/blob/main/src/main/java/frc/robot/subsystems/ApriltagVisionSubsystem.java) [DOWN] -- [8033 2023 rr tape code](https://github.com/HighlanderRobotics/Charged-Up/blob/main/src/main/java/frc/robot/subsystems/TapeVisionSubsystem.java) [DOWN] +While we have the roboRIO to run much of our code on the robot, it is not capable of handling vision processing at a usable speed. +Instead, we run this on a coprocessor, which is a second, faster computer also onboard the robot. +For vision, we've been using the Orange Pi 5, a single board computer which is quite similar to Raspberry Pis you may have seen elsewhere. +This is what we run Photonvision on. -### Exercises +![Orange Pi 5](/Assets/OrangePi.jfif) -- Once you have photonvision installed and the web dashboard set up, mess around with the pipeline tuning settings - +Of course, we also need a camera. +We've been using Arducam USB cameras (specifically OV9281s and OV2311s), which have worked well, though other USB cameras also work. +Generally, a reasonably high resolution, high speed, global shutter camera is best for FRC applications. +Some test data of other frequently used cameras is linked below in the Resources section. -### Notes +Arducam OV9281 -- Photonvision is installed on the coprocessor not on your computer, what you’re looking at on your computer is just a web dashboard -- Last year we used a system called a Limelight which had a camera bundled in with a coprocessor. It had a couple of performance issues, so this year we’re probably going to use custom hardware **still tbd if we’re going to use an opi or not so I’ll come back and edit this** +### Resources -### What's Next? +- [Photonvision docs](https://docs.photonvision.org/en/latest/index.html) +- [Photonvision repo](https://github.com/PhotonVision/photonvision) +- [WPILib article on vision](https://docs.wpilib.org/en/stable/docs/software/vision-processing/index.html) +- [Anand's Coprocessor Roundup 2023](https://docs.google.com/document/d/1N-Cda1iHJvoNv8osOsZ9h4vovqR4tkuL9hg8aaVzPwU/edit#heading=h.tq4turyc03l3) +- [Anand's camera testing 2023](https://docs.google.com/document/d/17DNCNHxUo31Rh-7VmXXyn-Y25UtGND3NPoGL9gRosaQ/edit#heading=h.t136qtqvhq75) + +### Examples -- Next course in course progression, if applicable +- [Photonlib examples](https://github.com/PhotonVision/photonvision/tree/master/photonlib-java-examples) +- [8033 2024 implementation](https://github.com/HighlanderRobotics/Crescendo/tree/main/src/main/java/frc/robot/subsystems/vision) \ No newline at end of file diff --git a/Docs/Specifics/image.png b/Docs/Specifics/image.png new file mode 100644 index 0000000..4412468 Binary files /dev/null and b/Docs/Specifics/image.png differ diff --git a/Examples/.DS_Store b/Examples/.DS_Store deleted file mode 100644 index f42d172..0000000 Binary files a/Examples/.DS_Store and /dev/null differ diff --git a/Examples/KitbotDemoBasic/.gitignore b/Examples/KitbotDemoBasic/.gitignore index a8d1911..34cbaac 100644 --- a/Examples/KitbotDemoBasic/.gitignore +++ b/Examples/KitbotDemoBasic/.gitignore @@ -169,4 +169,19 @@ out/ .fleet # Simulation GUI and other tools window save file +networktables.json +simgui.json *-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/Examples/KitbotDemoBasic/.vscode/settings.json b/Examples/KitbotDemoBasic/.vscode/settings.json index 4ed293b..612cdd0 100644 --- a/Examples/KitbotDemoBasic/.vscode/settings.json +++ b/Examples/KitbotDemoBasic/.vscode/settings.json @@ -25,5 +25,36 @@ } }, ], - "java.test.defaultConfig": "WPIlibUnitTests" + "java.test.defaultConfig": "WPIlibUnitTests", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] } diff --git a/Examples/KitbotDemoBasic/.wpilib/wpilib_preferences.json b/Examples/KitbotDemoBasic/.wpilib/wpilib_preferences.json index ad4fe20..ecbd975 100644 --- a/Examples/KitbotDemoBasic/.wpilib/wpilib_preferences.json +++ b/Examples/KitbotDemoBasic/.wpilib/wpilib_preferences.json @@ -1,6 +1,6 @@ { "enableCppIntellisense": false, "currentLanguage": "java", - "projectYear": "2023", + "projectYear": "2025", "teamNumber": 8033 } \ No newline at end of file diff --git a/Examples/KitbotDemoBasic/WPILib-License.md b/Examples/KitbotDemoBasic/WPILib-License.md index 3d5a824..645e542 100644 --- a/Examples/KitbotDemoBasic/WPILib-License.md +++ b/Examples/KitbotDemoBasic/WPILib-License.md @@ -1,4 +1,4 @@ -Copyright (c) 2009-2021 FIRST and other WPILib contributors +Copyright (c) 2009-2024 FIRST and other WPILib contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Examples/KitbotDemoBasic/build.gradle b/Examples/KitbotDemoBasic/build.gradle index 0fa24d3..1945af5 100644 --- a/Examples/KitbotDemoBasic/build.gradle +++ b/Examples/KitbotDemoBasic/build.gradle @@ -1,10 +1,12 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2025.2.1" } -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} def ROBOT_MAIN_CLASS = "frc.robot.Main" @@ -31,6 +33,8 @@ deploy { frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { files = project.fileTree('src/main/deploy') directory = '/home/lvuser/deploy' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project } } } @@ -43,11 +47,12 @@ def deployArtifact = deploy.targets.roborio.artifacts.frcJava wpi.java.debugJni = false // Set this to true to enable desktop support. -def includeDesktopSupport = true +def includeDesktopSupport = false // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. // Also defines JUnit 5. dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() implementation wpi.java.deps.wpilib() implementation wpi.java.vendor.java() @@ -65,9 +70,8 @@ dependencies { nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) simulationRelease wpi.sim.enableRelease() - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } test { @@ -84,6 +88,7 @@ wpi.sim.addDriverstation() // knows where to look for our Robot Class. jar { from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + from sourceSets.main.allSource manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) duplicatesStrategy = DuplicatesStrategy.INCLUDE } diff --git a/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.jar b/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.jar index 249e583..a4b76b9 100644 Binary files a/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.jar and b/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.properties b/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.properties index c23a1b3..34bd9ce 100644 --- a/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.properties +++ b/Examples/KitbotDemoBasic/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=permwrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=permwrapper/dists diff --git a/Examples/KitbotDemoBasic/gradlew b/Examples/KitbotDemoBasic/gradlew index a69d9cb..f5feea6 100644 --- a/Examples/KitbotDemoBasic/gradlew +++ b/Examples/KitbotDemoBasic/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/Examples/KitbotDemoBasic/gradlew.bat b/Examples/KitbotDemoBasic/gradlew.bat index f127cfd..9d21a21 100644 --- a/Examples/KitbotDemoBasic/gradlew.bat +++ b/Examples/KitbotDemoBasic/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -42,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/Examples/KitbotDemoBasic/settings.gradle b/Examples/KitbotDemoBasic/settings.gradle index 48c039e..c493958 100644 --- a/Examples/KitbotDemoBasic/settings.gradle +++ b/Examples/KitbotDemoBasic/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2025' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') @@ -20,8 +20,11 @@ pluginManagement { } def frcHomeMaven = new File(frcHome, 'maven') maven { - name 'frcHome' - url frcHomeMaven + name = 'frcHome' + url = frcHomeMaven } } } + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/Examples/KitbotDemoBasic/src/main/java/frc/robot/Subsystems/DrivetrainSubsystem.java b/Examples/KitbotDemoBasic/src/main/java/frc/robot/Subsystems/DrivetrainSubsystem.java index 1ffaad3..ee79ae2 100644 --- a/Examples/KitbotDemoBasic/src/main/java/frc/robot/Subsystems/DrivetrainSubsystem.java +++ b/Examples/KitbotDemoBasic/src/main/java/frc/robot/Subsystems/DrivetrainSubsystem.java @@ -6,12 +6,11 @@ import java.util.function.DoubleSupplier; -import com.ctre.phoenixpro.controls.VoltageOut; -import com.ctre.phoenixpro.hardware.TalonFX; +import com.ctre.phoenix6.controls.VoltageOut; +import com.ctre.phoenix6.hardware.TalonFX; import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import edu.wpi.first.wpilibj2.command.CommandBase; -import edu.wpi.first.wpilibj2.command.RunCommand; +import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Constants; @@ -31,15 +30,15 @@ private void setVoltages(double left, double right) { rightFalcon.setControl(rightVoltage.withOutput(left)); } - public CommandBase setVoltagesCommand(DoubleSupplier left, DoubleSupplier right) { - return new RunCommand(() -> this.setVoltages(left.getAsDouble(), right.getAsDouble()), this); + public Command setVoltagesCommand(DoubleSupplier left, DoubleSupplier right) { + return this.run(() -> this.setVoltages(left.getAsDouble(), right.getAsDouble())); } - public CommandBase setVoltagesArcadeCommand(DoubleSupplier drive, DoubleSupplier steer) { - return new RunCommand(() -> { + public Command setVoltagesArcadeCommand(DoubleSupplier drive, DoubleSupplier steer) { + return this.run(() -> { var speeds = DifferentialDrive.arcadeDriveIK(drive.getAsDouble(), steer.getAsDouble(), false); this.setVoltages(speeds.left * 12, speeds.right * 12); - }, this); + }); } @Override diff --git a/Examples/KitbotDemoFinal/vendordeps/PhoenixPro.json b/Examples/KitbotDemoBasic/vendordeps/Phoenix6-frc2025-latest.json similarity index 53% rename from Examples/KitbotDemoFinal/vendordeps/PhoenixPro.json rename to Examples/KitbotDemoBasic/vendordeps/Phoenix6-frc2025-latest.json index 4418679..6f40c84 100644 --- a/Examples/KitbotDemoFinal/vendordeps/PhoenixPro.json +++ b/Examples/KitbotDemoBasic/vendordeps/Phoenix6-frc2025-latest.json @@ -1,147 +1,234 @@ { - "fileName": "PhoenixPro.json", - "name": "CTRE-Phoenix (Pro)", - "version": "23.0.8", - "frcYear": 2023, + "fileName": "Phoenix6-frc2025-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "25.4.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/phoenixpro/PhoenixPro-frc2023-latest.json", + "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.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-java", - "version": "23.0.8" + "version": "25.4.0" } ], "jniDependencies": [ { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "tools-sim", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonSRX", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonFX", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "23.0.8", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "23.0.8", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" @@ -149,24 +236,25 @@ ], "cppDependencies": [ { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-cpp", - "version": "23.0.8", - "libName": "CTRE_PhoenixPro_WPI", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPI", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", "sharedLibrary": true, @@ -174,29 +262,31 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "wpiapi-cpp-sim", - "version": "23.0.8", - "libName": "CTRE_PhoenixPro_WPISim", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPISim", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_PhoenixTools_Sim", "headerClassifier": "headers", "sharedLibrary": true, @@ -204,14 +294,15 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimTalonSRX", "headerClassifier": "headers", "sharedLibrary": true, @@ -219,89 +310,95 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonFX", - "version": "23.0.8", - "libName": "CTRE_SimTalonFX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.4.0", + "libName": "CTRE_SimVictorSPX", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simVictorSPX", - "version": "23.0.8", - "libName": "CTRE_SimVictorSPX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.4.0", + "libName": "CTRE_SimPigeonIMU", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simPigeonIMU", - "version": "23.0.8", - "libName": "CTRE_SimPigeonIMU", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.4.0", + "libName": "CTRE_SimCANCoder", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simCANCoder", - "version": "23.0.8", - "libName": "CTRE_SimCANCoder", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFX", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simProTalonFX", - "version": "23.0.8", - "libName": "CTRE_SimProTalonFX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFXS", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimProCANcoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -309,14 +406,15 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", "sharedLibrary": true, @@ -324,6 +422,55 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" diff --git a/Examples/KitbotDemoBasic/vendordeps/WPILibNewCommands.json b/Examples/KitbotDemoBasic/vendordeps/WPILibNewCommands.json index bd535bf..3718e0a 100644 --- a/Examples/KitbotDemoBasic/vendordeps/WPILibNewCommands.json +++ b/Examples/KitbotDemoBasic/vendordeps/WPILibNewCommands.json @@ -3,6 +3,7 @@ "name": "WPILib-New-Commands", "version": "1.0.0", "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", "mavenUrls": [], "jsonUrl": "", "javaDependencies": [ diff --git a/Examples/KitbotDemoFinal/.gitignore b/Examples/KitbotDemoFinal/.gitignore index bfe0660..34cbaac 100644 --- a/Examples/KitbotDemoFinal/.gitignore +++ b/Examples/KitbotDemoFinal/.gitignore @@ -57,9 +57,6 @@ *.tar.gz *.rar -# Build Constants -src\main\java\frc\robot\BuildConstants.java - # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* @@ -172,4 +169,19 @@ out/ .fleet # Simulation GUI and other tools window save file +networktables.json +simgui.json *-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/Examples/KitbotDemoFinal/.vscode/settings.json b/Examples/KitbotDemoFinal/.vscode/settings.json index 4ed293b..612cdd0 100644 --- a/Examples/KitbotDemoFinal/.vscode/settings.json +++ b/Examples/KitbotDemoFinal/.vscode/settings.json @@ -25,5 +25,36 @@ } }, ], - "java.test.defaultConfig": "WPIlibUnitTests" + "java.test.defaultConfig": "WPIlibUnitTests", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] } diff --git a/Examples/KitbotDemoFinal/.wpilib/wpilib_preferences.json b/Examples/KitbotDemoFinal/.wpilib/wpilib_preferences.json index ad4fe20..ecbd975 100644 --- a/Examples/KitbotDemoFinal/.wpilib/wpilib_preferences.json +++ b/Examples/KitbotDemoFinal/.wpilib/wpilib_preferences.json @@ -1,6 +1,6 @@ { "enableCppIntellisense": false, "currentLanguage": "java", - "projectYear": "2023", + "projectYear": "2025", "teamNumber": 8033 } \ No newline at end of file diff --git a/Examples/KitbotDemoFinal/WPILib-License.md b/Examples/KitbotDemoFinal/WPILib-License.md index 3d5a824..645e542 100644 --- a/Examples/KitbotDemoFinal/WPILib-License.md +++ b/Examples/KitbotDemoFinal/WPILib-License.md @@ -1,4 +1,4 @@ -Copyright (c) 2009-2021 FIRST and other WPILib contributors +Copyright (c) 2009-2024 FIRST and other WPILib contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Examples/KitbotDemoFinal/build.gradle b/Examples/KitbotDemoFinal/build.gradle index ec37a4a..3134468 100644 --- a/Examples/KitbotDemoFinal/build.gradle +++ b/Examples/KitbotDemoFinal/build.gradle @@ -1,11 +1,12 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" - id "com.peterabeles.gversion" version "1.10" + id "edu.wpi.first.GradleRIO" version "2025.2.1" } -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} def ROBOT_MAIN_CLASS = "frc.robot.Main" @@ -32,6 +33,8 @@ deploy { frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { files = project.fileTree('src/main/deploy') directory = '/home/lvuser/deploy' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project } } } @@ -44,11 +47,12 @@ def deployArtifact = deploy.targets.roborio.artifacts.frcJava wpi.java.debugJni = false // Set this to true to enable desktop support. -def includeDesktopSupport = true +def includeDesktopSupport = false // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. // Also defines JUnit 5. dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() implementation wpi.java.deps.wpilib() implementation wpi.java.vendor.java() @@ -66,12 +70,11 @@ dependencies { nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) simulationRelease wpi.sim.enableRelease() - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' def akitJson = new groovy.json.JsonSlurper().parseText(new File(projectDir.getAbsolutePath() + "/vendordeps/AdvantageKit.json").text) - annotationProcessor "org.littletonrobotics.akit.junction:junction-autolog:$akitJson.version" + annotationProcessor "org.littletonrobotics.akit:akit-autolog:$akitJson.version" } test { @@ -88,6 +91,7 @@ wpi.sim.addDriverstation() // knows where to look for our Robot Class. jar { from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + from sourceSets.main.allSource manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) duplicatesStrategy = DuplicatesStrategy.INCLUDE } @@ -102,26 +106,7 @@ tasks.withType(JavaCompile) { options.compilerArgs.add '-XDstringConcat=inline' } -repositories { - maven { - url = uri("https://maven.pkg.github.com/Mechanical-Advantage/AdvantageKit") - credentials { - username = "Mechanical-Advantage-Bot" - password = "\u0067\u0068\u0070\u005f\u006e\u0056\u0051\u006a\u0055\u004f\u004c\u0061\u0079\u0066\u006e\u0078\u006e\u0037\u0051\u0049\u0054\u0042\u0032\u004c\u004a\u006d\u0055\u0070\u0073\u0031\u006d\u0037\u004c\u005a\u0030\u0076\u0062\u0070\u0063\u0051" - } - } -} - -configurations.all { - exclude group: "edu.wpi.first.wpilibj" -} - -project.compileJava.dependsOn(createVersionFile) -gversion { - srcDir = "src/main/java/" - classPackage = "frc.robot" - className = "BuildConstants" - dateFormat = "yyyy-MM-dd HH:mm:ss z" - timeZone = "America/Los_Angeles" // Use preferred time zone - indent = " " -} +task(replayWatch, type: JavaExec) { + mainClass = "org.littletonrobotics.junction.ReplayWatch" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.jar b/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.jar index 249e583..a4b76b9 100644 Binary files a/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.jar and b/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.properties b/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.properties index c23a1b3..34bd9ce 100644 --- a/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.properties +++ b/Examples/KitbotDemoFinal/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=permwrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=permwrapper/dists diff --git a/Examples/KitbotDemoFinal/gradlew b/Examples/KitbotDemoFinal/gradlew index a69d9cb..f5feea6 100644 --- a/Examples/KitbotDemoFinal/gradlew +++ b/Examples/KitbotDemoFinal/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/Examples/KitbotDemoFinal/gradlew.bat b/Examples/KitbotDemoFinal/gradlew.bat index f127cfd..9d21a21 100644 --- a/Examples/KitbotDemoFinal/gradlew.bat +++ b/Examples/KitbotDemoFinal/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -42,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/Examples/KitbotDemoFinal/networktables.json b/Examples/KitbotDemoFinal/networktables.json deleted file mode 100644 index fe51488..0000000 --- a/Examples/KitbotDemoFinal/networktables.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/Examples/KitbotDemoFinal/settings.gradle b/Examples/KitbotDemoFinal/settings.gradle index 48c039e..c493958 100644 --- a/Examples/KitbotDemoFinal/settings.gradle +++ b/Examples/KitbotDemoFinal/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2025' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') @@ -20,8 +20,11 @@ pluginManagement { } def frcHomeMaven = new File(frcHome, 'maven') maven { - name 'frcHome' - url frcHomeMaven + name = 'frcHome' + url = frcHomeMaven } } } + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/Examples/KitbotDemoFinal/simgui-ds.json b/Examples/KitbotDemoFinal/simgui-ds.json deleted file mode 100644 index 2bf389e..0000000 --- a/Examples/KitbotDemoFinal/simgui-ds.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "Keyboard 0 Settings": { - "window": { - "visible": true - } - }, - "keyboardJoysticks": [ - { - "axisConfig": [ - { - "decKey": 65, - "incKey": 68 - }, - { - "decKey": 87, - "incKey": 83 - }, - { - "decKey": 69, - "decayRate": 0.0, - "incKey": 82, - "keyRate": 0.009999999776482582 - } - ], - "axisCount": 3, - "buttonCount": 4, - "buttonKeys": [ - 90, - 88, - 67, - 86 - ], - "povConfig": [ - { - "key0": 328, - "key135": 323, - "key180": 322, - "key225": 321, - "key270": 324, - "key315": 327, - "key45": 329, - "key90": 326 - } - ], - "povCount": 1 - }, - { - "axisConfig": [ - { - "decKey": 74, - "incKey": 76 - }, - { - "decKey": 73, - "incKey": 75 - } - ], - "axisCount": 2, - "buttonCount": 4, - "buttonKeys": [ - 77, - 44, - 46, - 47 - ], - "povCount": 0 - }, - { - "axisConfig": [ - { - "decKey": 263, - "incKey": 262 - }, - { - "decKey": 265, - "incKey": 264 - } - ], - "axisCount": 2, - "buttonCount": 6, - "buttonKeys": [ - 260, - 268, - 266, - 261, - 269, - 267 - ], - "povCount": 0 - }, - { - "axisCount": 0, - "buttonCount": 0, - "povCount": 0 - } - ], - "robotJoysticks": [ - { - "guid": "Keyboard0" - } - ] -} diff --git a/Examples/KitbotDemoFinal/simgui.json b/Examples/KitbotDemoFinal/simgui.json deleted file mode 100644 index 3b59971..0000000 --- a/Examples/KitbotDemoFinal/simgui.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "NTProvider": { - "types": { - "/FMSInfo": "FMSInfo" - } - }, - "NetworkTables": { - "transitory": { - "AdvantageKit": { - "RealOutputs": { - "open": true - }, - "open": true - } - } - } -} diff --git a/Examples/KitbotDemoFinal/src/main/java/frc/robot/BuildConstants.java b/Examples/KitbotDemoFinal/src/main/java/frc/robot/BuildConstants.java index 5c4bd3c..e9cf366 100644 --- a/Examples/KitbotDemoFinal/src/main/java/frc/robot/BuildConstants.java +++ b/Examples/KitbotDemoFinal/src/main/java/frc/robot/BuildConstants.java @@ -7,12 +7,12 @@ public final class BuildConstants { public static final String MAVEN_GROUP = ""; public static final String MAVEN_NAME = "KitbotDemoFinal"; public static final String VERSION = "unspecified"; - public static final int GIT_REVISION = 66; - public static final String GIT_SHA = "bbe4ec0bcd806f52a50e66312b35e58315debd39"; - public static final String GIT_DATE = "2023-08-20 14:39:02 PDT"; - public static final String GIT_BRANCH = "kitbot-example-sim"; - public static final String BUILD_DATE = "2023-08-20 14:45:58 PDT"; - public static final long BUILD_UNIX_TIME = 1692567958878L; + public static final int GIT_REVISION = 202; + public static final String GIT_SHA = "2cc4b1e3ca5e56dc3af091243ce8814702adb871"; + public static final String GIT_DATE = "2025-07-25 17:20:42 PDT"; + public static final String GIT_BRANCH = "command-based-2024"; + public static final String BUILD_DATE = "2025-07-26 16:23:37 PDT"; + public static final long BUILD_UNIX_TIME = 1753572217766L; public static final int DIRTY = 1; private BuildConstants(){} diff --git a/Examples/KitbotDemoFinal/src/main/java/frc/robot/Robot.java b/Examples/KitbotDemoFinal/src/main/java/frc/robot/Robot.java index 9253763..01aa48d 100644 --- a/Examples/KitbotDemoFinal/src/main/java/frc/robot/Robot.java +++ b/Examples/KitbotDemoFinal/src/main/java/frc/robot/Robot.java @@ -21,17 +21,17 @@ public class Robot extends LoggedRobot { @Override public void robotInit() { - Logger.getInstance().recordMetadata("ProjectName", "KitbotExample"); // Set a metadata value + Logger.recordMetadata("ProjectName", "KitbotExample"); // Set a metadata value if (isReal()) { - Logger.getInstance().addDataReceiver(new WPILOGWriter("/media/sda1/")); // Log to a USB stick - Logger.getInstance().addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables + Logger.addDataReceiver(new WPILOGWriter("/media/sda1/")); // Log to a USB stick + Logger.addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables new PowerDistribution(1, ModuleType.kRev); // Enables power distribution logging } else { - Logger.getInstance().addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables + Logger.addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables } - Logger.getInstance().start(); // Start logging! No more data receivers, replay sources, or metadata values may + Logger.start(); // Start logging! No more data receivers, replay sources, or metadata values may // be added. m_robotContainer = new RobotContainer(); diff --git a/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainIOSim.java b/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainIOSim.java index 7811f3a..e980479 100644 --- a/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainIOSim.java +++ b/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainIOSim.java @@ -4,11 +4,9 @@ package frc.robot.subsystems.Drivetrain; -import org.littletonrobotics.junction.Logger; - -import com.ctre.phoenixpro.controls.VelocityDutyCycle; -import com.ctre.phoenixpro.controls.VoltageOut; -import com.ctre.phoenixpro.hardware.TalonFX; +import com.ctre.phoenix6.controls.VelocityDutyCycle; +import com.ctre.phoenix6.controls.VoltageOut; +import com.ctre.phoenix6.hardware.TalonFX; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; import edu.wpi.first.wpilibj.simulation.RoboRioSim; diff --git a/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainSubsystem.java b/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainSubsystem.java index 814e684..141b219 100644 --- a/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainSubsystem.java +++ b/Examples/KitbotDemoFinal/src/main/java/frc/robot/subsystems/Drivetrain/DrivetrainSubsystem.java @@ -13,8 +13,7 @@ import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import edu.wpi.first.wpilibj2.command.CommandBase; -import edu.wpi.first.wpilibj2.command.RunCommand; +import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DrivetrainSubsystem extends SubsystemBase { @@ -25,7 +24,7 @@ public class DrivetrainSubsystem extends SubsystemBase { // We could make this select an io type based off of if the robot was real or // not - // Robot.isReal() ? new DrivetrainIOFalcon() : new DrivetrainIOSim() + // Robot.isReal() ? new DrivetrainIOReal() : new DrivetrainIOSim() DrivetrainIO io = new DrivetrainIOSim(); DrivetrainIOInputsAutoLogged inputs = new DrivetrainIOInputsAutoLogged(); @@ -44,15 +43,15 @@ public void drive(double speed, double angle, boolean isClosedLoop) { } // Command that wraps drive method - public CommandBase driveCommand(DoubleSupplier speed, DoubleSupplier angle, BooleanSupplier isClosedLoop) { - return new RunCommand(() -> drive(speed.getAsDouble(), angle.getAsDouble(), isClosedLoop.getAsBoolean()), this); + public Command driveCommand(DoubleSupplier speed, DoubleSupplier angle, BooleanSupplier isClosedLoop) { + return this.run(() -> drive(speed.getAsDouble(), angle.getAsDouble(), isClosedLoop.getAsBoolean())); } @Override public void periodic() { // This method will be called once per scheduler run io.updateInputs(inputs); - Logger.getInstance().processInputs("Drivetrain", inputs); + Logger.processInputs("Drivetrain", inputs); odometry.update( // Here we have to do a little hack to get rotation to work in sim @@ -61,6 +60,6 @@ public void periodic() { // Use differential drive kinematics to find the rotation rate based on the wheel speeds and distance between wheels .plus(Rotation2d.fromRadians((inputs.leftSpeedMetersPerSecond - inputs.rightSpeedMetersPerSecond) * 0.020 / Units.inchesToMeters(26))), inputs.leftPositionMeters, inputs.rightPositionMeters); - Logger.getInstance().recordOutput("Drivebase Pose", odometry.getPoseMeters()); + Logger.recordOutput("Drivebase Pose", odometry.getPoseMeters()); } } diff --git a/Examples/KitbotDemoFinal/vendordeps/AdvantageKit.json b/Examples/KitbotDemoFinal/vendordeps/AdvantageKit.json index fa08705..bef4a15 100644 --- a/Examples/KitbotDemoFinal/vendordeps/AdvantageKit.json +++ b/Examples/KitbotDemoFinal/vendordeps/AdvantageKit.json @@ -1,39 +1,33 @@ { "fileName": "AdvantageKit.json", "name": "AdvantageKit", - "version": "2.2.4", + "version": "4.1.2", "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", - "mavenUrls": [], + "frcYear": "2025", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" + ], "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json", "javaDependencies": [ { - "groupId": "org.littletonrobotics.akit.junction", - "artifactId": "wpilib-shim", - "version": "2.2.4" - }, - { - "groupId": "org.littletonrobotics.akit.junction", - "artifactId": "junction-core", - "version": "2.2.4" - }, - { - "groupId": "org.littletonrobotics.akit.conduit", - "artifactId": "conduit-api", - "version": "2.2.4" + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-java", + "version": "4.1.2" } ], "jniDependencies": [ { - "groupId": "org.littletonrobotics.akit.conduit", - "artifactId": "conduit-wpilibio", - "version": "2.2.4", + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-wpilibio", + "version": "4.1.2", "skipInvalidPlatforms": false, "isJar": false, "validPlatforms": [ "linuxathena", - "windowsx86-64", "linuxx86-64", - "osxuniversal" + "linuxarm64", + "osxuniversal", + "windowsx86-64" ] } ], diff --git a/Examples/KitbotDemoBasic/vendordeps/PhoenixPro.json b/Examples/KitbotDemoFinal/vendordeps/Phoenix6-frc2025-latest.json similarity index 53% rename from Examples/KitbotDemoBasic/vendordeps/PhoenixPro.json rename to Examples/KitbotDemoFinal/vendordeps/Phoenix6-frc2025-latest.json index 4418679..6f40c84 100644 --- a/Examples/KitbotDemoBasic/vendordeps/PhoenixPro.json +++ b/Examples/KitbotDemoFinal/vendordeps/Phoenix6-frc2025-latest.json @@ -1,147 +1,234 @@ { - "fileName": "PhoenixPro.json", - "name": "CTRE-Phoenix (Pro)", - "version": "23.0.8", - "frcYear": 2023, + "fileName": "Phoenix6-frc2025-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "25.4.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/phoenixpro/PhoenixPro-frc2023-latest.json", + "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.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-java", - "version": "23.0.8" + "version": "25.4.0" } ], "jniDependencies": [ { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "tools-sim", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonSRX", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonFX", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "23.0.8", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "23.0.8", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" @@ -149,24 +236,25 @@ ], "cppDependencies": [ { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-cpp", - "version": "23.0.8", - "libName": "CTRE_PhoenixPro_WPI", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPI", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", "sharedLibrary": true, @@ -174,29 +262,31 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "wpiapi-cpp-sim", - "version": "23.0.8", - "libName": "CTRE_PhoenixPro_WPISim", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPISim", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_PhoenixTools_Sim", "headerClassifier": "headers", "sharedLibrary": true, @@ -204,14 +294,15 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimTalonSRX", "headerClassifier": "headers", "sharedLibrary": true, @@ -219,89 +310,95 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonFX", - "version": "23.0.8", - "libName": "CTRE_SimTalonFX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.4.0", + "libName": "CTRE_SimVictorSPX", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simVictorSPX", - "version": "23.0.8", - "libName": "CTRE_SimVictorSPX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.4.0", + "libName": "CTRE_SimPigeonIMU", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simPigeonIMU", - "version": "23.0.8", - "libName": "CTRE_SimPigeonIMU", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.4.0", + "libName": "CTRE_SimCANCoder", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simCANCoder", - "version": "23.0.8", - "libName": "CTRE_SimCANCoder", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFX", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simProTalonFX", - "version": "23.0.8", - "libName": "CTRE_SimProTalonFX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFXS", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimProCANcoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -309,14 +406,15 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", "sharedLibrary": true, @@ -324,6 +422,55 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" diff --git a/Examples/KitbotDemoFinal/vendordeps/WPILibNewCommands.json b/Examples/KitbotDemoFinal/vendordeps/WPILibNewCommands.json index bd535bf..3718e0a 100644 --- a/Examples/KitbotDemoFinal/vendordeps/WPILibNewCommands.json +++ b/Examples/KitbotDemoFinal/vendordeps/WPILibNewCommands.json @@ -3,6 +3,7 @@ "name": "WPILib-New-Commands", "version": "1.0.0", "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", "mavenUrls": [], "jsonUrl": "", "javaDependencies": [ diff --git a/Examples/KitbotDemoSim/.gitignore b/Examples/KitbotDemoSim/.gitignore index a8d1911..34cbaac 100644 --- a/Examples/KitbotDemoSim/.gitignore +++ b/Examples/KitbotDemoSim/.gitignore @@ -169,4 +169,19 @@ out/ .fleet # Simulation GUI and other tools window save file +networktables.json +simgui.json *-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/Examples/KitbotDemoSim/.vscode/settings.json b/Examples/KitbotDemoSim/.vscode/settings.json index 4ed293b..612cdd0 100644 --- a/Examples/KitbotDemoSim/.vscode/settings.json +++ b/Examples/KitbotDemoSim/.vscode/settings.json @@ -25,5 +25,36 @@ } }, ], - "java.test.defaultConfig": "WPIlibUnitTests" + "java.test.defaultConfig": "WPIlibUnitTests", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] } diff --git a/Examples/KitbotDemoSim/.wpilib/wpilib_preferences.json b/Examples/KitbotDemoSim/.wpilib/wpilib_preferences.json index ad4fe20..ecbd975 100644 --- a/Examples/KitbotDemoSim/.wpilib/wpilib_preferences.json +++ b/Examples/KitbotDemoSim/.wpilib/wpilib_preferences.json @@ -1,6 +1,6 @@ { "enableCppIntellisense": false, "currentLanguage": "java", - "projectYear": "2023", + "projectYear": "2025", "teamNumber": 8033 } \ No newline at end of file diff --git a/Examples/KitbotDemoSim/WPILib-License.md b/Examples/KitbotDemoSim/WPILib-License.md index 3d5a824..645e542 100644 --- a/Examples/KitbotDemoSim/WPILib-License.md +++ b/Examples/KitbotDemoSim/WPILib-License.md @@ -1,4 +1,4 @@ -Copyright (c) 2009-2021 FIRST and other WPILib contributors +Copyright (c) 2009-2024 FIRST and other WPILib contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Examples/KitbotDemoSim/build.gradle b/Examples/KitbotDemoSim/build.gradle index c9bf1a5..3134468 100644 --- a/Examples/KitbotDemoSim/build.gradle +++ b/Examples/KitbotDemoSim/build.gradle @@ -1,10 +1,12 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2025.2.1" } -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} def ROBOT_MAIN_CLASS = "frc.robot.Main" @@ -31,6 +33,8 @@ deploy { frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { files = project.fileTree('src/main/deploy') directory = '/home/lvuser/deploy' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project } } } @@ -43,11 +47,12 @@ def deployArtifact = deploy.targets.roborio.artifacts.frcJava wpi.java.debugJni = false // Set this to true to enable desktop support. -def includeDesktopSupport = true +def includeDesktopSupport = false // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. // Also defines JUnit 5. dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() implementation wpi.java.deps.wpilib() implementation wpi.java.vendor.java() @@ -65,12 +70,11 @@ dependencies { nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) simulationRelease wpi.sim.enableRelease() - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' def akitJson = new groovy.json.JsonSlurper().parseText(new File(projectDir.getAbsolutePath() + "/vendordeps/AdvantageKit.json").text) - annotationProcessor "org.littletonrobotics.akit.junction:junction-autolog:$akitJson.version" + annotationProcessor "org.littletonrobotics.akit:akit-autolog:$akitJson.version" } test { @@ -87,6 +91,7 @@ wpi.sim.addDriverstation() // knows where to look for our Robot Class. jar { from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + from sourceSets.main.allSource manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) duplicatesStrategy = DuplicatesStrategy.INCLUDE } @@ -101,16 +106,7 @@ tasks.withType(JavaCompile) { options.compilerArgs.add '-XDstringConcat=inline' } -repositories { - maven { - url = uri("https://maven.pkg.github.com/Mechanical-Advantage/AdvantageKit") - credentials { - username = "Mechanical-Advantage-Bot" - password = "\u0067\u0068\u0070\u005f\u006e\u0056\u0051\u006a\u0055\u004f\u004c\u0061\u0079\u0066\u006e\u0078\u006e\u0037\u0051\u0049\u0054\u0042\u0032\u004c\u004a\u006d\u0055\u0070\u0073\u0031\u006d\u0037\u004c\u005a\u0030\u0076\u0062\u0070\u0063\u0051" - } - } -} - -configurations.all { - exclude group: "edu.wpi.first.wpilibj" -} +task(replayWatch, type: JavaExec) { + mainClass = "org.littletonrobotics.junction.ReplayWatch" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.jar b/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.jar index 249e583..a4b76b9 100644 Binary files a/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.jar and b/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.properties b/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.properties index c23a1b3..34bd9ce 100644 --- a/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.properties +++ b/Examples/KitbotDemoSim/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=permwrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=permwrapper/dists diff --git a/Examples/KitbotDemoSim/gradlew b/Examples/KitbotDemoSim/gradlew index a69d9cb..f5feea6 100644 --- a/Examples/KitbotDemoSim/gradlew +++ b/Examples/KitbotDemoSim/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/Examples/KitbotDemoSim/gradlew.bat b/Examples/KitbotDemoSim/gradlew.bat index f127cfd..9d21a21 100644 --- a/Examples/KitbotDemoSim/gradlew.bat +++ b/Examples/KitbotDemoSim/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -42,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/Examples/KitbotDemoSim/networktables.json b/Examples/KitbotDemoSim/networktables.json deleted file mode 100644 index fe51488..0000000 --- a/Examples/KitbotDemoSim/networktables.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/Examples/KitbotDemoSim/settings.gradle b/Examples/KitbotDemoSim/settings.gradle index 48c039e..c493958 100644 --- a/Examples/KitbotDemoSim/settings.gradle +++ b/Examples/KitbotDemoSim/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2025' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') @@ -20,8 +20,11 @@ pluginManagement { } def frcHomeMaven = new File(frcHome, 'maven') maven { - name 'frcHome' - url frcHomeMaven + name = 'frcHome' + url = frcHomeMaven } } } + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/Examples/KitbotDemoSim/simgui-ds.json b/Examples/KitbotDemoSim/simgui-ds.json deleted file mode 100644 index 51e9918..0000000 --- a/Examples/KitbotDemoSim/simgui-ds.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "Keyboard 0 Settings": { - "window": { - "visible": true - } - }, - "keyboardJoysticks": [ - { - "axisConfig": [ - {}, - { - "decKey": 83, - "incKey": 87 - }, - { - "decKey": 69, - "decayRate": 0.0, - "incKey": 82, - "keyRate": 0.009999999776482582 - }, - {}, - { - "decKey": 65, - "incKey": 68 - } - ], - "axisCount": 8, - "buttonCount": 4, - "buttonKeys": [ - 90, - 88, - 67, - 86 - ], - "povConfig": [ - { - "key0": 328, - "key135": 323, - "key180": 322, - "key225": 321, - "key270": 324, - "key315": 327, - "key45": 329, - "key90": 326 - } - ], - "povCount": 1 - }, - { - "axisConfig": [ - { - "decKey": 74, - "incKey": 76 - }, - { - "decKey": 73, - "incKey": 75 - } - ], - "axisCount": 2, - "buttonCount": 4, - "buttonKeys": [ - 77, - 44, - 46, - 47 - ], - "povCount": 0 - }, - { - "axisConfig": [ - { - "decKey": 263, - "incKey": 262 - }, - { - "decKey": 265, - "incKey": 264 - } - ], - "axisCount": 2, - "buttonCount": 6, - "buttonKeys": [ - 260, - 268, - 266, - 261, - 269, - 267 - ], - "povCount": 0 - }, - { - "axisCount": 0, - "buttonCount": 0, - "povCount": 0 - } - ], - "robotJoysticks": [ - { - "guid": "Keyboard0" - } - ] -} diff --git a/Examples/KitbotDemoSim/simgui.json b/Examples/KitbotDemoSim/simgui.json deleted file mode 100644 index 44591ba..0000000 --- a/Examples/KitbotDemoSim/simgui.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "HALProvider": { - "Other Devices": { - "window": { - "visible": false - } - } - }, - "NTProvider": { - "types": { - "/FMSInfo": "FMSInfo", - "/LiveWindow/Ungrouped/Scheduler": "Scheduler", - "/LiveWindow/Ungrouped/Talon FX (Pro) [0]": "Motor Controller", - "/LiveWindow/Ungrouped/Talon FX (Pro) [1]": "Motor Controller" - } - }, - "NetworkTables Info": { - "Clients": { - "open": true - }, - "Connections": { - "open": true - }, - "Server": { - "Subscribers": { - "open": true - }, - "open": true - }, - "visible": true - } -} diff --git a/Examples/KitbotDemoSim/src/main/java/frc/robot/Robot.java b/Examples/KitbotDemoSim/src/main/java/frc/robot/Robot.java index 3d9c376..a14483b 100644 --- a/Examples/KitbotDemoSim/src/main/java/frc/robot/Robot.java +++ b/Examples/KitbotDemoSim/src/main/java/frc/robot/Robot.java @@ -21,17 +21,17 @@ public class Robot extends LoggedRobot { @Override public void robotInit() { - Logger.getInstance().recordMetadata("ProjectName", "KitbotExample"); // Set a metadata value + Logger.recordMetadata("ProjectName", "KitbotExample"); // Set a metadata value if (isReal()) { - Logger.getInstance().addDataReceiver(new WPILOGWriter("/media/sda1/")); // Log to a USB stick - Logger.getInstance().addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables + Logger.addDataReceiver(new WPILOGWriter("/media/sda1/")); // Log to a USB stick + Logger.addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables new PowerDistribution(1, ModuleType.kRev); // Enables power distribution logging } else { - Logger.getInstance().addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables + Logger.addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables } - Logger.getInstance().start(); // Start logging! No more data receivers, replay sources, or metadata values may + Logger.start(); // Start logging! No more data receivers, replay sources, or metadata values may // be added. m_robotContainer = new RobotContainer(); } diff --git a/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainIOSim.java b/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainIOSim.java index 9fa1eca..82888c4 100644 --- a/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainIOSim.java +++ b/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainIOSim.java @@ -4,8 +4,8 @@ package frc.robot.Subsystems.Drivetrain; -import com.ctre.phoenixpro.controls.VoltageOut; -import com.ctre.phoenixpro.hardware.TalonFX; +import com.ctre.phoenix6.controls.VoltageOut; +import com.ctre.phoenix6.hardware.TalonFX; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim.KitbotGearing; diff --git a/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainSubsystem.java b/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainSubsystem.java index a8df8a9..69f548a 100644 --- a/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainSubsystem.java +++ b/Examples/KitbotDemoSim/src/main/java/frc/robot/Subsystems/Drivetrain/DrivetrainSubsystem.java @@ -8,17 +8,12 @@ import org.littletonrobotics.junction.Logger; -import com.ctre.phoenixpro.controls.VoltageOut; -import com.ctre.phoenixpro.hardware.TalonFX; - import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import edu.wpi.first.wpilibj2.command.CommandBase; -import edu.wpi.first.wpilibj2.command.RunCommand; +import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; -import frc.robot.Constants; public class DrivetrainSubsystem extends SubsystemBase { DrivetrainIO io = new DrivetrainIOSim(); @@ -34,21 +29,21 @@ private void setVoltages(double left, double right) { io.setVolts(left, right); } - public CommandBase setVoltagesCommand(DoubleSupplier left, DoubleSupplier right) { - return new RunCommand(() -> this.setVoltages(left.getAsDouble(), right.getAsDouble()), this); + public Command setVoltagesCommand(DoubleSupplier left, DoubleSupplier right) { + return this.run(() -> this.setVoltages(left.getAsDouble(), right.getAsDouble())); } - public CommandBase setVoltagesArcadeCommand(DoubleSupplier drive, DoubleSupplier steer) { - return new RunCommand(() -> { + public Command setVoltagesArcadeCommand(DoubleSupplier drive, DoubleSupplier steer) { + return this.run(() -> { var speeds = DifferentialDrive.arcadeDriveIK(drive.getAsDouble(), steer.getAsDouble(), false); this.setVoltages(speeds.left * 12, speeds.right * 12); - }, this); + }); } @Override public void periodic() { io.updateInputs(inputs); - Logger.getInstance().processInputs("Drivetrain", inputs); + Logger.processInputs("Drivetrain", inputs); odometry.update( odometry.getPoseMeters().getRotation() @@ -57,6 +52,6 @@ public void periodic() { .plus(Rotation2d.fromRadians((inputs.leftVelocityMetersPerSecond - inputs.rightVelocityMetersPerSecond) * 0.020 / Units.inchesToMeters(26))), inputs.leftPositionMeters, inputs.rightPositionMeters); - Logger.getInstance().recordOutput("Drivetrain Pose", odometry.getPoseMeters()); + Logger.recordOutput("Drivetrain Pose", odometry.getPoseMeters()); } } diff --git a/Examples/KitbotDemoSim/vendordeps/AdvantageKit.json b/Examples/KitbotDemoSim/vendordeps/AdvantageKit.json index fa08705..bef4a15 100644 --- a/Examples/KitbotDemoSim/vendordeps/AdvantageKit.json +++ b/Examples/KitbotDemoSim/vendordeps/AdvantageKit.json @@ -1,39 +1,33 @@ { "fileName": "AdvantageKit.json", "name": "AdvantageKit", - "version": "2.2.4", + "version": "4.1.2", "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", - "mavenUrls": [], + "frcYear": "2025", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" + ], "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json", "javaDependencies": [ { - "groupId": "org.littletonrobotics.akit.junction", - "artifactId": "wpilib-shim", - "version": "2.2.4" - }, - { - "groupId": "org.littletonrobotics.akit.junction", - "artifactId": "junction-core", - "version": "2.2.4" - }, - { - "groupId": "org.littletonrobotics.akit.conduit", - "artifactId": "conduit-api", - "version": "2.2.4" + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-java", + "version": "4.1.2" } ], "jniDependencies": [ { - "groupId": "org.littletonrobotics.akit.conduit", - "artifactId": "conduit-wpilibio", - "version": "2.2.4", + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-wpilibio", + "version": "4.1.2", "skipInvalidPlatforms": false, "isJar": false, "validPlatforms": [ "linuxathena", - "windowsx86-64", "linuxx86-64", - "osxuniversal" + "linuxarm64", + "osxuniversal", + "windowsx86-64" ] } ], diff --git a/Examples/KitbotDemoSim/vendordeps/PhoenixPro.json b/Examples/KitbotDemoSim/vendordeps/Phoenix6-frc2025-latest.json similarity index 53% rename from Examples/KitbotDemoSim/vendordeps/PhoenixPro.json rename to Examples/KitbotDemoSim/vendordeps/Phoenix6-frc2025-latest.json index 4418679..6f40c84 100644 --- a/Examples/KitbotDemoSim/vendordeps/PhoenixPro.json +++ b/Examples/KitbotDemoSim/vendordeps/Phoenix6-frc2025-latest.json @@ -1,147 +1,234 @@ { - "fileName": "PhoenixPro.json", - "name": "CTRE-Phoenix (Pro)", - "version": "23.0.8", - "frcYear": 2023, + "fileName": "Phoenix6-frc2025-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "25.4.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/phoenixpro/PhoenixPro-frc2023-latest.json", + "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.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-java", - "version": "23.0.8" + "version": "25.4.0" } ], "jniDependencies": [ { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "tools-sim", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonSRX", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonFX", - "version": "23.0.8", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "23.0.8", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "23.0.8", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "23.0.8", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" @@ -149,24 +236,25 @@ ], "cppDependencies": [ { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-cpp", - "version": "23.0.8", - "libName": "CTRE_PhoenixPro_WPI", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPI", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro", + "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", "sharedLibrary": true, @@ -174,29 +262,31 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "linuxathena" ], "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "wpiapi-cpp-sim", - "version": "23.0.8", - "libName": "CTRE_PhoenixPro_WPISim", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPISim", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_PhoenixTools_Sim", "headerClassifier": "headers", "sharedLibrary": true, @@ -204,14 +294,15 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimTalonSRX", "headerClassifier": "headers", "sharedLibrary": true, @@ -219,89 +310,95 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simTalonFX", - "version": "23.0.8", - "libName": "CTRE_SimTalonFX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.4.0", + "libName": "CTRE_SimVictorSPX", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simVictorSPX", - "version": "23.0.8", - "libName": "CTRE_SimVictorSPX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.4.0", + "libName": "CTRE_SimPigeonIMU", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simPigeonIMU", - "version": "23.0.8", - "libName": "CTRE_SimPigeonIMU", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.4.0", + "libName": "CTRE_SimCANCoder", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simCANCoder", - "version": "23.0.8", - "libName": "CTRE_SimCANCoder", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFX", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", - "artifactId": "simProTalonFX", - "version": "23.0.8", - "libName": "CTRE_SimProTalonFX", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFXS", "headerClassifier": "headers", "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimProCANcoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -309,14 +406,15 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" }, { - "groupId": "com.ctre.phoenixpro.sim", + "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "23.0.8", + "version": "25.4.0", "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", "sharedLibrary": true, @@ -324,6 +422,55 @@ "binaryPlatforms": [ "windowsx86-64", "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" diff --git a/Examples/KitbotDemoSim/vendordeps/WPILibNewCommands.json b/Examples/KitbotDemoSim/vendordeps/WPILibNewCommands.json index bd535bf..3718e0a 100644 --- a/Examples/KitbotDemoSim/vendordeps/WPILibNewCommands.json +++ b/Examples/KitbotDemoSim/vendordeps/WPILibNewCommands.json @@ -3,6 +3,7 @@ "name": "WPILib-New-Commands", "version": "1.0.0", "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", "mavenUrls": [], "jsonUrl": "", "javaDependencies": [ diff --git a/Stage-3-Writeups/Example-Current-Zeroing.md b/Stage-3-Writeups/Example-Current-Zeroing.md new file mode 100644 index 0000000..ee61d5e --- /dev/null +++ b/Stage-3-Writeups/Example-Current-Zeroing.md @@ -0,0 +1,44 @@ +# This is an example: Using Current Sensing for Zeroing Mechanisms + +This is a made up example for a stage 3 project writeup. + +## Abstract + +Zeroing or homing mechanisms is an important part of reliable controls. +To achieve we have used limit switches and absolute encoders. +However, both of these methods have drawbacks. +Limit switches have had reliability issues, and absolute encoders are not always feasible to design in. +To get around these limitations, we attempt to use current sensing to zero mechanisms in this project. + +## Procedure + +Current sensing is the process of using a "spike" in current when a mechanism is stalled to detect the presence of a hard stop. +This gives the mechanism a reference for its position. + +To test this method, we use an elevator on our previous robot. +The following method generates a Command to home the mechanism. + +```Java +public Command zero() { + return this.run(() -> io.setVoltage(-1.0)) + .until(() -> inputs.statorCurrent > 20.0) + .finallyDo( + (interrupted) -> { + io.setVoltage(0.0); + if (!interrupted) io.setPosition(0.0); + } + ); +} +``` + +This Command starts by running the elevator at a set voltage towards the hard stop. +Once the current goes above a threshold, we set the output to 0 volts. +Then, if the elevator was not interrupted by another command we set the position recorded by the encoder to 0. +The current threshold must be tuned to not trigger when the elevator starts moving, but still trigger on stall without damaging the elevator. +Future improvements could include a minimum motion time or other filtering. + +This command worked reliably to zero the elevator, both from starts at or near zero and for starts while extended. + +## Conclusion + +Current zeroing is a reliable and low overhead way to home a mechanism 👍. diff --git a/Stage-3-Writeups/Superstructure-Triggers.md b/Stage-3-Writeups/Superstructure-Triggers.md new file mode 100644 index 0000000..4fbb40f --- /dev/null +++ b/Stage-3-Writeups/Superstructure-Triggers.md @@ -0,0 +1,143 @@ +# Use of a Trigger and State Machine Backed Superstructure File for Subsystem Coordination + +Lewis Seiden, 2024 Crescendo Offseason + +## Abstract + +This project is a [refactor of 8033s 2024 codebase](https://github.com/HighlanderRobotics/Crescendo/tree/superstructure-subsystem) to use an explicit finite state machine to control subsystems rather than simple Commands. +This refactor would help with approaching the codebase and organizing behavior, but would add some amount of boilerplate and a monolithic file. + +## Procedure and Findings + +I started on this project after seeing [2974's](https://github.com/WaltonRobotics/Crescendo/blob/main/src/main/java/frc/robot/subsystems/Superstructure.java) and [5940's](https://github.com/BREAD5940/2024-Onseason/blob/main/src/main/java/frc/robot/subsystems/Superstructure.Java) code, and wondered if using explicitly defined states could help manage our code. +These teams had a superstructure file which coordinated several subsystems based on a state machine. +This allowed for complex control flow with less places for confusion. + +5940's Superstructure takes the form of a single subsystem with several IO layers in it. +This subsystem handled their elevator, pivot, and feeder for their 2024 robot. +These mechanical systems worked in concert and required some compensation based on the state of each of the others, so having them tightly coupled in one subsystem makes sense. + +To request behavior the class has a number of boolean members, like `requestHome`. +The current state of the class is tracked, and passed through a massive if-else block. +Requests are used to manage state transitions, and IO outputs are set through the block. + +This has some advantages, its simple and easy to understand. +It clearly encapsulates several IO objects in a subsystem. +However it is limited to fully encapsulating everything in a subsystem, and doesn't address the problem of wanting additional layers on top of subsystem. +It also invites bugs with a large managed if-else block and no hardware mutexing within the superstructure. + +2974's superstructure file is not a subsystem. +Instead, it takes in several subsystems and maps *many* triggers to coordinate them. +They do not explicitly define states, instead they define command bindings using the many triggers defined in the file. + +```Java +// An example binding + (stateTrg_intake.and(trg_subwooferAngle.or(RobotModeTriggers.autonomous()).and(trg_straightThroughReq.negate()))) + .onTrue( + Commands.parallel(m_intake.fullPower(), m_conveyor.startSlower()) + ); +``` + +This setup is more flexible and easier to integrate with existing command based code, as well as demonstrating the power of Triggers. + +However, it requires setting up a *lot* of triggers and doesn't explicitly define states as clearly as 5940. + +To this end, I combine the two approaches by explicitly defining a state machine with a Trigger on each state. +By using a Trigger for each state, we can easily integrate with existing Subsystems. +By using an explicit state machine, it clearly structures the codebase. + +Because of our highly integrated robot architecture for Banshee, all non-swerve subsystems were integrated in the superstructure. +This includes the intake, carriage, feeder, pivot, both flywheels, and elevator. +I also split the shooter into flywheels and the pivot for cleanliness. + +To set up the superstructure, I wrote out all the states that the robot would be in and the edges between them. +This is the original state machine I worked from. + +Banshee State Machine + +And this is a cleaned up version from the final code. + +```mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' } } }%% +graph TD; + +IDLE ---> INTAKE; +INTAKE --> INDEX_SHOOTER; +INDEX_SHOOTER --> READY_INDEXED_SHOOTER; +READY_INDEXED_SHOOTER --> PRESHOOT; +READY_INDEXED_SHOOTER --> PREFEED; +READY_INDEXED_SHOOTER --> PRESUB; +PRESHOOT --> SHOOT; +PREFEED --> SHOOT; +PRESUB --> SHOOT; + +SHOOT --> IDLE; + +INTAKE --> INDEX_CARRIAGE; +INDEX_CARRIAGE --> READY_INDEXED_CARRIAGE; +READY_INDEXED_CARRIAGE --> PREAMP; +PREAMP --> AMP; +AMP --> IDLE; +READY_INDEXED_CARRIAGE --> INDEX_SHOOTER; +READY_INDEXED_SHOOTER --> REVERSE_INDEX_CARRIAGE; +REVERSE_INDEX_CARRIAGE --> INTAKE; + +IDLE --> SPIT; +READY_INDEXED_SHOOTER --> SPIT; +READY_INDEXED_CARRIAGE --> SPIT; +SPIT --> PRECLIMB; +PRECLIMB --> CLIMB; +PRECLIMB --> IDLE; +CLIMB --> HOME_ELEVATOR; +CLIMB --> PRECLIMB; + +IDLE --> HOME_ELEVATOR; +HOME_ELEVATOR --> IDLE; +IDLE --> HOME_SHOOTER; +HOME_SHOOTER --> IDLE; +``` + +The current state (and previous state) is stored in an enum member of the superstructure. +A HashMap of state -> Trigger bindings is created, and each Trigger is setup up to have a set of commands including a call to `setState` which advances through the graph. +`setState` is called through the use of Trigger composition with `.and`. +An example of one of these bindings is bellow. + +```Java +stateTriggers + .get(SuperState.INTAKE) + .whileTrue(intake.intake()) + .onFalse(intake.stop()) + .whileTrue(carriage.setVoltageCmd(CarriageSubsystem.INDEXING_VOLTAGE)) + // State Transition + .and(carriage.beambreakTrig.or(feeder.beambreakTrig)) + .onTrue( + this.setState( + target.get().isSpeakerAlike() + ? SuperState.INDEX_SHOOTER + : SuperState.INDEX_CARRIAGE)); +``` + +The binding starts with a call to get the Trigger from the map of state Triggers. +Then we bind the commands that run while that state is active (ie which subsystems have non-default behavior). +This includes running the intake and spinning the carriage indexing wheels. +Then we `.and` on another Trigger to check if either beambreak sees something. +If they do, we run a Command to move to the next state in the graph. +This state is a bit of a special case because of its branching behavior here. + +This setup is a simple way to declare the mechanism states for each robot state, and how the robot states transition between each other. +In my opinion, it is relatively readable if somewhat verbose. +I wrote each command as being triggered by a separate `.whileTrue` clause, but this makes sequences within states more difficult to write when multiple subsystems may need to coordinate with each other, forcing the state graph to grow larger. +In the future, I would likely use Command groups instead. + +Inputs to the superstructure are handled by several member Triggers, called "requests", of the superstructure such as `intakeRequest`. +These make it easy to bind default bindings, but make it hard to request states from other commands such as autos. +To remedy this, some requests had an additional boolean member request which could be set by a Command factory in the superstructure. +This boolean would be `.or`ed onto the existing Trigger, and the Command would be called from autos to request needed states. +This was a somewhat hacky workaround, and in the future going all Trigger or all boolean + Command bindings is likely optimal. + +## Conclusion + +This pattern is a useful tool to have for programming. +It provides a way to make "supersystems" on the robot that coordinate while still maintaining separate mutexes for their components. +The system is somewhat clunky when applied to a whole robot, especially for smaller states like "Home Elevator". +The process of writing out a full robot state machine was helpful to conceptualize what the robot needed to do and would be helpful to organize code whether or not the superstructure pattern is used.