Choreo: Physics-Based Path Planning (Alternative)
PathPlanner asks "what curve would look smooth?" Choreo asks "what trajectory would a robot with these specific motors, this mass, and these wheels actually complete in minimum time?" The difference is whether the planner knows your robot's physics — and that difference shows up in match performance.
By the end of this lesson, you will:
- Explain what trajectory optimization means and why it produces different paths than spline-based planners
- Describe the specific robot parameters Choreo uses to generate physics-accurate trajectories
- Install Choreo and set up the ChoreoLib Java dependency
- Load and follow a
.trajfile usingChoreo.loadTrajectory()andAutoFactory - Use Choreo's event triggers to fire robot commands at specific trajectory positions
- Make an informed decision about whether PathPlanner or Choreo is the better fit for your team's current needs
What Choreo Is — and What Makes It Different
Choreo (formerly SwerveLib) is an open-source trajectory planning tool for FRC swerve drives, maintained primarily by Team 3011 (Firebears). Where PathPlanner generates paths by fitting smooth spline curves through waypoints and then constraining velocity based on curvature, Choreo takes a fundamentally different approach: it solves a mathematical optimization problem to find the trajectory that minimizes total travel time, subject to the actual physical constraints of your specific robot.
This distinction matters more than it might initially sound. A PathPlanner path is "as smooth and fast as the spline shape allows, constrained by your chosen maxVelocity." A Choreo trajectory is "as fast as your specific motors, mass, wheel friction, and gear ratios physically allow, given that you need to arrive at this pose with this heading." The optimizer accounts for motor torque curves, drivetrain inertia, and wheel slip limits. The resulting trajectory is not necessarily a smooth spline — it's whatever shape is fastest for your robot's actual hardware.
In practice, on a well-configured competition swerve drivetrain, Choreo trajectories are typically 5–15% faster than equivalent PathPlanner paths because they use the robot's full physical capability throughout, not just on straight segments. They also tend to be more repeatable because the trajectory is derived from physics rather than from constraint parameters that require manual tuning.
How Choreo Generates Trajectories
When you define a trajectory in the Choreo GUI, you specify waypoints with position and heading constraints — the same high-level information you'd give PathPlanner. But then Choreo does something very different: it runs a numerical trajectory optimization algorithm using your robot's physical model.
The optimizer needs to know:
- Robot mass and moment of inertia — from CAD or estimation
- Motor model — motor type (Kraken, Falcon, NEO), number per module, gear ratio
- Wheel parameters — radius and coefficient of friction
- Module positions — the (x, y) offsets of each swerve module from robot center
Given these parameters, the optimizer solves a constrained optimization problem: "Find the trajectory from start to end that minimizes total travel time, subject to the constraint that at no point does any wheel exceed its friction limit or any motor exceed its torque capability." The result is a trajectory that is genuinely time-optimal for your specific robot — not "as fast as you set maxVelocity."
Teams like 3011 (Firebears, the maintainers), 1678 (Citrus Circuits), and 4414 (HighTide) have published Choreo-based autonomous routines. The recurring reason is time-optimality: when every tenth of a second counts in a three-piece auto routine, Choreo's physics-based planner consistently finds faster paths than manually configured PathPlanner constraints. Teams that have characterized their robots thoroughly with SysId (Unit 8, Lesson 13) can feed that characterization data directly into Choreo's robot model, which means the optimization accuracy is only as good as the characterization quality.
Installation and Setup
Installing the Choreo GUI
Download Choreo from the GitHub releases page at github.com/SleipnirGroup/Choreo/releases. The application is available for Windows, macOS, and Linux. Select your robot project folder when launching — Choreo stores trajectories in deploy/choreo/ within your project directory.
Adding ChoreoLib to Your Project
// Option 1: VS Code vendor library install // W key → "Manage Vendor Libraries" → "Install new libraries (online)" // Paste: https://sleipnirgroup.github.io/Choreo/vendordep/ChoreoLib.json // (Check github.com/SleipnirGroup/Choreo for the current season's URL) // Option 2: Direct Gradle dependency repositories { maven { url "https://frcmaven.wpi.edu/release" } } dependencies { implementation "com.choreo:ChoreoLib:2025.x.x" // Replace with the current season's version from the Choreo releases page } // Verify with: ./gradlew dependencies | grep -i choreo
Configuring Your Robot in the Choreo GUI
Open Choreo and navigate to Robot Configuration. Enter the same physical parameters you'd use for SysId characterization: robot mass (kg), moment of inertia (kg·m²), module positions as (x, y) offsets from center, motor type, gear ratio, wheel radius, and coefficient of friction. These values directly determine the optimizer's physical model — inaccurate values produce trajectories that look correct in the GUI but are wrong for your actual robot.
If you've already configured RobotConfig for PathPlanner (Lesson 7), you have all the values Choreo needs. Copy robot mass, MOI, module positions, wheel radius, gear ratio, and motor type directly from your DriveConstants. Keeping these in sync between Choreo and PathPlanner ensures that both tools model the same robot. Teams that maintain a single DriveConstants file with these values and reference it from both tools avoid the common problem of divergent physical models between their two planning approaches.
The Choreo Workflow
Place start and end waypoints with positions and headings, just like PathPlanner. Choreo also supports intermediate waypoints and "pose constraints" that force the robot to pass through a specific heading at a specific position. Unlike PathPlanner, you don't set maxVelocity or maxAcceleration — the optimizer determines these from the physics. You can, however, add velocity constraints at specific waypoints if you need the robot to arrive or depart at a particular speed.
Click "Generate" in the Choreo GUI. The optimizer runs — this typically takes 1–10 seconds depending on trajectory complexity and your machine. Unlike PathPlanner path files which store spline parameters, Choreo generates a .traj file containing the fully sampled trajectory states (position, velocity, acceleration, force, torque) at a high time resolution. The trajectory is computed offline, on your development machine, not on the roboRIO.
The Choreo GUI renders the generated trajectory with a velocity colormap — sections of the path near maximum speed show one color, sections where the optimizer chose to slow down (near tight turns or heading changes) show another. This visualization tells you where the physics is constraining the robot. If the trajectory looks unexpectedly slow in a region, the physical model says there's a constraint being hit there — motor torque, wheel friction, or centripetal acceleration limit.
ChoreoLib's Java API loads .traj files from the deploy directory and provides a command that follows them using a holonomic controller. The controller interface is similar to PathPlanner's: it takes pose, chassis speeds, and outputs corrected chassis speeds. Since the trajectory states include pre-computed velocities and accelerations, the feedforward is more accurate than a spline-derived trajectory — the optimizer knew exactly what velocity the robot needed at every timestep.
Choreo uses a trigger-based event system rather than PathPlanner's named command markers. You define events by their elapsed time or distance along the trajectory, then bind robot actions to those triggers. The trigger fires automatically when the trajectory reaches that point during execution. Event triggers are defined entirely in Java code — there's no GUI event marker interface in Choreo.
ChoreoLib Java Integration
ChoreoLib provides an AutoFactory pattern for assembling autonomous routines from trajectory commands and event triggers. The interface is different from PathPlanner's AutoBuilder but serves the same purpose: wire your drivetrain's methods into the trajectory execution layer once, then build auto routines from trajectory files.
import choreo.auto.AutoFactory; import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; import choreo.trajectory.SwerveSample; public class DriveSubsystem extends SubsystemBase { private final AutoFactory m_autoFactory; public DriveSubsystem() { // AutoFactory requires the same four drivetrain methods as PathPlanner's AutoBuilder m_autoFactory = new AutoFactory( this::getPose, // Supplier<Pose2d> — current odometry pose this::resetPose, // Consumer<Pose2d> — reset to trajectory start this::followSample, // Consumer<SwerveSample> — apply trajectory state true, // flipForAlliance: mirrors trajectory for red alliance this // subsystem requirement for all auto commands ); } public AutoFactory getAutoFactory() { return m_autoFactory; } // ChoreoLib calls this every loop with the desired trajectory state. // Compute feedforward from the sample's velocity + acceleration, // then apply PID correction from odometry error. private void followSample(SwerveSample sample) { ChassisSpeeds feedforward = sample.getChassisSpeeds(); Pose2d desired = sample.getPose(); Pose2d actual = getPose(); ChassisSpeeds correction = m_choreoController.calculate(actual, desired, feedforward.vxMetersPerSecond(), feedforward.vyMetersPerSecond(), feedforward.omegaRadiansPerSecond()); driveFieldRelative(correction); } }
public Command getTwoPieceChoreoAuto() { AutoFactory factory = m_drive.getAutoFactory(); // Create a routine — a named container for this auto's trajectories and events AutoRoutine routine = factory.newRoutine("TwoPieceChoreo"); // Load trajectory files from deploy/choreo/ AutoTrajectory scorePreload = routine.trajectory("ScorePreload"); AutoTrajectory pickupOne = routine.trajectory("PickupPieceOne"); AutoTrajectory scoreOne = routine.trajectory("ScoreOne"); // Wire up event triggers — fire commands at specific trajectory events // scorePreload.atTime(0.5) fires when 0.5 s into ScorePreload trajectory scorePreload.atTime(0.5).onTrue( new SpinUpShooterCommand(m_shooter, 4000)); scorePreload.done().onTrue( Commands.sequence( Commands.parallelRace(new EjectCommand(m_shooter), Commands.waitSeconds(0.5)), pickupOne.cmd())); // transition to next trajectory after scoring pickupOne.atTime(0.3).onTrue(new IntakeUntilSensorCommand(m_intake).withTimeout(2.0)); pickupOne.done().onTrue( Commands.parallel( scoreOne.cmd(), new SpinUpShooterCommand(m_shooter, 4000))); scoreOne.done().onTrue( Commands.parallelRace(new EjectCommand(m_shooter), Commands.waitSeconds(0.5))); // Return the routine command — starts with ScorePreload, transitions via triggers return routine.cmd().beforeStarting(scorePreload.cmd()); }
PathPlanner triggers mechanism commands from named markers placed in the GUI. Choreo triggers them from code using time-based (atTime()), distance-based (atDistance()), and completion-based (done()) triggers that return Trigger objects. You bind robot commands to these triggers with .onTrue() just like any WPILib trigger. The tradeoff: PathPlanner's marker approach is easier to iterate visually (move the marker in the GUI, no code change needed), while Choreo's code-based triggers are more precise and more testable. Both approaches produce the same robot behavior — they differ only in where the timing is defined.
PathPlanner vs. Choreo: Choosing the Right Tool
| Aspect | PathPlanner | Choreo |
|---|---|---|
| Planning method | Spline-based geometry with user-configured velocity/acceleration constraints | Physics-based numerical optimization — finds time-optimal trajectory for your specific robot |
| Robot model required | RobotConfig (mass, MOI, motor specs) used for feedforward; optional but recommended | Required — the optimization cannot run without accurate mass, MOI, motor, and wheel parameters |
| Path generation speed | Instantaneous — spline evaluation is trivial | 1–10 seconds per trajectory — optimization is numerically intensive (runs offline, not on robot) |
| On-the-fly generation | Full support — pathfindToPose(), dynamic replanning (Lesson 9) |
Not supported — trajectories must be generated offline before the match |
| Event system | Named GUI markers with NamedCommands.registerCommand() |
Code-based triggers: atTime(), atDistance(), done() |
| Trajectory accuracy | Good — accurate if well-tuned and characterized | Excellent — trajectory accounts for motor torque limits and wheel slip at every point |
| Setup complexity | Lower — vendor library + AutoBuilder.configure() |
Higher — requires accurate physical characterization and different Java API |
| Community adoption | Dominant standard — used by the majority of FRC teams | Growing — favored by teams where time-optimality is critical |
| Simulation support | Good — WPILib sim integration | Good — ChoreoLib includes simulation utilities |
Which Should Your Team Use?
- Your team is new to path following — PathPlanner has more documentation and community resources
- You need on-the-fly path generation for dynamic autonomous adaptations
- Your drive team needs to iterate paths quickly between matches via the GUI
- You're mixing sensor-based commands with path following in complex command groups
- Your robot characterization is incomplete or approximate
- Most of your alliance partners and the teams you'd ask for help use PathPlanner
- Every tenth of a second matters — your game has many scoring cycles in autonomous
- Your robot is thoroughly characterized (SysId data matches actual behavior)
- Your autonomous paths are largely fixed — you don't need on-the-fly generation
- You want trajectories that are physically guaranteed to be achievable by your hardware
- Your programming team is experienced enough to work with the trigger-based Java API
- You can validate path accuracy with simulation before competition
If this is your first year seriously using path following, use PathPlanner. Its documentation is better, its community is larger, and its constraint-based approach is more forgiving of imprecise robot characterization. Choreo's time-optimality advantage is real — but it only materializes if your robot model is accurate, your characterization is done, and your team has the experience to debug trajectories that look unusual but are physically correct. A team that transitions from manual sensor-based commands to well-tuned PathPlanner paths will see more competition improvement than one that jumps to Choreo before mastering the fundamentals. Choreo is the right upgrade when PathPlanner's paths are already working well and you want to squeeze out the last 10% of autonomous speed.
🔌 System Check
Choreo's physics-based trajectories are only as good as the robot model they're optimized for. Verify before your first test:
- Robot mass and MOI values match CAD or measured values. Weigh your robot (with bumpers, battery, and game pieces if applicable) and update the Choreo robot config. An underestimated mass produces trajectories that assume the robot accelerates faster than it actually can — the robot will lag the trajectory from the first loop.
- Motor model matches your actual hardware. Confirm that the motor type, gear ratio, and wheel radius in the Choreo config exactly match your drivetrain. One wrong motor type or off-by-one gear ratio means the optimization was done for a different robot than the one you're running.
- Coefficient of friction is estimated conservatively. Choreo uses the CoF to calculate maximum lateral acceleration before wheel slip. FRC carpet CoF is typically 0.6–0.9. Start with 0.7 — a conservative estimate — rather than the theoretical maximum. If trajectories appear to push wheels at field edges, reduce CoF and regenerate.
- ChoreoLib version matches the GUI version. Check the Choreo GUI's About dialog against the version in build.gradle. Mismatched versions produce file parsing errors. The
.trajfile format changes between versions. - Test with one simple trajectory before your full auto. Run a single short trajectory (2–3 meters) at its generated velocity to confirm the robot tracks it. Compare actual path (from SmartDashboard Field2d) against the Choreo visualization. If the robot consistently undershoots curves, the physical model is optimistic — increase the safety margin on wheel friction or reduce the motor torque estimate.
Knowledge Check
1. A team generates a Choreo trajectory with an assumed robot mass of 35 kg. The actual robot with bumpers and battery weighs 52 kg. What will happen when they run this trajectory on the real robot, and why?
2. A team needs to implement an autonomous routine where the robot's scoring target position is updated by an AprilTag vision measurement 0.5 seconds into the match. Which tool handles this requirement, and why?
3. A Choreo trajectory looks faster in the GUI visualization than the equivalent PathPlanner path for the same start and end poses. On the real robot, the Choreo path completes in 2.1 seconds vs. PathPlanner's 2.5 seconds. What is the most accurate explanation for this 0.4-second improvement?
Evaluate Choreo for Your Robot and Team
- Install the Choreo GUI and add ChoreoLib to your project's build.gradle. Open Choreo and navigate to the Robot Configuration panel. Enter your robot's mass, MOI, motor type, gear ratio, wheel radius, and module positions. Compare these values to what you have in your PathPlanner
DriveConstantsfile — do they match? Resolve any discrepancies and note which values you're least confident about. - Create the same path in both PathPlanner and Choreo: a simple S-curve from (1.5, 4.0) to (14.5, 5.0) with a 180° heading rotation. Generate both trajectories. Compare the estimated durations shown in each tool. Note the difference in seconds and compute the percentage improvement Choreo claims over PathPlanner.
- Add the
AutoFactorysetup from this lesson to your drivetrain subsystem. Create a simpleAutoRoutinethat follows one Choreo trajectory and prints "Trajectory complete!" when it finishes using the.done()trigger. Deploy and run in simulation or with wheels elevated — confirm the trajectory completes and the trigger fires. - Reflect and write a paragraph (as a code comment in a new file
PathPlannerVsChoreoAnalysis.java) comparing the two tools for your specific team situation. Consider: How complete is your robot characterization? Does your game require on-the-fly path generation? Is your programming team experienced enough to work with the Choreo trigger API? What is the autonomous scoring cycle time in your game, and would 10% faster paths be meaningful? Make a recommendation and justify it. - Bonus: If you have a real robot, run both the PathPlanner and Choreo versions of the same path and measure the actual completion time with a stopwatch or by logging timestamps in
autonomousInit()and the final command'send(). How close is the measured improvement to Choreo's estimated improvement? What factors might explain any discrepancy?