livox_mapping算法学习一

一、算法路径

https://github.com/Livox-SDK/livox_mapping

二、代码阅读

1、源文件:scanRegistration.cpp

2、对代码添加注解:

void laserCloudHandler(const sensor_msgs::PointCloud2ConstPtr& laserCloudMsg)
{
  pcl::PointCloud<PointType> laserCloudIn;
  pcl::fromROSMsg(*laserCloudMsg, laserCloudIn);

  int cloudSize = laserCloudIn.points.size();

  if(cloudSize > 32000) cloudSize = 32000;
  
  int count = cloudSize;
  PointType point;
  pcl::PointCloud<PointType> Allpoints;

  //剔除无解数据点,并做了一些没有用的计算(也暂不明白反射率为啥这样搞?)
  for (int i = 0; i < cloudSize; i++) {

    point.x = laserCloudIn.points[i].x;
    point.y = laserCloudIn.points[i].y;
    point.z = laserCloudIn.points[i].z;
    //计算扫描角度
    double theta = std::atan2(laserCloudIn.points[i].y,laserCloudIn.points[i].z) / M_PI * 180 + 180;
    //根据扫描角度分成9份,换算出扫描线ID
    scanID = std::floor(theta / 9);
    float dis = point.x * point.x
                + point.y * point.y
                + point.z * point.z;

    double dis2 = laserCloudIn.points[i].z * laserCloudIn.points[i].z + laserCloudIn.points[i].y * laserCloudIn.points[i].y;
    //计算偏转角
    double theta2 = std::asin(sqrt(dis2/dis)) / M_PI * 180;
    //反射率转反射强度???
    point.intensity = scanID+(laserCloudIn.points[i].intensity/10000);
    //point.intensity = scanID+(double(i)/cloudSize);

    if (!pcl_isfinite(point.x) ||
        !pcl_isfinite(point.y) ||
        !pcl_isfinite(point.z)) {
      continue;
    }
    //剔除非法数据点
    Allpoints.push_back(point);
  }

  pcl::PointCloud<PointType>::Ptr laserCloud(new pcl::PointCloud<PointType>());
  *laserCloud += Allpoints;
  cloudSize = laserCloud->size();

  for (int i = 0; i < cloudSize; i++) {
    CloudFeatureFlag[i] = 0;
  }
    
  pcl::PointCloud<PointType> cornerPointsSharp;

  pcl::PointCloud<PointType> surfPointsFlat;

  pcl::PointCloud<PointType> laserCloudFull;

  int debugnum1 = 0;
  int debugnum2 = 0;
  int debugnum3 = 0;
  int debugnum4 = 0;
  int debugnum5 = 0;

  int count_num = 1;
  bool left_surf_flag = false;
  bool right_surf_flag = false;
  Eigen::Vector3d surf_vector_current(0,0,0);
  Eigen::Vector3d surf_vector_last(0,0,0);
  int last_surf_position = 0;
  double depth_threshold = 0.1;
  //********************************************************************************************************************************************
  //第一次遍历找特征点(面、角)
  for (int i = 5; i < cloudSize - 5; i += count_num ) {
    float depth = sqrt(laserCloud->points[i].x * laserCloud->points[i].x +
                       laserCloud->points[i].y * laserCloud->points[i].y +
                       laserCloud->points[i].z * laserCloud->points[i].z);

    // if(depth < 2) depth_threshold = 0.05;
    // if(depth > 30) depth_threshold = 0.1;
    //left curvature
    float ldiffX = 
                laserCloud->points[i - 4].x + laserCloud->points[i - 3].x
                - 4 * laserCloud->points[i - 2].x
                + laserCloud->points[i - 1].x + laserCloud->points[i].x;

    float ldiffY = 
                laserCloud->points[i - 4].y + laserCloud->points[i - 3].y
                - 4 * laserCloud->points[i - 2].y
                + laserCloud->points[i - 1].y + laserCloud->points[i].y;

    float ldiffZ = 
                laserCloud->points[i - 4].z + laserCloud->points[i - 3].z
                - 4 * laserCloud->points[i - 2].z
                + laserCloud->points[i - 1].z + laserCloud->points[i].z;

    float left_curvature = ldiffX * ldiffX + ldiffY * ldiffY + ldiffZ * ldiffZ;
    //使用左边5个点的以中间那个点作为基准计算出曲率半径来衡量左面的曲率
    if(left_curvature < 0.01){

      std::vector<PointType> left_list;

      for(int j = -4; j < 0; j++){
        left_list.push_back(laserCloud->points[i+j]);
      }
      //曲率在0.001以下的视为平面,只设置左5中点标志位1(平面点)
      if( left_curvature < 0.001) CloudFeatureFlag[i-2] = 1; //surf point flag  && plane_judge(left_list,1000) 
      
      left_surf_flag = true;
    }
    else{
      left_surf_flag = false;
    }

    //right curvature
    float rdiffX = 
                laserCloud->points[i + 4].x + laserCloud->points[i + 3].x
                - 4 * laserCloud->points[i + 2].x
                + laserCloud->points[i + 1].x + laserCloud->points[i].x;

    float rdiffY = 
                laserCloud->points[i + 4].y + laserCloud->points[i + 3].y
                - 4 * laserCloud->points[i + 2].y
                + laserCloud->points[i + 1].y + laserCloud->points[i].y;

    float rdiffZ = 
                laserCloud->points[i + 4].z + laserCloud->points[i + 3].z
                - 4 * laserCloud->points[i + 2].z
                + laserCloud->points[i + 1].z + laserCloud->points[i].z;

    float right_curvature = rdiffX * rdiffX + rdiffY * rdiffY + rdiffZ * rdiffZ;
    //使用右边5个点的以中间那个点作为基准计算出曲率半径来衡量右面的曲率
    if(right_curvature < 0.01){
      std::vector<PointType> right_list;

      for(int j = 1; j < 5; j++){
        right_list.push_back(laserCloud->points[i+j]);
      }
      //曲率在0.001以下的视为平面,只设置右5中点标志位1(平面点)
      if(right_curvature < 0.001 ) CloudFeatureFlag[i+2] = 1; //surf point flag  && plane_judge(right_list,1000)

      //左右都是平面视为左右都在同一平面,则移到平面上最后一个点作为下次计算点
      count_num = 4;
      right_surf_flag = true;
    }
    else{
      //左右不都是平面,则使用下一个点作为下次计算点
      count_num = 1;
      right_surf_flag = false;
    }

    //当前点左右平面都是平面时,根据两平面夹角得出角点
    if(left_surf_flag && right_surf_flag){
     debugnum4 ++;

     Eigen::Vector3d norm_left(0,0,0);
     Eigen::Vector3d norm_right(0,0,0);
     //计算左四点与当前点距离加权矩阵
     for(int k = 1;k<5;k++){
         Eigen::Vector3d tmp = Eigen::Vector3d(laserCloud->points[i-k].x-laserCloud->points[i].x,
                            laserCloud->points[i-k].y-laserCloud->points[i].y,
                            laserCloud->points[i-k].z-laserCloud->points[i].z);
        tmp.normalize();
        norm_left += (k/10.0)* tmp;//k/10.0越远点权重越大
     }
     //计算右四点与当前点距离加权矩阵
     for(int k = 1;k<5;k++){
         Eigen::Vector3d tmp = Eigen::Vector3d(laserCloud->points[i+k].x-laserCloud->points[i].x,
                            laserCloud->points[i+k].y-laserCloud->points[i].y,
                            laserCloud->points[i+k].z-laserCloud->points[i].z);
        tmp.normalize();
        norm_right += (k/10.0)* tmp;
     }

      //左右加权矩阵组成的平面夹角
      double cc = fabs( norm_left.dot(norm_right) / (norm_left.norm()*norm_right.norm()) );
      //计算当前点与左右最远点的矩阵
      Eigen::Vector3d last_tmp = Eigen::Vector3d(laserCloud->points[i-4].x-laserCloud->points[i].x,
                                                 laserCloud->points[i-4].y-laserCloud->points[i].y,
                                                 laserCloud->points[i-4].z-laserCloud->points[i].z);
      Eigen::Vector3d current_tmp = Eigen::Vector3d(laserCloud->points[i+4].x-laserCloud->points[i].x,
                                                    laserCloud->points[i+4].y-laserCloud->points[i].y,
                                                    laserCloud->points[i+4].z-laserCloud->points[i].z);
      //取模,即距离值
      double last_dis = last_tmp.norm();
      double current_dis = current_tmp.norm();
      //如果夹角在60°~120°之间,并且左右最远距离点都大于0.05,则当前点视为角点,设置标志位150
      if(cc < 0.5 && last_dis > 0.05 && current_dis > 0.05 ){ //
        debugnum5 ++;
        CloudFeatureFlag[i] = 150;
      }
    }
  }

  //第二次遍历
  for(int i = 5; i < cloudSize - 5; i ++){
    float diff_left[2];
    float diff_right[2];
    //当前点深度
    float depth = sqrt(laserCloud->points[i].x * laserCloud->points[i].x +
                        laserCloud->points[i].y * laserCloud->points[i].y +
                        laserCloud->points[i].z * laserCloud->points[i].z);
    //分别与左/右近一、二两点的向量差值的深度(后面只用到了近一点的结果)
    for(int count = 1; count < 3; count++ ){
      float diffX1 = laserCloud->points[i + count].x - laserCloud->points[i].x;
      float diffY1 = laserCloud->points[i + count].y - laserCloud->points[i].y;
      float diffZ1 = laserCloud->points[i + count].z - laserCloud->points[i].z;
      diff_right[count - 1] = sqrt(diffX1 * diffX1 + diffY1 * diffY1 + diffZ1 * diffZ1);

      float diffX2 = laserCloud->points[i - count].x - laserCloud->points[i].x;
      float diffY2 = laserCloud->points[i - count].y - laserCloud->points[i].y;
      float diffZ2 = laserCloud->points[i - count].z - laserCloud->points[i].z;
      diff_left[count - 1] = sqrt(diffX2 * diffX2 + diffY2 * diffY2 + diffZ2 * diffZ2);
    }
    //右近一点的深度
    float depth_right = sqrt(laserCloud->points[i + 1].x * laserCloud->points[i + 1].x +
                    laserCloud->points[i + 1].y * laserCloud->points[i + 1].y +
                    laserCloud->points[i + 1].z * laserCloud->points[i + 1].z);
    //左近一点的深度
    float depth_left = sqrt(laserCloud->points[i - 1].x * laserCloud->points[i - 1].x +
                    laserCloud->points[i - 1].y * laserCloud->points[i - 1].y +
                    laserCloud->points[i - 1].z * laserCloud->points[i - 1].z);

     //当与左右近一点向量差值的深度大于当前点深度的0.1倍时,则视为异常点(遗弃),设置标志250,直接进入下一点计算
    if( (diff_right[0] > 0.1*depth && diff_left[0] > 0.1*depth) ){
      debugnum1 ++;  
      CloudFeatureFlag[i] = 250;
      continue;
    }

    //break points
    if(fabs(diff_right[0] - diff_left[0])>0.1){//左右两点的深度差大于0.1时
      if(diff_right[0] > diff_left[0]){//如果是右边更深

        Eigen::Vector3d surf_vector = Eigen::Vector3d(laserCloud->points[i-4].x-laserCloud->points[i].x,
                                                  laserCloud->points[i-4].y-laserCloud->points[i].y,
                                                  laserCloud->points[i-4].z-laserCloud->points[i].z);
        Eigen::Vector3d lidar_vector = Eigen::Vector3d(laserCloud->points[i].x,
                                                      laserCloud->points[i].y,
                                                      laserCloud->points[i].z);
        double left_surf_dis = surf_vector.norm();
        //计算当前点和最左点与当前点组成的面的夹角(入射角)
        double cc = fabs( surf_vector.dot(lidar_vector) / (surf_vector.norm()*lidar_vector.norm()) );

        std::vector<PointType> left_list;
        double min_dis = 10000;
        double max_dis = 0;
        //计算左边两点平面的模的最大最小值
        for(int j = 0; j < 4; j++){   //TODO: change the plane window size and add thin rod support
          left_list.push_back(laserCloud->points[i-j]);
          Eigen::Vector3d temp_vector = Eigen::Vector3d(laserCloud->points[i-j].x-laserCloud->points[i-j-1].x,
                                                  laserCloud->points[i-j].y-laserCloud->points[i-j-1].y,
                                                  laserCloud->points[i-j].z-laserCloud->points[i-j-1].z);

          if(j == 3) break;
          double temp_dis = temp_vector.norm();
          if(temp_dis < min_dis) min_dis = temp_dis;
          if(temp_dis > max_dis) max_dis = temp_dis;
        }
        //左临近两点的协方差矩阵最大特征值>100倍第二特征值时则认为是平面
        bool left_is_plane = plane_judge(left_list,100);
        //左边点是平面,两点深度差不多,最左点深度小于当前点深度0.05倍,入射角在36.9°~143.1°
        if(left_is_plane && (max_dis < 2*min_dis) && left_surf_dis < 0.05 * depth  && cc < 0.8){
          //设置点的标志为断点
          if(depth_right > depth_left){//右边点比左边点纵深
            CloudFeatureFlag[i] = 100;
          }
          else{//右边点比左边点纵浅为0时
            if(depth_right == 0) CloudFeatureFlag[i] = 100;
          }
        }
      }
      else{//如果是左边更深,跟上面一样的判断逻辑

        Eigen::Vector3d surf_vector = Eigen::Vector3d(laserCloud->points[i+4].x-laserCloud->points[i].x,
                                                      laserCloud->points[i+4].y-laserCloud->points[i].y,
                                                      laserCloud->points[i+4].z-laserCloud->points[i].z);
        Eigen::Vector3d lidar_vector = Eigen::Vector3d(laserCloud->points[i].x,
                                                      laserCloud->points[i].y,
                                                      laserCloud->points[i].z);
        double right_surf_dis = surf_vector.norm();
        //calculate the angle between the laser direction and the surface
        double cc = fabs( surf_vector.dot(lidar_vector) / (surf_vector.norm()*lidar_vector.norm()) );

        std::vector<PointType> right_list;
        double min_dis = 10000;
        double max_dis = 0;
        for(int j = 0; j < 4; j++){ //TODO: change the plane window size and add thin rod support
          right_list.push_back(laserCloud->points[i-j]);
          Eigen::Vector3d temp_vector = Eigen::Vector3d(laserCloud->points[i+j].x-laserCloud->points[i+j+1].x,
                                                  laserCloud->points[i+j].y-laserCloud->points[i+j+1].y,
                                                  laserCloud->points[i+j].z-laserCloud->points[i+j+1].z);

          if(j == 3) break;
          double temp_dis = temp_vector.norm();
          if(temp_dis < min_dis) min_dis = temp_dis;
          if(temp_dis > max_dis) max_dis = temp_dis;
        }
        bool right_is_plane = plane_judge(right_list,100);

        if(right_is_plane && (max_dis < 2*min_dis) && right_surf_dis < 0.05 * depth && cc < 0.8){ // 

          if(depth_right < depth_left){
            CloudFeatureFlag[i] = 100;
          }
          else{
            if(depth_left == 0) CloudFeatureFlag[i] = 100;       
          }
        }
      }
    }

    //对断点进一步复核左右加权向量的夹角大小
    if(CloudFeatureFlag[i] == 100){
      debugnum2++;
      std::vector<Eigen::Vector3d> front_norms;
      Eigen::Vector3d norm_front(0,0,0);
      Eigen::Vector3d norm_back(0,0,0);
      for(int k = 1;k<4;k++){
          Eigen::Vector3d tmp = Eigen::Vector3d(laserCloud->points[i-k].x-laserCloud->points[i].x,
                            laserCloud->points[i-k].y-laserCloud->points[i].y,
                            laserCloud->points[i-k].z-laserCloud->points[i].z);
        tmp.normalize();
        front_norms.push_back(tmp);
        norm_front += (k/6.0)* tmp;
      }
      std::vector<Eigen::Vector3d> back_norms;
      for(int k = 1;k<4;k++){
          Eigen::Vector3d tmp = Eigen::Vector3d(laserCloud->points[i+k].x-laserCloud->points[i].x,
                            laserCloud->points[i+k].y-laserCloud->points[i].y,
                            laserCloud->points[i+k].z-laserCloud->points[i].z);
        tmp.normalize();
        back_norms.push_back(tmp);
        norm_back += (k/6.0)* tmp;
      }
      //左右三点加权向量夹角范围必须在在36.9°~143.1°,如果不符合则清除断点标志
      double cc = fabs( norm_front.dot(norm_back) / (norm_front.norm()*norm_back.norm()) );
        if(cc < 0.8){
        debugnum3++;
      }else{
        CloudFeatureFlag[i] = 0;
      }

      continue;

    }
  }

  //特征点分类和发布
  for(int i = 0; i < cloudSize; i++){
    //此两处计算结果貌似没卵用
    //laserCloud->points[i].intensity = double(CloudFeatureFlag[i]) / 10000;
    float dis = laserCloud->points[i].x * laserCloud->points[i].x
                + laserCloud->points[i].y * laserCloud->points[i].y
                + laserCloud->points[i].z * laserCloud->points[i].z;
    float dis2 = laserCloud->points[i].y * laserCloud->points[i].y + laserCloud->points[i].z * laserCloud->points[i].z;
    float theta2 = std::asin(sqrt(dis2/dis)) / M_PI * 180;
    //std::cout<<"DEBUG theta "<<theta2<<std::endl;
    // if(theta2 > 34.2 || theta2 < 1){
    //    continue;
    // }
    //if(dis > 30*30) continue;

    if(CloudFeatureFlag[i] == 1){
      surfPointsFlat.push_back(laserCloud->points[i]);
      continue;
    }

    if(CloudFeatureFlag[i] == 100 || CloudFeatureFlag[i] == 150){
        cornerPointsSharp.push_back(laserCloud->points[i]);
    } 
  }


  std::cout<<"ALL point: "<<cloudSize<<" outliers: "<< debugnum1 << std::endl
            <<" break points: "<< debugnum2<<" break feature: "<< debugnum3 << std::endl
            <<" normal points: "<< debugnum4<<" surf-surf feature: " << debugnum5 << std::endl;

  sensor_msgs::PointCloud2 laserCloudOutMsg;
  pcl::toROSMsg(*laserCloud, laserCloudOutMsg);
  laserCloudOutMsg.header.stamp = laserCloudMsg->header.stamp;
  laserCloudOutMsg.header.frame_id = "/livox";
  pubLaserCloud.publish(laserCloudOutMsg);

  sensor_msgs::PointCloud2 cornerPointsSharpMsg;
  pcl::toROSMsg(cornerPointsSharp, cornerPointsSharpMsg);
  cornerPointsSharpMsg.header.stamp = laserCloudMsg->header.stamp;
  cornerPointsSharpMsg.header.frame_id = "/livox";
  pubCornerPointsSharp.publish(cornerPointsSharpMsg);

  sensor_msgs::PointCloud2 surfPointsFlat2;
  pcl::toROSMsg(surfPointsFlat, surfPointsFlat2);
  surfPointsFlat2.header.stamp = laserCloudMsg->header.stamp;
  surfPointsFlat2.header.frame_id = "/livox";
  pubSurfPointsFlat.publish(surfPointsFlat2);

}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
livox_mapping 是一种基于 Livox 雷达的激光 SLAM 实现,其中也包含了地面约束。地面约束的作用是约束机器人所在平面与地面平行,避免因为地形变化等原因导致的误差累积。 livox_mapping 中的地面约束可以分为两个部分: 1. 地面点云提取 livox_mapping 使用了基于局部窗口扫描的方法提取地面点云。具体来说,对于每一个激光点,livox_mapping 会选取其周围一定范围内的激光点进行统计,并计算这些激光点的平均高度。如果该点的高度与平均高度之差小于一定阈值,则认为该点属于地面点云。经过这样的处理后,livox_mapping 得到了一份地面点云数据。 2. 地面约束因子 livox_mapping 中对地面约束的建模采用了与前面介绍的因子图优化类似的方法。具体来说,livox_mapping 将机器人的位姿拆分成平移和旋转两个部分,然后对平移部分施加地面约束。地面约束的公式与前面介绍的相同,即: $$ p_{z,i} - \frac{a}{c}p_{x,i} - \frac{b}{c}p_{y,i} - \frac{d}{c} = 0 $$ 其中,$p_{x,i}$、$p_{y,i}$、$p_{z,i}$ 是机器人位姿下的激光点云坐标,$(a, b, c)$ 是地面的法向量,$d$ 是地面的截距。livox_mapping 中通过计算地面点云的平均法向量来得到地面的法向量和截距。然后,对于每一个机器人位姿,livox_mapping 都会将其对应的激光点云中的点与地面约束因子进行匹配,并将匹配得到的因子加入因子图中进行优化。 总的来说,livox_mapping 的地面约束主要包含了两个部分:地面点云提取和地面约束因子。通过这两个部分的处理,livox_mapping 可以在激光 SLAM 中有效地应对地形变化等因素带来的误差。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值