Skip to content
2 min read
In progress

Advanced Pose Estimation: Multi-Tag Fusion and Standard Deviations

Tune how much your estimator trusts vision vs. odometry, fuse multiple AprilTags, and reject bad measurements for centimeter-level field localization.

Lesson
49 / 51
Reward
+10XP

Sign in to track progress, earn XP, and save lessons.

Basic odometry drifts; basic vision is jumpy. The art of competitive localization is fusing them with the right trust levels so the robot knows where it is to a few centimeters across a whole match. WPILib's SwerveDrivePoseEstimator does this via Kalman-style standard deviations.

Standard deviations are the trust knobs

The estimator weighs each source by its declared uncertainty. Smaller std-dev = more trust. You set odometry trust at construction and vision trust per measurement (or globally):

m_poseEstimator = new SwerveDrivePoseEstimator(
    m_kinematics, gyroAngle, modulePositions, initialPose,
    VecBuilder.fill(0.05, 0.05, 0.01),   // state (odometry) std devs: x,y,theta
    VecBuilder.fill(0.5, 0.5, 9999999)); // default vision std devs

A huge theta std-dev for vision tells the estimator to ignore vision heading and trust the gyro -- the standard MegaTag2 pattern, since the IMU is more reliable for yaw than a single tag.

Scale trust with distance and tag count

The smart move is dynamic std-devs: trust vision more when you see multiple tags up close, less when one tag is far away (where small angle errors mean large position errors).

var est = LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2("");
if (est != null && est.tagCount > 0) {
  double xyStd = (est.tagCount >= 2) ? 0.5 : 1.2;        // tighter with 2+ tags
  xyStd *= (1 + est.avgTagDist * est.avgTagDist * 0.1);  // looser when far
  m_poseEstimator.setVisionMeasurementStdDevs(
      VecBuilder.fill(xyStd, xyStd, 9999999));
  m_poseEstimator.addVisionMeasurement(est.pose, est.timestampSeconds);
}

tagCount, avgTagDist, pose, and timestampSeconds are all fields on the LimelightHelpers PoseEstimate struct.

Reject obviously bad data

Guard the update: skip measurements while spinning fast (gyroRate > 720 deg/s), skip poses that land off the field or absurdly far from the current estimate, and skip when tagCount == 0. One bad teleport into the estimator can ruin an auto-align.

Latency compensation

Vision data is always a little old. The estimator latency-compensates if you pass the measurement timestamp (which MegaTag2's timestampSeconds provides) -- it rewinds, inserts the vision sample, and replays odometry forward. Never pass Timer.getFPGATimestamp() as the vision time; pass the camera's capture timestamp.

The payoff

With tuned fusion you can run closed-loop drive-to-pose for auto-scoring and trust odometry through brief vision dropouts. Validate in AdvantageScope's field view: the fused pose should track smoothly and snap gently toward tags, never teleport. This is the difference between vision that helps and vision that fights you.

Spot an error or something out of date?Log in to suggest an edit

Key takeaways

  • Pose estimator trust is set by standard deviations: smaller = more trusted; set a huge vision theta std-dev to let the gyro own heading.
  • Scale vision std-devs dynamically -- tighter with multiple/close tags, looser with one distant tag.
  • Reject bad measurements: skip while spinning fast, off-field, or with zero tags before calling addVisionMeasurement().
  • Pass the camera capture timestamp (MegaTag2 timestampSeconds) so the estimator latency-compensates correctly.

Lesson quiz

Required

Answer all 3 questions correctly to complete this lesson.

01.Why do you set a huge theta standard deviation on vision measurements in the MegaTag2 pattern?

02.In WPILib's SwerveDrivePoseEstimator, what does the vision measurement std-dev matrix represent and in what order?

03.How do larger values in the vision standard-deviation matrix affect the pose estimator?

Answer every question to submit.

All 51 lessons in Programming, Controls & Sensors