Lidar_imu自动标定源码阅读(五)——BALM部分

该代码实现了一个基于八叉树的数据结构,用于构建体素地图,并利用Ceres非线性优化求解外部参数变换矩阵。在多线程环境下,使用mutex保证数据一致性。通过降采样点云并计算特征向量,筛选出特征点,然后构建八叉树结构,递归地将体素切分成更小的块。在每个体素中计算特征值比率,根据阈值保留满足条件的特征点。此外,还提供了用于优化外参的残差函数和优化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

BALM.hpp:利用八叉树的思想,构建体素地图,然后利用ceres非线性优化进行求解,优化外参变化矩阵。

多个线程访问同一资源时,为了保证数据的一致性,最简单的方式就是使用 mutex(互斥锁)。

typedef 为C语言的关键字,作用是为一种数据类型定义一个新名字。

#pragma once

#include <pcl/common/transforms.h>
#include <unordered_map>
// #include <opencv/cv.h>
#include "optimize.hpp"
#include "utils/myso3.hpp"
#include <fstream>
#include <mutex>//互斥锁
#include <thread>//线程类

#define MIN_PS 7

typedef std::vector<Eigen::Vector3d> PL_VEC;
// typedef pcl::PointXYZINormal pcl::PointXYZINormal;
const double one_three = (1.0 / 3.0);
// double feat_eigen_limit[2] = {3 * 3, 2 * 2};
// double feat_eigen_limit[2] = {5 * 5, 3 * 3};
double feat_eigen_limit[2] = {4 * 4, 3 * 3};//特征极限
double opt_feat_eigen_limit[2] = {4 * 4, 3 * 3};//选择专长特征极限

// Key of hash table
class VOXEL_LOC {
public:
  int64_t x, y, z;
  //构造函数
  VOXEL_LOC(int64_t vx = 0, int64_t vy = 0, int64_t vz = 0)
      : x(vx), y(vy), z(vz) {}
  //判断是否与其他值相同
  bool operator==(const VOXEL_LOC &other) const {
    return (x == other.x && y == other.y && z == other.z);
  }
};

// Hash value
namespace std {
template <> struct hash<VOXEL_LOC> {
  size_t operator()(const VOXEL_LOC &s) const {
    using std::size_t;
    using std::hash;
    return ((hash<int64_t>()(s.x) ^ (hash<int64_t>()(s.y) << 1)) >> 1) ^
           (hash<int64_t>()(s.z) << 1);
  }
};
}
//定义M_POINT类
struct M_POINT {
  float xyz[3];
  int count = 0;
};

// get feature point if a single pcd
// Similar with PCL voxelgrid filter
//对仅含单帧点云的数据获取其特征点
void down_sampling_voxel(pcl::PointCloud<pcl::PointXYZINormal> &pl_feat,
                         double voxel_size) {
  if (voxel_size < 0.01) {
    return;
  }

  std::unordered_map<VOXEL_LOC, M_POINT> feat_map;
  uint plsize = pl_feat.size();

  //遍历点云中的每个点的数据,进行下采样,提取特征点
  for (uint i = 0; i < plsize; i++) {
    pcl::PointXYZINormal &p_c = pl_feat[i];
    float loc_xyz[3];
    for (int j = 0; j < 3; j++) {
      loc_xyz[j] = p_c.data[j] / voxel_size;
      if (loc_xyz[j] < 0) {
        loc_xyz[j] -= 1.0;
      }
    }

    VOXEL_LOC position((int64_t)loc_xyz[0], (int64_t)loc_xyz[1],
                       (int64_t)loc_xyz[2]);
   //提取特征点
    auto iter = feat_map.find(position);
    if (iter != feat_map.end()) {
      iter->second.xyz[0] += p_c.x;
      iter->second.xyz[1] += p_c.y;
      iter->second.xyz[2] += p_c.z;
      iter->second.count++;
    } else {
      M_POINT anp;
      anp.xyz[0] = p_c.x;
      anp.xyz[1] = p_c.y;
      anp.xyz[2] = p_c.z;
      anp.count = 1;
      feat_map[position] = anp;
    }
  }
  
  plsize = feat_map.size();
  pl_feat.clear();
  pl_feat.resize(plsize);

  //赋值操作
  uint i = 0;
  for (auto iter = feat_map.begin(); iter != feat_map.end(); ++iter) {
    pl_feat[i].x = iter->second.xyz[0] / iter->second.count;
    pl_feat[i].y = iter->second.xyz[1] / iter->second.count;
    pl_feat[i].z = iter->second.xyz[2] / iter->second.count;
    i++;
  }
}

// P_fix in the paper
// Summation of P_fix
class SIG_VEC_CLASS {
public:
  Eigen::Matrix3d sigma_vTv;
  Eigen::Vector3d sigma_vi;
  int sigma_size;

  SIG_VEC_CLASS() {
    sigma_vTv.setZero();
    sigma_vi.setZero();
    sigma_size = 0;
  }

  void tozero() {
    sigma_vTv.setZero();
    sigma_vi.setZero();
    sigma_size = 0;
  }
};

//定义OCTO_TREE(八叉数)类,对点云数据进行搜索
class OCTO_TREE {
public:
  static int voxel_windowsize;//体素窗口大小
  static std::vector<Eigen::Matrix4d> imu_transmat;//imu传输矩阵
  std::vector<PL_VEC *> plvec_orig;//原始数据
  std::vector<PL_VEC *> plvec_tran;//变换后的数据

  int octo_state; // 0 is end of tree, 1 is not
  PL_VEC sig_vec_points;
  SIG_VEC_CLASS sig_vec;
  int ftype;
  int points_size, sw_points_size;
  double feat_eigen_ratio, feat_eigen_ratio_test;
  pcl::PointXYZINormal ap_centor_direct;
  pcl::PointXYZINormal ap_centor_direct_zero;
  pcl::PointXYZINormal ap_centor_direct_orig;
  std::vector<pcl::PointXYZINormal> ap_centor_direct_origs;
  double voxel_center[3]; // x, y, z
  double quater_length;//四等长
  OCTO_TREE *leaves[8];
  bool is2opt;
  int capacity;
  pcl::PointCloud<pcl::PointXYZINormal> root_centors;//根中心
  //初始化八叉树
  OCTO_TREE(int ft, int capa) : ftype(ft), capacity(capa) {
    octo_state = 0;
    for (int i = 0; i < 8; i++) {
      leaves[i] = nullptr;
    }

    for (int i = 0; i < capacity; i++) {
      plvec_orig.push_back(new PL_VEC());
      plvec_tran.push_back(new PL_VEC());
      // ap_centor_direct_origs.resize(capacity);
    }
    is2opt = true;
  }
  //获取点的大小
  int getpointsize() {
    int num = 0;
    for (int i = 0; i < OCTO_TREE::voxel_windowsize; i++) {
      num += plvec_tran[i]->size();
    }
    return num;
  }
  //通过构造矩阵计算特征向量,得到正常的数据点
  void savePointNormal() {
    int asize;
    for (int i = 0; i < OCTO_TREE::voxel_windowsize; i++) {
      asize = plvec_tran[i]->size();
      Eigen::Matrix3d covMat(Eigen::Matrix3d::Zero());//定义3*3矩阵,初始化为0
      Eigen::Vector3d center(0, 0, 0);
      for (uint j = 0; j < asize; j++) {
        covMat += (*plvec_orig[i])[j] * (*plvec_orig[i])[j].transpose();//得到对每个数据点求和后矩阵
        center += (*plvec_orig[i])[j];//得到对每个向量求和后的向量
      }
      covMat += sig_vec.sigma_vTv;
      center += sig_vec.sigma_vi;
      //计算平均值
      center /= static_cast<double>(asize);
      covMat =
          covMat / static_cast<double>(asize) - center * center.transpose();
      //矩阵特征值分解和特征向量获取
      Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> saes(covMat);
      //得到特征向量Z方向的特征值
      Eigen::Vector3d direct_vec = saes.eigenvectors().col(2 * ftype);

      ap_centor_direct_origs[i].x = center.x();
      ap_centor_direct_origs[i].y = center.y();
      ap_centor_direct_origs[i].z = center.z();
      ap_centor_direct_origs[i].normal_x = direct_vec.x();
      ap_centor_direct_origs[i].normal_y = direct_vec.y();
      ap_centor_direct_origs[i].normal_z = direct_vec.z();
    }
  }
  // Used by "recut",被下面的recut()函数调用
  //功能和savePointNormal()差不多,通过搭建矩阵,计算特征向量,得到正常的数据点
  void calc_eigen() {
    //初始化一些为0的矩阵和向量
    Eigen::Matrix3d covMat(Eigen::Matrix3d::Zero());
    Eigen::Matrix3d covMat_zero(Eigen::Matrix3d::Zero());
    Eigen::Matrix3d covMat_orig(Eigen::Matrix3d::Zero());
    Eigen::Vector3d center(0, 0, 0);
    Eigen::Vector3d center_zero(0, 0, 0);
    Eigen::Vector3d center_orig(0, 0, 0);

    uint asize;
    for (int i = 0; i < OCTO_TREE::voxel_windowsize; i++) {
      if (i == 0) {
        asize = plvec_tran[i]->size();
        for (uint j = 0; j < asize; j++) {
          covMat_zero += (*plvec_tran[i])[j] * (*plvec_tran[i])[j].transpose();//得到对每个数据点求和后矩阵
          center_zero += (*plvec_tran[i])[j];//得到对每个向量求和后的向量
          covMat_orig += (*plvec_orig[i])[j] * (*plvec_orig[i])[j].transpose();//得到对每个数据点求和后矩阵
          center_orig += (*plvec_orig[i])[j];//得到对每个向量求和后的向量
        }
      }
      asize = plvec_tran[i]->size();
      for (uint j = 0; j < asize; j++) {
        covMat += (*plvec_tran[i])[j] * (*plvec_tran[i])[j].transpose();//得到对每个数据点求和后矩阵
        center += (*plvec_tran[i])[j];//得到对每个向量求和后的向量
      }
    }
    //求均值
    covMat += sig_vec.sigma_vTv;
    center += sig_vec.sigma_vi;
    center /= points_size;
    covMat_zero += sig_vec.sigma_vTv;
    center_zero += sig_vec.sigma_vi;
    center_zero /= double(plvec_orig[0]->size());
    covMat_orig += sig_vec.sigma_vTv;
    center_orig += sig_vec.sigma_vi;
    center_orig /= double(plvec_orig[0]->size());
    //得到差值矩阵
    covMat = covMat / points_size - center * center.transpose();
    covMat_zero = covMat_zero / double(plvec_tran[0]->size()) -
                  center_zero * center_zero.transpose();
    covMat_orig = covMat_orig / double(plvec_tran[0]->size()) -
                  center_orig * center_orig.transpose();
    //矩阵特征值分解和特征向量获取
    Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> saes(covMat);
    //计算本质特征比
    feat_eigen_ratio = saes.eigenvalues()[2] / saes.eigenvalues()[ftype];
    //得到特征向量Z方向的特征值
    Eigen::Vector3d direct_vec = saes.eigenvectors().col(2 * ftype);
    //矩阵特征值分解和特征向量获取
    Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> saes_zero(covMat_zero);
     //得到特征向量Z方向的特征值
    Eigen::Vector3d direct_vec_zero = saes_zero.eigenvectors().col(2 * ftype);
    //矩阵特征值分解和特征向量获取
    Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> saes_orig(covMat_orig);
     //得到特征向量Z方向的特征值
    Eigen::Vector3d direct_vec_orig = saes_orig.eigenvectors().col(2 * ftype);
    
    //数据的保存
    ap_centor_direct.x = center.x();
    ap_centor_direct.y = center.y();
    ap_centor_direct.z = center.z();
    ap_centor_direct.normal_x = direct_vec.x();
    ap_centor_direct.normal_y = direct_vec.y();
    ap_centor_direct.normal_z = direct_vec.z();

    ap_centor_direct_zero.x = center_zero.x();
    ap_centor_direct_zero.y = center_zero.y();
    ap_centor_direct_zero.z = center_zero.z();
    ap_centor_direct_zero.normal_x = direct_vec_zero.x();
    ap_centor_direct_zero.normal_y = direct_vec_zero.y();
    ap_centor_direct_zero.normal_z = direct_vec_zero.z();

    ap_centor_direct_orig.x = center_orig.x();
    ap_centor_direct_orig.y = center_orig.y();
    ap_centor_direct_orig.z = center_orig.z();
    ap_centor_direct_orig.normal_x = direct_vec_orig.x();
    ap_centor_direct_orig.normal_y = direct_vec_orig.y();
    ap_centor_direct_orig.normal_z = direct_vec_orig.z();
    // this->savePointNormal();
  }

  // Cut root voxel into small pieces将根体素切成小块
  // frame_head: Position of newest scan in sliding window frame_head:滑动窗口中最新扫描的位置
  void recut(int layer, uint frame_head,
             pcl::PointCloud<pcl::PointXYZINormal> &pl_feat_map) {
    //octo_state == 0,0是树的结尾,1不是
    if (octo_state == 0) {
      points_size = 0;
      for (int i = 0; i < OCTO_TREE::voxel_windowsize; i++) {
        points_size += plvec_tran[i]->size();
      }

      points_size += sig_vec.sigma_size;
      //MIN_PS=7
      if (points_size < MIN_PS) {
        feat_eigen_ratio = -1;
        return;
      }

      calc_eigen(); // calculate eigenvalue ratio调用calc_eigen(),计算特征值比

      // if (isnan(feat_eigen_ratio)) {
      //     feat_eigen_ratio = -1;
      //     return;
      // }
      //对所选取的点的本质特征比进行比较,满足条件进行保存
      if (feat_eigen_ratio >= feat_eigen_limit[ftype]) {
        pl_feat_map.push_back(ap_centor_direct);
        return;
      }

      // if(layer == 3)
      if (layer == 5) {
        return;
      }

      octo_state = 1;
      // All points in slidingwindow should be put into subvoxel滑动窗口中的所有点都应放入子体素中
      frame_head = 0;
    }

    int leafnum;//叶数
    uint a_size;
    //遍历每个数据,对数据是否位于八叉树中的一个分支做出判断,满足条件,给出数据所处在八叉树中的位置
    for (int i = frame_head; i < OCTO_TREE::voxel_windowsize; i++) {
      a_size = plvec_tran[i]->size();
      for (uint j = 0; j < a_size; j++) {
        int xyz[3] = {0, 0, 0};
        for (uint k = 0; k < 3; k++) {
          if ((*plvec_tran[i])[j][k] > voxel_center[k]) {
            xyz[k] = 1;
          }
        }
        leafnum = 4 * xyz[0] + 2 * xyz[1] + xyz[2];
        if (leaves[leafnum] == nullptr) {
          leaves[leafnum] = new OCTO_TREE(ftype, capacity);
          leaves[leafnum]->voxel_center[0] =
              voxel_center[0] + (2 * xyz[0] - 1) * quater_length;
          leaves[leafnum]->voxel_center[1] =
              voxel_center[1] + (2 * xyz[1] - 1) * quater_length;
          leaves[leafnum]->voxel_center[2] =
              voxel_center[2] + (2 * xyz[2] - 1) * quater_length;
          leaves[leafnum]->quater_length = quater_length / 2;
        }
        leaves[leafnum]->plvec_orig[i]->push_back((*plvec_orig[i])[j]);
        leaves[leafnum]->plvec_tran[i]->push_back((*plvec_tran[i])[j]);
      }
    }
    //layer 层
    if (layer != 0) {
      for (int i = frame_head; i < OCTO_TREE::voxel_windowsize; i++) {
        if (plvec_tran[i]->size() != 0) {
          std::vector<Eigen::Vector3d>().swap(*plvec_orig[i]);
          std::vector<Eigen::Vector3d>().swap(*plvec_tran[i]);
        }
      }
    }

    layer++;
    for (uint i = 0; i < 8; i++) {
      if (leaves[i] != nullptr) {
        leaves[i]->recut(layer, frame_head, pl_feat_map);
      }
    }
  }
};

int OCTO_TREE::voxel_windowsize = 0;
std::vector<Eigen::Matrix4d> OCTO_TREE::imu_transmat =
    std::vector<Eigen::Matrix4d>(0);

void clear_tree(OCTO_TREE *root) {
  if (root != nullptr) {
    for (int i = 0; i < 8; i++) {
      clear_tree(root->leaves[i]);
    }
    // for (int i = 0; i < root->plvec_orig.size(); i++) {
    //     // PL_VEC().swap(*(root->plvec_orig[i]));
    //     // PL_VEC().swap(*(root->plvec_tran[i]));
    //     delete(root->plvec_orig[i]);
    //     delete(root->plvec_tran[i]);
    // }
    delete (root);
    for (int i = 0; i < 8; i++) {
      if (root->leaves[i] != NULL)
        root->leaves[i] = NULL;
    }
    root = NULL;
  }
}

// Put feature points into root voxel 将特征点放入根体素中
// feat_map: The hash table which manages voxel map feat_ map:管理体素贴图的哈希表
// pl_feat: Current feature pointcloud pl_feat:点云的当前特性
// R_p, t_p: Current pose R_p,t_ p 当前姿势
// feattype: 0 is surf, 1 is corn 0表示面点,1表示角点
// fnum: The position in sliding window fnum:滑动窗口中的位置
// capacity: The capacity of sliding window, a little bigger than windowsize 容量:滑动窗口的容量,略大于WindowsSize
///
void cut_voxel(std::unordered_map<VOXEL_LOC, OCTO_TREE *> &feat_map,
               pcl::PointCloud<pcl::PointXYZI>::Ptr pl_feat, Eigen::Matrix4d T,
               int feattype, int fnum, int capacity) {
  double voxel_size[2] = {1, 1}; // {surf, corn}
  uint plsize = pl_feat->size();
  for (uint i = 0; i < plsize; i++) {
    // Transform point to world coordinate将点转换为世界坐标
    pcl::PointXYZI &p_c = pl_feat->points[i];
    Eigen::Vector3d pvec_orig(p_c.x, p_c.y, p_c.z);
    Eigen::Vector3d pvec_tran =
        T.block<3, 3>(0, 0) * pvec_orig + T.block<3, 1>(0, 3);

    // Eigen::Vector3d pvec_tran = R_p * pvec_orig + t_p;

    // Determine the key of hash table确定哈希表的键
    float loc_xyz[3];
    for (int j = 0; j < 3; j++) {
      loc_xyz[j] = pvec_tran[j] / voxel_size[feattype];
      if (loc_xyz[j] < 0) {
        loc_xyz[j] -= 1.0;
      }
    }
    VOXEL_LOC position((int64_t)loc_xyz[0], (int64_t)loc_xyz[1],
                       (int64_t)loc_xyz[2]);

    // Find corresponding voxel找到对应的体素
    auto iter = feat_map.find(position);
    if (iter != feat_map.end()) {
      iter->second->plvec_orig[fnum]->push_back(pvec_orig);
      iter->second->plvec_tran[fnum]->push_back(pvec_tran);
      iter->second->is2opt = true;
    } else // If not finding, build a new voxel如果未找到,则构建新体素
    {
      OCTO_TREE *ot = new OCTO_TREE(feattype, capacity);
      ot->plvec_orig[fnum]->push_back(pvec_orig);
      ot->plvec_tran[fnum]->push_back(pvec_tran);

      // Voxel center coordinate体素中心坐标
      ot->voxel_center[0] = (0.5 + position.x) * voxel_size[feattype];
      ot->voxel_center[1] = (0.5 + position.y) * voxel_size[feattype];
      ot->voxel_center[2] = (0.5 + position.z) * voxel_size[feattype];
      ot->quater_length = voxel_size[feattype] / 4.0; // A quater of side length
      feat_map[position] = ot;
    }
  }
}

void getVoxelMap(OCTO_TREE *root,
                 pcl::PointCloud<pcl::PointXYZRGB> &display_pcd,
                 int &voxel_num) {
  if (root == nullptr)
    return;
  if (root->octo_state == 0) {
    if (root->plvec_tran.size() != 0) {
      if (root->quater_length > 0.5)
        std::cout << root->quater_length << "  ";
      voxel_num++;
      int r = (rand() % 255 + 10) % 255;
      int g = (rand() % 255 + 10) % 255;
      int b = (rand() % 255 + 10) % 255;
      for (int j = 0; j < OCTO_TREE::voxel_windowsize; j++) {
        // std::cout << current_root->plvec_tran[j]->size() << "  ";
        for (int pt = 0; pt < root->plvec_tran[j]->size(); pt++) {
          pcl::PointXYZRGB pcd_pt;
          pcd_pt.x = (*root->plvec_tran[j])[pt](0);
          pcd_pt.y = (*root->plvec_tran[j])[pt](1);
          pcd_pt.z = (*root->plvec_tran[j])[pt](2);
          pcd_pt.r = r;
          pcd_pt.b = b;
          pcd_pt.g = g;
          display_pcd.push_back(pcd_pt);
        }
      }
    }
    return;
  } else {
    for (int i = 0; i < 8; i++)
      getVoxelMap(root->leaves[i], display_pcd, voxel_num);
  }
}

void displayVoxelMap(std::unordered_map<VOXEL_LOC, OCTO_TREE *> map) {
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr display_pcd(
      new pcl::PointCloud<pcl::PointXYZRGB>);
  srand((unsigned)time(NULL));
  int voxel_num = 0;
  for (auto iter = map.begin(); iter != map.end(); ++iter) {
    getVoxelMap(iter->second, *display_pcd, voxel_num);
  }
  std::cout << "voxel num: " << voxel_num << std::endl;
  std::cout << "display pcd " << display_pcd->points.size() << std::endl;
  pcl::PCDWriter writer;
  writer.write("voxel_map.pcd", *display_pcd);
}
void getVoxelResidual1(OCTO_TREE *root, ceres::Problem &problem,
                       double *deltaRPY, double *deltaT) {
  if (root == nullptr)
    return;
  if (root->octo_state == 0) {
    if ((*root->plvec_orig[0]).size() != 0) {
      Eigen::Vector3d point_average;
      Eigen::Vector3d normal;
      point_average(0) = root->ap_centor_direct_zero.x;
      point_average(1) = root->ap_centor_direct_zero.y;
      point_average(2) = root->ap_centor_direct_zero.z;
      normal(0) = root->ap_centor_direct_zero.normal_x;
      normal(1) = root->ap_centor_direct_zero.normal_y;
      normal(2) = root->ap_centor_direct_zero.normal_z;
      for (int j = 0; j < OCTO_TREE::voxel_windowsize; j++) {
        Eigen::Matrix4d imu_tran = OCTO_TREE::imu_transmat[j];
        for (int pt = 0; pt < root->plvec_orig[j]->size(); pt++) {
          Eigen::Vector3d current_laser_point = (*root->plvec_orig[j])[pt];
          ceres::CostFunction *cost_function = BalmVoxelEnergy1::Create(
              current_laser_point, point_average, normal, imu_tran);
          problem.AddResidualBlock(cost_function, nullptr, deltaRPY, deltaT);
        }
      }
    }
    return;
  } else {
    for (int i = 0; i < 8; i++)
      getVoxelResidual1(root->leaves[i], problem, deltaRPY, deltaT);
  }
}

void getVoxelResidual2(OCTO_TREE *root, ceres::Problem &problem,
                       double *deltaRPY, double *deltaT) {
  if (root == nullptr)
    return;
  if (root->octo_state == 0) {
    if ((*root->plvec_orig[0]).size() != 0) {
      Eigen::Matrix4d imu_tran_orig = OCTO_TREE::imu_transmat[0];
      Eigen::Vector3d point_average;
      Eigen::Vector3d normal;
      point_average(0) = root->ap_centor_direct_orig.x;
      point_average(1) = root->ap_centor_direct_orig.y;
      point_average(2) = root->ap_centor_direct_orig.z;
      normal(0) = root->ap_centor_direct_orig.normal_x;
      normal(1) = root->ap_centor_direct_orig.normal_y;
      normal(2) = root->ap_centor_direct_orig.normal_z;
      for (int j = 0; j < OCTO_TREE::voxel_windowsize; j++) {
        Eigen::Matrix4d imu_tran = OCTO_TREE::imu_transmat[j];
        for (int pt = 0; pt < root->plvec_orig[j]->size(); pt++) {
          Eigen::Vector3d current_laser_point = (*root->plvec_orig[j])[pt];
          ceres::CostFunction *cost_function =
              BalmVoxelEnergy2::Create(current_laser_point, point_average,
                                       normal, imu_tran, imu_tran_orig);
          problem.AddResidualBlock(cost_function, nullptr, deltaRPY, deltaT);
        }
      }
    }
    return;
  } else {
    for (int i = 0; i < 8; i++)
      getVoxelResidual2(root->leaves[i], problem, deltaRPY, deltaT);
  }
}

void getVoxelResidual2_NOT(OCTO_TREE *root, ceres::Problem &problem,
                           double *deltaRPY) {
  if (root == nullptr)
    return;
  if (root->octo_state == 0) {
    if ((*root->plvec_orig[0]).size() != 0) {
      Eigen::Matrix4d imu_tran_orig = OCTO_TREE::imu_transmat[0];
      Eigen::Vector3d point_average;
      Eigen::Vector3d normal;
      point_average(0) = root->ap_centor_direct_orig.x;
      point_average(1) = root->ap_centor_direct_orig.y;
      point_average(2) = root->ap_centor_direct_orig.z;
      normal(0) = root->ap_centor_direct_orig.normal_x;
      normal(1) = root->ap_centor_direct_orig.normal_y;
      normal(2) = root->ap_centor_direct_orig.normal_z;
      for (int j = 0; j < OCTO_TREE::voxel_windowsize; j++) {
        Eigen::Matrix4d imu_tran = OCTO_TREE::imu_transmat[j];
        for (int pt = 0; pt < root->plvec_orig[j]->size(); pt++) {
          Eigen::Vector3d current_laser_point = (*root->plvec_orig[j])[pt];
          ceres::CostFunction *cost_function =
              BalmVoxelEnergy2_NOT::Create(current_laser_point, point_average,
                                           normal, imu_tran, imu_tran_orig);
          problem.AddResidualBlock(cost_function, nullptr, deltaRPY);
        }
      }
    }
    return;
  } else {
    for (int i = 0; i < 8; i++)
      getVoxelResidual2_NOT(root->leaves[i], problem, deltaRPY);
  }
}

void getVoxelResidual3(OCTO_TREE *root, ceres::Problem &problem,
                       double *deltaRPY, double *deltaT) {
  if (root == nullptr)
    return;
  if (root->octo_state == 0) {
    if (root->plvec_tran.size() != 0) {
      int point_size = root->getpointsize();
      std::vector<Eigen::Matrix4d> imu_trans = OCTO_TREE::imu_transmat;
      std::vector<pcl::PointXYZINormal> ap_centor_origns;
      std::vector<std::vector<Eigen::Vector3d> *> current_laser_points;
      current_laser_points = root->plvec_orig;
      ap_centor_origns = root->ap_centor_direct_origs;
      for (int j = 0; j < OCTO_TREE::voxel_windowsize; j++) {
        Eigen::Matrix4d cur_imu_tran = OCTO_TREE::imu_transmat[j];
        for (int pt = 0; pt < root->plvec_orig[j]->size(); pt++) {
          Eigen::Vector3d cur_laser_point = (*root->plvec_orig[j])[pt];
          ceres::CostFunction *cost_function = BalmVoxelEnergy3::Create(
              cur_laser_point, cur_imu_tran, current_laser_points,
              ap_centor_origns, imu_trans);
          problem.AddResidualBlock(cost_function, nullptr, deltaRPY, deltaT);
        }
      }
    }
    return;
  } else {
    for (int i = 0; i < 8; i++)
      getVoxelResidual3(root->leaves[i], problem, deltaRPY, deltaT);
  }
}

void getVoxelResidualTrans(OCTO_TREE *root, ceres::Problem &problem,
                           const Eigen::Matrix4d deltaTrans, double *deltaT) {
  if (root == nullptr)
    return;
  if (root->octo_state == 0) {
    if ((*root->plvec_orig[0]).size() != 0) {
      Eigen::Matrix4d imu_tran_orig = OCTO_TREE::imu_transmat[0];
      Eigen::Vector3d point_average;
      Eigen::Vector3d normal;
      point_average(0) = root->ap_centor_direct_orig.x;
      point_average(1) = root->ap_centor_direct_orig.y;
      point_average(2) = root->ap_centor_direct_orig.z;
      normal(0) = root->ap_centor_direct_orig.normal_x;
      normal(1) = root->ap_centor_direct_orig.normal_y;
      normal(2) = root->ap_centor_direct_orig.normal_z;
      for (int j = 0; j < OCTO_TREE::voxel_windowsize; j++) {
        Eigen::Matrix4d imu_tran = OCTO_TREE::imu_transmat[j];
        for (int pt = 0; pt < root->plvec_orig[j]->size(); pt++) {
          Eigen::Vector3d current_laser_point = (*root->plvec_orig[j])[pt];
          ceres::CostFunction *cost_function = BalmVoxelTransEnergy::Create(
              current_laser_point, point_average, normal, imu_tran,
              imu_tran_orig, deltaTrans);
          problem.AddResidualBlock(cost_function, nullptr, deltaT);
        }
      }
    }
    return;
  } else {
    for (int i = 0; i < 8; i++)
      getVoxelResidualTrans(root->leaves[i], problem, deltaTrans, deltaT);
  }
}
// method = 1, 2, 3
void optimizeDeltaTrans(std::unordered_map<VOXEL_LOC, OCTO_TREE *> surf_map,
                        std::unordered_map<VOXEL_LOC, OCTO_TREE *> corn_map,
                        const int &method, double *deltaRPY, double *deltaT) {
  ceres::Problem problem;

  double deltaQ[4];
  ceres::AngleAxisToQuaternion(deltaRPY, deltaQ);
  // ceres::EulerAnglesToQuaternion(deltaRPY, deltaQ);
  std::cout << "deltaQ: " << deltaQ[0] << " " << deltaQ[1] << " " << deltaQ[2]
            << " " << deltaQ[3] << std::endl;

  if (method == 1) {
    for (auto iter = surf_map.begin(); iter != surf_map.end(); ++iter) {
      getVoxelResidual1(iter->second, problem, deltaQ, deltaT);
    }
    for (auto iter = corn_map.begin(); iter != corn_map.end(); ++iter) {
      getVoxelResidual1(iter->second, problem, deltaQ, deltaT);
    }
  }
  if (method == 2) {
    for (auto iter = surf_map.begin(); iter != surf_map.end(); ++iter) {
      getVoxelResidual2(iter->second, problem, deltaQ, deltaT);
    }
    for (auto iter = corn_map.begin(); iter != corn_map.end(); ++iter) {
      getVoxelResidual2(iter->second, problem, deltaQ, deltaT);
    }
  }
  if (method == 3) {
    for (auto iter = surf_map.begin(); iter != surf_map.end(); ++iter) {
      getVoxelResidual3(iter->second, problem, deltaQ, deltaT);
    }
    for (auto iter = corn_map.begin(); iter != corn_map.end(); ++iter) {
      getVoxelResidual3(iter->second, problem, deltaQ, deltaT);
    }
  }
  if (method == 4) {
    for (auto iter = surf_map.begin(); iter != surf_map.end(); ++iter) {
      getVoxelResidual2_NOT(iter->second, problem, deltaQ);
    }
    for (auto iter = corn_map.begin(); iter != corn_map.end(); ++iter) {
      getVoxelResidual2_NOT(iter->second, problem, deltaQ);
    }
  }

  ceres::Solver::Options options;
  options.linear_solver_type = ceres::DENSE_QR;
  options.minimizer_progress_to_stdout = true;
  ceres::Solver::Summary summary;
  ceres::Solve(options, &problem, &summary);
  std::cout << summary.BriefReport() << std::endl;
  ceres::QuaternionToAngleAxis(deltaQ, deltaRPY);
}

参考链接:

C++中的iterator->second_sigma_Tian的博客-优快云博客_iter->second

【PCL自学:ocTree】八叉树(octree)的原理及应用案例(点云压缩,搜索,空间变化)_斯坦福的兔子的博客-优快云博客_八叉树

Eigen学习(九)稠密问题之线性代数和分解_Rap_God的博客-优快云博客_selfadjointeigensolver是什么函数

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值