@@ -18,6 +18,10 @@ Navigation::Navigation(const std::uint32_t axis /*=0*/)
18
18
current_measurements_ (0 ),
19
19
is_imu_reliable_{{true , true , true , true }},
20
20
num_outlier_imus_ (0 ),
21
+ imu_outlier_counter_{{0 , 0 , 0 , 0 }},
22
+ is_encoder_reliable_{{true , true , true , true }},
23
+ num_outlier_encoders_ (0 ),
24
+ encoder_outlier_counter_{{0 , 0 , 0 , 0 }},
21
25
acceleration_ (0 , 0 .),
22
26
velocity_ (0 , 0 .),
23
27
displacement_ (0 , 0 .),
@@ -196,13 +200,17 @@ void Navigation::queryWheelEncoders()
196
200
{
197
201
const auto encoder_data = data_.getSensorsWheelEncoderData ();
198
202
199
- data::nav_t sum = 0 ;
203
+ EncoderArray encoder_data_array;
204
+ uint32_t sum = 0 ;
200
205
for (size_t i = 0 ; i < encoder_data.size (); ++i) {
201
- sum += static_cast <data::nav_t >(encoder_data.at (i).value );
206
+ sum += encoder_data.at (i).value ;
207
+ encoder_data_array.at (i) = encoder_data.at (i).value ;
202
208
}
203
209
204
- const data::nav_t average = sum / encoder_data.size ();
210
+ const data::nav_t average = static_cast <data:: nav_t >( sum / encoder_data.size () );
205
211
encoder_displacement_.value = average * data::Navigation::kWheelCircumfrence ;
212
+
213
+ wheelEncoderOutlierDetection (encoder_data_array);
206
214
}
207
215
208
216
void Navigation::queryImus ()
@@ -225,7 +233,7 @@ void Navigation::queryImus()
225
233
log_.debug (" Raw acceleration values: %.3f, %.3f, %.3f, %.3f" , raw_acceleration_moving[0 ],
226
234
raw_acceleration_moving[1 ], raw_acceleration_moving[2 ], raw_acceleration_moving[3 ]);
227
235
// Run outlier detection on moving axis
228
- imuOutlierDetection (raw_acceleration_moving, kInterQuartileScaler );
236
+ imuOutlierDetection (raw_acceleration_moving);
229
237
// TODO(Justus) how to run outlier detection on non-moving axes without affecting "reliable"
230
238
// Current idea: outlier function takes reliability write flag, on hold until z-score impl.
231
239
@@ -334,15 +342,15 @@ void Navigation::logWrite()
334
342
write_to_file_ = true ;
335
343
}
336
344
337
- Navigation::QuartileBounds Navigation::calculateImuQuartiles (NavigationArray &data_array)
345
+ Navigation::QuartileBounds Navigation::calculateImuQuartiles (const NavigationArray &data_array)
338
346
{
339
347
std::vector<data::nav_t > data_vector;
348
+ std::array<data::nav_t , 3 > quartile_bounds;
340
349
341
350
for (size_t i = 0 ; i < data::Sensors::kNumImus ; ++i) {
342
351
if (is_imu_reliable_.at (i)) { data_vector.push_back (data_array.at (i)); }
343
352
}
344
353
std::sort (data_vector.begin (), data_vector.end ());
345
- std::array<data::nav_t , 3 > quartile_bounds;
346
354
347
355
quartile_bounds.at (0 ) = (data_vector.at (0 ) + data_vector.at (1 )) / 2 .;
348
356
quartile_bounds.at (2 )
@@ -360,9 +368,36 @@ Navigation::QuartileBounds Navigation::calculateImuQuartiles(NavigationArray &da
360
368
return quartile_bounds;
361
369
}
362
370
363
- void Navigation::imuOutlierDetection (NavigationArray &data_array, const data:: nav_t threshold )
371
+ Navigation::QuartileBounds Navigation::calculateEncoderQuartiles ( const EncoderArray &data_array )
364
372
{
365
- std::array<data::nav_t , 3 > quartile_bounds = calculateImuQuartiles (data_array);
373
+ std::vector<uint32_t > data_vector;
374
+
375
+ for (size_t i = 0 ; i < data::Sensors::kNumEncoders ; ++i) {
376
+ if (is_encoder_reliable_.at (i)) { data_vector.push_back (data_array.at (i)); }
377
+ }
378
+ std::sort (data_vector.begin (), data_vector.end ());
379
+
380
+ std::array<data::nav_t , 3 > quartile_bounds;
381
+ quartile_bounds.at (0 ) = (data_vector.at (0 ) + data_vector.at (1 )) / 2 .;
382
+ quartile_bounds.at (2 )
383
+ = (data_vector.at (data_vector.size () - 2 ) + data_vector.at (data_vector.size () - 1 )) / 2 .;
384
+ if (num_outlier_encoders_ == 0 ) {
385
+ quartile_bounds.at (1 ) = (data_vector.at (1 ) + data_vector.at (2 )) / 2 .;
386
+ } else if (num_outlier_encoders_ == 1 ) {
387
+ quartile_bounds.at (1 ) = data_vector.at (1 );
388
+ } else {
389
+ auto navigation_data = data_.getNavigationData ();
390
+ navigation_data.module_status = data::ModuleStatus::kCriticalFailure ;
391
+ data_.setNavigationData (navigation_data);
392
+ log_.error (" At least two Encoders no longer reliable, entering CriticalFailure." );
393
+ }
394
+ return quartile_bounds;
395
+ }
396
+
397
+ void Navigation::imuOutlierDetection (NavigationArray &data_array)
398
+ {
399
+ const QuartileBounds quartile_bounds = calculateImuQuartiles (data_array);
400
+ static constexpr uint8_t threshold = kInterQuartileScaler ;
366
401
367
402
// find the thresholds
368
403
// clip IQR to upper bound to avoid issues with very large outliers
@@ -371,7 +406,7 @@ void Navigation::imuOutlierDetection(NavigationArray &data_array, const data::na
371
406
const auto lower_limit = quartile_bounds.at (0 ) - threshold * iqr;
372
407
// replace any outliers with the median
373
408
for (std::size_t i = 0 ; i < data::Sensors::kNumImus ; ++i) {
374
- const auto exceeds_limits = data_array.at (i) < lower_limit || data_array.at (i) > upper_limit;
409
+ const bool exceeds_limits = data_array.at (i) < lower_limit || data_array.at (i) > upper_limit;
375
410
if (exceeds_limits && is_imu_reliable_.at (i)) {
376
411
log_.debug (" Outlier detected in IMU %d, reading: %.3f not in [%.3f, %.3f]. Updated to %.3f" ,
377
412
i + 1 , data_array.at (i), lower_limit, upper_limit, quartile_bounds.at (1 ));
@@ -392,6 +427,40 @@ void Navigation::imuOutlierDetection(NavigationArray &data_array, const data::na
392
427
}
393
428
}
394
429
430
+ void Navigation::wheelEncoderOutlierDetection (EncoderArray &data_array)
431
+ {
432
+ const QuartileBounds quartile_bounds = calculateEncoderQuartiles (data_array);
433
+ static constexpr uint8_t threshold = kInterQuartileScaler ;
434
+
435
+ // find the thresholds
436
+ // clip IQR to upper bound to avoid issues with very large outliers
437
+ const auto iqr = std::min (quartile_bounds.at (2 ) - quartile_bounds.at (0 ), kMaxInterQuartileRange );
438
+ const auto upper_limit = quartile_bounds.at (2 ) + threshold * iqr;
439
+ const auto lower_limit = quartile_bounds.at (0 ) - threshold * iqr;
440
+ // replace any outliers with the median
441
+ for (std::size_t i = 0 ; i < data::Sensors::kNumEncoders ; ++i) {
442
+ const bool exceeds_limits = data_array.at (i) < lower_limit || data_array.at (i) > upper_limit;
443
+ if (exceeds_limits && is_encoder_reliable_.at (i)) {
444
+ log_.debug (
445
+ " Outlier detected in Encoder %d, reading: %.3f not in [%.3f, %.3f]. Updated to %.3f" , i + 1 ,
446
+ data_array.at (i), lower_limit, upper_limit, quartile_bounds.at (1 ));
447
+ data_array.at (i) = quartile_bounds.at (1 );
448
+ encoder_outlier_counter_.at (i)++;
449
+ // If this counter exceeds some threshold then that encoder is deemed unreliable
450
+ if (encoder_outlier_counter_.at (i) > 1000 && is_encoder_reliable_.at (i)) {
451
+ is_encoder_reliable_.at (i) = false ;
452
+ ++num_outlier_encoders_;
453
+ }
454
+ if (num_outlier_encoders_ > 1 ) {
455
+ status_ = data::ModuleStatus::kCriticalFailure ;
456
+ log_.error (" At least two Wheel Encoders no longer reliable, entering CriticalFailure." );
457
+ }
458
+ } else {
459
+ encoder_outlier_counter_.at (i) = 0 ;
460
+ }
461
+ }
462
+ }
463
+
395
464
void Navigation::updateData ()
396
465
{
397
466
data::Navigation nav_data;
@@ -439,4 +508,4 @@ void Navigation::initialiseTimestamps()
439
508
log_.debug (" Initial timestamp:%d" , initial_timestamp_);
440
509
previous_timestamp_ = initial_timestamp;
441
510
}
442
- } // namespace hyped::navigation
511
+ } // namespace hyped::navigation
0 commit comments