143 std::map<
double, std::vector<std::shared_ptr<ov_core::Feature>>> used_features_map)
146 static bool wait_for_steady_init =
true;
147 vio_packet.quality = -1;
151 wait_for_steady_init =
true;
157 fprintf(stderr,
"[PUBLISH] VIO in FAILED state - REQUESTING RESET\n");
161 memset(&vio_packet, 0,
sizeof(vio_data_t));
162 vio_packet.magic_number = VIO_MAGIC_NUMBER;
163 vio_packet.timestamp_ns = state->_timestamp * 1e9;
164 vio_packet.quality = -1;
165 vio_packet.state = VIO_STATE_FAILED;
168 pipe_server_write(
SIMPLE_CH, (
char *)&vio_packet,
sizeof(vio_data_t));
172 static int64_t last_failed_msg = 0;
173 int64_t now = _apps_time_monotonic_ns();
174 if (now - last_failed_msg > 1000000000)
176 printf(
"[PUBLISH] Published FAILED packet, reset_requested=%d\n",
178 last_failed_msg = now;
184 vio_packet.magic_number = VIO_MAGIC_NUMBER;
185 vio_packet.timestamp_ns = state->_timestamp * 1e9;
193 Eigen::Matrix<double, 4, 1> q_I_G = state->_imu->quat_fej();
199 Eigen::Matrix3d R_I_G = ov_core::quat_2_Rot(q_I_G);
200 auto RPY = ov_core::rot2rpy(R_I_G);
214 RPY(1) = M_PI - RPY(1);
215 RPY(2) = -M_PI + RPY(2);
216 R_I_G = ov_core::rot_x(RPY(0)) * ov_core::rot_y(RPY(1)) * ov_core::rot_z(RPY(2));
220 grav_vec[2] =
static_cast<float>(-9.81);
226 RPY(1) = -M_PI + RPY(1);
227 RPY(2) = M_PI - RPY(2);
228 R_I_G = ov_core::rot_x(RPY(0)) * ov_core::rot_y(RPY(1)) * ov_core::rot_z(RPY(2));
232 grav_vec[2] =
static_cast<float>(9.81);
238 printf(
"Frame transform is not initialized. Not publishing packet.\n");
242 memcpy(vio_packet.gravity_vector, grav_vec,
sizeof(
float) * 3);
247 Eigen::Matrix<double, 3, 1> v_I_G = ov2frd * state->_imu->vel();
249 Eigen::Matrix<double, 3, 1> p_I_G = ov2frd * (state->_imu->pos());
252 static Eigen::Matrix<double, 3, 3> ned_rot_zero = R_I_G;
253 R_I_G = R_I_G * ned_rot_zero.transpose();
254 v_I_G = ned_rot_zero * v_I_G;
255 p_I_G = ned_rot_zero * p_I_G;
258 if (
vio_state.load() == VIO_STATE_INITIALIZING)
264 for (
int i = 0; i < 3; i++)
266 vio_packet.T_imu_wrt_vio[i] =
static_cast<float>(p_I_G(i));
267 vio_packet.vel_imu_wrt_vio[i] =
static_cast<float>(v_I_G(i));
269 alt_z.store(
static_cast<float>(p_I_G(2)), std::memory_order_release);
270 for (
int i = 0; i < 3; i++)
272 for (
int j = 0; j < 3; j++)
274 vio_packet.R_imu_to_vio[i][j] =
static_cast<float>(R_I_G(i, j));
277 q_I_G = ov_core::rot_2_quat(R_I_G);
283 for (
int i = 0; i < 3; i++)
285 vio_packet.imu_angular_vel[i] = 0.0f;
291 Eigen::Matrix<double, 3, 1> ang_vel_imu =
dirtyOmega(q_I_G, past_q_I_G, state->_timestamp - past_state->_timestamp);
292 for (
int i = 0; i < 3; i++)
294 vio_packet.imu_angular_vel[i] =
static_cast<float>(ang_vel_imu(i));
300 std::vector<std::shared_ptr<ov_type::Type>> statevars;
301 statevars.push_back(state->_imu->p());
302 statevars.push_back(state->_imu->q());
303 statevars.push_back(state->_imu->v());
304 Eigen::Matrix<double, 9, 9> covariance_posori =
305 ov_msckf::StateHelper::get_marginal_covariance(state,
309 vio_packet.pose_covariance[0] =
static_cast<float>(covariance_posori(0, 0));
310 vio_packet.pose_covariance[6] =
static_cast<float>(covariance_posori(1, 1));
311 vio_packet.pose_covariance[11] =
static_cast<float>(covariance_posori(2, 2));
312 vio_packet.pose_covariance[15] =
static_cast<float>(covariance_posori(3, 3));
313 vio_packet.pose_covariance[18] =
static_cast<float>(covariance_posori(4, 4));
314 vio_packet.pose_covariance[20] =
static_cast<float>(covariance_posori(5, 5));
315 vio_packet.velocity_covariance[0] =
static_cast<float>(covariance_posori(6, 6));
316 vio_packet.velocity_covariance[6] =
static_cast<float>(covariance_posori(7, 7));
317 vio_packet.velocity_covariance[11] =
static_cast<float>(covariance_posori(8, 8));
320 Eigen::Matrix3d cam_out = ov_core::quat_2_Rot(state->_calib_IMUtoCAM[0]->quat()).transpose();
321 for (
int i = 0; i < 3; i++)
323 for (
int j = 0; j < 3; j++)
325 vio_packet.R_cam_to_imu[i][j] =
static_cast<float>(cam_out(i, j));
329 Eigen::Vector3d t_cam_wrt_imu = -(ov_core::quat_2_Rot(state->_calib_IMUtoCAM[0]->quat()).transpose() * state->_calib_IMUtoCAM[0]->pos());
330 for (
int i = 0; i < 3; i++)
332 vio_packet.T_cam_wrt_imu[i] =
static_cast<float>(t_cam_wrt_imu(i));
343 if (covariance_posori(3, 3) < 0.0 || covariance_posori(4, 4) < 0.0 || covariance_posori(5, 5) < 0.0)
345 fprintf(stderr,
"ERROR: covariance diagonal went negative\n");
350 static int64_t last_sent_timestamp_ns = 0;
351 if (vio_packet.timestamp_ns < last_sent_timestamp_ns)
356 first_packet =
false;
361 int64_t time_diff = last_sent_timestamp_ns - vio_packet.timestamp_ns;
362 if (time_diff > 1000000)
364 fprintf(stderr,
"WARNING: skipping pose data from the past %ld %ld (diff: %ld ns)\n",
365 vio_packet.timestamp_ns, last_sent_timestamp_ns, time_diff);
366 printf(
"[DEBUG-HK] Setting ERROR_CODE_BAD_TIMESTAMP in VIO packet\n");
371 last_sent_timestamp_ns = vio_packet.timestamp_ns;
374 double V_uncertainty = 0.0;
375 V_uncertainty += covariance_posori(6, 6) * covariance_posori(6, 6);
376 V_uncertainty += covariance_posori(7, 7) * covariance_posori(7, 7);
377 V_uncertainty += covariance_posori(8, 8) * covariance_posori(8, 8);
378 V_uncertainty = sqrt(V_uncertainty);
382 fprintf(stderr,
"ERROR: exceeded velocity uncertainty threshold %f vs %f\n",
388 double current_velocity = state->_imu->vel().norm();
391 fprintf(stderr,
"ERROR: exceeded maximum velocity %f vs %f\n",
396 std::unordered_map<size_t, std::shared_ptr<ov_type::Landmark>> SLAM_FEATS = state->_features_SLAM;
400 vio_packet.n_feature_points =
static_cast<uint16_t
>(SLAM_FEATS.size());
403 static int64_t last_good_feat_ts = 0;
404 static int64_t last_good_qual_ts = 0;
405 static bool wait_for_features =
true;
407 static int64_t last_good_state_ns = 0;
410 const uint32_t current_reset_count =
reset_num_counter.load(std::memory_order_acquire);
411 if (last_reset_count != current_reset_count)
415 last_good_state_ns = 0;
416 wait_for_steady_init =
true;
417 last_reset_count = current_reset_count;
418 wait_for_features =
true;
419 last_good_feat_ts = vio_packet.timestamp_ns;
420 last_good_qual_ts = vio_packet.timestamp_ns;
424 printf(
"[QUALITY] VIO reset detected (reset_count=%u), feature/quality tracking reset\n",
425 current_reset_count);
430 if (wait_for_features)
432 vio_error_codes.fetch_and(~ERROR_CODE_NO_FEATURES, std::memory_order_relaxed);
435 last_good_feat_ts = vio_packet.timestamp_ns;
436 wait_for_features =
false;
443 last_good_feat_ts = vio_packet.timestamp_ns;
446 double ts = (vio_packet.timestamp_ns - last_good_feat_ts) * 1e-9;
449 fprintf(stderr,
"ERROR: insufficient features for too long! cur: %d, min_req: %d\n",
452 wait_for_features =
true;
457 static int64_t start_spin_time = 0;
458 static bool spinning_detected =
false;
460 double yawrate = vio_packet.imu_angular_vel[2];
462 fabs(vio_packet.vel_imu_wrt_vio[0]) <= 1.0 &&
463 fabs(vio_packet.vel_imu_wrt_vio[1]) <= 1.0);
465 if (!spinning_in_place)
467 start_spin_time = vio_packet.timestamp_ns;
468 spinning_detected =
false;
470 else if (!spinning_detected)
472 double spin_duration = (vio_packet.timestamp_ns - start_spin_time) * 1e-9;
475 fprintf(stderr,
"ERROR: exceeded spin rate over time threshold %f!\n",
fast_yaw_timeout_s);
477 spinning_detected =
true;
488 double T_uncertainty = 0.0;
489 T_uncertainty += covariance_posori(0, 0) * covariance_posori(0, 0);
490 T_uncertainty += covariance_posori(1, 1) * covariance_posori(1, 1);
491 T_uncertainty += covariance_posori(2, 2) * covariance_posori(2, 2);
492 T_uncertainty = sqrt(T_uncertainty);
494 double R_uncertainty = 0.0;
495 R_uncertainty += covariance_posori(3, 3) * covariance_posori(3, 3);
496 R_uncertainty += covariance_posori(4, 4) * covariance_posori(4, 4);
497 R_uncertainty += covariance_posori(5, 5) * covariance_posori(5, 5);
498 R_uncertainty = sqrt(R_uncertainty);
501 constexpr double max_allowable_cep = 0.15;
502 auto calculate_cep_quality = [max_allowable_cep](
double t_uncertainty) ->
double
504 return std::max(0.0, 100.0 * (1.0 - t_uncertainty / max_allowable_cep));
508 double calculated_quality;
510 const bool throttling_active =
512 vio_state.load(std::memory_order_acquire) == VIO_STATE_OK &&
515 if (!is_during_initialization && !throttling_active)
518 calculated_quality =
calcQuality(used_features_map, SLAM_FEATS, state);
527 calculated_quality = calculate_cep_quality(T_uncertainty);
529 if (
en_debug && is_during_initialization)
531 printf(
"[QUALITY_INIT] VIO initializing - CEP quality: %.1f (features=%d, T_unc=%.4f m)\n",
532 calculated_quality, vio_packet.n_feature_points, T_uncertainty);
534 else if (
en_debug && throttling_active)
536 printf(
"[QUALITY_THROTTLE] Using CEP quality during throttling: %.1f (T_unc=%.4f m, static=%d, acc_no_jerk=%d)\n",
537 calculated_quality, T_uncertainty,
544 calculated_quality = std::max(0.0, std::min(100.0, calculated_quality));
552 if (calculated_quality >= 1)
554 last_good_qual_ts = vio_packet.timestamp_ns;
557 double qual_ts = (vio_packet.timestamp_ns - last_good_qual_ts) * 1e-9;
558 if (qual_ts > ts_threshold)
560 fprintf(stderr,
"ERROR: actual quality was bad for too long!\n");
586 static QualityState quality_state = INITIAL;
587 static int consecutive_above_40 = 0;
588 static int consecutive_below_20 = 0;
592 bool should_reset_quality_state = (last_quality_reset_count != current_reset_count) ||
595 if (should_reset_quality_state)
597 quality_state = INITIAL;
598 consecutive_above_40 = 0;
599 consecutive_below_20 = 0;
600 last_quality_reset_count = current_reset_count;
604 printf(
"[QUALITY] RESET to INITIAL state - will report quality as-is (reset_count=%u)\n",
605 current_reset_count);
610 if (!should_reset_quality_state)
612 switch (quality_state)
618 consecutive_below_20++;
622 consecutive_below_20 = 0;
623 consecutive_above_40 = 0;
625 printf(
"[QUALITY] Transition: INITIAL → BAD (quality degraded)\n");
630 consecutive_below_20 = 0;
634 consecutive_above_40++;
637 quality_state = GOOD;
638 consecutive_above_40 = 0;
639 consecutive_below_20 = 0;
645 consecutive_above_40 = 0;
653 consecutive_above_40++;
656 quality_state = GOOD;
657 consecutive_above_40 = 0;
658 consecutive_below_20 = 0;
664 consecutive_above_40 = 0;
672 consecutive_below_20++;
676 consecutive_below_20 = 0;
677 consecutive_above_40 = 0;
683 consecutive_below_20 = 0;
690 int reported_quality;
691 switch (quality_state)
699 const double cep_quality = calculate_cep_quality(T_uncertainty);
700 reported_quality =
static_cast<int>(cep_quality);
704 printf(
"[QUALITY_INIT] INITIAL state - CEP quality: %.1f (T_unc=%.4f m) [ZUPT done]\n",
705 cep_quality, T_uncertainty);
710 reported_quality = 0;
714 printf(
"[QUALITY_INIT] INITIAL state - Quality: 0 [waiting for ZUPT]\n");
721 reported_quality = 0;
725 reported_quality =
static_cast<int>(calculated_quality);
730 reported_quality = std::max(0, std::min(100, reported_quality));
733 if (
en_debug && (quality_state != GOOD || reported_quality < 10))
735 const char *state_str = (quality_state == INITIAL) ?
"INITIAL" : (quality_state == BAD) ?
"BAD"
737 printf(
"[QUALITY] State: %s, Reported: %d, Calculated: %.1f, Consec(>40): %d, Consec(≤20): %d\n",
738 state_str, reported_quality, calculated_quality,
739 consecutive_above_40, consecutive_below_20);
744 int quality_for_error_detection =
static_cast<int32_t
>(calculated_quality);
745 if (quality_for_error_detection > 100)
746 quality_for_error_detection = 100;
747 if (quality_for_error_detection < 0)
748 quality_for_error_detection = 0;
751 bool in_grace_period =
false;
754 const int64_t now_ns = _apps_time_monotonic_ns();
757 if ((last_good_state_ns == 0 || vio_packet.n_feature_points <
auto_reset_min_features) && wait_for_steady_init)
759 last_good_state_ns = now_ns;
763 const int64_t dt_ok_ns = now_ns - last_good_state_ns;
766 in_grace_period = (dt_ok_ns < grace_ns && wait_for_steady_init);
784 vio_state.store(VIO_STATE_INITIALIZING, std::memory_order_release);
787 vio_packet.state = VIO_STATE_OK;
791 vio_packet.state = VIO_STATE_INITIALIZING;
795 vio_packet.quality = reported_quality;
799 printf(
"[STATE] VIO_STATE_INITIALIZING - vio_manager: %s, initialized: %s, quality: %d\n",
809 vio_packet.state = VIO_STATE_INITIALIZING;
810 vio_state.store(VIO_STATE_INITIALIZING, std::memory_order_release);
811 vio_packet.quality = 0;
815 printf(
"[STATE] VIO_STATE_INITIALIZING (resetting) - quality: %d\n", vio_packet.quality);
819 else if (!in_grace_period &&
820 should_auto_reset(state, quality_for_error_detection, vio_packet.n_feature_points,
821 V_uncertainty, yawrate, current_velocity,
822 vio_packet.vel_imu_wrt_vio[0], vio_packet.vel_imu_wrt_vio[1]))
825 fprintf(stderr,
"WARNING: Auto-reset conditions detected! Quality: %d, Features: %d, V_uncertainty: %f\n",
826 quality_for_error_detection, vio_packet.n_feature_points, V_uncertainty);
828 vio_packet.quality = -1;
829 vio_packet.state = VIO_STATE_FAILED;
830 vio_state.store(VIO_STATE_FAILED, std::memory_order_release);
836 fprintf(stderr,
"[AUTO_RESET] REQUESTING RESET due to auto-reset conditions\n");
842 vio_packet.state = VIO_STATE_OK;
843 vio_state.store(VIO_STATE_OK, std::memory_order_release);
848 if (reported_quality == 0)
850 vio_packet.quality = 0;
855 vio_packet.quality = std::min(reported_quality, 15);
860 printf(
"[GRACE_PERIOD] In grace period: reported_quality=%d, vio_packet.quality=%d\n",
861 reported_quality, vio_packet.quality);
866 wait_for_steady_init =
false;
868 vio_packet.quality = reported_quality;
873 vio_packet.frame = 0;
878 pipe_server_write(
SIMPLE_CH, (
char *)&vio_packet,
sizeof(vio_data_t));
882 ext_vio_data_t ext_vio_packet;
883 ext_vio_packet.v = vio_packet;
884 ext_vio_packet.n_total_features = 0;
886 double current_timestamp = state->_timestamp;
887 auto timestamp_iter = used_features_map.find(current_timestamp);
889 if (timestamp_iter != used_features_map.end())
891 const auto &features = timestamp_iter->second;
894 std::map<int, std::vector<std::shared_ptr<ov_core::Feature>>> features_by_camera;
895 for (
const auto &feature : features)
898 for (
const auto &cam_meas : feature->timestamps)
900 int cam_id =
static_cast<int>(cam_meas.first);
901 features_by_camera[cam_id].push_back(feature);
905 ov_type::LandmarkRepresentation::Representation msckf_ref = state->_options.feat_rep_msckf;
908 for (
const auto &camera_pair : features_by_camera)
910 int cam_id = camera_pair.first;
911 const auto &cam_features = camera_pair.second;
913 Eigen::Matrix3d R_I_C = ov_core::quat_2_Rot(state->_calib_IMUtoCAM[cam_id]->quat()).transpose();
914 Eigen::Vector3d p_I_C = state->_calib_IMUtoCAM[cam_id]->pos();
916 for (
const auto &feature : cam_features)
919 if (feature->timestamps.find(cam_id) == feature->timestamps.end() ||
920 feature->uvs.find(cam_id) == feature->uvs.end())
926 const auto ×tamps = feature->timestamps.at(cam_id);
927 auto time_iter = std::find(timestamps.begin(), timestamps.end(), current_timestamp);
928 if (time_iter == timestamps.end())
933 size_t meas_idx = std::distance(timestamps.begin(), time_iter);
934 const auto &uv_meas = feature->uvs.at(cam_id)[meas_idx];
937 if (SLAM_FEATS.find(feature->featid) != SLAM_FEATS.end())
939 if (ext_vio_packet.n_total_features >= VIO_MAX_REPORTED_FEATURES)
942 int i_global = ext_vio_packet.n_total_features++;
944 ext_vio_packet.features[i_global].id = feature->featid;
945 ext_vio_packet.features[i_global].cam_id = cam_id;
948 ext_vio_packet.features[i_global].pix_loc[0] = uv_meas(0);
949 ext_vio_packet.features[i_global].pix_loc[1] = uv_meas(1);
951 Eigen::Vector3d p_FinG;
952 if (SLAM_FEATS[feature->featid]->_feat_representation == ov_type::LandmarkRepresentation::Representation::GLOBAL_3D)
954 p_FinG = SLAM_FEATS[feature->featid]->get_xyz(
true);
955 p_FinG = ov2frd * p_FinG;
957 else if (SLAM_FEATS[feature->featid]->_feat_representation == ov_type::LandmarkRepresentation::Representation::ANCHORED_MSCKF_INVERSE_DEPTH)
960 Eigen::Vector3d p_FinA = SLAM_FEATS[feature->featid]->get_xyz(
false);
961 Eigen::Vector3d T_I_C(0, 0, 0);
962 p_FinG = R_I_G * (R_I_C * p_FinA + p_I_C) + p_I_G;
966 printf(
"[WARNING] SLAM feat representation: %d not recognized, 3D point locations are likely invalid\n", SLAM_FEATS[feature->featid]->_feat_representation);
969 ext_vio_packet.features[i_global].tsf[0] = p_FinG[0];
970 ext_vio_packet.features[i_global].tsf[1] = p_FinG[1];
971 ext_vio_packet.features[i_global].tsf[2] = p_FinG[2];
973 ext_vio_packet.features[i_global].point_quality = HIGH;
978 if (ext_vio_packet.n_total_features >= VIO_MAX_REPORTED_FEATURES)
981 int i_global = ext_vio_packet.n_total_features++;
983 ext_vio_packet.features[i_global].id = feature->featid;
984 ext_vio_packet.features[i_global].cam_id = cam_id;
987 ext_vio_packet.features[i_global].pix_loc[0] = uv_meas(0);
988 ext_vio_packet.features[i_global].pix_loc[1] = uv_meas(1);
990 Eigen::Vector3d p_FinA = feature->p_FinA;
991 Eigen::Vector3d p_FinG = feature->p_FinG;
993 if (msckf_ref == ov_type::LandmarkRepresentation::Representation::GLOBAL_3D)
995 p_FinG = ov2frd * p_FinG;
997 else if (msckf_ref == ov_type::LandmarkRepresentation::Representation::ANCHORED_MSCKF_INVERSE_DEPTH)
1000 p_FinG = R_I_G * (R_I_C * p_FinA + p_I_C) + p_I_G;
1004 printf(
"[WARNING] MSCKF feat representation: %s not recognized, 3D point locations are likely invalid\n", ov_type::LandmarkRepresentation::as_string(msckf_ref).c_str());
1006 ext_vio_packet.features[i_global].tsf[0] = p_FinG[0];
1007 ext_vio_packet.features[i_global].tsf[1] = p_FinG[1];
1008 ext_vio_packet.features[i_global].tsf[2] = p_FinG[2];
1010 ext_vio_packet.features[i_global].point_quality = MEDIUM;
1016 pipe_server_write(
EXTENDED_CH, (
char *)&ext_vio_packet,
sizeof(ext_vio_data_t));
1178 std::unordered_map<
size_t, std::shared_ptr<ov_type::Landmark>> &slam_features,
1179 std::shared_ptr<ov_msckf::State> state)
1182 const int GRID_SIZE = 5;
1183 const int TARGET_FEATURES_PER_CAM = 50;
1184 const int TARGET_FEATURES_PER_GRID = TARGET_FEATURES_PER_CAM / (GRID_SIZE * GRID_SIZE);
1185 const int CLONES = 11;
1186 const double MAX_GRID_SCORE = 100.0;
1187 const double GRID_FILL_THRESHOLD = 0.5;
1188 const int en_qual_debug = 0;
1190 double total_quality = 0.0;
1191 int total_grids_filled = 0;
1192 int total_grids = 0;
1197 printf(
"[QUALITY_DEBUG] Total features in used_features_map: %zu\n", used_features_map.size());
1198 printf(
"[QUALITY_DEBUG] Total SLAM features in state: %zu\n", slam_features.size());
1202 double current_timestamp = state->_timestamp;
1204 printf(
"[QUALITY_DEBUG] Current timestamp: %.6f\n", current_timestamp);
1207 auto timestamp_iter = used_features_map.find(current_timestamp);
1208 if (timestamp_iter == used_features_map.end())
1212 printf(
"[QUALITY_DEBUG] No features found for current timestamp, returning low quality\n");
1216 const auto &features = timestamp_iter->second;
1218 printf(
"[QUALITY_DEBUG] Features found for current timestamp: %zu\n", features.size());
1221 std::map<int, std::vector<std::shared_ptr<ov_core::Feature>>> features_by_camera;
1222 for (
const auto &feature : features)
1225 for (
const auto &cam_meas : feature->timestamps)
1227 int cam_id =
static_cast<int>(cam_meas.first);
1228 features_by_camera[cam_id].push_back(feature);
1234 printf(
"[QUALITY_DEBUG] Features grouped by camera:\n");
1235 for (
const auto &cam_pair : features_by_camera)
1237 printf(
"[QUALITY_DEBUG] Camera %d: %zu features\n", cam_pair.first, cam_pair.second.size());
1242 int total_active_slam = 0;
1243 int total_active_msckf = 0;
1246 for (
const auto &camera_pair : features_by_camera)
1248 int cam_id = camera_pair.first;
1249 const auto &cam_features = camera_pair.second;
1252 printf(
"[QUALITY_DEBUG] Processing camera %d with %zu features\n", cam_id, cam_features.size());
1255 std::vector<std::vector<int>> grid(GRID_SIZE, std::vector<int>(GRID_SIZE, 0));
1256 std::vector<std::vector<double>> grid_quality(GRID_SIZE, std::vector<double>(GRID_SIZE, 0.0));
1258 int cam_slam_count = 0;
1259 int cam_msckf_count = 0;
1262 for (
const auto &feature : cam_features)
1265 if (feature->timestamps.find(cam_id) != feature->timestamps.end() &&
1266 !feature->uvs_norm.empty() &&
1267 feature->uvs_norm.find(cam_id) != feature->uvs_norm.end() &&
1268 !feature->uvs_norm.at(cam_id).empty())
1271 const auto &uv_norm = feature->uvs_norm.at(cam_id).back();
1273 printf(
"[QUALITY_DEBUG] Feature %zu - Camera %d - UV Norm: (%.3f, %.3f)\n",
1274 feature->featid, cam_id, uv_norm(0), uv_norm(1));
1278 int grid_x =
static_cast<int>((uv_norm(0) + 1.0) * 0.5 * GRID_SIZE);
1279 int grid_y =
static_cast<int>((uv_norm(1) + 1.0) * 0.5 * GRID_SIZE);
1282 grid_x = std::max(0, std::min(GRID_SIZE - 1, grid_x));
1283 grid_y = std::max(0, std::min(GRID_SIZE - 1, grid_y));
1286 double feature_quality = 0.0;
1289 if (slam_features.find(feature->featid) != slam_features.end())
1293 auto slam_feat = slam_features[feature->featid];
1296 printf(
"[QUALITY_DEBUG] Feature %zu (SLAM) - Grid[%d][%d] - Quality field: %.3f, uniq_cam: %d, anchor_cam: %d\n",
1297 feature->featid, grid_x, grid_y, slam_feat->_quality, slam_feat->_unique_camera_id, slam_feat->_anchor_cam_id);
1300 Eigen::MatrixXd slam_cov;
1301 if (slam_feat->id() >= 0)
1304 std::vector<std::shared_ptr<ov_type::Type>> slam_feat_vec = {slam_feat};
1305 slam_cov = ov_msckf::StateHelper::get_marginal_covariance(state, slam_feat_vec);
1310 slam_cov = Eigen::MatrixXd::Identity(3, 3) * 1.4 * 1.4;
1314 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigensolver(slam_cov);
1315 double max_eigenvalue = eigensolver.eigenvalues().maxCoeff();
1319 const double sigma_ref = 1.4;
1320 double cov_quality = 1.0 / (1.0 + max_eigenvalue / sigma_ref);
1323 double feat_quality_field = (slam_feat->_quality >= 0) ? slam_feat->_quality : 0.0;
1326 feature_quality = 0.5 * cov_quality + 0.5 * feat_quality_field;
1330 printf(
"[QUALITY_DEBUG] SLAM Feature %zu - Max eigenvalue: %.6f, Cov quality: %.3f, Final quality: %.3f\n",
1331 feature->featid, max_eigenvalue, cov_quality, feature_quality);
1339 int num_measurements = 0;
1340 for (
const auto &cam_meas : feature->timestamps)
1342 num_measurements +=
static_cast<int>(cam_meas.second.size());
1346 double measurement_weight = 1.0;
1347 if (num_measurements < CLONES)
1349 measurement_weight =
static_cast<double>(num_measurements) / CLONES;
1353 double feat_quality_field = (feature->quality >= 0) ? feature->quality : 0.0;
1356 feature_quality = 0.4 * feat_quality_field * measurement_weight;
1359 printf(
"[QUALITY_DEBUG] Feature %zu (MSCKF) - Grid[%d][%d] - Quality field: %.3f, Measurements: %d, Weight: %.3f, Final quality: %.3f\n",
1360 feature->featid, grid_x, grid_y, feat_quality_field, num_measurements, measurement_weight, feature_quality);
1364 grid[grid_y][grid_x]++;
1365 grid_quality[grid_y][grid_x] += feature_quality;
1369 total_active_slam += cam_slam_count;
1370 total_active_msckf += cam_msckf_count;
1373 printf(
"[QUALITY_DEBUG] Camera %d summary - SLAM: %d, MSCKF: %d\n", cam_id, cam_slam_count, cam_msckf_count);
1376 int grids_with_features = 0;
1377 double camera_grid_score = 0.0;
1380 printf(
"[QUALITY_DEBUG] Camera %d grid analysis:\n", cam_id);
1381 for (
int i = 0; i < GRID_SIZE; i++)
1383 for (
int j = 0; j < GRID_SIZE; j++)
1387 grids_with_features++;
1390 double grid_cell_quality = 0.0;
1391 if (grid[i][j] >= TARGET_FEATURES_PER_GRID)
1394 grid_cell_quality = grid_quality[i][j] / grid[i][j];
1399 double fill_ratio = std::min(1.0,
static_cast<double>(grid[i][j]) / TARGET_FEATURES_PER_GRID);
1400 grid_cell_quality = (grid_quality[i][j] / std::max(1, grid[i][j])) * std::sqrt(fill_ratio);
1402 camera_grid_score += grid_cell_quality;
1405 printf(
"[QUALITY_DEBUG] Grid[%d][%d]: %d features, avg quality: %.3f\n",
1406 i, j, grid[i][j], grid_quality[i][j] / grid[i][j]);
1412 double grid_fill_ratio =
static_cast<double>(grids_with_features) / (GRID_SIZE * GRID_SIZE);
1413 double grid_distribution_score = 0.0;
1415 if (grid_fill_ratio >= GRID_FILL_THRESHOLD)
1418 grid_distribution_score = MAX_GRID_SCORE;
1423 grid_distribution_score = (grid_fill_ratio / GRID_FILL_THRESHOLD) * MAX_GRID_SCORE;
1427 double camera_score = 0.5 * grid_distribution_score + 0.5 * camera_grid_score;
1430 printf(
"[QUALITY_DEBUG] Camera %d scores - Grid fill ratio: %.3f, Grid distribution: %.3f, Grid quality: %.3f, Final camera score: %.3f\n",
1431 cam_id, grid_fill_ratio, grid_distribution_score, camera_grid_score, camera_score);
1433 total_quality += camera_score;
1434 total_grids_filled += grids_with_features;
1435 total_grids += GRID_SIZE * GRID_SIZE;
1439 printf(
"[QUALITY_DEBUG] Overall feature counts - Active SLAM: %d, Active MSCKF: %d\n", total_active_slam, total_active_msckf);
1442 double final_quality = 0.0;
1443 if (!features_by_camera.empty())
1446 final_quality = total_quality / features_by_camera.size();
1451 final_quality = std::max(0.0, std::min(100.0, total_quality));
1454 printf(
"[QUALITY_DEBUG] Final quality calculation - Total quality: %.3f, Num cameras: %zu, Final quality: %.3f\n",
1455 total_quality, features_by_camera.size(), final_quality);
1457 return final_quality;