A great control scheme is invisible: the driver thinks "go there" and the robot goes there. Let's build one in WPILib command-based Java that an actual competition driver can use.
Start in RobotContainer with a CommandXboxController on port 0. WPILib gives you named factory methods for every button, so you bind declaratively instead of polling in a loop.
private final CommandXboxController driver = new CommandXboxController(0);
private final DriveSubsystem drive = new DriveSubsystem();
private void configureBindings() {
// Default command: field-relative joystick drive every loop
drive.setDefaultCommand(drive.run(() -> drive.driveFieldRelative(
-driver.getLeftY(), // forward (Y is inverted on Xbox sticks)
-driver.getLeftX(), // strafe
-driver.getRightX() // rotation
)));
// Reset field-relative heading to "away from driver station"
driver.start().onTrue(drive.runOnce(drive::zeroHeading));
// Hold left bumper for precision/slow mode
driver.leftBumper().whileTrue(drive.run(() -> drive.driveFieldRelative(
-driver.getLeftY() * 0.35,
-driver.getLeftX() * 0.35,
-driver.getRightX() * 0.35)));
}
Key design choices, each grounded in real practice:
- Invert the Y axes. Xbox sticks report +1 when pulled toward the driver, so forward needs a negative sign. Forgetting this is the single most common day-one bug.
onTruevswhileTrue.onTrueruns once on a rising edge (perfect for a gyro reset);whileTrueruns while held and cancels on release (perfect for slow mode). These are the two binding methods you'll use most.- Slow mode by scaling, not a separate command tree. Multiplying inputs by 0.35 gives about one-third speed for lining up on the HUB or playing careful defense, without duplicating logic.
Apply a deadband so a drifting stick doesn't creep the robot. WPILib's MathUtil.applyDeadband(value, 0.1) is the idiomatic call; the DifferentialDrive class already applies 0.02 internally, but swerve code should apply your own (0.05–0.10 is typical).
double x = MathUtil.applyDeadband(-driver.getLeftX(), 0.1);
Finally, write down the physical mapping and tape it to the operator console: Left stick = translate, Right stick = rotate, Start = reset gyro, Left bumper = slow. Hand this card to a substitute driver and they should be able to drive a qualification match cold.
Test it on blocks first, then on the practice field. Drive a figure-eight around two cones; if the robot tracks where you point in field-relative mode regardless of which way the bumper faces, your gyro and inversions are correct.
Key takeaways
- Use CommandXboxController factory methods (start(), leftBumper()) with onTrue for one-shot actions and whileTrue for held actions.
- Invert Xbox Y axes for forward, and apply your own deadband (0.05-0.10) on swerve before scaling for slow mode.
- Document the physical button map on a card at the operator console so any driver can run a match.
Lesson quiz
RequiredAnswer all 4 questions correctly to complete this lesson.
01.How is the gyro/heading reset typically wired to the controller in an FRC driver control scheme?
02.Why does a production driver-control scheme apply a joystick deadband?
03.How is precision/slow mode typically implemented when the left bumper is held?
04.Why is the Xbox controller's Y axis negated when commanding forward drive?
Answer every question to submit.
All 34 lessons in Drive Team
- Not started:Project 1: A Production-Ready Driver Control Scheme
- Not started:Project 2: A Drivetrain That Survives a Whole Match
- Not started:Project 3: Build a Paper + Spreadsheet Scouting System
- Not started:Project 4: Pull Live OPR & EPA Data with Python
- Not started:Project 5: The One-Page Pre-Match Strategy Brief