Project 1: A Production-Ready Driver Control Scheme
Wire an Xbox controller to a swerve or tank drivetrain using WPILib command-based bindings, with field-relative drive, a slow mode, and a gyro-reset button.
Sign in to track progress, earn XP, and save lessons.
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 3 questions correctly to complete this lesson.
1.Why is a WPILib SlewRateLimiter often preferred over a simple low-pass filter for softening driver joystick inputs?
2.In a production driver-control scheme, what is the purpose of applying a joystick deadband?
3.A SlewRateLimiter is a good fit for limiting a stream of velocity or voltage setpoints; which WPILib tool is recommended instead when you are controlling a position?
Answer every question to submit.