Lidar_imu自动标定源码阅读(二)——calibration部分

源码阅读,能力有限,如有某处理解错误,请指出,谢谢。

Lidar_parser_base.h:激光雷达分析器基础

#pragma once

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>

// constexpr float deg2rad = M_PI / 180.0f;

//定义激光校正类
struct LaserCorrection {
  //rot代表旋转、vert代表垂直
  float rot_correction = 0.f; //0.f代表浮点数0
  float vert_correction = 0.f;
  float dist_correction = 0.f;
  bool two_pt_correction_available = false;
  float dist_correction_x = 0.f;
  float dist_correction_y = 0.f;
  float vert_offset_correction = 0.f;
  float horiz_offset_correction = 0.f;
  int max_intensity = 255;
  int min_intensity = 0;
  float focal_distance = 0.f;
  float focal_slope = 0.f;

  /* cached values calculated when the calibration file is read */
  float cos_rot_correction = 1.0f;  ///< cosine of rot_correction
  float sin_rot_correction = 0.f;   ///< sine of rot_correction
  float cos_vert_correction = 0.f;  ///< cosine of vert_correction
  float sin_vert_correction = 1.0f; ///< sine of vert_correction

  int laser_ring = 0; ///< ring number for this laser
};

struct LidarPointXYZIRT {
  PCL_ADD_POINT4D;
  float intensity;
  uint16_t ring;
  double timestamp;
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
} EIGEN_ALIGN16;

POINT_CLOUD_REGISTER_POINT_STRUCT(
    LidarPointXYZIRT,
    (float, x, x)(float, y, y)(float, z, z)(float, intensity, intensity)(
        uint16_t, ring, ring)(double, timestamp, timestamp))

calibration.hpp:对一些函数进行声明,具体实现在cpp中
#include <eigen3/Eigen/Core> //包含Matrix和Array类,基础的线性代数运算和数组操作
#include <eigen3/Eigen/Dense> //包含了Core/Geometry/LU/Cholesky/SVD/QR/Eigenvalues模块
#include <eigen3/Eigen/Geometry> //包含旋转,平移,缩放,2维和3维的各种变换
#include <pcl/filters/voxel_grid.h> //基于体素网格化的滤波
#include <pcl/io/pcd_io.h> //PCD文件的读写
#include <pcl/octree/octree_search.h>//八叉树搜索
#include <pcl/point_cloud.h>//点云类定义
#include <pcl/point_types.h>//点类型定义

#pragma once

#include <eigen3/Eigen/Core> //包含Matrix和Array类,基础的线性代数运算和数组操作
#include <eigen3/Eigen/Dense> //包含了Core/Geometry/LU/Cholesky/SVD/QR/Eigenvalues模块
#include <eigen3/Eigen/Geometry> //包含旋转,平移,缩放,2维和3维的各种变换
#include <pcl/filters/voxel_grid.h> //基于体素网格化的滤波
#include <pcl/io/pcd_io.h> //PCD文件的读写
#include <pcl/octree/octree_search.h>//八叉树搜索
#include <pcl/point_cloud.h>//点云类定义
#include <pcl/point_types.h>//点类型定义

#include "common/Lidar_parser_base.h"

class Calibrator {
public:
  Calibrator();
  ~Calibrator();

  // load data Tl2i代表lidar到imu的外参变换矩阵
  void LoadTimeAndPoes(const std::string &filename, const Eigen::Matrix4d &Tl2i,
                       std::vector<std::string> &lidarTimes,
                       std::vector<Eigen::Matrix4d> &lidarPoses);

  Eigen::Matrix4d GetDeltaTrans(double R[3], double t[3]);

  void Calibration(const std::string lidar_path, const std::string odom_path,
                   const Eigen::Matrix4d init_Tl2i);
  void SaveStitching(const Eigen::Matrix4d transform,
                     const std::string pcd_name);

private:
  int turn_ = 35;
  int window_ = 10;
  std::vector<std::string> lidar_files_;
  std::vector<Eigen::Matrix4d> lidar_poses_;
  // std::vector<pcl::PointCloud<LidarPointXYZIRT>> pcd_seq_;
  double degree_2_radian = 0.017453293;
  std::string lidar_path_;
};

calibration.cpp:在其中用到了genPcdFeature()函数和optimizeDeltaTrans(),会在下篇博客中介绍。

std::unordered_map:

C++ std::unordered_map_肥喵王得福_ฅ・ω・ฅ的博客-CSDN博客_std unordered_mapunordered_map底层基于哈希表实现,拥有快速检索的功能。unordered_map是STL中的一种关联容器。容器中元素element成对出现(std::pair),element.first是该元素的键-key,容器element.second是该元素的键的值-value。unordered_map中每个key是唯一的,插入和查询速度接近于O(1)(在没有冲突的情况下),但是其内部元素的排列顺序是无序的。 1. unordered_map 底层原理2. 功能函数2.1 构造函数2.2..https://blog.csdn.net/u013271656/article/details/113810084?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166018243516781683963863%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166018243516781683963863&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-113810084-null-null.142%5Ev40%5Econtrol,185%5Ev2%5Econtrol&utm_term=%20std%3A%3Aunordered_map%3CVOXEL_LOC%2C%20OCTO_TREE%20*%3E%20surf_map%2C%20corn_map%3B&spm=1018.2226.3001.4187

#include "calibration.hpp"
#include "BALM.hpp"
#include "ceres/ceres.h"
#include "ceres/rotation.h"
#include "gen_BALM_feature.hpp"
#include "logging.hpp"

Calibrator::Calibrator(){

};

Calibrator::~Calibrator(){

};

//利用输入的文件,得到lidar的时间戳和对应的位姿
void Calibrator::LoadTimeAndPoes(const std::string &filename,
                                 const Eigen::Matrix4d &Tl2i,
                                 std::vector<std::string> &lidarTimes,
                                 std::vector<Eigen::Matrix4d> &lidarPoses) {
  std::ifstream file(filename);
  if (!file.is_open()) {
    std::cout << "ERROR--->>> cannot open: " << filename << std::endl;
    exit(1);
  }
  // load pose and lidar timestamp(filename)
  std::string line;
  double max_x, max_y, max_z, min_x, min_y, min_z;
 //INT_MAX表示最大的整数值
  max_x = max_y = max_z = -INT_MAX;
  min_x = min_y = min_z = INT_MAX;
  while (getline(file, line)) {
    std::stringstream ss(line);
    std::string timeStr;
    ss >> timeStr;
    // lidarTimes.emplace_back(timeStr);
    lidar_files_.emplace_back(timeStr);
    Eigen::Matrix4d Ti = Eigen::Matrix4d::Identity();
    ss >> Ti(0, 0) >> Ti(0, 1) >> Ti(0, 2) >> Ti(0, 3) >> Ti(1, 0) >>
        Ti(1, 1) >> Ti(1, 2) >> Ti(1, 3) >> Ti(2, 0) >> Ti(2, 1) >> Ti(2, 2) >>
        Ti(2, 3);
    Ti *= Tl2i;
    max_x = std::max(max_x, Ti(0, 3));
    max_y = std::max(max_y, Ti(1, 3));
    max_z = std::max(max_z, Ti(2, 3));
    min_x = std::min(min_x, Ti(0, 3));
    min_y = std::min(min_y, Ti(1, 3));
    min_z = std::min(min_z, Ti(2, 3));
    lidarPoses.emplace_back(Ti);
  }
  file.close();
}

//将传入的角度和平移变换变为用4*4的矩阵表示,得到deltaT
Eigen::Matrix4d Calibrator::GetDeltaTrans(double R[3], double t[3]) {
  Eigen::Matrix3d deltaR;
  double mat[9];
  // ceres::EulerAnglesToRotationMatrix(R, mat);
  ceres::AngleAxisToRotationMatrix(R, mat);
  deltaR << mat[0], mat[3], mat[6], mat[1], mat[4], mat[7], mat[2], mat[5],
      mat[8];
  // auto deltaR = Eigen::Matrix3d(
  //     Eigen::AngleAxisd(R[2], Eigen::Vector3d::UnitZ()) *
  //     Eigen::AngleAxisd(R[1], Eigen::Vector3d::UnitY()) *
  //     Eigen::AngleAxisd(R[0], Eigen::Vector3d::UnitX()));
  Eigen::Matrix4d deltaT = Eigen::Matrix4d::Identity();
  deltaT.block<3, 3>(0, 0) = deltaR;
  deltaT(0, 3) = t[0];
  deltaT(1, 3) = t[1];
  deltaT(2, 3) = t[2];
  return deltaT;
}

void Calibrator::Calibration(const std::string lidar_path,
                             const std::string odom_path,
                             const Eigen::Matrix4d init_Tl2i) {
  lidar_path_ = lidar_path;
  auto time_begin = std::chrono::steady_clock::now();
  int turn = 35;
  int window = 10;
  //   Eigen::Matrix4d init_Tl2i = Eigen::Matrix4d::Identity();
  Eigen::Matrix<double, 6, 1> last_deltaT;
  //调用LoadTimeAndPoses()函数,传入里程计数据,得到lidar的位姿
  LoadTimeAndPoes(odom_path, init_Tl2i, lidar_files_, lidar_poses_);

  //利用滑动窗口选取关键帧数据,进行外参标定的校正
  std::vector<int> frm_start_box;//定义frm起始框
  std::vector<int> frm_step_box;//定义frm步进盒
  std::vector<int> frm_num_box;//定义frm数字框
  int upper_bound = std::min(int(lidar_files_.size()), 1000);//设置上界
  int start_step = (upper_bound / 2) / turn_ - 1;//设置起始步骤
  //选取turn_(35)个窗口,可以理解为进行35次迭代
  for (int i = 0; i < turn_; i++) {
    int a = upper_bound / 2 - i * start_step - 1;
    frm_start_box.push_back(a);
    frm_step_box.push_back((upper_bound - a) / window_ - 1);
    frm_num_box.push_back(window_);
  }
  double deltaRPY[3] = {0, 0, 0};//RPYci表示欧拉角
  double deltaT[3] = {0, 0, 0};
  for (int i = 0; i < frm_start_box.size(); i++) {
    std::cout << "\n==>ROUND " << i << std::endl;
    int step = frm_step_box[i];
    int start = frm_start_box[i];
    int frmnum = frm_num_box[i];
    // The hash table of voxel map 体素映射的哈希表
    std::unordered_map<VOXEL_LOC, OCTO_TREE *> surf_map, corn_map;
    // build voxel_map
    OCTO_TREE::imu_transmat.clear();
    Eigen::Matrix4d deltaTrans = GetDeltaTrans(deltaRPY, deltaT);
    OCTO_TREE::voxel_windowsize = frmnum;
    int window_size = frmnum; 
    //每个窗口内选取frmnum(10)次数据
    for (size_t frmIdx = 0; frmIdx < frmnum; frmIdx++) {
      int real_frmIdx = start + frmIdx * step;
      std::string lidar_file_name =
          lidar_path + lidar_files_[real_frmIdx] + ".pcd";
      pcl::PointCloud<LidarPointXYZIRT>::Ptr cloud(
          new pcl::PointCloud<LidarPointXYZIRT>);
      if (pcl::io::loadPCDFile(lidar_file_name, *cloud) < 0) {
        std::cout << "cannot open pcd_file: " << lidar_file_name << "\n";
        exit(1);//非正常运行导致退出程序
      }
      pcl::PointCloud<pcl::PointXYZI>::Ptr pl_corn(
          new pcl::PointCloud<pcl::PointXYZI>);
      pcl::PointCloud<pcl::PointXYZI>::Ptr pl_surf(
          new pcl::PointCloud<pcl::PointXYZI>);
      pcl::PointCloud<pcl::PointXYZI>::Ptr pl_surf_sharp(
          new pcl::PointCloud<pcl::PointXYZI>);
      // generate feature points
      // GenFeature feature;
      genPcdFeature(cloud, pl_surf, pl_surf_sharp, pl_corn);
      Eigen::Matrix4d imu_T = lidar_poses_[real_frmIdx];
      Eigen::Matrix4d refined_T = imu_T * deltaTrans;
      OCTO_TREE::imu_transmat.push_back(imu_T);
      if (i < turn / 2) {
        cut_voxel(surf_map, pl_surf_sharp, refined_T, 0, frmIdx,
                  window_size + 5);
      } else {
        cut_voxel(surf_map, pl_surf, refined_T, 0, frmIdx, window_size + 5);
      }
      // if (i > turn / 2)
      //     cut_voxel(corn_map, pl_corn, refined_T, 1, frmIdx, window_size +
      //     5);

      // Points in new frame have been distributed in corresponding root node
      // voxel
      // Then continue to cut the root voxel until right size
      for (auto iter = surf_map.begin(); iter != surf_map.end(); ++iter) {
        if (iter->second->is2opt) // Sliding window of root voxel should
                                  // have points
        {
          iter->second->root_centors.clear();
          iter->second->recut(0, frmIdx, iter->second->root_centors);
        }
      }

      for (auto iter = corn_map.begin(); iter != corn_map.end(); ++iter) {
        if (iter->second->is2opt) {
          iter->second->root_centors.clear();
          iter->second->recut(0, frmIdx, iter->second->root_centors);
        }
      }
    }
    // display
    // displayVoxelMap(surf_map);
    // optimize delta R, t1, t2
    if (i < turn / 2) {
      optimizeDeltaTrans(surf_map, corn_map, 4, deltaRPY, deltaT);
    } else {
      optimizeDeltaTrans(surf_map, corn_map, 2, deltaRPY, deltaT);
    }
    std::cout << "delta rpy: " << deltaRPY[0] / degree_2_radian << " "
              << deltaRPY[1] / degree_2_radian << " "
              << deltaRPY[2] / degree_2_radian << std::endl;
    std::cout << "delta T: " << deltaT[0] << " " << deltaT[1] << " "
              << deltaT[2] << std::endl;

    //  clear tree
    for (auto iter = corn_map.begin(); iter != corn_map.end(); ++iter) {
      clear_tree(iter->second);
    }
    for (auto iter = surf_map.begin(); iter != surf_map.end(); ++iter) {
      clear_tree(iter->second);
    }
    std::cout << "Round Finish!\n";
  }
  //将得到的结果放到bestVal中
  double bestVal[6];
  bestVal[0] = deltaRPY[0];
  bestVal[1] = deltaRPY[1];
  bestVal[2] = deltaRPY[2];
  bestVal[3] = deltaT[0];
  bestVal[4] = deltaT[1];
  bestVal[5] = deltaT[2];
  auto time_end = std::chrono::steady_clock::now();
  std::cout << "calib cost "
            << std::chrono::duration<double>(time_end - time_begin).count()
            << "s" << std::endl;
  // save refined calib 保存得到的外参标定参数,并将数据输出到屏幕上
  std::string refine_calib_file = "./refined_calib_imu_to_lidar.txt";
  Eigen::Matrix4d deltaTrans = Eigen::Matrix4d::Identity();
  SaveStitching(deltaTrans,"before.pcd");
  deltaTrans = GetDeltaTrans(deltaRPY, deltaT);
  SaveStitching(deltaTrans,"after.pcd");
  std::cout << "delta T is:" << std::endl;
  std::cout << deltaTrans << std::endl;
  auto refined_Tl2i = init_Tl2i * deltaTrans;
  auto refined_Ti2l = refined_Tl2i.inverse().eval();
  std::cout << "refined T(imu 2 lidar): " << std::endl;
  std::cout << refined_Ti2l << std::endl;
  std::ofstream fCalib(refine_calib_file);
  if (!fCalib.is_open()) {
    std::cerr << "open file " << refine_calib_file << "failed." << std::endl;
    // return 1;
  }

  fCalib << "refined calib:" << std::endl;
  fCalib << "R: " << refined_Ti2l(0, 0) << " " << refined_Ti2l(0, 1) << " "
         << refined_Ti2l(0, 2) << " " << refined_Ti2l(1, 0) << " "
         << refined_Ti2l(1, 1) << " " << refined_Ti2l(1, 2) << " "
         << refined_Ti2l(2, 0) << " " << refined_Ti2l(2, 1) << " "
         << refined_Ti2l(2, 2) << std::endl;
  fCalib << "t: " << refined_Ti2l(0, 3) << " " << refined_Ti2l(1, 3) << " "
         << refined_Ti2l(2, 3) << std::endl;
  fCalib << "deltaTrans:" << std::endl;
  fCalib << deltaTrans << std::endl;
  fCalib << "delta roll, pitch, yaw, tx, ty, tz:" << std::endl;
  fCalib << bestVal[0] << " " << bestVal[1] << " " << bestVal[2] << " "
         << bestVal[3] << " " << bestVal[4] << " " << bestVal[5] << std::endl;
  fCalib << "delta roll, pitch, yaw, tx, ty, tz from begin:" << std::endl;
  fCalib << bestVal[0] + last_deltaT[0] << " " << bestVal[1] + last_deltaT[1]
         << " " << bestVal[2] + last_deltaT[2] << " "
         << bestVal[3] + last_deltaT[3] << " " << bestVal[4] + last_deltaT[4]
         << " " << bestVal[5] + last_deltaT[5] << std::endl;
  std::cout << "save refined calib to " << refine_calib_file << std::endl;
}

//实现SaveStihching()函数,利用标定后的外参变化矩阵,对点云数据进行校正
void Calibrator::SaveStitching(const Eigen::Matrix4d transform,
                               const std::string pcd_name) {

  pcl::PointCloud<pcl::PointXYZI>::Ptr all_cloud(
      new pcl::PointCloud<pcl::PointXYZI>());
  //实例化all_octree,并将体素分辨率设置为0.3
  pcl::octree::OctreePointCloudSearch<pcl::PointXYZI>::Ptr all_octree(
      new pcl::octree::OctreePointCloudSearch<pcl::PointXYZI>(0.3));

  all_octree->setInputCloud(all_cloud);
  pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(
      new pcl::PointCloud<pcl::PointXYZI>);
  for (size_t i = 0; i < lidar_files_.size(); i++) {
    std::string lidar_file_name = lidar_path_ + lidar_files_[i] + ".pcd";
    if (pcl::io::loadPCDFile(lidar_file_name, *cloud) < 0) {
      LOGW("can not open %s", lidar_file_name);
      return;
    }
    Eigen::Matrix4d T = lidar_poses_[i] * transform;
    for (const auto &src_pt : cloud->points) {
      if (!pcl_isfinite(src_pt.x) || !pcl_isfinite(src_pt.y) ||
          !pcl_isfinite(src_pt.z))
        continue;
      Eigen::Vector3d p(src_pt.x, src_pt.y, src_pt.z);
      Eigen::Vector3d p_res;
      p_res = T.block<3, 3>(0, 0) * p + T.block<3, 1>(0, 3);
      pcl::PointXYZI dst_pt;
      dst_pt.x = p_res(0);
      dst_pt.y = p_res(1);
      dst_pt.z = p_res(2);
      dst_pt.intensity = src_pt.intensity;
      if (!all_octree->isVoxelOccupiedAtPoint(dst_pt)) {
        all_octree->addPointToCloud(dst_pt, all_cloud);
      }
    }
  }
  pcl::io::savePCDFileASCII(pcd_name, *all_cloud);
  all_cloud->clear();
  all_octree->deleteTree();
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个问题翻译成中文是:b'lidar_imu_calib标定过程'。 回答:lidar_imu_calib是指激光雷达和惯性测量单元(IMU)之间的标定过程,通过该过程可以解决激光雷达IMU在安装中的相对姿态和时间同步问题。该过程需要收集一些基准数据,例如IMU的加速度计和陀螺仪数据,以及激光雷达的点云数据。然后将这些数据引入标定算法中,得到激光雷达IMU之间的相对位姿和时间偏差,最后将它们纠正并同步,从而使系统达到更高的精度。 ### 回答2: Lidar_imu_calib是一种激光雷达和惯性测量单元的联合标定方法。它旨在从激光雷达的数据和惯性传感器的数据中获取相机、激光雷达和车辆的位姿(即位置和姿态)信息。 lidar_imu_calib标定过程可以分为以下几步: 1.采集数据:首先需要采集车辆在各种不同的姿态和运动条件下的数据,包括车速变化、车辆俯仰和横滚角变化等。同时,需要记录激光雷达和惯性传感器的输出数据。 2.匹配点云和IMU数据:利用系统时间戳进行点云数据和IMU数据的对齐,通过坐标系变换将两者的数据进行匹配。 3.计算位姿:根据匹配后的数据,计算车辆的位姿,包括车辆位置和姿态(即旋转角度),这是通过解决非线性优化问题来完成的。 4.评估误差:标定结果需要进行评估,通过比较计算出的车辆真实姿态和标定结果之间的差异来确定标定的准确性。 5.优化标定结果:根据评估结果进行标定结果的优化,即根据误差来调整标定结果,以提高标定准确性。 总之,lidar_imu_calib标定是激光雷达与惯性测量单元联合标定的方法,通过匹配点云和IMU数据,计算位姿,评估误差和优化标定结果等步骤,得到车辆的位姿信息,从而提高自动驾驶车辆的安全性和性能。 ### 回答3: Lidar_imu_calib是一种遥感设备,由激光雷达和惯性测量单元(IMU)组成,它的目的是3D空间中的可视化地图构建。Lidar可以测量环境中物体的位置和形状,而IMU可以测量设备的位置和运动状态。因此,Lidar_imu_calib的精度和准确性对于它的应用非常重要。为此,在使用Lidar_imu_calib之前,必须进行标定Lidar_imu_calib标定过程试图确定几何和姿态转换矩阵,这个矩阵用于将LidarIMU测量结果在同一个坐标系下进行配置。其中,几何变换矩阵被用来纠正从LidarIMU获得的点云数据中发生的误差,姿态变换矩阵则用于纠正导致视角变化的角度问题。 标定过程的首要步骤是采集数据,包括LidarIMU的原始数据以及因为设备不同而引起的差异。通过在一定时间内在多个场景下对数据进行采集,可以获得更加丰富的数据,并确保标定能够在多种条件下表示准确。 数据采集之后,接下来需要进行数据处理。主要是通过使用非线性最小乘法以最小化两个矩阵的几何和姿态误差。 这个过程需要大量的计算能力和优秀的算法以最大化标定的准确度。最终的标定参数是由几何和姿态矩阵的组合产生的,并被应用到Lidar_imu_calib设备以及期望的应用程序中。 总之,Lidar_imu_calib标定是一项复杂的过程,它需要充分的数据采集、数据处理和优秀的算法来确保标定结果的准确性和精度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值