Unit 9 · Lesson 7

PathPlanner: GUI Path Creation

PathPlanner separates path design from code. You draw on a field, set headings and constraints, place event markers — and the library turns that into a trajectory the robot follows. This lesson covers the entire workflow from first launch to a deployable path file.

By the end of this lesson, you will:

  • Install PathPlanner and configure it for your robot's dimensions and drive constants
  • Create a path with multiple waypoints, set control handle directions, and understand how they shape the spline
  • Set independent rotation targets at each waypoint and explain why this is only possible on swerve
  • Configure global path constraints (max velocity and acceleration) and per-waypoint zone overrides
  • Place named event markers along a path and connect them to WPILib commands via NamedCommands
  • Create a .auto file combining multiple paths into a single routine, and generate the auto chooser with AutoBuilder.buildAutoChooser()

What PathPlanner Is — and What It's Not

PathPlanner is two things packaged together: a desktop GUI application for drawing paths, and a Java library that makes the robot follow them. The GUI doesn't write any robot code — it produces .path and .auto files that the Java library reads at runtime. The Java library doesn't draw anything — it reads those files, generates trajectories, and provides commands that your RobotContainer schedules.

This separation is intentional and powerful. Your drive coach or strategy lead can update paths between matches without touching Java code. A mechanical team member can draw an initial path without knowing the WPILib API. The programmer configures the integration once, and from that point, adding or changing paths requires nothing but the GUI.

PathPlanner GUI
.path + .auto files
deploy/pathplanner/
AutoBuilder.loadAutoCommand()

Installation and Initial Setup

PathPlanner has two components to install separately: the GUI application and the Java library.

Installing the GUI

Download the PathPlanner application from the Microsoft Store (Windows) or the PathPlanner GitHub releases page (github.com/mjansen4857/pathplanner/releases). The app is available for Windows, macOS, and Linux. Always install the latest stable release, not a pre-release. Launch it and select your robot project folder — PathPlanner will automatically find and use the deploy/pathplanner/ directory within your project.

Adding the Java Library

build.gradle — adding PathPlanner as a vendor dependency
// Option 1: Use WPILib's "Manage Vendor Libraries" in VS Code
// W key → "Manage Vendor Libraries" → "Install new libraries (online)"
// Paste: https://3015rangerfinder.com/vendordeps/PathplannerLib.json
// (URL may update each season — check pathplanner.dev for current link)

// Option 2: Add directly to build.gradle dependencies block
dependencies {
    implementation "com.pathplanner.lib:PathplannerLib:2025.x.x"
    // Replace 2025.x.x with the current season's version from pathplanner.dev
}

// After adding, run: ./gradlew dependencies
// Verify PathPlannerLib appears in the output.
// If VS Code can't resolve PathPlanner imports, run a Gradle refresh.

Configuring Robot Settings in the GUI

Before drawing any paths, open the PathPlanner settings panel and configure your robot's physical dimensions and drive constants. PathPlanner uses these to correctly visualize the robot footprint on the field and to generate physically achievable trajectories.

  • Robot width and length — bumper-to-bumper dimensions in meters. PathPlanner shows the robot footprint at each waypoint so you can see clearances.
  • Max module speed — the top speed of your swerve modules in m/s. Used for constraint validation.
  • Drive base radius — distance from robot center to the farthest module, in meters. Used for rotation calculations.
  • Max velocity and acceleration — global defaults applied to all paths unless overridden by a zone.
💡 Set robot dimensions conservatively at first

The robot footprint visualization in PathPlanner helps you avoid driving near field elements. If your bumper-to-bumper width is 0.76 m, enter exactly 0.76 — not an estimate. An incorrect robot width means the clearance visualization lies to you, and you won't know a path clips a field element until you run it. Get these numbers from your CAD model or measure the physical robot with a tape measure before drawing the first path.

The PathPlanner Editor: Key Concepts

The interactive editor below simulates the core elements of the PathPlanner GUI. Click to place waypoints, drag control handles to shape the spline, add event markers, and watch how changes affect the generated path. This is designed to build intuition for the real application — the actual PathPlanner GUI has more features, but the same concepts apply.

PathPlanner — Two Piece Left.path
Selected Waypoint
Click a waypoint to inspect
Global Constraints
maxVelocity m/s
maxAccel m/s²
Path Info
waypoints4
est. duration
path length
event markers2
File Output
deploy/
└── pathplanner/
├── paths/
│ └── TwoPieceLeft.path
└── autos/
└── TwoPiece.auto
Click a waypoint to inspect it

Waypoint Properties in Depth

Each waypoint in PathPlanner has properties that control how the trajectory passes through or ends at that point. Understanding what each property does prevents the common mistake of drawing paths that look right in the GUI but produce unexpected robot behavior.

Property What it controls Common mistake
X, Y position The point the robot drives through. In PathPlanner's coordinate system: x is along the long field axis (meters from alliance wall), y is along the short axis. The blue alliance origin is at (0,0) in the lower left. Forgetting that PathPlanner uses the WPILib field coordinate system, which is mirrored for red alliance. If your path works for blue but drives to the wrong side for red, you need red-alliance path mirroring (AutoBuilder handles this automatically).
Heading (rotation) The direction the robot's front faces at this waypoint — completely independent of the direction of travel. Only available on holonomic (swerve) drives. The robot can be moving left while facing forward, for example. Confusing "robot heading" with "direction of travel." The heading is where the intake or shooter points — it has nothing to do with which direction the robot moves along the path. Setting all headings to 0° when you need the robot to face the scoring target at the end of the path.
Control handles The Bezier curve tangent vectors at each waypoint. Longer handles produce smoother, more gradual curves. Shorter handles produce tighter turns. The angle of the handle controls the direction the path enters and exits the waypoint. Pulling handles too short, creating a path with sharp direction reversals the robot can't physically execute at speed. If the robot slows dramatically at a waypoint but the constraint isn't set that way, the path geometry is requiring a near-180° heading change at that point.
Velocity override Forces the path to pass through this waypoint at a specific velocity instead of the optimizer-calculated one. Useful for ensuring the robot arrives at a scoring position with minimal residual speed. Setting velocity override to 0 at an intermediate waypoint — the path planner will stop the robot there, producing a "stop-start" motion that's no faster than the sequential command approach.
Constraint zone A path segment between two waypoints that overrides the global velocity/acceleration constraints. Used to slow down near field obstacles or speed up in open field sections. Not using constraint zones near field elements. A path with global maxVelocity = 5 m/s will try to pass within 30 cm of a speaker grille at 5 m/s — which leaves no margin for odometry error. Add a zone constraint to slow to 2 m/s in that region.

Event Markers: Decoupling Actions from Movement

Event markers are the most strategically important feature of PathPlanner. They let you trigger robot commands at specific points along a path, decoupling mechanism actions from drivetrain movement. Instead of "drive, then activate intake," you can "activate intake while driving through this arc," saving time on every cycle.

Each event marker has a name (a string) and a distance-from-start position. You give the marker a name in the GUI, then register the corresponding Command in your robot code using NamedCommands.registerCommand(). PathPlanner matches names at runtime and fires the command at the right moment.

🟣 On Path Trigger NamedCommands.registerCommand("startIntake", ...)

Fires a command when the robot reaches the marker's position on the path. The command runs in parallel with path following — it does not interrupt or pause the drivetrain. Use for intake start, shooter spin-up, or any action that should begin at a specific field location.

🟢 Pose Trigger Trigger within AutoBuilder

Fires a command when the robot's actual pose is within a specified radius of a field point, regardless of path progress. Useful for vision-confirmed actions: "when pose estimate shows we're within 0.3 m of the scoring position, fire the shooter." More robust than path-position markers when odometry might drift.

🟡 Time-based Event via trajectory timestamp

Fires at a specific elapsed time into the path. Less common than position-based markers because timing varies with constraint changes. When you retune the path to be faster, a time-based marker fires at a different field position. Position-based markers are more stable across constraint iterations.

🔴 Zone Trigger PathPoint zone in GUI

Runs a command for the duration of a path zone — activating when the robot enters the zone and stopping (interrupting) when it exits. Use for mechanisms that should be active over a path segment: "run the conveyor from the intake waypoint to the scoring waypoint." The command is automatically interrupted when the robot exits the zone.

RobotContainer.java — registering named commands for event markers
public RobotContainer() {
    // Register BEFORE configuring AutoBuilder.
    // Names must exactly match the marker names in the PathPlanner GUI.
    // Case-sensitive. "StartIntake" != "startIntake".

    NamedCommands.registerCommand("startIntake",
        new IntakeUntilSensorCommand(m_intake).withTimeout(2.0));

    NamedCommands.registerCommand("spinUpShooter",
        Commands.runOnce(() -> m_shooter.setTargetRPM(4000), m_shooter));

    NamedCommands.registerCommand("shoot",
        Commands.parallelRace(
            new EjectCommand(m_shooter),
            Commands.waitSeconds(0.5)));

    NamedCommands.registerCommand("stopIntake",
        Commands.runOnce(() -> m_intake.stop(), m_intake));

    // Configure AutoBuilder AFTER registering named commands.
    configureAutoBuilder();
}
💡 Register NamedCommands before configureAutoBuilder()

The order matters. NamedCommands.registerCommand() populates a static map that PathPlanner reads when AutoBuilder loads .auto files and resolves marker references. If you call configureAutoBuilder() first and then register commands, the .auto file loading happens before the commands are registered — the event markers fire nothing. Always register all named commands at the very top of the RobotContainer constructor, before any AutoBuilder calls.

AutoBuilder.configure(): The Integration Point

AutoBuilder.configure() is the bridge between your drivetrain subsystem and the PathPlanner library. It tells PathPlanner which methods to call on every loop to get the current pose, set chassis speeds, and access robot configuration. You call it once, in the drivetrain subsystem constructor or in RobotContainer, and from that point every PathPlanner command knows how to drive your specific robot.

DriveSubsystem.java or RobotContainer.java — AutoBuilder.configure()
private void configureAutoBuilder() {
    AutoBuilder.configure(
        // 1. Supplier of current robot pose (from odometry)
        m_drive::getPose,

        // 2. Consumer to reset pose (called at start of each auto)
        m_drive::resetPose,

        // 3. Supplier of current robot-relative ChassisSpeeds
        m_drive::getRobotRelativeSpeeds,

        // 4. Consumer of field-relative ChassisSpeeds (to apply to modules)
        (speeds, feedforwards) -> m_drive.driveFieldRelative(speeds),

        // 5. Holonomic path-following controller configuration
        new PPHolonomicDriveController(
            new PIDConstants(5.0, 0.0, 0.0),  // translation PID
            new PIDConstants(3.0, 0.0, 0.0)   // rotation PID
        ),

        // 6. Robot configuration (drive base geometry + motor specs)
        new RobotConfig(
            DriveConstants.ROBOT_MASS_KG,
            DriveConstants.ROBOT_MOI,
            new ModuleConfig(
                DriveConstants.WHEEL_RADIUS_M,
                DriveConstants.MAX_MODULE_SPEED_MPS,
                DriveConstants.WHEEL_COF,
                DCMotor.getKrakenX60(1),
                DriveConstants.DRIVE_GEAR_RATIO,
                1  // motors per module
            ),
            DriveConstants.MODULE_POSITIONS  // Translation2d[] of each module
        ),

        // 7. Path flip supplier: returns true if we're on red alliance
        // (PathPlanner mirrors paths for red alliance automatically)
        () -> {
            var alliance = DriverStation.getAlliance();
            return alliance.isPresent() &&
                   alliance.get() == DriverStation.Alliance.Red;
        },
        m_drive  // the subsystem requirement for all PathPlanner commands
    );
}
🔍 The path flip lambda is critical for full-field play

FRC fields are symmetric — both alliances play on the same field from opposite ends. PathPlanner paths are drawn for blue alliance. When the robot is on red, AutoBuilder mirrors the path across the field's center line automatically — if the path flip lambda returns true when appropriate. If you skip this lambda (return false always), your red-alliance auto will drive to the blue alliance side of the field. Always implement the alliance check using DriverStation.getAlliance(), and always test that both alliance paths end up at the correct field position.

Paths vs. Autos: The Two File Types

PathPlanner distinguishes between paths and autos. Understanding the difference is what lets you build reusable, composable autonomous routines.

A .path file describes a single continuous trajectory: a sequence of waypoints with headings, control handles, constraints, and event markers. A path does one physical movement. A path is reusable — you can use "DriveToSpeaker" in multiple autos.

A .auto file is a routine: an ordered sequence of paths and standalone commands assembled in the PathPlanner GUI's auto editor. An auto says "run path A, then run command 'shoot', then run path B." An auto file corresponds to a single SendableChooser option — one selectable strategy for the drive team.

RobotContainer.java — auto chooser with AutoBuilder
// After configureAutoBuilder() has been called:
private final SendableChooser<Command> m_autoChooser =
    AutoBuilder.buildAutoChooser();
// ↑ Scans deploy/pathplanner/autos/ for every .auto file.
//   Each .auto file becomes one dropdown option.
//   No addOption() calls needed — add a .auto file → it appears automatically.
//   New option naming: use the file name (e.g. "TwoPieceLeft.auto" → "TwoPieceLeft")

public RobotContainer() {
    registerNamedCommands();    // 1. Register named commands first
    configureAutoBuilder();      // 2. Configure AutoBuilder second
    // m_autoChooser is built with buildAutoChooser() as a field initializer,
    // which runs after the constructor body — but after AutoBuilder.configure()
    // is called via configureAutoBuilder(), so the order is correct.

    SmartDashboard.putData("Auto Mode", m_autoChooser);
    configureButtonBindings();
}

public Command getAutonomousCommand() {
    return m_autoChooser.getSelected();
}

// You can also load a specific auto by name without the chooser:
Command specificAuto = AutoBuilder.buildAuto("TwoPieceLeft");
// Useful for testing a single routine without showing all options on dashboard.
🔍 The .auto file names are what the drive team sees

When buildAutoChooser() scans the deploy directory, it uses the file names as the dropdown option labels. "TwoPieceLeft.auto" appears as "TwoPieceLeft" in the chooser. This means your file naming convention is your drive team UI. Name files descriptively: "TwoPieceLeft", "ScoreAndDefenseCenter", "TimedFallback" — not "auto1", "test", "final_v2". I have seen teams at competition frantically trying to remember which numbered file corresponds to which strategy because someone named their autos "path1" through "path7." Your drive coach is reading under time pressure. Make the names obvious.

🔌 System Check

⚙️ Before Running Your First PathPlanner Path

Work through this checklist before enabling any PathPlanner path on the real robot:

  • PathPlanner library version matches the GUI version. Open the GUI → Help → About. Check the version number. Your Java library's version in build.gradle must match. Mismatched versions produce silent errors where the library can't read the path file format.
  • .path and .auto files are in the correct directory. Confirm that files are in src/main/deploy/pathplanner/paths/ and src/main/deploy/pathplanner/autos/. The deploy task copies these to the roboRIO. If you saved to the wrong folder, buildAuto() will throw a FileNotFoundException at runtime.
  • NamedCommands registration happens before AutoBuilder.configure(). Add a temporary print in each registered command's initialize(). Enable auto and verify you see the print at the expected path position. If a marker fires nothing and there's no warning, the name has a typo — check case sensitivity.
  • Path flip lambda is implemented and tested for both alliances. Enable the robot on a practice DS set to red alliance. Confirm the path drives to the red-alliance starting zone, not the blue-alliance starting zone. The easiest test: check SmartDashboard for the path visualization — it should show the flipped version for red.
  • First path is short and slow. Your first real path test should cover at most 2 meters at 50% of your planned max velocity. Confirm the robot roughly tracks the path before opening up to full constraints. A controller gain that's too high is much easier to diagnose at slow speed than at 4 m/s.

Knowledge Check

1. A team draws a PathPlanner path for blue alliance, then runs it on red alliance with the path flip lambda always returning false. The robot drives to the correct position on the field relative to blue alliance's side. What will happen when the same path runs in a real match on red alliance?

  • A PathPlanner automatically detects the alliance color and flips the path regardless of the lambda
  • B The robot will drive to the blue alliance side of the field — the path is not flipped, so it targets the mirror-wrong coordinates for red alliance — potentially colliding with field elements or alliance robots on the opposite side
  • C The robot will drive to the correct red alliance position because PathPlanner coordinates are always field-relative
  • D The lambda only affects heading, not position, so translation will still be correct

2. A team's event marker named "spinUpShooter" in the PathPlanner GUI fires at the correct path position, but the shooter doesn't spin up. The path follows correctly. The shooter subsystem works fine in teleop. What is the most likely cause?

  • A The shooter subsystem is not available during autonomous — it must be enabled separately
  • B The NamedCommands.registerCommand() call for "spinUpShooter" was never added to the robot code — the GUI marker has a name, but there's no corresponding command registered to match it, so nothing fires
  • C Event markers can only fire commands that were defined in the PathPlanner GUI, not Java command classes
  • D The path is running too fast for the event marker to trigger — reduce maxVelocity to make the marker fire correctly

3. A team wants to run the same "DriveToSpeaker" path in two different autos: a two-piece routine and a one-piece-plus-defense routine. What is the correct PathPlanner approach?

  • A Create two separate .path files with identical content — one for each auto — so they can be maintained independently
  • B Create one DriveToSpeaker.path file and reference it in both .auto files — paths are reusable across multiple autos, and updating the single path file updates both routines simultaneously
  • C Paths cannot be shared between autos — each .auto file must contain its own embedded path data
  • D Use AutoBuilder.buildAuto("DriveToSpeaker") twice in RobotContainer and add each to the chooser manually
💪 Practice Prompt

Create and Deploy Your First PathPlanner Path

  1. Install the PathPlanner GUI and add the PathPlannerLib vendor dependency to your project. Open PathPlanner and configure your robot's width, length, and drive constants. Verify the field image loads correctly and the robot footprint is the right size.
  2. Create a new path named "TestPath". Place a starting waypoint at approximately (1.5, 4.0) — a typical center-field starting position — and an ending waypoint 3 meters forward at (4.5, 4.0). Set the rotation at the starting waypoint to 0° and the rotation at the ending waypoint to 180° (robot turns to face backward as it arrives). Observe how the heading constraint changes the robot rotation visualization along the path without affecting the direction of travel.
  3. Create a second path named "ReturnPath" from (4.5, 4.0) back to (1.5, 4.0) with appropriate headings. Create a .auto file named "SimpleTest" that runs TestPath, then a command named "stopAndWait" (0.5 seconds), then ReturnPath. Register NamedCommands.registerCommand("stopAndWait", Commands.waitSeconds(0.5)) in your RobotContainer constructor.
  4. Call AutoBuilder.configure() with your drivetrain's methods. Verify the build succeeds. Open SmartDashboard and confirm "Auto Mode" dropdown shows "SimpleTest". Enable autonomous mode and confirm the robot attempts to follow the path (elevated on blocks for initial testing).
  5. Bonus: Add an event marker to TestPath at 1.5 meters from the start named "midpointMarker". Register a command that prints "Midpoint reached!" to SmartDashboard. Deploy and run the path. Verify the message appears at the correct field position by checking the SmartDashboard timestamp against the path's estimated duration.