velo2cam_calibration.cpp
Go to the documentation of this file.
1 /*
2  velo2cam_calibration - Automatic calibration algorithm for extrinsic
3  parameters of a stereo camera and a velodyne Copyright (C) 2017-2021 Jorge
4  Beltran, Carlos Guindel
5 
6  This file is part of velo2cam_calibration.
7 
8  velo2cam_calibration is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 2 of the License, or
11  (at your option) any later version.
12 
13  velo2cam_calibration is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with velo2cam_calibration. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 /*
23  velo2cam_calibration: Perform the registration step
24 */
25 
26 #define PCL_NO_PRECOMPILE
27 
28 #include <pcl/registration/transformation_estimation_svd.h>
30 #include <pcl_ros/transforms.h>
31 #include <ros/package.h>
32 #include <ros/ros.h>
33 #include <std_msgs/Empty.h>
34 #include <std_msgs/Int32.h>
35 #include <tf/tf.h>
37 #include <tf/transform_listener.h>
38 #include <tinyxml.h>
39 #include <velo2cam_calibration/ClusterCentroids.h>
40 #include <velo2cam_utils.h>
41 
42 using namespace std;
43 using namespace sensor_msgs;
44 
49 int nFrames;
51 
52 pcl::PointCloud<pcl::PointXYZ>::Ptr sensor1_cloud, sensor2_cloud;
53 pcl::PointCloud<pcl::PointXYZI>::Ptr isensor1_cloud, isensor2_cloud;
54 std::vector<pcl::PointXYZ> sensor1_vector(4), sensor2_vector(4);
55 
57 
60 string sensor1_frame_id = "";
62 string sensor2_frame_id = "";
64 
65 typedef Eigen::Matrix<double, 12, 12> Matrix12d;
66 typedef Eigen::Matrix<double, 12, 1> Vector12d;
67 
69 
70 std::vector<std::vector<std::tuple<int, int, pcl::PointCloud<pcl::PointXYZ>,
71  std::vector<pcl::PointXYZ>>>>
73 std::vector<std::vector<std::tuple<int, int, pcl::PointCloud<pcl::PointXYZ>,
74  std::vector<pcl::PointXYZ>>>>
76 
78 bool S1_WARMUP_DONE = false, S2_WARMUP_DONE = false;
81 
87 
89 
90 std::ofstream savefile;
91 
92 void sensor1_callback(
93  const velo2cam_calibration::ClusterCentroids::ConstPtr sensor1_centroids);
94 void sensor2_callback(
95  velo2cam_calibration::ClusterCentroids::ConstPtr sensor2_centroids);
96 
98 
100 
101 void calibrateExtrinsics(int seek_iter = -1) {
102  std::vector<pcl::PointXYZ> local_sensor1_vector, local_sensor2_vector;
103  pcl::PointCloud<pcl::PointXYZ>::Ptr local_sensor1_cloud(
104  new pcl::PointCloud<pcl::PointXYZ>),
105  local_sensor2_cloud(new pcl::PointCloud<pcl::PointXYZ>);
106  pcl::PointCloud<pcl::PointXYZ> local_l_cloud, local_c_cloud;
107 
108  int used_sensor2, used_sensor1;
109 
110  // Get final frame names for TF broadcaster
111  string sensor1_final_transformation_frame = sensor1_frame_id;
112  if (is_sensor1_cam) {
113  sensor1_final_transformation_frame = sensor1_rotated_frame_id;
114  }
115  string sensor2_final_transformation_frame = sensor2_frame_id;
116  if (is_sensor2_cam) {
117  sensor2_final_transformation_frame = sensor2_rotated_frame_id;
118  }
119 
120  int total_sensor1, total_sensor2;
121 
122  if (seek_iter > 0) { // Add clouds (per sensor) from every position using
123  // last 'seek_iter' detection
124  if (DEBUG) ROS_INFO("Seeking %d iterations", seek_iter);
125 
126  for (int i = 0; i < TARGET_POSITIONS_COUNT + 1; ++i) {
127  if (DEBUG)
128  ROS_INFO("Target position: %d, Last sensor2: %d, last sensor1: %d",
129  i + 1, std::get<0>(sensor2_buffer[i].back()),
130  std::get<0>(sensor1_buffer[i].back()));
131  // Sensor 1
132  auto it1 = std::find_if(
133  sensor1_buffer[i].begin(), sensor1_buffer[i].end(),
134  [&seek_iter](
135  const std::tuple<int, int, pcl::PointCloud<pcl::PointXYZ>,
136  std::vector<pcl::PointXYZ>> &e) {
137  return std::get<0>(e) == seek_iter;
138  });
139  if (it1 == sensor1_buffer[i].end()) {
140  ROS_WARN("Could not sync sensor1");
141  return;
142  }
143 
144  local_sensor1_vector.insert(
145  local_sensor1_vector.end(), std::get<3>(*it1).begin(),
146  std::get<3>(*it1).end()); // Add sorted centers (for equations)
147  *local_sensor1_cloud +=
148  std::get<2>(*it1); // Add centers cloud (for registration)
149  used_sensor1 = std::get<1>(*it1);
150  total_sensor1 = std::get<0>(*it1);
151 
152  // Sensor 2
153  auto it2 = std::find_if(
154  sensor2_buffer[i].begin(), sensor2_buffer[i].end(),
155  [&seek_iter](
156  const std::tuple<int, int, pcl::PointCloud<pcl::PointXYZ>,
157  std::vector<pcl::PointXYZ>> &e) {
158  return std::get<0>(e) == seek_iter;
159  });
160  if (it2 == sensor2_buffer[i].end()) {
161  ROS_WARN("Could not sync sensor2");
162  return;
163  }
164 
165  local_sensor2_vector.insert(
166  local_sensor2_vector.end(), std::get<3>(*it2).begin(),
167  std::get<3>(*it2).end()); // Add sorted centers (for equations)
168  *local_sensor2_cloud +=
169  std::get<2>(*it2); // Add centers cloud (for registration)
170  used_sensor2 = std::get<1>(*it2);
171  total_sensor2 = std::get<0>(*it2);
172  }
173  ROS_INFO("Synchronizing cluster centroids");
174  } else { // Add clouds (per sensor) from every position using last available
175  // detection
176  for (int i = 0; i < TARGET_POSITIONS_COUNT + 1; ++i) {
177  // Sensor 1
178  local_sensor1_vector.insert(
179  local_sensor1_vector.end(),
180  std::get<3>(sensor1_buffer[i].back()).begin(),
181  std::get<3>(sensor1_buffer[i].back())
182  .end()); // Add sorted centers (for equations)
183  *local_sensor1_cloud += std::get<2>(
184  sensor1_buffer[i].back()); // Add centers cloud (for registration)
185  used_sensor1 = std::get<1>(sensor2_buffer[i].back());
186 
187  // Sensor 2
188  local_sensor2_vector.insert(
189  local_sensor2_vector.end(),
190  std::get<3>(sensor2_buffer[i].back()).begin(),
191  std::get<3>(sensor2_buffer[i].back())
192  .end()); // Add sorted centers (for equations)
193  *local_sensor2_cloud += std::get<2>(
194  sensor2_buffer[i].back()); // Add centers cloud (for registration)
195  }
196  }
197 
198  if (DEBUG) {
199  sensor_msgs::PointCloud2 ros_cloud;
200  pcl::toROSMsg(*local_sensor2_cloud, ros_cloud);
201  ros_cloud.header.frame_id = sensor2_rotated_frame_id;
202  clusters_sensor2_pub.publish(ros_cloud);
203 
204  pcl::toROSMsg(*local_sensor1_cloud, ros_cloud);
205  ros_cloud.header.frame_id = sensor1_frame_id;
206  clusters_sensor1_pub.publish(ros_cloud);
207  }
208 
209  // SVD code
210  pcl::PointCloud<pcl::PointXYZ>::Ptr sorted_centers1(
211  new pcl::PointCloud<pcl::PointXYZ>());
212  pcl::PointCloud<pcl::PointXYZ>::Ptr sorted_centers2(
213  new pcl::PointCloud<pcl::PointXYZ>());
214 
215  for (int i = 0; i < local_sensor1_vector.size(); ++i) {
216  sorted_centers1->push_back(local_sensor1_vector[i]);
217  sorted_centers2->push_back(local_sensor2_vector[i]);
218  }
219 
220  Eigen::Matrix4f final_transformation;
221  const pcl::registration::TransformationEstimationSVD<pcl::PointXYZ,
222  pcl::PointXYZ>
223  trans_est_svd(true);
224  trans_est_svd.estimateRigidTransformation(*sorted_centers1, *sorted_centers2,
225  final_transformation);
226 
227  tf::Matrix3x3 tf3d;
228  tf3d.setValue(final_transformation(0, 0), final_transformation(0, 1),
229  final_transformation(0, 2), final_transformation(1, 0),
230  final_transformation(1, 1), final_transformation(1, 2),
231  final_transformation(2, 0), final_transformation(2, 1),
232  final_transformation(2, 2));
233 
234  tf::Quaternion tfqt;
235  tf3d.getRotation(tfqt);
236 
237  tf::Vector3 origin;
238  origin.setValue(final_transformation(0, 3), final_transformation(1, 3),
239  final_transformation(2, 3));
240 
241  transf.setOrigin(origin);
242  transf.setRotation(tfqt);
243 
244  static tf::TransformBroadcaster br;
245  tf_sensor1_sensor2 = tf::StampedTransform(transf.inverse(), ros::Time::now(),
246  sensor1_final_transformation_frame,
247  sensor2_final_transformation_frame);
248  if (publish_tf_) br.sendTransform(tf_sensor1_sensor2);
249 
250  tf::Transform inverse = tf_sensor1_sensor2.inverse();
251  double roll, pitch, yaw;
252  double xt = inverse.getOrigin().getX(), yt = inverse.getOrigin().getY(),
253  zt = inverse.getOrigin().getZ();
254  inverse.getBasis().getRPY(roll, pitch, yaw);
255 
256  if (save_to_file_) {
257  savefile << seek_iter << ", " << xt << ", " << yt << ", " << zt << ", "
258  << roll << ", " << pitch << ", " << yaw << ", " << used_sensor1
259  << ", " << used_sensor2 << ", " << total_sensor1 << ", "
260  << total_sensor2 << endl;
261  }
262 
263  cout << setprecision(4) << std::fixed;
264  cout << "Calibration finished succesfully." << endl;
265  cout << "Extrinsic parameters:" << endl;
266  cout << "x = " << xt << "\ty = " << yt << "\tz = " << zt << endl;
267  cout << "roll = " << roll << "\tpitch = " << pitch << "\tyaw = " << yaw << endl;
268 
269  sensor1Received = false;
270  sensor2Received = false;
271 }
272 
274  const velo2cam_calibration::ClusterCentroids::ConstPtr sensor1_centroids) {
275  sensor1_frame_id = sensor1_centroids->header.frame_id;
276  if (!S1_WARMUP_DONE) {
277  S1_WARMUP_COUNT++;
278  cout << "Clusters from " << sensor1_frame_id << ": " << S1_WARMUP_COUNT
279  << "/10" << '\r' << flush;
280  if (S1_WARMUP_COUNT >= 10) // TODO: Change to param?
281  {
282  cout << endl;
283  sensor1_sub.shutdown();
284  sensor2_sub.shutdown();
285 
286  cout << "Clusters from " << sensor1_frame_id
287  << " received. Is the warmup done? [Y/n]" << endl;
288  string answer;
289  getline(cin, answer);
290  if (answer == "y" || answer == "Y" || answer == "") {
292 
293  if (!S2_WARMUP_DONE) {
294  cout << "Filters for sensor 1 are adjusted now. Please, proceed with "
295  "the other sensor."
296  << endl;
297  } else { // Both sensors adjusted
298  cout << "Warmup phase completed. Starting calibration phase." << endl;
299  std_msgs::Empty myMsg;
300  sensor_switch_pub.publish(myMsg); //
301  }
302  } else { // Reset counter to allow further warmup
303  S1_WARMUP_COUNT = 0;
304  }
305 
306  sensor1_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
307  "cloud1", 100, sensor1_callback);
308  sensor2_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
309  "cloud2", 100, sensor2_callback);
310  }
311  return;
312  }
313 
314  if (!S2_WARMUP_DONE) {
315  return;
316  }
317 
318  if (DEBUG) ROS_INFO("sensor1 (%s) pattern ready!", sensor1_frame_id.c_str());
319 
320  if (sensor1_buffer.size() == TARGET_POSITIONS_COUNT) {
322  }
323 
324  if (is_sensor1_cam) {
325  std::ostringstream sstream;
326  sstream << "rotated_" << sensor1_frame_id;
327  sensor1_rotated_frame_id = sstream.str();
328 
329  pcl::PointCloud<pcl::PointXYZ>::Ptr xy_sensor1_cloud(
330  new pcl::PointCloud<pcl::PointXYZ>());
331 
332  fromROSMsg(sensor1_centroids->cloud, *xy_sensor1_cloud);
333 
334  tf::TransformListener listener;
335  tf::StampedTransform transform;
336  try {
337  listener.waitForTransform(sensor1_rotated_frame_id, sensor1_frame_id,
338  ros::Time(0), ros::Duration(20.0));
339  listener.lookupTransform(sensor1_rotated_frame_id, sensor1_frame_id,
340  ros::Time(0), transform);
341  } catch (tf::TransformException &ex) {
342  ROS_WARN("TF exception:\n%s", ex.what());
343  return;
344  }
345 
346  tf::Transform inverse = transform.inverse();
347  double roll, pitch, yaw;
348  inverse.getBasis().getRPY(roll, pitch, yaw);
349 
350  pcl_ros::transformPointCloud(*xy_sensor1_cloud, *sensor1_cloud, transform);
351  } else {
352  fromROSMsg(sensor1_centroids->cloud, *sensor1_cloud);
353  }
354 
355  sensor1Received = true;
356 
358  if (DEBUG) {
360 
361  sensor_msgs::PointCloud2 colour_cloud;
362  pcl::toROSMsg(*isensor1_cloud, colour_cloud);
363  colour_cloud.header.frame_id =
365  colour_sensor1_pub.publish(colour_cloud);
366  }
367 
369  std::tuple<int, int, pcl::PointCloud<pcl::PointXYZ>,
370  std::vector<pcl::PointXYZ>>(
371  sensor1_centroids->total_iterations,
372  sensor1_centroids->cluster_iterations, *sensor1_cloud,
373  sensor1_vector));
374  sensor1_count = sensor1_centroids->total_iterations;
375 
376  if (DEBUG) ROS_INFO("[V2C] sensor1");
377 
378  for (vector<pcl::PointXYZ>::iterator it = sensor1_vector.begin();
379  it < sensor1_vector.end(); ++it) {
380  if (DEBUG)
381  cout << "l" << it - sensor1_vector.begin() << "="
382  << "[" << (*it).x << " " << (*it).y << " " << (*it).z << "]" << endl;
383  }
384 
385  // sync_iterations is designed to extract a calibration result every single
386  // frame, so we cannot wait until TARGET_ITERATIONS
387  if (sync_iterations) {
388  if (sensor2_count >= sensor1_count) {
390  } else {
391  if (tf_sensor1_sensor2.frame_id_ != "" &&
392  tf_sensor1_sensor2.child_frame_id_ != "") {
393  static tf::TransformBroadcaster br;
394  tf_sensor1_sensor2.stamp_ = ros::Time::now();
395  if (publish_tf_) br.sendTransform(tf_sensor1_sensor2);
396  }
397  }
398  return;
399  }
400 
401  // Normal operation (sync_iterations=false)
403  cout << min(sensor1_count, sensor2_count) << "/30 iterations" << '\r' << flush;
404 
405  std_msgs::Int32 it;
406  it.data = min(sensor1_count, sensor2_count);
407  iterations_pub.publish(it);
408 
411  cout << endl;
412  sensor1_sub.shutdown();
413  sensor2_sub.shutdown();
414 
415  string answer;
416  if (single_pose_mode) {
417  answer = "n";
418  } else {
419  cout << "Target iterations reached. Do you need another target "
420  "location? [y/N]"
421  << endl;
422  getline(cin, answer);
423  }
424  if (answer == "n" || answer == "N" || answer == "") {
426  calibration_ended = true;
427  } else { // Move the target and start over
430  cout << "Please, move the target to its new position and adjust the "
431  "filters for each sensor before the calibration starts."
432  << endl;
433  // Start over if other position of the target is required
434  std_msgs::Empty myMsg;
435  sensor_switch_pub.publish(myMsg); // Set sensor nodes to warmup phase
436  S1_WARMUP_DONE = false;
437  S1_WARMUP_COUNT = 0;
438  S2_WARMUP_DONE = false;
439  S2_WARMUP_COUNT = 0;
440  sensor1Received = false;
441  sensor2Received = false;
442  sensor1_count = 0;
443  sensor2_count = 0;
444  }
445  sensor1_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
446  "cloud1", 100, sensor1_callback);
447  sensor2_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
448  "cloud2", 100, sensor2_callback);
449  return;
450  }
451  } else {
452  if (tf_sensor1_sensor2.frame_id_ != "" &&
453  tf_sensor1_sensor2.child_frame_id_ != "") {
454  static tf::TransformBroadcaster br;
455  tf_sensor1_sensor2.stamp_ = ros::Time::now();
456  if (publish_tf_) br.sendTransform(tf_sensor1_sensor2);
457  }
458  }
459 }
460 
462  velo2cam_calibration::ClusterCentroids::ConstPtr sensor2_centroids) {
463  sensor2_frame_id = sensor2_centroids->header.frame_id;
464  if (!S2_WARMUP_DONE && S1_WARMUP_DONE) {
465  S2_WARMUP_COUNT++;
466  cout << "Clusters from " << sensor2_frame_id << ": " << S2_WARMUP_COUNT
467  << "/10" << '\r' << flush;
468  if (S2_WARMUP_COUNT >= 10) // TODO: Change to param?
469  {
470  cout << endl;
471  sensor1_sub.shutdown();
472  sensor2_sub.shutdown();
473 
474  cout << "Clusters from " << sensor2_frame_id
475  << " received. Is the warmup done? (you can also reset this "
476  "position) [Y/n/r]"
477  << endl;
478  string answer;
479  getline(cin, answer);
480  if (answer == "y" || answer == "Y" || answer == "") {
482 
483  if (!S1_WARMUP_DONE) {
484  cout << "Filters for sensor 2 are adjusted now. Please, proceed with "
485  "the other sensor."
486  << endl;
487  } else { // Both sensors adjusted
488  cout << "Warmup phase completed. Starting calibration phase." << endl;
489  std_msgs::Empty myMsg;
490  sensor_switch_pub.publish(myMsg); //
491  }
492  } else if (answer == "r" ||
493  answer == "R") { // Reset this position and
494  // go back to Sensor 1 warmup
495  S1_WARMUP_DONE = false;
496  S1_WARMUP_COUNT = 0;
497  S2_WARMUP_DONE = false;
498  S2_WARMUP_COUNT = 0;
499  sensor1Received = false;
500  sensor2Received = false;
501  sensor1_count = 0;
502  sensor2_count = 0;
503  cout << "Please, adjust the filters for each sensor before the "
504  "calibration starts."
505  << endl;
506  } else { // Reset counter to allow further warmup
507  S2_WARMUP_COUNT = 0;
508  }
509  sensor1_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
510  "cloud1", 100, sensor1_callback);
511  sensor2_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
512  "cloud2", 100, sensor2_callback);
513  }
514  return;
515  } else if (!S2_WARMUP_DONE) {
516  return;
517  }
518  if (DEBUG) ROS_INFO("sensor2 (%s) pattern ready!", sensor2_frame_id.c_str());
519 
520  if (sensor2_buffer.size() == TARGET_POSITIONS_COUNT) {
522  }
523 
524  if (is_sensor2_cam) {
525  std::ostringstream sstream;
526  sstream << "rotated_" << sensor2_frame_id;
527  sensor2_rotated_frame_id = sstream.str();
528 
529  pcl::PointCloud<pcl::PointXYZ>::Ptr xy_sensor2_cloud(
530  new pcl::PointCloud<pcl::PointXYZ>());
531 
532  fromROSMsg(sensor2_centroids->cloud, *xy_sensor2_cloud);
533 
534  tf::TransformListener listener;
535  tf::StampedTransform transform;
536  try {
537  listener.waitForTransform(sensor2_rotated_frame_id, sensor2_frame_id,
538  ros::Time(0), ros::Duration(20.0));
539  listener.lookupTransform(sensor2_rotated_frame_id, sensor2_frame_id,
540  ros::Time(0), transform);
541  } catch (tf::TransformException &ex) {
542  ROS_WARN("TF exception:\n%s", ex.what());
543  return;
544  }
545 
546  tf::Transform inverse = transform.inverse();
547  double roll, pitch, yaw;
548  inverse.getBasis().getRPY(roll, pitch, yaw);
549 
550  pcl_ros::transformPointCloud(*xy_sensor2_cloud, *sensor2_cloud, transform);
551  } else {
552  fromROSMsg(sensor2_centroids->cloud, *sensor2_cloud);
553  }
554 
555  sensor2Received = true;
556 
558 
559  if (DEBUG) {
561 
562  sensor_msgs::PointCloud2 colour_cloud;
563  pcl::toROSMsg(*isensor2_cloud, colour_cloud);
564  colour_cloud.header.frame_id =
566  colour_sensor2_pub.publish(colour_cloud);
567  }
568 
570  std::tuple<int, int, pcl::PointCloud<pcl::PointXYZ>,
571  std::vector<pcl::PointXYZ>>(
572  sensor2_centroids->total_iterations,
573  sensor2_centroids->cluster_iterations, *sensor2_cloud,
574  sensor2_vector));
575  sensor2_count = sensor2_centroids->total_iterations;
576 
577  if (DEBUG) ROS_INFO("[V2C] sensor2");
578 
579  for (vector<pcl::PointXYZ>::iterator it = sensor2_vector.begin();
580  it < sensor2_vector.end(); ++it) {
581  if (DEBUG)
582  cout << "c" << it - sensor2_vector.begin() << "="
583  << "[" << (*it).x << " " << (*it).y << " " << (*it).z << "]" << endl;
584  }
585 
586  // sync_iterations is designed to extract a calibration result every single
587  // frame, so we cannot wait until TARGET_ITERATIONS
588  if (sync_iterations) {
589  if (sensor1_count >= sensor2_count) {
591  } else {
592  if (tf_sensor1_sensor2.frame_id_ != "" &&
593  tf_sensor1_sensor2.child_frame_id_ != "") {
594  static tf::TransformBroadcaster br;
595  tf_sensor1_sensor2.stamp_ = ros::Time::now();
596  if (publish_tf_) br.sendTransform(tf_sensor1_sensor2);
597  }
598  }
599  return;
600  }
601 
602  // Normal operation (sync_iterations=false)
604  cout << min(sensor1_count, sensor2_count) << "/30 iterations" << '\r' << flush;
605 
606  std_msgs::Int32 it;
607  it.data = min(sensor1_count, sensor2_count);
608  iterations_pub.publish(it);
609 
612  cout << endl;
613  sensor1_sub.shutdown();
614  sensor2_sub.shutdown();
615 
616  string answer;
617  if (single_pose_mode) {
618  answer = "n";
619  } else {
620  cout << "Target iterations reached. Do you need another target "
621  "location? [y/N]"
622  << endl;
623  getline(cin, answer);
624  }
625  if (answer == "n" || answer == "N" || answer == "") {
627  calibration_ended = true;
628  } else { // Move the target and start over
631  cout << "Please, move the target to its new position and adjust the "
632  "filters for each sensor before the calibration starts."
633  << endl;
634  // Start over if other position of the target is required
635  std_msgs::Empty myMsg;
636  sensor_switch_pub.publish(myMsg); // Set sensor nodes to warmup phase
637  S1_WARMUP_DONE = false;
638  S1_WARMUP_COUNT = 0;
639  S2_WARMUP_DONE = false;
640  S2_WARMUP_COUNT = 0;
641  sensor1Received = false;
642  sensor2Received = false;
643  sensor1_count = 0;
644  sensor2_count = 0;
645  }
646  sensor1_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
647  "cloud1", 100, sensor1_callback);
648  sensor2_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
649  "cloud2", 100, sensor2_callback);
650  return;
651  }
652  } else {
653  if (tf_sensor1_sensor2.frame_id_ != "" &&
654  tf_sensor1_sensor2.child_frame_id_ != "") {
655  static tf::TransformBroadcaster br;
656  tf_sensor1_sensor2.stamp_ = ros::Time::now();
657  if (publish_tf_) br.sendTransform(tf_sensor1_sensor2);
658  }
659  }
660 }
661 
662 int main(int argc, char **argv) {
663  ros::init(argc, argv, "velo2cam_calibration");
664  ros::NodeHandle nh; // GLOBAL
665  nh_ = new ros::NodeHandle("~"); // LOCAL
666 
667  string csv_name;
668 
669  nh_->param<bool>("sync_iterations", sync_iterations, false);
670  nh_->param<bool>("save_to_file", save_to_file_, false);
671  nh_->param<bool>("publish_tf", publish_tf_, true);
672  nh_->param<bool>("is_sensor2_cam", is_sensor2_cam, false);
673  nh_->param<bool>("is_sensor1_cam", is_sensor1_cam, false);
674  nh_->param<bool>("skip_warmup", skip_warmup, false);
675  nh_->param<bool>("single_pose_mode", single_pose_mode, false);
676  nh_->param<bool>("results_every_pose", results_every_pose, false);
677  nh_->param<string>("csv_name", csv_name,
678  "registration_" + currentDateTime() + ".csv");
679 
680  sensor1Received = false;
681  sensor1_cloud =
682  pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
684  pcl::PointCloud<pcl::PointXYZI>::Ptr(new pcl::PointCloud<pcl::PointXYZI>);
685  sensor2Received = false;
686  sensor2_cloud =
687  pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
689  pcl::PointCloud<pcl::PointXYZI>::Ptr(new pcl::PointCloud<pcl::PointXYZI>);
690 
691  sensor1_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
692  "cloud1", 100, sensor1_callback);
693  sensor2_sub = nh_->subscribe<velo2cam_calibration::ClusterCentroids>(
694  "cloud2", 100, sensor2_callback);
695 
696  if (DEBUG) {
697  clusters_sensor2_pub =
698  nh_->advertise<sensor_msgs::PointCloud2>("clusters_sensor2", 1);
699  clusters_sensor1_pub =
700  nh_->advertise<sensor_msgs::PointCloud2>("clusters_sensor1", 1);
701 
702  colour_sensor2_pub =
703  nh_->advertise<sensor_msgs::PointCloud2>("colour_sensor2", 1);
704  colour_sensor1_pub =
705  nh_->advertise<sensor_msgs::PointCloud2>("colour_sensor1", 1);
706  }
707 
708  sensor_switch_pub = nh.advertise<std_msgs::Empty>("warmup_switch", 1);
709  iterations_pub = nh_->advertise<std_msgs::Int32>("iterations", 1);
710 
711  calibration_ended = false;
712 
713  if (save_to_file_) {
714  ostringstream os;
715  os << getenv("HOME") << "/v2c_experiments/" << csv_name;
716  if (save_to_file_) {
717  if (DEBUG) ROS_INFO("Opening %s", os.str().c_str());
718  savefile.open(os.str().c_str());
719  savefile << "it, x, y, z, r, p, y, used_sen1, used_sen2, total_sen1, "
720  "total_sen2"
721  << endl;
722  }
723  }
724 
725  if (skip_warmup) {
726  S1_WARMUP_DONE = true;
727  S2_WARMUP_DONE = true;
728  ROS_WARN("Skipping warmup");
729  } else {
730  cout << "Please, adjust the filters for each sensor before the calibration "
731  "starts."
732  << endl;
733  }
734 
735  ros::Rate loop_rate(30);
736  while (ros::ok() && !calibration_ended) {
737  ros::spinOnce();
738  }
739 
740  sensor1_sub.shutdown();
741  sensor2_sub.shutdown();
742 
743  if (save_to_file_) savefile.close();
744 
745  // Save calibration params to launch file for testing
746 
747  // Get time
748  time_t rawtime;
749  struct tm *timeinfo;
750  char buffer[80];
751 
752  time(&rawtime);
753  timeinfo = localtime(&rawtime);
754 
755  strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
756  std::string str(buffer);
757 
758  // Get tf data
759  tf::Transform inverse = tf_sensor1_sensor2.inverse();
760  double roll, pitch, yaw;
761  double xt = inverse.getOrigin().getX(), yt = inverse.getOrigin().getY(),
762  zt = inverse.getOrigin().getZ();
763  inverse.getBasis().getRPY(roll, pitch, yaw);
764 
765  std::string path = ros::package::getPath("velo2cam_calibration");
766  string backuppath = path + "/launch/calibrated_tf_" + str + ".launch";
767  path = path + "/launch/calibrated_tf.launch";
768 
769  cout << endl
770  << "Creating .launch file with calibrated TF in: " << endl
771  << path.c_str() << endl;
772  // Create .launch file with calibrated TF
773  TiXmlDocument doc;
774  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
775  doc.LinkEndChild(decl);
776  TiXmlElement *root = new TiXmlElement("launch");
777  doc.LinkEndChild(root);
778 
779  TiXmlElement *arg = new TiXmlElement("arg");
780  arg->SetAttribute("name", "stdout");
781  arg->SetAttribute("default", "screen");
782  root->LinkEndChild(arg);
783 
784  string sensor2_final_transformation_frame = sensor2_frame_id;
785  if (is_sensor2_cam) {
786  sensor2_final_transformation_frame = sensor2_rotated_frame_id;
787  std::ostringstream sensor2_rot_stream_pub;
788  sensor2_rot_stream_pub << "0 0 0 -1.57079632679 0 -1.57079632679 "
789  << sensor2_rotated_frame_id << " "
790  << sensor2_frame_id << " 10";
791  string sensor2_rotation = sensor2_rot_stream_pub.str();
792 
793  TiXmlElement *sensor2_rotation_node = new TiXmlElement("node");
794  sensor2_rotation_node->SetAttribute("pkg", "tf");
795  sensor2_rotation_node->SetAttribute("type", "static_transform_publisher");
796  sensor2_rotation_node->SetAttribute("name", "sensor2_rot_tf");
797  sensor2_rotation_node->SetAttribute("args", sensor2_rotation);
798  root->LinkEndChild(sensor2_rotation_node);
799  }
800 
801  string sensor1_final_transformation_frame = sensor1_frame_id;
802  if (is_sensor1_cam) {
803  sensor1_final_transformation_frame = sensor1_rotated_frame_id;
804  std::ostringstream sensor1_rot_stream_pub;
805  sensor1_rot_stream_pub << "0 0 0 -1.57079632679 0 -1.57079632679 "
806  << sensor1_rotated_frame_id << " "
807  << sensor1_frame_id << " 10";
808  string sensor1_rotation = sensor1_rot_stream_pub.str();
809 
810  TiXmlElement *sensor1_rotation_node = new TiXmlElement("node");
811  sensor1_rotation_node->SetAttribute("pkg", "tf");
812  sensor1_rotation_node->SetAttribute("type", "static_transform_publisher");
813  sensor1_rotation_node->SetAttribute("name", "sensor1_rot_tf");
814  sensor1_rotation_node->SetAttribute("args", sensor1_rotation);
815  root->LinkEndChild(sensor1_rotation_node);
816  }
817 
818  std::ostringstream sstream;
819  sstream << xt << " " << yt << " " << zt << " " << yaw << " " << pitch << " "
820  << roll << " " << sensor2_final_transformation_frame << " "
821  << sensor1_final_transformation_frame << " 100";
822  string tf_args = sstream.str();
823 
824  TiXmlElement *node = new TiXmlElement("node");
825  node->SetAttribute("pkg", "tf");
826  node->SetAttribute("type", "static_transform_publisher");
827  node->SetAttribute("name", "velo2cam_tf");
828  node->SetAttribute("args", tf_args);
829  root->LinkEndChild(node);
830 
831  // Save XML file and copy
832  doc.SaveFile(path);
833  doc.SaveFile(backuppath);
834 
835  if (DEBUG) cout << "Calibration process finished." << endl;
836 
837  return 0;
838 }
int nFrames
long int sensor2_count
TFSIMD_FORCE_INLINE void setRotation(const Quaternion &q)
bool single_pose_mode
bool S1_WARMUP_DONE
int S2_WARMUP_COUNT
void publish(const boost::shared_ptr< M > &message) const
string sensor2_rotated_frame_id
int S1_WARMUP_COUNT
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
pcl::PointCloud< pcl::PointXYZ >::Ptr sensor2_cloud
TFSIMD_FORCE_INLINE Matrix3x3 & getBasis()
ros::Publisher colour_sensor2_pub
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
pcl::PointCloud< pcl::PointXYZI >::Ptr isensor2_cloud
std::ofstream savefile
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
string sensor2_frame_id
TFSIMD_FORCE_INLINE const tfScalar & getY() const
std::vector< std::vector< std::tuple< int, int, pcl::PointCloud< pcl::PointXYZ >, std::vector< pcl::PointXYZ > > > > sensor2_buffer
ros::NodeHandle * nh_
void setValue(const tfScalar &xx, const tfScalar &xy, const tfScalar &xz, const tfScalar &yx, const tfScalar &yy, const tfScalar &yz, const tfScalar &zx, const tfScalar &zy, const tfScalar &zz)
bool sync_iterations
TFSIMD_FORCE_INLINE const tfScalar & getZ() const
#define ROS_WARN(...)
bool publish_tf_
bool is_sensor2_cam
TFSIMD_FORCE_INLINE void setValue(const tfScalar &x, const tfScalar &y, const tfScalar &z)
void getRotation(Quaternion &q) const
std::vector< pcl::PointXYZ > sensor2_vector(4)
void calibrateExtrinsics(int seek_iter=-1)
ros::Publisher clusters_sensor2_pub
bool results_every_pose
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
std::vector< std::vector< std::tuple< int, int, pcl::PointCloud< pcl::PointXYZ >, std::vector< pcl::PointXYZ > > > > sensor1_buffer
bool save_to_file_
void sensor1_callback(const velo2cam_calibration::ClusterCentroids::ConstPtr sensor1_centroids)
#define DEBUG
ros::Publisher colour_sensor1_pub
std::string child_frame_id_
Eigen::Matrix< double, 12, 12 > Matrix12d
ros::Publisher clusters_sensor1_pub
ros::Publisher sensor_switch_pub
void getRPY(tfScalar &roll, tfScalar &pitch, tfScalar &yaw, unsigned int solution_number=1) const
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
TFSIMD_FORCE_INLINE Quaternion inverse(const Quaternion &q)
pcl::PointCloud< pcl::PointXYZ >::Ptr sensor1_cloud
std::vector< pcl::PointXYZ > sensor1_vector(4)
ROSCPP_DECL bool ok()
long int sensor1_count
void sendTransform(const StampedTransform &transform)
Transform inverse() const
double min(double a, double b)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void colourCenters(const std::vector< pcl::PointXYZ > pc, pcl::PointCloud< pcl::PointXYZI >::Ptr coloured)
tf::StampedTransform tf_sensor1_sensor2
void sensor2_callback(velo2cam_calibration::ClusterCentroids::ConstPtr sensor2_centroids)
bool S2_WARMUP_DONE
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
ROSLIB_DECL std::string getPath(const std::string &package_name)
string sensor1_frame_id
ros::Publisher iterations_pub
void sortPatternCenters(pcl::PointCloud< pcl::PointXYZ >::Ptr pc, vector< pcl::PointXYZ > &v)
bool skip_warmup
ros::Subscriber sensor1_sub
bool calibration_ended
void transformPointCloud(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const tf::Transform &transform)
const std::string currentDateTime()
ros::Subscriber sensor2_sub
int TARGET_ITERATIONS
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
TFSIMD_FORCE_INLINE const tfScalar & getX() const
static Time now()
bool sensor2Received
pcl::PointCloud< pcl::PointXYZI >::Ptr isensor1_cloud
int main(int argc, char **argv)
ROSCPP_DECL void spinOnce()
bool is_sensor1_cam
bool sensor1Received
TFSIMD_FORCE_INLINE void setOrigin(const Vector3 &origin)
tf::Transform transf
std::string frame_id_
string sensor1_rotated_frame_id
int TARGET_POSITIONS_COUNT
Eigen::Matrix< double, 12, 1 > Vector12d


velo2cam_calibration
Author(s): Jorge Beltran , Carlos Guindel
autogenerated on Fri Feb 26 2021 03:40:57