Skip to content

vision.hpp

IVision / ITagSource — the AI Vision seams.

This header declares 4 types (20 members).

Extracted from include/shulib/hal/vision.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.

Contents

struct TagObservation

struct TagObservation

One visible AprilTag, reduced to a robot-relative planar pose.

struct, declared at include/shulib/hal/vision.hpp:32.

TagObservation::id

int id = 0

AprilTag id within the configured family. DEFAULTED, like every other value struct in the tree: these two were the only sensor observations with no default member initializers, so TagObservation t; t.poseInRobot = …; left id and confidence INDETERMINATE — and the corrector's screen cannot save it, because the screen IS the read (!std::isfinite(obs.confidence) on an indeterminate double is already UB), while an indeterminate id that happens to hit a real map entry yields a confident fix against the wrong tag. Aggregate initialisation is unaffected. A corrector looks this up in its map of known field placements; an id with no map entry is discarded, never guessed at.

field, declared at include/shulib/hal/vision.hpp:41.

TagObservation::poseInRobot

math::Pose2d poseInRobot{}

Tag pose RELATIVE to the robot, canonical body frame (F1: +X forward, +Y left, heading CCW-positive), inches and radians. Already the PLANAR reduction: the tag's height above the camera, its pitch and its roll were discarded at the edge and are not recoverable.

field, declared at include/shulib/hal/vision.hpp:45.

TagObservation::confidence

double confidence = 0.0

Detector confidence, [0, 1]. Not a probability that the pose is right — a corrector DIVIDES its measurement sigma by it, so larger means a tighter fix, and 0 means unusable.

field, declared at include/shulib/hal/vision.hpp:48.

struct ObjectObservation

struct ObjectObservation

One visible classified object / color, reduced to a robot-relative bearing.

struct, declared at include/shulib/hal/vision.hpp:52.

ObjectObservation::classId

int classId = 0

Detected class / color descriptor id, as configured on the detector. Opaque to shulib: nothing here maps an id to a meaning — the manipulation code that asked for it owns that.

field, declared at include/shulib/hal/vision.hpp:55.

ObjectObservation::bearing

math::Angle bearing{}

Horizontal angle to the object measured from robot +X (forward), CCW-positive, wrapped to (-π, π]. A BEARING only: a bounding box carries no range, so this says which way to turn and never how far to drive.

field, declared at include/shulib/hal/vision.hpp:59.

ObjectObservation::confidence

double confidence = 0.0

Detector confidence, [0, 1]. Carried for M4 targeting to rank candidates with; no consumer in the tree reads it yet, so nothing currently gates on a low value.

field, declared at include/shulib/hal/vision.hpp:62.

class ITagSource

class ITagSource

AprilTag source (decision #7: V5 AI Vision OR a coprocessor, behind this one seam).

class, declared at include/shulib/hal/vision.hpp:66.

ITagSource::~ITagSource

virtual ~ITagSource() = default

The polymorphic-base boilerplate, and why it is spelled out: the destructor is virtual so deleting through ITagSource* is well-defined, and declaring it suppresses the implicit copy/move, which are therefore re-defaulted. The seam holds no state, so all five are trivial — an implementation is REFERENCED and never owned (RobotContext keeps a non-owning pointer, and the adapter must outlive the context).

function, declared at include/shulib/hal/vision.hpp:73.

ITagSource::ITagSource

ITagSource() = default

Covered by the comment on ~ITagSource — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:74.

ITagSource::ITagSource (overload 2)

ITagSource(const ITagSource&) = default

Covered by the comment on ~ITagSource — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:75.

ITagSource::ITagSource (overload 3)

ITagSource(ITagSource&&) = default

Covered by the comment on ~ITagSource — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:76.

ITagSource::operator=

ITagSource& operator=(const ITagSource&) = default

Covered by the comment on ~ITagSource — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:77.

ITagSource::operator= (overload 2)

ITagSource& operator=(ITagSource&&) = default

Covered by the comment on ~ITagSource — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:78.

ITagSource::tags

[[nodiscard]] virtual std::vector<TagObservation> tags() const = 0

AprilTags currently visible, each as a relative pose in the robot frame.

function, declared at include/shulib/hal/vision.hpp:81.

class IVision

class IVision

Object / color detection source (manipulation targeting, M4).

class, declared at include/shulib/hal/vision.hpp:85.

IVision::~IVision

virtual ~IVision() = default

Same polymorphic-base boilerplate as ITagSource, and for the same reason: a virtual destructor for delete-through-base, with copy/move re-defaulted after declaring it. It matters here that this base is stateless — decision #7 expects ONE adapter to inherit both this and ITagSource off a single V5 AI Vision sensor, and two empty bases cost that adapter nothing.

function, declared at include/shulib/hal/vision.hpp:92.

IVision::IVision

IVision() = default

Covered by the comment on ~IVision — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:93.

IVision::IVision (overload 2)

IVision(const IVision&) = default

Covered by the comment on ~IVision — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:94.

IVision::IVision (overload 3)

IVision(IVision&&) = default

Covered by the comment on ~IVision — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:95.

IVision::operator=

IVision& operator=(const IVision&) = default

Covered by the comment on ~IVision — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:96.

IVision::operator= (overload 2)

IVision& operator=(IVision&&) = default

Covered by the comment on ~IVision — one comment documents this run of special members.

function, declared at include/shulib/hal/vision.hpp:97.

IVision::objects

[[nodiscard]] virtual std::vector<ObjectObservation> objects() const = 0

Classified objects / colors currently visible.

function, declared at include/shulib/hal/vision.hpp:100.

Design commentary, from the header

The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.

The header’s own reasoning — 21 lines
 IVision / ITagSource — the AI Vision seams. Decision #7 (locked): the V5 AI Vision
 sensor AND a coprocessor (Pi) both sit behind ITagSource. The V5 sensor reports BOTH
 AprilTags (4 image corners + id) and classified objects/colors (bounding box + score),
 so one hal/pros adapter implements BOTH interfaces from it; a Pi adapter can too.

 CANONICAL FORMS — the raw V5 pixel corners / boxes are reduced to canonical
 robot-relative quantities in the adapter ("convert once at the edge", §7):
  * ITagSource yields each visible AprilTag as a RELATIVE POSE in the robot BODY frame
    (+X forward, +Y left — F1), a planar reduction of the tag's 6-DOF pose suitable for
    ground-plane localization. The corners→pose PnP (needs camera intrinsics + tag size)
    is a pure, host-testable function built with the M3 AprilTagCorrector; at M1 this is
    the seam the corrector reads.
  * IVision yields each object as a class id + BEARING (horizontal angle to the object,
    relative to robot +X) + confidence, for manipulation targeting (M4). The
    box-center→bearing reduction (needs camera FOV) is likewise an M4 pure function.

 Detections are returned BY VALUE: vision runs OFF the 10 ms control hot path (the
 adapter polls it at a lower rate). The consumer timestamps via IClock and owns
 staleness/latency handling (the corrector, M3) — the detection data itself is
 timestamp-free, like the GPS pose.

Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.