Tutorial 08: A lift that cannot hurt itself
You will build: the lift from Tutorial 07, now with limits it cannot drive past, a bottom switch, and stick control that takes over from the buttons. In the main track, the lift part has these limits built in: An intake, a lift and a claw.
You will learn: how SafeMotor keeps a mechanism inside its range with soft limits, a limit
switch, homing and a slow zone; and the standard pattern for “set heights with buttons, plus the
stick to take over by hand”.
You need: the lift from Tutorial 07 (motor lift, positive = up), its PID gains, and
ideally a touch sensor at the bottom named lift_bottom. Tutorials 01-03 and 07.
Full program: Tut08SafeLift.java
Reference: safety.md
1. What can go wrong with a lift
Section titled “1. What can go wrong with a lift”- Driven past the top: string snaps, slide pulls apart, robot breaks the 29 in height rule.
- Driven into the bottom: motor stalls, gets hot, belt skips.
- Encoder “drifts” (belt skips, robot turned on with the lift up): now every height is wrong.
SafeMotor handles all three in software. It does not replace hard stops (a physical block
that the lift hits at each end). Rule R105 B says
the robot must stay inside its size limits without software. Software stops the motor before it
hits the block; the block holds if the software is wrong.
2. Wrap the motor
Section titled “2. Wrap the motor”DcMotorEx liftMotor = MotorSetup.get(hardwareMap, "lift") .brake().resetEncoder().runWithoutEncoder().done();SafeMotor lift = new SafeMotor(liftMotor) .setLimits(BOTTOM, MAX) // 0 to 2600 ticks .setSlowZone(250, 0.2) // within 250 ticks of an end, slow down to 20% .setMaxPower(0.9);- Soft limits (limits in software, not metal): power that pushes past 0 or 2600 becomes 0. Power AWAY from the limit always works, so the lift can never get “stuck at the top”.
- Slow zone: close to an end, power toward it is reduced, so it does not slam.
- Max power: the most it ever gets.
Set MAX a little below the real top, so the soft limit stops it before the hard stop.
3. The bottom switch and homing
Section titled “3. The bottom switch and homing”TouchSensor bottom = Hw.tryGet(hardwareMap, TouchSensor.class, "lift_bottom");if (bottom != null) lift.setLowerSwitch(bottom::isPressed);bottom::isPressed hands SafeMotor a way to ask the switch “are you pressed?” whenever it needs to.
It is short for the lambda () -> bottom.isPressed().
With a lower switch:
- The switch always stops downward power, whatever the encoder says.
- Pressing it sets the position to 0 (homing). Encoder drift is fixed every time the lift reaches the bottom.
- Until it is homed the first time, the lift only goes DOWN, slowly (30%). The position is not known yet, so up is not safe. The driver pushes it down once at the start; if it is already on the switch, it homes at once.
Without a switch, position 0 is wherever the lift was when the robot turned on: always turn on with it at the bottom.
4. The pattern: set heights + hand control
Section titled “4. The pattern: set heights + hand control”private Integer target = null; // null = by hand....bindPress(Pad.G2, Btn.A, () -> goTo(BOTTOM, pid)).bindPress(Pad.G2, Btn.Y, () -> goTo(TOP, pid)).bindPress(Pad.G2, Btn.B, () -> target = null) // stop holdingAnd every loop:
lift.update(); // FIRST: switches and homingcontrols.update(gamepad1, gamepad2);
double stick = StickShaping.shape(-gamepad2.left_stick_y, 0.1, 1);if (stick != 0 || lift.isWaitingForHoming()) target = null; // the stick takes overif (target == null) { lift.setPower(stick);} else { lift.moveTo(target, pid);}targetis anInteger(notint) so it can benull, meaning “no set height, by hand”.- Touching the stick cancels the set height. The driver is always in control.
moveTo(target, pid)ispid.calculate+setPower, with the limits applied and the target kept inside them.lift.update()must come first every loop, and every power for this motor goes through the SafeMotor. CallingliftMotor.setPower(...)directly skips the limits.
5. Telemetry that explains itself
Section titled “5. Telemetry that explains itself”if (lift.isWaitingForHoming()) telemetry.addLine("Push the lift DOWN to the switch to home it.");TelemetryHelper.value(telemetry, "Asked power", lift.getRequestedPower(), 2);TelemetryHelper.value(telemetry, "Sent power", lift.getAppliedPower(), 2);When “Asked” is 0.5 and “Sent” is 0, a limit stopped it. This answers “why won’t the lift move?” in two seconds.
Run it
Section titled “Run it”- Start with the lift a little UP (with a switch). Telemetry asks you to home it. Stick up: nothing. Stick down: it goes down slowly, reaches the switch, position becomes 0.
- Stick up: it moves, then slows near the top and stops at MAX. Stick down: it moves away at once.
- Y: goes to TOP. Touch the stick: the button target is cancelled, you are in control.
- Hold the stick down at the bottom: “Asked -0.5, Sent 0”.
If something goes wrong
Section titled “If something goes wrong”| Problem | Fix |
|---|---|
| Only goes down, slowly | Not homed yet. Push it down to the switch. |
| Will not go down at all | It thinks it is at 0 or the switch is pressed. Check At bottom and the switch wiring. |
| Stops early at the top | MAX too low, or the position drifted. Re-home at the bottom. |
| Goes past the top | Positive power is not “up” (reverse the motor), or MAX is wrong. STOP. |
| Hums at the bottom when holding BOTTOM | The F push in the PID. Normal; or use target = null at the bottom. |
Try this
Section titled “Try this”- Make B “go to bottom, then let go”: after reaching BOTTOM (
pid.atTarget()), settarget = null. - Add an upper limit switch with
setUpperSwitch(...). - Let the driver re-home on demand: a button that sets
target = nulland drives down at -0.3 untillift.isAtLower().