-
When writing your opmodes, always include the line
///@author <your-name>just above the@TeleOpor@Autonomousannotation. This provides clean documentation about who wrote the opmode, which is useful for collaborative purposes and knowing who to blame when something goes wrong. Jokes aside, it is very important when collaborating on code to sign your work.- Example:
///@author Josh Kelley @TeleOp(name = "My TeleOp") public class MyTeleOp extends TeleOpBase {...}
- Example:
-
If you are using the PlayStation controllers, use the button map below:
PlayStation Gamepad (in code) X A O B △ Y □ X For example:
gamepad1.awill be automatically mapped to the X button on the PlayStation controller. -
In TeleOp, the following inputs are consumed by the built-in drive logic inside
TeleOpBase(gamepad1only):left_stick_xleft_stick_yright_stick_x
These are forwarded to Pedro Pathing's
setTeleOpDrive(). Using these inputs elsewhere in your teleop code means that one action will control both driving and whatever else you mapped it to. If you need to change or override the drive logic, seeTeleOpBase.loopInternal().
All code is under TeamCode/src/main/java/gcsrobotics/. Here you will find the following packages:
examples: Example opmodes (as.java.mdfiles) that show how to use the framework for Autos and TeleOpscontrol: The base classes for all opmodes —OpModeBase,AutoBase, andTeleOpBase. You will only go here to change the hardware configuration inOpModeBase.javaopmode: This is where your opmodes will reside. It contains boilerplate files to get you started. This is where you spend most of your time.pedroPathing: Pedro Pathing integration —Constants.java(tune this for your robot) and theTuningopmode suitevertices: The command system. These are the building blocks you combine to create autonomous routines and teleop actionscommands: Pre-built commands likeFollowPathand the example command template. Add your own custom commands here
Setting up a new project is very easy — just follow these steps and you are ready to go!
- Fork the repo — Go to the main GitHub page for this repo and look for the Fork button. Click it and give your fork a name.
- Once forked, you will be taken to a page that looks like this one, but with your name on it.
- Clone the repo — Click the green Code button on the main page of your repository (not this one). Copy the URL shown there.
- Open Android Studio and go to the top-left three-line menu → File → New → Project from Version Control. Paste in the URL from step 3 and continue.
- It may take a few minutes to sync and build, but you'll know you're ready when you see three folders in the left-hand panel:
FtcRobotController,TeamCode, andGradle Scripts. - You're done! Your code goes under
TeamCode.
Code that is written inside functions with loop in their name(runLoop(), loop(), etc.) are automatically run inside the main loop.
You do NOT have to write a while (opModeIsActive) {...} inside of them. Doing so will break the functionality of the entire framework.
It is exceedingly rare that you would ever have to write one, if at all
This documentation covers all classes and methods relevant to writing opmodes with this framework.
OpModeBaseAutoBaseTeleOpBaseConstantsCommandCommandRunnerSeriesCommandParallelCommandInstantCommandSleepCommandAwaitCommandTimeoutCommandCancelCommandSwitchCommandButtonActionFollowPath
Purpose:
Abstract base for all OpModes (autonomous or teleop).
Handles hardware initialization and provides access to the Pedro Pathing follower and a shared INSTANCE reference used by commands.
follower— The Pedro PathingFollowerinstance, used for all path following and pose trackingINSTANCE— A static, volatile reference to the currently running opmode. Used by commands to access robot hardware viaOpModeBase.INSTANCE
Returns the robot's current X position, Y position, or heading, respectively, as reported by the Pedro Pathing localizer. Available in any Auto or TeleOp that extends this class.
Initializes all hardware. You will need to modify this method for your specific robot — declare and configure motors, servos, and any other devices here.
The following are internal methods. You won't need to interact with them directly.
Implemented by AutoBase and TeleOpBase. Runs during the init phase before waitForStart().
Implemented by AutoBase and TeleOpBase. Runs every iteration of the main loop after start.
The main entrypoint for the opmode. Calls initHardware(), creates the follower, sets INSTANCE, calls initInternal(), waits for start, then runs the main loop.
Purpose:
AutoBase is an abstract class for autonomous opmodes.
It extends OpModeBase and integrates the command system, so your entire autonomous routine is expressed as a tree of commands that run automatically.
- Extend
AutoBasein your autonomous opmode class. - Override
buildCommands()to construct your command objects. - Override
initialize()to set the robot's starting pose, build paths, and assemble theCommandRunner. - Override
runLoop()for anything you want to run continuously alongside the commands (e.g., telemetry).
Overriding means providing your own implementation of a method that is declared in a parent class:
@Override // Not strictly required but recommended for readability
protected void initialize() {
follower.setStartingPose(new Pose(0, 0, Math.toRadians(0)));
}These methods are declared in AutoBase but you define what they actually do.
Override to instantiate your Command objects.
@Override
protected void buildCommands() {
driveToGoal = new SeriesCommand(
new FollowPath(paths.toGoal),
new Shoot()
);
}Override to set the starting pose, build your Paths, and create the CommandRunner with the full sequence.
@Override
protected void initialize() {
follower.setStartingPose(new Pose(26, 128, Math.toRadians(-38)));
paths = new Paths(follower);
commandRunner = new CommandRunner(new SeriesCommand(
driveToGoal,
intakeAndScore
));
}Override to add logic that runs every loop tick during the autonomous (alongside command execution). Great for telemetry:
@Override
protected void runLoop() {
telemetry.addData("x", getX());
telemetry.addData("y", getY());
}By convention, a static class Paths inside your Auto holds all PathChain objects. Generate path code from Pedro Pathing Visualizer and paste it in here.
Purpose: Base class for teleop opmodes. Integrates Pedro Pathing's teleop drive and the command system for button-triggered actions.
commandRunner— ACommandRunnerinstance, available for use withButtonActiondriveMode— Set tofalseto temporarily disable the driver-controlled drive (e.g., when a path command takes over)
Override to set up your ButtonAction objects and any other init logic.
Override to define your main teleop loop. Call buttonAction.update(gamepad.button) here for each action.
Each loop tick, TeleOpBase automatically calls:
follower.setTeleOpDrive(
-gamepad1.left_stick_y,
-gamepad1.left_stick_x,
-gamepad1.right_stick_x,
false
);This gives you full mecanum drive out of the box. Set driveMode = false to pause it (useful when a FollowPath command is running in teleop).
Purpose: Central location for all tunable robot constants — motor names, directions, odometry pod offsets, and path constraints. This is the first file you configure when setting up for a new robot.
- Motor names: must match your hardware configuration file in the Driver Station app
rightFrontMotorName,rightRearMotorName,leftFrontMotorName,leftRearMotorName
- Motor directions:
FORWARDorREVERSEper motor maxPower: Maximum drive power (0–1)
hardwareMapName: The name of the Pinpoint device in your hardware config (default:"pinpoint")forwardPodY,strafePodX: Pod offsets in the configureddistanceUnitencoderResolution: Pod type, e.g.goBILDA_4_BAR_PODforwardEncoderDirection,strafeEncoderDirection:FORWARDorREVERSED
Limits for velocity and acceleration during path following. Tune these to prevent wheel slip.
Used internally by OpModeBase — builds and returns the configured Follower. You don't call this directly.
Add static fields for servo positions, motor targets, PID coefficients, etc.:
// eg.
public static double CLAW_CLOSED = 0.2;
public static double CLAW_OPEN = 0.8;
public static int ARM_UP = 1200;Purpose: The core interface of the Vertices command system. All commands implement this:
public interface Command {
void init(); // Called once when the command starts
void loop(); // Called every loop tick while the command is running
boolean isFinished(); // Return true to end the command
}To write a custom command, implement this interface. Access robot hardware via OpModeBase.INSTANCE:
public class MyCommand implements Command {
private OpModeBase robot = OpModeBase.INSTANCE;
public void init() { /* startup logic */ }
public void loop() { /* per-tick logic */ }
public boolean isFinished() { return /* done condition */; }
}See commands/ExampleCommand.java as a starting template.
Purpose: Manages and executes a list of active commands. It runs all added commands concurrently, removing each one as it finishes.
Constructs a runner pre-loaded with commands (used in AutoBase.initialize()).
Adds and immediately starts a command. Used by ButtonAction in teleop.
Called every loop tick — runs loop() on all active commands and removes finished ones.
Returns true when all commands have completed.
Purpose: Runs a list of commands one at a time, in order. The next command starts only after the previous one finishes.
new SeriesCommand(
new FollowPath(paths.toGoal),
new Shoot(),
new FollowPath(paths.toIntake)
)Dynamically appends more commands before the series starts.
Purpose: Runs multiple commands simultaneously. Finishes when all commands have finished.
new ParallelCommand(
new FollowPath(paths.toIntake),
new StartIntake()
)Appends more commands before execution starts.
Purpose:
Executes a single Runnable action on the first loop tick, then immediately finishes. Great for one-liners like setting a servo position.
new InstantCommand(() -> claw.setPosition(0.5))Purpose: Pauses the command chain for a specified number of milliseconds without blocking the rest of the program.
new SleepCommand(1000) // wait 1 secondUnlike Thread.sleep(), this keeps the opmode loop running while waiting.
Purpose:
Blocks the command chain until a Condition becomes true. Optionally times out after a set duration.
// Wait indefinitely
new AwaitCommand(() -> sensor.getDistance() < 5)
// Wait with a 3-second timeout
new AwaitCommand(() -> sensor.getDistance() < 5, 3000)Condition is a functional interface: boolean getValue().
Purpose: Wraps another command and forcefully finishes it if it takes longer than the specified timeout.
new TimeoutCommand(new FollowPath(paths.toGoal), 3000) // give up after 3 secondsPurpose: Wraps one or more commands and gives you external control to cancel or pause them mid-execution.
cancel()— Permanently stops all wrapped commands and marks as finisheddisable()— Temporarily pauses the wrapped commandsenable()— Resumes paused commandsisDisabled()— Returns the current disabled state
CancelCommand movingArm = new CancelCommand(new SeriesCommand(armUp, deliver));
// later:
movingArm.cancel(); // abort if something goes wrongPurpose:
Evaluates a Condition at the moment it starts, and runs either the action command (if true) or a fallback command (if false). Think of it as an if/else for commands.
// With fallback
new SwitchCommand(
() -> isRed, // condition
new DriveLeft(), // run if true
new DriveRight() // run if false
)
// Without fallback (no-op if false)
new SwitchCommand(() -> hasSample, new Shoot())- Configure
Constants.java— set motor names, directions, and pod offsets to match your hardware config - Add your hardware to
initHardware()inOpModeBase.java - Create an OpMode in the
opmode/package:- Autonomous: extend
AutoBase— useBoilerplateAuto.javaas your starting point - TeleOp: extend
TeleOpBase— useBoilerplateTeleOp.javaas your starting point
- Autonomous: extend
- Override the required abstract methods (
buildCommands,initialize,runLoop, etc.) - Build your command trees using
SeriesCommand,ParallelCommand,FollowPath, and your own customCommandclasses - Wire buttons to actions in teleop using
ButtonAction - Tune using the
Tuningopmode suite (inpedroPathing/Tuning.java)
- Each class and method is documented with purpose, usage, and examples in this file.
- For detailed code reference, browse the
.javafiles underTeamCode/src/main/java/gcsrobotics/. - For path generation, use the Pedro Pathing Visualizer.
- For FTC SDK reference, see FTC documentation.
- For advanced help, use an AI such as Claude or ChatGPT with your code attached
- If nothing else works, ask Josh.