Class VisionGates

java.lang.Object
frc.robot.subsystems.vision.VisionGates

public final class VisionGates extends Object
Gate chains and trust models for FM's pose sources.

The AprilTag values are ported unchanged, comments and all, from the 2026 Spectrum code: main's FM Vision.java and the offseason branch's evidence-driven additions (the lookback spin gate, the height gate, the one-second age gate). Change a threshold here only with log evidence, the standard the originals held themselves to. AdvantageKit replay is the way to get that evidence: edit, replay a match, and compare the Localization/Shadow/* tracks.

  • Field Details

    • MAX_AMBIGUITY

      public static final double MAX_AMBIGUITY
      Any single tag with ambiguity above this rejects the frame (pose-flip risk).
      See Also:
    • MAX_YAW_RATE_RAD_PER_SEC

      public static final double MAX_YAW_RATE_RAD_PER_SEC
      Chassis yaw rate (rad/s) above which no estimate is fused. The camera stamps its heading from whatever the robot last pushed, so a fast spin turns timestamp error into heading error, and heading error into MegaTag2 translation error.
      See Also:
    • YAW_RATE_LOOKBACK_SECONDS

      public static final double YAW_RATE_LOOKBACK_SECONDS
      How far back (seconds) the yaw-rate gate looks. A frame that arrives just after a spin stops was captured during it, so the gate rejects on the peak rate over this window, not the rate right now. 254 uses the same 0.3 s; it covers camera latency, NetworkTables latency and the pose estimator's own lag with room to spare.
      See Also:
    • MIN_TARGET_SIZE_PERCENT

      public static final double MIN_TARGET_SIZE_PERCENT
      Limelight target area (% of image) at or below which a frame is too far to trust.
      See Also:
    • MAX_TILT_DEGREES

      public static final double MAX_TILT_DEGREES
      Roll or pitch of the solve beyond this means the camera was knocked or the solve is bad.
      See Also:
    • MAX_Z_ERROR_METERS

      public static final double MAX_Z_ERROR_METERS
      Solves whose robot height is further than this (metres) from the carpet are rejected. The robot cannot leave the floor, so a solve that says it did is a bad solve or a bad mount transform: the 30-versus-60 deg mount pitch of 2026-09-07 would have shown up here as every pose sitting well above or below zero. The value is the AdvantageKit template default, on the loose side on purpose until it has been watched at an event.
      See Also:
    • MAX_ESTIMATE_AGE_SECONDS

      public static final double MAX_ESTIMATE_AGE_SECONDS
      Estimates older than this (seconds, capture time to now) are rejected. The pose estimator silently drops anything older than its 1.5 s buffer, so without this gate a camera with a bad clock looks "integrating" while never moving the pose.
      See Also:
    • MAX_FUTURE_SECONDS

      public static final double MAX_FUTURE_SECONDS
      Estimates captured more than this many seconds after now are rejected. A future frame stays the newest measurement, so every later frame re-applies it until odometry catches up (TXMAN1 Q19: the Orins stamped frames up to 333 s ahead). The margin covers the loop timestamp being read at the start of the loop.
      See Also:
    • LARGE_VARIANCE

      public static final double LARGE_VARIANCE
      Variance used to effectively ignore a measurement dimension. Here it is the heading of every estimate fused while enabled, so the gyro owns heading during a match.
      See Also:
    • FUTURE_TIMESTAMP_GATE

      public static final Gate FUTURE_TIMESTAMP_GATE
      Rejects a capture time more than MAX_FUTURE_SECONDS ahead of now.
    • MAX_ROBOT_TILT_DEGREES

      public static final double MAX_ROBOT_TILT_DEGREES
      Robot tilt (degrees, from the Pigeon) above which sources that assume a level robot are rejected: Limelight MegaTag2 and the Orin's gyro and tx/ty sources take only the yaw, so on a bump their camera is not where they think it is.
      See Also:
    • ROBOT_TILT_GATE

      public static final Gate ROBOT_TILT_GATE
      Rejects frames while the robot is tilted, for sources that assume it is level.
    • ORIN_MAX_SINGLE_TAG_AMBIGUITY

      public static final double ORIN_MAX_SINGLE_TAG_AMBIGUITY
      A single-tag frame is kept only when its best PnP candidate's reprojection error is under this fraction of the other's. PhotonLib's ambiguity is best error / alternate error. (6328)
      See Also:
    • ORIN_MIN_Z_METERS

      public static final double ORIN_MIN_Z_METERS
      Solved robot height must be within this range (metres). (6328)
      See Also:
    • ORIN_MAX_Z_METERS

      public static final double ORIN_MAX_Z_METERS
      See Also:
    • ORIN_MAX_SINGLE_TAG_DISTANCE_METERS

      public static final double ORIN_MAX_SINGLE_TAG_DISTANCE_METERS
      Single tags further than this (metres) are dropped. (AOS uses about 5 m)
      See Also:
    • ORIN_MAX_HEADING_ERROR_DEG

      public static final double ORIN_MAX_HEADING_ERROR_DEG
      Once the heading is seeded, a solve whose heading is further than this from the fused heading is a bad solve (AOS). Sits clear of two-tag heading noise.
      See Also:
    • ORIN_AUTO_START_IGNORE_SECONDS

      public static final double ORIN_AUTO_START_IGNORE_SECONDS
      Vision is ignored for this long after auto starts. (6328)
      See Also:
    • LIMELIGHT_TIERS

      public static final StdDevModel LIMELIGHT_TIERS
      The 2026 Limelight confidence tiers.

      Heading is never fused while enabled. The Pigeon drifts a small fraction of a degree over a match, while MegaTag1 yaw jitters by a degree or more frame to frame; heading is corrected by the disabled pre-seeding and the gross-heading safety net in Vision, not here.

      MegaTag2 relaxes the two pose-difference tiers while disabled, as the 2026 code did.

    • ORIN_MODEL

      public static final StdDevModel ORIN_MODEL
      The Orin: 6328's model (issue #10, 8b). xy = 0.01 * d^2 / n^2, where d is the mean camera-to-tag distance of the tags in the solve and n their count, times the observation's PoseObservation.stdDevScale (the camera's factor and the image-edge scale from OrinSolver). A starting point: retune with replay.

      6328 also fuses multi-tag heading (theta = 0.03 * d^2 / n^2). Here heading is left to the gyro, as for every other source, until the Orin's heading has logs behind it.

    • QUEST_MODEL

      public static final StdDevModel QUEST_MODEL
      QuestNav: QuestNav's own suggested std-devs for translation. Heading is left to the gyro by default so that the Quest can be compared on translation alone first; raise trust in its heading only once logs show it holds up.
  • Method Details

    • aprilTagGates

      public static List<Gate> aprilTagGates()
      The shared AprilTag chain, for Limelight MegaTag1/2 and PhotonVision.
    • orinGates

      public static List<Gate> orinGates(DoubleSupplier secondsSinceAutoStart)
      The Orin's chain: the shared AprilTag gates, then 6328's and AOS's extra rejections. Each has its own reason, so RejectionCounts shows why frames were dropped.
      Parameters:
      secondsSinceAutoStart - seconds since auto started, or infinity outside auto
    • orinGyroGates

      public static List<Gate> orinGyroGates(DoubleSupplier secondsSinceAutoStart)
      Gates for the Orin's gyro-based sources (OrinGyroSourceIO): the shared AprilTag gates, the z range, single-tag distance and start-of-auto gates. No ambiguity or heading gate: the heading is the robot's own.
      Parameters:
      secondsSinceAutoStart - seconds since auto started, or infinity outside auto