HiveVision
Point a Limelight 3A at the field, get game-element positions in field coordinates, and route autonomous paths around obstacles. Built for FTC.
Install
Add the repository and the modules you need to your TeamCode/build.gradle:
repositories {
maven { url = uri("https://sidhuharjas.github.io/hvtest") }
}
dependencies {
implementation "org.hivevisionftc:core:1.0.0"
// optional — auto-routing with Marrow Weaver
implementation "org.hivevisionftc:weaver:1.0.0"
}
Sync, and you're done. core is the perception pipeline; weaver is an
add-on that turns detections into obstacle-avoiding paths.
Quickstart
1. Set up the camera
Measure your camera mount (see Calibration), then create the config:
VisionConfig config = new VisionConfig()
.camPitchDeg(25.0) // tilt below horizontal, degrees
.camHeight(9.2) // floor to lens center, inches
.ballHeight(3.0) // floor to game element center, inches
.camXOffset(4.0) // lens forward of robot center, inches
.camYOffset(0.0) // lens left of robot center (+ left)
.minConfidence(0.44) // ignore weak detections
.maxStalenessMs(120) // discard stale data
.pipeline(3) // Limelight pipeline index
.pollingRateHz(150);
Limelight3A limelight = hardwareMap.get(Limelight3A.class, "limelight");
HiveVision vision = new HiveVision(limelight, config);
// optional: restrict which classes to chase (default = all three)
vision.setAllowedClasses(
BiobuzzClasses.POLLEN, // neutral yellow pollen
BiobuzzClasses.NECTAR_RED, // alliance red nectar
BiobuzzClasses.NECTAR_BLUE); // alliance blue nectar
Class IDs match the model labels: POLLEN = 0, NECTAR_RED = 1,
NECTAR_BLUE = 2. Skip setAllowedClasses entirely to keep all three
enabled, or pass only the classes you want (e.g. NECTAR_RED, NECTAR_BLUE to ignore pollen).
2. Ask for the closest target
Pass your current pose so detections come back in field coordinates:
FieldPose robotPose = new FieldPose(
follower.pose().x(),
follower.pose().y(),
follower.pose().heading());
Detection target = vision.detections()
.fetch(robotPose)
.sortByDistance()
.getClosest();
if (target != null) {
telemetry.addData("class", target.getBallClass());
telemetry.addData("field x", target.getX());
telemetry.addData("field y", target.getY());
}
3. Route a path through the detections
The optional weaver module turns the same list into a Marrow path that dodges
obstacles. Feed it to Pedro Pathing and drive:
PathResult path = vision.detections()
.fetch(robotPose)
.sortByDistance()
.with(WeaverRoute.fromFetchPose()
.avoid(new CircleZone(new Point(72, 72), 18.0))
.maxTargets(4));
if (path != null) {
follower.follow(PedroPathingConverter.toPath(path));
}
avoid(...) zones are optional. with(...) returns null
when nothing was detected — always null-check.
PedroPathingConverter is built into Marrow core itself, not Pedro:
import com.skeletonarmy.marrow.weaver.PedroPathingConverter;
Run the examples
Two finished OpModes ship in docs/examples/:
| OpMode | What it does |
|---|---|
AutoBallHuntOpMode"HiveVision Weaver Auto" |
One-shot autonomous. Detects targets, builds a Weaver route through the closest 2, drives it. |
ManualBallChaseOpMode"HiveVision Loop with Obstacles" |
Runs a detection loop the whole match. Re-plans around an obstacle whenever the robot is idle. |
- Copy those two files plus
SampleFollowerFactory.javainto your TeamCode module (they already declarepackage org.firstinspires.ftc.teamcode). - Fix the
VisionConfignumbers for your camera mount. The config file expects the Limelight under the hardware name"limelight"— rename it in yourhardwareMap/ config if yours differs. - Build and deploy TeamCode to the robot the way you normally ship code.
- On the Driver Station pick Autonomous → HiveVision Weaver Auto (or the loop one), turn on the Limelight, press start.
Telemetry shows what it sees: closest element class, field X/Y, and route status. If it says “No targets detected”, check the Limelight pipeline index and that the game elements are inside the camera view.
Calibration
Distance estimation comes from your camera geometry. Measure these once, write them down, and positions on the field will line up:
| Method | What to measure |
|---|---|
camHeight | Floor to lens center (inches) |
ballHeight | Floor to game element center (inches) |
camPitchDeg | Tilt below horizontal (degrees) |
camXOffset | Lens forward of robot center (+ forward) |
camYOffset | Lens left of robot center (+ left, − right) |
minConfidence | Lowest confidence you trust (0.0–1.0) |
maxStalenessMs | How old a detection may be (ms) |
Full measurement procedure and coordinate frames: docs/calibration.md. API reference: docs/MODULES.md. Example OpModes: docs/examples/.
Build from source
Clone sidhuharjas/hvtest, then:
./gradlew test # unit tests, no camera needed ./gradlew spotlessApply # format ./gradlew assembleRelease
Every push of a v* tag (or a manual run of the
Publish to Pages Maven repo workflow)
rebuilds the AARs and republishes them to this site. Bump version in
build.gradle.kts before tagging.