Skip to main content

Lesson 14.1: Configuring VisionPortal and Opening Webcam Streams


Why Vision Requires a Different Initialization Pattern

Every hardware device covered in this curriculum so far, including motors, servos, and sensors, is retrieved from the hardwareMap using a single get() call and is immediately ready to use. The USB webcam follows a different pattern for an important technical reason: it operates through Android's camera subsystem, which requires asynchronous initialization. The camera does not open instantaneously. It negotiates a stream format, allocates frame buffers, and confirms a stable connection before it begins delivering frames.

The VisionPortal class is the SDK's abstraction layer over all of this complexity. It manages the camera lifecycle, delivers frames to processing pipelines on a background thread, and provides methods to pause or resume streaming. Your code never interacts with Android's camera APIs directly. You configure a portal, give it the camera reference and the processors you want to run, and the SDK handles everything underneath.


The WebcamName Object

The first step is retrieving a WebcamName object from the hardwareMap. This is the SDK's typed reference to a USB camera that has been configured in the Robot Controller app. The configuration name you use in hardwareMap.get(WebcamName.class, "Webcam 1") must match the name assigned in the configuration file exactly, following the same case-sensitive rule that applies to every other hardware device.

WebcamName by itself does nothing. It is simply a handle. It becomes useful when passed to VisionPortal, which uses it to locate and open the physical device.


Building a VisionPortal

The most straightforward way to create a VisionPortal is through the static factory method VisionPortal.easyCreateWithDefaults(webcamName, processor). This method handles default resolution, frame format, and threading configuration internally. For teams that need finer control, such as a specific camera resolution for better performance, the VisionPortal.Builder pattern is available. The defaults are appropriate for the vast majority of FTC use cases.

A VisionPortal is a container. You can attach one or more processor objects to it. Each processor receives every frame captured by the camera and performs its own analysis independently. The portal routes frames to all attached processors automatically.

Official reference

Use the FTC SDK Javadocs to confirm current VisionPortal, AprilTagProcessor, and hardware-map APIs: FTC RobotCore Javadocs.


Resource Management: Stopping or Closing the Portal

A running camera stream consumes a meaningful portion of the Control Hub's CPU budget. Frame capture, color space conversion, and the processing pipelines running on top all compete with the motor control and sensor polling loops that the robot depends on for accurate movement. Running the stream during the driving phase of autonomous, after the vision decision has already been made, wastes resources the drivetrain PID controllers could use.

Call visionPortal.stopStreaming() once the vision decision is complete and before driving begins when the same OpMode may need the camera again. This pauses frame delivery while keeping the portal object alive, and resumeStreaming() can restart it later.

Call visionPortal.close() when camera work is finished for the rest of the OpMode. close() releases the portal and camera completely, so it is the required cleanup path when no later state will resume vision. A safe routine may pause the stream before a CPU-intensive drive phase and then close the portal during final cleanup.

Mentor note: camera bugs are usually lifecycle bugs

If the Driver Station preview is black, check the configured webcam name, USB connection, and whether the stream was stopped before waitForStart(). If autonomous drives inconsistently while streaming, stop the stream after the vision decision and compare CPU load behavior.


Annotated Code

note

This is an autonomous program. No gamepad input is used.

package org.firstinspires.ftc.teamcode;

import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.hardware.WebcamName;
import org.firstinspires.ftc.vision.VisionPortal;
import org.firstinspires.ftc.vision.apriltag.AprilTagProcessor;

@Autonomous(name="Vision_Portal_Demo")
public class VisionPortalDemo extends LinearOpMode {

private VisionPortal visionPortal;
private AprilTagProcessor aprilTagProcessor;

@Override
public void runOpMode() {
// Step 1: Retrieve the physical camera reference from the hardware map
WebcamName webcam = hardwareMap.get(WebcamName.class, "Webcam 1");

// Step 2: Create a processor. This one handles AprilTag detection.
aprilTagProcessor = AprilTagProcessor.easyCreateWithDefaults();

// Step 3: Bind the camera and processor together inside a VisionPortal
visionPortal = VisionPortal.easyCreateWithDefaults(webcam, aprilTagProcessor);

telemetry.addData("Camera", "Stream active");
telemetry.update();

// The camera is now streaming and the processor is receiving frames.
// During waitForStart(), the drive team can see the camera feed
// on the Driver Station and confirm the camera is aimed correctly.
waitForStart();

if (opModeIsActive()) {
// Vision decision logic would go here in a real routine.
// Once decided, stop the stream to free CPU for driving.
visionPortal.stopStreaming();

telemetry.addData("Camera", "Stream stopped: driving phase");
telemetry.update();

// Driving logic follows here...
sleep(2000);
}

// No later state needs the camera, so release it completely.
visionPortal.close();
}
}

Fill-in-the-Blank Practice

  1. The hardware map class used to retrieve a reference to a USB webcam is __________.
  2. To create a VisionPortal with minimal configuration using a camera and one processor, use the static method VisionPortal.__________().
  3. To pause frame delivery while retaining the option to resume it, call visionPortal.__________().
  4. To release the camera completely when vision work is finished, call visionPortal.__________().
Show answers
  1. WebcamName
  2. easyCreateWithDefaults
  3. stopStreaming()
  4. close()

Simulator Challenge

Write the initialization block for an autonomous OpMode that opens a webcam named "front_cam", attaches a default AprilTagProcessor to it, and stops streaming the moment the match begins. The camera should be active and visible to the drive team during the waitForStart() phase only.

note

This is an autonomous program. No gamepad input is used.

Telemark Unit 14.1 Simulator
Loads the lesson-specific Telemark computer vision challenge with incomplete starter code for students to complete.
The simulator editor contains the starter code; use the answer below only after trying the challenge.
Show answer
aprilTagProcessor = AprilTagProcessor.easyCreateWithDefaults();

visionPortal = new VisionPortal.Builder()
.setCamera(hardwareMap.get(WebcamName.class, "front_cam"))
.addProcessor(aprilTagProcessor)
.build();

// After waitForStart():
visionPortal.stopStreaming();

// After all autonomous work that might use the camera:
visionPortal.close();

Ready to move on?

Only mark complete if you genuinely understand the material. Your progress will be saved in this browser.

Stuck on this lesson?

Ask about anything on this page. It can see which lesson you have open and which part you are reading.