@@ -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;
0 commit comments