OpenVINS学习6——VioManagerHelper.cpp,VioManagerOptions.h学习与注释

前言

VioManager类里还有VioManagerHelper.cpp,VioManagerOptions.h这两个文件,也包含了一些函数,这次接着看这个 。

整体分析

void VioManager::initialize_with_gt(Eigen::Matrix<double, 17, 1> imustate)
给一个状态,然后初始化IMU的状态

bool VioManager::try_to_initialize(const ov_core::CameraData &message)
尝试初始化状态

void VioManager::retriangulate_active_tracks(const ov_core::CameraData &message)
此函数将对当前帧中的所有特征重新进行三角测量。
对于系统当前正在跟踪的所有特征,重新对它们进行三角测量。
这对于需要当前点云(例如闭环)的下游应用程序非常有用。
这将尝试对所有点进行三角测量,而不仅仅是更新中使用的点。

cv::Mat VioManager::get_historical_viz_image()
获取我们拥有的轨迹的清晰可视化图像。

std::vectorEigen::Vector3d VioManager::get_features_SLAM()
返回全局框架中的 3d SLAM 特征。

std::vectorEigen::Vector3d VioManager::get_features_ARUCO()
返回全局框架中的 3d ARUCO 特征。
VioManagerOptions.h的主要函数如下:
它分为几个不同的部分:估计器、跟踪器和模拟。如
在这里插入图片描述

源码注释

/*
 * OpenVINS: An Open Platform for Visual-Inertial Research
 * Copyright (C) 2018-2023 Patrick Geneva
 * Copyright (C) 2018-2023 Guoquan Huang
 * Copyright (C) 2018-2023 OpenVINS Contributors
 * Copyright (C) 2018-2019 Kevin Eckenhoff
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "VioManager.h"

#include "feat/Feature.h"
#include "feat/FeatureDatabase.h"
#include "feat/FeatureInitializer.h"
#include "types/LandmarkRepresentation.h"
#include "utils/print.h"

#include "init/InertialInitializer.h"

#include "state/Propagator.h"
#include "state/State.h"
#include "state/StateHelper.h"

using namespace ov_core;
using namespace ov_type;
using namespace ov_msckf;

void VioManager::initialize_with_gt(Eigen::Matrix<double, 17, 1> imustate) {//给一个状态,然后初始化IMU的状态

  // Initialize the system
  //初始化系统
  state->_imu->set_value(imustate.block(1, 0, 16, 1));
  state->_imu->set_fej(imustate.block(1, 0, 16, 1));

  // Fix the global yaw and position gauge freedoms
  // TODO: Why does this break out simulation consistency metrics?
  // 修复全局偏航和位置仪自由度
  // TODO:为什么要打破模拟一致性指标?
  std::vector<std::shared_ptr<ov_type::Type>> order = {state->_imu};
  Eigen::MatrixXd Cov = std::pow(0.02, 2) * Eigen::MatrixXd::Identity(state->_imu->size(), state->_imu->size());
  Cov.block(0, 0, 3, 3) = std::pow(0.017, 2) * Eigen::Matrix3d::Identity(); // q
  Cov.block(3, 3, 3, 3) = std::pow(0.05, 2) * Eigen::Matrix3d::Identity();  // p
  Cov.block(6, 6, 3, 3) = std::pow(0.01, 2) * Eigen::Matrix3d::Identity();  // v (static)
  StateHelper::set_initial_covariance(state, Cov, order);

  // Set the state time
  //设置状态时间
  state->_timestamp = imustate(0, 0);
  startup_time = imustate(0, 0);
  is_initialized_vio = true;

  // Cleanup any features older then the initialization time
  //清除所有比初始化时间早的特征
  trackFEATS->get_feature_database()->cleanup_measurements(state->_timestamp);
  if (trackARUCO != nullptr) {
    trackARUCO->get_feature_database()->cleanup_measurements(state->_timestamp);
  }

  // Print what we init'ed with
  //打印信息
  PRINT_DEBUG(GREEN "[INIT]: INITIALIZED FROM GROUNDTRUTH FILE!!!!!\n" RESET);
  PRINT_DEBUG(GREEN "[INIT]: orientation = %.4f, %.4f, %.4f, %.4f\n" RESET, state->_imu->quat()(0), state->_imu->quat()(1),
              state->_imu->quat()(2), state->_imu->quat()(3));
  PRINT_DEBUG(GREEN "[INIT]: bias gyro = %.4f, %.4f, %.4f\n" RESET, state->_imu->bias_g()(0), state->_imu->bias_g()(1),
              state->_imu->bias_g()(2));
  PRINT_DEBUG(GREEN "[INIT]: velocity = %.4f, %.4f, %.4f\n" RESET, state->_imu->vel()(0), state->_imu->vel()(1), state->_imu->vel()(2));
  PRINT_DEBUG(GREEN "[INIT]: bias accel = %.4f, %.4f, %.4f\n" RESET, state->_imu->bias_a()(0), state->_imu->bias_a()(1),
              state->_imu->bias_a()(2));
  PRINT_DEBUG(GREEN "[INIT]: position = %.4f, %.4f, %.4f\n" RESET, state->_imu->pos()(0), state->_imu->pos()(1), state->_imu->pos()(2));
}

bool VioManager::try_to_initialize(const ov_core::CameraData &message) {//尝试初始化状态
//这应该调用我们的初始化程序并尝试初始化状态。
//将来我们应该从这里调用结构运动代码。
//该功能还可以用于在发生故障后重新初始化系统。
  // Directly return if the initialization thread is running
  // Note that we lock on the queue since we could have finished an update
  // And are using this queue to propagate the state forward. We should wait in this case
  // 如果初始化线程正在运行则直接返回
  // 请注意,我们锁定队列,因为我们本来可以完成更新
  // 并使用该队列向前传播状态。在这种情况下我们应该等待
  if (thread_init_running) {
    std::lock_guard<std::mutex> lck(camera_queue_init_mtx);
    camera_queue_init.push_back(message.timestamp);
    return false;
  }

  // If the thread was a success, then return success!
  //如果线程成功,返回成功
  if (thread_init_success) {
    return true;
  }

  // Run the initialization in a second thread so it can go as slow as it desires
  //在第二个线程运行初始化,从而可以随心所欲地慢下来
  thread_init_running = true;
  std::thread thread([&] {
    // Returns from our initializer
    //从我们的初始化器返回
    double timestamp;
    Eigen::MatrixXd covariance;
    std::vector<std::shared_ptr<ov_type::Type>> order;
    auto init_rT1 = boost::posix_time::microsec_clock::local_time();

    // Try to initialize the system
    // We will wait for a jerk if we do not have the zero velocity update enabled
    // Otherwise we can initialize right away as the zero velocity will handle the stationary case
    // 尝试初始化系统
    // 如果我们没有启用零速度更新,我们将等待一个急动
    // 否则我们可以立即初始化,因为零速度将处理静止情况
    bool wait_for_jerk = (updaterZUPT == nullptr);
    bool success = initializer->initialize(timestamp, covariance, order, state->_imu, wait_for_jerk);

    // If we have initialized successfully we will set the covariance and state elements as needed
    // TODO: set the clones and SLAM features here so we can start updating right away...
    // 如果初始化成功,我们将根据需要设置协方差和状态元素
    // TODO:在此处设置克隆和 SLAM 特征,以便我们可以立即开始更新...
    if (success) {

      // Set our covariance (state should already be set in the initializer)
      // 设置我们的协方差(状态应该已经在初始化器中设置)
      StateHelper::set_initial_covariance(state, covariance, order);

      // Set the state time
      //设置状态时间
      state->_timestamp = timestamp;
      startup_time = timestamp;

      // Cleanup any features older than the initialization time
      // Also increase the number of features to the desired amount during estimation
      // NOTE: we will split the total number of features over all cameras uniformly
      // 清理所有早于初始化时间的特征
      // 在估计期间还将特征数量增加到所需的数量
      // 注意:我们将统一划分所有相机的特征总数
      trackFEATS->get_feature_database()->cleanup_measurements(state->_timestamp);
      trackFEATS->set_num_features(std::floor((double)params.num_pts / (double)params.state_options.num_cameras));
      if (trackARUCO != nullptr) {
        trackARUCO->get_feature_database()->cleanup_measurements(state->_timestamp);
      }

      // If we are moving then don't do zero velocity update4
      // 如果我们正在移动,则不要进行零速度更新
      if (state->_imu->vel().norm() > params.zupt_max_velocity) {
        has_moved_since_zupt = true;
      }

      // Else we are good to go, print out our stats
      // 否则我们就可以开始了,打印出我们的统计数据
      auto init_rT2 = boost::posix_time::microsec_clock::local_time();
      PRINT_INFO(GREEN "[init]: successful initialization in %.4f seconds\n" RESET, (init_rT2 - init_rT1).total_microseconds() * 1e-6);
      PRINT_INFO(GREEN "[init]: orientation = %.4f, %.4f, %.4f, %.4f\n" RESET, state->_imu->quat()(0), state->_imu->quat()(1),
                 state->_imu->quat()(2), state->_imu->quat()(3));
      PRINT_INFO(GREEN "[init]: bias gyro = %.4f, %.4f, %.4f\n" RESET, state->_imu->bias_g()(0), state->_imu->bias_g()(1),
                 state->_imu->bias_g()(2));
      PRINT_INFO(GREEN "[init]: velocity = %.4f, %.4f, %.4f\n" RESET, state->_imu->vel()(0), state->_imu->vel()(1), state->_imu->vel()(2));
      PRINT_INFO(GREEN "[init]: bias accel = %.4f, %.4f, %.4f\n" RESET, state->_imu->bias_a()(0), state->_imu->bias_a()(1),
                 state->_imu->bias_a()(2));
      PRINT_INFO(GREEN "[init]: position = %.4f, %.4f, %.4f\n" RESET, state->_imu->pos()(0), state->_imu->pos()(1), state->_imu->pos()(2));

      // Remove any camera times that are order then the initialized time
      // This can happen if the initialization has taken a while to perform
      // 删除所有顺序为初始化时间的相机时间
      // 如果初始化需要一段时间才能执行,则可能会发生这种情况
      std::lock_guard<std::mutex> lck(camera_queue_init_mtx);
      std::vector<double> camera_timestamps_to_init;
      for (size_t i = 0; i < camera_queue_init.size(); i++) {
        if (camera_queue_init.at(i) > timestamp) {
          camera_timestamps_to_init.push_back(camera_queue_init.at(i));
        }
      }

      // Now we have initialized we will propagate the state to the current timestep
      // In general this should be ok as long as the initialization didn't take too long to perform
      // Propagating over multiple seconds will become an issue if the initial biases are bad
      // 现在我们已经初始化了,我们将把状态传播到当前时间步
      // 一般来说,只要初始化执行时间不长就应该没问题
      // 如果初始偏差不好,那么在多秒内传播将成为一个问题
      size_t clone_rate = (size_t)((double)camera_timestamps_to_init.size() / (double)params.state_options.max_clone_size) + 1;
      for (size_t i = 0; i < camera_timestamps_to_init.size(); i += clone_rate) {
        propagator->propagate_and_clone(state, camera_timestamps_to_init.at(i));
        StateHelper::marginalize_old_clone(state);
      }
      PRINT_DEBUG(YELLOW "[init]: moved the state forward %.2f seconds\n" RESET, state->_timestamp - timestamp);
      thread_init_success = true;
      camera_queue_init.clear();

    } else {
      auto init_rT2 = boost::posix_time::microsec_clock::local_time();
      PRINT_DEBUG(YELLOW "[init]: failed initialization in %.4f seconds\n" RESET, (init_rT2 - init_rT1).total_microseconds() * 1e-6);
      thread_init_success = false;
      std::lock_guard<std::mutex> lck(camera_queue_init_mtx);
      camera_queue_init.clear();
    }

    // Finally, mark that the thread has finished running
    //最后,标记线程已经运行完毕
    thread_init_running = false;
  });

  // If we are single threaded, then run single threaded
  // Otherwise detach this thread so it runs in the background!
  // 如果我们是单线程,则运行单线程
  // 否则分离该线程,使其在后台运行!
  if (!params.use_multi_threading_subs) {
    thread.join();
  } else {
    thread.detach();
  }
  return false;
}

void VioManager::retriangulate_active_tracks(const ov_core::CameraData &message) {
  //此函数将对当前帧中的所有特征重新进行三角测量。
  //对于系统当前正在跟踪的所有特征,重新对它们进行三角测量。
  //这对于需要当前点云(例如闭环)的下游应用程序非常有用。
  //这将尝试对所有点进行三角测量,而不仅仅是更新中使用的点。

  // Start timing
  //开始计时
  boost::posix_time::ptime retri_rT1, retri_rT2, retri_rT3;
  retri_rT1 = boost::posix_time::microsec_clock::local_time();

  // Clear old active track data
  //清楚以前活跃的跟踪数据
  assert(state->_clones_IMU.find(message.timestamp) != state->_clones_IMU.end());
  active_tracks_time = message.timestamp;
  active_image = cv::Mat();
  trackFEATS->display_active(active_image, 255, 255, 255, 255, 255, 255, " ");
  if (!active_image.empty()) {
    active_image = active_image(cv::Rect(0, 0, message.images.at(0).cols, message.images.at(0).rows));
  }
  active_tracks_posinG.clear();
  active_tracks_uvd.clear();

  // Current active tracks in our frontend
  // TODO: should probably assert here that these are at the message time...
  // 我们前端中当前活跃的跟踪
  // TODO:可能应该在这里断言这些是在消息时间...
  auto last_obs = trackFEATS->get_last_obs();
  auto last_ids = trackFEATS->get_last_ids();

  // New set of linear systems that only contain the latest track info
  // 一套新的线性系统,仅包含最新的跟踪信息
  std::map<size_t, Eigen::Matrix3d> active_feat_linsys_A_new;
  std::map<size_t, Eigen::Vector3d> active_feat_linsys_b_new;
  std::map<size_t, int> active_feat_linsys_count_new;
  std::unordered_map<size_t, Eigen::Vector3d> active_tracks_posinG_new;

  // Append our new observations for each camera
  //加入每个相机的新观测
  std::map<size_t, cv::Point2f> feat_uvs_in_cam0;
  for (auto const &cam_id : message.sensor_ids) {

    // IMU historical clone
    //IMU历史的克隆
    Eigen::Matrix3d R_GtoI = state->_clones_IMU.at(active_tracks_time)->Rot();
    Eigen::Vector3d p_IinG = state->_clones_IMU.at(active_tracks_time)->pos();

    // Calibration for this cam_id
    //对当前cam_id的标定
    Eigen::Matrix3d R_ItoC = state->_calib_IMUtoCAM.at(cam_id)->Rot();
    Eigen::Vector3d p_IinC = state->_calib_IMUtoCAM.at(cam_id)->pos();

    // Convert current CAMERA position relative to global
    //将当前相机坐标转变为全局坐标
    Eigen::Matrix3d R_GtoCi = R_ItoC * R_GtoI;
    Eigen::Vector3d p_CiinG = p_IinG - R_GtoCi.transpose() * p_IinC;

    // Loop through each measurement
    //每个测量循环
    assert(last_obs.find(cam_id) != last_obs.end());
    assert(last_ids.find(cam_id) != last_ids.end());
    for (size_t i = 0; i < last_obs.at(cam_id).size(); i++) {

      // Record this feature uv if is seen from cam0
      //如果能从cam0看到则记录这个特征
      size_t featid = last_ids.at(cam_id).at(i);
      cv::Point2f pt_d = last_obs.at(cam_id).at(i).pt;
      if (cam_id == 0) {
        feat_uvs_in_cam0[featid] = pt_d;
      }

      // Skip this feature if it is a SLAM feature (the state estimate takes priority)
      //如果是一个SLAM特征则跳过(状态估算优先)
      if (state->_features_SLAM.find(featid) != state->_features_SLAM.end()) {
        continue;
      }

      // Get the UV coordinate normal
      //获取法线UV坐标
      cv::Point2f pt_n = state->_cam_intrinsics_cameras.at(cam_id)->undistort_cv(pt_d);
      Eigen::Matrix<double, 3, 1> b_i;
      b_i << pt_n.x, pt_n.y, 1;
      b_i = R_GtoCi.transpose() * b_i;
      b_i = b_i / b_i.norm();
      Eigen::Matrix3d Bperp = skew_x(b_i);

      // Append to our linear system
      //加入线性系统
      Eigen::Matrix3d Ai = Bperp.transpose() * Bperp;
      Eigen::Vector3d bi = Ai * p_CiinG;
      if (active_feat_linsys_A.find(featid) == active_feat_linsys_A.end()) {
        active_feat_linsys_A_new.insert({featid, Ai});
        active_feat_linsys_b_new.insert({featid, bi});
        active_feat_linsys_count_new.insert({featid, 1});
      } else {
        active_feat_linsys_A_new[featid] = Ai + active_feat_linsys_A[featid];
        active_feat_linsys_b_new[featid] = bi + active_feat_linsys_b[featid];
        active_feat_linsys_count_new[featid] = 1 + active_feat_linsys_count[featid];
      }

      // For this feature, recover its 3d position if we have enough observations!
      //对这个特征,如果我们有足够观测则恢复3d坐标
      if (active_feat_linsys_count_new.at(featid) > 3) {

        // Recover feature estimate
        //恢复特征估计
        Eigen::Matrix3d A = active_feat_linsys_A_new[featid];
        Eigen::Vector3d b = active_feat_linsys_b_new[featid];
        Eigen::MatrixXd p_FinG = A.colPivHouseholderQr().solve(b);
        Eigen::MatrixXd p_FinCi = R_GtoCi * (p_FinG - p_CiinG);

        // Check A and p_FinCi
        //检查A和p_FinCi
        Eigen::JacobiSVD<Eigen::Matrix3d> svd(A);
        Eigen::MatrixXd singularValues;
        singularValues.resize(svd.singularValues().rows(), 1);
        singularValues = svd.singularValues();
        double condA = singularValues(0, 0) / singularValues(singularValues.rows() - 1, 0);

        // If we have a bad condition number, or it is too close
        // Then set the flag for bad (i.e. set z-axis to nan)
        // 如果我们的条件数不好,或者太接近
        // 然后设置 bad 标志(即将 z 轴设置为 nan)
        if (std::abs(condA) <= params.featinit_options.max_cond_number && p_FinCi(2, 0) >= params.featinit_options.min_dist &&
            p_FinCi(2, 0) <= params.featinit_options.max_dist && !std::isnan(p_FinCi.norm())) {
          active_tracks_posinG_new[featid] = p_FinG;
        }
      }
    }
  }
  size_t total_triangulated = active_tracks_posinG.size();

  // Update active set of linear systems
  //更新线性系统活跃集
  active_feat_linsys_A = active_feat_linsys_A_new;
  active_feat_linsys_b = active_feat_linsys_b_new;
  active_feat_linsys_count = active_feat_linsys_count_new;
  active_tracks_posinG = active_tracks_posinG_new;
  retri_rT2 = boost::posix_time::microsec_clock::local_time();

  // Return if no features
  //没有特征则返回
  if (active_tracks_posinG.empty() && state->_features_SLAM.empty())
    return;

  // Append our SLAM features we have
  //如果有加入SLAM特征
  for (const auto &feat : state->_features_SLAM) {
    Eigen::Vector3d p_FinG = feat.second->get_xyz(false);
    if (LandmarkRepresentation::is_relative_representation(feat.second->_feat_representation)) {
      // Assert that we have an anchor pose for this feature
      //确保当前特征有一个锚点位姿
      assert(feat.second->_anchor_cam_id != -1);
      // Get calibration for our anchor camera
      //从我们的锚点相机得到标定
      Eigen::Matrix3d R_ItoC = state->_calib_IMUtoCAM.at(feat.second->_anchor_cam_id)->Rot();
      Eigen::Vector3d p_IinC = state->_calib_IMUtoCAM.at(feat.second->_anchor_cam_id)->pos();
      // Anchor pose orientation and position
      //锚点位姿旋转和位置
      Eigen::Matrix3d R_GtoI = state->_clones_IMU.at(feat.second->_anchor_clone_timestamp)->Rot();
      Eigen::Vector3d p_IinG = state->_clones_IMU.at(feat.second->_anchor_clone_timestamp)->pos();
      // Feature in the global frame
      //全局坐标系中的特征
      p_FinG = R_GtoI.transpose() * R_ItoC.transpose() * (feat.second->get_xyz(false) - p_IinC) + p_IinG;
    }
    active_tracks_posinG[feat.second->_featid] = p_FinG;
  }

  // Calibration of the first camera (cam0)
  //第一个相机的标定
  std::shared_ptr<Vec> distortion = state->_cam_intrinsics.at(0);
  std::shared_ptr<PoseJPL> calibration = state->_calib_IMUtoCAM.at(0);
  Eigen::Matrix<double, 3, 3> R_ItoC = calibration->Rot();
  Eigen::Matrix<double, 3, 1> p_IinC = calibration->pos();

  // Get current IMU clone state
  //得到当前IMU的克隆状态
  std::shared_ptr<PoseJPL> clone_Ii = state->_clones_IMU.at(active_tracks_time);
  Eigen::Matrix3d R_GtoIi = clone_Ii->Rot();
  Eigen::Vector3d p_IiinG = clone_Ii->pos();

  // 4. Next we can update our variable with the global position
  //    We also will project the features into the current frame
  // 4. 接下来我们可以用全局位置更新我们的变量
  // 我们还将把特征投影到当前帧中
  for (const auto &feat : active_tracks_posinG) {

    // For now skip features not seen from current frame
    // TODO: should we publish other features not tracked in cam0??
    // 现在跳过当前帧中未看到的特征
    // TODO: 我们应该发布 cam0 中未跟踪的其他功能吗?
    if (feat_uvs_in_cam0.find(feat.first) == feat_uvs_in_cam0.end())
      continue;

    // Calculate the depth of the feature in the current frame
    // Project SLAM feature and non-cam0 features into the current frame of reference
    // 计算当前帧中特征的深度
    // 将 SLAM 特征和非 cam0 特征投影到当前参考系中
    Eigen::Vector3d p_FinIi = R_GtoIi * (feat.second - p_IiinG);
    Eigen::Vector3d p_FinCi = R_ItoC * p_FinIi + p_IinC;
    double depth = p_FinCi(2);
    Eigen::Vector2d uv_dist;
    if (feat_uvs_in_cam0.find(feat.first) != feat_uvs_in_cam0.end()) {
      uv_dist << (double)feat_uvs_in_cam0.at(feat.first).x, (double)feat_uvs_in_cam0.at(feat.first).y;
    } else {
      Eigen::Vector2d uv_norm;
      uv_norm << p_FinCi(0) / depth, p_FinCi(1) / depth;
      uv_dist = state->_cam_intrinsics_cameras.at(0)->distort_d(uv_norm);
    }

    // Skip if not valid (i.e. negative depth, or outside of image)
    // 如果无效则跳过(即负深度或图像外部)
    if (depth < 0.1) {
      continue;
    }

    // Skip if not valid (i.e. negative depth, or outside of image)
    // 如果无效则跳过(即负深度或图像外部)
    int width = state->_cam_intrinsics_cameras.at(0)->w();
    int height = state->_cam_intrinsics_cameras.at(0)->h();
    if (uv_dist(0) < 0 || (int)uv_dist(0) >= width || uv_dist(1) < 0 || (int)uv_dist(1) >= height) {
      // PRINT_DEBUG("feat %zu -> depth = %.2f | u_d = %.2f | v_d = %.2f\n",(*it2)->featid,depth,uv_dist(0),uv_dist(1));
      continue;
    }

    // Finally construct the uv and depth
    //最后构建uv和深度
    Eigen::Vector3d uvd;
    uvd << uv_dist, depth;
    active_tracks_uvd.insert({feat.first, uvd});
  }
  retri_rT3 = boost::posix_time::microsec_clock::local_time();

  // Timing information
  PRINT_ALL(CYAN "[RETRI-TIME]: %.4f seconds for triangulation (%zu tri of %zu active)\n" RESET,
            (retri_rT2 - retri_rT1).total_microseconds() * 1e-6, total_triangulated, active_feat_linsys_A.size());
  PRINT_ALL(CYAN "[RETRI-TIME]: %.4f seconds for re-projection into current\n" RESET, (retri_rT3 - retri_rT2).total_microseconds() * 1e-6);
  PRINT_ALL(CYAN "[RETRI-TIME]: %.4f seconds total\n" RESET, (retri_rT3 - retri_rT1).total_microseconds() * 1e-6);
}

cv::Mat VioManager::get_historical_viz_image() {
  //获取我们拥有的轨迹的清晰可视化图像。

  // Return if not ready yet
  //没准备好返回
  if (state == nullptr || trackFEATS == nullptr)
    return cv::Mat();

  // Build an id-list of what features we should highlight (i.e. SLAM)
  // 构建一个我们应该突出显示的功能的 ID 列表(即 SLAM)
  std::vector<size_t> highlighted_ids;
  for (const auto &feat : state->_features_SLAM) {
    highlighted_ids.push_back(feat.first);
  }

  // Text we will overlay if needed
  // 如果需要,我们将覆盖文本
  std::string overlay = (did_zupt_update) ? "zvupt" : "";
  overlay = (!is_initialized_vio) ? "init" : overlay;

  // Get the current active tracks
  // 获取当前活动跟踪
  cv::Mat img_history;
  trackFEATS->display_history(img_history, 255, 255, 0, 255, 255, 255, highlighted_ids, overlay);
  if (trackARUCO != nullptr) {
    trackARUCO->display_history(img_history, 0, 255, 255, 255, 255, 255, highlighted_ids, overlay);
    // trackARUCO->display_active(img_history, 0, 255, 255, 255, 255, 255, overlay);
  }

  // Finally return the image
  //最后返回图像
  return img_history;
}

std::vector<Eigen::Vector3d> VioManager::get_features_SLAM() {//返回全局框架中的 3d SLAM 特征。
  std::vector<Eigen::Vector3d> slam_feats;
  for (auto &f : state->_features_SLAM) {
    if ((int)f.first <= 4 * state->_options.max_aruco_features)
      continue;
    if (ov_type::LandmarkRepresentation::is_relative_representation(f.second->_feat_representation)) {
      // Assert that we have an anchor pose for this feature
      // 确保我们有这个特征的锚点姿态
      assert(f.second->_anchor_cam_id != -1);
      // Get calibration for our anchor camera
      // 获取锚点相机的校准
      Eigen::Matrix<double, 3, 3> R_ItoC = state->_calib_IMUtoCAM.at(f.second->_anchor_cam_id)->Rot();
      Eigen::Matrix<double, 3, 1> p_IinC = state->_calib_IMUtoCAM.at(f.second->_anchor_cam_id)->pos();
      // Anchor pose orientation and position
      // 锚点姿态方向和位置
      Eigen::Matrix<double, 3, 3> R_GtoI = state->_clones_IMU.at(f.second->_anchor_clone_timestamp)->Rot();
      Eigen::Matrix<double, 3, 1> p_IinG = state->_clones_IMU.at(f.second->_anchor_clone_timestamp)->pos();
      // Feature in the global frame
      //全局坐标系中的位置
      slam_feats.push_back(R_GtoI.transpose() * R_ItoC.transpose() * (f.second->get_xyz(false) - p_IinC) + p_IinG);
    } else {
      slam_feats.push_back(f.second->get_xyz(false));
    }
  }
  return slam_feats;
}

std::vector<Eigen::Vector3d> VioManager::get_features_ARUCO() {//返回全局框架中的 3d ARUCO 特征。
  std::vector<Eigen::Vector3d> aruco_feats;
  for (auto &f : state->_features_SLAM) {
    if ((int)f.first > 4 * state->_options.max_aruco_features)
      continue;
    if (ov_type::LandmarkRepresentation::is_relative_representation(f.second->_feat_representation)) {
      // Assert that we have an anchor pose for this feature
      assert(f.second->_anchor_cam_id != -1);
      // Get calibration for our anchor camera
      Eigen::Matrix<double, 3, 3> R_ItoC = state->_calib_IMUtoCAM.at(f.second->_anchor_cam_id)->Rot();
      Eigen::Matrix<double, 3, 1> p_IinC = state->_calib_IMUtoCAM.at(f.second->_anchor_cam_id)->pos();
      // Anchor pose orientation and position
      Eigen::Matrix<double, 3, 3> R_GtoI = state->_clones_IMU.at(f.second->_anchor_clone_timestamp)->Rot();
      Eigen::Matrix<double, 3, 1> p_IinG = state->_clones_IMU.at(f.second->_anchor_clone_timestamp)->pos();
      // Feature in the global frame
      aruco_feats.push_back(R_GtoI.transpose() * R_ItoC.transpose() * (f.second->get_xyz(false) - p_IinC) + p_IinG);
    } else {
      aruco_feats.push_back(f.second->get_xyz(false));
    }
  }
  return aruco_feats;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/299509.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv3开发构建道路交通场景下CCTSDB2021交通标识检测识别系统

交通标志检测是交通标志识别系统中的一项重要任务。与其他国家的交通标志相比&#xff0c;中国的交通标志有其独特的特点。卷积神经网络&#xff08;CNN&#xff09;在计算机视觉任务中取得了突破性进展&#xff0c;在交通标志分类方面取得了巨大的成功。CCTSDB 数据集是由长沙…

【Java】设计模式之两阶段终止

两阶段终止 两阶段终止&#xff0c;即Two Phase Termination。是用来终止线程的套路。 它的思想是&#xff0c;如何在一个线程T1中优雅地终止线程T2&#xff1f;这里的【优雅】指的是给T2一个料理后事的机会。 错误思路&#xff1a; 使用stop方法。stop 方法会真正杀死线程…

SpringMVC的工作流程

SpringMVC的工作流程图 SpringMVC的工作流程 1. 用户通过客户端向服务器发送请求&#xff0c;请求会被 SpringMVC的前端控制器DispatcherServlet所拦截。 2. DispatcherServlet拦截到请求后&#xff0c;会调用HandlerMapping处理器映射器。 3. 处理器映射器根据请求URL找到具…

HNU-数据库系统-实验4-存储过程与事务处理

数据库系统 课程实验4存储过程与事务处理 计科210X 甘晴void 202108010XXX 目录 文章目录 数据库系统 课程实验4<br>存储过程与事务处理实验目的实验环境实验准备表设计初始数据 实验内容4.1 存储过程实验实验内容与要求实验重点和难点实验过程&#xff08;0&#xff0…

八大算法排序@堆排序(C语言版本)

目录 堆排序大堆排序概念算法思想建堆建堆核心算法建堆的代码 排序代码实现 小堆排序代码实现时间复杂度空间复杂度 特性总结 堆排序 堆排序借用的是堆的特性来实现排序功能的。大堆需要满足父节点大于子节点&#xff0c;因此堆顶是整个数组中的最大元素。小堆则相反&#xff0…

【C程序设计】C指针

学习 C 语言的指针既简单又有趣。通过指针&#xff0c;可以简化一些 C 编程任务的执行&#xff0c;还有一些任务&#xff0c;如动态内存分配&#xff0c;没有指针是无法执行的。所以&#xff0c;想要成为一名优秀的 C 程序员&#xff0c;学习指针是很有必要的。 正如您所知道的…

功能强大且易于使用的视频转换软件—Avdshare Video Converter for Mac/win

在当今的数字时代&#xff0c;我们的生活离不开各种形式的媒体娱乐&#xff0c;而视频内容无疑是其中最为受欢迎的一种。然而&#xff0c;我们常常会遇到一些问题&#xff0c;比如我们在电脑上下载的视频无法在手机上播放&#xff0c;或是我们想将视频转换为其他格式以适应不同…

基于Redis + Lua脚本语言 + 注解:构建高效的请求接口限流方案

为什么接口限流 黑客疯狂请求系统接口的某一个接口 而且每次都需要数据库io操作 。如果并发量很大。导致的结果就是 宕机。 解决方案很多 今天我们就先来基于Redis Lua脚本语言 注解&#xff1a;构建高效的请求接口限流方案 限流效果 ~~~~连续点击 源码地址在最下面 lua安装…

JS事件循环

目录 概述1. 堆栈&#xff08;Call Stack&#xff09;2. 堆&#xff08;Heap&#xff09;3. 事件队列&#xff08;Event Queue&#xff09;4. 宿主环境&#xff08;Host Environment&#xff09; 事件循环&#xff08;Event Loop&#xff09;微任务和宏任务&#xff08;Microta…

[JavaWeb玩耍日记]JDBC(不常用)

项目结构 目录 一.快速入门 二.开启事务 三.sql执行对象的executeUpdate方法 四.查询数据库 五.SQL注入案例 六.使用PreparedStatement防止Sql注入 七.数据库连接池 一.快速入门 创建新项目&#xff0c;导入mysql-connector-java-5.1.48的jar包1.使用JDBC更新一条数据有…

【HarmonyOS】装饰器下的状态管理与页面路由跳转实现

从今天开始&#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”&#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前&#xff0c;有必要先了解一下鸿蒙&#xff0c;从你的角度来讲&#xff0c;你认为什么是鸿蒙呢&#xff1f;它出现的意义又是…

使用VS Code远程开发小游戏,并实现公网访问本地游戏

使用VS Code远程开发小游戏&#xff0c;并实现公网访问本地游戏 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 在本篇博客中&#xff0c;我们将分享如何通过VS Code实现远程开发MEN…

Java多态,包,权限修饰符,final关键字

文章目录 今日内容教学目标 第一章 多态1.1 多态的形式1.2 多态的使用场景1.3 多态的定义和前提1.4 多态的运行特点1.5 多态的弊端1.6 引用类型转换1.6.1 为什么要转型1.6.2 向上转型&#xff08;自动转换&#xff09;1.6.3 向下转型&#xff08;强制转换&#xff09;1.6.4 案例…

JWT 详解

前言&#xff1a; 本博客为转载整合博客&#xff08;主打一个&#xff1a;我们只做博客的搬运工&#xff09;&#xff0c;参考博客主要有&#xff1a; https://blog.csdn.net/weixin_45070175/article/details/118559272?ops_request_misc%257B%2522request%255Fid%2522%253A…

【LeetCode每日一题】383. 赎金信(计数模拟)

2024-1-7 文章目录 [383. 赎金信](https://leetcode.cn/problems/ransom-note/)思路&#xff1a;计数模拟 383. 赎金信 思路&#xff1a;计数模拟 1.通过数组对字母进行计数 2.magazine 中的每个字符只能在 ransomNote 中使用一次。 3.判断减一后&#xff0c;是否小于等于0。…

前端ui库搜集

涟漪动画效果 - MDUI 开发文档, Material Design 前端框架添加涟漪动画效果后&#xff0c;会在点击元素时&#xff0c;产生向外扩散的水波纹效果。https://www.mdui.org/docs/ripple#ripple https://semantic-ui.com/ https://getuikit.com/ https://www.purecss.cn/grids.htm…

Linux进程间通讯 -- 管道

Linux进程间通讯 – 管道 文章目录 Linux进程间通讯 -- 管道1. 原理2. 进程间通讯2.1 管道2.1.1 匿名管道 pipe2.2.2 有名管道 FIFO 2.2 信号2.3 共享内存2.4 本地套接字 1. 原理 Linux 进程间通讯&#xff0c;也称为IPC(InterProcess Communication) 在 Linux 中每个进程都具…

Doris初识(01)

Doris初识 初识 Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;也能支持高吞吐的复杂分析场景…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第三天-Linux进程(物联技术666)

更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…

H266/VVC网络适配层概述

视频编码标准的分层结构 视频数据分层的必要性&#xff1a;网络类型的多样性、不同的应用场景对视频有不同的需求。 编码标准的分层结构&#xff1a;为了适应不同网络和应用需求&#xff0c;视频编码数据根据其内容特性被分成若干NAL单元&#xff08;NAL Unit&#xff0c;NALU…