From 7369e2ab06443d48d33175d80ddeb96a1d9beda0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20del=20Pino?= <idelpino@iri.upc.edu>
Date: Fri, 13 May 2022 14:27:39 +0200
Subject: [PATCH] first full implementation achieved!

---
 src/ground_segmentation_alg_node.cpp | 208 +++++++++++++++++++++++----
 1 file changed, 182 insertions(+), 26 deletions(-)

diff --git a/src/ground_segmentation_alg_node.cpp b/src/ground_segmentation_alg_node.cpp
index 4d0f2df..db07423 100644
--- a/src/ground_segmentation_alg_node.cpp
+++ b/src/ground_segmentation_alg_node.cpp
@@ -473,8 +473,8 @@ void transformPointCloud(const Eigen::Matrix<float, 6, 1> pose,
 }
 
 void transformInversePointCloud(const Eigen::Matrix<float, 6, 1> pose,
-                                 const pcl::PointCloud<pcl::PointXYZRGBNormal> input_pcl_cloud,
-                                 pcl::PointCloud<pcl::PointXYZRGBNormal> &output_pcl_cloud)
+                                const pcl::PointCloud<pcl::PointXYZRGBNormal> input_pcl_cloud,
+                                pcl::PointCloud<pcl::PointXYZRGBNormal> &output_pcl_cloud)
 {
   Eigen::Matrix<float, 4, 4> rigid_transform = Eigen::Matrix<float, 4, 4>::Identity();
 
@@ -508,24 +508,26 @@ void transformInversePointCloud(const Eigen::Matrix<float, 6, 1> pose,
   pcl::transformPointCloud(input_pcl_cloud, output_pcl_cloud, rigid_transform);
 }
 
-void discardPointsOutOfROI(const Eigen::Matrix<float, 6, 1> pose, const float ground_grid_size, pcl::PointCloud<pcl::PointXYZRGBNormal> &point_cloud_in_pose_frame)
+void discardPointsOutOfROI(const Eigen::Matrix<float, 6, 1> pose, const float ground_grid_size,
+                           pcl::PointCloud<pcl::PointXYZRGBNormal> &point_cloud_in_pose_frame)
 {
-  pcl::PointCloud<pcl::PointXYZRGBNormal> point_cloud_in_local_frame;
+  pcl::PointCloud < pcl::PointXYZRGBNormal > point_cloud_in_local_frame;
   transformInversePointCloud(pose, point_cloud_in_pose_frame, point_cloud_in_local_frame);
 
-  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr point_cloud_in_local_frame_ptr(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
+  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr point_cloud_in_local_frame_ptr(
+      new pcl::PointCloud<pcl::PointXYZRGBNormal>);
   *point_cloud_in_local_frame_ptr = point_cloud_in_local_frame;
 
-  pcl::PassThrough<pcl::PointXYZRGBNormal> pass;
-  pass.setInputCloud (point_cloud_in_local_frame_ptr);
-  pass.setFilterFieldName ("x");
-  pass.setFilterLimits (-1.0*ground_grid_size, ground_grid_size);
-  pass.filter (*point_cloud_in_local_frame_ptr);
+  pcl::PassThrough < pcl::PointXYZRGBNormal > pass;
+  pass.setInputCloud(point_cloud_in_local_frame_ptr);
+  pass.setFilterFieldName("x");
+  pass.setFilterLimits(-1.0 * ground_grid_size, ground_grid_size);
+  pass.filter(*point_cloud_in_local_frame_ptr);
 
-  pass.setInputCloud (point_cloud_in_local_frame_ptr);
-  pass.setFilterFieldName ("y");
-  pass.setFilterLimits (-1.0*ground_grid_size, ground_grid_size);
-  pass.filter (*point_cloud_in_local_frame_ptr);
+  pass.setInputCloud(point_cloud_in_local_frame_ptr);
+  pass.setFilterFieldName("y");
+  pass.setFilterLimits(-1.0 * ground_grid_size, ground_grid_size);
+  pass.filter(*point_cloud_in_local_frame_ptr);
 
   point_cloud_in_local_frame.clear();
   point_cloud_in_local_frame = *point_cloud_in_local_frame_ptr;
@@ -533,14 +535,169 @@ void discardPointsOutOfROI(const Eigen::Matrix<float, 6, 1> pose, const float gr
   transformPointCloud(pose, point_cloud_in_local_frame, point_cloud_in_pose_frame);
 }
 
+void keepOnlyHigherPoints(pcl::PointCloud<pcl::PointXYZRGBNormal> &ground_points_accumulation_in_odom_frame,
+                          const float ground_grid_resolution)
+{
+  pcl::PointCloud<pcl::PointXY>::Ptr pcl_pointcloud_2D_ptr(new pcl::PointCloud<pcl::PointXY>);
+  pcl::PointXY point;
+  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::const_iterator it = ground_points_accumulation_in_odom_frame.begin();
+      it != ground_points_accumulation_in_odom_frame.end(); ++it)
+  {
+    point.x = it->x;
+    point.y = it->y;
+    pcl_pointcloud_2D_ptr->points.push_back(point);
+  }
+
+//std::cout << "Number of points in pcl_pointcloud_2D_ptr = " << pcl_pointcloud_2D_ptr->points.size() << std::endl;
+
+  pcl::KdTreeFLANN < pcl::PointXY > kdtree_2D;
+  kdtree_2D.setInputCloud(pcl_pointcloud_2D_ptr);
+
+// We want to find the x and y values, but not interested in z
+//std::cout << "Elevation grid resolution = " << filtering_configuration.elevation_grid_resolution << std::endl;
+  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_points_accumulation_in_odom_frame_ptr(
+      new pcl::PointCloud<pcl::PointXYZRGBNormal>);
+  *ground_points_accumulation_in_odom_frame_ptr = ground_points_accumulation_in_odom_frame;
+
+  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
+
+  pcl::VoxelGrid < pcl::PointXYZRGBNormal > voxel_grid;
+  voxel_grid.setInputCloud(ground_points_accumulation_in_odom_frame_ptr);
+  voxel_grid.setLeafSize(ground_grid_resolution, ground_grid_resolution, OUT_OF_RANGE);
+  voxel_grid.filter(*elevation_cloud);
+//std::cout << "Number of points after voxelization = " << elevation_cloud->points.size() << std::endl;
+
+  const float DISTANCE_FROM_CELL_CENTER_TO_CELL_CORNER = 0.7071067812 * ground_grid_resolution; //sqrt(2)/2.0
+
+  int i = 0;
+  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator it = elevation_cloud->begin(); it != elevation_cloud->end();
+      ++it)
+  {
+    point.x = it->x;
+    point.y = it->y;
+
+    std::vector<int> pointIdxRadiusSearch;
+    std::vector<float> pointRadiusSearchSquaredDistance;
+
+    kdtree_2D.radiusSearch(point, DISTANCE_FROM_CELL_CENTER_TO_CELL_CORNER, pointIdxRadiusSearch,
+                           pointRadiusSearchSquaredDistance);
+
+    std::vector<float> z_coordinates;
+    std::vector<float> x_coordinates;
+    std::vector<float> y_coordinates;
+
+    for (std::vector<int>::const_iterator iterator = pointIdxRadiusSearch.begin();
+        iterator != pointIdxRadiusSearch.end(); ++iterator)
+    {
+      z_coordinates.push_back(ground_points_accumulation_in_odom_frame_ptr->points[*iterator].z);
+      x_coordinates.push_back(ground_points_accumulation_in_odom_frame_ptr->points[*iterator].x);
+      y_coordinates.push_back(ground_points_accumulation_in_odom_frame_ptr->points[*iterator].y);
+    }
+
+    it->data_c[DATA_C_1_ID_CLASS] = CLASS_UNKNOWN;
+    it->r = R_CLASS_UNKNOWN;
+    it->g = G_CLASS_UNKNOWN;
+    it->b = B_CLASS_UNKNOWN;
+
+    float sum = std::accumulate(z_coordinates.begin(), z_coordinates.end(), 0.0);
+    float z_mean = sum / (float)z_coordinates.size();
+    float var = 0;
+    for (int n = 0; n < z_coordinates.size(); ++n)
+      var += ((z_coordinates[n] - z_mean) * (z_coordinates[n] - z_mean));
+
+    var /= (float)z_coordinates.size();
+
+    std::vector<float>::iterator highest_point_in_cell = std::max_element(z_coordinates.begin(), z_coordinates.end());
+    int position_in_vector = std::distance(z_coordinates.begin(), highest_point_in_cell);
+
+    it->z = z_coordinates[position_in_vector];
+    it->x = x_coordinates[position_in_vector];
+    it->y = y_coordinates[position_in_vector];
+
+    // Filling remaining fields
+    it->data_n[DATA_N_0_INTENSITY] = INTENSITY_UNKNOWN;
+    it->data_n[DATA_N_1_Z_GROUND] = Z_GROUND_UNKNOWN;
+    it->data_n[DATA_N_2_Z_MEAN] = z_mean;
+    it->data_n[DATA_N_3_Z_VARIANCE] = var;
+
+    it->data_c[DATA_C_2_CAR_PROB] = PROB_UNKNOWN;
+    it->data_c[DATA_C_3_ORIGINAL_INDEX] = i++;    //storing the index for later in labelling step
+
+    //std::cout << "var z = " << it->data_n[DATA_N_3_Z_VARIANCE] << std::endl;
+    //std::cout << "z_coordinates.size() = " << z_coordinates.size() << std::endl;
+  }
+
+  ground_points_accumulation_in_odom_frame.clear();
+  ground_points_accumulation_in_odom_frame = *elevation_cloud;
+}
+
+void labelLowObstacles(pcl::PointCloud<pcl::PointXYZRGBNormal> &ground_points_accumulation_in_odom_frame,
+                       const float number_of_neighbours_to_compare_height, const float slope_threshold)
+{
+  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_points_accumulation_in_odom_frame_ptr(
+      new pcl::PointCloud<pcl::PointXYZRGBNormal>);
+  *ground_points_accumulation_in_odom_frame_ptr = ground_points_accumulation_in_odom_frame;
+
+  pcl::KdTreeFLANN < pcl::PointXYZRGBNormal > kdtree;
+  kdtree.setInputCloud(ground_points_accumulation_in_odom_frame_ptr);
+  int K = number_of_neighbours_to_compare_height;
+  std::vector<int> pointIdxKNNSearch(K);
+  std::vector<float> pointKNNSquaredDistance(K);
+
+  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator it = ground_points_accumulation_in_odom_frame_ptr->begin();
+      it != ground_points_accumulation_in_odom_frame_ptr->end(); ++it)
+  {
+    kdtree.nearestKSearch(*it, K, pointIdxKNNSearch, pointKNNSquaredDistance);
+    const int NUM_OF_NEIGHBOURS = pointIdxKNNSearch.size();
+
+    bool low_obstacle_found = false;
+
+    if (NUM_OF_NEIGHBOURS > 0)
+    {
+      for (int i = 0; i < NUM_OF_NEIGHBOURS; i++)
+      {
+        pcl::PointXYZRGBNormal neighbour = ground_points_accumulation_in_odom_frame_ptr->points[pointIdxKNNSearch[i]];
+        float delta_x = it->x - neighbour.x;
+        float delta_y = it->y - neighbour.y;
+        float delta_z = it->z - neighbour.z;
+
+        float distance_2d = sqrt(delta_x*delta_x + delta_y*delta_y);
+        float slope = delta_z / distance_2d;
+
+        if (slope > slope_threshold)
+          low_obstacle_found = true;
+      }
+
+      if(low_obstacle_found)
+      {
+        //std::cout << "low obstacle found!!" << std::endl;
+        it->data_c[DATA_C_1_ID_CLASS] = CLASS_OBSTACLE;
+        it->r = R_CLASS_OBSTACLE;
+        it->g = G_CLASS_OBSTACLE;
+        it->b = B_CLASS_OBSTACLE;
+      }
+      else
+      {
+        //std::cout << "just another boring ground point :(" << std::endl;
+        it->data_c[DATA_C_1_ID_CLASS] = CLASS_GROUND;
+        it->r = R_CLASS_GROUND;
+        it->g = G_CLASS_GROUND;
+        it->b = B_CLASS_GROUND;
+      }
+    }
+  }
+  ground_points_accumulation_in_odom_frame.clear();
+  ground_points_accumulation_in_odom_frame = *ground_points_accumulation_in_odom_frame_ptr;
+}
+
 void GroundSegmentationAlgNode::detectLowObstacles(pcl::PointCloud<pcl::PointXYZRGBNormal> &lidar_pcl_cloud)
 {
-  pcl::PointCloud<pcl::PointXYZRGBNormal> ground_points;
+  pcl::PointCloud < pcl::PointXYZRGBNormal > ground_points;
   for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = lidar_pcl_cloud.begin(); i != lidar_pcl_cloud.end(); ++i)
-    if((int)std::floor(i->data_c[DATA_C_1_ID_CLASS]) == CLASS_GROUND)
+    if ((int)std::floor(i->data_c[DATA_C_1_ID_CLASS]) == CLASS_GROUND)
       ground_points.push_back(*i);
 
-  pcl::PointCloud<pcl::PointXYZRGBNormal> ground_points_in_odom_frame;
+  pcl::PointCloud < pcl::PointXYZRGBNormal > ground_points_in_odom_frame;
 
   transformPointCloud(X_k_, ground_points, ground_points_in_odom_frame);
 
@@ -548,18 +705,15 @@ void GroundSegmentationAlgNode::detectLowObstacles(pcl::PointCloud<pcl::PointXYZ
 
   discardPointsOutOfROI(X_k_, ground_grid_size_, ground_points_accumulation_in_odom_frame_);
 
-  pcl::PointCloud<pcl::PointXYZRGBNormal> ground_points_accumulation;
-  transformInversePointCloud(X_k_, ground_points_accumulation_in_odom_frame_, ground_points_accumulation);
+  keepOnlyHigherPoints(ground_points_accumulation_in_odom_frame_, ground_grid_resolution_);
 
-  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = ground_points_accumulation.begin(); i != ground_points_accumulation.end(); ++i)
-  {
-    i->r = R_CLASS_GROUND;
-    i->g = G_CLASS_GROUND;
-    i->b = B_CLASS_GROUND;
+  labelLowObstacles(ground_points_accumulation_in_odom_frame_, number_of_neighbours_to_compare_height_,
+                    slope_threshold_);
 
-    lidar_pcl_cloud.push_back(*i);
-  }
+  pcl::PointCloud < pcl::PointXYZRGBNormal > ground_points_accumulation;
+  transformInversePointCloud(X_k_, ground_points_accumulation_in_odom_frame_, ground_points_accumulation);
 
+  lidar_pcl_cloud += ground_points_accumulation;
 }
 
 void GroundSegmentationAlgNode::mainNodeThread(void)
@@ -691,6 +845,7 @@ void GroundSegmentationAlgNode::node_config_update(Config &config, uint32_t leve
   // low obstacle detection parameters (requires a good 3d odometry source)
   this->detect_low_obstacles_using_odometry_ = config.detect_low_obstacles_using_odometry;
   this->ground_grid_resolution_ = config.ground_grid_resolution;
+  this->ground_grid_size_ = config.ground_grid_size;
   this->number_of_neighbours_to_compare_height_ = config.number_of_neighbours_to_compare_height;
   this->slope_threshold_ = config.slope_threshold;
 
@@ -740,6 +895,7 @@ void GroundSegmentationAlgNode::node_config_update(Config &config, uint32_t leve
 
   std::cout << "detect_low_obstacles_using_odometry = " << this->detect_low_obstacles_using_odometry_ << std::endl;
   std::cout << "ground_grid_resolution = " << this->ground_grid_resolution_ << std::endl;
+  std::cout << "ground_grid_size = " << this->ground_grid_size_ << std::endl;
   std::cout << "number_of_neighbours_to_compare_height = " << this->number_of_neighbours_to_compare_height_
       << std::endl;
   std::cout << "slope_threshold = " << this->slope_threshold_ << std::endl;
-- 
GitLab