# 150 lesson: give the driver both :::{figure} ../_static/figures/fig-l150-bumper-held.png :name: fig-l150-bumper-held ::: The sticks mean the field, the way L140 left them, until the right bumper is held. While it is held they mean the robot. Let go and they mean the field again. ::::{only} mentor **Before you start.** - L140 is done. - AdvantageScope is installed on the same laptop, and a gamepad is plugged into the laptop. - The robot, the Driver Station, and about 2 m by 2 m of clear floor for the last step. :::: ## the steps ### L150S010: copy the starter into mytry **Copy.** In `lessons`, click `L150RobotRelativeButtonOpMode` once and copy it. Click `mytry` once and paste. In the **Copy Class** box, click **OK**. If Android Studio asks whether to add the file to Git, click **Add**. **Check.** - `mytry` holds `L150RobotRelativeButtonOpMode`, and the new file is open. ### L150S020: switch your copy on **Change.** Delete the line `@Disabled`, under the comment that names this step. **Check.** - Under the step's comment, the next line reads `// 'public' so the robot can run this OpMode. 'public' allows access from any code.` ### L150S030: choose with the bumper Field relative is the easier way to drive most of the time. It is not the easier way to line up against something. Backing straight out of a corner, or nudging along a wall, is easier when forward means the robot's own forward. So the driver gets both, and an `if` chooses. The starter already works out `robotForward` and `robotStrafe`, L140's turn. `forward` and `strafe` are the sticks as they are, the robot's own directions. **Change.** In `driveFromTheSticks()`, find the comment that names this step. Delete the line `drivetrain.sticks(robotForward, robotStrafe, turn);` under it, and type: ```{code-block} java :emphasize-lines: 7-13 double heading = follower.pose().heading(); double robotForward = forward * Math.cos(heading) + strafe * Math.sin(heading); double robotStrafe = -forward * Math.sin(heading) + strafe * Math.cos(heading); // When on L150S030, see // https://msb-stitt.github.io/ftc31459-26-27-biobuzz/guide/tasks/l150.html#l150s030 // for what to do here. boolean robotRelative = gamepad1.right_bumper; Tracker.publish("drive/robotRelative", robotRelative); if (robotRelative) { drivetrain.sticks(forward, strafe, turn); } else { drivetrain.sticks(robotForward, robotStrafe, turn); } ``` When you type `{` and press Enter, Android Studio adds the `}` for you. The bumper is read fresh every loop. Let go, and the next loop drives field relative again. The driver's thumb tells them which way the sticks work, so there is nothing to remember in a match. **Simulator.** In the Terminal, run: ``` ./gradlew :TeamCode:simRun --args="mytry.L150RobotRelativeButtonOpMode --pad" ``` **AdvantageScope.** Connect to the simulator as in L020, click **2D Field**, and open the `drive` folder, then `Localizer` and `pinPoint`. **Check.** - Hold the right bumper and push the left stick a little forward. `robotRelative` turns green, and the robot drives up the field, the way its nose points. - `y_in` climbs and `x_in` stays at 72. - Let go of the bumper and keep pushing. The robot slides to the right, along +x, and `x_in` climbs. ### L150S040: drive both ways on the floor **Robot.** Stop the simulator. Send the code, and run `L150 Robot Relative Button` from the Driver Station. Stand off the robot's left side, as in L140. **Check.** - Turn the robot so its nose points to one side, and push the left stick away from you. - The robot drives away from you. - Now hold the right bumper and push the same way. The robot drives along its own nose. - Let go, and it drives away from you again. ::::{only} mentor **No gamepad.** This holds the bumper and the stick, for the picture at the top: ``` ./gradlew :TeamCode:simRun --args="mytry.L150RobotRelativeButtonOpMode --left_stick_y=-0.5 --right_bumper=true" ``` Without `--right_bumper=true`, the same run was watched at `x_in` 126.674 and `y_in` 10.5. The simulator has no walls, so a run left going drives off the field. **If it didn't**, and `LessonsTest.l150_theBumperSwitchesToRobotRelative` fails with `robot relative while held expected:<1.0> but was:<5.551115123125783E-17>`, the line still reads `drivetrain.sticks(robotForward, robotStrafe, turn);`. That long number is 0.0 with a rounding error from the turn. **If it didn't**, and it fails with `field relative by default expected:<0.0> but was:<1.0>`, the two branches are swapped. The bumper is the exception, so the `if` reads the bumper, not `!` the bumper. **If it didn't**, and it fails with `and back again on release expected:<0.0> but was:<1.0>`, the bumper flips a remembered flag instead of being read fresh every loop. That is a toggle, and this is the check that catches it. No test watches the `drive/robotRelative` line. It is there for AdvantageScope: a driver who says "it went crooked" and a graph that says which way the sticks worked settle the question between them. **What you just did.** This is the first lesson that is a driver's choice rather than a robot's. Nothing here is new arithmetic. Both ways of driving existed already, and what you added is who chooses and when. That is most of what a competition teleop turns out to be: a few ways to drive, and a gamepad laid out so a driver can reach them without looking. **Where next.** [L160](l160.md) holds a heading for the driver, so the robot stops turning when nobody is turning it. ::::