This is a hands-on course that teaches Java while building working swerve-drive robot code. You start from the WPILib Command Robot template and, lesson by lesson, grow it into a robot you can drive with a joystick, watch on live plots, run in simulation, steer to angles, drive exact distances, turn to a compass heading, and finally string it all together into an autonomous routine.
Each lesson introduces as few new ideas as possible and ends with a real, runnable result. Do them in order — every lesson builds on the previous one. Read the lessons carefully, not all code changes are in code sections and try out your own ideas within the lessons as well as the exercises. Take time in between each lesson to get to understand the code you've added. The concepts introduced are not explored at the same level as a real programming course. It would be to your benefit to pair this with an actual course in Java before or during.
- TalonFX motors (CTRE Phoenix 6) — drive and steering.
- Pigeon 2 gyro (CTRE Phoenix 6).
- An Xbox controller on the driver station.
- A PhotonVision coprocessor and camera (Lesson 15 only) — entirely optional; Lesson 15's own simulated cameras cover the whole lesson without one.
You do not need a real robot for most of this course — the simulation lessons
let you see everything working on your laptop. Device IDs in the examples
(0, 1, …) are placeholders; change them to match your robot.
API note: Code targets WPILib 2026 and Phoenix 6. If a method name has drifted in a newer vendordep, use the editor's autocomplete (Ctrl+Space) and the Phoenix 6 docs to find the current one.
- Read the Goal and New concepts at the top so you know what you're learning.
- Work through the Walkthrough, typing the code yourself (don't copy-paste — typing is how the syntax sticks).
- Run it (
./gradlew simulateJavafor sim, or deploy to the robot). - Do the Try it challenge before moving on. That's where the real learning is.
| What | Command (PowerShell) |
|---|---|
| Build / check it compiles | ./gradlew build |
| Desktop simulation (SimGUI) | ./gradlew simulateJava |
| Deploy to the roboRIO | ./gradlew deploy |
| Live console from the robot | ./gradlew riolog |
| # | Lesson | You'll build | Key Java ideas |
|---|---|---|---|
| 0 | Orientation | Run the template, print a message | Classes, packages, methods, the loop |
| 1 | Your first motor | Spin a drive motor with a button | Objects, fields, constructors, import |
| 2 | Joystick control | Drive the motor with a joystick | Methods, parameters, double, lambdas |
| 3 | Telemetry & plots | Plot position & velocity live | Return values, sensors, AdvantageKit logging |
| 4 | Simulation | Make it move on your laptop | simulationPeriodic, physics models |
| 5 | Steering with P control | Point the module to an angle, primed from a CANcoder at boot | if, arithmetic, error, setpoints |
| 6 | Distance & commands | Drive an exact distance | Unit conversion, commands that finish |
| 7 | Four modules | Assemble a 4-module chassis you can translate and rotate | Arrays, for loops, helper classes |
| 8 | Gyro & heading | Turn the chassis to a compass heading | Reusing patterns, extracting helpers |
| 9 | Autonomous | Compose a drive-turn-drive auto | Command composition, sequences & groups |
| 10 | Full swerve — kinematics | Drive-and-rotate at once with SwerveDriveKinematics |
Data-carrier types, indexed loops |
| 11 | Odometry & the field view | Track and draw the robot on a virtual field | Building arrays in loops, small bundle types |
| 12 | Model-based control | Onboard 1 kHz closed loop with feedforward, replacing boot-time CANcoder priming with continuous remote-sensor feedback | Config objects, control requests |
| 13 | IO layers & replay | Re-run a logged session through changed code | Interfaces, enums, subclassing, annotations |
| 14 | Pose estimator & localizer | A localization subsystem fused from pluggable pose providers | Interfaces, registries, timestamps |
| 15 | Real vision — PhotonVision | A real, replay-capable PhotonVision pose provider, plus simulated multi-camera coverage | Optional, static fields, record |
| 16 | maple-sim — a world to drive in | Physics-engine simulation with mass, tire grip, walls, and game pieces | Callbacks, anonymous class bodies |
| 17 | B-Line autos | Follow a drawn path against the fused pose, with event markers | PIDController, method refs as actions |
| 18 | Scoring elevator | A second mechanism on the same IO spine, profiled and gravity-compensated | Clamping a goal, reuse over novelty |
| 19 | A picture of the elevator | A live stick figure of the mechanism, with a second piece riding on it | Composition as attachment |
| 20 | Intake arm | A swinging arm with a roller, mounted on the elevator, gravity-compensated by angle | Two motors in one subsystem |
| 21 | Homing & limit sensors | A limit switch that tells the elevator where zero actually is | Trigger from any boolean |
| 22 | Beam breaks & the handoff | A sensor that knows you caught a game piece, driving both mechanisms at once | Trigger combinators |
| 23 | LEDs | A strip that shows what the robot knows, and a priority order you chose | Combinator methods |
| 24 | A superstructure | One named state the whole robot reads, with the illegal moves made impossible | Enums with fields and methods, exhaustive switch |
| 25 | Doing two things at once | Autos that intake, stow and aim while they drive, instead of after | Composition applied, not new syntax |
| 26 | Getting there exactly | A two-stage drive-to-pose: fast across the field, then precise onto the spot | Building a Path in code |
| 27 | Going to get something you just saw | A camera that finds a game piece, and an approach built while the robot runs | Commands.defer |
| 28 | Keeping the nose on the target | Aim assist that holds while you drive, shared by the driver and by autos | None — the point is that it needs none |
| 29 | A wheel that holds a speed | A shooter flywheel with a speedometer, and the first mechanism with no destination | None — the spine repeats |
| 30 | One battery, everything on it | Current limits chosen as a budget, and a brownout you cause on purpose | None |
| 31 | The robot tells you what's wrong | A pre-match checklist the robot runs on itself, surfaced where a human looks | None |
| 32 | Tests that catch what a plot won't | JUnit tests against your own subsystems, with the simulation running inside them | JUnit, arrange/act/assert |
| 33 | Reading a match log | Diagnose a failure that already happened, from the log alone, and prove the fix by replaying the match | None |
| 34 | Tuning your robot when build team hands it over | Measure a real machine's gains with SysIdRoutine, and find out how close the computed ones were |
None |
- Setting up the project and connecting it to GitHub —
installing WPILib, Git, and the GitHub CLI; creating the project from the
template; the daily
add/commit/pushloop. Start here before Lesson 0 if you're new to any of it. - Branches: one per lesson, merged into a
mainthat always works — branching, merging, and the day two lessons touch the same file. Reads on from the setup aside, so you can start using it from Lesson 1. Covers merge conflicts (and why "keep both" is usually the answer), pull requests, and whenrebasebeatsmerge. - Debugging in VSCode and reading stack traces — breakpoints, stepping, watches, conditional breakpoints, and how to read the error trail Java prints when something crashes. Useful from Lesson 1 onward; the worked example uses Lesson 5's P control.
- A second thread: sampling odometry faster than the robot loop —
the AdvantageKit odometry thread, and the Java it needs: threads, locks, and
waitForAll. Readable any time after Lesson 16, and genuinely optional — Lessons 17–34 neither need it nor break with it. Includes the measurement of what 250 Hz odometry actually buys, which is less than you'd think. - Commands V3: what changes when a command becomes a normal method — the command framework WPILib is designing to replace the one this course teaches, and what your own commands would look like written against it. Readable any time after Lesson 9. Deliberately ahead of this course's target — it's a design document, not a library, so none of its code runs.
The lessons are cumulative — Lesson 18 assumes the
Drivetrain, ModuleIO, Localizer and everything else that lessons 0–17
built is already sitting in your project. So you can't just open Lesson 18 and
start typing.
You can, though, have this repo build that starting point for you. The
tools/verify-lessons.sh script exists to compile-check the course's reference
code, and it does that by rolling the pristine WPILib template forward through
one lesson snapshot at a time. Point its output at a folder you want to keep and
what falls out is a complete, buildable project in exactly the state the course
would have left it in.
Roll forward to the lesson before the one you want to start on. To begin at Lesson 18, build the state as of the end of Lesson 17:
git clone https://github.com/FRC5010/BasicRobotLessons.git
cd BasicRobotLessons
VERIFY_SANDBOX=~/dev/MyRobot ./tools/verify-lessons.sh 17On Windows, run that in Git Bash (the WPILib/Git installers from
the setup aside give you one), not PowerShell, and
use a forward-slash path like ~/dev/MyRobot. The script needs bash, curl,
python3, and network access — it downloads the vendordeps each lesson requires
as it goes.
The result is a real GradleRIO project. Vendordeps are fetched and pinned, the
AdvantageKit build.gradle blocks are in place, and the files the lessons
deleted along the way are gone. Verify it before you start:
cd ~/dev/MyRobot
./gradlew build
./gradlew simulateJavaThen make it yours: git init it and push it somewhere (see
the setup aside), and set your team number in
.wpilib/wpilib_preferences.json — the template ships 5010.
Three things to know before you do this:
- Pick a folder outside this repo, and don't re-run the script against it. That folder is deleted at the start of every run. Once you've generated it, treat it as your project and leave the script alone. (The script refuses a path inside this repo for the same reason.)
- The device IDs are placeholders. The reference code uses
0,1, … for CAN IDs and camera names; change them to match your robot. - You're skipping the explanations, not just the typing. The snapshots are reference code, and the reasoning behind them lives in the lesson text. If you jump to Lesson 18, at least skim the lessons that introduced the patterns you're about to build on — the table above says what each one added.
The same trick works when you're stuck rather than ahead: generate a known-good state as of the last lesson you finished and diff it against your own project to find what drifted.
Robotis a metronome. ~50 times a second it ticks the scheduler. You almost never edit it.- Subsystems own hardware (a motor, the gyro) and expose commands — little descriptions of work to do.
- Commands say what to do. The scheduler decides when, and makes sure two commands never fight over the same motor.
RobotContaineris the wiring diagram: it creates subsystems and says which button or joystick triggers which command.
Keep that picture in mind and every lesson will fit into it.