laserIMUCalibration
laserNAVCalib.cpp
Go to the documentation of this file.
1 
12 #include "laserNAVCalib.h"
13 
14 // copy pointclouds
15 #include <pcl/common/io.h>
16 // transform pointclouds
17 #include <pcl/common/transforms.h>
18 
19 // setw()
20 #include <iomanip>
21 // fixed
22 #include <ios>
23 // File access
24 #include <fstream>
25 #include <sstream>
26 #include <sys/stat.h>
27 // Bind function-pointer (needed for Levenberg-Marquardt)
28 #include <boost/bind.hpp>
29 
30 // register pointclouds with different optimizations
31 #include "pointcloudRegistration.h"
32 // optimization with Levenberg-Marquardt
33 #include "lmmin.h"
34 
35 unsigned int iterations;
36 
38 // used pointclouds
41 // Average error for LM
43 // optimizations
52 
54  double pos_x
55  , double pos_y
56  , double pos_z
57  , double phi
58  , double theta
59  , double psi)
60  : sampled_scans(0)
61  // we have no scans received initially
62  , scan_counter(0)
63  , plot_all_movements(true)
64  , calibration_wanted(true)
65  , last(new pointCloud)
66  , latest(new pointCloud)
67  , nr_scan_pairs(0)
68  // control flow
69  , add_to_data_set(false)
70  , init(true)
71 {
72  // From the user estimated/measured pose
79 }
80 
82 {
83  last.reset();
84  latest.reset();
85  last_to_draw.reset();
86  latest_to_draw.reset();
87  data_set.clear();
88 }
89 
90 void
92  double pos_x, double pos_y, double pos_z
93  , double phi, double theta, double psi
94  , std::string name)
95 {
96  const double width = 20;
97  std::cout << std::endl
98  << std::setw( width ) << std::left << name << ":" << std::endl
99  << std::fixed
100  << std::setw( width ) << std::right << "x" << " " << pos_x << std::endl
101  << std::setw( width ) << std::right << "y" << " " << pos_y << std::endl
102  << std::setw( width ) << std::right << "z" << " " << pos_z << std::endl
103  << std::setw( width ) << std::right << "phi" << " " << math::c_RAD_TO_DEG * phi << std::endl
104  << std::setw( width ) << std::right << "theta" << " " << math::c_RAD_TO_DEG * theta << std::endl
105  << std::setw( width ) << std::right << "psi" << " " << math::c_RAD_TO_DEG * psi << std::endl
106  << std::endl;
107 }
108 
109 void
111  Eigen::Affine3d* transformation_to_print
112  , std::string name)
113 {
114  print(&transformation_to_print->matrix(), name);
115 }
116 
117 void
119  Eigen::Matrix4d* transformation_to_print
120  , std::string name)
121 {
122  double d_x, d_y, d_z, phi, theta, psi;
123  pcl::getTranslationAndEulerAngles(
124  Eigen::Affine3d(*transformation_to_print), d_x, d_y, d_z, phi, theta, psi);
125  print(d_x, d_y, d_z, phi, theta, psi, name);
126 }
127 
128 bool
130  const std::string& name)
131 {
132  struct stat buffer;
133  return (stat (name.c_str(), &buffer) == 0);
134 }
135 
136 void
138  std::string header
139  , std::string data
140  , std::string filename)
141 {
142  std::string folder = "../log/plots/";
143  std::string extension = ".csv";
144 
145  std::stringstream plot_file_ss;
146  plot_file_ss << folder << filename << extension;
147  std::string plot_file_s = plot_file_ss.str();
148 
149  std::fstream plot_file;
150 
151  if (exists(plot_file_s)) {
152  plot_file.open(
153  plot_file_s.c_str()
154  , std::fstream::in | std::fstream::out | std::fstream::app);
155  } else {
156  plot_file.open(
157  plot_file_s.c_str()
158 
159  , std::fstream::in | std::fstream::out | std::fstream::app);
160  plot_file << header << std::endl;
161  }
162  plot_file << data << std::endl;
163 
164  plot_file.close();
165 }
166 
167 Eigen::Matrix4d
169  pointCloud::Ptr last
170  , pointCloud::Ptr latest)
171 {
172  Eigen::Affine3d T_movement;
173  // const sensors::NavData* lastNav
174  // = boost::static_pointer_cast<pointCloud>(last)->nav_data;
175  // const sensors::NavData* latestNav
176  // = boost::static_pointer_cast<pointCloud>(latest)->nav_data;
177 
178  // if (lastNav && latestNav) {
179  // double d_x, d_y, d_z, d_phi, d_theta, d_psi;
180 
181  // d_x = latestNav->getPos().getX() - lastNav->getPos().getX();
182  // d_y = latestNav->getPos().getY() - lastNav->getPos().getY();
183  // d_z = latestNav->getPos().getZ() - lastNav->getPos().getZ();
184  // d_phi = latestNav->getEuler().getX() - lastNav->getEuler().getX();
185  // d_theta = latestNav->getEuler().getY() - lastNav->getEuler().getY();
186  // d_psi = latestNav->getEuler().getZ() - lastNav->getEuler().getZ();
187 
188  // pcl::getTransformation(d_x, d_y, d_z, d_phi, d_theta, d_psi, T_movement);
189 
190  // std::string movement_header = "x_1;y_1;z_1;phi_1;theta_1;psi_1;x_2;y_2;z_2;phi_2;theta_2;psi_2;d_x;d_y;d_z;d_phi;d_theta;d_psi";
191  // std::stringstream movement;
192  // movement << std::setprecision(10)
193  // << lastNav->getPos().getX() << ";"
194  // << lastNav->getPos().getY() << ";"
195  // << lastNav->getPos().getZ() << ";"
196  // << lastNav->getEuler().getX() << ";"
197  // << lastNav->getEuler().getY() << ";"
198  // << lastNav->getEuler().getZ() << ";"
199  // << latestNav->getPos().getX() << ";"
200  // << latestNav->getPos().getY() << ";"
201  // << latestNav->getPos().getZ() << ";"
202  // << latestNav->getEuler().getX() << ";"
203  // << latestNav->getEuler().getY() << ";"
204  // << latestNav->getEuler().getZ() << ";"
205  // << d_x << ";" << d_y << ";" << d_z << ";" << d_phi << ";" << d_theta << ";" << d_psi;
206  // plot(movement_header, movement.str(), "movement");
207  // }
208  return T_movement.matrix();
209 }
210 
211 Eigen::Matrix4d
213  pointCloud::Ptr last
214  , pointCloud::Ptr latest
215  , double* icp_fitness_score
216  , double iterations)
217 {
218  Eigen::Matrix4d transformationBetweenScans;
219 
220  pcl::copyPointCloud(*last, *last_to_draw);
221 
222  if (downsampling) {
223  filter::downsample(last);
224  filter::downsample(latest);
225  }
226 
227  if (bilateral_filtering) {
230  }
231 
232  if (calculate_normals || covariance_sampling || initial_aligning) {
233  computeSurfaceNormals(last);
234  computeSurfaceNormals(latest);
235 
236  if (covariance_sampling) {
238  covarianceDownsampling(latest);
239  }
240 
241  if (initial_aligning) {
242  pcl::PointCloud< pcl::FPFHSignature33 >::Ptr last_descriptors
244  pcl::PointCloud< pcl::FPFHSignature33 >::Ptr latest_descriptors
246 
247  transformationBetweenScans = filter::initialAlignment(last, last_descriptors
248  , latest, latest_descriptors);
249  }
250  }
251 
252  pcl::copyPointCloud(*last, *last_to_draw);
253 
254  switch (ICPMethod) {
255  case E_STANDARD:
256  transformationBetweenScans = filter::icp(last, latest, icp_fitness_score, iterations);
257  break;
258  case E_LEVENBERG_MARQUARDT:
259  transformationBetweenScans = filter::icpLM(last, latest, icp_fitness_score, iterations);
260  break;
261  }
262  return transformationBetweenScans;
263 }
264 
266  pointCloud::Ptr first
267  , pointCloud::Ptr second
268  , double pos_x
269  , double pos_y
270  , double pos_z
271  , double phi
272  , double theta
273  , double psi
274 )
275 {
276  const unsigned int iterate_once = 0;
277  double icp_fitness_score = 0.0;
278 
279  Eigen::Affine3d T_mounting_pose;
280  pcl::getTransformation(pos_x, pos_y, pos_z, phi, theta, psi, T_mounting_pose);
281 
282  // create temporary copies from point-clouds
283  pointCloud::Ptr temp_first (new pointCloud);
284  pcl::copyPointCloud(*first, *temp_first);
285  pointCloud::Ptr temp_second (new pointCloud);
286  pcl::copyPointCloud(*second, *temp_second);
287 
288  // transform both from sensor to carrier with estimated mounting-pose
289  pcl::transformPointCloud(*temp_first, *temp_first, T_mounting_pose);
290  pcl::transformPointCloud(*temp_second, *temp_second, T_mounting_pose);
291 
292  // transform first point-cloud to next pose with given nav-data
293  Eigen::Affine3d T_movement = Eigen::Affine3d(getMovementFrom(temp_first, temp_first));
294  pcl::transformPointCloud(*temp_first, *temp_first, T_movement);
295 
296  // get error-metric depending on the difference between both scans (wrong mounting-pose)
297  getScanTransformationFrom(
298  temp_first
299  , temp_second
300  , &icp_fitness_score
301  , iterate_once);
302  return icp_fitness_score;
303 }
304 
306  const double *par
307  , int m_dat
308  , const void *data
309  , double *fvec
310  , int *info )
311 {
312  const std::vector<last_and_latest> *curr_data_set
313  = reinterpret_cast<const std::vector<last_and_latest>* >(data);
314  unsigned int nr_scan_pairs = curr_data_set->size();
315 
316  double score_sum = 0.0;
317 
318  for (int i = 0; i < nr_scan_pairs; ++i) {
319 
320  double icp_fitness_score
321  = evaluateICPFitnessScore(
322  curr_data_set->at(i).first, curr_data_set->at(i).second
323  , par[0], par[1], par[2], par[3], par[4], par[5]);
324 
325  fvec[i] = icp_fitness_score;
326  if (fvec[i] > worst_score) {
327  worst_pair = i;
328  worst_score = fvec[i];
329  }
330  score_sum += fvec[i];
331 
332  std::string pose_header = "data_pair;d_x;d_y;d_z;phi;theta;psi;icp_fitness_score";
333  std::stringstream pose;
334  pose << i
335  << par[0] << ";" << par[1] << ";" << par[2] << ";"
336  << par[3] << ";" << par[4] << ";" << par[5] << ";"
337  << icp_fitness_score;
338  plot(pose_header, pose.str(), "estimated_mounting_pose");
339  }
340  std::cout << ++iterations << "\r" << std::flush;
341  avr_cal_err = score_sum / nr_scan_pairs;
342 }
343 
344 const pointCloud::Ptr
346 {
347  return latest_to_draw;
348 }
349 
350 const pointCloud::Ptr
352 {
353  return last_to_draw;
354 }
355 
356 void
358 {
359  downsampling = new_state;
360 }
361 
362 bool
364 {
365  return downsampling;
366 }
367 
368 void
370 {
371  bilateral_filtering = new_state;
372 }
373 
374 bool
376 {
377  return bilateral_filtering;
378 }
379 
380 void
382 {
383  calculate_normals = new_state;
384 }
385 
386 bool
388 {
389  return calculate_normals;
390 }
391 
392 void
394 {
395  covariance_sampling = new_state;
396 }
397 
398 bool
400 {
401  return covariance_sampling;
402 }
403 
404 void
406 {
407  initial_aligning = new_state;
408 }
409 
410 bool
412 {
413  return initial_aligning;
414 }
415 
416 void
418 {
419  add_to_data_set = true;
420 }
421 
422 void
424 {
425  data_set.clear();
426 }
427 
428 void
430 {
431  std::swap(data_set[worst_pair], data_set.back());
432  data_set.pop_back();
433 }
434 
435 void
437  unsigned int scans_between_last_and_latest)
438 {
439  this->scans_between_last_and_latest = scans_between_last_and_latest;
440 }
441 
442 void
443 filter::LaserNAVCalibration::update(pointCloud::Ptr cloud_to_draw)
444 {
445  iterations = 0;
446  if (plot_all_movements) {
447  if (0 == scan_counter) {
448  if (cloud_to_draw && !cloud_to_draw->points.empty()) {
449  pcl::copyPointCloud(*cloud_to_draw, *last);
450  // boost::static_pointer_cast<pointCloud>(last)->setNavData(
451  // boost::static_pointer_cast<pointCloud>(cloud_to_draw)->nav_data);
452  ++scan_counter;
453  }
454  }
455  else if (scans_between_last_and_latest == scan_counter) {
456  if (cloud_to_draw && !cloud_to_draw->points.empty()) {
457  pcl::copyPointCloud(*cloud_to_draw, *latest);
458  // boost::static_pointer_cast<pointCloud>(latest)->setNavData(
459  // boost::static_pointer_cast<pointCloud>(cloud_to_draw)->nav_data);
460  double d_x, d_y, d_z, d_phi, d_theta, d_psi;
461  pcl::getTranslationAndEulerAngles(
462  Eigen::Affine3d(getMovementFrom(last, latest)), d_x, d_y, d_z, d_phi, d_theta, d_psi);
463  if (sampled_scans == 170 || sampled_scans == 200 || sampled_scans == 75
464  || sampled_scans == 140 || sampled_scans == 60 || sampled_scans == 190) {
465  pointCloud::Ptr temp_last (new pointCloud);
466  pointCloud::Ptr temp_latest (new pointCloud);
467  pcl::copyPointCloud(*last, *temp_last);
468  pcl::copyPointCloud(*latest, *temp_latest);
469  data_set.push_back(last_and_latest(temp_last, temp_latest));
470 // double icp_fitness_score;
471 // getScanTransformationFrom(
472 // temp_last
473 // , temp_latest
474 // , &icp_fitness_score
475 // , 1);
476  ++nr_scan_pairs;
477  }
478  ++sampled_scans;
479  scan_counter = 0;
480  }
481  }
482  ++scan_counter;
483  }
484 
485 // if(add_to_data_set) {
486 // switch (scan_counter) {
487 // case 0: {
488 // if(cloud_to_draw && !cloud_to_draw->points.empty()) {
489 // pcl::copyPointCloud(*cloud_to_draw, *last);
490 // boost::static_pointer_cast<pointCloud>(last)->setNavData(
491 // boost::static_pointer_cast<pointCloud>(cloud_to_draw)->nav_data);
492 // ++scan_counter;
493 // }
494 // break;
495 // }
496 // case scans_between_last_and_latest: {
497 // if(cloud_to_draw && !cloud_to_draw->points.empty()) {
498 // pcl::copyPointCloud(*cloud_to_draw, *latest);
499 // boost::static_pointer_cast<pointCloud>(latest)->setNavData(
500 // boost::static_pointer_cast<pointCloud>(cloud_to_draw)->nav_data);
501 // pointCloud::Ptr temp_last (new pointCloud);
502 // pointCloud::Ptr temp_latest (new pointCloud);
503 // pcl::copyPointCloud(*last, *temp_last);
504 // pcl::copyPointCloud(*latest, *temp_latest);
505 // data_set.push_back(last_and_latest(temp_last, temp_latest));
506 // ++nr_scan_pairs;
507 // add_to_data_set = false;
508 // scan_counter = 0;
509 // }
510 // break;
511 // }
512 // default:
513 // ++scan_counter;
514 // }
515 
516  if (nr_scan_pairs >= nr_unknowns && calibration_wanted) {
517  // set the mounting_pose initially
518  if (init) {
519  mounting_pose[0] = pos_x;
520  mounting_pose[1] = pos_y;
521  mounting_pose[2] = pos_z;
522  mounting_pose[3] = phi;
523  mounting_pose[4] = theta;
524  mounting_pose[5] = psi;
525  init = false;
526  }
527 
528  /* auxiliary parameters */
529  lm_status_struct status;
531  control.verbosity = 3;
532 
533 // control.ftol = LM_USERTOL;
534 // control.xtol = LM_USERTOL;
535 // control.gtol = LM_USERTOL;
536  control.epsilon = math::c_PI;
537 
538  /* perform the fit */
539  printf( "Fitting:\n" );
540  lmmin( nr_unknowns, mounting_pose, nr_scan_pairs, (const void*)&data_set
542  , &control, &status );
543 
544  std::cout << "Iterations: " << status.nfev << std::endl;
545 
546  print(
547  mounting_pose[0], mounting_pose[1], mounting_pose[2]
548  , mounting_pose[3], mounting_pose[4], mounting_pose[5]
549  , "Pose");
550 
551  plot_all_movements = false;
552 
553  // maybe we are trapped in a local-minimum so re-init the solution
554  if (avr_cal_err > 10.0) {
555  optimizeDataSet();
556 
557  Eigen::Affine3d T_new_init;
558  Eigen::Affine3d T_scan_pairs;
559  double smallest_score = 100.0;
560  double scan_pairs_score = 100.0;
561  for (unsigned int i = 0; i < nr_scan_pairs; ++i) {
562  T_scan_pairs = getScanTransformationFrom(
563  data_set.at(i).first
564  , data_set.at(i).second
565  , &scan_pairs_score
566  , 20);
567  if (scan_pairs_score < smallest_score) {
568  smallest_score = scan_pairs_score;
569  T_new_init = T_scan_pairs;
570  }
571  }
572  pcl::getTranslationAndEulerAngles(
573  T_new_init
574  , mounting_pose[0], mounting_pose[1], mounting_pose[2]
575  , mounting_pose[3], mounting_pose[4], mounting_pose[5]);
576 
577  print(&T_new_init, "new-init");
578  } else {
579  calibration_wanted = false;
580  }
581  }
582 // ScannerOverviewFilter::update();
583 }
double epsilon
Step used to calculate the Jacobian.
Definition: lmstruct.h:59
static bool bilateral_filtering
When true: bilateral-filtering is used (computes fpfh-features!)
Declarations for Levenberg-Marquardt minimization.
void print(double pos_x, double pos_y, double pos_z, double phi, double theta, double psi, std::string name="Transformation")
Print the given transformation on command-line-interface.
void bilateralFiltering(pointCloud::Ptr organized_cloud)
Use bilateral filtering.
void resetDataSet(void)
Reset/Remove all point-cloud-pairs from the data-set.
Collection of input parameters for fit control.
Definition: lmstruct.h:33
static void getMountingPose(const double *par, int m_dat, const void *data, double *fvec, int *info)
Optimized the mounting-pose as part of the Levenberg-Marquardt.
const pointCloud::Ptr getLastCloud(void) const
Returns the last stable cloud.
static bool exists(const std::string &name)
Checks if the given filename-at-path exists.
This file implements all the filter and registration methods for point-cloud data using wrappers for ...
bool getInitialAligning(void)
Retrieves if the initial alignment is activated.
double pos_x
Class-wide latest position (this is altered after optimization)
Definition: laserNAVCalib.h:85
double c_RAD_TO_DEG
static bool initial_aligning
When true: Initial Alignment is calculated using Sample-Consensus (untested)
bool getBilateralFiltering(void)
Retrieves if the bilateral filter is activated.
static e_registrationMethod ICPMethod
Selector for the calibration-method (not supported atm)
static Eigen::Matrix4d getMovementFrom(pointCloud::Ptr last, pointCloud::Ptr latest)
Retrieves the movement of the Navigation-Data between the last and latest point-cloud.
e_registrationMethod
Selector for the calibration-method (not supported atm)
Definition: laserNAVCalib.h:47
This file introduces the class LaserNAVCalibration which is used to calibrate the laser-pose with nav...
Eigen::Matrix4d icpLM(const pointCloud::Ptr source, const pointCloud::Ptr target, double *fitness_score, const double iterations)
Calls the pcl iterativeClosestPoint-algorithm levenberg-marquardt alternative (internally) ...
std::pair< pointCloud::Ptr, pointCloud::Ptr > last_and_latest
Typedef for shorter call (used as data-set for the levenberg-marquardt data)
Definition: laserNAVCalib.h:30
double c_PI
pcl::PointCloud< pcl::PointNormal > pointCloud
Typedef for shorter call of point-cloud-pointer-type.
Definition: laserNAVCalib.h:25
const lm_control_struct lm_control_double
Preset (and recommended) control parameter settings.
Definition: lmmin.c:121
Collection of output parameters for status info.
Definition: lmstruct.h:106
void setCovarianceSampling(bool new_state)
Sets the covariance-sampling of point-normals.
virtual ~LaserNAVCalibration()
Virtual std. dtor.
int verbosity
Controls the ouput verbosity.
Definition: lmstruct.h:90
void setInitialAligning(bool new_state)
Sets the initial alignment of point-normals.
static double worst_score
Holds the worst ICP-fitness-score for the according point-cloud-pair.
pcl::PointCloud< pcl::FPFHSignature33 >::Ptr fastPointFeatureHistogram(const pointCloud::Ptr input)
Extracts all (F)PFHs.
virtual void update(pointCloud::Ptr cloud_to_draw)
Updates the mounting-pose using Levenberg-Marquardt non-linear optimization.
void setBilateralFiltering(bool new_state)
Sets the bilateral filter.
static unsigned int scans_between_last_and_latest
Number of scans between point-cloud-pairs (those are simply dropped)
Definition: laserNAVCalib.h:59
void setDownsampling(bool new_state)
Sets the filter voxel-grid.
static bool downsampling
When true: all point-clouds are downsampled using Voxel-Grids.
bool getCalculateNormals(void)
Retrieves if the bilateral filter is activated.
void covarianceDownsampling(pointCloud::Ptr cloud)
Use covariances for downsampling.
void computeSurfaceNormals(pointCloud::Ptr cloud)
Compute the surface normals.
bool getDownsampling(void)
Retrieves if the voxel-grid filter is activated.
static bool calculate_normals
When true: calculate normals (basis for covariance-sampling and initial-alignment) ...
static void plot(std::string header, std::string data, std::string filename)
Plot the given header + data to file.
static size_t worst_pair
Index of the point-cloud-pair with the worst ICP-fitness-score.
static Eigen::Matrix4d getScanTransformationFrom(pointCloud::Ptr last, pointCloud::Ptr latest, double *icp_fitness_score, double iterations)
Retrieves the transformation from last to latest point-cloud.
void downsample(pointCloud::Ptr cloud)
Simple Voxel-Grid downsampling.
LaserNAVCalibration(double pos_x=0.0, double pos_y=0.0, double pos_z=0.0, double phi=math::c_DEG_TO_RAD *0.0, double theta=math::c_DEG_TO_RAD *0.0, double psi=math::c_DEG_TO_RAD *0.0)
std. ctor.
bool getCovarianceSampling(void)
Retrieves if the covariance-sampling is activated.
void setCalculateNormals(bool new_state)
Sets the calculation of point-normals.
static double avr_cal_err
Mean of the summed ICP-fitness-score's (updated each iteration)
void lmmin(int n, double *x, int m, const void *data, void(*evaluate)(const double *par, int m_dat, const void *data, double *fvec, int *userbreak), const lm_control_struct *C, lm_status_struct *S)
Levenberg-Marquardt minimization.
Definition: lmmin.c:183
const pointCloud::Ptr getLatestCloud(void) const
Returns the latest stable cloud.
Eigen::Matrix4d initialAlignment(pointCloud::Ptr source, pcl::PointCloud< pcl::FPFHSignature33 >::Ptr source_descriptors, pointCloud::Ptr target, pcl::PointCloud< pcl::FPFHSignature33 >::Ptr target_descriptors)
Aligns the point-clouds initially using Sample Consensus.
void optimizeDataSet(void)
Deletes the worst-pair according to its ICP-fitness-score.
Eigen::Matrix4d icp(const pointCloud::Ptr source, const pointCloud::Ptr target, double *fitness_score, const double iterations)
Calls the pcl iterativeClosestPoint-algorithm.
static double evaluateICPFitnessScore(pointCloud::Ptr first, pointCloud::Ptr second, double pos_x, double pos_y, double pos_z, double phi, double theta, double psi)
void addToDataSet(void)
Sets the boolean to add the point-clouds at the active index to the data-set-pairs.
int nfev
actual number of iterations.
Definition: lmstruct.h:114
static pointCloud::Ptr latest_to_draw
Cloud-ptr that stores the latest point-cloud that will be drawn (stable, switched after registration-...
static bool covariance_sampling
When true: Samples the point-cloud using covariance-matrices.
void setScansBetweenLastAndLatest(unsigned int scans_between_last_and_latest)
Sets the number of scans to skip between one registration.
unsigned int iterations
static pointCloud::Ptr last_to_draw
Cloud-ptr that stores the last point-cloud that will be drawn (stable, switched after registration-pr...