Intermediate · 3 of 720 minintermediateIncredibots playbook
The FollowPathCommand
Writing a wrapper class to bridge FTCLib's Command Scheduler and PedroPathing.
By the end you can
- Understand why a wrapper class is necessary.
- Review the FollowPathCommand.java implementation from the team's repository.
- Understand how isFinished() works with PedroPathing tolerances and stall detection.
On this page · 3 sections
PedroPathing is incredible at driving the robot, but it doesn't know anything about FTCLib Commands. If you want to use a SequentialCommandGroup to drive to a position and then run your LaunchBallsCommand, FTCLib needs a way to know when PedroPathing is finished driving!
To solve this, our team uses a Wrapper Class. A wrapper class is a standard FTCLib CommandBase that simply "wraps" around PedroPathing's functions.
Writing the Wrapper
Let's look at FollowPathCommand.java directly from the Unearthed-Alberta repository.
- Open your project in Android Studio.
- Navigate to
TeamCode > src > main > java > org > firstinspires > ftc > teamcode > commands. - Create a new Java Class named
FollowPathCommand. - Copy the code below.
package org.firstinspires.ftc.teamcode.commands;
import android.util.Log;
import com.arcrobotics.ftclib.command.CommandBase;
import com.pedropathing.follower.Follower;
import com.pedropathing.paths.PathChain;
import com.qualcomm.robotcore.util.ElapsedTime;
public class FollowPathCommand extends CommandBase {
private final Follower follower;
private final PathChain path;
private final boolean holdEnd;
// Stall detection variables
private ElapsedTime stallTimer = new ElapsedTime();
private final ElapsedTime initializationTimer = new ElapsedTime();
private static final double STALL_VELOCITY_THRESHOLD = 0.5; // inches per second
private static final double STALL_TIMEOUT = 500; // milliseconds before giving up
private static final double END_TOLERANCE = 3.0; // Finish when 3 inches away
private static final double MINIMUM_RUN_TIME = 500.0; // Ensure at least 500ms of run time
private boolean initialized = false;
public FollowPathCommand(Follower follower, PathChain path, boolean holdEnd) {
this.follower = follower;
this.path = path;
this.holdEnd = holdEnd;
this.initialized = false;
}
@Override
public void initialize() {
// 1. Tell Pedro to start driving!
follower.followPath(path, holdEnd);
// 2. Force an update immediately so the state locks in
follower.update();
}
@Override
public void execute() {
if (!initialized) {
initializationTimer.reset();
stallTimer.reset();
initialized = true;
}
double currentVelocity = follower.getVelocity().getMagnitude();
// 3. Stall Detection: If we are moving faster than the threshold, reset the timer
if (currentVelocity > STALL_VELOCITY_THRESHOLD) {
stallTimer.reset();
}
}
@Override
public boolean isFinished() {
// SAFETY: Do not allow the command to finish if it hasn't been running for 500ms.
// This prevents race conditions where isBusy() is checked before the path latches.
if (initializationTimer.milliseconds() < MINIMUM_RUN_TIME) {
return false;
}
// 4. Finish Logic
double distanceRemaining = follower.getCurrentPath().getDistanceRemaining();
boolean closeEnough = distanceRemaining < END_TOLERANCE;
// Finish if Pedro says done, OR we are close enough, OR we stalled (hit a wall)
return !follower.isBusy() || closeEnough || stallTimer.milliseconds() > STALL_TIMEOUT;
}
@Override
public void end(boolean interrupted) {
if (stallTimer.milliseconds() > STALL_TIMEOUT) {
Log.w("Follow Path Command", "Path Stalled! Moving to next command.");
follower.breakFollowing(); // Stop the motors immediately
}
initialized = false;
}
}Breaking Down the Magic
Why is this file so complicated? Because the real world is messy!
follower.followPath(): This is called insideinitialize()to start the movement.- The "Latch" Bug: Sometimes, if you ask PedroPathing "Are you busy?" a millisecond after telling it to follow a path, it says "No" because it hasn't loaded the path yet! We fix this by calling
follower.update()immediately, and enforcing aMINIMUM_RUN_TIMEof 500ms before it's allowed to finish. - Stall Detection: If your robot drives into a wall or gets stuck on a game piece, PedroPathing will sit there forever trying to reach the end of the path, ruining your Autonomous. The
stallTimerconstantly checks your velocity. If your robot stops moving (< 0.5inches/sec) for more than500ms, it cancels the path and moves on to the next command! END_TOLERANCE: Just like we learned in the Advanced Control lesson, waiting for exact perfection takes too long. If the robot is within 3 inches of the target, we declare the path "finished" so the shooter can start spinning up early, saving precious time!
Check Your Understanding
Check yourself
Why does the FollowPathCommand implement stall detection using the robot's velocity?
Finished this lesson?
Mark it complete to track your progress.