diff --git a/.codespellignore b/.codespellignore deleted file mode 100644 index 9bc0532..0000000 --- a/.codespellignore +++ /dev/null @@ -1 +0,0 @@ -tread diff --git a/.editorconfig b/.editorconfig index 227542f..575ff0a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,4 +12,8 @@ trim_trailing_whitespace = true # 4 space indentation [*.java] indent_style = space -indent_size = 4 \ No newline at end of file +indent_size = 4 + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index dfe0770..dbb9326 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,10 @@ # Auto detect text files and perform LF normalization * text=auto +src/main/deploy/photon-configs/front-right.zip filter=lfs diff=lfs merge=lfs -text +src/main/deploy/photon-configs/back-left.zip filter=lfs diff=lfs merge=lfs -text +src/main/deploy/photon-configs/front-left.zip filter=lfs diff=lfs merge=lfs -text +customAssets/Robot_Pumbaa/model_0.glb filter=lfs diff=lfs merge=lfs -text +customAssets/Robot_Pumbaa/model_1.glb filter=lfs diff=lfs merge=lfs -text +customAssets/Robot_Pumbaa/model_2.glb filter=lfs diff=lfs merge=lfs -text +customAssets/Robot_Pumbaa/model_3.glb filter=lfs diff=lfs merge=lfs -text +customAssets/Robot_Pumbaa/model.glb filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cdff286..9523108 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,16 +18,14 @@ jobs: - name: Checkout uses: actions/checkout@v3 # v2 minimum required - name: Run check style - uses: nikitasavinov/checkstyle-action@master + uses: dbelyaev/action-checkstyle@master with: github_token: ${{ secrets.GITHUB_TOKEN }} reporter: 'github-check' - tool_name: 'testtool' fail_on_error: true filter_mode: nofilter level: error checkstyle_config: checks.xml - checkstyle_version: '9.2.1' spell_check: name: Spell Check runs-on: ubuntu-latest @@ -38,8 +36,7 @@ jobs: uses: codespell-project/actions-codespell@master with: check_filenames: true - ignore_words_file: .codespellignore - merge_conflict: + merge_conflict_job: runs-on: ubuntu-latest name: Find merge conflicts steps: @@ -48,6 +45,22 @@ jobs: # Run the actual merge conflict finder - name: Merge Conflict finder uses: olivernybroe/action-conflict-finder@v4.0 + javadoc: + name: Test Javadocs Generation + # The type of runner that the job will run on + runs-on: ubuntu-22.04 + # This grabs the WPILib docker container + container: wpilib/roborio-cross-ubuntu:2024-22.04 + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + # Grant execute permission for gradlew + - name: Grant execute permission for gradlew + run: chmod +x gradlew + # Runs a single command using the runners shell + - name: Create Javadocs + run: ./gradlew javadoc build: name: Build # The type of runner that the job will run on diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..f893315 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,51 @@ +# This is a basic workflow to help you get started with Actions + +name: Javadoc + +# Controls when the action will run. +on: + # Triggers the workflow on push + push: + branches: + - main + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + javadoc: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + name: Generate Javadocs + # The type of runner that the job will run on + runs-on: ubuntu-22.04 + # This grabs the WPILib docker container + container: wpilib/roborio-cross-ubuntu:2023-22.04 + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + # Grant execute permission for gradlew + - name: Grant execute permission for gradlew + run: chmod +x gradlew + # Runs a single command using the runners shell + - name: Create Javadocs + run: ./gradlew javadoc + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: './build/docs/javadoc' + - name: Deploy to Github Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore index 5528d4f..8f73c0c 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,13 @@ logs/ # Folder that has CTRE Phoenix Sim device config storage ctre_sim/ + +BuildConstants.java +networktables.json +simgui-ds.json +simgui.json + +!src/main/deploy/photon-configs/*.zip + +/*.wpilog +/*-profile.json diff --git a/.pathplanner/settings.json b/.pathplanner/settings.json new file mode 100644 index 0000000..423969a --- /dev/null +++ b/.pathplanner/settings.json @@ -0,0 +1,18 @@ +{ + "robotWidth": 0.876, + "robotLength": 0.876, + "holonomicMode": true, + "pathFolders": [ + "P321", + "P123", + "Resnick 3", + "Resnick 4", + "Resnick 5" + ], + "autoFolders": [], + "defaultMaxVel": 5.0, + "defaultMaxAccel": 4.0, + "defaultMaxAngVel": 540.0, + "defaultMaxAngAccel": 720.0, + "maxModuleSpeed": 4.5 +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..f78081d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "recommendations": [ + "vscjava.vscode-java-debug", + "editorconfig.editorconfig", + "vscjava.vscode-java-pack", + "github.vscode-pull-request-github", + "eamodio.gitlens", + "redhat.java", + "wpilibsuite.vscode-wpilib" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index c9c9713..14c10b0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,18 +4,24 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - + { + "type": "java", + "name": "Main", + "request": "launch", + "mainClass": "frc.robot.Main", + "projectName": "FRC2024" + }, { "type": "wpilib", "name": "WPILib Desktop Debug", "request": "launch", - "desktop": true, + "desktop": true }, { "type": "wpilib", "name": "WPILib roboRIO Debug", "request": "launch", - "desktop": false, + "desktop": false } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index ded1ec5..41509c4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,11 +18,9 @@ { "name": "WPIlibUnitTests", "workingDirectory": "${workspaceFolder}/build/jni/release", - "vmargs": [ - "-Djava.library.path=${workspaceFolder}/build/jni/release" - ], + "vmargs": [ "-Djava.library.path=${workspaceFolder}/build/jni/release" ], "env": { - "LD_LIBRARY_PATH": "${workspaceFolder}/build/jni/release", + "LD_LIBRARY_PATH": "${workspaceFolder}/build/jni/release" , "DYLD_LIBRARY_PATH": "${workspaceFolder}/build/jni/release" } }, diff --git a/README.md b/README.md index f02539a..6156647 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,25 @@ -# BaseFalconSwerve
-**Basic Swerve Code for a Swerve Module using Falcon Motors, a CTRE CANCoder, and a CTRE Pigeon Gyro**
-This code was designed with SDS MK3 style modules in mind, but should be easily adaptable to other styles of modules.
- -**Setting Constants** ----- -The following things must be adjusted to your robot and module's specific constants in the Constants.java file (all distance units must be in meters, and rotation units in radians):
-1. Gyro Settings: ```pigeonID``` and ```invertGyro``` (ensure that the gyro rotation is CCW+ (Counter Clockwise Positive) -2. ```trackWidth``` (Center to Center distance of left and right modules) -3. ```wheelBase``` (Center to Center distance of front and rear module wheels) -4. ```wheelDiameter``` -5. ```driveGearRatio``` (for SDS MK3 either: (8.16 / 1) or (6.86 / 1)) -6. ```angleGearRatio``` (for SDS MK3: (12.8 / 1)) -7. Angle Motor PID Values: - * To tune start with a low P value (0.01). - * Multiply by 10 until the module starts oscillating around the set point - * Scale back by searching for the value (for example, if it starts oscillating at a P of 10, then try (10 -> 5 -> 7.5 -> etc)) until the module overshoots the setpoint but corrects with no oscillation. - * Repeat the process for D. The D value will basically help prevent the overshoot. Ignore I. -8. Get the drive characterization values (KS, KV, KA) by using the WPILib characterization tool, found [here](https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-characterization/introduction.html). You will need to lock your modules straight forward, and complete the characterization as if it was a standard tank drive. -9. Tune drive kP until it doesn't overshoot and doesn't oscillate around a target velocity. -10. For ```maxSpeed``` and ```maxAngularVelocity``` you can use the theoretical values, but it is better to physically drive the robot and find the actual max values. -11. Set ```canCoderInvert``` and ```angleMotorInvert``` such that both are CCW+. -12. In the module specific constants, set the can ID's of the motors and CANCoders for the respective modules. -13. Setting Offsets - * For finding the offsets, use a piece of 1x1 metal that is straight against the forks of the front and back modules (on the left and right side) to ensure that the modules are straight. - * You need to point the bevel gears of all the wheels in the same direction (either facing left or right). And preferably you should have the wheels facing in the direction where a positive input to the drive motor drives forward. If for some reason you set the offsets with the wheels backwards, you can change the ```driveMotorInvert``` to fix. - * Open smartdashboard (or shuffleboard and go to the smartdashboard tab), you will see 4 printouts called "Mod 0 Cancoder", "Mod 1 Cancoder", etc. If you have already straightened the modules, copy those 4 numbers exactly (to 2 decimal places) to their respective ```angleOffset``` variable in constants. - - -**Controller Mappings** ----- -This code is natively setup to use a xbox controller to control the swerve driver.
-The Left Stick controls translation (forwards and sideways movement), and the Right Stick controls rotation.
-The Y button is mapped to zero the gyro, useful if the gyro drifts mid match, just rotate the robot forwards, and press Y to rezero. +# Jetsi Offseason + +[![CI](https://github.com/Frc5572/Jetsi-Offseason/actions/workflows/main.yml/badge.svg)](https://github.com/Frc5572/Jetsi-Offseason/actions/workflows/main.yml) [![Java Docs](https://img.shields.io/badge/docs-2024-blue)](https://frc5572.github.io/FRC2024/) + +Code for our 2024 robot + +## CAN IDs + +### Swerve + +Front Left Drive Motor - +Front Left Angle Motor - +Front Left canCoder - + +Front Right Drive Motor - +Front Right Angle Motor - +Front Right canCoder - + +Back Left Drive Motor - +Back Left Angle Motor - +Back Left canCoder - + +Back Right Drive Motor - +Back Right Angle Motor - +Back Right canCoder - diff --git a/build.gradle b/build.gradle index 8fa63ce..ea5f98b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2024.2.1" + id "edu.wpi.first.GradleRIO" version "2024.3.2" + id "com.peterabeles.gversion" version "1.10" } java { @@ -49,7 +50,32 @@ def includeDesktopSupport = false // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. // Also defines JUnit 5. + +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" + } + } + mavenLocal() + maven { url 'https://jitpack.io' } +} + +configurations.all { + exclude group: "edu.wpi.first.wpilibj" +} + +task(checkAkitInstall, dependsOn: "classes", type: JavaExec) { + mainClass = "org.littletonrobotics.junction.CheckInstall" + classpath = sourceSets.main.runtimeClasspath +} +compileJava.finalizedBy checkAkitInstall + dependencies { + def akitJson = new groovy.json.JsonSlurper().parseText(new File(projectDir.getAbsolutePath() + "/vendordeps/AdvantageKit.json").text) + annotationProcessor "org.littletonrobotics.akit.junction:junction-autolog:$akitJson.version" implementation wpi.java.deps.wpilib() implementation wpi.java.vendor.java() @@ -67,13 +93,7 @@ dependencies { nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) simulationRelease wpi.sim.enableRelease() - testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' - // testRuntimeOnly 'org.junit.platform:junit-platform-launcher' -} - -test { - useJUnitPlatform() - systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true' + annotationProcessor 'com.github.Frc5572:RobotTools:v1.1.1' } // Simulation configuration (e.g. environment variables). @@ -99,3 +119,24 @@ wpi.java.configureTestTasks(test) tasks.withType(JavaCompile) { options.compilerArgs.add '-XDstringConcat=inline' } + +javadoc { + configure(options) { + options.addBooleanOption("-allow-script-in-comments",true) + options.addStringOption("link", "https://github.wpilib.org/allwpilib/docs/release/java/") + options.header = "" + } +} + +// Create version file +project.compileJava.dependsOn(createVersionFile) +gversion { + srcDir = "src/main/java/" + classPackage = "frc.robot" + className = "BuildConstants" + dateFormat = "yyyy-MM-dd HH:mm:ss z" + timeZone = "America/New_York" + indent = " " +} diff --git a/checks.xml b/checks.xml index b00a9c0..67cbaef 100644 --- a/checks.xml +++ b/checks.xml @@ -186,6 +186,11 @@ + + + + --> - + @@ -346,6 +351,11 @@ RECORD_DEF, ANNOTATION_DEF"/> + + + + diff --git a/customAssets/Robot_Pumbaa/config.json b/customAssets/Robot_Pumbaa/config.json new file mode 100644 index 0000000..564f68c --- /dev/null +++ b/customAssets/Robot_Pumbaa/config.json @@ -0,0 +1,70 @@ +{ + "name": "Pumbaa", + "sourceUrl": "", + "rotations": [ + { + "axis": "x", + "degrees": 90.0 + } + ], + "position": [ + 0.0, + 0.0, + 0.0 + ], + "cameras": [], + "components": [ + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90.0 + } + ], + "zeroedPosition": [ + 0.0, + 0.0, + 0.0 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90.0 + } + ], + "zeroedPosition": [ + 0.0, + 0.0, + 0.0 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90.0 + } + ], + "zeroedPosition": [ + 0.0, + 0.0, + 0.0 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90.0 + } + ], + "zeroedPosition": [ + 0.0, + 0.0, + 0.0 + ] + } + ] +} \ No newline at end of file diff --git a/profile.json b/profile.json new file mode 100644 index 0000000..164932f --- /dev/null +++ b/profile.json @@ -0,0 +1,236 @@ +{ + "visitCount": 221, + "totalTime": 6.136004, + "percentOfRoot": 100.0, + "maxTime": 1.02012, + "minTime": 0.01113, + "avgTime": 0.02776472398190045, + "children": { + "robotPeriodic()": { + "visitCount": 221, + "totalTime": 3.466921, + "percentOfParent": 56.50128324557807, + "percentOfRoot": 56.50128324557807, + "maxTime": 0.9518, + "minTime": 0.005917, + "avgTime": 0.015687425339366515, + "children": { + "manual-gc": { + "visitCount": 221, + "totalTime": 0.005231, + "percentOfParent": 0.1508831611680797, + "percentOfRoot": 0.08525092226145875, + "maxTime": 0.001187, + "minTime": 0.0, + "avgTime": 2.3669683257918552E-5, + "children": {} + }, + "draw_state_to_shuffleboard": { + "visitCount": 221, + "totalTime": 0.029186, + "percentOfParent": 0.8418420840855617, + "percentOfRoot": 0.4756515804096607, + "maxTime": 0.001187, + "minTime": 5.5E-5, + "avgTime": 1.320633484162896E-4, + "children": {} + }, + "command_scheduler": { + "visitCount": 221, + "totalTime": 3.377213, + "percentOfParent": 97.41245906670501, + "percentOfRoot": 55.03928941376179, + "maxTime": 0.921008, + "minTime": 0.005769, + "avgTime": 0.015281506787330316, + "children": { + "swerve_periodic": { + "visitCount": 221, + "totalTime": 2.353467, + "percentOfParent": 69.68666175334513, + "percentOfRoot": 38.35504344521288, + "maxTime": 0.826199, + "minTime": 0.002671, + "avgTime": 0.010649171945701358, + "children": { + "update_shuffleboard": { + "visitCount": 221, + "totalTime": 0.728499, + "percentOfParent": 30.954289990044476, + "percentOfRoot": 11.87253137383874, + "maxTime": 0.604044, + "minTime": 2.79E-4, + "avgTime": 0.0032963755656108596, + "children": {} + }, + "update_inputs": { + "visitCount": 221, + "totalTime": 0.008889, + "percentOfParent": 0.37769809391846154, + "percentOfRoot": 0.14486626801416688, + "maxTime": 0.001076, + "minTime": 0.0, + "avgTime": 4.022171945701357E-5, + "children": {} + }, + "do_camera_stuff": { + "visitCount": 221, + "totalTime": 0.664337, + "percentOfParent": 28.228014244516707, + "percentOfRoot": 10.826867127205263, + "maxTime": 0.037712, + "minTime": 7.05E-4, + "avgTime": 0.003006049773755656, + "children": {} + }, + "process_cameras": { + "visitCount": 221, + "totalTime": 0.242648, + "percentOfParent": 10.310235920027772, + "percentOfRoot": 3.9544954664305956, + "maxTime": 0.061085, + "minTime": 3.89E-4, + "avgTime": 0.0010979547511312218, + "children": {} + }, + "update_swerve_odometry": { + "visitCount": 221, + "totalTime": 0.251536, + "percentOfParent": 10.687891523441799, + "percentOfRoot": 4.0993454371933264, + "maxTime": 0.131389, + "minTime": 1.3E-4, + "avgTime": 0.0011381719457013574, + "children": {} + }, + "process_inputs": { + "visitCount": 221, + "totalTime": 0.022414, + "percentOfParent": 0.9523821663953648, + "percentOfRoot": 0.3652865936854018, + "maxTime": 0.001781, + "minTime": 3.7E-5, + "avgTime": 1.01420814479638E-4, + "children": {} + }, + "update_swerve_mods": { + "visitCount": 221, + "totalTime": 0.379823, + "percentOfParent": 16.13887086583326, + "percentOfRoot": 6.19007093215715, + "maxTime": 0.014469, + "minTime": 7.24E-4, + "avgTime": 0.001718656108597285, + "children": { + "updateInputs": { + "visitCount": 884, + "totalTime": 0.200015, + "percentOfParent": 52.66005481500594, + "percentOfRoot": 3.259694745961704, + "maxTime": 0.002319, + "minTime": 7.4E-5, + "avgTime": 2.2626131221719457E-4, + "children": {} + }, + "processInputs": { + "visitCount": 884, + "totalTime": 0.089001, + "percentOfParent": 23.43223027568104, + "percentOfRoot": 1.4504716750510593, + "maxTime": 0.001262, + "minTime": 3.7E-5, + "avgTime": 1.0067986425339367E-4, + "children": {} + } + } + } + } + }, + "teleop_swerve": { + "visitCount": 84, + "totalTime": 0.15469, + "percentOfParent": 4.580404019527344, + "percentOfRoot": 2.521021824627233, + "maxTime": 0.063682, + "minTime": 5.75E-4, + "avgTime": 0.001841547619047619, + "children": { + "swerve.drive()": { + "visitCount": 84, + "totalTime": 0.147641, + "percentOfParent": 95.44314435322258, + "percentOfRoot": 2.406142499255216, + "maxTime": 0.063293, + "minTime": 5.01E-4, + "avgTime": 0.0017576309523809524, + "children": {} + } + } + }, + "ElevatorWrist periodic": { + "visitCount": 221, + "totalTime": 0.27999, + "percentOfParent": 8.290563846580005, + "percentOfRoot": 4.563067429551871, + "maxTime": 0.009127, + "minTime": 5.56E-4, + "avgTime": 0.001266923076923077, + "children": { + "Publish to SmartDashboard": { + "visitCount": 221, + "totalTime": 0.05901, + "percentOfParent": 21.075752705453766, + "percentOfRoot": 0.961700807235458, + "maxTime": 0.00306, + "minTime": 1.11E-4, + "avgTime": 2.670135746606335E-4, + "children": {} + }, + "PID stuff": { + "visitCount": 221, + "totalTime": 0.096493, + "percentOfParent": 34.46301653630487, + "percentOfRoot": 1.5725706828092028, + "maxTime": 0.004638, + "minTime": 1.67E-4, + "avgTime": 4.366199095022624E-4, + "children": {} + }, + "updateInputs": { + "visitCount": 221, + "totalTime": 0.039408, + "percentOfParent": 14.074788385299474, + "percentOfRoot": 0.6422420845879501, + "maxTime": 0.004136, + "minTime": 7.4E-5, + "avgTime": 1.7831674208144795E-4, + "children": {} + }, + "processInputs": { + "visitCount": 221, + "totalTime": 0.051389, + "percentOfParent": 18.353869781063608, + "percentOfRoot": 0.8374994540420769, + "maxTime": 0.002411, + "minTime": 1.11E-4, + "avgTime": 2.3252941176470586E-4, + "children": {} + } + } + } + } + } + } + }, + "teleopInit()": { + "visitCount": 1, + "totalTime": 1.8E-5, + "percentOfParent": 2.933505258471148E-4, + "percentOfRoot": 2.933505258471148E-4, + "maxTime": 1.8E-5, + "minTime": 1.8E-5, + "avgTime": 1.8E-5, + "children": {} + } + } +} \ No newline at end of file diff --git a/shuffleboard config/shuffleboard.json b/shuffleboard config/shuffleboard.json new file mode 100644 index 0000000..631b51d --- /dev/null +++ b/shuffleboard config/shuffleboard.json @@ -0,0 +1,868 @@ +{ + "tabPane": [ + { + "title": "SmartDashboard", + "autoPopulate": true, + "autoPopulatePrefix": "SmartDashboard/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "titleType": 0, + "tiles": { + "0,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/limelight_PipelineName", + "_title": "limelight_PipelineName" + } + }, + "1,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 2 Integrated", + "_title": "Mod 2 Integrated" + } + }, + "2,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "ComboBox Chooser", + "_source0": "network_table:///SmartDashboard/Choose Auto: ", + "_title": "Choose Auto: " + } + }, + "3,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 0 Velocity", + "_title": "Mod 0 Velocity" + } + }, + "4,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 3 Cancoder", + "_title": "Mod 3 Cancoder" + } + }, + "5,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 0 Cancoder", + "_title": "Mod 0 Cancoder" + } + }, + "6,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 0 Integrated", + "_title": "Mod 0 Integrated" + } + }, + "7,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/Magazine Switch", + "_title": "Magazine Switch", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + }, + "8,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 3 Integrated", + "_title": "Mod 3 Integrated" + } + }, + "9,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 1 Velocity", + "_title": "Mod 1 Velocity" + } + }, + "10,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/limelight_Interface", + "_title": "limelight_Interface" + } + }, + "11,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 1 Cancoder", + "_title": "Mod 1 Cancoder" + } + }, + "12,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 1 Integrated", + "_title": "Mod 1 Integrated" + } + }, + "0,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 2 Velocity", + "_title": "Mod 2 Velocity" + } + }, + "1,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 2 Cancoder", + "_title": "Mod 2 Cancoder" + } + }, + "2,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/limelight_Stream", + "_title": "limelight_Stream" + } + }, + "3,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Mod 3 Velocity", + "_title": "Mod 3 Velocity" + } + }, + "4,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/SetPoint (RPM)", + "_title": "SetPoint (RPM)" + } + }, + "5,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/Slider 0", + "_title": "DB/Slider 0" + } + }, + "6,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/Slider 1", + "_title": "DB/Slider 1" + } + }, + "7,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/Slider 2", + "_title": "DB/Slider 2" + } + }, + "8,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/Slider 3", + "_title": "DB/Slider 3" + } + }, + "9,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Auto Selector", + "_title": "Auto Selector" + } + }, + "10,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/DB/Button 0", + "_title": "DB/Button 0", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + }, + "11,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/DB/Button 1", + "_title": "DB/Button 1", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + }, + "12,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/DB/Button 2", + "_title": "DB/Button 2", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + }, + "0,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/DB/Button 3", + "_title": "DB/Button 3", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + }, + "1,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 0", + "_title": "DB/String 0" + } + }, + "2,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 1", + "_title": "DB/String 1" + } + }, + "3,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 2", + "_title": "DB/String 2" + } + }, + "4,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 3", + "_title": "DB/String 3" + } + }, + "5,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 4", + "_title": "DB/String 4" + } + }, + "6,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 5", + "_title": "DB/String 5" + } + }, + "7,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 6", + "_title": "DB/String 6" + } + }, + "8,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 7", + "_title": "DB/String 7" + } + }, + "9,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 8", + "_title": "DB/String 8" + } + }, + "10,2": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/DB/String 9", + "_title": "DB/String 9" + } + } + } + } + }, + { + "title": "LiveWindow", + "autoPopulate": true, + "autoPopulatePrefix": "LiveWindow/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "titleType": 0, + "tiles": { + "0,0": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/Ungrouped", + "_title": "Ungrouped", + "Layout/Label position": "BOTTOM", + "_children": [ + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[16]", + "_title": "PIDController[16]" + }, + { + "_type": "Text View", + "_source0": "network_table:///LiveWindow/Ungrouped/DoubleSolenoid[1,2]/Value", + "_title": "DoubleSolenoid[1,2]/Value" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[25]", + "_title": "PIDController[25]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[13]", + "_title": "PIDController[13]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[17]", + "_title": "PIDController[17]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[14]", + "_title": "PIDController[14]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[22]", + "_title": "PIDController[22]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[26]", + "_title": "PIDController[26]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[6]", + "_title": "PIDController[6]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[23]", + "_title": "PIDController[23]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[7]", + "_title": "PIDController[7]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[18]", + "_title": "PIDController[18]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[8]", + "_title": "PIDController[8]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[11]", + "_title": "PIDController[11]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[19]", + "_title": "PIDController[19]" + }, + { + "_type": "Gyro", + "_source0": "network_table:///LiveWindow/Ungrouped/navX-Sensor[4]", + "_title": "navX-Sensor[4]", + "Visuals/Major tick spacing": 45.0, + "Visuals/Starting angle": 180.0, + "Visuals/Show tick mark ring": true, + "Visuals/Counter clockwise": false + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[10]", + "_title": "PIDController[10]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[20]", + "_title": "PIDController[20]" + }, + { + "_type": "Boolean Box", + "_source0": "network_table:///LiveWindow/Ungrouped/DigitalInput[1]/Value", + "_title": "DigitalInput[1]/Value", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[5]", + "_title": "PIDController[5]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[15]", + "_title": "PIDController[15]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[24]", + "_title": "PIDController[24]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[4]", + "_title": "PIDController[4]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[9]", + "_title": "PIDController[9]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[12]", + "_title": "PIDController[12]" + }, + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[21]", + "_title": "PIDController[21]" + }, + { + "_type": "Text View", + "_source0": "network_table:///LiveWindow/Ungrouped/DoubleSolenoid[1,0]/Value", + "_title": "DoubleSolenoid[1,0]/Value" + }, + { + "_type": "Motor Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [11]", + "_title": "LiveWindow/Ungrouped/Talon FX [11]", + "Visuals/Orientation": "HORIZONTAL" + }, + { + "_type": "Motor Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [12]", + "_title": "LiveWindow/Ungrouped/Talon FX [12]", + "Visuals/Orientation": "HORIZONTAL" + }, + { + "_type": "Motor Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [9]", + "_title": "LiveWindow/Ungrouped/Talon FX [9]", + "Visuals/Orientation": "HORIZONTAL" + }, + { + "_type": "Motor Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/MotorControllerGroup[2]", + "_title": "LiveWindow/Ungrouped/MotorControllerGroup[2]", + "Visuals/Orientation": "HORIZONTAL" + }, + { + "_type": "Motor Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/MotorControllerGroup[1]", + "_title": "LiveWindow/Ungrouped/MotorControllerGroup[1]", + "Visuals/Orientation": "HORIZONTAL" + }, + { + "_type": "Motor Controller", + "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [10]", + "_title": "LiveWindow/Ungrouped/Talon FX [10]", + "Visuals/Orientation": "HORIZONTAL" + } + ] + } + }, + "2,0": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/OutsideClimber", + "_title": "OutsideClimber", + "Layout/Label position": "BOTTOM", + "_children": [] + } + }, + "4,0": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/InsideClimber", + "_title": "InsideClimber", + "Layout/Label position": "BOTTOM", + "_children": [] + } + }, + "6,0": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/OuterMagazine", + "_title": "OuterMagazine", + "Layout/Label position": "BOTTOM", + "_children": [ + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/OuterMagazine/PID Controller", + "_title": "PID Controller" + } + ] + } + }, + "8,0": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/LEDs", + "_title": "LEDs", + "Layout/Label position": "BOTTOM", + "_children": [] + } + }, + "10,0": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/Intake", + "_title": "Intake", + "Layout/Label position": "BOTTOM", + "_children": [] + } + }, + "0,4": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/InnerMagazine", + "_title": "InnerMagazine", + "Layout/Label position": "BOTTOM", + "_children": [ + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/InnerMagazine/PID Controller", + "_title": "PID Controller" + } + ] + } + }, + "2,4": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/Turret", + "_title": "Turret", + "Layout/Label position": "BOTTOM", + "_children": [] + } + }, + "4,4": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/Swerve", + "_title": "Swerve", + "Layout/Label position": "BOTTOM", + "_children": [] + } + }, + "6,4": { + "size": [ + 2, + 4 + ], + "content": { + "_type": "Subsystem Layout", + "_source0": "network_table:///LiveWindow/Shooter", + "_title": "Shooter", + "Layout/Label position": "BOTTOM", + "_children": [ + { + "_type": "PID Controller", + "_source0": "network_table:///LiveWindow/Shooter/PID Controller", + "_title": "PID Controller" + } + ] + } + } + } + } + }, + { + "title": "Tab 3", + "autoPopulate": false, + "autoPopulatePrefix": "", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "titleType": 0, + "tiles": { + "0,1": { + "size": [ + 7, + 4 + ], + "content": { + "_type": "Camera Stream", + "_source0": "camera_server://Magazine Camera", + "_title": "Magazine Camera", + "Crosshair/Show crosshair": true, + "Crosshair/Crosshair color": "#FFFFFFFF", + "Controls/Show controls": false, + "Controls/Rotation": "NONE", + "compression": -1.0, + "fps": -1, + "imageWidth": -1, + "imageHeight": -1 + } + }, + "7,1": { + "size": [ + 6, + 4 + ], + "content": { + "_type": "Camera Stream", + "_source0": "camera_server://limelight", + "_title": "limelight", + "Crosshair/Show crosshair": true, + "Crosshair/Crosshair color": "#FFFFFFFF", + "Controls/Show controls": false, + "Controls/Rotation": "NONE", + "compression": -1.0, + "fps": -1, + "imageWidth": -1, + "imageHeight": -1 + } + }, + "0,0": { + "size": [ + 2, + 1 + ], + "content": { + "_type": "ComboBox Chooser", + "_source0": "network_table:///SmartDashboard/Choose Auto: ", + "_title": "SmartDashboard/Choose Auto: " + } + }, + "2,0": { + "size": [ + 2, + 1 + ], + "content": { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/Magazine Switch", + "_title": "SmartDashboard/Magazine Switch", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + }, + "4,0": { + "size": [ + 2, + 1 + ], + "content": { + "_type": "Text View", + "_source0": "network_table:///LiveWindow/Ungrouped/navX-Sensor[4]/Value", + "_title": "LiveWindow/Ungrouped/navX-Sensor[4]/Value" + } + } + } + } + } + ], + "windowGeometry": { + "x": -8.0, + "y": -8.0, + "width": 1936.0, + "height": 1056.0 + } +} diff --git a/shuffleboard/shuffleboard.json b/shuffleboard/shuffleboard.json deleted file mode 100644 index 1a643b3..0000000 --- a/shuffleboard/shuffleboard.json +++ /dev/null @@ -1,1306 +0,0 @@ -{ - "tabPane": [ - { - "title": "SmartDashboard", - "autoPopulate": true, - "autoPopulatePrefix": "SmartDashboard/", - "widgetPane": { - "gridSize": 128.0, - "showGrid": true, - "hgap": 16.0, - "vgap": 16.0, - "titleType": 0, - "tiles": { - "0,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/limelight_PipelineName", - "_title": "limelight_PipelineName", - "_glyph": 148, - "_showGlyph": false - } - }, - "1,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 2 Integrated", - "_title": "Mod 2 Integrated", - "_glyph": 148, - "_showGlyph": false - } - }, - "2,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "ComboBox Chooser", - "_source0": "network_table:///SmartDashboard/Choose Auto: ", - "_title": "Choose Auto: ", - "_glyph": 148, - "_showGlyph": false - } - }, - "3,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 0 Velocity", - "_title": "Mod 0 Velocity", - "_glyph": 148, - "_showGlyph": false - } - }, - "4,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 3 Cancoder", - "_title": "Mod 3 Cancoder", - "_glyph": 148, - "_showGlyph": false - } - }, - "5,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 0 Cancoder", - "_title": "Mod 0 Cancoder", - "_glyph": 148, - "_showGlyph": false - } - }, - "6,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 0 Integrated", - "_title": "Mod 0 Integrated", - "_glyph": 148, - "_showGlyph": false - } - }, - "7,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/Magazine Switch", - "_title": "Magazine Switch", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "8,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 3 Integrated", - "_title": "Mod 3 Integrated", - "_glyph": 148, - "_showGlyph": false - } - }, - "9,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 1 Velocity", - "_title": "Mod 1 Velocity", - "_glyph": 148, - "_showGlyph": false - } - }, - "10,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/limelight_Interface", - "_title": "limelight_Interface", - "_glyph": 148, - "_showGlyph": false - } - }, - "11,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 1 Cancoder", - "_title": "Mod 1 Cancoder", - "_glyph": 148, - "_showGlyph": false - } - }, - "12,0": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 1 Integrated", - "_title": "Mod 1 Integrated", - "_glyph": 148, - "_showGlyph": false - } - }, - "0,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 2 Velocity", - "_title": "Mod 2 Velocity", - "_glyph": 148, - "_showGlyph": false - } - }, - "1,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 2 Cancoder", - "_title": "Mod 2 Cancoder", - "_glyph": 148, - "_showGlyph": false - } - }, - "2,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/limelight_Stream", - "_title": "limelight_Stream", - "_glyph": 148, - "_showGlyph": false - } - }, - "3,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 3 Velocity", - "_title": "Mod 3 Velocity", - "_glyph": 148, - "_showGlyph": false - } - }, - "4,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/SetPoint (RPM)", - "_title": "SetPoint (RPM)", - "_glyph": 148, - "_showGlyph": false - } - }, - "5,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/Slider 0", - "_title": "DB/Slider 0", - "_glyph": 148, - "_showGlyph": false - } - }, - "6,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/Slider 1", - "_title": "DB/Slider 1", - "_glyph": 148, - "_showGlyph": false - } - }, - "7,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/Slider 2", - "_title": "DB/Slider 2", - "_glyph": 148, - "_showGlyph": false - } - }, - "8,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/Slider 3", - "_title": "DB/Slider 3", - "_glyph": 148, - "_showGlyph": false - } - }, - "9,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Auto Selector", - "_title": "Auto Selector", - "_glyph": 148, - "_showGlyph": false - } - }, - "10,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/DB/Button 0", - "_title": "DB/Button 0", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "11,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/DB/Button 1", - "_title": "DB/Button 1", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "12,1": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/DB/Button 2", - "_title": "DB/Button 2", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "0,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/DB/Button 3", - "_title": "DB/Button 3", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "1,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 0", - "_title": "DB/String 0", - "_glyph": 148, - "_showGlyph": false - } - }, - "2,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 1", - "_title": "DB/String 1", - "_glyph": 148, - "_showGlyph": false - } - }, - "3,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 2", - "_title": "DB/String 2", - "_glyph": 148, - "_showGlyph": false - } - }, - "4,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 3", - "_title": "DB/String 3", - "_glyph": 148, - "_showGlyph": false - } - }, - "5,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 4", - "_title": "DB/String 4", - "_glyph": 148, - "_showGlyph": false - } - }, - "6,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 5", - "_title": "DB/String 5", - "_glyph": 148, - "_showGlyph": false - } - }, - "7,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 6", - "_title": "DB/String 6", - "_glyph": 148, - "_showGlyph": false - } - }, - "8,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 7", - "_title": "DB/String 7", - "_glyph": 148, - "_showGlyph": false - } - }, - "9,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 8", - "_title": "DB/String 8", - "_glyph": 148, - "_showGlyph": false - } - }, - "10,2": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/DB/String 9", - "_title": "DB/String 9", - "_glyph": 148, - "_showGlyph": false - } - }, - "11,2": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Field", - "_source0": "network_table:///SmartDashboard/Field Pos", - "_title": "Field Pos", - "_glyph": 148, - "_showGlyph": false, - "Game/Game": "A2023_Charged_Up", - "Visuals/Robot Icon Size": 50.0, - "Visuals/Show Outside Circles": false - } - }, - "0,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/Has Initialized", - "_title": "Has Initialized", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "1,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Robot X", - "_title": "Robot X", - "_glyph": 148, - "_showGlyph": false - } - }, - "2,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Robot Y", - "_title": "Robot Y", - "_glyph": 148, - "_showGlyph": false - } - }, - "3,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Robot Rotation", - "_title": "Robot Rotation", - "_glyph": 148, - "_showGlyph": false - } - }, - "4,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Gyro Yaw", - "_title": "Gyro Yaw", - "_glyph": 148, - "_showGlyph": false - } - }, - "5,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Field Offset", - "_title": "Field Offset", - "_glyph": 148, - "_showGlyph": false - } - }, - "6,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Gyro Yaw - Offset", - "_title": "Gyro Yaw - Offset", - "_glyph": 148, - "_showGlyph": false - } - }, - "7,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 0 Position", - "_title": "Mod 0 Position", - "_glyph": 148, - "_showGlyph": false - } - }, - "8,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 1 Position", - "_title": "Mod 1 Position", - "_glyph": 148, - "_showGlyph": false - } - }, - "9,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 2 Position", - "_title": "Mod 2 Position", - "_glyph": 148, - "_showGlyph": false - } - }, - "10,3": { - "size": [ - 1, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Mod 3 Position", - "_title": "Mod 3 Position", - "_glyph": 148, - "_showGlyph": false - } - }, - "11,3": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Field", - "_source0": "network_table:///SmartDashboard/Field Pos2", - "_title": "Field Pos2", - "_glyph": 148, - "_showGlyph": false, - "Game/Game": "A2023_Charged_Up", - "Visuals/Robot Icon Size": 50.0, - "Visuals/Show Outside Circles": false - } - }, - "0,4": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Field", - "_source0": "network_table:///SmartDashboard/Field Pos Something", - "_title": "Field Pos Something", - "_glyph": 148, - "_showGlyph": false, - "Game/Game": "A2023_Charged_Up", - "Visuals/Robot Icon Size": 50.0, - "Visuals/Show Outside Circles": false - } - } - } - } - }, - { - "title": "LiveWindow", - "autoPopulate": true, - "autoPopulatePrefix": "LiveWindow/", - "widgetPane": { - "gridSize": 128.0, - "showGrid": true, - "hgap": 16.0, - "vgap": 16.0, - "titleType": 0, - "tiles": { - "0,0": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/Ungrouped", - "_title": "Ungrouped", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [ - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[16]", - "_title": "PIDController[16]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "Text View", - "_source0": "network_table:///LiveWindow/Ungrouped/DoubleSolenoid[1,2]/Value", - "_title": "DoubleSolenoid[1,2]/Value", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[25]", - "_title": "PIDController[25]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[13]", - "_title": "PIDController[13]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[17]", - "_title": "PIDController[17]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[14]", - "_title": "PIDController[14]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[22]", - "_title": "PIDController[22]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[26]", - "_title": "PIDController[26]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[6]", - "_title": "PIDController[6]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[23]", - "_title": "PIDController[23]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[7]", - "_title": "PIDController[7]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[18]", - "_title": "PIDController[18]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[8]", - "_title": "PIDController[8]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[11]", - "_title": "PIDController[11]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[19]", - "_title": "PIDController[19]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "Gyro", - "_source0": "network_table:///LiveWindow/Ungrouped/navX-Sensor[4]", - "_title": "navX-Sensor[4]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Major tick spacing": 45.0, - "Visuals/Starting angle": 180.0, - "Visuals/Show tick mark ring": true, - "Visuals/Counter clockwise": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[10]", - "_title": "PIDController[10]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[20]", - "_title": "PIDController[20]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "Boolean Box", - "_source0": "network_table:///LiveWindow/Ungrouped/DigitalInput[1]/Value", - "_title": "DigitalInput[1]/Value", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[5]", - "_title": "PIDController[5]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[15]", - "_title": "PIDController[15]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[24]", - "_title": "PIDController[24]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[4]", - "_title": "PIDController[4]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[9]", - "_title": "PIDController[9]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[12]", - "_title": "PIDController[12]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/PIDController[21]", - "_title": "PIDController[21]", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "Text View", - "_source0": "network_table:///LiveWindow/Ungrouped/DoubleSolenoid[1,0]/Value", - "_title": "DoubleSolenoid[1,0]/Value", - "_glyph": 148, - "_showGlyph": false - }, - { - "_type": "Motor Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [11]", - "_title": "LiveWindow/Ungrouped/Talon FX [11]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Orientation": "HORIZONTAL" - }, - { - "_type": "Motor Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [12]", - "_title": "LiveWindow/Ungrouped/Talon FX [12]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Orientation": "HORIZONTAL" - }, - { - "_type": "Motor Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [9]", - "_title": "LiveWindow/Ungrouped/Talon FX [9]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Orientation": "HORIZONTAL" - }, - { - "_type": "Motor Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/MotorControllerGroup[2]", - "_title": "LiveWindow/Ungrouped/MotorControllerGroup[2]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Orientation": "HORIZONTAL" - }, - { - "_type": "Motor Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/MotorControllerGroup[1]", - "_title": "LiveWindow/Ungrouped/MotorControllerGroup[1]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Orientation": "HORIZONTAL" - }, - { - "_type": "Motor Controller", - "_source0": "network_table:///LiveWindow/Ungrouped/Talon FX [10]", - "_title": "LiveWindow/Ungrouped/Talon FX [10]", - "_glyph": 148, - "_showGlyph": false, - "Visuals/Orientation": "HORIZONTAL" - } - ] - } - }, - "2,0": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/OutsideClimber", - "_title": "OutsideClimber", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [] - } - }, - "4,0": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/InsideClimber", - "_title": "InsideClimber", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [] - } - }, - "6,0": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/OuterMagazine", - "_title": "OuterMagazine", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [ - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/OuterMagazine/PID Controller", - "_title": "PID Controller", - "_glyph": 148, - "_showGlyph": false - } - ] - } - }, - "8,0": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/LEDs", - "_title": "LEDs", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [] - } - }, - "10,0": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/Intake", - "_title": "Intake", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [] - } - }, - "0,4": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/InnerMagazine", - "_title": "InnerMagazine", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [ - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/InnerMagazine/PID Controller", - "_title": "PID Controller", - "_glyph": 148, - "_showGlyph": false - } - ] - } - }, - "2,4": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/Turret", - "_title": "Turret", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [] - } - }, - "4,4": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/Swerve", - "_title": "Swerve", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [] - } - }, - "6,4": { - "size": [ - 2, - 4 - ], - "content": { - "_type": "Subsystem Layout", - "_source0": "network_table:///LiveWindow/Shooter", - "_title": "Shooter", - "_glyph": 149, - "_showGlyph": false, - "Layout/Label position": "BOTTOM", - "_children": [ - { - "_type": "PID Controller", - "_source0": "network_table:///LiveWindow/Shooter/PID Controller", - "_title": "PID Controller", - "_glyph": 148, - "_showGlyph": false - } - ] - } - } - } - } - }, - { - "title": "Tab 3", - "autoPopulate": false, - "autoPopulatePrefix": "", - "widgetPane": { - "gridSize": 128.0, - "showGrid": true, - "hgap": 16.0, - "vgap": 16.0, - "titleType": 0, - "tiles": { - "0,1": { - "size": [ - 7, - 4 - ], - "content": { - "_type": "Camera Stream", - "_source0": "camera_server://Magazine Camera", - "_title": "Magazine Camera", - "_glyph": 148, - "_showGlyph": false, - "Crosshair/Show crosshair": true, - "Crosshair/Crosshair color": "#FFFFFFFF", - "Controls/Show controls": false, - "Controls/Rotation": "NONE", - "compression": -1.0, - "fps": -1, - "imageWidth": -1, - "imageHeight": -1 - } - }, - "7,1": { - "size": [ - 6, - 4 - ], - "content": { - "_type": "Camera Stream", - "_source0": "camera_server://limelight", - "_title": "limelight", - "_glyph": 148, - "_showGlyph": false, - "Crosshair/Show crosshair": true, - "Crosshair/Crosshair color": "#FFFFFFFF", - "Controls/Show controls": false, - "Controls/Rotation": "NONE", - "compression": -1.0, - "fps": -1, - "imageWidth": -1, - "imageHeight": -1 - } - }, - "0,0": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "ComboBox Chooser", - "_source0": "network_table:///SmartDashboard/Choose Auto: ", - "_title": "SmartDashboard/Choose Auto: ", - "_glyph": 148, - "_showGlyph": false - } - }, - "2,0": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Boolean Box", - "_source0": "network_table:///SmartDashboard/Magazine Switch", - "_title": "SmartDashboard/Magazine Switch", - "_glyph": 148, - "_showGlyph": false, - "Colors/Color when true": "#7CFC00FF", - "Colors/Color when false": "#8B0000FF" - } - }, - "4,0": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///LiveWindow/Ungrouped/navX-Sensor[4]/Value", - "_title": "LiveWindow/Ungrouped/navX-Sensor[4]/Value", - "_glyph": 148, - "_showGlyph": false - } - } - } - } - }, - { - "title": "Tab 4", - "autoPopulate": false, - "autoPopulatePrefix": "", - "widgetPane": { - "gridSize": 128.0, - "showGrid": true, - "hgap": 16.0, - "vgap": 16.0, - "titleType": 0, - "tiles": { - "0,0": { - "size": [ - 13, - 4 - ], - "content": { - "_type": "Field", - "_source0": "network_table:///SmartDashboard/Field Pos", - "_title": "/SmartDashboard/Field Pos", - "_glyph": 148, - "_showGlyph": false, - "Game/Game": "A2023_Charged_Up", - "Visuals/Robot Icon Size": 50.0, - "Visuals/Show Outside Circles": false - } - }, - "3,4": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Robot X", - "_title": "SmartDashboard/Robot X", - "_glyph": 148, - "_showGlyph": false - } - }, - "5,4": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Robot Y", - "_title": "SmartDashboard/Robot Y", - "_glyph": 148, - "_showGlyph": false - } - }, - "7,4": { - "size": [ - 2, - 1 - ], - "content": { - "_type": "Text View", - "_source0": "network_table:///SmartDashboard/Robot Rotation", - "_title": "SmartDashboard/Robot Rotation", - "_glyph": 148, - "_showGlyph": false - } - } - } - } - } - ], - "windowGeometry": { - "x": -7.333333492279053, - "y": -7.333333492279053, - "width": 2014.6666259765625, - "height": 1300.0 - } -} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/autos/New Auto.auto b/src/main/deploy/pathplanner/autos/New Auto.auto deleted file mode 100644 index 2daa85c..0000000 --- a/src/main/deploy/pathplanner/autos/New Auto.auto +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": 1.0, - "startingPose": null, - "command": { - "type": "sequential", - "data": { - "commands": [ - { - "type": "path", - "data": { - "pathName": "New Path" - } - }, - { - "type": "wait", - "data": { - "waitTime": 5.0 - } - }, - { - "type": "path", - "data": { - "pathName": "New New Path" - } - } - ] - } - }, - "folder": null, - "choreoAuto": false -} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/navgrid.json b/src/main/deploy/pathplanner/navgrid.json deleted file mode 100644 index bab0da9..0000000 --- a/src/main/deploy/pathplanner/navgrid.json +++ /dev/null @@ -1 +0,0 @@ -{"field_size":{"x":16.54,"y":8.21},"nodeSizeMeters":0.3,"grid":[[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true],[true,true,true,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true],[true,true,true,true,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true]]} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/paths/New New Path.path b/src/main/deploy/pathplanner/paths/New New Path.path deleted file mode 100644 index 3395ddf..0000000 --- a/src/main/deploy/pathplanner/paths/New New Path.path +++ /dev/null @@ -1,49 +0,0 @@ -{ - "version": 1.0, - "waypoints": [ - { - "anchor": { - "x": 7.0, - "y": 7.057898448431419 - }, - "prevControl": null, - "nextControl": { - "x": 7.0, - "y": 4.277329229738948 - }, - "isLocked": false, - "linkedName": null - }, - { - "anchor": { - "x": 7.0, - "y": 1.0 - }, - "prevControl": { - "x": 7.0, - "y": 2.5206906325745546 - }, - "nextControl": null, - "isLocked": false, - "linkedName": null - } - ], - "rotationTargets": [], - "constraintZones": [], - "eventMarkers": [], - "globalConstraints": { - "maxVelocity": 3.0, - "maxAcceleration": 3.0, - "maxAngularVelocity": 540.0, - "maxAngularAcceleration": 720.0 - }, - "goalEndState": { - "velocity": 0, - "rotation": 0, - "rotateFast": false - }, - "reversed": false, - "folder": null, - "previewStartingState": null, - "useDefaultConstraints": false -} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/paths/New Path.path b/src/main/deploy/pathplanner/paths/New Path.path deleted file mode 100644 index 177a14b..0000000 --- a/src/main/deploy/pathplanner/paths/New Path.path +++ /dev/null @@ -1,66 +0,0 @@ -{ - "version": 1.0, - "waypoints": [ - { - "anchor": { - "x": 2.0, - "y": 7.0 - }, - "prevControl": null, - "nextControl": { - "x": 3.118033988749895, - "y": 7.0 - }, - "isLocked": false, - "linkedName": null - }, - { - "anchor": { - "x": 6.975681093221026, - "y": 7.0 - }, - "prevControl": { - "x": 5.772436660297699, - "y": 7.0 - }, - "nextControl": null, - "isLocked": false, - "linkedName": null - } - ], - "rotationTargets": [ - { - "waypointRelativePos": 0.5, - "rotationDegrees": 90.0, - "rotateFast": true - } - ], - "constraintZones": [], - "eventMarkers": [ - { - "name": "BOB", - "waypointRelativePos": 0.5, - "command": { - "type": "sequential", - "data": { - "commands": [] - } - } - } - ], - "globalConstraints": { - "maxVelocity": 3.0, - "maxAcceleration": 3.0, - "maxAngularVelocity": 540.0, - "maxAngularAcceleration": 720.0 - }, - "goalEndState": { - "velocity": 0, - "rotation": 0.0, - "rotateFast": false - }, - "reversed": false, - "folder": null, - "previewStartingState": null, - "useDefaultConstraints": true -} \ No newline at end of file diff --git a/src/main/java/frc/lib/math/Conversions.java b/src/main/java/frc/lib/math/Conversions.java index 5b1c66e..534a246 100644 --- a/src/main/java/frc/lib/math/Conversions.java +++ b/src/main/java/frc/lib/math/Conversions.java @@ -1,7 +1,9 @@ package frc.lib.math; +import frc.robot.Constants; + /** - * Static Conversion functions + * Mathematical conversions for swerve calculations */ public class Conversions { @@ -10,7 +12,7 @@ public class Conversions { * @param circumference Wheel Circumference: (in Meters) * @return Wheel Velocity: (in Meters per Second) */ - public static double RPSToMPS(double wheelRPS, double circumference) { + public static double rotationPerSecondToMetersPerSecond(double wheelRPS, double circumference) { double wheelMPS = wheelRPS * circumference; return wheelMPS; } @@ -20,7 +22,7 @@ public static double RPSToMPS(double wheelRPS, double circumference) { * @param circumference Wheel Circumference: (in Meters) * @return Wheel Velocity: (in Rotations per Second) */ - public static double MPSToRPS(double wheelMPS, double circumference) { + public static double metersPerSecondToRotationPerSecond(double wheelMPS, double circumference) { double wheelRPS = wheelMPS / circumference; return wheelRPS; } @@ -44,4 +46,96 @@ public static double metersToRotations(double wheelMeters, double circumference) double wheelRotations = wheelMeters / circumference; return wheelRotations; } + + /** + * @param counts Falcon Counts + * @param gearRatio Gear Ratio between Falcon and Mechanism + * @return Degrees of Rotation of Mechanism falconToDegrees + */ + public static double falconToDegrees(double counts, double gearRatio) { + return counts * (360.0 / (gearRatio * 2048.0)); + } + + /** + * @param degrees Degrees of rotation of Mechanism + * @param gearRatio Gear Ratio between Falcon and Mechanism + * @return Falcon Counts degreesToFalcon + */ + public static double degreesToFalcon(double degrees, double gearRatio) { + double ticks = degrees / (360.0 / (gearRatio * 2048.0)); + return ticks; + } + + /** + * @param velocityCounts Falcon Velocity Counts + * @param gearRatio Gear Ratio between Falcon and Mechanism (set to 1 for Falcon RPM) + * @return RPM of Mechanism + */ + public static double falconToRPM(double velocityCounts, double gearRatio) { + double motorRPM = velocityCounts * (600.0 / 2048.0); + double mechRPM = motorRPM / gearRatio; + return mechRPM; + } + + /** + * @param rpm RPM of mechanism + * @param gearRatio Gear Ratio between Falcon and Mechanism (set to 1 for Falcon RPM) + * @return RPM of Mechanism + */ + public static double rpmToFalcon(double rpm, double gearRatio) { + double motorRPM = rpm * gearRatio; + double sensorCounts = motorRPM * (2048.0 / 600.0); + return sensorCounts; + } + + /** + * @param counts Falcon Counts + * @param gearRatio Gear Ratio between Falcon and Mechanism + * @param circumference Circumference of Wheel + * @return Degrees of Rotation of Mechanism falconToDegrees + */ + public static double falconToMeters(double counts, double gearRatio, double circumference) { + return counts * circumference / (gearRatio * 2048.0); + } + + /** + * @param velocitycounts Falcon Velocity Counts + * @param circumference Circumference of Wheel + * @param gearRatio Gear Ratio between Falcon and Mechanism (set to 1 for Falcon RPM) + * @return Falcon Velocity Counts + */ + public static double falconToMPS(double velocitycounts, double circumference, + double gearRatio) { + double wheelRPM = falconToRPM(velocitycounts, gearRatio); + double wheelMPS = (wheelRPM * circumference) / 60; + return wheelMPS; + } + + /** + * @param velocity Velocity MPS + * @param circumference Circumference of Wheel + * @param gearRatio Gear Ratio between Falcon and Mechanism (set to 1 for Falcon RPM) + * @return Falcon Velocity Counts + */ + public static double mpsToFalcon(double velocity, double circumference, double gearRatio) { + double wheelRPM = ((velocity * 60) / circumference); + double wheelVelocity = rpmToFalcon(wheelRPM, gearRatio); + return wheelVelocity; + } + + /** + * Normalize angle to between 0 to 360 + * + * @param goal initial angle + * @return normalized angle + */ + public static double reduceTo0_360(double goal) { + return goal % 360; + } + + + public static double wristOffsetToVisualAngle(double desiredAngle) { + return desiredAngle + Constants.ElevatorWristConstants.SetPoints.HOME_ANGLE.getDegrees(); + } + } diff --git a/src/main/java/frc/lib/profiling/EmptyProfiler.java b/src/main/java/frc/lib/profiling/EmptyProfiler.java new file mode 100644 index 0000000..d969a4e --- /dev/null +++ b/src/main/java/frc/lib/profiling/EmptyProfiler.java @@ -0,0 +1,45 @@ +package frc.lib.profiling; + +/** + * This logger explicitly does nothing. Because there is no variance in doing nothing, a single + * instance is provided as {@link #INSTANCE}. + */ +public final class EmptyProfiler implements Profiler { + + /** + * The sole instance of {@link EmptyProfiler}. + */ + public static final EmptyProfiler INSTANCE = new EmptyProfiler(); + + private EmptyProfiler() {} + + @Override + public void save() { + + } + + @Override + public void startTick() { + + } + + @Override + public void endTick() { + + } + + @Override + public void push(String location) { + + } + + @Override + public void pop() { + + } + + @Override + public void reset() { + + } +} diff --git a/src/main/java/frc/lib/profiling/LoggingProfiler.java b/src/main/java/frc/lib/profiling/LoggingProfiler.java new file mode 100644 index 0000000..3ae4559 --- /dev/null +++ b/src/main/java/frc/lib/profiling/LoggingProfiler.java @@ -0,0 +1,226 @@ +package frc.lib.profiling; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.LongSupplier; +import org.littletonrobotics.junction.Logger; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import frc.lib.util.LongArrayList; + +/** + * This logger saves runtime performance metrics to memory. These metrics are subsequently written + * to a file when {@link #save() save} is called. + */ +public final class LoggingProfiler implements Profiler { + private static final char SPLIT_CHAR = '\u001e'; + + private final List path = new ArrayList<>(); + private final LongArrayList timeList = new LongArrayList(); + private final Map locationInfos = new HashMap<>(); + private final LongSupplier timeGetter; + private final double timeDivisor; + private boolean tickStarted; + private String fullPath = ""; + private LoggingProfiler.LocatedInfo currentInfo; + + /** + * @param timeGetter a supplier for the current time. + * @param timeDivisor a conversion factor turning the units of {@code timeGetter} to the units + * output to a file. + */ + public LoggingProfiler(LongSupplier timeGetter, double timeDivisor) { + this.timeGetter = timeGetter; + this.timeDivisor = timeDivisor; + } + + @Override + public void save() { + long start = timeGetter.getAsLong(); + HashMap jsons = new HashMap<>(); + for (var entry : locationInfos.entrySet()) { + var value = new LocatedInfoJSON(); + value.visits = entry.getValue().visits; + value.maxTime = entry.getValue().maxTime; + value.minTime = entry.getValue().minTime; + value.totalTime = entry.getValue().totalTime; + value.children = new HashMap<>(); + jsons.put(entry.getKey(), value); + } + + System.out.println("----"); + for (var entry : jsons.entrySet()) { + System.out.println(entry.getKey()); + } + System.out.println("----"); + + for (var entry : jsons.entrySet()) { + String key = entry.getKey(); + String[] parts = key.split("\\" + SPLIT_CHAR); + if (parts.length < 2) { + continue; + } + StringBuilder parent = new StringBuilder(); + for (int i = 0; i < parts.length - 2; i++) { + parent.append(parts[i]); + parent.append(SPLIT_CHAR); + } + parent.append(parts[parts.length - 2]); + System.out.println(parent.toString()); + jsons.get(parent.toString()).children.put(parts[parts.length - 1], entry.getValue()); + } + + // Write to file. + try { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + LocatedInfoJSON root = jsons.get("root"); + JsonFactory factory = new JsonFactory(); + JsonGenerator generator = factory.createGenerator(outStream); + root.writeJSON(generator, timeDivisor, 0, root.totalTime); + generator.flush(); + generator.close(); + Logger.recordOutput("profile/json", new String(outStream.toByteArray())); + } catch (IOException e) { + e.printStackTrace(); + } + long end = timeGetter.getAsLong(); + double timeDiff = (end - start) / timeDivisor; + Logger.recordOutput("profile/timeToWrite", timeDiff); + } + + @Override + public void startTick() { + if (this.tickStarted) { + throw new RuntimeException("Profiler tick already started. Missing endTick()?"); + } else { + this.tickStarted = true; + this.fullPath = ""; + this.path.clear(); + this.push("root"); + } + } + + @Override + public void endTick() { + if (!this.tickStarted) { + throw new RuntimeException("Profiler tick already ended. Missing startTick()?"); + } else { + this.pop(); + this.tickStarted = false; + if (!this.fullPath.isEmpty()) { + throw new RuntimeException( + "Profiler tick ended before path was fully popped. Mismatched push/pop?"); + } + } + } + + @Override + public void push(String location) { + if (!this.tickStarted) { + throw new RuntimeException("Cannot push '" + location + + "' to the profiler if profiler tick hasn't started. Missing startTick()?"); + } else { + if (!this.fullPath.isEmpty()) { + this.fullPath = this.fullPath + SPLIT_CHAR; + } + this.fullPath += location; + this.path.add(this.fullPath); + this.timeList.add(timeGetter.getAsLong()); + this.currentInfo = null; + } + } + + @Override + public void pop() { + if (!this.tickStarted) { + throw new RuntimeException( + "Cannot pop from profiler if profiler tick hasn't started. Missing startTick()?"); + } else if (this.timeList.isEmpty()) { + throw new RuntimeException( + "Tried to pop one too many times! Mismatched push() and pop()?"); + } else { + this.path.remove(this.path.size() - 1); + + long currentTime = timeGetter.getAsLong(); + long startTime = this.timeList.remove(this.timeList.size() - 1); + long timeSpan = currentTime - startTime; + + LocatedInfo locatedInfo = this.getCurrentInfo(); + + locatedInfo.totalTime += timeSpan; + locatedInfo.visits++; + locatedInfo.maxTime = Math.max(locatedInfo.maxTime, timeSpan); + locatedInfo.minTime = Math.min(locatedInfo.minTime, timeSpan); + this.fullPath = this.path.isEmpty() ? "" : this.path.get(this.path.size() - 1); + this.currentInfo = null; + } + } + + private LocatedInfo getCurrentInfo() { + if (this.currentInfo == null) { + this.currentInfo = + this.locationInfos.computeIfAbsent(this.fullPath, _k -> new LocatedInfo()); + } + return this.currentInfo; + } + + /** + * Performance metrics for a given state of the profile stack. + */ + public static class LocatedInfo { + long maxTime = Long.MIN_VALUE; + long minTime = Long.MAX_VALUE; + long totalTime; + long visits; + } + + /** + * JSON Tree version of {@link LocatedInfo} + */ + private static class LocatedInfoJSON { + long maxTime = Long.MIN_VALUE; + long minTime = Long.MAX_VALUE; + long totalTime; + long visits; + HashMap children; + + void writeJSON(JsonGenerator generator, double timeDivisor, long parent_total, + long root_total) throws IOException { + generator.writeStartObject(); + generator.writeNumberField("visitCount", visits); + generator.writeNumberField("totalTime", totalTime / timeDivisor); + if (parent_total > 0) { + double percent = (double) totalTime / (double) parent_total * 100.0; + generator.writeNumberField("percentOfParent", percent); + } + if (root_total > 0) { + double percent = (double) totalTime / (double) root_total * 100.0; + generator.writeNumberField("percentOfRoot", percent); + } + generator.writeNumberField("maxTime", maxTime / timeDivisor); + generator.writeNumberField("minTime", minTime / timeDivisor); + generator.writeNumberField("avgTime", totalTime / timeDivisor / visits); + generator.writeFieldName("children"); + generator.writeStartObject(); + for (var entry : children.entrySet()) { + generator.writeFieldName(entry.getKey()); + entry.getValue().writeJSON(generator, timeDivisor, totalTime, root_total); + } + generator.writeEndObject(); + generator.writeEndObject(); + } + } + + @Override + public void reset() { + this.locationInfos.clear(); + this.fullPath = ""; + this.path.clear(); + this.timeList.clear(); + } + +} diff --git a/src/main/java/frc/lib/profiling/ProfileLoggingFormat.java b/src/main/java/frc/lib/profiling/ProfileLoggingFormat.java new file mode 100644 index 0000000..8bd5cf6 --- /dev/null +++ b/src/main/java/frc/lib/profiling/ProfileLoggingFormat.java @@ -0,0 +1,31 @@ +package frc.lib.profiling; + +import java.io.OutputStream; +import frc.lib.profiling.LoggingProfiler.LocatedInfo; + +/** + * Describes how a profile is written to a file. + */ +public interface ProfileLoggingFormat { + + /** + * Comparison function used for sorting profile information. + */ + int compare(String leftName, LocatedInfo leftInfo, String rightName, LocatedInfo rightInfo); + + /** + * Write a profile to the file specified in {@code outStream}. + */ + void write(String name, LocatedInfo info, double timeDivisor, OutputStream outStream); + + /** + * Write information before seeing any profiles. + */ + void begin(OutputStream outputStream); + + /** + * Write information after seeing all profiles. + */ + void end(OutputStream outputStream); + +} diff --git a/src/main/java/frc/lib/profiling/Profiler.java b/src/main/java/frc/lib/profiling/Profiler.java new file mode 100644 index 0000000..8455df9 --- /dev/null +++ b/src/main/java/frc/lib/profiling/Profiler.java @@ -0,0 +1,95 @@ +package frc.lib.profiling; + +/** + * Interface supporting profiling calls for measuring and saving runtime performance. + * + *

+ * Operates on the idea of a "profile stack", which is just a stack of names with associated + * profiling metrics. For instance, the following code + * + *

+ * {@code
+ * Profiler profiler = someProfiler();
+ * profiler.startTick();
+ * profiler.push("Hello")
+ * someExpensiveFunction();
+ * profiler.swap("World")
+ * someLessExpensiveFunction();
+ * profiler.pop();
+ * profiler.endTick();
+ * }
+ * 
+ * + *

+ * will produce a profile with three sections: + *

    + *
  • {@code root}, which describes the time between {@link #startTick() profiler.startTick()} and + * {@link #endTick() profiler.endTick()},
  • + *
  • {@code root.Hello}, which describes the time between {@link #push(String) + * profiler.push("Hello")} and {@link #swap(String) profiler.swap("World")}, and
  • + *
  • {@code root.World}, which describes the time between {@link #swap(String) + * profiler.swap("World")} and {@link #pop() profiler.pop()}. + *
+ * + *

+ * A call to {@link #save() profiler.save()} would output the performance metrics of these sections + * in the case of a {@link LoggingProfiler}. + */ +public sealed interface Profiler permits EmptyProfiler, LoggingProfiler, ValidatingProfiler { + /** + * Called at the top of the main loop. Indicates the profiler is at "root" and records the start + * time. Within the main loop, no meaningful work should occur before this call. + * + * @throws RuntimeException if called twice without a call to {@link #endTick() endTick} in + * between. + */ + void startTick(); + + /** + * Called at the bottom of the main loop. Indicates the profiler's "root" is finished, and its + * time usage metrics are updated. Within the main loop, no meaningful work should occur after + * this call. + * + * @throws RuntimeException if the profile stack is not empty or {@link #startTick() startTick} + * has not been called. + */ + void endTick(); + + /** + * The profile stack is pushed with the {@code location} as its top. Must be accompanied by a + * call to {@link #pop() pop}. + * + * @throws RuntimeException if {@link #startTick() startTick} hasn't been called yet. + */ + void push(String location); + + /** + * The profile stack is popped. Must be preceded (at some point) by a call to + * {@link #push(String) push}. + * + * @throws RuntimeException if {@link #startTick() startTick} hasn't been called yet or the + * profile stack is empty. + */ + void pop(); + + /** + * The top of the profile stack is replaced with {@code location}. This is equivalent to a call + * to {@link #pop() pop} followed immediately by a call to {@link #push(String) push}. + * + * @throws RuntimeException if the profile stack is empty. + */ + default void swap(String location) { + pop(); + push(location); + } + + /** + * Write profile data to a file. + */ + void save(); + + /** + * Reset logger. + */ + void reset(); +} diff --git a/src/main/java/frc/lib/profiling/ValidatingProfiler.java b/src/main/java/frc/lib/profiling/ValidatingProfiler.java new file mode 100644 index 0000000..8c4ae7c --- /dev/null +++ b/src/main/java/frc/lib/profiling/ValidatingProfiler.java @@ -0,0 +1,141 @@ +package frc.lib.profiling; + +import java.util.HashMap; +import java.util.Map; + +/** + * This logger only performs validation (i.e. throws if {@link #pop() pop} is called with an empty + * stack). Otherwise, does nothing. Because there is no variance in doing nothing, a single instance + * is provided as {@link #INSTANCE}. + */ +public final class ValidatingProfiler implements Profiler { + + /** + * The sole instance of {@link ValidatingProfiler}. + */ + public static final ValidatingProfiler INSTANCE = new ValidatingProfiler(); + + private ValidatingProfiler() {} + + /** + * Keeps track of methods and how many times they call push/pop. + */ + private Map methodPushHelper = new HashMap<>(); + + private boolean tickStarted = false; + private int pathLen = 0; + private boolean fullPathEmpty = true; + + @Override + public void save() { + + } + + @Override + public void startTick() { + if (this.tickStarted) { + throw new RuntimeException("Profiler tick already started. Missing endTick()?"); + } else { + tickStarted = true; + pathLen = 0; + fullPathEmpty = true; + this.push("root"); + } + } + + @Override + public void endTick() { + if (!this.tickStarted) { + StringBuilder builder = new StringBuilder(); + for (var entry : methodPushHelper.entrySet()) { + if (entry.getValue() != 0) { + builder.append(entry.getKey()); + builder.append(" has "); + builder.append(entry.getValue()); + builder.append(" more pushes that pops.\n"); + } + } + throw new RuntimeException( + "Profiler tick already ended. Missing startTick()?\n" + builder.toString()); + } else { + this.pop(); + this.tickStarted = false; + if (!this.fullPathEmpty) { + StringBuilder builder = new StringBuilder(); + for (var entry : methodPushHelper.entrySet()) { + if (entry.getValue() != 0) { + builder.append(entry.getKey()); + builder.append(" has "); + builder.append(entry.getValue()); + builder.append(" more pushes that pops.\n"); + } + } + throw new RuntimeException( + "Profiler tick ended before path was fully popped. Mismatched push/pop?\n" + + builder.toString()); + } + } + } + + @Override + public void push(String location) { + if (!this.tickStarted) { + throw new RuntimeException("Cannot push '" + location + + "' to the profiler if profiler tick hasn't started. Missing startTick()?"); + } else { + for (var stack : Thread.currentThread().getStackTrace()) { + if (stack.getClassName().equals(ValidatingProfiler.class.getCanonicalName()) + || stack.getClassName().equals(Profiler.class.getCanonicalName()) + || stack.getClassName().equals(Thread.class.getCanonicalName())) { + continue; + } + String key = stack.getClassName() + "." + stack.getMethodName(); + methodPushHelper.put(key, methodPushHelper.computeIfAbsent(key, (_k) -> 0) + 1); + break; + } + fullPathEmpty = false; + pathLen += 1; + } + } + + @Override + public void pop() { + if (!this.tickStarted) { + throw new RuntimeException( + "Cannot pop from profiler if profiler tick hasn't started. Missing startTick()?"); + } else if (pathLen == 0) { + StringBuilder builder = new StringBuilder(); + for (var entry : methodPushHelper.entrySet()) { + if (entry.getValue() != 0) { + builder.append(entry.getKey()); + builder.append(" has "); + builder.append(-entry.getValue()); + builder.append(" more pops than pushes.\n"); + } + } + throw new RuntimeException( + "Tried to pop one too many times! Mismatched push() and pop()?\n" + + builder.toString()); + } else { + for (var stack : Thread.currentThread().getStackTrace()) { + if (stack.getClassName().equals(ValidatingProfiler.class.getCanonicalName()) + || stack.getClassName().equals(Profiler.class.getCanonicalName()) + || stack.getClassName().equals(Thread.class.getCanonicalName())) { + continue; + } + String key = stack.getClassName() + "." + stack.getMethodName(); + methodPushHelper.put(key, methodPushHelper.computeIfAbsent(key, (_k) -> 0) - 1); + break; + } + pathLen -= 1; + if (pathLen == 0) { + fullPathEmpty = true; + } + } + } + + @Override + public void reset() { + + } +} diff --git a/src/main/java/frc/lib/util/FieldConstants.java b/src/main/java/frc/lib/util/FieldConstants.java index 9e32e77..70a3570 100644 --- a/src/main/java/frc/lib/util/FieldConstants.java +++ b/src/main/java/frc/lib/util/FieldConstants.java @@ -1,34 +1,12 @@ -/* - * MIT License - * - * Copyright (c) 2023 FRC 6328 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -// Modified by 5572 to include fieldSdf and associated Signed Distance Field functions package frc.lib.util; -import java.util.Map; -import edu.wpi.first.math.MathUtil; +import static edu.wpi.first.apriltag.AprilTagFields.k2024Crescendo; +import java.io.IOException; +import java.util.Optional; +import edu.wpi.first.apriltag.AprilTagFieldLayout; import edu.wpi.first.math.geometry.Pose2d; -import edu.wpi.first.math.geometry.Pose3d; import edu.wpi.first.math.geometry.Rotation2d; -import edu.wpi.first.math.geometry.Rotation3d; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.math.geometry.Translation3d; import edu.wpi.first.math.util.Units; @@ -36,219 +14,87 @@ import edu.wpi.first.wpilibj.DriverStation.Alliance; /** - * Field Constants + * Contains various field dimensions and useful reference points. Dimensions are in meters, and sets + * of corners start in the lower left moving clockwise. All units in Meters
+ *
+ * + *

+ * All translations and poses are stored with the origin at the rightmost point on the BLUE ALLIANCE + * wall.
+ *
+ * Length refers to the x direction (as described by wpilib)
+ * Width refers to the y direction (as described by wpilib) */ public class FieldConstants { - public static final double fieldLength = Units.inchesToMeters(651.25); - public static final double fieldWidth = Units.inchesToMeters(315.5); - public static final double tapeWidth = Units.inchesToMeters(2.0); - - /** - * Dimensions for community and charging station, including the tape. - */ - public static final class Community { - // Region dimensions - public static final double innerX = 0.0; - // Tape to the left of charging station - public static final double midX = Units.inchesToMeters(132.375); - // Tape to the right of charging station - public static final double outerX = Units.inchesToMeters(193.25); - public static final double leftY = Units.feetToMeters(18.0); - public static final double midY = leftY - Units.inchesToMeters(59.39) + tapeWidth; - public static final double rightY = 0.0; - public static final Translation2d[] regionCorners = new Translation2d[] { - new Translation2d(innerX, rightY), new Translation2d(innerX, leftY), - new Translation2d(midX, leftY), new Translation2d(midX, midY), - new Translation2d(outerX, midY), new Translation2d(outerX, rightY)}; + public static double fieldLength = Units.inchesToMeters(651.223); + public static double fieldWidth = Units.inchesToMeters(323.277); + public static double wingX = Units.inchesToMeters(229.201); + public static double podiumX = Units.inchesToMeters(126.75); + public static double startingLineX = Units.inchesToMeters(74.111); - // Charging station dimensions - public static final double chargingStationLength = Units.inchesToMeters(76.125); - public static final double chargingStationWidth = Units.inchesToMeters(97.25); - public static final double chargingStationOuterX = outerX - tapeWidth; - public static final double chargingStationInnerX = - chargingStationOuterX - chargingStationLength; - public static final double chargingStationLeftY = midY - tapeWidth; - public static final double chargingStationRightY = - chargingStationLeftY - chargingStationWidth; - public static final Translation2d[] chargingStationCorners = - new Translation2d[] {new Translation2d(chargingStationInnerX, chargingStationRightY), - new Translation2d(chargingStationInnerX, chargingStationLeftY), - new Translation2d(chargingStationOuterX, chargingStationRightY), - new Translation2d(chargingStationOuterX, chargingStationLeftY)}; + public static Translation2d ampCenter = + new Translation2d(Units.inchesToMeters(72.455), Units.inchesToMeters(322.996)); - // Cable bump - public static final double cableBumpInnerX = - innerX + Grids.outerX + Units.inchesToMeters(95.25); - public static final double cableBumpOuterX = cableBumpInnerX + Units.inchesToMeters(7); - public static final Translation2d[] cableBumpCorners = - new Translation2d[] {new Translation2d(cableBumpInnerX, 0.0), - new Translation2d(cableBumpInnerX, chargingStationRightY), - new Translation2d(cableBumpOuterX, 0.0), - new Translation2d(cableBumpOuterX, chargingStationRightY)}; - } - - /** - * Dimensions for grids and nodes - */ - public static final class Grids { - // X layout - public static final double outerX = Units.inchesToMeters(54.25); - // Centered when under cube nodes - public static final double lowX = outerX - (Units.inchesToMeters(14.25) / 2.0); - public static final double midX = outerX - Units.inchesToMeters(22.75); - public static final double highX = outerX - Units.inchesToMeters(39.75); - - // Y layout - public static final int nodeRowCount = 9; - public static final double nodeFirstY = Units.inchesToMeters(20.19); - public static final double nodeSeparationY = Units.inchesToMeters(22.0); + /** Staging locations for each note */ + public static final class StagingLocations { + public static double centerlineX = fieldLength / 2.0; - // Z layout - public static final double cubeEdgeHigh = Units.inchesToMeters(3.0); - public static final double highCubeZ = Units.inchesToMeters(35.5) - cubeEdgeHigh; - public static final double midCubeZ = Units.inchesToMeters(23.5) - cubeEdgeHigh; - public static final double highConeZ = Units.inchesToMeters(46.0); - public static final double midConeZ = Units.inchesToMeters(34.0); + // need to update + public static double centerlineFirstY = Units.inchesToMeters(29.638); + public static double centerlineSeparationY = Units.inchesToMeters(66); + public static double spikeX = Units.inchesToMeters(114); + // need + public static double spikeFirstY = Units.inchesToMeters(161.638); + public static double spikeSeparationY = Units.inchesToMeters(57); - // Translations (all nodes in the same column/row have the same X/Y coordinate) - public static final Translation2d[] lowTranslations = new Translation2d[nodeRowCount]; - public static final Translation2d[] midTranslations = new Translation2d[nodeRowCount]; - public static final Translation3d[] mid3dTranslations = new Translation3d[nodeRowCount]; - public static final Translation2d[] highTranslations = new Translation2d[nodeRowCount]; - public static final Translation3d[] high3dTranslations = new Translation3d[nodeRowCount]; + public static Translation2d[] centerlineTranslations = new Translation2d[5]; + public static Translation2d[] spikeTranslations = new Translation2d[3]; static { - for (int i = 0; i < nodeRowCount; i++) { - boolean isCube = i == 1 || i == 4 || i == 7; - lowTranslations[i] = new Translation2d(lowX, nodeFirstY + nodeSeparationY * i); - midTranslations[i] = new Translation2d(midX, nodeFirstY + nodeSeparationY * i); - mid3dTranslations[i] = new Translation3d(midX, nodeFirstY + nodeSeparationY * i, - isCube ? midCubeZ : midConeZ); - high3dTranslations[i] = new Translation3d(highX, nodeFirstY + nodeSeparationY * i, - isCube ? highCubeZ : highConeZ); - highTranslations[i] = new Translation2d(highX, nodeFirstY + nodeSeparationY * i); + for (int i = 0; i < centerlineTranslations.length; i++) { + centerlineTranslations[i] = + new Translation2d(centerlineX, centerlineFirstY + (i * centerlineSeparationY)); } } - // Complex low layout - // shifted to account for cube vs cone rows and wide edge nodes - public static final double complexLowXCones = outerX - Units.inchesToMeters(16.0) / 2.0; - // Centered X under cone nodes - public static final double complexLowXCubes = lowX; - // Centered X under cube nodes - public static final double complexLowOuterYOffset = - nodeFirstY - Units.inchesToMeters(3.0) - (Units.inchesToMeters(25.75) / 2.0); - - public static final Translation2d[] complexLowTranslations = new Translation2d[] { - new Translation2d(complexLowXCones, nodeFirstY - complexLowOuterYOffset), - new Translation2d(complexLowXCubes, nodeFirstY + nodeSeparationY * 1), - new Translation2d(complexLowXCones, nodeFirstY + nodeSeparationY * 2), - new Translation2d(complexLowXCones, nodeFirstY + nodeSeparationY * 3), - new Translation2d(complexLowXCubes, nodeFirstY + nodeSeparationY * 4), - new Translation2d(complexLowXCones, nodeFirstY + nodeSeparationY * 5), - new Translation2d(complexLowXCones, nodeFirstY + nodeSeparationY * 6), - new Translation2d(complexLowXCubes, nodeFirstY + nodeSeparationY * 7), - new Translation2d(complexLowXCones, - nodeFirstY + nodeSeparationY * 8 + complexLowOuterYOffset)}; + static { + for (int i = 0; i < spikeTranslations.length; i++) { + spikeTranslations[i] = + new Translation2d(spikeX, spikeFirstY + (i * spikeSeparationY)); + } + } } - /** - * Dimensions for loading zone and substations, including the tape - */ - public static final class LoadingZone { - // Region dimensions - public static final double width = Units.inchesToMeters(99.0); - public static final double innerX = FieldConstants.fieldLength; - public static final double midX = fieldLength - Units.inchesToMeters(132.25); - public static final double outerX = fieldLength - Units.inchesToMeters(264.25); - public static final double leftY = FieldConstants.fieldWidth; - public static final double midY = leftY - Units.inchesToMeters(50.5); - public static final double rightY = leftY - width; - // Start at lower left next to border with opponent community - public static final Translation2d[] regionCorners = - new Translation2d[] {new Translation2d(midX, rightY), new Translation2d(midX, midY), - new Translation2d(outerX, midY), new Translation2d(outerX, leftY), - new Translation2d(innerX, leftY), new Translation2d(innerX, rightY)}; - - // Double substation dimensions - public static final double doubleSubstationLength = Units.inchesToMeters(14.0); - public static final double doubleSubstationX = innerX - doubleSubstationLength; - public static final double doubleSubstationShelfZ = Units.inchesToMeters(37.375); - - // Single substation dimensions - public static final double singleSubstationWidth = Units.inchesToMeters(22.75); - public static final double singleSubstationLeftX = - FieldConstants.fieldLength - doubleSubstationLength - Units.inchesToMeters(88.77); - public static final double singleSubstationCenterX = - singleSubstationLeftX + (singleSubstationWidth / 2.0); - public static final double singleSubstationRightX = - singleSubstationLeftX + singleSubstationWidth; - public static final Translation2d singleSubstationTranslation = - new Translation2d(singleSubstationCenterX, leftY); + /** Each corner of the speaker * */ + public static final class Speaker { - public static final double singleSubstationHeight = Units.inchesToMeters(18.0); - public static final double singleSubstationLowZ = Units.inchesToMeters(27.125); - public static final double singleSubstationCenterZ = - singleSubstationLowZ + (singleSubstationHeight / 2.0); - public static final double singleSubstationHighZ = - singleSubstationLowZ + singleSubstationHeight; + /** Center of the speaker opening (blue alliance) */ + public static Pose2d centerSpeakerOpening = + new Pose2d(0.0, fieldWidth - Units.inchesToMeters(104.0), new Rotation2d()); } - /** - * Locations of staged game pieces - */ - public static final class StagingLocations { - public static final double centerOffsetX = Units.inchesToMeters(47.36); - public static final double positionX = fieldLength / 2.0 - Units.inchesToMeters(47.36); - public static final double firstY = Units.inchesToMeters(36.19); - public static final double separationY = Units.inchesToMeters(48.0); - public static final Translation2d[] translations = new Translation2d[4]; + // corners (blue alliance origin) + public static Translation3d topRightSpeaker = new Translation3d(Units.inchesToMeters(18.055), + Units.inchesToMeters(238.815), Units.inchesToMeters(83.091)); - static { - for (int i = 0; i < translations.length; i++) { - translations[i] = new Translation2d(positionX, firstY + (i * separationY)); - } - } - } + public static Translation3d topLeftSpeaker = new Translation3d(Units.inchesToMeters(18.055), + Units.inchesToMeters(197.765), Units.inchesToMeters(83.091)); - /** - * AprilTag locations (do not flip for red alliance) - */ - public static final Map aprilTags = Map.of(1, - new Pose3d(Units.inchesToMeters(610.77), Units.inchesToMeters(42.19), - Units.inchesToMeters(18.22), new Rotation3d(0.0, 0.0, Math.PI)), - 2, - new Pose3d(Units.inchesToMeters(610.77), Units.inchesToMeters(108.19), - Units.inchesToMeters(18.22), new Rotation3d(0.0, 0.0, Math.PI)), - 3, - new Pose3d(Units.inchesToMeters(610.77), Units.inchesToMeters(174.19), - Units.inchesToMeters(18.22), new Rotation3d(0.0, 0.0, Math.PI)), - 4, - new Pose3d(Units.inchesToMeters(636.96), Units.inchesToMeters(265.74), - Units.inchesToMeters(27.38), new Rotation3d(0.0, 0.0, Math.PI)), - 5, - new Pose3d(Units.inchesToMeters(14.25), Units.inchesToMeters(265.74), - Units.inchesToMeters(27.38), new Rotation3d()), - 6, - new Pose3d(Units.inchesToMeters(40.45), Units.inchesToMeters(174.19), - Units.inchesToMeters(18.22), new Rotation3d()), - 7, - new Pose3d(Units.inchesToMeters(40.45), Units.inchesToMeters(108.19), - Units.inchesToMeters(18.22), new Rotation3d()), - 8, new Pose3d(Units.inchesToMeters(40.45), Units.inchesToMeters(42.19), - Units.inchesToMeters(18.22), new Rotation3d())); + public static Translation3d bottomRightSpeaker = + new Translation3d(0.0, Units.inchesToMeters(238.815), Units.inchesToMeters(78.324)); + public static Translation3d bottomLeftSpeaker = + new Translation3d(0.0, Units.inchesToMeters(197.765), Units.inchesToMeters(78.324)); + public static double centerSpeaker = topLeftSpeaker.getZ() - bottomLeftSpeaker.getZ(); - /** - * Flips a translation to the correct side of the field based on the current alliance color. By - * default, all translations and poses in {@link FieldConstants} are stored with the origin at - * the rightmost point on the BLUE ALLIANCE wall. - */ - public static Translation2d allianceFlip(Translation2d translation) { - if (DriverStation.getAlliance().get() == Alliance.Red) { - return new Translation2d(fieldLength - translation.getX(), translation.getY()); - } else { - return translation; + public static double aprilTagWidth = Units.inchesToMeters(6.50); + public static AprilTagFieldLayout aprilTags; + + static { + try { + aprilTags = AprilTagFieldLayout.loadFromResource(k2024Crescendo.m_resourceFile); + } catch (IOException e) { + throw new RuntimeException(e); } } @@ -261,125 +107,49 @@ public static Translation2d allianceFlip(Translation2d translation) { * @return Pose2d flipped to Red Alliance */ public static Pose2d allianceFlip(Pose2d pose) { - if (DriverStation.getAlliance().get() == Alliance.Red) { - return new Pose2d(fieldLength - pose.getX(), pose.getY(), - new Rotation2d(-pose.getRotation().getCos(), pose.getRotation().getSin())); - } else { - return pose; + Optional ally = DriverStation.getAlliance(); + if (ally.isPresent()) { + if (ally.get() == Alliance.Red) { + pose = new Pose2d(fieldLength - pose.getX(), pose.getY(), + new Rotation2d(-pose.getRotation().getCos(), pose.getRotation().getSin())); + } } + return pose; } /** - * elementwise maximum + * Flips a pose to the correct side of the field based on the current alliance color. By + * default, all translations and poses in {@link FieldConstants} are stored with the origin at + * the rightmost point on the BLUE ALLIANCE wall. * - * @param x Translation - * @return Max Translation + * @param pose Initial Pose + * @return Pose2d flipped to Red Alliance */ - private static Translation2d max(Translation2d... x) { - Translation2d r = x[0]; - for (int i = 1; i < x.length; i++) { - if (x[i].getX() > r.getX()) { - r = new Translation2d(x[i].getX(), r.getY()); - } - if (x[i].getY() > r.getY()) { - r = new Translation2d(r.getX(), x[i].getY()); + public static Translation2d allianceFlip(Translation2d pose) { + Optional ally = DriverStation.getAlliance(); + if (ally.isPresent()) { + if (ally.get() == Alliance.Red) { + pose = new Translation2d(fieldLength - pose.getX(), pose.getY()); } } - return r; - } - - /** - * Vector dot product for Translation2d types - * - * @param a Translation A - * @param b Translation B - * @return Dot - */ - private static double dot(Translation2d a, Translation2d b) { - return a.getX() * b.getX() + a.getY() * b.getY(); - } - - /** - * https://www.youtube.com/watch?v=PMltMdi1Wzg - * - * @param P P - * @param p0 P0 - * @param p1 P1 - * @return Line SDF - */ - private static double lineSdf(Translation2d p, Translation2d p0, Translation2d p1) { - Translation2d pa = p.minus(p0); - Translation2d ba = p1.minus(p0); - double h = MathUtil.clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return pa.minus(ba.times(h)).getNorm(); - } - - /** - * https://www.youtube.com/watch?v=62-pRVZuS5c - * - * @param P P - * @param p0 P0 - * @param p1 P1 - * @return Box SDF - */ - private static double boxSdf(Translation2d P, Translation2d p0, Translation2d p1) { - Translation2d min = - new Translation2d(Math.min(p0.getX(), p1.getX()), Math.min(p0.getY(), p1.getY())); - Translation2d max = - new Translation2d(Math.max(p0.getX(), p1.getX()), Math.max(p0.getY(), p1.getY())); - Translation2d b = max.minus(min).times(0.5); - Translation2d p = P.minus(b).minus(min); - Translation2d d = new Translation2d(Math.abs(p.getX()), Math.abs(p.getY())).minus(b); - return max(d, new Translation2d()).getNorm() + Math.min(Math.max(d.getX(), d.getY()), 0.0); + return pose; } /** - * find minimum distance given a set of distances (variadic min) + * Flips a pose to the correct side of the field based on the current alliance color. By + * default, all translations and poses in {@link FieldConstants} are stored with the origin at + * the rightmost point on the BLUE ALLIANCE wall. * - * @param x Something - * @return UNion SDF + * @param rotation Initial Pose + * @return Rotation2d flipped to Red Alliance */ - private static double unionSdf(double... x) { - double y = x[0]; - for (int i = 1; i < x.length; i++) { - if (x[i] < y) { - y = x[i]; + public static Rotation2d allianceFlip(Rotation2d rotation) { + Optional ally = DriverStation.getAlliance(); + if (ally.isPresent()) { + if (ally.get() == Alliance.Red) { + rotation = new Rotation2d(-rotation.getCos(), rotation.getSin()); } } - return y; + return rotation; } - - /** - * Find the minimum distance from a circular robot to an obstacle on the field. - * - * @param p Starting translation - * @param radius Radius - * @return field SDF - */ - public static double fieldSdf(Translation2d p, double radius) { - return fieldSdf(p) - radius; - } - - /** - * Find the minimum distance from a point to an obstacle on the field. - */ - public static double fieldSdf(Translation2d p) { - Translation2d q = new Translation2d(fieldLength - p.getX(), p.getY()); - Translation2d gridCorner = new Translation2d(Grids.outerX, Community.leftY); - - double inField = - -boxSdf(p, new Translation2d(), new Translation2d(fieldLength, fieldWidth)); - - double blueGrid = boxSdf(p, new Translation2d(), gridCorner); - double blueRail = lineSdf(p, gridCorner, Community.regionCorners[2]); - double blueRamp = - boxSdf(p, Community.chargingStationCorners[0], Community.chargingStationCorners[3]); - double redGrid = boxSdf(q, new Translation2d(), gridCorner); - double redRail = lineSdf(q, gridCorner, Community.regionCorners[2]); - double redRamp = - boxSdf(q, Community.chargingStationCorners[0], Community.chargingStationCorners[3]); - - return unionSdf(inField, blueGrid, blueRail, blueRamp, redGrid, redRail, redRamp); - } - } diff --git a/src/main/java/frc/lib/util/LazySelectCommand.java b/src/main/java/frc/lib/util/LazySelectCommand.java new file mode 100644 index 0000000..ae5e8ef --- /dev/null +++ b/src/main/java/frc/lib/util/LazySelectCommand.java @@ -0,0 +1,73 @@ +package frc.lib.util; + +import static edu.wpi.first.util.ErrorMessages.requireNonNullParam; +import java.util.Map; +import java.util.function.Supplier; +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.SelectCommand; + +/** + * Similar to {@link SelectCommand} but allows commands for all variants of an enum instead. Also + * doesn't require all subsystems of all commands, only the selected command on run time. + */ +public class LazySelectCommand extends Command { + private final Map m_commands; + private final Supplier m_selector; + private Command m_command; + + /** + * Create new LazySelectCommand. Note that the length of enumValues and commandValues must be + * the same. + * + * @throws IllegalArgumentException when enumValues and commandValues have different lengths. + */ + public LazySelectCommand(Map commands, Supplier selector) { + m_commands = requireNonNullParam(commands, "commands", "LazySelectCommand"); + m_selector = requireNonNullParam(selector, "selector", "LazySelectCommand"); + } + + @Override + public void initialize() { + m_command = m_commands.getOrDefault(m_selector.get(), Commands.none()); + m_command.schedule(); + } + + @Override + public void end(boolean interrupted) { + if (interrupted) { + m_command.cancel(); + } + m_command = null; + } + + @Override + public void execute() {} + + @Override + public boolean isFinished() { + // because we're between `initialize` and `end`, `m_command` is necessarily not null + // but if called otherwise and m_command is null, + // it's UB, so we can do whatever we want -- like return true. + return m_command == null || !m_command.isScheduled(); + } + + /** + * Whether the given command should run when the robot is disabled. Override to return true if + * the command should run when disabled. + * + * @return true. Otherwise, this proxy would cancel commands that do run when disabled. + */ + @Override + public boolean runsWhenDisabled() { + return true; + } + + @Override + public void initSendable(SendableBuilder builder) { + super.initSendable(builder); + builder.addStringProperty("lazy select command", + () -> m_command == null ? "null" : m_command.getName(), null); + } +} diff --git a/src/main/java/frc/lib/util/LongArrayList.java b/src/main/java/frc/lib/util/LongArrayList.java new file mode 100644 index 0000000..cb05dac --- /dev/null +++ b/src/main/java/frc/lib/util/LongArrayList.java @@ -0,0 +1,117 @@ +package frc.lib.util; + +import java.util.List; +import java.util.Objects; +import java.util.RandomAccess; +import frc.lib.profiling.LoggingProfiler; + +/** + * An optimized long array. Used in lieu of an {@code ArrayList} to avoid boxing (and the + * performance implications that come with it). + * + *

+ * Current interface is limited to the methods used by {@link LoggingProfiler}, though its interface + * may expand in the future to match the full {@link List} interface. + */ +public class LongArrayList implements RandomAccess, Cloneable, java.io.Serializable { + private static final long serialVersionUID = -7046029254386353130L; + + /** + * During list creation, many reallocations at low capacities may be common, so we jump from 0 + * to 10 before our normal reallocation scheme. + */ + public static final int DEFAULT_INITIAL_CAPACITY = 10; + + protected transient long[] backing; + protected int size; + + /** + * A static, final, empty array to be used as default array in allocations. + */ + private static final long[] DEFAULT_EMPTY_ARRAY = {}; + + + /** + * Creates a new array list with {@link #DEFAULT_INITIAL_CAPACITY} capacity. + */ + public LongArrayList() { + backing = DEFAULT_EMPTY_ARRAY; + } + + /** + * Grows this arraylist, ensuring it can contain {@code capacity} longs. + * + * @param capacity the new minimum capacity for this array list. + */ + private void grow(int capacity) { + if (capacity <= backing.length) { + return; + } + if (backing != DEFAULT_EMPTY_ARRAY) { + capacity = (int) Math.max( + Math.min((long) backing.length + (backing.length >> 1), Integer.MAX_VALUE - 8), + capacity); + } else if (capacity < DEFAULT_INITIAL_CAPACITY) { + capacity = DEFAULT_INITIAL_CAPACITY; + } + backing = forceCapacity(backing, capacity, size); + assert size <= backing.length; + } + + /** + * Appends {@code k} to the end of this list. + */ + public void add(final long k) { + grow(size + 1); + backing[size++] = k; + assert size <= backing.length; + } + + /** + * Get long element at index {@code index}. + */ + public long get(int index) { + Objects.checkIndex(index, size); + return backing[index]; + } + + /** + * Checks if the list has no elements. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the number of elements in this list. + */ + public int size() { + return size; + } + + /** + * Removes the element at the specified position in this list. + */ + public long remove(final int index) { + Objects.checkIndex(index, size); + final long[] a = this.backing; + final long old = a[index]; + size--; + if (index != size) { + System.arraycopy(a, index + 1, a, index, size); + } + assert size <= a.length; + return old; + } + + public void clear() { + this.size = 0; + } + + private static long[] forceCapacity(final long[] array, final int length, final int preserve) { + final long[] newArray = new long[length]; + System.arraycopy(array, 0, newArray, 0, preserve); + return newArray; + } + +} diff --git a/src/main/java/frc/lib/util/ReconfigurableSimpleMotorFeedforward.java b/src/main/java/frc/lib/util/ReconfigurableSimpleMotorFeedforward.java new file mode 100644 index 0000000..4294871 --- /dev/null +++ b/src/main/java/frc/lib/util/ReconfigurableSimpleMotorFeedforward.java @@ -0,0 +1,150 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc.lib.util; + +import edu.wpi.first.math.MatBuilder; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.controller.LinearPlantInversionFeedforward; +import edu.wpi.first.math.system.plant.LinearSystemId; + +/** A helper class that computes feedforward outputs for a simple permanent-magnet DC motor. */ +public class ReconfigurableSimpleMotorFeedforward { + /** The static gain. */ + public double ks; + + /** The velocity gain. */ + public double kv; + + /** The acceleration gain. */ + public double ka; + + /** + * Creates a new SimpleMotorFeedforward with the specified gains. Units of the gain values will + * dictate units of the computed feedforward. + * + * @param ks The static gain. + * @param kv The velocity gain. + * @param ka The acceleration gain. + * @throws IllegalArgumentException for kv < zero. + * @throws IllegalArgumentException for ka < zero. + */ + public ReconfigurableSimpleMotorFeedforward(double ks, double kv, double ka) { + this.ks = ks; + this.kv = kv; + this.ka = ka; + } + + /** + * Creates a new SimpleMotorFeedforward with the specified gains. Acceleration gain is defaulted + * to zero. Units of the gain values will dictate units of the computed feedforward. + * + * @param ks The static gain. + * @param kv The velocity gain. + */ + public ReconfigurableSimpleMotorFeedforward(double ks, double kv) { + this(ks, kv, 0); + } + + /** + * Calculates the feedforward from the gains and setpoints. + * + * @param velocity The velocity setpoint. + * @param acceleration The acceleration setpoint. + * @return The computed feedforward. + */ + public double calculate(double velocity, double acceleration) { + return ks * Math.signum(velocity) + kv * velocity + ka * acceleration; + } + + /** + * Calculates the feedforward from the gains and setpoints. + * + * @param currentVelocity The current velocity setpoint. + * @param nextVelocity The next velocity setpoint. + * @param dtSeconds Time between velocity setpoints in seconds. + * @return The computed feedforward. + */ + public double calculate(double currentVelocity, double nextVelocity, double dtSeconds) { + var plant = LinearSystemId.identifyVelocitySystem(this.kv, this.ka); + var feedforward = new LinearPlantInversionFeedforward<>(plant, dtSeconds); + + var r = MatBuilder.fill(Nat.N1(), Nat.N1(), currentVelocity); + var nextR = MatBuilder.fill(Nat.N1(), Nat.N1(), nextVelocity); + + return ks * Math.signum(currentVelocity) + feedforward.calculate(r, nextR).get(0, 0); + } + + // Rearranging the main equation from the calculate() method yields the + // formulas for the methods below: + + /** + * Calculates the feedforward from the gains and velocity setpoint (acceleration is assumed to + * be zero). + * + * @param velocity The velocity setpoint. + * @return The computed feedforward. + */ + public double calculate(double velocity) { + return calculate(velocity, 0); + } + + /** + * Calculates the maximum achievable velocity given a maximum voltage supply and an + * acceleration. Useful for ensuring that velocity and acceleration constraints for a + * trapezoidal profile are simultaneously achievable - enter the acceleration constraint, and + * this will give you a simultaneously-achievable velocity constraint. + * + * @param maxVoltage The maximum voltage that can be supplied to the motor. + * @param acceleration The acceleration of the motor. + * @return The maximum possible velocity at the given acceleration. + */ + public double maxAchievableVelocity(double maxVoltage, double acceleration) { + // Assume max velocity is positive + return (maxVoltage - ks - acceleration * ka) / kv; + } + + /** + * Calculates the minimum achievable velocity given a maximum voltage supply and an + * acceleration. Useful for ensuring that velocity and acceleration constraints for a + * trapezoidal profile are simultaneously achievable - enter the acceleration constraint, and + * this will give you a simultaneously-achievable velocity constraint. + * + * @param maxVoltage The maximum voltage that can be supplied to the motor. + * @param acceleration The acceleration of the motor. + * @return The minimum possible velocity at the given acceleration. + */ + public double minAchievableVelocity(double maxVoltage, double acceleration) { + // Assume min velocity is negative, ks flips sign + return (-maxVoltage + ks - acceleration * ka) / kv; + } + + /** + * Calculates the maximum achievable acceleration given a maximum voltage supply and a velocity. + * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are + * simultaneously achievable - enter the velocity constraint, and this will give you a + * simultaneously-achievable acceleration constraint. + * + * @param maxVoltage The maximum voltage that can be supplied to the motor. + * @param velocity The velocity of the motor. + * @return The maximum possible acceleration at the given velocity. + */ + public double maxAchievableAcceleration(double maxVoltage, double velocity) { + return (maxVoltage - ks * Math.signum(velocity) - velocity * kv) / ka; + } + + /** + * Calculates the minimum achievable acceleration given a maximum voltage supply and a velocity. + * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are + * simultaneously achievable - enter the velocity constraint, and this will give you a + * simultaneously-achievable acceleration constraint. + * + * @param maxVoltage The maximum voltage that can be supplied to the motor. + * @param velocity The velocity of the motor. + * @return The minimum possible acceleration at the given velocity. + */ + public double minAchievableAcceleration(double maxVoltage, double velocity) { + return maxAchievableAcceleration(-maxVoltage, velocity); + } +} diff --git a/src/main/java/frc/lib/util/swerve/SwerveModule.java b/src/main/java/frc/lib/util/swerve/SwerveModule.java index a1fa103..492e905 100644 --- a/src/main/java/frc/lib/util/swerve/SwerveModule.java +++ b/src/main/java/frc/lib/util/swerve/SwerveModule.java @@ -1,123 +1,53 @@ package frc.lib.util.swerve; -import com.ctre.phoenix6.configs.CANcoderConfiguration; -import com.ctre.phoenix6.configs.TalonFXConfiguration; -import com.ctre.phoenix6.controls.DutyCycleOut; -import com.ctre.phoenix6.controls.PositionVoltage; -import com.ctre.phoenix6.controls.VelocityVoltage; -import com.ctre.phoenix6.hardware.CANcoder; -import com.ctre.phoenix6.hardware.TalonFX; -import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import org.littletonrobotics.junction.Logger; import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.kinematics.SwerveModulePosition; import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.lib.math.Conversions; import frc.robot.Constants; +import frc.robot.Robot; /** - * Swerve Module + * Swerve Module Subsystem */ public class SwerveModule { public int moduleNumber; private Rotation2d angleOffset; + // private double lastAngle; - private TalonFX mAngleMotor; - private TalonFX mDriveMotor; - private CANcoder angleEncoder; - private TalonFXConfiguration swerveAngleFXConfig = new TalonFXConfiguration(); - private TalonFXConfiguration swerveDriveFXConfig = new TalonFXConfiguration(); - private CANcoderConfiguration swerveCANcoderConfig = new CANcoderConfiguration(); - - private final SimpleMotorFeedforward driveFeedForward = new SimpleMotorFeedforward( - Constants.Swerve.driveKS, Constants.Swerve.driveKV, Constants.Swerve.driveKA); - - /* drive motor control requests */ - private final DutyCycleOut driveDutyCycle = new DutyCycleOut(0); - private final VelocityVoltage driveVelocity = new VelocityVoltage(0); - - /* angle motor control requests */ - private final PositionVoltage anglePosition = new PositionVoltage(0); + private SwerveModuleIO io; + private SwerveModuleInputsAutoLogged inputs = new SwerveModuleInputsAutoLogged(); /** * Swerve Module * * @param moduleNumber Module Number - * @param driveMotorID CAN ID of the Drive Motor - * @param angleMotorID CAN ID of the Angle Motor - * @param cancoderID CAN ID of the CANCoder * @param angleOffset Angle Offset of the CANCoder to align the wheels */ - public SwerveModule(int moduleNumber, int driveMotorID, int angleMotorID, int cancoderID, - Rotation2d angleOffset) { - this.moduleNumber = moduleNumber; - this.angleOffset = angleOffset; + public SwerveModule(int moduleNumber, Rotation2d angleOffset, SwerveModuleIO io) { + this.io = io; - /* Angle Encoder Config */ - swerveCANcoderConfig.MagnetSensor.SensorDirection = Constants.Swerve.cancoderInvert; - - angleEncoder = new CANcoder(cancoderID); - angleEncoder.getConfigurator().apply(swerveCANcoderConfig); - - /* Angle Motor Config */ - /* Motor Inverts and Neutral Mode */ - swerveAngleFXConfig.MotorOutput.Inverted = Constants.Swerve.angleMotorInvert; - swerveAngleFXConfig.MotorOutput.NeutralMode = Constants.Swerve.angleNeutralMode; - - /* Gear Ratio and Wrapping Config */ - swerveAngleFXConfig.Feedback.SensorToMechanismRatio = Constants.Swerve.angleGearRatio; - swerveAngleFXConfig.ClosedLoopGeneral.ContinuousWrap = true; + this.moduleNumber = moduleNumber; - /* Current Limiting */ - swerveAngleFXConfig.CurrentLimits.SupplyCurrentLimitEnable = - Constants.Swerve.angleEnableCurrentLimit; - swerveAngleFXConfig.CurrentLimits.SupplyCurrentLimit = Constants.Swerve.angleCurrentLimit; - swerveAngleFXConfig.CurrentLimits.SupplyCurrentThreshold = - Constants.Swerve.angleCurrentThreshold; - swerveAngleFXConfig.CurrentLimits.SupplyTimeThreshold = - Constants.Swerve.angleCurrentThresholdTime; + this.angleOffset = angleOffset; - /* PID Config */ - swerveAngleFXConfig.Slot0.kP = Constants.Swerve.angleKP; - swerveAngleFXConfig.Slot0.kI = Constants.Swerve.angleKI; - swerveAngleFXConfig.Slot0.kD = Constants.Swerve.angleKD; - mAngleMotor = new TalonFX(angleMotorID); - mAngleMotor.getConfigurator().apply(swerveAngleFXConfig); + // lastAngle = getState().angle.getDegrees(); + io.updateInputs(inputs); resetToAbsolute(); + Logger.processInputs("SwerveModule" + moduleNumber, inputs); + } - /* Drive Motor Config */ - /* Motor Inverts and Neutral Mode */ - swerveDriveFXConfig.MotorOutput.Inverted = Constants.Swerve.driveMotorInvert; - swerveDriveFXConfig.MotorOutput.NeutralMode = Constants.Swerve.driveNeutralMode; - - /* Gear Ratio Config */ - swerveDriveFXConfig.Feedback.SensorToMechanismRatio = Constants.Swerve.driveGearRatio; - - /* Current Limiting */ - swerveDriveFXConfig.CurrentLimits.SupplyCurrentLimitEnable = - Constants.Swerve.driveEnableCurrentLimit; - swerveDriveFXConfig.CurrentLimits.SupplyCurrentLimit = Constants.Swerve.driveCurrentLimit; - swerveDriveFXConfig.CurrentLimits.SupplyCurrentThreshold = - Constants.Swerve.driveCurrentThreshold; - swerveDriveFXConfig.CurrentLimits.SupplyTimeThreshold = - Constants.Swerve.driveCurrentThresholdTime; - - /* PID Config */ - swerveDriveFXConfig.Slot0.kP = Constants.Swerve.driveKP; - swerveDriveFXConfig.Slot0.kI = Constants.Swerve.driveKI; - swerveDriveFXConfig.Slot0.kD = Constants.Swerve.driveKD; - - /* Open and Closed Loop Ramping */ - swerveDriveFXConfig.OpenLoopRamps.DutyCycleOpenLoopRampPeriod = - Constants.Swerve.openLoopRamp; - swerveDriveFXConfig.OpenLoopRamps.VoltageOpenLoopRampPeriod = Constants.Swerve.openLoopRamp; - - swerveDriveFXConfig.ClosedLoopRamps.DutyCycleClosedLoopRampPeriod = - Constants.Swerve.closedLoopRamp; - swerveDriveFXConfig.ClosedLoopRamps.VoltageClosedLoopRampPeriod = - Constants.Swerve.closedLoopRamp; - mDriveMotor = new TalonFX(driveMotorID); - mDriveMotor.getConfigurator().apply(swerveDriveFXConfig); - mDriveMotor.getConfigurator().setPosition(0.0); + /** + * Update inputs for a Swerve Module. + */ + public void periodic() { + Robot.profiler.push("updateInputs"); + io.updateInputs(inputs); + Robot.profiler.swap("processInputs"); + Logger.processInputs("SwerveModule" + moduleNumber, inputs); + Robot.profiler.pop(); } /** @@ -128,8 +58,12 @@ public SwerveModule(int moduleNumber, int driveMotorID, int angleMotorID, int ca */ public void setDesiredState(SwerveModuleState desiredState, boolean isOpenLoop) { desiredState = SwerveModuleState.optimize(desiredState, getState().angle); - mAngleMotor.setControl(anglePosition.withPosition(desiredState.angle.getRotations())); + io.setAngleMotor(desiredState.angle.getRotations()); setSpeed(desiredState, isOpenLoop); + SmartDashboard.putNumber("desired state speed/" + moduleNumber, + desiredState.speedMetersPerSecond); + SmartDashboard.putNumber("desired state angle/" + moduleNumber, + desiredState.angle.getDegrees()); } /** @@ -140,14 +74,10 @@ public void setDesiredState(SwerveModuleState desiredState, boolean isOpenLoop) */ private void setSpeed(SwerveModuleState desiredState, boolean isOpenLoop) { if (isOpenLoop) { - driveDutyCycle.Output = desiredState.speedMetersPerSecond / Constants.Swerve.maxSpeed; - mDriveMotor.setControl(driveDutyCycle); + double power = desiredState.speedMetersPerSecond / Constants.Swerve.maxSpeed; + io.setDriveMotorPower(power); } else { - driveVelocity.Velocity = Conversions.MPSToRPS(desiredState.speedMetersPerSecond, - Constants.Swerve.wheelCircumference); - driveVelocity.FeedForward = - driveFeedForward.calculate(desiredState.speedMetersPerSecond); - mDriveMotor.setControl(driveVelocity); + io.setDriveMotor(desiredState.speedMetersPerSecond); } } @@ -157,7 +87,7 @@ private void setSpeed(SwerveModuleState desiredState, boolean isOpenLoop) { * @return The rotation of the CANCoder in {@link Rotation2d} */ public Rotation2d getCANcoder() { - return Rotation2d.fromRotations(angleEncoder.getAbsolutePosition().getValue()); + return Rotation2d.fromRotations(inputs.absolutePositionAngleEncoder); } /** @@ -165,7 +95,8 @@ public Rotation2d getCANcoder() { */ public void resetToAbsolute() { double absolutePosition = getCANcoder().getRotations() - angleOffset.getRotations(); - mAngleMotor.setPosition(absolutePosition); + io.setPositionAngleMotor(absolutePosition); + inputs.angleMotorSelectedPosition = absolutePosition; } /** @@ -175,9 +106,9 @@ public void resetToAbsolute() { */ public SwerveModuleState getState() { return new SwerveModuleState( - Conversions.RPSToMPS(mDriveMotor.getVelocity().getValue(), + Conversions.rotationPerSecondToMetersPerSecond(inputs.driveMotorSelectedSensorVelocity, Constants.Swerve.wheelCircumference), - Rotation2d.fromRotations(mAngleMotor.getPosition().getValue())); + Rotation2d.fromRotations(inputs.angleMotorSelectedPosition)); } /** @@ -187,8 +118,9 @@ public SwerveModuleState getState() { */ public SwerveModulePosition getPosition() { return new SwerveModulePosition( - Conversions.rotationsToMeters(mDriveMotor.getPosition().getValue(), + Conversions.rotationsToMeters(inputs.driveMotorSelectedPosition, Constants.Swerve.wheelCircumference), - Rotation2d.fromRotations(mAngleMotor.getPosition().getValue())); + Rotation2d.fromRotations(inputs.angleMotorSelectedPosition)); + } } diff --git a/src/main/java/frc/lib/util/swerve/SwerveModuleIO.java b/src/main/java/frc/lib/util/swerve/SwerveModuleIO.java new file mode 100644 index 0000000..011a051 --- /dev/null +++ b/src/main/java/frc/lib/util/swerve/SwerveModuleIO.java @@ -0,0 +1,33 @@ +package frc.lib.util.swerve; + +import org.littletonrobotics.junction.AutoLog; + +/** IO Class for SwerveModule */ +public interface SwerveModuleIO { + /** Inputs Class for SwerveModule */ + @AutoLog + public static class SwerveModuleInputs { + public double driveMotorSelectedPosition; + public double driveMotorSelectedSensorVelocity; + public double angleMotorSelectedPosition; + public double absolutePositionAngleEncoder; + public double[] odometryTimestamps; + // public double driveMotorTemp; + // public double angleMotorTemp; + } + + public default void setModNumber(int number) {} + + public default void updateInputs(SwerveModuleInputs inputs) {} + + public default void setDriveMotor(double mps) {} + + public default void setDriveMotorPower(double power) {} + + public default void setAngleMotor(double angle) {} + + public default void setAngleSelectedSensorPosition(double angle) {} + + public default void setPositionAngleMotor(double absolutePosition) {} + +} diff --git a/src/main/java/frc/lib/util/swerve/SwerveModuleReal.java b/src/main/java/frc/lib/util/swerve/SwerveModuleReal.java new file mode 100644 index 0000000..c2f3d38 --- /dev/null +++ b/src/main/java/frc/lib/util/swerve/SwerveModuleReal.java @@ -0,0 +1,161 @@ +package frc.lib.util.swerve; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.DutyCycleOut; +import com.ctre.phoenix6.controls.PositionVoltage; +import com.ctre.phoenix6.controls.VelocityVoltage; +import com.ctre.phoenix6.hardware.CANcoder; +import com.ctre.phoenix6.hardware.TalonFX; +import frc.lib.math.Conversions; +import frc.robot.Constants; + +/** + * Swerve Module IO + */ +public class SwerveModuleReal implements SwerveModuleIO { + + private TalonFX mAngleMotor; + private TalonFX mDriveMotor; + private CANcoder angleEncoder; + private TalonFXConfiguration swerveAngleFXConfig = new TalonFXConfiguration(); + private TalonFXConfiguration swerveDriveFXConfig = new TalonFXConfiguration(); + private CANcoderConfiguration swerveCANcoderConfig = new CANcoderConfiguration(); + + private StatusSignal driveMotorSelectedPosition; + private StatusSignal driveMotorSelectedSensorVelocity; + private StatusSignal angleMotorSelectedPosition; + private StatusSignal absolutePositionAngleEncoder; + + /* drive motor control requests */ + private final DutyCycleOut driveDutyCycle = new DutyCycleOut(0); + private final VelocityVoltage driveVelocity = new VelocityVoltage(0); + + /* angle motor control requests */ + private final PositionVoltage anglePosition = new PositionVoltage(0); + + /** Instantiating motors and Encoders */ + public SwerveModuleReal(int driveMotorID, int angleMotorID, int cancoderID) { + + angleEncoder = new CANcoder(cancoderID, "canivore"); + mDriveMotor = new TalonFX(driveMotorID, "canivore"); + mAngleMotor = new TalonFX(angleMotorID, "canivore"); + + configAngleEncoder(); + configAngleMotor(); + configDriveMotor(); + + driveMotorSelectedPosition = mDriveMotor.getPosition(); + driveMotorSelectedSensorVelocity = mDriveMotor.getVelocity(); + angleMotorSelectedPosition = mAngleMotor.getPosition(); + absolutePositionAngleEncoder = angleEncoder.getAbsolutePosition(); + } + + private void configAngleMotor() { + /* Angle Motor Config */ + /* Motor Inverts and Neutral Mode */ + swerveAngleFXConfig.MotorOutput.Inverted = Constants.Swerve.angleMotorInvert; + swerveAngleFXConfig.MotorOutput.NeutralMode = Constants.Swerve.angleNeutralMode; + + /* Gear Ratio and Wrapping Config */ + swerveAngleFXConfig.Feedback.SensorToMechanismRatio = Constants.Swerve.angleGearRatio; + swerveAngleFXConfig.ClosedLoopGeneral.ContinuousWrap = true; + + /* Current Limiting */ + swerveAngleFXConfig.CurrentLimits.SupplyCurrentLimitEnable = + Constants.Swerve.angleEnableCurrentLimit; + swerveAngleFXConfig.CurrentLimits.SupplyCurrentLimit = Constants.Swerve.angleCurrentLimit; + swerveAngleFXConfig.CurrentLimits.SupplyCurrentThreshold = + Constants.Swerve.angleCurrentThreshold; + swerveAngleFXConfig.CurrentLimits.SupplyTimeThreshold = + Constants.Swerve.angleCurrentThresholdTime; + + /* PID Config */ + swerveAngleFXConfig.Slot0.kP = Constants.Swerve.angleKP; + swerveAngleFXConfig.Slot0.kI = Constants.Swerve.angleKI; + swerveAngleFXConfig.Slot0.kD = Constants.Swerve.angleKD; + + mAngleMotor.getConfigurator().apply(swerveAngleFXConfig); + } + + private void configDriveMotor() { + /* Drive Motor Config */ + /* Motor Inverts and Neutral Mode */ + swerveDriveFXConfig.MotorOutput.Inverted = Constants.Swerve.driveMotorInvert; + swerveDriveFXConfig.MotorOutput.NeutralMode = Constants.Swerve.driveNeutralMode; + + /* Gear Ratio Config */ + swerveDriveFXConfig.Feedback.SensorToMechanismRatio = Constants.Swerve.driveGearRatio; + + /* Current Limiting */ + swerveDriveFXConfig.CurrentLimits.SupplyCurrentLimitEnable = + Constants.Swerve.driveEnableCurrentLimit; + swerveDriveFXConfig.CurrentLimits.SupplyCurrentLimit = Constants.Swerve.driveCurrentLimit; + swerveDriveFXConfig.CurrentLimits.SupplyCurrentThreshold = + Constants.Swerve.driveCurrentThreshold; + swerveDriveFXConfig.CurrentLimits.SupplyTimeThreshold = + Constants.Swerve.driveCurrentThresholdTime; + + /* PID Config */ + swerveDriveFXConfig.Slot0.kP = Constants.Swerve.driveKP; + swerveDriveFXConfig.Slot0.kI = Constants.Swerve.driveKI; + swerveDriveFXConfig.Slot0.kD = Constants.Swerve.driveKD; + swerveDriveFXConfig.Slot0.kS = Constants.Swerve.driveKS; + swerveDriveFXConfig.Slot0.kV = Constants.Swerve.driveKV; + swerveDriveFXConfig.Slot0.kA = Constants.Swerve.driveKA; + + /* Open and Closed Loop Ramping */ + swerveDriveFXConfig.OpenLoopRamps.DutyCycleOpenLoopRampPeriod = + Constants.Swerve.openLoopRamp; + swerveDriveFXConfig.OpenLoopRamps.VoltageOpenLoopRampPeriod = Constants.Swerve.openLoopRamp; + + swerveDriveFXConfig.ClosedLoopRamps.DutyCycleClosedLoopRampPeriod = + Constants.Swerve.closedLoopRamp; + swerveDriveFXConfig.ClosedLoopRamps.VoltageClosedLoopRampPeriod = + Constants.Swerve.closedLoopRamp; + + mDriveMotor.getConfigurator().apply(swerveDriveFXConfig); + mDriveMotor.getConfigurator().setPosition(0.0); + } + + private void configAngleEncoder() { + /* Angle Encoder Config */ + swerveCANcoderConfig.MagnetSensor.SensorDirection = Constants.Swerve.cancoderInvert; + + angleEncoder.getConfigurator().apply(swerveCANcoderConfig); + } + + @Override + public void setAngleMotor(double angle) { + mAngleMotor.setControl(anglePosition.withPosition(angle)); + } + + @Override + public void setDriveMotor(double mps) { + // driveVelocity.FeedForward = feedforward; + double driveRPS = Conversions.metersPerSecondToRotationPerSecond(mps, + Constants.Swerve.wheelCircumference); + driveVelocity.Velocity = driveRPS; + mDriveMotor.setControl(driveVelocity); + } + + @Override + public void updateInputs(SwerveModuleInputs inputs) { + BaseStatusSignal.refreshAll(driveMotorSelectedPosition, driveMotorSelectedSensorVelocity, + angleMotorSelectedPosition, absolutePositionAngleEncoder); + inputs.driveMotorSelectedPosition = driveMotorSelectedPosition.getValue(); + inputs.driveMotorSelectedSensorVelocity = driveMotorSelectedSensorVelocity.getValue(); + inputs.angleMotorSelectedPosition = angleMotorSelectedPosition.getValue(); + inputs.absolutePositionAngleEncoder = absolutePositionAngleEncoder.getValue(); + // inputs.driveMotorTemp = mDriveMotor.getDeviceTemp().getValueAsDouble(); + // inputs.angleMotorTemp = mAngleMotor.getDeviceTemp().getValueAsDouble(); + } + + @Override + public void setPositionAngleMotor(double absolutePosition) { + mAngleMotor.setPosition(absolutePosition); + } + +} diff --git a/src/main/java/frc/lib/util/swerve/SwerveModuleSim.java b/src/main/java/frc/lib/util/swerve/SwerveModuleSim.java new file mode 100644 index 0000000..7cc8166 --- /dev/null +++ b/src/main/java/frc/lib/util/swerve/SwerveModuleSim.java @@ -0,0 +1,92 @@ +package frc.lib.util.swerve; + +import org.littletonrobotics.junction.LoggedRobot; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.simulation.FlywheelSim; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.lib.math.Conversions; +import frc.robot.Constants; + +/** + * Swerve Module Sim + */ +public class SwerveModuleSim implements SwerveModuleIO { + public int moduleNumber; + + private FlywheelSim driveSim = + new FlywheelSim(DCMotor.getFalcon500(1), Constants.Swerve.driveGearRatio, 0.025); + + private double angle; + private double distance; + + private double driveAppliedVolts = 0.0; + private SimpleMotorFeedforward driveFeedforward = new SimpleMotorFeedforward(0.0, 0.13); + private PIDController driveFeedback = new PIDController(0.5, 0.0, 0.0); + + /** + * Swerve Module Sim + */ + public SwerveModuleSim() {} + + @Override + public void setModNumber(int number) { + this.moduleNumber = number; + } + + @Override + public void updateInputs(SwerveModuleInputs inputs) { + driveSim.update(LoggedRobot.defaultPeriodSecs); + double driveSpeed = Units.radiansToRotations(driveSim.getAngularVelocityRadPerSec()); + this.distance += driveSpeed * LoggedRobot.defaultPeriodSecs; + inputs.driveMotorSelectedPosition = this.distance; + inputs.driveMotorSelectedSensorVelocity = driveSpeed; + + inputs.angleMotorSelectedPosition = angle; + + inputs.absolutePositionAngleEncoder = angle; + inputs.odometryTimestamps = new double[] {Timer.getFPGATimestamp()}; + } + + /** + * Set drive motor in Meter per Sec + * + * @param mps Meters per Second + */ + public void setDriveMotor(double mps) { + double rpm = Conversions.metersPerSecondToRotationPerSecond(mps, + Constants.Swerve.wheelCircumference); + driveFeedback.setSetpoint(rpm); + double driveFF = driveFeedforward.calculate(mps); + SmartDashboard.putNumber("ff/" + moduleNumber, driveFF); + double volts = driveFeedback.calculate(mps) + driveFF; + if (rpm == 0) { + volts = 0; + } + SmartDashboard.putNumber("Drive volts/" + moduleNumber, volts); + setDriveVoltage(volts); + } + + /** + * Set Angle for steering motor + * + * @param angle Angle to set + */ + public void setAngleMotor(double angle) { + this.angle = angle; + } + + /** + * Set Drive Voltage + * + * @param volts Voltage + */ + public void setDriveVoltage(double volts) { + driveAppliedVolts = MathUtil.clamp(volts, -12.0, 12.0); + driveSim.setInputVoltage(driveAppliedVolts); + } +} diff --git a/src/main/java/frc/robot/Constants.java b/src/main/java/frc/robot/Constants.java index f9ac7fb..82b77c0 100644 --- a/src/main/java/frc/robot/Constants.java +++ b/src/main/java/frc/robot/Constants.java @@ -7,31 +7,195 @@ import com.pathplanner.lib.util.PIDConstants; import com.pathplanner.lib.util.ReplanningConfig; import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation3d; import edu.wpi.first.math.kinematics.SwerveDriveKinematics; import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.util.Color; +import frc.lib.util.FieldConstants; /** - * Project Constants + * Constants file. */ public final class Constants { - public static final double stickDeadband = 0.1; + /** + * Stick Deadband + */ + public static final double STICK_DEADBAND = 0.1; + + /** + * Driver ID + */ + public static final int DRIVER_ID = 0; + + /** + * Operator ID + */ + public static final int OPERATOR_ID = 1; + + /** + * How far in the future we should "lead" the aiming of the shooter for shooting while moving. + */ + public static final double LEAD_GAIN = 0.3; + + + /** + * MoveToPos constants. + */ + public static class SwerveTransformPID { + public static final double PID_XKP = 3.5; + public static final double PID_XKI = 0.0; + public static final double PID_XKD = 0.0; + public static final double PID_YKP = 3.5; + public static final double PID_YKI = 0.0; + public static final double PID_YKD = 0.0; + public static final double PID_TKP = 3.0; + public static final double PID_TKI = 0.0; + public static final double PID_TKD = 0.0; + + public static final double MAX_ANGULAR_VELOCITY = 9.0; + public static final double MAX_ANGULAR_ACCELERATION = 9 * 5; + public static final double STD_DEV_MOD = 2.0; + } + + /** + * Motor CAN id's. + */ + public static final class Motors { + /** + * Shooter Id's + */ + public static final class Shooter { + public static final int SHOOTER_TOP_ID = 13; + public static final int SHOOTER_BOTTOM_ID = 15; + } + + /** + * Class for elevator and wrist motor constants + */ + public static final class ElevatorWrist { + public static final int ELEVATOR_RIGHT_NEO_ID = 57; + public static final int ELEVATOR_LEFT_NEO_ID = 12; + public static final int WRIST_NEO_ID = 52; + } + + /** + * Intake and indexer motor constants + */ + public static final class Intake { + public static final int INTAKE_MOTOR_ID_LEFT = 14; + public static final int INTAKE_MOTOR_ID_RIGHT = 61; + public static final int INDEXER_MOTOR_ID = 32; + } + + /** + * Climber motor constants + */ + public static final class Climber { + public static final int LEFT_MOTOR_ID = 60; + public static final int RIGHT_MOTOR_ID = 48; + } + } + + /** + * Camera offset constants. + */ + public static class CameraConstants { + + public static double XY_STD_DEV_COEFF = 0.005; + public static double THETA_STD_DEV_COEFF = 0.01; + + /** + * Constants for Front Left Camera + */ + public static class FrontLeftFacingCamera { + public static final double ROLL = 0.0; + public static final double PITCH = Math.toRadians(0); + public static final double YAW = Math.toRadians(12.0); + public static final Transform3d KCAMERA_TO_ROBOT = new Transform3d( + new Translation3d(Units.inchesToMeters(17), Units.inchesToMeters(4.0), + Units.inchesToMeters(15)), + new Rotation3d(ROLL, PITCH, YAW)); + + public static final String CAMERA_NAME = "front-left"; + public static final String CAMERA_IP = "10.55.72.12"; + public static final double LARGEST_DISTANCE = 0.1; + } + + /** + * Constants for Front Right Camera + */ + public static class FrontRightFacingCamera { + public static final double ROLL = Math.toRadians(180); + public static final double PITCH = Math.toRadians(0); + public static final double YAW = Math.toRadians(-6); + public static final Transform3d KCAMERA_TO_ROBOT = new Transform3d( + new Translation3d(Units.inchesToMeters(17), Units.inchesToMeters(-5), + Units.inchesToMeters(15)), + new Rotation3d(ROLL, PITCH, YAW)); + + public static final String CAMERA_NAME = "front-right"; + public static final String CAMERA_IP = "10.55.72.10"; + public static final double LARGEST_DISTANCE = 0.1; + } + + /** + * Constants for Back Left Camera + */ + public static class BackLeftFacingCamera { + public static final double ROLL = 0.0; + public static final double PITCH = Math.toRadians(0); + public static final double YAW = Math.toRadians(184); + public static final Transform3d KCAMERA_TO_ROBOT = new Transform3d( + new Translation3d(Units.inchesToMeters(-13.0), Units.inchesToMeters(14), + Units.inchesToMeters(0)), + new Rotation3d(ROLL, PITCH, YAW)); + + public static final String CAMERA_NAME = "back-left"; + public static final String CAMERA_IP = "10.55.72.13"; + public static final double LARGEST_DISTANCE = 0.1; + } + + // /** + // * Constants for Back Right Camera + // */ + // public static class BackRightFacingCamera { + // public static final double ROLL = 0.0; + // public static final double PITCH = Math.toRadians(0); + // public static final double YAW = Math.toRadians(180); + // public static final Transform3d KCAMERA_TO_ROBOT = + // new Transform3d(new Translation3d(Units.inchesToMeters(12.831), + // Units.inchesToMeters(-8.56), Units.inchesToMeters(17.85)), + // new Rotation3d(ROLL, PITCH, YAW)).inverse(); + + // public static final String CAMERA_NAME = "back-right"; + // // public static final String CAMERA_IP = "10.55.72.10"; + // public static final double LARGEST_DISTANCE = 0.1; + // } + + } /** * Swerve Constants */ public static final class Swerve { + public static final double AUTO_ROTATION_KP = 5.0; + public static final double AUTO_ROTATION_KI = 0.0; + public static final double AUTO_ROTATION_KD = 0.0; + public static final edu.wpi.first.wpilibj.SPI.Port navXID = edu.wpi.first.wpilibj.SPI.Port.kMXP; - public static final boolean invertGyro = false; + public static final boolean invertGyro = true; public static final boolean isFieldRelative = true; public static final boolean isOpenLoop = false; /* Drivetrain Constants */ - public static final double trackWidth = Units.inchesToMeters(22.5); - public static final double wheelBase = Units.inchesToMeters(29.0); - public static final double wheelDiameter = Units.inchesToMeters(4.0); + public static final double trackWidth = Units.inchesToMeters(23.75); // TODO + public static final double wheelBase = Units.inchesToMeters(17.75); // TODO + public static final double wheelDiameter = Units.inchesToMeters(3.8); // TODO public static final double wheelCircumference = wheelDiameter * Math.PI; public static final Translation2d MOD0_MODOFFSET = new Translation2d(wheelBase / 2.0, trackWidth / 2.0); @@ -72,7 +236,7 @@ public static final class Swerve { /* * These values are used by the drive falcon to ramp in open loop and closed loop driving. - * We found a small open loop ramp (0.25) helps with tread wear, tipping, etc + * We found a small open loop ramp (0.25) helps with wear, tipping, etc */ public static final double openLoopRamp = 0.25; public static final double closedLoopRamp = 0.0; @@ -95,64 +259,94 @@ public static final class Swerve { /* Swerve Profiling Values */ /** Meters per Second */ - public static final double maxSpeed = 4.5; + public static final double maxSpeed = 10.0; + public static final double AUTO_MAX_SPEED = 3.0; /** Radians per Second */ - public static final double maxAngularVelocity = 10.0; + public static final double maxAngularVelocity = 15.0; /* Neutral Modes */ public static final NeutralModeValue angleNeutralMode = NeutralModeValue.Coast; public static final NeutralModeValue driveNeutralMode = NeutralModeValue.Brake; /* Module Specific Constants */ + /** * Front Left Module - Module 0 */ public static final class Mod0 { - public static final int driveMotorID = 10; - public static final int angleMotorID = 8; - public static final int canCoderID = 10; - public static final Rotation2d angleOffset = Rotation2d.fromDegrees(115.400390625); + public static final int driveMotorID = 6; + public static final int angleMotorID = 51; + public static final int canCoderID = 4; + // public static final Rotation2d angleOffset = Rotation2d.fromDegrees(183.955078125); + public static final Rotation2d angleOffset = Rotation2d.fromRotations(-0.496826); + } /** * Front Right Module - Module 1 */ public static final class Mod1 { - public static final int driveMotorID = 3; - public static final int angleMotorID = 9; - public static final int canCoderID = 1; - public static final Rotation2d angleOffset = Rotation2d.fromDegrees(-55.37109375); + public static final int driveMotorID = 2; + public static final int angleMotorID = 40; + public static final int canCoderID = 2; + // public static final Rotation2d angleOffset = Rotation2d.fromDegrees(325.01953125); + public static final Rotation2d angleOffset = Rotation2d.fromRotations(0.405518 + 0.5); + } /** * Back Left Module - Module 2 */ public static final class Mod2 { - public static final int driveMotorID = 2; - public static final int angleMotorID = 40; - public static final int canCoderID = 2; - public static final Rotation2d angleOffset = Rotation2d.fromDegrees(145.01953125); + public static final int driveMotorID = 3; + public static final int angleMotorID = 9; + public static final int canCoderID = 1; + // public static final Rotation2d angleOffset = Rotation2d.fromDegrees(124.62890625); + public static final Rotation2d angleOffset = Rotation2d.fromRotations(0.348145); + } /** * Back Right Module - Module 3 */ public static final class Mod3 { - public static final int driveMotorID = 6; - public static final int angleMotorID = 51; - public static final int canCoderID = 4; - public static final Rotation2d angleOffset = Rotation2d.fromDegrees(3.955078125); + public static final int driveMotorID = 10; + public static final int angleMotorID = 8; + public static final int canCoderID = 10; + // public static final Rotation2d angleOffset = Rotation2d.fromDegrees(295.400390625); + public static final Rotation2d angleOffset = Rotation2d.fromRotations(0.317627 + 0.5); } public static final HolonomicPathFollowerConfig pathFollowerConfig = - new HolonomicPathFollowerConfig(new PIDConstants(5.0, 0, 0), // Translation constants - new PIDConstants(5.0, 0, 0), // Rotation constants + new HolonomicPathFollowerConfig(new PIDConstants(5.0, 0, 0), + new PIDConstants(AUTO_ROTATION_KP, AUTO_ROTATION_KI, AUTO_ROTATION_KD), // Drive base radius (distance from center to furthest module) - maxSpeed, MOD0_MODOFFSET.getNorm(), new ReplanningConfig()); + AUTO_MAX_SPEED, MOD0_MODOFFSET.getNorm(), new ReplanningConfig()); + } + + /** + * Climber constants + */ + public static final class ClimberConstants { + public static final double CLIMBER_KP = 0.1; + public static final double CLIMBER_KI = 0.1; + public static final double CLIMBER_KD = 0.1; + public static final double CLIMBER_MAX_VELOCITY = 0; + public static final double CLIMBER_MAX_ACCELERATION = 0; + public static final double CLIMBER_KS = 0.1; + public static final double CLIMBER_KG = 0.1; + public static final double CLIMBER_KV = 0.1; + public static final double CLIMBER_POWER = 0.8; + + public static final double CLIMBING_DISTANCE = Units.inchesToMeters(15); + public static final double MAX_CLIMBING_DISTANCE = Units.inchesToMeters(21); + + // 2pi * radius + public static final double LINEAR_DISTANCE = Units.inchesToMeters(2 * Math.PI * 1); } /** - * Auto Constants + * Auto constants */ public static final class AutoConstants { public static final double kMaxSpeedMetersPerSecond = 3; @@ -169,4 +363,160 @@ public static final class AutoConstants { new TrapezoidProfile.Constraints(kMaxAngularSpeedRadiansPerSecond, kMaxAngularSpeedRadiansPerSecondSquared); } + + /** + * Class for elevator and wrist constants + */ + public static final class ElevatorWristConstants { + + public static final Rotation2d WRIST_REF_1_ANGLE_MEASURED = Rotation2d.fromRotations(0.454); + public static final Rotation2d WRIST_REF_2_ANGLE_MEASURED = Rotation2d.fromRotations(0.585); + public static final Rotation2d WRIST_REF_1_ANGLE_ACTUAL = Rotation2d.fromDegrees(0.0); + public static final Rotation2d WRIST_REF_2_ANGLE_ACTUAL = Rotation2d.fromDegrees(45.0); + + public static final double WRIST_M; + public static final double WRIST_B; + + static { + WRIST_M = + (WRIST_REF_2_ANGLE_ACTUAL.getRotations() - WRIST_REF_1_ANGLE_ACTUAL.getRotations()) + / (WRIST_REF_2_ANGLE_MEASURED.getRotations() + - WRIST_REF_1_ANGLE_MEASURED.getRotations()); + // meas_1 * m + b = act_1 + // b = act_1 - meas_1 * m + WRIST_B = WRIST_REF_1_ANGLE_ACTUAL.getRotations() + - WRIST_REF_1_ANGLE_MEASURED.getRotations() * WRIST_M; + } + + public static final Rotation2d ELEVATOR_REF_1_ANGLE_MEASURED = + Rotation2d.fromRotations(0.0); + public static final Rotation2d ELEVATOR_REF_2_ANGLE_MEASURED = + Rotation2d.fromRotations(-10030.96); + public static final double ELEVATOR_REF_1_HEIGHT = 24.0; + public static final double ELEVATOR_REF_2_HEIGHT = 44.75; + + public static final double ELEVATOR_M; + public static final double ELEVATOR_B; + + static { + ELEVATOR_M = (ELEVATOR_REF_2_HEIGHT - ELEVATOR_REF_1_HEIGHT) + / (ELEVATOR_REF_2_ANGLE_MEASURED.getRotations() + - ELEVATOR_REF_1_ANGLE_MEASURED.getRotations()); + // meas_1 * m + b = act_1 + // b = act_1 - meas_1 * m + ELEVATOR_B = + ELEVATOR_REF_1_HEIGHT - ELEVATOR_REF_1_ANGLE_MEASURED.getRotations() * ELEVATOR_M; + } + + /** + * PID constants + */ + public static final class PID { + + public static final double ELEVATOR_KP = 10.0; + public static final double ELEVATOR_KI = 0; + public static final double ELEVATOR_KD = 0; + public static final double ELEVATOR_MAX_VELOCITY = 320; + public static final double ELEVATOR_MAX_ACCELERATION = 200; + public static final double ELEVATOR_KS = 0; + public static final double ELEVATOR_KG = 0; + public static final double ELEVATOR_KV = 0; + + public static final double WRIST_KP = 110; + public static final double WRIST_AMP_KP = 150; + public static final double WRIST_LARGE_KP = 60; + public static final double WRIST_KI = 0.1; + public static final double WRIST_AMP_KI = 0; + public static final double WRIST_KD = 0.15; + public static final double WRIST_AMP_KD = 0; + public static final double WRIST_MAX_VELOCITY = 0.000001; + public static final double WRIST_MAX_ACCELERATION = 0.0000000001; + public static final double WRIST_KS = 0; + public static final double WRIST_KG = 0; + public static final double WRIST_KV = 0; + public static final double WRIST_LOWPASS = 1; + } + + /** + * Set points constants for elevator and wrist + */ + public static final class SetPoints { + + public static final double HOME_HEIGHT = 24; + public static final Rotation2d INTAKE_ANGLE = Rotation2d.fromDegrees(39); + public static final Rotation2d HOME_ANGLE = Rotation2d.fromDegrees(5); + // public static final double AMP_HEIGHT = Units.inchesToMeters(34); + public static final double AMP_HEIGHT = 43.5; + public static final Rotation2d AMP_ANGLE = Rotation2d.fromDegrees(-29.95); + // public static final double TRAP_HEIGHT = Units.inchesToMeters(40); + public static final double TRAP_HEIGHT = 44; + public static final Rotation2d TRAP_ANGLE = Rotation2d.fromDegrees(-10); + // public static final double MAX_EXTENSION = Units.inchesToMeters(48); + public static final double MAX_EXTENSION = 43.5; + public static final double CLIMBING_HEIGHT = 40.0; + public static final Rotation2d CLIMBING_ANGLE = Rotation2d.fromDegrees(-40); + public static final Rotation2d MIN_ANGLE = Rotation2d.fromDegrees(-44); + public static final Rotation2d MAX_ANGLE = Rotation2d.fromDegrees(50); + + public static final double LINEAR_DISTANCE = Units.inchesToMeters(2 * Math.PI * 659); + + public static final Rotation2d PODIUM_ANGLE = Rotation2d.fromDegrees(33.0); + + } + + + } + /** + * Pneumatics CAN id constants. + */ + + public static final class Pneumatics { + } + + + + /** + * Constants of Shooters + */ + public static final class ShooterConstants { + public static final double KP = 0.0014; + public static final double KI = 0; + public static final double KD = 0; + public static final double KS = 0; + public static final double TOP_KV = 6.18e-4; + public static final double BOTTOM_KV = 6.18e-4; + public static final double GEAR_RATIO = 3; + public static final double HEIGHT_FROM_LOWEST_POS = Units.inchesToMeters(32.0); + public static final double HEIGHT_FROM_SPEAKER = + FieldConstants.centerSpeaker - HEIGHT_FROM_LOWEST_POS; + public static final double SHOOT_SPEAKER_RPM = 11250.0; + } + + /** + * Constants for intake + */ + public static final class IntakeConstants { + public static final double INTAKE_MOTOR_FORWARD = 0; + public static final double INTAKE_MOTOR_BACKWARD = -0; + public static final double INTAKE_MOTOR_STOP = 0; + public static final double INDEX_MOTOR_FORWARD = .2; + public static final double INDEX_MOTOR_BACKWARD = -0; + public static final double INDEX_MOTOR_STOP = 0; + public static final boolean INTAKE_MOTOR_INVERTED = true; + + public static final int INDEXER_BEAM_BRAKE_DIO_PORT = 8; + public static final int INTAKE_BEAM_BRAKE_DIO_PORT = 6; + } + + /** + * LED constants. + */ + public static final class LEDConstants { + public static final int PWM_PORT = 9; + public static final int LED_COUNT = 60; + public static final Color INTAKE_COLOR = Color.kGreen; + public static final Color INDEXER_COLOR = Color.kPurple; + public static final Color ALERT_COLOR = Color.kWhite; + } + } diff --git a/src/main/java/frc/robot/Main.java b/src/main/java/frc/robot/Main.java index 04437ec..66a8c84 100644 --- a/src/main/java/frc/robot/Main.java +++ b/src/main/java/frc/robot/Main.java @@ -19,6 +19,8 @@ private Main() {} * *

* If you change your main robot class, change the parameter type. + * + * @param args String args */ public static void main(String... args) { RobotBase.startRobot(Robot::new); diff --git a/src/main/java/frc/robot/Robot.java b/src/main/java/frc/robot/Robot.java index 9f71594..142cf3d 100644 --- a/src/main/java/frc/robot/Robot.java +++ b/src/main/java/frc/robot/Robot.java @@ -4,32 +4,118 @@ package frc.robot; -import edu.wpi.first.wpilibj.TimedRobot; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Scanner; +import org.littletonrobotics.junction.LogFileUtil; +import org.littletonrobotics.junction.LoggedRobot; +import org.littletonrobotics.junction.Logger; +import org.littletonrobotics.junction.networktables.NT4Publisher; +import org.littletonrobotics.junction.wpilog.WPILOGReader; +import org.littletonrobotics.junction.wpilog.WPILOGWriter; +import edu.wpi.first.wpilibj.Filesystem; +import edu.wpi.first.wpilibj.PowerDistribution; +import edu.wpi.first.wpilibj.PowerDistribution.ModuleType; +import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.lib.profiling.EmptyProfiler; +import frc.lib.profiling.LoggingProfiler; +import frc.lib.profiling.Profiler; /** - * The VM is configured to automatically run this class, and to call the functions corresponding to - * each mode, as described in the TimedRobot documentation. If you change the name of this class or - * the package after creating this project, you must also update the build.gradle file in the - * project. + * Runs tasks on Roborio in this file. */ -public class Robot extends TimedRobot { +public class Robot extends LoggedRobot { + private RobotContainer robotContainer; + private Command autoChooser; - private Command m_autonomousCommand; + public static Profiler profiler; - private RobotContainer m_robotContainer; - - // private Ultrasonic ultrasonic = new Ultrasonic(); /** - * This function is run when the robot is first started up and should be used for any - * initialization code. + * Robot Run type */ - @Override - public void robotInit() { - // Instantiate our RobotContainer. This will perform all our button bindings, and put our - // autonomous chooser on the dashboard. - m_robotContainer = new RobotContainer(); + public static enum RobotRunType { + /** Real Robot. */ + kReal, + /** Simulation runtime. */ + kSimulation, + /** Replay runtime. */ + kReplay; + } + + public static boolean inAuto = false; + public RobotRunType robotRunType = RobotRunType.kReal; + private Timer gcTimer = new Timer(); + private Timer profileTimer = new Timer(); + // We don't want to write empty profiles, so we have a boolean that only becomes true once + // teleop or auto has started. + private boolean hasDoneSomething = false; + private boolean hasStarted = false; + + /** Set up logging, profiling, and robotContainer. */ + @SuppressWarnings("resource") + public Robot() { + // Record metadata + Logger.recordMetadata("ProjectName", BuildConstants.MAVEN_NAME); + Logger.recordMetadata("BuildDate", BuildConstants.BUILD_DATE); + Logger.recordMetadata("GitSHA", BuildConstants.GIT_SHA); + Logger.recordMetadata("GitDate", BuildConstants.GIT_DATE); + Logger.recordMetadata("GitBranch", BuildConstants.GIT_BRANCH); + switch (BuildConstants.DIRTY) { + case 0: + Logger.recordMetadata("GitDirty", "All changes committed"); + break; + case 1: + Logger.recordMetadata("GitDirty", "Uncommitted changes"); + break; + default: + Logger.recordMetadata("GitDirty", "Unknown"); + break; + } + + + + if (isReal()) { + 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 + setUseTiming(true); + robotRunType = RobotRunType.kReal; + } else { + String logPath = findReplayLog(); + if (logPath == null) { + Logger.addDataReceiver( + new WPILOGWriter(Filesystem.getOperatingDirectory().getAbsolutePath())); + Logger.addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables + setUseTiming(true); + robotRunType = RobotRunType.kSimulation; + } else { + Logger.setReplaySource(new WPILOGReader(logPath)); // Read replay log + Logger + .addDataReceiver(new WPILOGWriter(LogFileUtil.addPathSuffix(logPath, "_sim"))); + // Save outputs to a new log + setUseTiming(false); // Run as fast as possible + robotRunType = RobotRunType.kReplay; + } + } + Logger.start(); // Start logging! No more data receivers, replay sources, or metadata values + switch (robotRunType) { + case kReal -> profiler = + new LoggingProfiler(() -> Logger.getRealTimestamp(), 1000000.0); + case kReplay -> profiler = EmptyProfiler.INSTANCE; + case kSimulation -> profiler = + new LoggingProfiler(() -> Logger.getRealTimestamp(), 1000000.0); + default -> { + } + } + // Logger.disableDeterministicTimestamps() // See "Deterministic Timestamps" in the + // "Understanding Data Flow" page + + // Instantiate our RobotContainer. This will perform all our button bindings, + // and put our autonomous chooser on the dashboard. + robotContainer = new RobotContainer(robotRunType); } /** @@ -40,16 +126,40 @@ public void robotInit() { * This runs after the mode specific periodic functions, but before LiveWindow and * SmartDashboard integrated updating. */ + @Override public void robotPeriodic() { + if (hasStarted) { + profiler.endTick(); + if (profileTimer.advanceIfElapsed(1)) { + if (hasDoneSomething) { + profiler.save(); + profiler.reset(); + } + } + } else { + hasStarted = true; + } + profiler.startTick(); + profiler.push("robotPeriodic()"); + profiler.push("draw_state_to_shuffleboard"); + // Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled - // commands, running already-scheduled commands, removing finished or interrupted commands, - // and running subsystem periodic() methods. This must be called from the robot's periodic - // block in order for anything in the Command-based framework to work. + // commands, + // running already-scheduled commands, removing finished or interrupted commands, and + // running + // subsystem periodic() methods. This must be called from the robot's periodic block in + // order for + // anything in the Command-based framework to work. + profiler.swap("command_scheduler"); CommandScheduler.getInstance().run(); + profiler.swap("manual-gc"); + if (gcTimer.advanceIfElapsed(5)) { + System.gc(); + } + profiler.pop(); } - /** This function is called once each time the robot enters Disabled mode. */ @Override public void disabledInit() {} @@ -61,12 +171,18 @@ public void disabledPeriodic() {} */ @Override public void autonomousInit() { - m_autonomousCommand = m_robotContainer.getAutonomousCommand(); + hasDoneSomething = true; + profiler.push("autonomousInit()"); + inAuto = true; + + robotContainer.getAutonomousCommand().schedule(); + autoChooser = robotContainer.getAutonomousCommand(); // schedule the autonomous command (example) - if (m_autonomousCommand != null) { - m_autonomousCommand.schedule(); + if (autoChooser != null) { + autoChooser.schedule(); } + profiler.pop(); } /** This function is called periodically during autonomous. */ @@ -75,18 +191,19 @@ public void autonomousPeriodic() {} @Override public void teleopInit() { - // This makes sure that the autonomous stops running when - // teleop starts running. If you want the autonomous to - // continue until interrupted by another command, remove - // this line or comment it out. - if (m_autonomousCommand != null) { - m_autonomousCommand.cancel(); + hasDoneSomething = true; + profiler.push("teleopInit()"); + inAuto = false; + if (autoChooser != null) { + autoChooser.cancel(); } + profiler.pop(); } /** This function is called periodically during operator control. */ @Override public void teleopPeriodic() { + // robotContainer.elevatorWrist.setWristPower(robotContainer.driver.getRightY() * 0.2); // vision.update(); } @@ -99,4 +216,41 @@ public void testInit() { /** This function is called periodically during test mode. */ @Override public void testPeriodic() {} + + @Override + public void simulationPeriodic() {} + + + private static final String environmentVariable = "AKIT_LOG_PATH"; + private static final String advantageScopeFileName = "akit-log-path.txt"; + + /** + * Finds the path to a log file for replay, using the following priorities: 1. The value of the + * "AKIT_LOG_PATH" environment variable, if set 2. The file currently open in AdvantageScope, if + * available 3. The result of the prompt displayed to the user + */ + public static String findReplayLog() { + // Read environment variables + String envPath = System.getenv(environmentVariable); + if (envPath != null) { + System.out.println("Using log from " + environmentVariable + + " environment variable - \"" + envPath + "\""); + return envPath; + } + + // Read file from AdvantageScope + Path advantageScopeTempPath = + Paths.get(System.getProperty("java.io.tmpdir"), advantageScopeFileName); + String advantageScopeLogPath = null; + try (Scanner fileScanner = new Scanner(advantageScopeTempPath)) { + advantageScopeLogPath = fileScanner.nextLine(); + } catch (IOException e) { + System.out.println("Something went wrong"); + } + if (advantageScopeLogPath != null) { + System.out.println("Using log from AdvantageScope - \"" + advantageScopeLogPath + "\""); + return advantageScopeLogPath; + } + return null; + } } diff --git a/src/main/java/frc/robot/RobotContainer.java b/src/main/java/frc/robot/RobotContainer.java index 50617d4..75fcf89 100644 --- a/src/main/java/frc/robot/RobotContainer.java +++ b/src/main/java/frc/robot/RobotContainer.java @@ -1,23 +1,18 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - package frc.robot; -import java.util.List; -import com.pathplanner.lib.commands.PathPlannerAuto; -import com.pathplanner.lib.path.PathPlannerPath; -import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.InstantCommand; -import edu.wpi.first.wpilibj2.command.WaitCommand; import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Robot.RobotRunType; import frc.robot.commands.TeleopSwerve; -import frc.robot.subsystems.Swerve; +import frc.robot.subsystems.swerve.Swerve; +import frc.robot.subsystems.swerve.SwerveIO; +import frc.robot.subsystems.swerve.SwerveReal; +import frc.robot.subsystems.swerve.SwerveSim; + /** * This class is where the bulk of the robot should be declared. Since Command-based is a @@ -26,60 +21,60 @@ * subsystems, commands, and button mappings) should be declared here. */ public class RobotContainer { - /* Controllers */ - private final CommandXboxController driver = new CommandXboxController(0); - private final SendableChooser autoChooser = new SendableChooser<>(); + /* Shuffleboard */ + private final SendableChooser autoChooser = new SendableChooser<>(); + + /* Controllers */ + public final CommandXboxController driver = new CommandXboxController(Constants.DRIVER_ID); /* Subsystems */ - private final Swerve s_Swerve = new Swerve(); + private Swerve s_Swerve; + + /** + */ + public RobotContainer(RobotRunType runtimeType) { + switch (runtimeType) { + case kReal: + s_Swerve = new Swerve(new SwerveReal()); + break; + case kSimulation: + s_Swerve = new Swerve(new SwerveSim()); + break; + default: + s_Swerve = new Swerve(new SwerveIO() {}); + } + + // autoChooser.addOption("P32", new P32(s_Swerve, elevatorWrist, intake, shooter)); + // autoChooser.addOption("P675", new P675(s_Swerve, elevatorWrist, intake, shooter)); + // autoChooser.addOption("P3675", new P3675(s_Swerve, elevatorWrist, intake, shooter)); - /** The container for the robot. Contains subsystems, OI devices, and commands. */ - public RobotContainer() { s_Swerve.setDefaultCommand(new TeleopSwerve(s_Swerve, driver, Constants.Swerve.isFieldRelative, Constants.Swerve.isOpenLoop)); - // autoChooser.addOption(resnickAuto, new ResnickAuto(s_Swerve)); - SmartDashboard.putData("Choose Auto: ", autoChooser); // Configure the button bindings configureButtonBindings(); } - public void periodic() { - - } - /** - * Use this method to define your button->command mappings. Buttons can be created by - * instantiating a {@link GenericHID} or one of its subclasses - * ({@link edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a + * Use this method to vol your button->command mappings. Buttons can be created by instantiating + * a {@link GenericHID} or one of its subclasses ({@link edu.wpi.first.wpilibj.Joystick} or + * {@link XboxController}), and then passing it to a * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. */ private void configureButtonBindings() { + + /* Driver Buttons */ - driver.y().whileTrue(new InstantCommand(() -> s_Swerve.resetFieldRelativeOffset())); + driver.y().onTrue(new InstantCommand(() -> s_Swerve.resetFieldRelativeOffset())); } /** - * Use this to pass the autonomous command to the main {@link Robot} class. + * Gets the user's selected autonomous command. * - * @return the command to run in autonomous + * @return Returns autonomous command selected. */ - public Command getAutonomousCommand() { - String stuff = autoChooser.getSelected(); - Command autocommand; - switch (stuff) { - case "Test Auto": - List paths = PathPlannerAuto.getPathGroupFromAutoFile("New Auto"); - Pose2d initialState = paths.get(0).getPreviewStartingHolonomicPose(); - s_Swerve.resetOdometry(initialState); - autocommand = new InstantCommand(() -> s_Swerve.resetOdometry(initialState)) - .andThen(new PathPlannerAuto("New Auto")); - - break; - default: - autocommand = new WaitCommand(1.0); - } + Command autocommand = autoChooser.getSelected(); return autocommand; } } diff --git a/src/main/java/frc/robot/commands/CommandFactory.java b/src/main/java/frc/robot/commands/CommandFactory.java new file mode 100644 index 0000000..59783ee --- /dev/null +++ b/src/main/java/frc/robot/commands/CommandFactory.java @@ -0,0 +1,8 @@ +package frc.robot.commands; + +/** + * File to create commands using factories + */ +public class CommandFactory { + +} diff --git a/src/main/java/frc/robot/commands/MoveToPos.java b/src/main/java/frc/robot/commands/MoveToPos.java new file mode 100644 index 0000000..4fc5d92 --- /dev/null +++ b/src/main/java/frc/robot/commands/MoveToPos.java @@ -0,0 +1,122 @@ +package frc.robot.commands; + +import java.util.function.Supplier; +import edu.wpi.first.math.controller.HolonomicDriveController; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.DriverStation.Alliance; +import edu.wpi.first.wpilibj2.command.Command; +import frc.lib.util.FieldConstants; +import frc.robot.Constants; +import frc.robot.subsystems.swerve.Swerve; + +/** + * Move to Position + */ +public class MoveToPos extends Command { + + public Swerve swerve; + public Pose2d pose2d; + public Supplier pose2dSupplier; + private boolean flipForRed = true; + + HolonomicDriveController holonomicDriveController = new HolonomicDriveController( + new PIDController(Constants.SwerveTransformPID.PID_XKP, + Constants.SwerveTransformPID.PID_XKI, Constants.SwerveTransformPID.PID_XKD), + new PIDController(Constants.SwerveTransformPID.PID_YKP, + Constants.SwerveTransformPID.PID_YKI, Constants.SwerveTransformPID.PID_YKD), + new ProfiledPIDController(Constants.SwerveTransformPID.PID_TKP, + Constants.SwerveTransformPID.PID_TKI, Constants.SwerveTransformPID.PID_TKD, + new TrapezoidProfile.Constraints(Constants.SwerveTransformPID.MAX_ANGULAR_VELOCITY, + Constants.SwerveTransformPID.MAX_ANGULAR_ACCELERATION))); + + /** + * Move to Position + * + * @param swerve Swerve Drive Subsystem + * @param pose2dSupplier Supplier of Pose2d + * @param flipForRed Flip the Pose2d relative to the Red Alliance + */ + public MoveToPos(Swerve swerve, Supplier pose2dSupplier, boolean flipForRed, + double tol) { + this(swerve, pose2dSupplier, flipForRed, tol, Constants.SwerveTransformPID.PID_TKP); + } + + /** + * Move to Position + * + * @param swerve Swerve Drive Subsystem + * @param pose2dSupplier Supplier of Pose2d + * @param flipForRed Flip the Pose2d relative to the Red Alliance + */ + public MoveToPos(Swerve swerve, Supplier pose2dSupplier, boolean flipForRed, double tol, + double turnP) { + this.swerve = swerve; + this.pose2dSupplier = pose2dSupplier; + this.flipForRed = flipForRed; + this.addRequirements(swerve); + holonomicDriveController.getThetaController().setP(turnP); + holonomicDriveController.setTolerance(new Pose2d(tol, tol, Rotation2d.fromDegrees(1))); + } + + /** + * Move to Position + * + * @param swerve Swerve Drive Subsystem + * @param pose2dSupplier Supplier of Pose2d + * @param flipForRed Flip the Pose2d relative to the Red Alliance + */ + public MoveToPos(Swerve swerve, Supplier pose2dSupplier, boolean flipForRed) { + this(swerve, pose2dSupplier, flipForRed, 0.05); + } + + + /** + * Move to Position + * + * @param swerve Swerve Drive Subsystem + * @param pose2dSupplier Supplier of Pose2d + */ + public MoveToPos(Swerve swerve, Supplier pose2dSupplier) { + this(swerve, pose2dSupplier, true); + } + + /** + * Move to Position + * + * @param swerve Swerve Drive Subsystem + */ + public MoveToPos(Swerve swerve) { + this(swerve, () -> new Pose2d()); + } + + @Override + public void initialize() { + pose2d = pose2dSupplier.get(); + if (flipForRed && DriverStation.getAlliance().get() == Alliance.Red) { + pose2d = FieldConstants.allianceFlip(pose2d); + } + } + + @Override + public void execute() { + ChassisSpeeds ctrlEffort = + holonomicDriveController.calculate(swerve.getPose(), pose2d, 0, pose2d.getRotation()); + swerve.setModuleStates(ctrlEffort); + } + + @Override + public void end(boolean interrupted) { + swerve.setMotorsZero(); + } + + @Override + public boolean isFinished() { + return holonomicDriveController.atReference(); + } +} diff --git a/src/main/java/frc/robot/commands/TeleopSwerve.java b/src/main/java/frc/robot/commands/TeleopSwerve.java index 4310916..dad2a80 100644 --- a/src/main/java/frc/robot/commands/TeleopSwerve.java +++ b/src/main/java/frc/robot/commands/TeleopSwerve.java @@ -4,7 +4,8 @@ import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.button.CommandXboxController; import frc.robot.Constants; -import frc.robot.subsystems.Swerve; +import frc.robot.Robot; +import frc.robot.subsystems.swerve.Swerve; /** * Creates an command for driving the swerve drive during tele-op @@ -15,9 +16,10 @@ public class TeleopSwerve extends Command { private boolean openLoop; private Swerve swerveDrive; private CommandXboxController controller; + private double speedMultiplier = 1; /** - * Creates an command for driving the swerve drive during tele-op + * Creates a command for driving the swerve drive during tele-op * * @param swerveDrive The instance of the swerve drive subsystem * @param fieldRelative Whether the movement is relative to the field or absolute @@ -32,21 +34,41 @@ public TeleopSwerve(Swerve swerveDrive, CommandXboxController controller, boolea this.controller = controller; } + /** + * Creates a command for driving the swerve drive during tele-op + * + * @param swerveDrive The instance of the swerve drive subsystem + * @param fieldRelative Whether the movement is relative to the field or absolute + * @param openLoop Open or closed loop system + * @param speedMultiplier Speed multiplier to increase or decrease speed + */ + public TeleopSwerve(Swerve swerveDrive, CommandXboxController controller, boolean fieldRelative, + boolean openLoop, double speedMultiplier) { + this(swerveDrive, controller, fieldRelative, openLoop); + this.speedMultiplier = speedMultiplier; + } + @Override public void execute() { - double yaxis = -controller.getLeftY(); - double xaxis = -controller.getLeftX(); - double raxis = -controller.getRightX(); + Robot.profiler.push("teleop_swerve"); + double yaxis = -controller.getLeftY() * speedMultiplier; + double xaxis = -controller.getLeftX() * speedMultiplier; + double raxis = -controller.getRightX() * speedMultiplier; /* Deadbands */ - yaxis = (Math.abs(yaxis) < Constants.stickDeadband) ? 0 : yaxis; - xaxis = (Math.abs(xaxis) < Constants.stickDeadband) ? 0 : xaxis; - raxis = (Math.abs(raxis) < Constants.stickDeadband) ? 0 : raxis; + yaxis = (Math.abs(yaxis) < Constants.STICK_DEADBAND) ? 0 + : (yaxis - Constants.STICK_DEADBAND) / (1.0 - Constants.STICK_DEADBAND); + xaxis = (Math.abs(xaxis) < Constants.STICK_DEADBAND) ? 0 + : (xaxis - Constants.STICK_DEADBAND) / (1.0 - Constants.STICK_DEADBAND); + xaxis *= xaxis * Math.signum(xaxis); + yaxis *= yaxis * Math.signum(yaxis); + raxis = (Math.abs(raxis) < Constants.STICK_DEADBAND) ? 0 : raxis; // System.out.println(swerveDrive.getStringYaw()); Translation2d translation = new Translation2d(yaxis, xaxis).times(Constants.Swerve.maxSpeed); double rotation = raxis * Constants.Swerve.maxAngularVelocity; swerveDrive.drive(translation, rotation, fieldRelative, openLoop); + Robot.profiler.pop(); } } diff --git a/src/main/java/frc/robot/commands/TurnToAngle.java b/src/main/java/frc/robot/commands/TurnToAngle.java new file mode 100644 index 0000000..db2e469 --- /dev/null +++ b/src/main/java/frc/robot/commands/TurnToAngle.java @@ -0,0 +1,70 @@ +package frc.robot.commands; + +import edu.wpi.first.math.controller.HolonomicDriveController; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.Constants; +import frc.robot.subsystems.swerve.Swerve; + +/** + * This command will turn the robot to a specified angle. + */ +public class TurnToAngle extends Command { + + private Swerve swerve; + private HolonomicDriveController holonomicDriveController = + new HolonomicDriveController(new PIDController(0, 0, 0), new PIDController(0, 0, 0), + new ProfiledPIDController(Constants.SwerveTransformPID.PID_TKP / 2, + Constants.SwerveTransformPID.PID_TKI, Constants.SwerveTransformPID.PID_TKD, + new TrapezoidProfile.Constraints(Constants.SwerveTransformPID.MAX_ANGULAR_VELOCITY, + Constants.SwerveTransformPID.MAX_ANGULAR_ACCELERATION))); + private Rotation2d targetRotation2d; + private Pose2d position; + private Rotation2d offset = new Rotation2d(); + + /** + * Turns robot to specified angle. Uses absolute rotation on field. + * + * @param swerve Swerve subsystem + * @param angle Requested angle to turn to + */ + public TurnToAngle(Swerve swerve, Rotation2d angle) { + addRequirements(swerve); + this.swerve = swerve; + this.offset = angle; + holonomicDriveController.setTolerance(new Pose2d(1, 1, Rotation2d.fromDegrees(1))); + } + + @Override + public void initialize() { + this.targetRotation2d = offset.plus(Rotation2d.fromDegrees(180)); + if (DriverStation.getAlliance().isPresent() + && DriverStation.getAlliance().get() == DriverStation.Alliance.Blue) { + this.targetRotation2d = Rotation2d.fromDegrees(180).minus(offset); + } + } + + @Override + public void execute() { + position = new Pose2d(0, 0, swerve.getFieldRelativeHeading()); + ChassisSpeeds chassisSpeeds = this.holonomicDriveController.calculate(position, + new Pose2d(0, 0, targetRotation2d), 0, targetRotation2d); + swerve.setModuleStates(chassisSpeeds); + } + + @Override + public void end(boolean interrupt) { + swerve.setMotorsZero(); + } + + @Override + public boolean isFinished() { + return holonomicDriveController.atReference(); + } +} diff --git a/src/main/java/frc/robot/commands/ZeroMotorsWaitCommand.java b/src/main/java/frc/robot/commands/ZeroMotorsWaitCommand.java index 1082587..8305e1c 100644 --- a/src/main/java/frc/robot/commands/ZeroMotorsWaitCommand.java +++ b/src/main/java/frc/robot/commands/ZeroMotorsWaitCommand.java @@ -3,8 +3,7 @@ import edu.wpi.first.util.sendable.SendableRegistry; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj2.command.Command; -import frc.robot.Constants; -import frc.robot.subsystems.Swerve; +import frc.robot.subsystems.swerve.Swerve; /** * Zeros motors then executes normal wait command. @@ -19,15 +18,15 @@ public class ZeroMotorsWaitCommand extends Command { * * @param seconds how long the wait command should run */ - public ZeroMotorsWaitCommand(double seconds) { + public ZeroMotorsWaitCommand(double seconds, Swerve swerve) { this.m_duration = seconds; SendableRegistry.setName(this, getName() + ": " + seconds + " seconds"); - this.s_Swerve = new Swerve(); + this.s_Swerve = swerve; } @Override public void initialize() { - s_Swerve.setMotorsZero(Constants.Swerve.isOpenLoop, Constants.Swerve.isFieldRelative); + s_Swerve.setMotorsZero(); m_timer.reset(); m_timer.start(); } diff --git a/src/main/java/frc/robot/subsystems/Swerve.java b/src/main/java/frc/robot/subsystems/swerve/Swerve.java similarity index 62% rename from src/main/java/frc/robot/subsystems/Swerve.java rename to src/main/java/frc/robot/subsystems/swerve/Swerve.java index bb13942..10c569d 100644 --- a/src/main/java/frc/robot/subsystems/Swerve.java +++ b/src/main/java/frc/robot/subsystems/swerve/Swerve.java @@ -1,56 +1,68 @@ -package frc.robot.subsystems; +package frc.robot.subsystems.swerve; import java.util.Optional; -import com.kauailabs.navx.frc.AHRS; +import org.littletonrobotics.junction.AutoLogOutput; +import org.littletonrobotics.junction.Logger; import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.util.PathPlannerLogging; +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.math.kinematics.ChassisSpeeds; import edu.wpi.first.math.kinematics.SwerveDriveKinematics; -import edu.wpi.first.math.kinematics.SwerveDriveOdometry; import edu.wpi.first.math.kinematics.SwerveModulePosition; import edu.wpi.first.math.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.DriverStation.Alliance; +import edu.wpi.first.wpilibj.smartdashboard.Field2d; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.lib.util.FieldConstants; import frc.lib.util.swerve.SwerveModule; import frc.robot.Constants; +import frc.robot.Robot; /** * Swerve Subsystem */ public class Swerve extends SubsystemBase { - public SwerveDriveOdometry swerveOdometry; + public SwerveDrivePoseEstimator swerveOdometry; public SwerveModule[] swerveMods; - public AHRS gyro = new AHRS(Constants.Swerve.navXID); - private double fieldOffset = gyro.getYaw(); + private final Field2d field = new Field2d(); + private double fieldOffset; + private SwerveInputsAutoLogged inputs = new SwerveInputsAutoLogged(); + private SwerveIO swerveIO; + /** * Swerve Subsystem */ - public Swerve() { - swerveMods = new SwerveModule[] { - new SwerveModule(0, Constants.Swerve.Mod0.driveMotorID, - Constants.Swerve.Mod0.angleMotorID, Constants.Swerve.Mod0.canCoderID, - Constants.Swerve.Mod0.angleOffset), - new SwerveModule(1, Constants.Swerve.Mod1.driveMotorID, - Constants.Swerve.Mod1.angleMotorID, Constants.Swerve.Mod1.canCoderID, - Constants.Swerve.Mod1.angleOffset), - new SwerveModule(2, Constants.Swerve.Mod2.driveMotorID, - Constants.Swerve.Mod2.angleMotorID, Constants.Swerve.Mod2.canCoderID, - Constants.Swerve.Mod2.angleOffset), - new SwerveModule(3, Constants.Swerve.Mod3.driveMotorID, - Constants.Swerve.Mod3.angleMotorID, Constants.Swerve.Mod3.canCoderID, - Constants.Swerve.Mod3.angleOffset)}; - - swerveOdometry = new SwerveDriveOdometry(Constants.Swerve.swerveKinematics, getGyroYaw(), - getModulePositions()); + public Swerve(SwerveIO swerveIO) { + this.swerveIO = swerveIO; + swerveMods = swerveIO.createModules(); + fieldOffset = getGyroYaw().getDegrees(); + + swerveOdometry = new SwerveDrivePoseEstimator(Constants.Swerve.swerveKinematics, + getGyroYaw(), getModulePositions(), new Pose2d()); + + swerveIO.updateInputs(inputs); AutoBuilder.configureHolonomic(this::getPose, this::resetOdometry, this::getChassisSpeeds, this::setModuleStates, Constants.Swerve.pathFollowerConfig, () -> shouldFlipPath(), this); + + // Logging callback for target robot pose + PathPlannerLogging.setLogTargetPoseCallback((pose) -> { + // Do whatever you want with the pose here + field.getObject("target pose").setPose(pose); + }); + + // Logging callback for the active path, this is sent as a list of poses + PathPlannerLogging.setLogActivePathCallback((poses) -> { + // Do whatever you want with the poses here + field.getObject("path").setPoses(poses); + }); } /** @@ -63,12 +75,14 @@ public Swerve() { */ public void drive(Translation2d translation, double rotation, boolean fieldRelative, boolean isOpenLoop) { + Robot.profiler.push("swerve.drive()"); ChassisSpeeds chassisSpeeds = fieldRelative ? ChassisSpeeds.fromFieldRelativeSpeeds(translation.getX(), translation.getY(), rotation, getFieldRelativeHeading()) : new ChassisSpeeds(translation.getX(), translation.getY(), rotation); setModuleStates(chassisSpeeds); + Robot.profiler.pop(); } /** @@ -78,7 +92,7 @@ rotation, getFieldRelativeHeading()) */ public void setModuleStates(SwerveModuleState[] desiredStates) { SwerveDriveKinematics.desaturateWheelSpeeds(desiredStates, Constants.Swerve.maxSpeed); - + Logger.recordOutput("/Swerve/DesiredStates", desiredStates); for (SwerveModule mod : swerveMods) { mod.setDesiredState(desiredStates[mod.moduleNumber], false); } @@ -136,8 +150,9 @@ public SwerveModulePosition[] getModulePositions() { * * @return Pose2d on the field */ + @AutoLogOutput(key = "Odometry/Robot") public Pose2d getPose() { - return swerveOdometry.getPoseMeters(); + return swerveOdometry.getEstimatedPosition(); } /** @@ -147,6 +162,7 @@ public Pose2d getPose() { */ public void resetOdometry(Pose2d pose) { swerveOdometry.resetPosition(getGyroYaw(), getModulePositions(), pose); + this.swerveIO.setPose(pose); } /** @@ -164,7 +180,7 @@ public Rotation2d getHeading() { * @return Current rotation/yaw of gyro as {@link Rotation2d} */ public Rotation2d getGyroYaw() { - float yaw = gyro.getYaw(); + float yaw = inputs.yaw; return (Constants.Swerve.invertGyro) ? Rotation2d.fromDegrees(-yaw) : Rotation2d.fromDegrees(yaw); } @@ -183,7 +199,7 @@ public Rotation2d getFieldRelativeHeading() { */ public void resetFieldRelativeOffset() { // gyro.zeroYaw(); - fieldOffset = getGyroYaw().getDegrees(); + fieldOffset = getGyroYaw().getDegrees() + 180; } /** @@ -197,25 +213,39 @@ public void resetModulesToAbsolute() { @Override public void periodic() { + Robot.profiler.push("swerve_periodic"); + Robot.profiler.push("update_inputs"); + swerveIO.updateInputs(inputs); + Robot.profiler.swap("update_swerve_mods"); + for (var mod : swerveMods) { + mod.periodic(); + } + Robot.profiler.swap("update_swerve_odometry"); swerveOdometry.update(getGyroYaw(), getModulePositions()); + Robot.profiler.swap("process_inputs"); + Logger.processInputs("Swerve", inputs); + Robot.profiler.swap("update_shuffleboard"); + Robot.profiler.push("field"); + field.setRobotPose(getPose()); + Robot.profiler.swap("apriltag"); - for (SwerveModule mod : swerveMods) { - SmartDashboard.putNumber("Mod " + mod.moduleNumber + " CANcoder", - mod.getCANcoder().getDegrees()); - SmartDashboard.putNumber("Mod " + mod.moduleNumber + " Angle", - mod.getPosition().angle.getDegrees()); - SmartDashboard.putNumber("Mod " + mod.moduleNumber + " Velocity", - mod.getState().speedMetersPerSecond); - } + Robot.profiler.swap("dist-to-speaker"); + SmartDashboard.putNumber("Distance to Speaker", + FieldConstants.allianceFlip(FieldConstants.Speaker.centerSpeakerOpening) + .getTranslation().minus(getPose().getTranslation()).getNorm()); + Robot.profiler.swap("simple"); + SmartDashboard.putNumber("Gyro Yaw", getGyroYaw().getDegrees()); + Logger.recordOutput("/Swerve/ActualStates", getModuleStates()); + Robot.profiler.pop(); + Robot.profiler.pop(); + Robot.profiler.swap("viz"); + Robot.profiler.pop(); } /** * Sets motors to 0 or inactive. - * - * @param isOpenLoop Open or closed loop system - * @param fieldRelative Whether the movement is relative to the field or absolute */ - public void setMotorsZero(boolean isOpenLoop, boolean fieldRelative) { + public void setMotorsZero() { System.out.println("Setting Zero!!!!!!"); setModuleStates(new ChassisSpeeds(0, 0, 0)); } @@ -229,7 +259,18 @@ public void wheelsIn() { swerveMods[2].setDesiredState(new SwerveModuleState(2, Rotation2d.fromDegrees(-45)), false); swerveMods[3].setDesiredState(new SwerveModuleState(2, Rotation2d.fromDegrees(-135)), false); - this.setMotorsZero(Constants.Swerve.isOpenLoop, Constants.Swerve.isFieldRelative); + this.setMotorsZero(); + } + + /** + * Gets a list containing all 4 swerve module positions + */ + public SwerveModulePosition[] getSwerveModulePositions() { + SwerveModulePosition[] positions = new SwerveModulePosition[4]; + for (SwerveModule mod : swerveMods) { + positions[mod.moduleNumber] = mod.getPosition(); + } + return positions; } /** @@ -244,4 +285,17 @@ public static boolean shouldFlipPath() { } return false; } + + /** + * Returns the distance between the speaker and Swerve + * + * @return the difference between the pose of speaker and swerve + */ + public double distanceFromSpeaker() { + double distance = + Math.hypot(FieldConstants.Speaker.centerSpeakerOpening.getY() - getPose().getY(), + FieldConstants.allianceFlip(FieldConstants.Speaker.centerSpeakerOpening).getX() + - getPose().getX()); + return distance; + } } diff --git a/src/main/java/frc/robot/subsystems/swerve/SwerveIO.java b/src/main/java/frc/robot/subsystems/swerve/SwerveIO.java new file mode 100644 index 0000000..b0ae91b --- /dev/null +++ b/src/main/java/frc/robot/subsystems/swerve/SwerveIO.java @@ -0,0 +1,39 @@ +package frc.robot.subsystems.swerve; + +import java.util.Optional; +import org.littletonrobotics.junction.AutoLog; +import edu.wpi.first.math.geometry.Pose2d; +import frc.lib.util.swerve.SwerveModule; + +/** IO Class for Swerve */ +public interface SwerveIO { + + /** Inputs Class for Swerve */ + + @AutoLog + public static class SwerveInputs { + public float yaw; + public float roll; + public float pitch; + } + + public default void updateInputs(SwerveInputs inputs) {} + + // /** Instantiating SwerveModules */ + // public default SwerveModule createSwerveModule(int moduleNumber, int driveMotorID, + // int angleMotorID, int cancoderID, Rotation2d angleOffset) { + // return new SwerveModule(moduleNumber, driveMotorID, angleMotorID, cancoderID, angleOffset, + // new SwerveModuleIO() {}); + // } + + public default SwerveModule[] createModules() { + return new SwerveModule[] {}; + } + + public default Optional getInitialPose() { + return Optional.empty(); + } + + public default void setPose(Pose2d pose) {} + +} diff --git a/src/main/java/frc/robot/subsystems/swerve/SwerveReal.java b/src/main/java/frc/robot/subsystems/swerve/SwerveReal.java new file mode 100644 index 0000000..410e8ca --- /dev/null +++ b/src/main/java/frc/robot/subsystems/swerve/SwerveReal.java @@ -0,0 +1,48 @@ +package frc.robot.subsystems.swerve; + +import com.kauailabs.navx.frc.AHRS; +import edu.wpi.first.math.geometry.Rotation2d; +import frc.lib.util.swerve.SwerveModule; +import frc.lib.util.swerve.SwerveModuleReal; +import frc.robot.Constants; + +/** Real Class for Swerve */ +public class SwerveReal implements SwerveIO { + + private AHRS gyro = new AHRS(Constants.Swerve.navXID); + + /** Real Swerve Initializer */ + public SwerveReal() {} + + @Override + public void updateInputs(SwerveInputs inputs) { + inputs.yaw = gyro.getYaw(); + inputs.pitch = gyro.getPitch(); + inputs.roll = gyro.getRoll(); + + } + + public SwerveModule createSwerveModule(int moduleNumber, int driveMotorID, int angleMotorID, + int cancoderID, Rotation2d angleOffset) { + return new SwerveModule(moduleNumber, angleOffset, + new SwerveModuleReal(driveMotorID, angleMotorID, cancoderID)); + } + + @Override + public SwerveModule[] createModules() { + return new SwerveModule[] { + createSwerveModule(0, Constants.Swerve.Mod0.driveMotorID, + Constants.Swerve.Mod0.angleMotorID, Constants.Swerve.Mod0.canCoderID, + Constants.Swerve.Mod0.angleOffset), + createSwerveModule(1, Constants.Swerve.Mod1.driveMotorID, + Constants.Swerve.Mod1.angleMotorID, Constants.Swerve.Mod1.canCoderID, + Constants.Swerve.Mod1.angleOffset), + createSwerveModule(2, Constants.Swerve.Mod2.driveMotorID, + Constants.Swerve.Mod2.angleMotorID, Constants.Swerve.Mod2.canCoderID, + Constants.Swerve.Mod2.angleOffset), + createSwerveModule(3, Constants.Swerve.Mod3.driveMotorID, + Constants.Swerve.Mod3.angleMotorID, Constants.Swerve.Mod3.canCoderID, + Constants.Swerve.Mod3.angleOffset)}; + } + +} diff --git a/src/main/java/frc/robot/subsystems/swerve/SwerveSim.java b/src/main/java/frc/robot/subsystems/swerve/SwerveSim.java new file mode 100644 index 0000000..1218970 --- /dev/null +++ b/src/main/java/frc/robot/subsystems/swerve/SwerveSim.java @@ -0,0 +1,94 @@ +package frc.robot.subsystems.swerve; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Twist2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.wpilibj.AnalogGyro; +import edu.wpi.first.wpilibj.simulation.AnalogGyroSim; +import frc.lib.util.swerve.SwerveModule; +import frc.lib.util.swerve.SwerveModuleSim; +import frc.robot.Constants; + +/** + * Real Class for Swerve + */ +public class SwerveSim implements SwerveIO { + + private SwerveModule[] swerveMods; + private final AnalogGyro m_gyro = new AnalogGyro(0); + private final AnalogGyroSim m_gyroSim = new AnalogGyroSim(m_gyro); + private Rotation2d rawGyroRotation = new Rotation2d(); + private SwerveModulePosition[] modulePositions = new SwerveModulePosition[4]; + private SwerveModulePosition[] moduleDeltas = new SwerveModulePosition[4]; + private SwerveModulePosition[] lastModulePositions = // For delta tracking + new SwerveModulePosition[] {new SwerveModulePosition(), new SwerveModulePosition(), + new SwerveModulePosition(), new SwerveModulePosition()}; + + private Pose2d currentPose = new Pose2d(); + + /** + * Real Swerve Initializer + */ + public SwerveSim() {} + + @Override + public void updateInputs(SwerveInputs inputs) { + m_gyroSim.setAngle(getHeading().getDegrees()); + if (Constants.Swerve.invertGyro) { + inputs.yaw = (float) -m_gyroSim.getAngle(); + } else { + inputs.yaw = (float) m_gyroSim.getAngle(); + } + // inputs.pitch = 0; + // inputs.roll = 0; + + } + + /** + * Create Swerve Module + * + * @param moduleNumber Module Number + * @return Swerve Module + */ + public SwerveModule createSwerveModule(int moduleNumber) { + SwerveModuleSim modIO = new SwerveModuleSim(); + modIO.setModNumber(moduleNumber); + SwerveModule mod = + new SwerveModule(moduleNumber, Rotation2d.fromRotations(Math.random()), modIO); + return mod; + } + + @Override + public SwerveModule[] createModules() { + swerveMods = new SwerveModule[] {createSwerveModule(0), createSwerveModule(1), + createSwerveModule(2), createSwerveModule(3)}; + return swerveMods; + } + + /** + * Get Heading + * + * @return Get robot heading + */ + private Rotation2d getHeading() { + for (int moduleIndex = 0; moduleIndex < 4; moduleIndex++) { + modulePositions[moduleIndex] = swerveMods[moduleIndex].getPosition(); + moduleDeltas[moduleIndex] = new SwerveModulePosition( + modulePositions[moduleIndex].distanceMeters + - lastModulePositions[moduleIndex].distanceMeters, + modulePositions[moduleIndex].angle); + lastModulePositions[moduleIndex] = modulePositions[moduleIndex]; + } + Twist2d twist = Constants.Swerve.swerveKinematics.toTwist2d(moduleDeltas); + this.currentPose = this.currentPose.exp(twist); + rawGyroRotation = rawGyroRotation.plus(new Rotation2d(twist.dtheta)); + return rawGyroRotation; + } + + @Override + public void setPose(Pose2d pose) { + this.currentPose = pose; + } + +} diff --git a/vendordeps/AdvantageKit.json b/vendordeps/AdvantageKit.json new file mode 100644 index 0000000..63dacbb --- /dev/null +++ b/vendordeps/AdvantageKit.json @@ -0,0 +1,42 @@ +{ + "fileName": "AdvantageKit.json", + "name": "AdvantageKit", + "version": "3.2.1", + "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", + "frcYear": "2024", + "mavenUrls": [], + "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json", + "javaDependencies": [ + { + "groupId": "org.littletonrobotics.akit.junction", + "artifactId": "wpilib-shim", + "version": "3.2.1" + }, + { + "groupId": "org.littletonrobotics.akit.junction", + "artifactId": "junction-core", + "version": "3.2.1" + }, + { + "groupId": "org.littletonrobotics.akit.conduit", + "artifactId": "conduit-api", + "version": "3.2.1" + } + ], + "jniDependencies": [ + { + "groupId": "org.littletonrobotics.akit.conduit", + "artifactId": "conduit-wpilibio", + "version": "3.2.1", + "skipInvalidPlatforms": false, + "isJar": false, + "validPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ] + } + ], + "cppDependencies": [] +} \ No newline at end of file diff --git a/vendordeps/PathplannerLib.json b/vendordeps/PathplannerLib.json index 0bf11fb..6dc648d 100644 --- a/vendordeps/PathplannerLib.json +++ b/vendordeps/PathplannerLib.json @@ -1,7 +1,7 @@ { "fileName": "PathplannerLib.json", "name": "PathplannerLib", - "version": "2024.1.4", + "version": "2024.2.8", "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", "frcYear": "2024", "mavenUrls": [ @@ -12,7 +12,7 @@ { "groupId": "com.pathplanner.lib", "artifactId": "PathplannerLib-java", - "version": "2024.1.4" + "version": "2024.2.8" } ], "jniDependencies": [], @@ -20,7 +20,7 @@ { "groupId": "com.pathplanner.lib", "artifactId": "PathplannerLib-cpp", - "version": "2024.1.4", + "version": "2024.2.8", "libName": "PathplannerLib", "headerClassifier": "headers", "sharedLibrary": false, diff --git a/vendordeps/Phoenix6.json b/vendordeps/Phoenix6.json index 69a4079..0322385 100644 --- a/vendordeps/Phoenix6.json +++ b/vendordeps/Phoenix6.json @@ -1,7 +1,7 @@ { "fileName": "Phoenix6.json", "name": "CTRE-Phoenix (v6)", - "version": "24.1.0", + "version": "24.3.0", "frcYear": 2024, "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", "mavenUrls": [ @@ -19,14 +19,14 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-java", - "version": "24.1.0" + "version": "24.3.0" } ], "jniDependencies": [ { "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -39,7 +39,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -52,7 +52,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -65,7 +65,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonFX", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -78,7 +78,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -91,7 +91,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -104,7 +104,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -117,7 +117,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -130,7 +130,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -143,7 +143,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "24.1.0", + "version": "24.3.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -158,7 +158,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-cpp", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_Phoenix6_WPI", "headerClassifier": "headers", "sharedLibrary": true, @@ -173,7 +173,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "tools", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", "sharedLibrary": true, @@ -188,7 +188,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "wpiapi-cpp-sim", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_Phoenix6_WPISim", "headerClassifier": "headers", "sharedLibrary": true, @@ -203,7 +203,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_PhoenixTools_Sim", "headerClassifier": "headers", "sharedLibrary": true, @@ -218,7 +218,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimTalonSRX", "headerClassifier": "headers", "sharedLibrary": true, @@ -233,7 +233,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonFX", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimTalonFX", "headerClassifier": "headers", "sharedLibrary": true, @@ -248,7 +248,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimVictorSPX", "headerClassifier": "headers", "sharedLibrary": true, @@ -263,7 +263,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimPigeonIMU", "headerClassifier": "headers", "sharedLibrary": true, @@ -278,7 +278,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimCANCoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -293,7 +293,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimProTalonFX", "headerClassifier": "headers", "sharedLibrary": true, @@ -308,7 +308,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimProCANcoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -323,7 +323,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", - "version": "24.1.0", + "version": "24.3.0", "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", "sharedLibrary": true, diff --git a/vendordeps/REVLib.json b/vendordeps/REVLib.json deleted file mode 100644 index 0f3520e..0000000 --- a/vendordeps/REVLib.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "fileName": "REVLib.json", - "name": "REVLib", - "version": "2024.2.0", - "frcYear": "2024", - "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", - "mavenUrls": [ - "https://maven.revrobotics.com/" - ], - "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2024.json", - "javaDependencies": [ - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-java", - "version": "2024.2.0" - } - ], - "jniDependencies": [ - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-driver", - "version": "2024.2.0", - "skipInvalidPlatforms": true, - "isJar": false, - "validPlatforms": [ - "windowsx86-64", - "windowsx86", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - } - ], - "cppDependencies": [ - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-cpp", - "version": "2024.2.0", - "libName": "REVLib", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "windowsx86", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - }, - { - "groupId": "com.revrobotics.frc", - "artifactId": "REVLib-driver", - "version": "2024.2.0", - "libName": "REVLibDriver", - "headerClassifier": "headers", - "sharedLibrary": false, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "windowsx86", - "linuxarm64", - "linuxx86-64", - "linuxathena", - "linuxarm32", - "osxuniversal" - ] - } - ] -} \ No newline at end of file diff --git a/vendordeps/photonlib.json b/vendordeps/photonlib.json deleted file mode 100644 index 282cfa8..0000000 --- a/vendordeps/photonlib.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "fileName": "photonlib.json", - "name": "photonlib", - "version": "v2024.2.0", - "uuid": "515fe07e-bfc6-11fa-b3de-0242ac130004", - "frcYear": "2024", - "mavenUrls": [ - "https://maven.photonvision.org/repository/internal", - "https://maven.photonvision.org/repository/snapshots" - ], - "jsonUrl": "https://maven.photonvision.org/repository/internal/org/photonvision/photonlib-json/1.0/photonlib-json-1.0.json", - "jniDependencies": [], - "cppDependencies": [ - { - "groupId": "org.photonvision", - "artifactId": "photonlib-cpp", - "version": "v2024.2.0", - "libName": "photonlib", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxathena", - "linuxx86-64", - "osxuniversal" - ] - }, - { - "groupId": "org.photonvision", - "artifactId": "photontargeting-cpp", - "version": "v2024.2.0", - "libName": "photontargeting", - "headerClassifier": "headers", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "windowsx86-64", - "linuxathena", - "linuxx86-64", - "osxuniversal" - ] - } - ], - "javaDependencies": [ - { - "groupId": "org.photonvision", - "artifactId": "photonlib-java", - "version": "v2024.2.0" - }, - { - "groupId": "org.photonvision", - "artifactId": "photontargeting-java", - "version": "v2024.2.0" - } - ] -} \ No newline at end of file