livox_mapping 特征提取代码解析

代码来自于 livox_mapping,先简单说结论:

异常点提取:与左右点的距离大于深度值的十分之一

平面点提取: 主要通过左边或者右边四个点的曲率小于 0.001 计算得到;

角点提取:主要有几个来源

平面角点,左右都是平面且平面夹角 60-120之间; (可以理解为方形柱子的边缘特征点)

断点角点:(可以理解为物体的边缘到远处的扫描点 )根据当前点与左右附近点的距离判断,断点阈值0.1; 并且有一边的点是平面; 点与点之间没有太远; 最远点距离不超过深度值的二十分之一; 当前点和最远点合成的平面 和当前点向量的夹角,夹角 37度到 143度;

其他的livox 项目特征提取也是大同小异的,看懂了这个基本其他的也都类似;



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

  int cloudSize = laserCloudIn.points.size();

  std::cout<<"DEBUG first cloudSize "<<cloudSize<<std::endl; 

  if(cloudSize > 32000) cloudSize = 32000;
  
  int count = cloudSize;

  PointType point;
  std::vector<pcl::PointCloud<PointType>> laserCloudScans(N_SCANS);
  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;
    point.intensity = laserCloudIn.points[i].intensity;
    point.curvature = laserCloudIn.points[i].curvature;
    int scanID = 0;
    if (N_SCANS == 6) {
      scanID = (int)point.intensity;
    }
    laserCloudScans[scanID].push_back(point);
  }  //todo  这里先把他分成了6份,又在下面把点云合成了一份 意思是先把点云按照每段分开,然后再合成;

  pcl::PointCloud<PointType>::Ptr laserCloud(new pcl::PointCloud<PointType>());

  for (int i = 0; i < N_SCANS; i++) {
    *laserCloud += laserCloudScans[i];
  }

  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;

//  todo 第一次遍历 找特征点
  //********************************************************************************************************************************************
  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//  todo 以 i-2 为中心的曲率计算
    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;
//  todo 左边曲率
    float left_curvature = ldiffX * ldiffX + ldiffY * ldiffY + ldiffZ * ldiffZ;

    if(left_curvature < 0.01){  //  todo 平面点

      std::vector<PointType> left_list;  //  todo 这个list没卵用啊。。。。

      for(int j = -4; j < 0; j++){
        left_list.push_back(laserCloud->points[i+j]);
      }
//  todo 为啥还弄了2个平面点的标准   这个更严格  但是它是把 i-2 设置为平面点了, 不是i   平面点主要在这里找到的 还是靠曲率 不过标准好高啊
      if( left_curvature < 0.001) CloudFeatureFlag[i-2] = 1; //surf point flag  && plane_judge(left_list,1000) 
      //  todo 平面点判断在这里

      left_surf_flag = true;  //  todo 起作用的是它
    }
    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;

    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]);
      }
        if(right_curvature < 0.001 ) CloudFeatureFlag[i+2] = 1; //surf point flag  && plane_judge(right_list,1000)
//  todo 平面点判断在这里

      count_num = 4;
      right_surf_flag = true;
    }
    else{
      count_num = 1;
      right_surf_flag = false;
    }

    //surf-surf corner feature
    //  todo 如果左右都是平面,那么就继续处理; 这里找到的是左右都是平面的角点
    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;  //  todo 左右点的向量相加,越远的权重越大
     }
     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;
     }

// todo 左右点群的向量夹角
      //calculate the angle between this group and the previous group
      double cc = fabs( norm_left.dot(norm_right) / (norm_left.norm()*norm_right.norm()) );
      //calculate the maximum distance, the distance cannot be too small
      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();
//  todo   与左,与右最远点的距离大于阈值  并且 左右点群的向量夹角 小于 0.5;  0.5 是60度,0.95是18度; 0.8是37度; 这里就是 60-120度
  todo   为什么 小于 60度的不算角点呢??? 可能因为这里还只是初步计算了平面类型的角点,不是所有类型的
      if(cc < 0.5 && last_dis > 0.05 && current_dis > 0.05 ){ //
        debugnum5 ++;
        CloudFeatureFlag[i] = 150;  //  todo 角点
      }
    }
  }


  //  todo   第二次遍历计算特征   断点角点
  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);
//  todo  就是与左右点的距离计算 得到了一个列表 这里是计算了左右各三个点的距离
      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);
    }

//  todo  这里是左右单个点的深度值
    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);

     // todo outliers   与左右点的距离大于深度值的十分之一,就是异常点,不要他
    if( (diff_right[0] > 0.1*depth && diff_left[0] > 0.1*depth) ){
      debugnum1 ++;  
      CloudFeatureFlag[i] = 250;
      continue;
    }

    //break points  //  todo  与左边点的距离差 - 与右边点的距离差 大于 0.1 ,就是断点
    if(fabs(diff_right[0] - diff_left[0])>0.1){
      if(diff_right[0] > diff_left[0]){  //  todo  当与右边点距离更大的时候,使用左边点
//  todo  最远点合成的向量
        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();
        //  todo 最远点平面 和当前点向量的夹角
        //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> 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;
        }
        //  todo   第2个特征根大于100倍的最小特征根,就是平面
        bool left_is_plane = plane_judge(left_list,100);
//  todo  左边点是平面;  点与点之间没有太远; 左边最远点距离不超过深度值的二十分之一; 夹角 37度到 143度;
        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;  //  todo  角点
          }
          else{
            if(depth_right == 0) CloudFeatureFlag[i] = 100;  //  todo   只在右边点深度值为0的时候才是角点
          }
        }
      }
      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;       
          }
        }
      }
    }

    // break point select  //  todo 如果当前点是断点 继续操作
    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;
      }
      //  todo 左边三个点群的加权向量 和右边三个点群的加强向量的夹角  37~143之间 ; 超过了就把它标签设置为0, 对之前判断的100标签的再次判断
      double cc = fabs( norm_front.dot(norm_back) / (norm_front.norm()*norm_back.norm()) );
        if(cc < 0.8){
        debugnum3++;
      }else{
        CloudFeatureFlag[i] = 0;
      }

      continue;

    }
  }


//  todo 第三次遍历 放入特征点

  //push_back feature
  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);

}



  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云-激光雷达-Slam-三维牙齿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值