Introduction to AdvantageKit
SmartDashboard tells you what your robot is doing right now. AdvantageKit tells you what your robot did during every loop of every match you've ever run — and lets you re-run that match's exact logic offline, after the fact, as many times as you need. This lesson shows you what AdvantageKit is, how its Logger works, and why logging structured inputs before using them is what makes everything else in Unit 11 possible.
By the end of this lesson, you will:
- Explain the difference between SmartDashboard-style logging and AdvantageKit's structured input logging
- Add AdvantageKit to a robot project as a vendor dependency and configure
Robot.javato start the Logger - Use
Logger.recordOutput()to log any value — double, boolean,Pose2d, enum, or custom type - Understand why
Logger.processInputs()must be called immediately afterupdateInputs()and what it does in each mode - Describe AdvantageKit's three output backends: real robot (WPILOG file), simulation (console/NT), and replay (log overwrite)
- Locate and download a
.wpilogfile from the roboRIO after a match
What AdvantageKit Is
AdvantageKit is an open-source logging framework for FRC robots, created and maintained by Team 6328 (Mechanical Advantage). It runs on top of WPILib and provides two capabilities that WPILib's built-in logging does not: structured input capture and deterministic replay.
Structured input capture means AdvantageKit records every sensor input that enters the robot's subsystems — velocity readings, encoder positions, current draws, motor temperatures — at every loop iteration, in a format that ties each value to the specific subsystem and IO layer that produced it. Not just "shooter velocity was 3450 RPM at some point" but "in loop 1,247 of the match, Shooter/velocityRpm was 3450.2 and the subsystem considered itself at-speed, which is why the eject command started."
Deterministic replay means you can take that log file and re-run your robot code's logic against it, offline, on a laptop, without any hardware — and get the exact same subsystem outputs that the robot produced during the match. If you add a System.out.println or a new SmartDashboard value after the fact and replay, you can see what that value would have been during the match. This is the most powerful debugging capability available to an FRC team.
After a frustrating match where the robot missed its last shot, the team downloads the .wpilog file from the roboRIO, opens it in AdvantageScope, and replays the autonomous period. They add a new Logger.recordOutput("Shooter/pidError", error) line to the shooter subsystem and replay again — the log now shows them the PID error at every loop during the sequence. The robot's shooter was still spinning down when the shot was triggered. It took 20 minutes to find, with no robot present, at 11pm. Without AdvantageKit, this would have required reproducing the exact scenario on the physical robot the next morning — if it was even reproducible.
AdvantageKit vs. SmartDashboard
| Capability | SmartDashboard / Shuffleboard | AdvantageKit Logger |
|---|---|---|
| Live monitoring | Yes — values visible in real time during teleop/auto | Yes — also visible live via NetworkTables when configured |
| Post-match review | Limited — only what Driver Station logs capture | Full — every logged value at every loop since match start |
| Historical replay | No — values are not stored in a format that supports replay | Yes — entire subsystem logic re-runs on historical inputs |
| Input logging | Manual — programmer decides what to log each time | Automatic for IO layer inputs via processInputs() |
| Log format | Driver Station log (.dsevents) — limited data types | WPILOG (.wpilog) — typed, timestamped, all WPILib types |
| Offline analysis | Very limited | Full AdvantageScope visualization and replay |
| Replay-based debugging | Not possible | Core capability — add new log statements and replay to see what they would have shown |
How Data Flows Through AdvantageKit
Select a mode below to see how data travels from your subsystem's IO layer through the Logger to its destination. The path changes depending on whether the robot is running on real hardware, in simulation, or in replay mode.
Logger.processInputs() writes all inputs to the WPILOG file on the roboRIO's USB drive. Logger.recordOutput() calls append computed values (PID error, target RPM, state enum) to the same file. After the match, the WPILOG file can be downloaded and opened in AdvantageScope.
The Three Logger Backends
Writes a .wpilog binary log file to a USB drive plugged into the roboRIO, or to the roboRIO's internal storage. Captures every processInputs() call and every recordOutput() call at full loop rate (50 Hz). File grows at roughly 1–5 MB per minute depending on what's logged.
In simulation mode, logged values are published to NetworkTables and visible in SmartDashboard or AdvantageScope in real time. No file is written by default in sim (configurable). This lets you observe all the same logged values during sim development that you'd see replaying a real match log.
In replay mode, Logger.processInputs() reads inputs from the log file instead of from hardware. The subsystem logic runs on historical data. Computed outputs — everything from recordOutput() — are written to a new log file. Open both in AdvantageScope to compare original vs. new computed values.
Installation and Setup
Adding AdvantageKit to Your Project
Download the AdvantageKit vendordep JSON from github.com/Mechanical-Advantage/AdvantageKit/releases. In VS Code: W key → "Manage Vendor Libraries" → "Install new libraries (online)" and paste the URL. Alternatively, place the JSON file directly in your project's vendordeps/ directory and run ./gradlew dependencies to confirm it resolved.
Configuring Robot.java
AdvantageKit requires the Logger to be configured and started before any subsystems are created. The configuration happens in Robot.java before new RobotContainer() is called.
import org.littletonrobotics.junction.Logger; import org.littletonrobotics.junction.LoggedRobot; import org.littletonrobotics.junction.wpilog.WPILOGWriter; import org.littletonrobotics.junction.wpilog.WPILOGReader; import org.littletonrobotics.junction.networktables.NT4Publisher; // Extend LoggedRobot instead of TimedRobot // LoggedRobot is a drop-in replacement that handles Logger tick timing public class Robot extends LoggedRobot { private RobotContainer m_robotContainer; @Override public void robotInit() { // ── Logger configuration: MUST happen before RobotContainer is created ──── // Metadata is stored in the log file for later identification Logger.recordMetadata("ProjectName", BuildConstants.MAVEN_NAME); Logger.recordMetadata("GitSHA", BuildConstants.GIT_SHA); Logger.recordMetadata("BuildDate", BuildConstants.BUILD_DATE); if (RobotBase.isReal()) { // On real robot: write to USB drive (preferred) or roboRIO internal storage Logger.addDataReceiver(new WPILOGWriter("/U/logs")); // USB drive path Logger.addDataReceiver(new NT4Publisher()); // also publish to NT for live view } else if (Constants.getMode() == Constants.Mode.REPLAY) { // Replay mode: read from a previous log file, write a new one with // computed outputs from the re-run. The input log path is configured // in Constants.java. Logger.setReplaySource(new WPILOGReader(Constants.REPLAY_LOG_PATH)); Logger.addDataReceiver(new WPILOGWriter(Constants.REPLAY_OUTPUT_PATH)); } else { // Simulation: publish to NT only (no file) Logger.addDataReceiver(new NT4Publisher()); } // Start the Logger — must be called before any subsystem is constructed Logger.start(); // Now safe to create subsystems (which reference Logger in their periodic()) m_robotContainer = new RobotContainer(); } @Override public void robotPeriodic() { CommandScheduler.getInstance().run(); } }
AdvantageKit includes a Gradle plugin that generates a BuildConstants.java file at compile time containing the current Git SHA, branch name, build date, and project name. When logged as metadata at robot startup, every .wpilog file carries an exact record of which version of the code produced it. After a competition, you can correlate a log file to a specific commit in your repository — which is essential when debugging whether a problem was introduced between matches. To enable it, add id "org.littletonrobotics.junction.toolsPlugin" to the plugins block in your build.gradle.
The Logger API: recordOutput and processInputs
Two Logger methods cover nearly all of the API you'll use day to day.
Logger.recordOutput() — log any computed value
Call this anywhere in periodic() to record any value. The first argument is the log key — a hierarchical string using "/" as a separator. The key becomes the path in AdvantageScope's log viewer. Good key naming conventions make logs searchable and self-documenting.
Velocity RPM, PID error, voltage commands, distance estimates
isAtSpeed, isHomed, beamBreak state, motor connected
Swerve module states, multi-value sensor arrays
Robot field position — renders as robot icon on Field2d in AdvantageScope
Array of poses — trajectories, vision targets, multiple robots
State machine enum values — renders as readable string in log
@Override public void periodic() { // 1. Read inputs from IO // 2. processInputs — logs inputs AND overwrites them in replay mode m_io.updateInputs(m_inputs); Logger.processInputs("Shooter", m_inputs); // 3. Run control logic using m_inputs (now safe to read) // 4. Record ALL computed values — these are what replay adds insight to // Scalars Logger.recordOutput("Shooter/TargetRpm", m_targetRpm); Logger.recordOutput("Shooter/VelocityRpm", m_inputs.velocityRpm); Logger.recordOutput("Shooter/VelocityError", m_targetRpm - m_inputs.velocityRpm); Logger.recordOutput("Shooter/PIDOutput", m_pid.calculate(m_inputs.velocityRpm)); // Booleans Logger.recordOutput("Shooter/AtTargetSpeed", isAtTargetSpeed()); Logger.recordOutput("Shooter/MotorConnected", m_inputs.motorConnected); // State enum (logs as string: "IDLE", "SPINNING_UP", "READY", "EJECTING") Logger.recordOutput("Shooter/State", m_currentState); }
Logger.processInputs() — the critical call
As described in Lesson 1, Logger.processInputs(prefix, inputs) does two things simultaneously:
- In real robot / simulation mode: logs the current state of every field in the inputs object to the log file / NetworkTables under the given prefix. This is how sensor readings become part of the permanent match record.
- In replay mode: reads the values for these fields from the log file at the current timestamp and writes them into the inputs object. The subsystem then runs its logic on historical data from the match.
This dual behavior is what makes replay deterministic. The subsystem code doesn't need to know which mode it's in — it just calls processInputs() and the framework handles the rest.
AdvantageKit provides an @AutoLog annotation that automatically generates the code needed to make a class's fields loggable with processInputs(). Instead of manually implementing the logging interface, annotate your inputs class with @AutoLogOutput (for output recording) or use the generated ShooterIOInputsAutoLogged class that AdvantageKit creates at compile time when @AutoLog is applied to your ShooterIOInputs inner class. This removes the need to write custom serialization code for each inputs type. Refer to the AdvantageKit documentation for the current annotation and generated class pattern — the details change slightly between versions.
Getting Log Files Off the Robot
After a match, the .wpilog file is on the roboRIO (or the USB drive plugged into it). There are three ways to retrieve it:
- USB drive (preferred): Plug a USB drive into the roboRIO before matches. AdvantageKit writes logs directly to
/U/logs/on the drive. After the match, pull the drive and plug it into a laptop. Logs are immediately accessible. This is the fastest method and doesn't require network access. - FTP over USB tether: Connect the laptop to the roboRIO via USB-B cable. Use an FTP client (FileZilla, or the built-in sftp in Terminal/PowerShell) to connect to
172.22.11.2and navigate to/home/lvuser/. Download the most recent.wpilogfile. - FTP over robot network: Connect to the robot's WiFi. FTP to
10.TE.AM.2. Same path as above. Slower than USB, but works wirelessly in the pit during between-match review.
Keep a USB drive permanently plugged into the roboRIO throughout competition. Do not rely on FTP during a match queue — you won't have time. The USB drive should be dedicated to logs (nothing else on it), labeled with the team number and year, and have at least 8 GB of capacity (one competition generates roughly 200–500 MB of logs across 10+ matches). After each event, archive the drive's contents to a shared team folder organized by event name and date. These logs are your debugging history for the entire season. Teams that lose them lose the ability to diagnose intermittent failures that only appear at events.
Log Key Naming Conventions
Log keys are hierarchical strings that become the navigation tree in AdvantageScope. Consistent naming conventions matter — a messy log namespace is as hard to navigate as messy code.
// ── Pattern: SubsystemName/Category/SpecificValue ───────────────────────── // IO layer inputs (set by processInputs prefix) // These appear under: Shooter/velocityRpm, Shooter/currentAmps, etc. Logger.processInputs("Shooter", m_inputs); // creates Shooter/* namespace Logger.processInputs("Drive/FrontLeft", inputs); // Drive/FrontLeft/* // Computed outputs (use SubsystemName/ValueName) Logger.recordOutput("Shooter/TargetRpm", m_targetRpm); Logger.recordOutput("Shooter/VelocityError", error); Logger.recordOutput("Shooter/State", m_state); // Robot-level pose and field visualization Logger.recordOutput("Odometry/Robot", m_drive.getPose()); Logger.recordOutput("Odometry/VisionPose", visionPose); Logger.recordOutput("Odometry/Trajectory", trajectoryPoses); // Command scheduler (AdvantageKit logs active commands automatically) // These appear under: ActiveCommands/SchedulerActions // Avoid: vague names, no namespace, duplicate keys across subsystems Logger.recordOutput("rpm", value); // ❌ which subsystem? Logger.recordOutput("Shooter/rpm", value); // ✓ clear and findable
🔌 System Check
After installing AdvantageKit and configuring Robot.java, verify each of these before relying on it:
- Robot.java extends
LoggedRobot, notTimedRobot. Check the class declaration. If it still extendsTimedRobot, AdvantageKit's timing integration isn't active. This won't prevent the code from compiling, but the Logger won't tick correctly and log timestamps will be wrong. - Logger.start() is called before new RobotContainer(). Check the order in
robotInit(). IfRobotContaineris constructed beforeLogger.start(), anyprocessInputs()orrecordOutput()calls during subsystem construction will fail silently or throw exceptions. - A .wpilog file is created after running on the real robot. After running auto or teleop on the physical robot, connect via FTP or pull the USB drive and confirm a
.wpilogfile exists in/U/logs/(or/home/lvuser/if no USB is connected). If no file exists, verify theWPILOGWriterpath is correct and the robot has write permissions. - Log keys appear in AdvantageScope when the .wpilog is opened. Open the file in AdvantageScope (Lesson 4). Expand the subsystem namespaces. Confirm that IO input fields (e.g.,
Shooter/velocityRpm) and output fields (e.g.,Shooter/TargetRpm) are present. If only some keys appear, verify thatprocessInputs()is called before any logic readsm_inputsfields (Lesson 1 order requirement). - Git SHA appears in the log metadata. Open the .wpilog in AdvantageScope and look for the metadata fields. Confirm
GitSHAandBuildDateare present. If they show as empty strings, thetoolsPluginis not active inbuild.gradleandBuildConstantswasn't generated.
Knowledge Check
1. A robot runs a match and the team finds the shooter missed shots repeatedly. They download the .wpilog file. The file contains Shooter/velocityRpm and Shooter/TargetRpm but they want to also see the PID correction voltage at each loop — a value they forgot to log before the match. Can they recover this information, and if so, how?
2. A programmer adds Logger.start() at the bottom of robotInit(), after new RobotContainer(). The code compiles and runs. What is likely broken?
3. A team logs Logger.recordOutput("rpm", m_inputs.velocityRpm) in their shooter subsystem and Logger.recordOutput("rpm", m_inputs.angleRad) in their arm subsystem. Both use the same key "rpm". What happens in the log file, and how should this be fixed?
Add AdvantageKit to Your Robot Project
- Install AdvantageKit using the vendor library URL from
github.com/Mechanical-Advantage/AdvantageKit/releases. ChangeRobot.javato extendLoggedRobot. Configure the Logger inrobotInit()with aWPILOGWriterfor real robot mode andNT4Publisherfor simulation. CallLogger.start()beforenew RobotContainer(). Confirm the code builds without errors. - Add
Logger.recordOutput()calls to one subsystem'speriodic(): at minimum, log the target value, the actual sensor value, and the error. Use namespaced keys ("SubsystemName/ValueName"). Deploy to simulation and confirm the keys appear in SmartDashboard or AdvantageScope. - Connect a USB drive to the roboRIO. Deploy and run the robot in auto or teleop for at least 30 seconds. Pull the USB drive and confirm a
.wpilogfile was created in/U/logs/. Open it in AdvantageScope (covered fully in Lesson 4) and confirm your logged keys appear. - Update your subsystem from Lesson 1's Practice Prompt (the IO layer refactor) to use
Logger.processInputs()afterupdateInputs(). Confirm the order is correct:updateInputs()→processInputs()→ logic readsm_inputs. Verify in a.wpilogfile that the IO layer inputs appear under the correct namespace. - Bonus: Add Git metadata logging using
BuildConstants. Enable thetoolsPlugininbuild.gradle. Rebuild. After running, verify that the.wpilogfile's metadata section contains your current Git SHA and build date. Commit a change to your code, rebuild, and run again — confirm the SHA in the new log file is different from the previous one.