Skip to content

Commit a50d26a

Browse files
committed
Increase ball tracker damping during extended occlusion
Blends damping from 0.994 toward 0.3 after 1s grace period to prevent the filter from predicting unbounded motion when the ball is occluded.
1 parent a3d9065 commit a50d26a

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

src/software/sensor_fusion/filter/ball_tracker.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ namespace {
3838
1, 0, 0, 0,
3939
0, 1, 0, 0).finished();
4040

41-
// Empirically measured
42-
const double DAMPING = 1;
41+
// Empirically measured per-second velocity retention during normal tracking
42+
const double DAMPING = 0.994;
43+
// Per-second retention when fully occluded long enough (velocity decays much faster)
44+
const double DAMPING_OCCLUDED = 0.3;
45+
// Occlusion time (seconds) before damping starts to increase
46+
const double OCCLUSION_GRACE_SECONDS = 1.0;
47+
// Occlusion time (seconds) past the grace period at which damping fully transitions to DAMPING_OCCLUDED
48+
const double MAX_OCCLUSION_SECONDS = 1.0;
4349

4450
const double MAHANALOGIS_THRESHOLD = 1;
4551
const int CONSECUTIVE_OUTLIERS_THRESHOLD = 3;
@@ -78,11 +84,17 @@ std::optional<Ball> BallTracker::estimateBallState(
7884
dt = (current_time - *prev_detection_timestamp).toSeconds();
7985
prev_detection_timestamp = current_time;
8086

87+
const double occlusion_seconds = last_measurement_timestamp.has_value()
88+
? (current_time - *last_measurement_timestamp).toSeconds()
89+
: 0.0;
90+
const double alpha = std::min(std::max(occlusion_seconds - OCCLUSION_GRACE_SECONDS, 0.0) / MAX_OCCLUSION_SECONDS, 1.0);
91+
const double effective_damping = DAMPING * (1.0 - alpha) + DAMPING_OCCLUDED * alpha;
92+
8193
Eigen::Matrix<double,4,4> A;
8294
A << 1, 0, dt, 0,
8395
0, 1, 0, dt,
84-
0, 0, DAMPING, 0,
85-
0, 0, 0, DAMPING;
96+
0, 0, effective_damping, 0,
97+
0, 0, 0, effective_damping;
8698
kalman_filter.process_model = A;
8799
kalman_filter.predict(Eigen::Vector<double,1>::Zero());
88100
}
@@ -109,8 +121,9 @@ std::optional<Ball> BallTracker::estimateBallState(
109121
kalman_filter.state_estimate << measurement(0), measurement(1), 0, 0;
110122
kalman_filter.state_covariance = INITIAL_COV;
111123
}
112-
prev_detection_timestamp = current_time;
113-
consecutive_outliers = 0;
124+
prev_detection_timestamp = current_time;
125+
last_measurement_timestamp = current_time;
126+
consecutive_outliers = 0;
114127
}
115128
else if (best_ball_detection){
116129
Eigen::Vector<double, 2> measurement;
@@ -132,6 +145,7 @@ std::optional<Ball> BallTracker::estimateBallState(
132145

133146
if (mahalanobis < MAHANALOGIS_THRESHOLD){
134147
kalman_filter.update(measurement);
148+
last_measurement_timestamp = current_time;
135149
}
136150
else{
137151
consecutive_outliers++;
@@ -140,6 +154,7 @@ std::optional<Ball> BallTracker::estimateBallState(
140154
if (consecutive_outliers > CONSECUTIVE_OUTLIERS_THRESHOLD){
141155
kalman_filter.state_estimate << measurement(0), measurement(1), 0, 0;
142156
kalman_filter.state_covariance = INITIAL_COV;
157+
last_measurement_timestamp = current_time;
143158
consecutive_outliers = 0;
144159
}
145160
prev_detection_timestamp = current_time;

src/software/sensor_fusion/filter/ball_tracker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,5 @@ class BallTracker
6363
int consecutive_outliers;
6464
KalmanFilter<4, 2, 1> kalman_filter;
6565
std::optional<Timestamp> prev_detection_timestamp;
66+
std::optional<Timestamp> last_measurement_timestamp;
6667
};

0 commit comments

Comments
 (0)