Skip to main content

Lesson 11.5: Challenge — Creating an Automated Sensor-Gated Intake System


What a Sensor Gate Is

A sensor-gated system replaces a driver command with an environmental condition. Instead of requiring the driver to decide when to start and stop the intake, the robot's sensors make that decision automatically. The driver focuses on positioning the robot, and the intake handles itself.

This approach reduces the number of simultaneous decisions the driver must make during a match cycle, which directly reduces errors. It also makes the mechanism more consistent: a sensor reacts in microseconds, whereas a driver's reaction time is measured in tenths of a second.


Combining Multiple Sensor Types

This challenge integrates a DistanceSensor and a DigitalChannel to create a two-condition gate. The intake motor should run only when both of the following are true:

  • A game element is within detection range of the distance sensor (close enough to collect).
  • The internal storage is not yet full (the touch sensor at the back of the intake is not triggered).

If either condition fails, the motor stops. The && operator in the if statement enforces this precisely:

if (dist < 10.0 && !isFull) {
intakeMotor.setPower(0.8);
} else {
intakeMotor.setPower(0.0);
}

Hysteresis: Preventing Motor Chatter at the Threshold

One subtlety that appears in real hardware: if the distance reading fluctuates right at the threshold boundary, the motor will start and stop many times per second. This rapid switching is called chatter, and it causes mechanical wear and inconsistent behavior.

The professional solution is to use two different threshold values. A smaller value triggers the start of collection (for example, 8 cm means "an element is definitely in range"), and a slightly larger value defines when the system allows stopping (for example, 12 cm means "definitely no element present"). The gap between these two values is called a hysteresis band.

Implementing hysteresis requires tracking a state variable -- a boolean that records whether the intake is currently active -- and updating it based on which threshold was crossed most recently. This prevents the motor from switching states on every tiny fluctuation.


Annotated Code

package org.firstinspires.ftc.teamcode;

import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.DigitalChannel;
import com.qualcomm.robotcore.hardware.DistanceSensor;
import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;

@TeleOp(name="Sensor_Gate_Demo")
public class SensorGateDemo extends LinearOpMode {

private static final double START_THRESHOLD = 8.0; // cm -- element is in range
private static final double STOP_THRESHOLD = 12.0; // cm -- element is gone

@Override
public void runOpMode() {
DcMotor intake = hardwareMap.get(DcMotor.class, "intake");
DistanceSensor range = hardwareMap.get(DistanceSensor.class, "intake_range");
DigitalChannel fullStop = hardwareMap.get(DigitalChannel.class, "storage_full");

fullStop.setMode(DigitalChannel.Mode.INPUT);

waitForStart();

boolean intakeActive = false;

while (opModeIsActive()) {
double dist = range.getDistance(DistanceUnit.CM);
boolean isFull = !fullStop.getState(); // active-low inversion

// Hysteresis: only start the intake when the element is close enough
if (!intakeActive && dist < START_THRESHOLD && !isFull) {
intakeActive = true;
}

// Stop collecting when the element moves away or storage is full
if (intakeActive && (dist > STOP_THRESHOLD || isFull)) {
intakeActive = false;
}

intake.setPower(intakeActive ? 0.8 : 0.0);

telemetry.addData("Distance (cm)", "%.1f", dist);
telemetry.addData("Storage Full", isFull);
telemetry.addData("Intake Active", intakeActive);
telemetry.update();
}
}
}

Fill-in-the-Blank Practice

  1. Combining data from two or more sensors to make a single control decision is a form of sensor __________.
  2. The rapid and undesirable switching of a motor on and off as a sensor reading fluctuates at a boundary is called __________.
  3. The gap between a "start" threshold and a "stop" threshold that prevents this switching behavior is called a __________ band.
Show answers
  1. fusion (or integration)
  2. chatter
  3. hysteresis

Template Challenge

Robot Scenario: Design an intake system that runs the motor only when a game element is within 10.0 cm AND the storage touch sensor named "limit" is not pressed. Use a single if/else for the gate. Assume range and limit are already mapped and limit is already set to INPUT mode.

package org.firstinspires.ftc.teamcode;

import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.DigitalChannel;
import com.qualcomm.robotcore.hardware.DistanceSensor;
import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;

@Autonomous(name="Auto_Intake_Challenge")
public class AutoIntakeChallenge extends LinearOpMode {

@Override
public void runOpMode() {
DcMotor intake = hardwareMap.get(DcMotor.class, "intake");
DistanceSensor range = hardwareMap.get(DistanceSensor.class, "intake_range");
DigitalChannel limit = hardwareMap.get(DigitalChannel.class, "limit");
limit.setMode(DigitalChannel.Mode.INPUT);

waitForStart();

while (opModeIsActive()) {
// INSERT CODE HERE: Get distance in CM
double dist = // INSERT VALUE HERE

// INSERT CODE HERE: Read and invert the limit sensor for active-low
boolean isFull = // INSERT VALUE HERE

// INSERT CODE HERE: Run motor at 0.6 if dist < 10.0 AND not full,
// else stop motor

telemetry.addData("Distance", dist);
telemetry.addData("Full", isFull);
telemetry.update();
}
}
}
Show answer
double dist = range.getDistance(DistanceUnit.CM);

boolean isFull = !limit.getState();

if (dist < 10.0 && !isFull) {
intake.setPower(0.6);
} else {
intake.setPower(0.0);
}

Ready to move on?

Sign in with Google to save your progress with Telemark, or continue without saving.