| name | pedropathing |
| description | Expert guidance for creating FTC robot autonomous paths using pedroPathing v2.0. Use when building paths, configuring PathBuilder, using PathChains, adding callbacks, setting speed controls, working with Bezier curves, or debugging path following issues. |
pedroPathing FTC Path Following Skill
This skill provides expert guidance for using pedroPathing v2.0, a professional path-following library for FTC robots using Bezier curves, PIDF control, and centripetal force correction.
Overview
pedroPathing enables smooth, fast, and accurate autonomous navigation for FTC robots with:
- Omnidirectional drivetrains (mecanum, X-drive, swerve)
- Active localization (dead wheels, GoBilda Pinpoint, SparkFun OTOS, or drive encoders)
- Bezier curve-based paths with heading interpolation
- Advanced features: callbacks, speed control, path constraints
Core Concepts
Coordinate System
- X-axis: Increases moving right across field
- Y-axis: Increases moving up on field
- Heading: Radians, counterclockwise positive (0 = right, π/2 = up, π = left, 3π/2 = down)
- Units: Inches
- IMPORTANT: Always use
Math.toRadians(degrees) - all headings are in RADIANS
Path Types
- BezierLine: Straight line between two poses
- BezierCurve: Smooth curve through control points
- Typically use 3 points total (start, control, end)
- Avoid 3+ control points beyond start/end as it's difficult to track
- No degenerate paths (duplicate or improperly ordered collinear points)
PathBuilder vs Path vs PathChain
- PathBuilder: Method for combining multiple paths with independent/dependent interpolations
- Path: Single path segment
- PathChain: Result of
pathBuilder.build(), contains multiple paths with shared configuration
Basic Path Creation Pattern
val myPath = follower.pathBuilder()
.addPath(BezierLine(Pose(0.0, 0.0), Pose(24.0, 0.0)))
.setLinearHeadingInterpolation(0.0, 0.0)
.build()
follower.followPath(myPath)
PathBuilder API
Adding Paths
.addPath(path)
Heading Interpolation (choose one per path segment)
1. Linear Heading Interpolation
.setLinearHeadingInterpolation(startHeading, endHeading, endTime = 0.8)
- Rotates from start to end heading over the path
endTime (0.0-1.0): when rotation completes (0.8 works well)
- Too low = oscillations, too high = inaccurate end heading
2. Constant Heading Interpolation
.setConstantHeadingInterpolation(setHeading)
- Maintains fixed heading throughout path
- Rotates to heading first if starting with different heading
3. Tangent Heading Interpolation
.setTangentHeadingInterpolation()
- Robot orientation aligns with path's slope
- Drives tangentially to curve
4. Facing Point Interpolation
HeadingInterpolator.facingPoint()
- Robot orients toward specific coordinate while traversing
5. Piecewise Interpolation
- Combines different strategies across path segments using t-values
6. Custom Interpolation
- Implement
HeadingInterpolator interface
Reverse Option
.reversed()
Global Interpolation (across entire PathChain)
.setGlobalHeadingInterpolation(HeadingInterpolator)
Path Direction
.setReversed()
Deceleration Control
Default Deceleration (only on last path)
.setBrakingStrength(double)
- Value of 1 ≈ old ZPAM of 4
Global Deceleration (across entire PathChain)
.setGlobalDeceleration(double BrakingStrength)
.setGlobalDeceleration()
.setBrakingStart(double)
No Deceleration
.setNoDeceleration()
Path Completion Constraints
.setTValueConstraint(double)
Individual Path methods (call on Path object):
path.setTimeoutConstraint(double millis)
path.setTValueConstraint(double)
path.setVelocityConstraint(double)
path.setTranslationalConstraint(double)
path.setHeadingConstraint(double)
All 5 constraints must be met for follower.isBusy() to return false.
Path Callbacks
Parametric Callback (RECOMMENDED - distance-based)
.addParametricCallback(double percent, Runnable action)
.addParametricCallback(0.5) {
robot.intake.deploy()
}
Temporal Callback (time-based, less reliable)
.addTemporalCallback(double millis, Runnable action)
Pose Callback (position-based)
.addPoseCallback(Pose pose, Runnable action, double guess)
Custom Callback
.addPathCallback(PathCallback callback)
.addPathCallback(CallbackCondition isReady, Runnable action)
Building
.build()
Follower Methods
Following Paths
follower.followPath(pathChain)
follower.followPath(pathChain, maxPower, holdEnd)
follower.update()
Speed Control (3 methods)
1. Global Speed Limit (in Constants)
driveConstants.maxPower(0.8)
2. Per-Path Speed
follower.followPath(pathChain, 0.6, true)
3. Persistent Setting
follower.setMaxPower(0.7)
Path Completion Detection
Primary method:
!follower.isBusy()
Alternative (no waiting for correction):
follower.atParametricEnd() &&
follower.getHeadingError() < follower.getCurrentPath().getHeadingConstraint()
Troubleshooting stuck paths:
follower.getVelocity().getMagnitude() < follower.getCurrentPath().getVelocityConstraint()
follower.getAngularVelocity() < 0.055
Pose Control
follower.setStartingPose(Pose)
follower.setPose(Pose)
follower.getPose()
Pause/Resume
follower.pausePathFollowing()
follower.resumePathFollowing()
follower.breakFollowing()
TeleOp Drive
follower.setTeleOpDrive(
-gamepad1.left_stick_y,
-gamepad1.left_stick_x,
-gamepad1.right_stick_x,
true
)
Path Creation Examples
Simple Forward
val forward = follower.pathBuilder()
.addPath(BezierLine(Pose(0.0, 0.0), Pose(24.0, 0.0)))
.setLinearHeadingInterpolation(0.0, 0.0)
.build()
Curved Path with Control Point
val curve = follower.pathBuilder()
.addPath(
BezierCurve(
Pose(0.0, 0.0),
Pose(12.0, 6.0),
Pose(24.0, 0.0)
)
)
.setTangentHeadingInterpolation()
.build()
Multi-Segment PathChain
val pickupCycle = follower.pathBuilder()
.addPath(BezierLine(scorePose, pickup1Pose))
.setLinearHeadingInterpolation(scorePose.heading, pickup1Pose.heading)
.addPath(BezierLine(pickup1Pose, scorePose))
.setLinearHeadingInterpolation(pickup1Pose.heading, scorePose.heading)
.build()
Reversed Path
val backward = follower.pathBuilder()
.addPath(BezierLine(Pose(0.0, 0.0), Pose(24.0, 0.0)))
.setTangentHeadingInterpolation()
.setReversed()
.build()
Path with Callbacks
val pathWithActions = follower.pathBuilder()
.addPath(BezierLine(Pose(0.0, 0.0), Pose(40.0, 0.0)))
.setLinearHeadingInterpolation(0.0, 0.0)
.addParametricCallback(0.3) { robot.arm.extend() }
.addParametricCallback(0.8) { robot.claw.open() }
.build()
Lazy Path Generation
val dynamicPath = follower.pathBuilder()
.addPath(BezierCurve(
follower::getPose,
Pose(24.0, 24.0)
))
.setTangentHeadingInterpolation()
.build()
Integration with This Codebase
In Autonomous OpModes
val pathing = Pathing(hardwareMap)
pathing.follower.setStartingPose(Pose(x, y, heading))
class Paths(follower: Follower) {
val Path1: PathChain
val Path2: PathChain
init {
Path1 = follower.pathBuilder()
.addPath(...)
.build()
}
}
FollowPedroPath(pathing, paths.Path1).schedule()
robot.run()
With FTCLib Commands
SequentialCommandGroup(
FollowPedroPath(pathing, paths.Path1),
FollowPedroPath(pathing, paths.Path2)
).schedule()
ParallelRaceGroup(
FollowPedroPath(pathing, paths.Path1),
RunIntake(robot.intake),
AutoIndex(robot.spiceRack)
).schedule()
Constants Configuration
Edit pedroPathing/Constants.java:
.mass(23)
new PathConstraints(
0.99,
100,
1,
1
)
.maxPower(1)
.rightFrontMotorName("driveFrontRight")
.forwardEncoder_HardwareMapName("driveFrontRight")
.strafeEncoder_HardwareMapName("driveBackLeft")
.forwardPodY(-2.4)
.strafePodX(4.5)
.forwardTicksToInches(0.0019370678275907824)
.strafeTicksToInches(0.0019649286280731)
Vision Integration (AprilTags)
follower.setPose(getRobotPoseFromCamera())
val ftcPose = Pose2D(...)
val ftcStandard = PoseConverter.pose2DToPose(ftcPose, InvertedFTCCoordinates.INSTANCE)
val pedroPose = ftcStandard.getAsCoordinateSystem(PedroCoordinates.INSTANCE)
Use KalmanFilter or LowPassFilter for smoothing camera data.
Dashboard & Visualization
Panels Dashboard: 192.168.43.1:8001
- Live tuning of constants
- Field visualization
- Real-time logging/graphing
- Pre-installed in this project
Path Visualizer: https://visualizer.pedropathing.com/
Common Patterns for This Codebase
Intake While Driving
ParallelRaceGroup(
FollowPedroPath(pathing, paths.toSample),
RunIntake(robot.intake),
AutoIndex(robot.spiceRack)
)
Shoot After Path
SequentialCommandGroup(
FollowPedroPath(pathing, paths.toBasket),
ShootMotif(robot.spiceRack, motif)
).raceWith(RunShooter(robot.shooter))
Field-Relative Paths
Use Pinpoint odometry pose to create paths from current position:
val currentPose = Pose(
robot.hardware.pinpointPos().x * 39.37,
robot.hardware.pinpointPos().y * 39.37,
robot.hardware.pinpointPos().heading
)
Troubleshooting
Robot doesn't follow path smoothly
- Tune PIDF values in Constants
- Check localization accuracy
- Reduce maxPower
- Simplify Bezier curves (fewer control points)
Path never completes (stuck at end)
- Decrease timeout constraint
- Decrease T-value constraint
- Relax heading/translational constraints
- Check secondaryPIDF is tuned
Robot overshoots
- Increase braking strength
- Start braking earlier (setBrakingStart)
- Reduce velocity constraint
Heading alignment off
- Tune heading PID
- Adjust endTime in linear heading interpolation
- Check IMU calibration
Path seems jerky
- Increase number of Bezier control points
- Smooth transitions between paths
- Check for abrupt heading changes
Resources
Quick Reference
| Task | Method |
|---|
| Create path | follower.pathBuilder().addPath(...).build() |
| Follow path | follower.followPath(pathChain) |
| Check if done | !follower.isBusy() |
| Update (REQUIRED) | follower.update() in loop |
| Set speed | .followPath(path, 0.8, true) |
| Add callback | .addParametricCallback(0.5) { action } |
| Set heading | .setLinearHeadingInterpolation(start, end) |
| Reverse path | .setReversed() |
| Control braking | .setBrakingStrength(1.0) |
| Get pose | follower.getPose() |
| TeleOp drive | follower.setTeleOpDrive(y, x, turn, robotCentric) |
Remember: All headings in RADIANS. Use Math.toRadians(degrees).