FTCLib
Run a command sequence after START and clean up when it ends
FTCLib provides utilities and a command scheduler. A command describes an action; the scheduler it, advances it, and checks whether it has finished. This page uses a short test to show that lifecycle.
Installation
The example uses org.ftclib.ftclib:core:2.1.1. Follow the author's installation guide for repositories and project setup. FTCLib and the FTC SDK have independent version numbers; choose compatible releases. These APIs were checked against FTC SDK 12.0.0.
Checked 14 October 2026: 2.1.1 is still the latest release, originally published in February 2023. The repository is unarchived but release activity is slow. SolversLib is a separately maintained fork with different imports and lifecycle details; use its own installation and migration instructions if choosing that route.
Command sequence
An InstantCommand runs its action once. WaitCommand(1000) waits one second while the scheduler continues running. A SequentialCommandGroup initializes the next command when the previous one finishes.
Scheduling can execute code immediately. In FTCLib 2.1.1, schedule() calls the command's initialize(). A sequential group's first instant action can therefore run during the scheduling call. Constructing a sequence during INIT is fine; schedule this motor sequence only after START. Putting it in CommandOpMode.initialize() would request motor power during INIT.
Motor test
Configure an unloaded motor as testMotor. Save the selected language's complete file as FTCLibMotorTest.java or FTCLibMotorTest.kt under TeamCode. The Kotlin tab requires Kotlin support in the project.
package org.firstinspires.ftc.teamcode;
import com.arcrobotics.ftclib.command.Command;
import com.arcrobotics.ftclib.command.CommandScheduler;
import com.arcrobotics.ftclib.command.InstantCommand;
import com.arcrobotics.ftclib.command.SequentialCommandGroup;
import com.arcrobotics.ftclib.command.WaitCommand;
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.hardware.DcMotor;
@Autonomous(name = "FTCLib: Timed Motor Test")
public class FTCLibMotorTest extends LinearOpMode {
@Override
public void runOpMode() {
DcMotor motor = hardwareMap.get(DcMotor.class, "testMotor");
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
Command sequence = new SequentialCommandGroup(
new InstantCommand(() -> motor.setPower(0.15)),
new WaitCommand(1000),
new InstantCommand(() -> motor.setPower(0.0))
);
CommandScheduler scheduler = CommandScheduler.getInstance();
telemetry.addLine("Test unloaded, then press START.");
telemetry.update();
waitForStart();
if (!opModeIsActive()) return;
try {
scheduler.schedule(sequence);
while (opModeIsActive() && scheduler.isScheduled(sequence)) {
scheduler.run();
}
} finally {
scheduler.cancel(sequence);
motor.setPower(0.0);
scheduler.reset();
}
}
}package org.firstinspires.ftc.teamcode
import com.arcrobotics.ftclib.command.CommandScheduler
import com.arcrobotics.ftclib.command.InstantCommand
import com.arcrobotics.ftclib.command.SequentialCommandGroup
import com.arcrobotics.ftclib.command.WaitCommand
import com.qualcomm.robotcore.eventloop.opmode.Autonomous
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode
import com.qualcomm.robotcore.hardware.DcMotor
@Autonomous(name = "FTCLib: Timed Motor Test")
class FTCLibMotorTest : LinearOpMode() {
override fun runOpMode() {
val motor = hardwareMap.get(DcMotor::class.java, "testMotor")
motor.zeroPowerBehavior = DcMotor.ZeroPowerBehavior.BRAKE
val sequence = SequentialCommandGroup(
InstantCommand({ motor.power = 0.15 }),
WaitCommand(1000),
InstantCommand({ motor.power = 0.0 })
)
val scheduler = CommandScheduler.getInstance()
telemetry.addLine("Test unloaded, then press START.")
telemetry.update()
waitForStart()
if (!opModeIsActive()) return
try {
scheduler.schedule(sequence)
while (opModeIsActive() && scheduler.isScheduled(sequence)) {
scheduler.run()
}
} finally {
scheduler.cancel(sequence)
motor.power = 0.0
scheduler.reset()
}
}
}INIT maps the device and builds the sequence. START allows scheduling. The active loop advances the scheduler until the sequence finishes or STOP is requested. The finally block cancels the sequence, writes zero, and resets this practice 's scheduler even if execution exits with an exception.
The 0.15 power is an example for an unloaded test. It is not a position controller or a suitable holding command for a loaded lift. Before adding other commands, give commands that share a the same subsystem requirement so the scheduler can prevent conflicts.
Testing
Select the OpMode and press INIT: the motor should remain stopped. Press START: it should run briefly, then stop. Repeat and press STOP during the wait. Confirm that no later action restarts it. Keep the test mechanism clear and use a device whose low-power behavior you have checked.
Pure Pursuit
FTCLib's own documentation labels its Pure Pursuit implementation buggy and does not recommend it. Use the Pure Pursuit article for geometry, and compare maintained implementations such as Pedro Pathing and Road Runner for a robot route.