lidar.cpp
Go to the documentation of this file.
1 
3 
4 // Original Author of FLOAM: Wang Han
5 // Email wh200720041@gmail.com
6 // Homepage https://wanghan.pro
7 
8 #include <pcl/features/organized_edge_detection.h>
9 #include <pcl/features/integral_image_normal.h>
10 #include <pcl/features/normal_3d.h>
11 #include <pcl/impl/point_types.hpp>
12 
13 #include "floam/lidar_utils.hpp"
14 #include "floam/lidar.hpp"
15 
16 #include <iostream>
17 
18 namespace floam
19 {
20 namespace lidar
21 {
22 
24 template <>
26  const pcl::PointCloud<pcl::PointXYZ>::Ptr & points,
27  pcl::PointCloud<pcl::PointXYZL>::Ptr & edges)
28 {
29  int N_SCANS = m_settings.lines;
30 
32  m_addedPoints.clear();
33 
35  m_lidarScans.clear();
36 
37  if(N_SCANS != 1) {
38  for(int i = 0; i < N_SCANS; i++){
39  m_lidarScans.push_back(pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>()));
40  }
41  }
42 
43  // initialize kdTree for nearest neighbor search
44  m_kdTree.setInputCloud(points);
45 
46  // k points around point to search for
47  int kNN = m_settings.searchK;
48  // radius around point to search for points
49  double radius = m_settings.searchRadius;
50 
53  const int increment = m_settings.skipPoints;
54 
55  double diffX, diffY, diffZ = 0;
56  double diffTotal, diffLeft, diffRight = 0;
57  double tempX, tempY, tempZ = 0;
58 
59  int index = 0;
60  // separate out pointcloud into different scan lines
61  // essentially trying to make it an "ordered pointcloud"
62  for (int i = 0; i < (int) points->points.size(); i+=increment)
63  {
64  int scanID=0;
65 
67  double distance =
68  sqrt(
69  points->points[i].x * points->points[i].x +
70  points->points[i].y * points->points[i].y);
71 
72  // filter pointcloud by min and max distance settings
73  if (distance < m_settings.common.limits.distance.min ||
74  distance > m_settings.common.limits.distance.max)
75  {
76  // distance out of min/max limits, go to next point
77  continue;
78  }
79  double angle = atan(points->points[i].z / distance) * 180 / M_PI;
80 
81  if (N_SCANS == 16) {
82  scanID = int((angle + 15) / 2 + 0.5);
83  if (scanID > (N_SCANS - 1) || scanID < 0) {
84  continue;
85  }
86  } else if (N_SCANS == 32) {
87  scanID = int((angle + 92.0/3.0) * 3.0 / 4.0);
88  if (scanID > (N_SCANS - 1) || scanID < 0) {
89  continue;
90  }
91  } else if (N_SCANS == 64) {
92  if (angle >= -8.83) {
93  scanID = int((2 - angle) * 3.0 + 0.5);
94  } else {
95  scanID = N_SCANS / 2 + int((-8.83 - angle) * 2.0 + 0.5);
96  }
97 
98  if (angle > 2 || angle < -24.33 || scanID > 63 || scanID < 0) {
99  continue;
100  }
101  } else {
102  // assume single scan, use kdTree to do either radius search or nearest K search
103  // check if point has been already added
104  // TODO(flynneva): add option to do k nearest search as well
105  if(
106  m_kdTree.radiusSearch(
107  points->points[i], radius, pointSearch,
108  pointSquaredDistance) > 0)
109  {
110  // TODO(flynneva): this should be its own function, used multiple times within code
111  // classifyPoint()
112  // reset m_covariance
113  diffTotal = 0;
114  diffX = 0;
115  diffY = 0;
116  diffZ = 0;
117 
118  // reset m_covariance values
119  m_covariance.setZero(3, 3);
120 
121  // points found within K
122  for(std::size_t j = 0; j < pointSearch.size(); ++j) {
123  // calculate diff to current point
124  diffX = (*points)[pointSearch[j]].x - points->points[i].x;
125  diffY = (*points)[pointSearch[j]].y - points->points[i].y;
126  diffZ = (*points)[pointSearch[j]].z - points->points[i].z;
127 
128  // sum up diffs, store in m_covariance matrix for later
129  m_covariance(0, 0) += diffX * diffX;
130  m_covariance(1, 0) += diffX * diffY;
131  m_covariance(2, 0) += diffX * diffZ;
132  m_covariance(0, 1) += m_covariance(1, 0); // same as cov(x, y)
133  m_covariance(1, 1) += diffY * diffY;
134  m_covariance(2, 1) += diffY * diffZ;
135  m_covariance(0, 2) += m_covariance(2, 0); // same as cov(x, z)
136  m_covariance(1, 2) += m_covariance(2, 1); // same as cov(z, y)
137  m_covariance(2, 2) += diffZ * diffZ;
138  }
139 
141  index = (int)pointSearch.size() - 1;
142 
144  m_covariance = m_covariance / index;
145 
147  auto solver(m_covariance);
148  m_eigenValues = solver.eigenvalues();
149  diffTotal = std::real(m_eigenValues(0) / (m_eigenValues(0) + m_eigenValues(1) + m_eigenValues(2)));
150 
151  // get current point
152  pcl::PointXYZL tempPointL;
153  tempPointL.x = points->points[i].x;
154  tempPointL.y = points->points[i].y;
155  tempPointL.z = points->points[i].z;
156 
157  if (diffTotal <= m_settings.common.limits.edgeThreshold &&
158  diffTotal >= -m_settings.common.limits.edgeThreshold)
159  {
160  // value is smaller than threshold, so assume it is a surface
161  tempPointL.label = 0;
162  } else {
163  // value is larger than threshold, assume it is an edge
164  tempPointL.label = 1;
165  }
167 
168  m_addedPoints.push_back(i);
169  edges->push_back(tempPointL);
170 
171  // also add all "found" points to cloud, classify same as point above
172  // for(std::size_t j = 0; j < pointSearch.size(); ++j) {
173  // // check if point has been already added
174  // if(std::find(m_addedPoints.begin(), m_addedPoints.end(), j)==m_addedPoints.end()) {
175  // tempPointL.x = (*points)[pointSearch[j]].x;
176  // tempPointL.y = (*points)[pointSearch[j]].y;
177  // tempPointL.z = (*points)[pointSearch[j]].z;
178 
179  // // m_addedPoints.push_back(i);
180  // edges->push_back(tempPointL);
181  // }
182  // }
183  continue;
184  }
185  }
186  m_lidarScans[scanID]->push_back(points->points[i]);
187  }
188 
189  // check to see if we already detected the edges
190  if (!edges->empty()) {
191  return;
192  }
193 
194  for(int i = 0; i < N_SCANS; i++) {
196  // if pointcloud is too small, ignore it
197  if(m_lidarScans[i]->points.size() < 131) {
198  continue;
199  }
200 
205  std::vector<floam::lidar::Double2d> cloudCurvature;
206  // size of window to calculate curvature of
207  // TODO(flynneva): make windowSize adjustable
208  int windowSize = 10;
209  int halfWindow = windowSize / 2;
210  // (TODO: flynneva): do we really need to subtract the windowSize from the total points?
211  int totalPoints = m_lidarScans[i]->points.size() - windowSize;
212 
213  // calculate number of sectors and the length/size of each sector
214  // TODO(flynneva): make sectors a parameter?
215  m_settings.common.limits.sectors = 6;
216  int sectorSize = (int)m_lidarScans[i]->points.size() / m_settings.common.limits.sectors;
217 
218  double diffX, diffY, diffZ;
219 
220  for(int j = halfWindow; j < (int)(m_lidarScans[i]->points.size() - halfWindow); j += 1) {
221  // reset diff's at new point
222  diffX = 0;
223  diffY = 0;
224  diffZ = 0;
225  diffTotal = 0;
226  diffLeft = 0;
227  diffRight = 0;
228 
229  for (int k = -halfWindow; k <= halfWindow; k++) {
230  // the middle point of each window is the "baseline"
231  if (k == 0) {
232  // calculate diff for window so far (-halfWindow to 0)
233  tempX = diffX - (halfWindow * m_lidarScans[i]->points[j].x);
234  tempY = diffY - (halfWindow * m_lidarScans[i]->points[j].y);
235  tempZ = diffZ - (halfWindow * m_lidarScans[i]->points[j].z);
236  diffLeft = tempX * tempX + tempY * tempY + tempZ * tempZ;
237 
238  // subtract middle point * size (because we add the rest of the points)
239  diffX -= windowSize * m_lidarScans[i]->points[j].x;
240  diffY -= windowSize * m_lidarScans[i]->points[j].y;
241  diffZ -= windowSize * m_lidarScans[i]->points[j].z;
242  } else {
243  // add points left and right of middle of window
244  diffX += m_lidarScans[i]->points[j + k].x;
245  diffY += m_lidarScans[i]->points[j + k].y;
246  diffZ += m_lidarScans[i]->points[j + k].z;
247  }
248  }
249  // if diff total is large, sector is very curved or could be an edge
250  // j - 1 to store actual location in points index
251  diffTotal = diffX * diffX + diffY * diffY + diffZ * diffZ;
252  diffRight = diffTotal - diffLeft;
253  floam::lidar::Double2d distance(j - 1, diffTotal, diffLeft, diffRight);
254  cloudCurvature.push_back(distance);
255  }
257 
259  for(int j = 0; j < m_settings.common.limits.sectors; j++) {
260  int sectorStart = sectorSize * j;
261  int sectorEnd = sectorSize * (j + 1) - 1;
262  // for last sector, sectorEnd is last point (may or may not be the same size as the rest of the sectors)
263  if (j == (m_settings.common.limits.sectors - 1)) {
264  sectorEnd = totalPoints - 1;
265  }
266 
267  std::vector<floam::lidar::Double2d> subCloudCurvature(
268  cloudCurvature.begin() + sectorStart,
269  cloudCurvature.begin() + sectorEnd);
270 
271  // sort diff's within sector
272  std::sort(subCloudCurvature.begin(), subCloudCurvature.end(),
273  [](const floam::lidar::Double2d & a, const floam::lidar::Double2d & b)
274  {
275  return a.diffTotal < b.diffTotal;
276  });
277 
278  // determine if point is an edge or a surface
279  for (int k = subCloudCurvature.size() - 1; k >= 0; k--) {
280  // get index of point
281  index = subCloudCurvature[k].id;
282  // check if point has been already added
283  pcl::PointXYZL tempPointL;
284  tempPointL.x = m_lidarScans[i]->points[index].x;
285  tempPointL.y = m_lidarScans[i]->points[index].y;
286  tempPointL.z = m_lidarScans[i]->points[index].z;
287 
288  // determine if point is an edge or surface
289  if (subCloudCurvature[k].diffTotal <= m_settings.common.limits.edgeThreshold)
290  {
291  // value is smaller than threshold, so it is not an edge and assume its a surface
292  tempPointL.label = 0;
293  } else {
294  tempPointL.label = 1;
295  }
296  edges->push_back(tempPointL);
297  m_addedPoints.push_back(index);
298  }
299  }
300  }
301 }
302 
304 template <>
306  const pcl::PointCloud<pcl::PointXYZ>::Ptr & points,
307  pcl::PointCloud<pcl::PointXYZL>::Ptr & edges)
308 {
309  // pointcloud in has to be organized (i.e. height and width)
310  pcl::OrganizedEdgeBase <pcl::PointXYZ, pcl::Label> edgeDetector;
311  pcl::PointCloud<pcl::Label>::Ptr labels(new pcl::PointCloud<pcl::Label> ());
312 
313  std::vector<pcl::PointIndices> indicies;
314  edgeDetector.setInputCloud(points);
315 
316  // TODO(flynneva): make these adjustable
317  edgeDetector.setDepthDisconThreshold(0.02f);
318  edgeDetector.setMaxSearchNeighbors(50);
319 
320  // calculate edges
321  edgeDetector.compute(*labels, indicies);
322  // combine xyz cloud with labels
323  pcl::concatenateFields(*points, *labels, *edges);
324 }
325 
327 template <>
329  const pcl::PointCloud<pcl::PointXYZ>::Ptr & points,
330  pcl::PointCloud<pcl::PointNormal>::Ptr & normals)
331 {
332  // Create the normal estimation class, and pass the input pointcloud to it
333  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalDetector;
334  pcl::PointCloud<pcl::Normal>::Ptr normalCloud(new pcl::PointCloud<pcl::Normal> ());
335  normalDetector.setInputCloud(points);
336 
337  // Create an empty kdtree representation, and pass it to the normal estimation object.
338  // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
339  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
340  normalDetector.setSearchMethod (tree);
341  // Use all neighbors in a sphere of K 3cm
342  normalDetector.setKSearch (0.03);
343 
344  // Compute the features
345  normalDetector.compute(*normalCloud);
346  // combine xyz cloud with normals
347  pcl::concatenateFields(*points, *normalCloud, *normals);
348 }
349 
351 template <>
353  const pcl::PointCloud<pcl::PointXYZ>::Ptr & points,
354  pcl::PointCloud<pcl::PointNormal>::Ptr & normals)
355 {
356  pcl::IntegralImageNormalEstimation<pcl::PointXYZ, pcl::Normal> normalDetector;
357  pcl::PointCloud<pcl::Normal> normalCloud;
358 
359  // only for structured pointclouds
360  normalDetector.setNormalEstimationMethod(normalDetector.AVERAGE_3D_GRADIENT);
361  // TODO(flynneva): make these adjustable
362  normalDetector.setMaxDepthChangeFactor(0.02f);
363  normalDetector.setNormalSmoothingSize(7.0f);
364  normalDetector.setInputCloud(points);
365 
366  normalDetector.compute(normalCloud);
367 
368  // combine xyz cloud with normals
369  pcl::concatenateFields(*points, normalCloud, *normals);
370 }
371 
372 } // namespace lidar
373 } // namespace floam
angle
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
lidar.hpp
f
f
floam::lidar::Lidar
Definition: lidar.hpp:22
floam::lidar::Double2d
Definition: lidar_utils.hpp:91
lidar_utils.hpp
floam::lidar::Lidar::detectEdges
void detectEdges(const pcl::PointCloud< pcl::PointXYZ >::Ptr &points, pcl::PointCloud< pcl::PointXYZL >::Ptr &edges)
floam
Major rewrite Author: Evan Flynn.
Definition: lidar.hpp:15


floam
Author(s): Han Wang
autogenerated on Wed Mar 2 2022 00:23:09