The Operator Interface
robot/oi.py converts controller inputs into robot actions. A driver can ask
you to change this file during an event. Therefore you must know it well.
How the bindings operate
We use XboxCommandController. This is our own class. It contains WPILib
controller classes. Each button on the controller is a trigger. Attach a
function to a trigger with a decorator:
@self.driver1.A.whenPressed
def _():
self.robot.shooter_at_default = False
The name of the function is _, because no other code calls the function. The
registration of the function is the purpose. Three decorators are sufficient
for almost all bindings:
| Decorator | When the function operates |
|---|---|
whenPressed |
One time, on the loop when the button goes down |
whenHeld |
Each loop while the button is down. This is approximately 50 times each second |
whenReleased |
One time, on the loop when the button goes up |
To select the correct decorator is most of the work. These rules come from failures at competitions:
- Use
whenPressedfor a single action. Examples are a shot, a change of state, and a gyro reset. If you usewhenHeldfor a single action, the action occurs 50 times each second. - A continuous action needs
whenHeldandwhenReleased.whenHeldstarts the intake. Nothing stops the intake unless you write thewhenReleasedfunction. This is the most frequent error in this file. - Do not use
whenHeldto start a shooter. A shooter needs approximately one second to reach speed. UsewhenPressedand let the shooter continue.
Available buttons
A, B, X, Y, LEFT_BUMPER, RIGHT_BUMPER, START, BACK,
LEFT_STICK, RIGHT_STICK, POV.UP, POV.DOWN, POV.LEFT and POV.RIGHT.
The triggers are analog. Therefore each trigger has two forms. LEFT_TRIGGER
is a CustomAnalog and gives a value from 0 to 1. LEFT_TRIGGER_AS_BUTTON is
a button. It is pressed above approximately 5%. Use the button form unless you
need the analog value.
The joysticks are also analog. Set a deadzone on each joystick in
OI.__init__:
self.driver1.LEFT_JOY_X.setDeadzone(0.02)
self.driver1.RIGHT_JOY_X.setDeadzone(0.1)
Read Joystick Deadbands first. That page gives the reason for these two values.
Exercise
The robot below has an intake, a shooter and a driver station. Write the bindings, load them, then complete a scoring cycle with your keyboard. Your Python operates 50 times each second, with the same rules as the robot.
The cycle is:
- Drive to the depot.
- Collect one piece of fuel with the intake.
- Start the shooter.
- Move to a position within range of the goal.
- Shoot.
Complete the cycle two times.
The depot changes color when the robot is in it. The shooter shows a ring that increases as the speed increases. A shot moves to the goal. If you shoot too early or from too far away, the piece stops before the goal.
Reset robot moves the robot back to the start position. It does not change your bindings. Restore starter code replaces your bindings, and it asks you first.
You have a driver controller and a robot. The robot has three attributes:
| Attribute | Effect |
|---|---|
robot.intake_running |
While True, the intake collects fuel. It operates only inside the depot |
robot.shooter_spinning |
While True, the shooter increases speed. It needs approximately one second to reach full speed |
robot.shoot |
Set to True to shoot one piece of fuel. The value returns to False each loop. Therefore set it from a pressed binding |
Write the bindings for a full cycle. Then press Load bindings and complete the cycle with your keyboard.
| Key | Controller | Key | Controller |
|---|---|---|---|
| WASD | left joystick, to drive | J | A |
| QE | right joystick, to turn | K | B |
| Shift | LEFT_TRIGGER_AS_BUTTON |
U I | LEFT_BUMPER / RIGHT_BUMPER |
| L | RIGHT_TRIGGER_AS_BUTTON |
arrows | POV.UP, POV.DOWN, POV.LEFT, POV.RIGHT |
Differences from the robot
The real oi.py is longer than this exercise:
- Two controllers.
driver1controls the drivetrain.driver2controls the superstructure. Discuss the division of the actions with the drive team. - Default commands. The drivetrain has a
setDefaultCommand. It reads the joysticks each loop when no other command uses the drivetrain. - Coroutines. Many bindings start a coroutine instead of a flag. A coroutine can continue across many loops. An example sequence is: start the shooter, wait for the speed, feed the piece, stop. See Libraries.
- Rumble. Controller vibration uses the same decorators. Rumble tells the driver that an event occurred, and the driver does not look at the dashboard.
Change the bindings before a practice session. Do not change the bindings between matches. A driver learns the button positions. A button that moved after the last match is worse than a button with no function.