Skip to main content

Lesson 15.3: Creating Pedro 3 Paths with Bézier Curves


Why Curves Beat Stop–Turn–Drive

A stop–turn–drive routine throws away momentum at every corner. A smooth Bézier path can carry speed through a direction change while Pedro continuously corrects tracking error. Actual performance still depends on localization, tuning, curvature, and endpoint constraints.

Sharp Waypoints vs Bezier Path
field obstacletraffic zoneStartScoreWaypoint route: same anchors, hard stops at cornersBezier route: same anchors, smooth obstacle-aware curvature

Pedro 3's Paths API

Pedro 3 replaced the old Point, BezierLine, BezierCurve, PathChain, and follower.pathBuilder() lesson API with static helpers from com.pedropathing.api.Paths:

import static com.pedropathing.api.Paths.*;
  • line(start, end) creates a straight Path between two Poses.
  • curve(start, control..., end) creates a Bézier Path.
  • path(first, second, ...) combines paths into one compound Path.
  • through(pose...) creates a Bézier curve that passes through all supplied poses.

A Bézier curve passes through its first and last poses. Intermediate poses are control poses: they pull the curve without normally lying on it.

Match the official visualizer

The embedded Pedro Pathing Visualizer generates the same PoseFactory, line(), curve(), and path() API shown here. Prefer returning paths from methods, which is also the format generated by the visualizer.


Heading Is Separate from Geometry

The curve describes where the robot travels. Heading interpolation describes which way it faces while moving.

line(startPose, scorePose).linear(startPose, scorePose);
curve(scorePose, controlPose, pickupPose).constant(pickupPose);

Common Pedro 3 heading choices include:

  • .linear(startPose, endPose): smoothly rotate between two headings.
  • .constant(pose): keep one heading for the path.
  • .tangent(): face along the direction of travel.
  • .facingPoint(targetPose): continuously face a field point.

Compound Paths Replace PathChain

Use path() when several geometric segments should behave as one path:

private Path scoringRoutine() {
return path(
line(startPose, scorePose).linear(startPose, scorePose),
curve(scorePose, controlPose, pickupPose)
.linear(scorePose, pickupPose)
);
}

The follower receives the compound path through the same Pedro 3 method as any other path:

follower.follow(scoringRoutine());

With Ivy, use follow(follower, scoringRoutine()) inside a command group instead.


Annotated Code

package org.firstinspires.ftc.teamcode;

import static com.pedropathing.api.Paths.*;
import static com.pedropathing.ivy.Scheduler.schedule;
import static com.pedropathing.ivy.pedro.PedroCommands.follow;

import com.pedropathing.api.PoseFactory;
import com.pedropathing.follower.Follower;
import com.pedropathing.ivy.Scheduler;
import com.pedropathing.math.Pose;
import com.pedropathing.paths.Path;
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import org.firstinspires.ftc.teamcode.pedro.Constants;

@Autonomous(name="Pedro_3_Bezier")
public class Pedro3Bezier extends LinearOpMode {

private Follower follower;
private final PoseFactory poseFactory = PoseFactory.degrees();

private final Pose startPose = poseFactory.of(12, 12, 0);
private final Pose scorePose = poseFactory.of(108, 12, 0);
private final Pose controlPose = poseFactory.of(120, 60, 45);
private final Pose pickupPose = poseFactory.of(108, 108, 90);

private Path scoringRoutine() {
return path(
line(startPose, scorePose).linear(startPose, scorePose),
curve(scorePose, controlPose, pickupPose)
.linear(scorePose, pickupPose)
);
}

@Override
public void runOpMode() {
Scheduler.reset();
follower = Constants.create(hardwareMap);
follower.setPose(startPose);
follower.update();

waitForStart();
if (isStopRequested()) return;

schedule(follow(follower, scoringRoutine()));

while (opModeIsActive()) {
follower.update();
Scheduler.execute();

telemetry.addData("X", "%.1f", follower.pose().x());
telemetry.addData("Y", "%.1f", follower.pose().y());
telemetry.addData("Segment", follower.pathIndex());
telemetry.update();
}
}
}

Fill-in-the-Blank Practice

  1. Pedro 3 creates a straight path with __________(startPose, endPose).
  2. Multiple paths become one compound path with __________(first, second).
  3. A path's geometry and heading __________ are configured separately.
Show answers
  1. line
  2. path
  3. interpolation

Simulator Challenge

Create a compound Path named scoringRoutine. Start at (0, 0, 0°), drive in a straight line to (60, 0, 0°), then follow a quadratic Bézier curve through the control pose (80, 30, 45°) to (60, 60, 90°). Use PoseFactory.degrees() and the Pedro 3 path(line(...), curve(...)) API.

The path preview and robot animation remain on the DECODE field. The embedded official visualizer can be opened beside the editor for creating coordinates and comparing its generated Java.

Telemark Unit 15.3 Simulator
Loads the lesson-specific Telemark advanced autonomous challenge with incomplete starter code for students to complete.
The simulator editor contains the starter code; use the answer below only after trying the challenge.
Show answer
PoseFactory poseFactory = PoseFactory.degrees();

Pose start = poseFactory.of(0, 0, 0);
Pose join = poseFactory.of(60, 0, 0);
Pose control = poseFactory.of(80, 30, 45);
Pose end = poseFactory.of(60, 60, 90);

Path scoringRoutine = path(
line(start, join).linear(start, join),
curve(join, control, end).linear(join, end)
);

Ready to move on?

Only mark complete if you genuinely understand the material. Your progress will be saved in this browser.

Stuck on this lesson?

Ask about anything on this page. It can see which lesson you have open and which part you are reading.