Path Callbacks
Execute actions at specific points along paths without stopping the robot
What Are Callbacks?
Callbacks are functions that execute automatically when the robot reaches specific points along a path. Perfect for coordinating mechanism actions with movement without complex manual timing.
Progress Callbacks
Trigger Actions at Path Percentage:
Path path = new BezierLine(new Point(48, 0, Point.CARTESIAN))
.setConstantHeadingInterpolation(0)
.addCallback(0.25, () -> {
// Execute when 25% complete (12 inches)
intake.setPower(1.0);
telemetry.addLine("Starting intake");
})
.addCallback(0.5, () -> {
// Execute when 50% complete (24 inches)
arm.setTargetPosition(RAISED_POSITION);
})
.addCallback(0.75, () -> {
// Execute when 75% complete (36 inches)
telemetry.addLine("Almost there!");
});Parameters:
- progress (0.0 - 1.0): Where along path to trigger (0 = start, 0.5 = halfway, 1 = end)
- callback function: Code to execute (lambda or method reference)
- Each callback triggers exactly once when progress reaches that point
Path End Callbacks
Path path = new BezierCurve(/* ... */)
.setTangentHeadingInterpolation()
.setPathEndCallback(() -> {
// Execute when path completes (within tolerances)
intake.setPower(0);
arm.setTargetPosition(SCORE_POSITION);
telemetry.addLine("Ready to score!");
});
// Cleaner than manually checking completion in loopPractical Examples
Example 1: Intake While Driving
// Start intake as robot approaches game element
Path toPixelStack = new BezierCurve(
new Point(0, 0, Point.CARTESIAN),
new Point(0, 0, Point.CARTESIAN),
new Point(20, 10, Point.CARTESIAN),
new Point(35, 20, Point.CARTESIAN),
new Point(48, 24, Point.CARTESIAN)
).setTangentHeadingInterpolation()
.addCallback(0.7, () -> {
// 70% there - start intake early
intake.setPower(1.0);
})
.setPathEndCallback(() -> {
// Arrived - give intake time to grab
sleep(500);
intake.setPower(0);
});Example 2: Multi-Stage Mechanism Sequence
Path complexPath = new BezierLine(new Point(60, 0, Point.CARTESIAN))
.setConstantHeadingInterpolation(0)
.addCallback(0.2, () -> {
// Early: Raise arm while moving
arm.setTargetPosition(TRAVEL_HEIGHT);
})
.addCallback(0.5, () -> {
// Midway: Rotate wrist to scoring position
wrist.setPosition(SCORE_ANGLE);
})
.addCallback(0.8, () -> {
// Near end: Final arm position
arm.setTargetPosition(SCORE_HEIGHT);
})
.setPathEndCallback(() -> {
// Arrived: Release game element
claw.open();
});
// All mechanism movements coordinated with path automaticallyExample 3: Dynamic Path Adjustment
Path adaptivePath = new BezierCurve(/* ... */)
.setTangentHeadingInterpolation()
.addCallback(0.4, () -> {
// Check vision midway through path
if (aprilTagDetected()) {
// Adjust follower based on vision
Point correction = getAprilTagCorrection();
follower.adjustTargetPoint(correction);
}
})
.addCallback(0.6, () -> {
// Decision point based on game state
if (alliancePixelDetected()) {
queueNextPath(PATH_TO_ALLIANCE_PIXEL);
} else {
queueNextPath(PATH_TO_PARK);
}
});Callback Best Practices
✓ Keep Callbacks Fast
Callbacks execute during the path following loop. Long-running code (sleep, loops) will pause path following. Keep actions quick - set targets, not wait for completion.
⚠️ Avoid Blocking Operations
// BAD - blocks path following
.addCallback(0.5, () -> {
arm.setTargetPosition(1000);
while (arm.isBusy()) { sleep(10); } // BLOCKS!
})
// GOOD - non-blocking
.addCallback(0.5, () -> {
arm.setTargetPosition(1000); // Set and continue
})💡 Use Path End for Final Actions
Place final scoring/placement actions in pathEndCallback, not at high progress values like 0.99. Ensures actions happen only when robot is truly stopped.
💡 Test Callback Timing
Use telemetry in callbacks to verify they trigger at expected positions. Adjust progress values based on actual robot behavior.
Complete Example
CallbackDemo.java
@Autonomous
public class CallbackDemo extends LinearOpMode {
@Override
public void runOpMode() {
Robot robot = new Robot(hardwareMap, RobotConfig.class);
robot.initializeLocalizer(hardwareMap);
Follower follower = new Follower(hardwareMap);
waitForStart();
// Complex path with multiple callbacks
follower.followPath(
new BezierCurve(
new Point(0, 0, Point.CARTESIAN),
new Point(0, 0, Point.CARTESIAN),
new Point(24, 12, Point.CARTESIAN),
new Point(36, 36, Point.CARTESIAN),
new Point(48, 48, Point.CARTESIAN)
).setLinearHeadingInterpolation(Math.toRadians(90))
.addCallback(0.3, () -> {
telemetry.addLine("30% - Starting intake");
intake.setPower(1.0);
})
.addCallback(0.5, () -> {
telemetry.addLine("50% - Raising arm");
arm.setTargetPosition(SCORE_HEIGHT);
})
.addCallback(0.7, () -> {
telemetry.addLine("70% - Preparing to score");
intake.setPower(0);
})
.addCallback(0.9, () -> {
telemetry.addLine("90% - Almost there!");
})
.setPathEndCallback(() -> {
telemetry.addLine("100% - Scoring!");
claw.open();
})
);
// Execute path
while (!follower.isPathComplete() && opModeIsActive()) {
robot.update();
follower.update();
robot.setDrivePowers(follower.getMotorPowers());
telemetry.addData("Progress", "%.0f%%", follower.getPathProgress() * 100);
telemetry.update();
}
telemetry.addLine("Path complete - all callbacks executed!");
}
}