Ball Kalman Filter - #3900
Open
StarrryNight wants to merge 46 commits into
Open
Ball Kalman Filter#3900StarrryNight wants to merge 46 commits into
StarrryNight wants to merge 46 commits into
Conversation
StarrryNight
marked this pull request as ready for review
August 24, 2026 16:50
StarrryNight
requested review from
Andrewyx,
nycrat,
sauravbanna and
williamckha
as code owners
August 24, 2026 16:50
sunghyuneun
reviewed
Aug 26, 2026
sunghyuneun
left a comment
Contributor
There was a problem hiding this comment.
This pr was very informative, thank you.
Crazy work on your description.
| double min_max_magnitude_average; | ||
| }; | ||
| // KF Dimensions | ||
| // State: position x, position y, veloity x, velocity y |
| static constexpr int STATE_SIZE = 4; | ||
| // Measurement: x and y from vision | ||
| static constexpr int MEASUREMENT_SIZE = 2; | ||
| // No control |
Contributor
There was a problem hiding this comment.
unsure how true this statement is, but perhaps it might be unclear to readers that we're using a linear kalman filter to track, which is why we have a control size?
might be beneficial to mention that in one of the comments above.
| boost::circular_buffer<BallDetection> ball_detections); | ||
| static std::optional<BallDetection> getBestBallDetection( | ||
| const std::vector<BallDetection>& new_ball_detections, | ||
| const Rectangle& filter_area); |
Contributor
There was a problem hiding this comment.
The param filter_area was deleted and replaced with field, and in ball_filter.cpp it was replaced with a field.fieldBoundary(), so the comment above and this parameter should change to reflect that
|
|
||
| return regularized_innovation_covariance.completeOrthogonalDecomposition() | ||
| .pseudoInverse(); | ||
| } |
Contributor
There was a problem hiding this comment.
appreciate how this is much clearer
StarrryNight
force-pushed
the
ball_kalman_filter
branch
from
August 29, 2026 04:40
b2430e5 to
be12302
Compare
mkhlb
self-requested a review
August 29, 2026 20:01
…ots into ball_kalman_filter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
TLDR:
Implements a kalman filter with several heuristics to estimate the position of the ball. The kalman filter nominally functions as well as the old filter, while removing catastrophic failures (position error >3) by 30x and reducing overall positional MSE by 29x. In addition, velocity estimation is also improved by approximately 3x.
This PR replaces the current linear-regression based ball tracker. The original ball filter test doesn't apply anymore and they are temporarily disabled.
Results
Results taken by running thunderscope in ci mode, record stats, enable realism.
MSE & Median Error Comparison
Error Distribution Comparison
Non-technical explanation
A kalman filter is an algorithm that combines the prediction of a physical model and measurement from the real world. Every frame, based on how confident we are of the prediction and measurement, we mathematically blend them into one state estimate.
E.g. (not mathematically accurate)
Prediction (confidence 70%): position at 1
Measurement (confidence 30%): Position at 2
We take the state estimate at 1.3
The prediction and measurement is a gaussian probability. The variance of each individual variable is recorded in the covariance matrix. It is the analog of the uncertainty we have on each one of them. The higher the covariance, the lower our confidence. Now, you can see how we blend two gaussian probabilities into one final estimation:

If we run a vanilla kalman filter on a ball moving and slowing down, our estimate will almost always be in the middle of the prediction and the measurement. Our physical model will always be ahead (it doesnt take account of friction) and our measurement will always be behind (it has a delay). This is precisely what the kalman filter does when the ball is in free flight, when there are no robots, field boundaries, or goals nearby.
Heuristic number 1: Collision handling
When the ball is near an obstacle (robot, goalpost, or boundary), our standard ballistic model breaks down because external impulses (kicks/bounces) cannot be anticipated from vision alone. In these zones:
We increase prediction covariance, forcing the filter to prioritize incoming vision measurements over model extrapolation.
We damp/zero the velocity state to prevent momentum-carryover artifacts (velocity spikes/overshoot) when contact occurs.
Heuristic number 2: Bad packet handling
We also need to know when to reject bad positional data as they are noisy. There are two gating applied. First, the mahalanobis gating is used as a statistical method. It basically reject position data based on how many standard deviations it is from the measurement gaussian distribution. Second, is a physical gating. We calculate how fast the ball must travel to reach the new packet destination from the current state. If it is too high, the packet is probably wrong and we reject it
Note that we count how many rejects we have had in a row. If we have 3 bad packets in a row that means our current estimate is probably wrong. Then we will use the new packet and reset our filter.
Heuristic number 3: Occlusion handling
When the ball is blocked by a robot, vision measurements cease entirely. In this state, Heuristic 1 is bypassed so the filter relies strictly on the physical motion model to propagate the ball's position until we get packets again.
Heuristic number 4: Breakbeam
After our breambeam triggers and we put the ball in dribble position, we force update the kalman filter state to be the dribble position with 0 velocity. In addition, the thresholding used for breakbeam trigger is also updated to use the kalman filter prediction of the frame instead of the initial occlusion frame.
Resolved Issues
#2169
#2752
#3689
#3009
Length Justification and Key Files to Review
Tests
num_dropped_detections_before_ball_not_in_dribblerReview Checklist
It is the reviewers responsibility to also make sure every item here has been covered
.hfile) should have a javadoc style comment at the start of them. For examples, see the functions defined inthunderbots/software/geom. Similarly, all classes should have an associated Javadoc comment explaining the purpose of the class.TODO(or similar) statements should either be completed or associated with a github issue