Skip to content

Vision Fusion & Filtration

In the FRC 2026: REBUILT game, absolute field localization is a journey of Discovery. MARSLib’s MARSVision subsystem represents the peak of technical Innovation—combining data from multiple cameras and strictly filtering out “hallucinations” to ensure your robot always knows exactly where it belongs. This level of precision is what enables championship-grade Impact during the most chaotic moments of a match.

Vision poses are frequently wrong during high-speed gameplay. Before a measurement reaches the Pose Estimator, it must survive five strict boundary checks:

  • Z-Height Hallucinations: If the pose estimates the robot is flying (Z > 0.5m), it’s rejected.

  • Out of Bounds: If the pose is tracked outside the physical field, it’s rejected.

  • Motion Blur: If the gyro reports > 120°/s yaw rate, vision is entirely blocked.

  • Beaching (Pitch/Roll): If you ride over an obstacle and tilt > 15°, the pose is rejected.

  • Ambiguity: Low quality single-tag solutions are discarded.

Vision poses should mathematically never be trusted equally. A pose from 6 meters away is tiny on the camera sensor—1 pixel of error dramatically shifts the calculated location.

MARSLib enforces Quadratic StdDev Scaling. Trust in vision decays exponentially at long range, stopping the robot from making violent odometry correction jumps based on far-away tags.

To ensure tuning works globally, the AprilTagVisionIOSim in MARSLib purposefully injects:

  • Gaussian Noise: StdDev matched jitter scaled by distance.

  • Dropped Frames: A 5% chance every frame that no pose is returned.

  • Latency: Simulates a 10ms-30ms phase delay offset.

1. Physical Installation & Network:

  • Mount camera slightly upward (10-15 degrees), 12-24 inches off ground.
  • Connect computer to limelight-<team>, go to http://10.0.0.1
  • Set your FRC team number in Settings.

2. Configure AprilTag Pipeline:

  • Pipeline: 0
  • Exposure: 25
  • Brightness: 50
  • Gain: 50

3. Robot Code:

// MARSLib wrapper
private final LimeLight limelight = new LimeLight();
public void periodic() {
if (limelight.getIsTargetFound()) {
SmartDashboard.putNumber("TargetX", limelight.getTx());
}
}

1. Installation:

  • Flash PhotonVision image to Raspberry Pi or OrangePi using BalenaEtcher.
  • Connect computer to PhotonVision network, go to http://10.0.0.1

2. Configure AprilTag Pipeline:

  • Tag family: k16h5 (or 36h11 for newer seasons)
  • Exposure: 15
  • Brightness: 50
  • Gain: 30

3. Robot Code:

// MARSLib IO Abstraction
private final PhotonPoseEstimator poseEstimator;
public void addVisionMeasurements() {
vision.getBestTarget().ifPresent(target -> {
var pose = poseEstimator.update();
pose.ifPresent(p -> odometry.addVisionMeasurement(p.estimatedPose.toPose2d(), p.timestampSeconds));
});
}

Keep vision updates fast:

// Update vision at 50Hz (not faster!)
@Override
public void periodic() {
double now = Timer.getFPGATimestamp();
// Only process measurements every 20ms
if (now - lastUpdate > 0.02) {
addVisionMeasurements();
lastUpdate = now;
}
}