{"id":15149269,"url":"https://github.com/strykeforce/thirdcoast","last_synced_at":"2026-03-14T20:00:47.272Z","repository":{"id":25798340,"uuid":"97531449","full_name":"strykeforce/thirdcoast","owner":"strykeforce","description":"Third Coast swerve drive and telemetry API for FRC robots","archived":false,"fork":false,"pushed_at":"2025-01-29T23:19:16.000Z","size":1763,"stargazers_count":36,"open_issues_count":1,"forks_count":13,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-01-30T00:22:28.100Z","etag":null,"topics":["frc","java","swerve-drive"],"latest_commit_sha":null,"homepage":"https://www.strykeforce.org/resources/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/strykeforce.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-17T23:35:45.000Z","updated_at":"2025-01-29T23:18:33.000Z","dependencies_parsed_at":"2024-01-13T23:43:49.448Z","dependency_job_id":"447dec6e-6f3e-413c-9eef-0d1e7e8cd64c","html_url":"https://github.com/strykeforce/thirdcoast","commit_stats":{"total_commits":520,"total_committers":7,"mean_commits":74.28571428571429,"dds":0.06730769230769229,"last_synced_commit":"3eb3f4bcb272dd12071d30c3742df9168d956f90"},"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strykeforce%2Fthirdcoast","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strykeforce%2Fthirdcoast/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strykeforce%2Fthirdcoast/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strykeforce%2Fthirdcoast/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/strykeforce","download_url":"https://codeload.github.com/strykeforce/thirdcoast/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237918710,"owners_count":19387305,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["frc","java","swerve-drive"],"created_at":"2024-09-26T13:43:25.515Z","updated_at":"2025-10-24T05:31:25.695Z","avatar_url":"https://github.com/strykeforce.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stryke Force Third Coast Java Libraries\n\nThis project consists of three libraries that are used by Team 2767 Stryke Force.\n\n- **Swerve Drive** - software control of [Third Coast swerve drive modules](https://www.strykeforce.org/resources/).\n  This code should generally work with swerve drives that use CTRE motor controllers and a CTRE magnetic encoder for\n  azimuth position.\n- **Telemetry** - provide real-time streaming telemetry information from a robot. Used with\n  our [Grapher](https://github.com/strykeforce/grapher) LabView strip-chart recorder.\n- **Health Check** - configure automated motor health checks for use by pit crew during competitions.\n\nOther Stryke Force engineering resources are at [strykeforce.org](https://www.strykeforce.org/resources/).\n\n## Installation\n\nThe Third Coast `vendordeps` file is at: http://packages.strykeforce.org/thirdcoast.json\n\nTo install, use **Install new libraries (online)** in VS Code or download manually to your project `vendordeps`\ndirectory.\n\n```\n$ ./gradlew vendordep --url=http://packages.strykeforce.org/thirdcoast.json\n```\n\n## Swerve Drive\n\nWe have wrapped the FRC WPILib Swerve Drive kinematics and\nodometry [classes](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/math/kinematics/package-summary.html)\nto work easily with our swerve module design and to facilitate use of the rest of the Third Coast libraries.\n\nFor example, here is how you can configure our swerve drive.\n\n```java\npublic class SwerveDriveSubsystem extends SubsystemBase {\n\n    private final SwerveDrive swerveDrive;\n\n    public SwerveDriveSubsystem() { // Pretend to set up a swerve drive\n\n        var moduleBuilder =\n                new TalonSwerveModule.Builder()\n                        .driveGearRatio(DriveConstants.kDriveGearRatio)\n                        .wheelDiameterInches(DriveConstants.kWheelDiameterInches)\n                        .driveMaximumMetersPerSecond(DriveConstants.kMaxSpeedMetersPerSecond);\n\n        TalonSwerveModule[] swerveModules = new TalonSwerveModule[4];\n        Translation2d[] wheelLocations = DriveConstants.getWheelLocationMeters();\n\n        // initialize the swerve modules\n        for (int i = 0; i \u003c 4; i++) {\n            var azimuthTalon = new TalonSRX(i);\n            // configure azimuth Phoenix API settings...\n            var driveTalon = new TalonFX(i + 10);\n            // configure drive Phoenix API settings...\n\n            swerveModules[i] =\n                    moduleBuilder\n                            .azimuthTalon(azimuthTalon)\n                            .driveTalon(driveTalon)\n                            .wheelLocationMeters(wheelLocations[i])\n                            .build();\n\n            swerveModules[i].loadAndSetAzimuthZeroReference();\n        }\n\n        // initialize the swerve drive with configured swerve modules\n        swerveDrive = new SwerveDrive(swerveModules);\n    }\n}\n\n```\n\nIn the simplest case, you can control this configured `SwerveDrive` in open-loop, for example during tele-operation.\n\n```java\npublic class SwerveDriveSubsystem extends SubsystemBase {\n    private final SwerveDrive swerveDrive;\n\n    // ...\n\n    public void drive(double vxMetersPerSecond, double vyMetersPerSecond, double omegaRadiansPerSecond, boolean isFieldOriented) {\n        swerveDrive.drive(vxMetersPerSecond, vyMetersPerSecond, omegaRadiansPerSecond, isFieldOriented);\n    }\n}\n```\n\n## Telemetry\n\nOur Telemetry library is used to instrument subsystems and stream measurements to\nour [Grapher](https://github.com/strykeforce/grapher) LabView strip-chart recorder. We find it invaluable during the\nseason for many tasks, not the least of which is motor controller closed-loop tuning. During motor controller\nclosed-loop\ntuning we often will stream telemetry while manually controlling the motors using\nour `tct` [application](https://github.com/strykeforce/thirdcoast-tct).\n\nContinuing on with our example from above, to instrument the `SwerveDriveSubsystem` class we subclass our\nabstract `MeasureableSubsystem` instead of `SubsystemBase` and then implement the `getMeasures()` method.\n\n```java\npublic class SwerveDriveSubsystem extends MeasurableSubsystem {\n    private final SwerveDrive swerveDrive;\n    // ...\n\n    @Override\n    public Set\u003cMeasure\u003e getMeasures() {\n        return Set.of(\n                new Measure(\"Gyro Rotation2D(deg)\", () -\u003e swerveDrive.getHeading().getDegrees()),\n                new Measure(\"Odometry X\", () -\u003e swerveDrive.getPoseMeters().getX()),\n                new Measure(\"Odometry Y\", () -\u003e swerveDrive.getPoseMeters().getY()),\n                // other measurements...\n        );\n    }\n}\n```\n\nEach of the `Measure` objects supplied from `getMeasures()` are created with a name, optional description, and\na `DoubleSupplier` (typically a lambda expression) to provide the measured data.\n\nDuring robot start-up, we register the instrumented subsystems with the Telemetry library. When you connect to the robot\nusing the Grapher application, you are presented with these subsystems and their measurements as options to view in the\nstrip-chart.\n\n```java\npublic class RobotContainer {\n    private final SwerveDriveSubsystem swerveDriveSubsystem;\n    private final TelemetryService telemetryService = new TelemetryService(TelemetryController::new);\n\n    public RobotContainer() {\n        swerveDriveSubsystem.registerWith(telemetryService);\n        // ...\n    }\n}\n```\n\n## Health Check\n\nThis system provides our pit team with the ability to define a set of pre-defined motor health checks that can be run\nwith the press of a button. It is intended for use with subsystems that contain Talon motor controllers.\n\nSubsystems that contain motors to be health checked are annotated with `@HealthCheck` and optionally, annotations that\ndefine the health check in more detail. There are three possible ways to configure a talon for testing:\n\n- `@Timed` - runs the motor at the specified output for the specified amount of time.\n- `@Position` - runs the motor at the specified output until the specified amount of encoder ticks have occurred.\n- `@Follower` - the motor is configured to follow another Talon and therefor is not commanded, only measured.\n\nThis subsystem has some examples of Talons being tested in various ways.\n\n```java\npublic class ExampleSubsystem extends SubsystemBase {\n\n    private final TalonSRXConfiguration talonSRXConfiguration = new TalonSRXConfiguration();\n\n    // default duration healthcheck at default output percentages in forward and reverse direction\n    @HealthCheck\n    private final TalonSRX talonOne = new TalonFX(1);\n\n    // run each of 3 output percentages for 4 seconds each\n    @HealthCheck\n    @Timed(percentOutput = {0.5, 0.75, -0.25}, duration = 4.0)\n    private final TalonFX talonTwo = new TalonFX(2);\n\n    // run each of 4 output percentages until 20,000 encoder ticks have occurred\n    @HealthCheck\n    @Position(percentOutput = {0.25, -0.25, 0.5, -0.5}, encoderChange = 20_000)\n    private final TalonSRX talonThree = new TalonFX(3);\n\n    // follows talonTwo (device id = 2) so just take measurements\n    @HealthCheck\n    @Follow(leader = 2)\n    private final TalonFX talonFour = new Talon(4);\n\n    public ExampleSubsystem() {\n        talonFour.follow(talonTwo);\n    }\n\n    // These Health Check lifecycle methods are all optional.\n\n    @BeforeHealthCheck\n    private boolean beforeHealthCheck() {\n        // perform any set-up such as positioning subsystems on the robot to\n        // allow clear running, or override Talon configurations temporarily\n        // while testing. Called periodically by the robot loop while running,\n        // return true when finished.\n        return true;\n    }\n\n    @AfterHealthCheck\n    private boolean afterHealthCheck() {\n        // perform any tear-down such as position subsystems, or resetting\n        // Talon configurations. Called periodically by the robot loop while\n        // running, return true when finished.\n        return true;\n    }\n\n}\n```\n\nThe `@BeforeHealthCheck` and `@AfterHealthCheck` methods are run before and after the health checks respectively and are\ncalled periodically by the robot loop while they are running. They should return `true` when finished. If there is more\nthan one of either, you can specify the order in a similar fashion to `@HealthCheck`.\n\nIf you annotate an instance of `SwerveDrive`, it will run a series of standard health checks on the azimuth and drive\nTalons.\n\n```java\npublic class SwerveDriveSubsystem extends SubsystemBase {\n    @HealthCheck\n    private final SwerveDrive swerveDrive;\n    // ...\n}\n```\n\nTo run the health checks, pass the subsystems to test to the `HealthCheckCommand` and connect to a suitable button.\n\n```java\npublic class RobotContainer {\n\n    private final ExampleSubsystem exampleSubsystem = new ExampleSubsystem();\n    private final SwerveDriveSubsystem swerveDriveSubsystem = new SwerveDriveSubsystem();\n\n    public RobotContainer() {\n        new Trigger(RobotController::getUserButton).onTrue(new HealthCheckCommand(exampleSubsystem, swerveDriveSubsystem));\n        // ...\n    }\n}\n```\n\nAfter running the healthcheck, the JSON-formatted raw data is available at `http://10.27.67.2:2767/data` and can be\nsaved and analyzed with a tool of choice. For example, the data can be loaded into\na [Pandas](https://pandas.pydata.org/pandas-docs/stable/) dataframe for analysis and visualization using the following.\n\n```python\nimport pandas as pd\nimport requests\n\nr = requests.get(\"http://10.27.67.2:2767/data\")\nj = r.json()\n\nmeta = pd.DataFrame(j[\"meta\"])\ndata = pd.DataFrame(j[\"data\"])\ndf = pd.merge(meta, data, on=\"case\", suffixes=('_set', '_measured'))\ndf.head(3)\n```\n\n\u003cdiv\u003e\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003ecase\u003c/th\u003e\n      \u003cth\u003ecase_uuid\u003c/th\u003e\n      \u003cth\u003ename\u003c/th\u003e\n      \u003cth\u003etalon_set\u003c/th\u003e\n      \u003cth\u003etype\u003c/th\u003e\n      \u003cth\u003eoutput\u003c/th\u003e\n      \u003cth\u003eduration\u003c/th\u003e\n      \u003cth\u003edatetime\u003c/th\u003e\n      \u003cth\u003emsec_elapsed\u003c/th\u003e\n      \u003cth\u003etalon_measured\u003c/th\u003e\n      \u003cth\u003evoltage\u003c/th\u003e\n      \u003cth\u003eposition\u003c/th\u003e\n      \u003cth\u003espeed\u003c/th\u003e\n      \u003cth\u003esupply_current\u003c/th\u003e\n      \u003cth\u003estator_current\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e0\u003c/th\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e8efc0583-8f90-4ab8-9930-5403b1f51fb2\u003c/td\u003e\n      \u003ctd\u003eDriveSubsystem\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003etime\u003c/td\u003e\n      \u003ctd\u003e0.25\u003c/td\u003e\n      \u003ctd\u003e5000000\u003c/td\u003e\n      \u003ctd\u003e2023-02-18 14:14:23.819275\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.125\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e1\u003c/th\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e8efc0583-8f90-4ab8-9930-5403b1f51fb2\u003c/td\u003e\n      \u003ctd\u003eDriveSubsystem\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003etime\u003c/td\u003e\n      \u003ctd\u003e0.25\u003c/td\u003e\n      \u003ctd\u003e5000000\u003c/td\u003e\n      \u003ctd\u003e2023-02-18 14:14:23.819275\u003c/td\u003e\n      \u003ctd\u003e7144\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.125\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e2\u003c/th\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e8efc0583-8f90-4ab8-9930-5403b1f51fb2\u003c/td\u003e\n      \u003ctd\u003eDriveSubsystem\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003etime\u003c/td\u003e\n      \u003ctd\u003e0.25\u003c/td\u003e\n      \u003ctd\u003e5000000\u003c/td\u003e\n      \u003ctd\u003e2023-02-18 14:14:23.819275\u003c/td\u003e\n      \u003ctd\u003e13316\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0.125\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrykeforce%2Fthirdcoast","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrykeforce%2Fthirdcoast","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrykeforce%2Fthirdcoast/lists"}