MRPT  1.9.9
CPointsMap_crtp_common.h
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 #ifndef cpointsmap_crtp_common_H
10 #define cpointsmap_crtp_common_H
11 
12 #include <mrpt/core/round.h>
15 
16 namespace mrpt::maps::detail
17 {
18 template <class Derived>
19 struct loadFromRangeImpl
20 {
21  static inline void templ_loadFromRangeScan(
22  Derived& obj, const mrpt::obs::CObservation2DRangeScan& rangeScan,
23  const mrpt::poses::CPose3D* robotPose)
24  {
25  using namespace mrpt::poses;
26  using mrpt::square;
27  using mrpt::DEG2RAD;
28  obj.mark_as_modified();
29 
30  // The next may seem useless, but it's required in case the observation
31  // underwent a move or copy operator, which may change the reserved mem
32  // of std::vector's, which need to be >=4*N for SEE instructions to work
33  // without "undefined behavior" of accessing out of vector memory:
34  const_cast<mrpt::obs::CObservation2DRangeScan&>(rangeScan).resizeScan(
35  rangeScan.getScanSize());
36 
37  // If robot pose is supplied, compute sensor pose relative to it.
38  CPose3D sensorPose3D(UNINITIALIZED_POSE);
39  if (!robotPose)
40  sensorPose3D = rangeScan.sensorPose;
41  else
42  sensorPose3D.composeFrom(*robotPose, rangeScan.sensorPose);
43 
44  // Insert vs. load and replace:
45  if (!obj.insertionOptions.addToExistingPointsMap)
46  obj.resize(0); // Resize to 0 instead of clear() so the
47  // std::vector<> memory is not actually deadllocated
48  // and can be reused.
49 
50  const int sizeRangeScan = rangeScan.scan.size();
51 
52  if (!sizeRangeScan) return; // Nothing to do.
53 
54  // For a great gain in efficiency:
55  if (obj.m_x.size() + sizeRangeScan > obj.m_x.capacity())
56  {
57  obj.reserve((size_t)(obj.m_x.size() * 1.2f) + 3 * sizeRangeScan);
58  }
59 
60  // GENERAL CASE OF SCAN WITH ARBITRARY 3D ORIENTATION:
61  // Specialize a bit the equations since we know that z=0 always for the
62  // scan in local coordinates:
64  sensorPose3D.getHomogeneousMatrix(lric.HM);
65 
66  // For quicker access as "float" numbers:
67  float m00 = lric.HM.get_unsafe(0, 0);
68  float m01 = lric.HM.get_unsafe(0, 1);
69  float m03 = lric.HM.get_unsafe(0, 3);
70  float m10 = lric.HM.get_unsafe(1, 0);
71  float m11 = lric.HM.get_unsafe(1, 1);
72  float m13 = lric.HM.get_unsafe(1, 3);
73  float m20 = lric.HM.get_unsafe(2, 0);
74  float m21 = lric.HM.get_unsafe(2, 1);
75  float m23 = lric.HM.get_unsafe(2, 3);
76 
77  float lx_1, ly_1, lz_1, lx = 0, ly = 0,
78  lz = 0; // Punto anterior y actual:
79  float lx_2, ly_2; // Punto antes del anterior
80 
81  // Initial last point:
82  lx_1 = -100;
83  ly_1 = -100;
84  lz_1 = -100;
85  lx_2 = -100;
86  ly_2 = -100;
87 
88  // Minimum distance between points to reduce high density scans:
89  const bool useMinDist =
90  obj.insertionOptions.minDistBetweenLaserPoints > 0;
91  const float minDistSqrBetweenLaserPoints =
92  square(obj.insertionOptions.minDistBetweenLaserPoints);
93 
94  // ----------------------------------------------------------------
95  // Transform these points into 3D using the pose transformation:
96  // ----------------------------------------------------------------
97  bool lastPointWasValid = true;
98  bool thisIsTheFirst = true;
99  bool lastPointWasInserted = false;
100 
101  // Initialize extra stuff in derived class:
103 
104  // Resize now for efficiency, if there're invalid or filtered points,
105  // buffers
106  // will be reduced at the end:
107  const size_t nPointsAtStart = obj.size();
108  size_t nextPtIdx = nPointsAtStart;
109 
110  {
111  const size_t expectedMaxSize =
112  nPointsAtStart +
113  (sizeRangeScan *
114  (obj.insertionOptions.also_interpolate ? 3 : 1));
115  obj.m_x.resize(expectedMaxSize);
116  obj.m_y.resize(expectedMaxSize);
117  obj.m_z.resize(expectedMaxSize);
118  }
119 
120  // ------------------------------------------------------
121  // Pass range scan to a set of 2D points:
122  // ------------------------------------------------------
123  // Use a LUT to convert ranges -> (x,y) ; Automatically computed upon
124  // first usage.
126  sincos_vals = obj.m_scans_sincos_cache.getSinCosForScan(rangeScan);
127 
128  // Build list of points in global coordinates:
129  Eigen::Array<float, Eigen::Dynamic, 1> scan_gx(sizeRangeScan + 3),
130  scan_gy(sizeRangeScan + 3),
131  scan_gz(
132  sizeRangeScan +
133  3); // The +3 is to assure there's room for "nPackets*4"
134  {
135 #if MRPT_HAS_SSE2
136  // Number of 4-floats:
137  size_t nPackets = sizeRangeScan / 4;
138  if ((sizeRangeScan & 0x03) != 0) nPackets++;
139 
140  // We want to implement:
141  // scan_gx = m00*scan_x+m01*scan_y+m03;
142  // scan_gy = m10*scan_x+m11*scan_y+m13;
143  // scan_gz = m20*scan_x+m21*scan_y+m23;
144  //
145  // With: scan_x = ccos*range
146  // scan_y = csin*range
147  //
148  const __m128 m00_4val =
149  _mm_set1_ps(m00); // load 4 copies of the same value
150  const __m128 m01_4val = _mm_set1_ps(m01);
151  const __m128 m03_4val = _mm_set1_ps(m03);
152 
153  const __m128 m10_4val = _mm_set1_ps(m10);
154  const __m128 m11_4val = _mm_set1_ps(m11);
155  const __m128 m13_4val = _mm_set1_ps(m13);
156 
157  const __m128 m20_4val = _mm_set1_ps(m20);
158  const __m128 m21_4val = _mm_set1_ps(m21);
159  const __m128 m23_4val = _mm_set1_ps(m23);
160 
161  // Make sure the input std::vector<> has room enough for reads of
162  // 4-float at a time:
163  // Invalid reads should not be a problem, but just for safety...
164  // JLBC: OCT/2016: rangeScan.scan() is now, by design, ensured to
165  // hold vectors of 4*N capacity, so there is no need to call
166  // reserve() here.
167 
168  const float* ptr_in_scan = &rangeScan.scan[0];
169  const float* ptr_in_cos = &sincos_vals.ccos[0];
170  const float* ptr_in_sin = &sincos_vals.csin[0];
171 
172  float* ptr_out_x = &scan_gx[0];
173  float* ptr_out_y = &scan_gy[0];
174  float* ptr_out_z = &scan_gz[0];
175 
176  for (; nPackets; nPackets--, ptr_in_scan += 4, ptr_in_cos += 4,
177  ptr_in_sin += 4, ptr_out_x += 4, ptr_out_y += 4,
178  ptr_out_z += 4)
179  {
180  const __m128 scan_4vals =
181  _mm_loadu_ps(ptr_in_scan); // *Unaligned* load
182 
183  const __m128 xs =
184  _mm_mul_ps(scan_4vals, _mm_load_ps(ptr_in_cos));
185  const __m128 ys =
186  _mm_mul_ps(scan_4vals, _mm_load_ps(ptr_in_sin));
187 
188  _mm_store_ps(
189  ptr_out_x, _mm_add_ps(
190  m03_4val, _mm_add_ps(
191  _mm_mul_ps(xs, m00_4val),
192  _mm_mul_ps(ys, m01_4val))));
193  _mm_store_ps(
194  ptr_out_y, _mm_add_ps(
195  m13_4val, _mm_add_ps(
196  _mm_mul_ps(xs, m10_4val),
197  _mm_mul_ps(ys, m11_4val))));
198  _mm_store_ps(
199  ptr_out_z, _mm_add_ps(
200  m23_4val, _mm_add_ps(
201  _mm_mul_ps(xs, m20_4val),
202  _mm_mul_ps(ys, m21_4val))));
203  }
204 #else // MRPT_HAS_SSE2
205  // The "+3" is to assure the buffer has room for the SSE2 method
206  // which works with 4-tuples of floats.
207  Eigen::Array<float, Eigen::Dynamic, 1> scan_x(sizeRangeScan + 3),
208  scan_y(sizeRangeScan + 3);
209 
210  // Convert from the std::vector format:
211  const Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, 1>> scan_vals(
212  const_cast<float*>(&rangeScan.scan[0]), rangeScan.scan.size(),
213  1);
214  // SinCos table allocates N+4 floats for the convenience of SSE2:
215  // Map to make it appears it has the correct size:
216  const Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, 1>> ccos(
217  const_cast<float*>(&sincos_vals.ccos[0]), rangeScan.scan.size(),
218  1);
219  const Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, 1>> csin(
220  const_cast<float*>(&sincos_vals.csin[0]), rangeScan.scan.size(),
221  1);
222 
223  // Vectorized (optimized) scalar multiplications:
224  scan_x = scan_vals.array() * ccos.array();
225  scan_y = scan_vals.array() * csin.array();
226 
227  // To global:
228  // Non (manually) vectorized version:
229  scan_gx = m00 * scan_x + m01 * scan_y + m03;
230  scan_gy = m10 * scan_x + m11 * scan_y + m13;
231  scan_gz = m20 * scan_x + m21 * scan_y + m23;
232 #endif // MRPT_HAS_SSE2
233  }
234 
235  for (int i = 0; i < sizeRangeScan; i++)
236  {
237  if (rangeScan.validRange[i])
238  {
239  lx = scan_gx[i];
240  ly = scan_gy[i];
241  lz = scan_gz[i];
242 
243  // Specialized work in derived classes:
246  obj, lx, ly, lz, lric);
247 
248  lastPointWasInserted = false;
249 
250  // Add if distance > minimum only:
251  bool pt_pass_min_dist = true;
252  float d2 = 0;
253  if (useMinDist || obj.insertionOptions.also_interpolate)
254  {
255  if (!lastPointWasValid)
256  pt_pass_min_dist = false;
257  else
258  {
259  d2 =
260  (square(lx - lx_1) + square(ly - ly_1) +
261  square(lz - lz_1));
262  pt_pass_min_dist = (d2 > minDistSqrBetweenLaserPoints);
263  }
264  }
265 
266  if (thisIsTheFirst || pt_pass_min_dist)
267  {
268  thisIsTheFirst = false;
269  // Si quieren que interpolemos tb. los puntos lejanos,
270  // hacerlo:
271 
272  if (obj.insertionOptions.also_interpolate && i > 1)
273  {
274  float changeInDirection;
275  const float d = std::sqrt(d2);
276 
277  if ((lx != lx_1 || ly != ly_1) &&
278  (lx_1 != lx_2 || ly_1 != ly_2))
279  changeInDirection = atan2(ly - ly_1, lx - lx_1) -
280  atan2(ly_1 - ly_2, lx_1 - lx_2);
281  else
282  changeInDirection = 0;
283 
284  // Conditions to really interpolate the points:
285  if (d >= 2 *
286  obj.insertionOptions
287  .minDistBetweenLaserPoints &&
288  d < obj.insertionOptions
289  .maxDistForInterpolatePoints &&
290  fabs(changeInDirection) < DEG2RAD(5))
291  {
292  int nInterpol = mrpt::round(
293  d / (2 * sqrt(minDistSqrBetweenLaserPoints)));
294 
295  for (int q = 1; q < nInterpol; q++)
296  {
297  float i_x = lx_1 + q * (lx - lx_1) / nInterpol;
298  float i_y = ly_1 + q * (ly - ly_1) / nInterpol;
299  float i_z = lz_1 + q * (lz - lz_1) / nInterpol;
300  if (!obj.m_heightfilter_enabled ||
301  (i_z >= obj.m_heightfilter_z_min &&
302  i_z <= obj.m_heightfilter_z_max))
303  {
304  obj.m_x.push_back(i_x);
305  obj.m_y.push_back(i_y);
306  obj.m_z.push_back(i_z);
307  // Allow derived classes to add any other
308  // information to that point:
311  obj, lric);
312  } // end if
313  } // end for
314  } // End of interpolate:
315  }
316 
317  if (!obj.m_heightfilter_enabled ||
318  (lz >= obj.m_heightfilter_z_min &&
319  lz <= obj.m_heightfilter_z_max))
320  {
321  obj.m_x[nextPtIdx] = lx;
322  obj.m_y[nextPtIdx] = ly;
323  obj.m_z[nextPtIdx] = lz;
324  nextPtIdx++;
325 
326  // Allow derived classes to add any other information to
327  // that point:
330  obj, lric);
331 
332  lastPointWasInserted = true;
333  if (useMinDist)
334  {
335  lx_2 = lx_1;
336  ly_2 = ly_1;
337 
338  lx_1 = lx;
339  ly_1 = ly;
340  lz_1 = lz;
341  }
342  }
343  }
344  }
345 
346  // Save for next iteration:
347  lastPointWasValid = rangeScan.validRange[i] != 0;
348  }
349 
350  // The last point
351  if (lastPointWasValid && !lastPointWasInserted)
352  {
353  if (!obj.m_heightfilter_enabled ||
354  (lz >= obj.m_heightfilter_z_min &&
355  lz <= obj.m_heightfilter_z_max))
356  {
357  obj.m_x[nextPtIdx] = lx;
358  obj.m_y[nextPtIdx] = ly;
359  obj.m_z[nextPtIdx] = lz;
360  nextPtIdx++;
361  // Allow derived classes to add any other information to that
362  // point:
365  }
366  }
367 
368  // Adjust size:
369  obj.m_x.resize(nextPtIdx);
370  obj.m_y.resize(nextPtIdx);
371  obj.m_z.resize(nextPtIdx);
372  }
373 
374  static inline void templ_loadFromRangeScan(
375  Derived& obj, const mrpt::obs::CObservation3DRangeScan& rangeScan,
376  const mrpt::poses::CPose3D* robotPose)
377  {
378  using namespace mrpt::poses;
379  using mrpt::square;
380  obj.mark_as_modified();
381 
382  // If robot pose is supplied, compute sensor pose relative to it.
383  CPose3D sensorPose3D(UNINITIALIZED_POSE);
384  if (!robotPose)
385  sensorPose3D = rangeScan.sensorPose;
386  else
387  sensorPose3D.composeFrom(*robotPose, rangeScan.sensorPose);
388 
389  // Insert vs. load and replace:
390  if (!obj.insertionOptions.addToExistingPointsMap)
391  obj.resize(0); // Resize to 0 instead of clear() so the
392  // std::vector<> memory is not actually deadllocated
393  // and can be reused.
394 
395  if (!rangeScan.hasPoints3D) return; // Nothing to do!
396 
397  const size_t sizeRangeScan = rangeScan.points3D_x.size();
398 
399  // For a great gain in efficiency:
400  if (obj.m_x.size() + sizeRangeScan > obj.m_x.capacity())
401  obj.reserve(size_t(obj.m_x.size() + 1.1 * sizeRangeScan));
402 
403  // GENERAL CASE OF SCAN WITH ARBITRARY 3D ORIENTATION:
404  // --------------------------------------------------------------------------
406  sensorPose3D.getHomogeneousMatrix(lric.HM);
407  // For quicker access to values as "float" instead of "doubles":
408  float m00 = lric.HM.get_unsafe(0, 0);
409  float m01 = lric.HM.get_unsafe(0, 1);
410  float m02 = lric.HM.get_unsafe(0, 2);
411  float m03 = lric.HM.get_unsafe(0, 3);
412  float m10 = lric.HM.get_unsafe(1, 0);
413  float m11 = lric.HM.get_unsafe(1, 1);
414  float m12 = lric.HM.get_unsafe(1, 2);
415  float m13 = lric.HM.get_unsafe(1, 3);
416  float m20 = lric.HM.get_unsafe(2, 0);
417  float m21 = lric.HM.get_unsafe(2, 1);
418  float m22 = lric.HM.get_unsafe(2, 2);
419  float m23 = lric.HM.get_unsafe(2, 3);
420 
421  float lx_1, ly_1, lz_1, lx = 0, ly = 0,
422  lz = 0; // Punto anterior y actual:
423 
424  // Initial last point:
425  lx_1 = -100;
426  ly_1 = -100;
427  lz_1 = -100;
428 
429  float minDistSqrBetweenLaserPoints =
430  square(obj.insertionOptions.minDistBetweenLaserPoints);
431 
432  // If the user doesn't want a minimum distance:
433  if (obj.insertionOptions.minDistBetweenLaserPoints <= 0)
434  minDistSqrBetweenLaserPoints = -1;
435 
436  // ----------------------------------------------------------------
437  // Transform these points into 3D using the pose transformation:
438  // ----------------------------------------------------------------
439  bool lastPointWasValid = true;
440  bool thisIsTheFirst = true;
441  bool lastPointWasInserted = false;
442 
443  // Initialize extra stuff in derived class:
445 
446  for (size_t i = 0; i < sizeRangeScan; i++)
447  {
448  // Valid point?
449  if (rangeScan.points3D_x[i] != 0 || rangeScan.points3D_y[i] != 0 ||
450  rangeScan.points3D_z[i] != 0 ||
451  obj.insertionOptions.insertInvalidPoints)
452  {
453  lric.scan_x = rangeScan.points3D_x[i];
454  lric.scan_y = rangeScan.points3D_y[i];
455  lric.scan_z = rangeScan.points3D_z[i];
456 
457  lx = m00 * lric.scan_x + m01 * lric.scan_y + m02 * lric.scan_z +
458  m03;
459  ly = m10 * lric.scan_x + m11 * lric.scan_y + m12 * lric.scan_z +
460  m13;
461  lz = m20 * lric.scan_x + m21 * lric.scan_y + m22 * lric.scan_z +
462  m23;
463 
464  // Specialized work in derived classes:
467  obj, lx, ly, lz, lric);
468 
469  lastPointWasInserted = false;
470 
471  // Add if distance > minimum only:
472  float d2 =
473  (square(lx - lx_1) + square(ly - ly_1) + square(lz - lz_1));
474  if (thisIsTheFirst ||
475  (lastPointWasValid && (d2 > minDistSqrBetweenLaserPoints)))
476  {
477  thisIsTheFirst = false;
478 
479  obj.m_x.push_back(lx);
480  obj.m_y.push_back(ly);
481  obj.m_z.push_back(lz);
482  // Allow derived classes to add any other information to
483  // that point:
486 
487  lastPointWasInserted = true;
488 
489  lx_1 = lx;
490  ly_1 = ly;
491  lz_1 = lz;
492  }
493 
494  lastPointWasValid = true;
495  }
496  else
497  {
498  lastPointWasValid = false;
499  }
500 
502  obj, lric);
503  }
504 
505  // The last point
506  if (lastPointWasValid && !lastPointWasInserted)
507  {
508  if (lx != 0 || ly != 0 || lz != 0)
509  {
510  obj.m_x.push_back(lx);
511  obj.m_y.push_back(ly);
512  obj.m_z.push_back(lz);
513  // Allow derived classes to add any other information to that
514  // point:
517  }
518  }
519  }
520 };
521 
522 }
523 #endif
524 
525 
double DEG2RAD(const double x)
Degrees to radians.
GLdouble GLdouble GLdouble GLdouble q
Definition: glext.h:3721
Declares a class derived from "CObservation" that encapsules a 3D range scan measurement, as from a time-of-flight range camera or any other RGBD sensor.
GLsizei GLsizei GLuint * obj
Definition: glext.h:4070
size_t getScanSize() const
Get number of scan rays.
mrpt::containers::ContainerReadOnlyProxyAccessor< mrpt::aligned_std_vector< char > > validRange
It&#39;s false (=0) on no reflected rays, referenced to elements in scan.
static void templ_loadFromRangeScan(Derived &obj, const mrpt::obs::CObservation3DRangeScan &rangeScan, const mrpt::poses::CPose3D *robotPose)
T square(const T x)
Inline function for the square of a number.
mrpt::containers::ContainerReadOnlyProxyAccessor< mrpt::aligned_std_vector< float > > scan
The range values of the scan, in meters.
void composeFrom(const CPose3D &A, const CPose3D &B)
Makes "this = A (+) B"; this method is slightly more efficient than "this= A + B;" since it avoids th...
Definition: CPose3D.cpp:565
mrpt::math::CMatrixDouble44 HM
Homog matrix of the local sensor pose within the robot.
Definition: CPointsMap.h:102
float scan_x
In internal_loadFromRangeScan3D_prepareOneRange, these are the local coordinates of the scan points b...
Definition: CPointsMap.h:106
static void templ_loadFromRangeScan(Derived &obj, const mrpt::obs::CObservation2DRangeScan &rangeScan, const mrpt::poses::CPose3D *robotPose)
bool hasPoints3D
true means the field points3D contains valid data.
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
A "CObservation"-derived class that represents a 2D range scan measurement (typically from a laser sc...
A pair of vectors with the cos and sin values.
mrpt::poses::CPose3D sensorPose
The 6D pose of the sensor on the robot.
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:86
std::vector< float > points3D_x
If hasPoints3D=true, the (X,Y,Z) coordinates of the 3D point cloud detected by the camera...
Helper struct used for internal_loadFromRangeScan3D_prepareOneRange()
Definition: CPointsMap.h:94
mrpt::math::CMatrixDouble44 HM
Homog matrix of the local sensor pose within the robot.
Definition: CPointsMap.h:84
mrpt::poses::CPose3D sensorPose
The 6D pose of the sensor on the robot at the moment of starting the scan.
void getHomogeneousMatrix(mrpt::math::CMatrixDouble44 &out_HM) const
Returns the corresponding 4x4 homogeneous transformation matrix for the point(translation) or pose (t...
Definition: CPose3D.h:221
Helper struct used for internal_loadFromRangeScan2D_prepareOneRange()
Definition: CPointsMap.h:76
int round(const T value)
Returns the closer integer (int) to x.
Definition: round.h:23



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020