Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,21 @@ directories, lowercased:
- **`SwerveBase` extends `SubsystemBase`.** C++ mixes the two in via multiple inheritance,
which Java does not have, so the hierarchy is `SubsystemBase` → `SwerveBase` →
`SwerveChassis` → your drivetrain.
- **Path following is wired up in the robot project.** The library does not configure a
path follower or depend on one, in either language, so a robot can follow paths with
PathPlanner, BLine or anything else.
[documentation/swerve](documentation/swerve/README.md) has a worked Java example of each.
- **Standard deviations are `Matrix<N3, N1>`** (built with `VecBuilder.fill`) rather than
the C++ `wpi::array<double, 3>`, because that is what the WPILib pose estimator takes.
- `LimelightHelpers.java` is vendored from
[limelightlib-wpijava](https://github.com/LimelightVision/limelightlib-wpijava) (v1.14).
Only its `package` declaration and one malformed Javadoc tag were changed.

## Documentation

Guides and example robot code live in [documentation/](documentation/README.md). The
examples are based on Shelby's code and are not compiled by this build.

## Building

```bash
Expand Down Expand Up @@ -115,14 +124,18 @@ Java vendor dependencies are read directly out of `vendordeps/*.json` at configu
time, so the Java and C++ builds always consume the same vendor versions. Adding or
bumping a vendordep JSON is all that is needed — there is no second list to update.

Currently: WPILib New Commands, CTRE Phoenix 6, PathPlannerLib, PhotonLib and maple-sim.
Currently: WPILib New Commands, CTRE Phoenix 6, PhotonLib and maple-sim.

### Robot projects install these themselves

`OvertureLib.json` declares only the OvertureLib artifact, so none of the vendor libraries
above come along with it. That has always been true: a robot project already installs
Phoenix 6, PathPlanner and PhotonLib of its own accord even though it reaches most of them
through this library rather than directly.
Phoenix 6 and PhotonLib of its own accord even though it reaches most of them through this
library rather than directly.

A path following library is the robot's own choice and its own vendordep. Nothing in the
library needs one, so a robot that follows paths with BLine does not install PathPlanner,
and the other way round.

maple-sim is simply the newest entry on that list, and the one a robot set up before 2026
will not have. Without it, simulation fails immediately. A real robot most likely runs
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ dependencies {
implementation "com.fasterxml.jackson.core:jackson-databind:2.19.2"
implementation 'edu.wpi.first.thirdparty.frc2025.opencv:opencv-java:4.10.0-3'

// WPILibNewCommands, Phoenix 6, PathPlannerLib and PhotonLib
// WPILibNewCommands, Phoenix 6, PhotonLib and maple-sim
vendordepJava.deps.each {implementation it}

testImplementation 'org.junit.jupiter:junit-jupiter:5.13.4'
Expand Down
17 changes: 17 additions & 0 deletions documentation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# OvertureLib documentation

Guides for using the library from a robot project, with the example code they refer to.

| Guide | What it covers |
| ----- | -------------- |
| [swerve](swerve/README.md) | Building a drivetrain on `SwerveChassis` and following paths with it, using PathPlanner or BLine |

## About the examples

The examples are plain Java files, written the way they would sit in a robot project and
based on Shelby's code ([FRC-Shelby-2026OS](https://github.com/Overture-7421/FRC-Shelby-2026OS)).
Copy them into a robot and change the numbers.

They are not part of the Gradle build. They depend on libraries OvertureLib deliberately
does not, so nothing compiles them when the library changes: if you change an API one of
them uses, update the example with it.
159 changes: 159 additions & 0 deletions documentation/swerve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Swerve and path following

OvertureLib builds the drivetrain. It does not choose how the robot follows paths: that is
wired up in the robot project, so a robot can use PathPlanner, BLine, both or neither
without the library carrying any of them.

## What a path follower plugs into

Every path follower asks for the same handful of things, and `SwerveChassis` already has
them as public methods:

| The follower wants | Give it | Notes |
| ------------------ | ------- | ----- |
| The robot pose | `chassis::getEstimatedPose` | Odometry fused with vision |
| A way to reset the pose | `chassis::resetPose` | Not `resetOdometry`, see below |
| The current speeds | `chassis::getCurrentSpeeds` | Robot relative |
| Somewhere to send speeds | `chassis::setTargetSpeeds` | Robot relative |
| The subsystem it requires | `chassis` | |

**`resetPose` or `resetOdometry`?** A path follower resetting the pose means "the robot is
here now", so `resetPose` also places the simulated robot there. `resetOdometry` only
corrects the estimate, which is what a heading reset or a vision correction wants. Hand a
follower `resetOdometry` and autos still run on the real robot, but in simulation the
physics robot stays where it was while odometry jumps to the start of the auto.

Speeds sent to `setTargetSpeeds` go through the same path as the driver's: an active
`SpeedsHelper` still rewrites them, and they are discretized and desaturated before
reaching the modules.

## The examples

| File | What it is |
| ---- | ---------- |
| [Chassis.java](examples/Chassis.java) | Shelby's drivetrain. No path follower in sight |
| [PathPlannerConfig.java](examples/PathPlannerConfig.java) | Hands that chassis to PathPlanner |
| [BLinePaths.java](examples/BLinePaths.java) | Hands that chassis to BLine |

The chassis is the same for both followers. Each follower lives in one small class beside
it, in `frc.robot.Subsystems.Chassis`, and switching is a matter of which one
`RobotContainer` uses.

They were compiled against PathPlannerLib 2026.1.2 and BLine-Lib v0.9.2.

## PathPlanner

1. Install the vendordep in the robot project (WPILib: Manage Vendor Libraries, Install new
libraries (online)):

```text
https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json
```

2. Open the robot project in the PathPlanner app and fill in the robot config. It is saved
to `src/main/deploy/pathplanner/settings.json`, which is what
`RobotConfig.fromGUISettings()` reads. Nothing checks it against the chassis, so the
numbers have to be kept in step by hand. For Shelby:

| `settings.json` | Value | Same number in `Chassis.java` |
| --------------- | ----- | ----------------------------- |
| `robotMass` | 61.235 | `withRobotMass(Kilograms.of(61.235))` |
| `driveGearing` | 7.03 | `kDriveGearRatio` |
| `driveWheelRadius` | 0.0508 | `kWheelRadiusInches` (2 in) |
| `maxDriveSpeed` | 4.541 | `getMaxModuleSpeed()` |
| `flModuleX`, `flModuleY` | 0.282575, 0.276225 | `kTrackXInches`, `kTrackYInches` (11.125 in, 10.875 in) |
| `driveCurrentLimit` | 60.0 | `SupplyCurrentLimit` |

3. Copy [PathPlannerConfig.java](examples/PathPlannerConfig.java) next to the chassis and
call it from `RobotContainer`:

```java
public final Chassis chassis = new Chassis();
private final SendableChooser<Command> autoChooser;

public RobotContainer() {
// Before the chooser, which builds every auto in deploy/pathplanner/autos as it is
// created. NamedCommands.registerCommand(...) goes before it too, for the same reason.
PathPlannerConfig.configure(chassis);

autoChooser = AutoBuilder.buildAutoChooser();
SmartDashboard.putData("Auto Chooser", autoChooser);
}

public Command getAutonomousCommand() {
return autoChooser.getSelected();
}
```

The path following gains are the two `PIDConstants` at the top of `PathPlannerConfig`.

## BLine

1. Install the vendordep in the robot project:

```text
https://bline-metrics.edan-liahovetsky.workers.dev/vendor/BLine-Lib.json
```

If that URL is down, BLine's README gives
`https://raw.githubusercontent.com/edanliahovetsky/BLine-Lib/main/BLine-Lib.json` as
the fallback.

2. Draw the paths in the [BLine editor](https://bline-web.pages.dev/). BLine loads them
from `src/main/deploy/autos/paths/<name>.json`, and the default constraints from
`src/main/deploy/autos/config.json`.

3. Copy [BLinePaths.java](examples/BLinePaths.java) next to the chassis and build the
autos from it in `RobotContainer`:

```java
public final Chassis chassis = new Chassis();
private final BLinePaths paths = new BLinePaths(chassis);
private final SendableChooser<Command> autoChooser = new SendableChooser<>();

public RobotContainer() {
autoChooser.setDefaultOption("None", Commands.none());
autoChooser.addOption(
"Left double pass",
Commands.sequence(
paths.resetAndFollow("LeftDoublePass1"),
paths.follow("LeftDoublePass2")));
SmartDashboard.putData("Auto Chooser", autoChooser);
}

public Command getAutonomousCommand() {
return autoChooser.getSelected();
}
```

Two things about BLine that the example is built around:

- **The pose reset belongs to the first path only.** BLine resets the pose every time a
command built with a reset starts. `resetAndFollow` is for the first path of an auto and
`follow` for the rest; a reset on a later path throws away what odometry and vision
worked out on the way there.
- **A path file is read when its command is built**, and a missing one throws. Building
the autos in the `RobotContainer` constructor, as above, makes that a crash at startup
in the pits instead of at the start of a match.

If a path has event triggers that schedule commands, BLine's README recommends composing
the auto with its own `BLineCommands.sequence` instead of `Commands.sequence`.

The path following gains are the three `PIDController`s in `BLinePaths`. They are BLine's
suggested starting values and have not been tuned on a robot.

## Updating a robot from 2026.4.0-beta-3 or earlier

Up to 2026.4.0-beta-3, `SwerveBase.configureSwerveBase()` configured PathPlanner itself. A
robot written against those versions needs three changes when it updates:

1. **Delete `getTranslationPID()` and `getRotationPID()` from the chassis.** They no longer
override anything, so the `@Override` on them stops compiling. The gains move to
`PathPlannerConfig`.
2. **Add `PathPlannerConfig` and call `PathPlannerConfig.configure(chassis)`.** Without it
`AutoBuilder` is never configured, and PathPlanner refuses to build autos.
3. **Stop relying on `/PathPlanner/ResetPose`.** The library no longer publishes that
NetworkTables topic. It existed for the old external simulator; the built in simulation
is moved by `resetPose` directly.

Robots that never ran an auto need only the first.
74 changes: 74 additions & 0 deletions documentation/swerve/examples/BLinePaths.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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.Subsystems.Chassis;

import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.wpilibj2.command.Command;
import frc.robot.lib.BLine.FollowPath;
import frc.robot.lib.BLine.Path;

/**
* Hands the chassis to BLine and builds the commands that follow its paths.
*
* <p>This is robot code. OvertureLib does not know BLine exists, so this class is the only place
* the two meet. Build one of these in RobotContainer and ask it for every path command.
*/
public final class BLinePaths {
private final Chassis chassis;
private final FollowPath.Builder builder;

/**
* Wires BLine to the chassis.
*
* @param chassis the drivetrain BLine will drive
*/
public BLinePaths(Chassis chassis) {
this.chassis = chassis;

// The gains are BLine's own starting values, not ones tuned on Shelby.
builder =
new FollowPath.Builder(
chassis,
chassis::getEstimatedPose,
// Robot relative, both the speeds read and the speeds commanded.
chassis::getCurrentSpeeds,
chassis::setTargetSpeeds,
// Translation: how hard to drive at what is left of the path.
new PIDController(5.0, 0.0, 0.0),
// Rotation: how hard to turn towards the heading the path asks for.
new PIDController(3.0, 0.0, 0.0),
// Cross track: how hard to pull back onto the line between two points.
new PIDController(2.0, 0.0, 0.0))
// Paths are drawn on the blue side and flipped when we are red.
.withDefaultShouldFlip();
}

/**
* Follows a path from wherever the robot believes it is.
*
* @param pathName the path file in deploy/autos/paths, without the .json
* @return the command
*/
public Command follow(String pathName) {
// The builder remembers its pose reset between builds, so it is set on every build rather
// than left to whichever method happened to run last.
return builder.withPoseReset(pose -> {}).build(new Path(pathName));
}

/**
* Places the robot at the start of a path, then follows it. For the first path of an auto.
*
* <p>Only the first. BLine resets the pose every time a command built with a reset starts, so
* using this for a later path throws away whatever odometry and vision worked out on the way
* there.
*
* @param pathName the path file in deploy/autos/paths, without the .json
* @return the command
*/
public Command resetAndFollow(String pathName) {
// resetPose rather than resetOdometry: it places the simulated robot there as well.
return builder.withPoseReset(chassis::resetPose).build(new Path(pathName));
}
}
Loading
Loading