lidar_pattern.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  lidar_pattern: Find the circle centers in the lidar cloud
24 */
25 
26 #define PCL_NO_PRECOMPILE
27 
28 #include <dynamic_reconfigure/server.h>
29 #include <pcl/common/eigen.h>
30 #include <pcl/common/transforms.h>
31 #include <pcl/filters/extract_indices.h>
32 #include <pcl/filters/passthrough.h>
33 #include <pcl/point_cloud.h>
34 #include <pcl/point_types.h>
35 #include <pcl/segmentation/sac_segmentation.h>
37 #include <ros/ros.h>
38 #include <sensor_msgs/PointCloud2.h>
39 #include <std_msgs/Empty.h>
40 #include <velo2cam_calibration/ClusterCentroids.h>
41 #include <velo2cam_calibration/LidarConfig.h>
42 #include <velo2cam_utils.h>
43 
44 using namespace std;
45 using namespace sensor_msgs;
46 
49 pcl::PointCloud<pcl::PointXYZ>::Ptr cumulative_cloud;
50 
51 // Dynamic parameters
52 double threshold_;
57 Eigen::Vector3f axis_;
70 std::ofstream savefile;
71 
72 bool WARMUP_DONE = false;
73 
74 void callback(const PointCloud2::ConstPtr &laser_cloud) {
75  if (DEBUG) ROS_INFO("[LiDAR] Processing cloud...");
76 
77  pcl::PointCloud<Velodyne::Point>::Ptr velocloud(
78  new pcl::PointCloud<Velodyne::Point>),
79  velo_filtered(new pcl::PointCloud<Velodyne::Point>),
80  pattern_cloud(new pcl::PointCloud<Velodyne::Point>),
81  edges_cloud(new pcl::PointCloud<Velodyne::Point>);
82 
83  clouds_proc_++;
84 
85  // This cloud is already xyz-filtered
86  fromROSMsg(*laser_cloud, *velocloud);
87 
88  Velodyne::addRange(*velocloud);
89 
90  // Range passthrough filter
91  pcl::PassThrough<Velodyne::Point> pass2;
92  pass2.setInputCloud(velocloud);
93  pass2.setFilterFieldName("range");
94  pass2.setFilterLimits(passthrough_radius_min_, passthrough_radius_max_);
95  pass2.filter(*velo_filtered);
96 
97  // Publishing "range_filtered_velo" cloud (segmented plane)
98  sensor_msgs::PointCloud2 range_ros;
99  pcl::toROSMsg(*velo_filtered, range_ros);
100  range_ros.header = laser_cloud->header;
101  range_pub.publish(range_ros);
102 
103  // Plane segmentation
104  pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
105  pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
106 
107  pcl::SACSegmentation<Velodyne::Point> plane_segmentation;
108  plane_segmentation.setModelType(pcl::SACMODEL_PARALLEL_PLANE);
109  plane_segmentation.setDistanceThreshold(plane_threshold_);
110  plane_segmentation.setMethodType(pcl::SAC_RANSAC);
111  plane_segmentation.setAxis(Eigen::Vector3f(axis_[0], axis_[1], axis_[2]));
112  plane_segmentation.setEpsAngle(angle_threshold_);
113  plane_segmentation.setOptimizeCoefficients(true);
114  plane_segmentation.setMaxIterations(1000);
115  plane_segmentation.setInputCloud(velo_filtered);
116  plane_segmentation.segment(*inliers, *coefficients);
117 
118  if (inliers->indices.size() == 0) {
119  // Exit 1: plane not found
120  ROS_WARN(
121  "[LiDAR] Could not estimate a planar model for the given dataset.");
122  return;
123  }
124 
125  // Copy coefficients to proper object for further filtering
126  Eigen::VectorXf coefficients_v(4);
127  coefficients_v(0) = coefficients->values[0];
128  coefficients_v(1) = coefficients->values[1];
129  coefficients_v(2) = coefficients->values[2];
130  coefficients_v(3) = coefficients->values[3];
131 
132  // Get edges points by range
133  vector<vector<Velodyne::Point *>> rings =
134  Velodyne::getRings(*velocloud, rings_count_);
135  for (vector<vector<Velodyne::Point *>>::iterator ring = rings.begin();
136  ring < rings.end(); ++ring) {
137  Velodyne::Point *prev, *succ;
138  if (ring->empty()) continue;
139 
140  (*ring->begin())->intensity = 0;
141  (*(ring->end() - 1))->intensity = 0;
142  for (vector<Velodyne::Point *>::iterator pt = ring->begin() + 1;
143  pt < ring->end() - 1; pt++) {
144  Velodyne::Point *prev = *(pt - 1);
145  Velodyne::Point *succ = *(pt + 1);
146  (*pt)->intensity =
147  max(max(prev->range - (*pt)->range, succ->range - (*pt)->range), 0.f);
148  }
149  }
150 
151  float THRESHOLD =
152  gradient_threshold_; // 10 cm between the pattern and the background
153  for (pcl::PointCloud<Velodyne::Point>::iterator pt =
154  velocloud->points.begin();
155  pt < velocloud->points.end(); ++pt) {
156  if (pt->intensity > THRESHOLD) {
157  edges_cloud->push_back(*pt);
158  }
159  }
160 
161  if (edges_cloud->points.size() == 0) {
162  // Exit 2: pattern edges not found
163  ROS_WARN("[LiDAR] Could not detect pattern edges.");
164  return;
165  }
166 
167  // Get points belonging to plane in pattern pointcloud
168  pcl::SampleConsensusModelPlane<Velodyne::Point>::Ptr dit(
169  new pcl::SampleConsensusModelPlane<Velodyne::Point>(edges_cloud));
170  std::vector<int> inliers2;
171  dit->selectWithinDistance(coefficients_v, plane_distance_inliers_, inliers2);
172  pcl::copyPointCloud<Velodyne::Point>(*edges_cloud, inliers2, *pattern_cloud);
173 
174  // Velodyne specific info no longer needed for calibration
175  // so standard PointXYZ is used from now on
176  pcl::PointCloud<pcl::PointXYZ>::Ptr circles_cloud(
177  new pcl::PointCloud<pcl::PointXYZ>),
178  xy_cloud(new pcl::PointCloud<pcl::PointXYZ>),
179  aux_cloud(new pcl::PointCloud<pcl::PointXYZ>),
180  auxrotated_cloud(new pcl::PointCloud<pcl::PointXYZ>),
181  cloud_f(new pcl::PointCloud<pcl::PointXYZ>),
182  centroid_candidates(new pcl::PointCloud<pcl::PointXYZ>);
183 
184  vector<vector<Velodyne::Point *>> rings2 =
185  Velodyne::getRings(*pattern_cloud, rings_count_);
186 
187  // Conversion from Velodyne::Point to pcl::PointXYZ
188  for (vector<vector<Velodyne::Point *>>::iterator ring = rings2.begin();
189  ring < rings2.end(); ++ring) {
190  for (vector<Velodyne::Point *>::iterator pt = ring->begin();
191  pt < ring->end(); ++pt) {
192  pcl::PointXYZ point;
193  point.x = (*pt)->x;
194  point.y = (*pt)->y;
195  point.z = (*pt)->z;
196  circles_cloud->push_back(point);
197  }
198  }
199 
200  // Publishing "pattern_circles" cloud (points belonging to the detected plane)
201  if (DEBUG) {
202  sensor_msgs::PointCloud2 velocloud_ros2;
203  pcl::toROSMsg(*circles_cloud, velocloud_ros2);
204  velocloud_ros2.header = laser_cloud->header;
205  pattern_pub.publish(velocloud_ros2);
206  }
207 
208  // Rotate cloud to face pattern plane
209  Eigen::Vector3f xy_plane_normal_vector, floor_plane_normal_vector;
210  xy_plane_normal_vector[0] = 0.0;
211  xy_plane_normal_vector[1] = 0.0;
212  xy_plane_normal_vector[2] = -1.0;
213 
214  floor_plane_normal_vector[0] = coefficients->values[0];
215  floor_plane_normal_vector[1] = coefficients->values[1];
216  floor_plane_normal_vector[2] = coefficients->values[2];
217 
218  Eigen::Affine3f rotation =
219  getRotationMatrix(floor_plane_normal_vector, xy_plane_normal_vector);
220  pcl::transformPointCloud(*circles_cloud, *xy_cloud, rotation);
221 
222  // Publishing "rotated_pattern" cloud (plane transformed to be aligned with
223  // XY)
224  if (DEBUG) {
225  sensor_msgs::PointCloud2 ros_rotated_pattern;
226  pcl::toROSMsg(*xy_cloud, ros_rotated_pattern);
227  ros_rotated_pattern.header = laser_cloud->header;
228  rotated_pattern_pub.publish(ros_rotated_pattern);
229  }
230 
231  // Force pattern points to belong to computed plane
232  pcl::PointXYZ aux_point;
233  aux_point.x = 0;
234  aux_point.y = 0;
235  aux_point.z = (-coefficients_v(3) / coefficients_v(2));
236  aux_cloud->push_back(aux_point);
237  pcl::transformPointCloud(*aux_cloud, *auxrotated_cloud, rotation);
238  double zcoord_xyplane = auxrotated_cloud->at(0).z;
239 
240  for (pcl::PointCloud<pcl::PointXYZ>::iterator pt = xy_cloud->points.begin();
241  pt < xy_cloud->points.end(); ++pt) {
242  pt->z = zcoord_xyplane;
243  }
244 
245  // RANSAC circle detection
246  pcl::ModelCoefficients::Ptr coefficients3(new pcl::ModelCoefficients);
247  pcl::PointIndices::Ptr inliers3(new pcl::PointIndices);
248 
249  pcl::SACSegmentation<pcl::PointXYZ> circle_segmentation;
250  circle_segmentation.setModelType(pcl::SACMODEL_CIRCLE2D);
251  circle_segmentation.setDistanceThreshold(circle_threshold_);
252  circle_segmentation.setMethodType(pcl::SAC_RANSAC);
253  circle_segmentation.setOptimizeCoefficients(true);
254  circle_segmentation.setMaxIterations(1000);
255  circle_segmentation.setRadiusLimits(
258 
259  pcl::ExtractIndices<pcl::PointXYZ> extract;
260  if (DEBUG)
261  ROS_INFO("[LiDAR] Searching for points in cloud of size %lu",
262  xy_cloud->points.size());
263  while (xy_cloud->points.size() > 3) {
264  circle_segmentation.setInputCloud(xy_cloud);
265  circle_segmentation.segment(*inliers3, *coefficients3);
266  if (inliers3->indices.size() == 0) {
267  if (DEBUG)
268  ROS_INFO(
269  "[LiDAR] Optimized circle segmentation failed, trying unoptimized "
270  "version");
271  circle_segmentation.setOptimizeCoefficients(false);
272 
273  circle_segmentation.setInputCloud(xy_cloud);
274  circle_segmentation.segment(*inliers3, *coefficients3);
275 
276  // Reset for next iteration
277  circle_segmentation.setOptimizeCoefficients(true);
278  if (inliers3->indices.size() == 0) {
279  break;
280  }
281  }
282 
283  // Add center point to cloud
284  pcl::PointXYZ center;
285  center.x = *coefficients3->values.begin();
286  center.y = *(coefficients3->values.begin() + 1);
287  center.z = zcoord_xyplane;
288 
289  centroid_candidates->push_back(center);
290 
291  // Remove inliers from pattern cloud to find next circle
292  extract.setInputCloud(xy_cloud);
293  extract.setIndices(inliers3);
294  extract.setNegative(true);
295  extract.filter(*cloud_f);
296  xy_cloud.swap(cloud_f);
297 
298  if (DEBUG)
299  ROS_INFO("[LiDAR] Remaining points in cloud %lu",
300  xy_cloud->points.size());
301  }
302 
303  if (centroid_candidates->size() < TARGET_NUM_CIRCLES) {
304  // Exit 3: all centers not found
305  ROS_WARN("[LiDAR] Not enough centers: %ld", centroid_candidates->size());
306  return;
307  }
308 
319  std::vector<std::vector<int>> groups;
320  comb(centroid_candidates->size(), TARGET_NUM_CIRCLES, groups);
321  double groups_scores[groups.size()]; // -1: invalid; 0-1 normalized score
322  for (int i = 0; i < groups.size(); ++i) {
323  std::vector<pcl::PointXYZ> candidates;
324  // Build candidates set
325  for (int j = 0; j < groups[i].size(); ++j) {
326  pcl::PointXYZ center;
327  center.x = centroid_candidates->at(groups[i][j]).x;
328  center.y = centroid_candidates->at(groups[i][j]).y;
329  center.z = centroid_candidates->at(groups[i][j]).z;
330  candidates.push_back(center);
331  }
332 
333  // Compute candidates score
334  Square square_candidate(candidates, delta_width_circles_,
336  groups_scores[i] = square_candidate.is_valid()
337  ? 1.0
338  : -1; // -1 when it's not valid, 1 otherwise
339  }
340 
341  int best_candidate_idx = -1;
342  double best_candidate_score = -1;
343  for (int i = 0; i < groups.size(); ++i) {
344  if (best_candidate_score == 1 && groups_scores[i] == 1) {
345  // Exit 4: Several candidates fit target's geometry
346  ROS_ERROR(
347  "[LiDAR] More than one set of candidates fit target's geometry. "
348  "Please, make sure your parameters are well set. Exiting callback");
349  return;
350  }
351  if (groups_scores[i] > best_candidate_score) {
352  best_candidate_score = groups_scores[i];
353  best_candidate_idx = i;
354  }
355  }
356 
357  if (best_candidate_idx == -1) {
358  // Exit 5: No candidates fit target's geometry
359  ROS_WARN(
360  "[LiDAR] Unable to find a candidate set that matches target's "
361  "geometry");
362  return;
363  }
364 
365  // Build selected centers set
366  pcl::PointCloud<pcl::PointXYZ>::Ptr rotated_back_cloud(
367  new pcl::PointCloud<pcl::PointXYZ>());
368  for (int j = 0; j < groups[best_candidate_idx].size(); ++j) {
369  pcl::PointXYZ center_rotated_back = pcl::transformPoint(
370  centroid_candidates->at(groups[best_candidate_idx][j]),
371  rotation.inverse());
372  center_rotated_back.x = (-coefficients->values[1] * center_rotated_back.y -
373  coefficients->values[2] * center_rotated_back.z -
374  coefficients->values[3]) /
375  coefficients->values[0];
376 
377  rotated_back_cloud->push_back(center_rotated_back);
378  cumulative_cloud->push_back(center_rotated_back);
379  }
380 
381  if (save_to_file_) {
382  std::vector<pcl::PointXYZ> sorted_centers;
383  sortPatternCenters(rotated_back_cloud, sorted_centers);
384  for (std::vector<pcl::PointXYZ>::iterator it = sorted_centers.begin();
385  it < sorted_centers.end(); ++it) {
386  savefile << it->x << ", " << it->y << ", " << it->z << ", ";
387  }
388  }
389 
390  // Publishing "cumulative_cloud" cloud (centers found from the beginning)
391  if (DEBUG) {
392  sensor_msgs::PointCloud2 ros_pointcloud;
393  pcl::toROSMsg(*cumulative_cloud, ros_pointcloud);
394  ros_pointcloud.header = laser_cloud->header;
395  cumulative_pub.publish(ros_pointcloud);
396  }
397 
398  xy_cloud.reset(); // Free memory
399  cloud_f.reset(); // Free memory
400 
401  ++clouds_used_;
402 
403  // Publishing "plane_model"
404  pcl_msgs::ModelCoefficients m_coeff;
405  pcl_conversions::moveFromPCL(*coefficients, m_coeff);
406  m_coeff.header = laser_cloud->header;
407  coeff_pub.publish(m_coeff);
408 
409  if (DEBUG)
410  ROS_INFO("[LiDAR] %d/%d frames: %ld pts in cloud", clouds_used_,
411  clouds_proc_, cumulative_cloud->points.size());
412 
413  // Create cloud for publishing centers
414  pcl::PointCloud<pcl::PointXYZ>::Ptr centers_cloud(
415  new pcl::PointCloud<pcl::PointXYZ>);
416 
417  // Compute circles centers
418  if (!WARMUP_DONE) { // Compute clusters from detections in the latest frame
420  1);
421  } else { // Use cumulative information from previous frames
423  min_cluster_factor_ * clouds_used_, clouds_used_);
424  if (centers_cloud->points.size() > TARGET_NUM_CIRCLES) {
426  3.0 * clouds_used_ / 4.0, clouds_used_);
427  }
428  }
429 
430  // Exit 6: clustering failed
431  if (centers_cloud->points.size() == TARGET_NUM_CIRCLES) {
432  sensor_msgs::PointCloud2 ros2_pointcloud;
433  pcl::toROSMsg(*centers_cloud, ros2_pointcloud);
434  ros2_pointcloud.header = laser_cloud->header;
435 
436  velo2cam_calibration::ClusterCentroids to_send;
437  to_send.header = laser_cloud->header;
438  to_send.cluster_iterations = clouds_used_;
439  to_send.total_iterations = clouds_proc_;
440  to_send.cloud = ros2_pointcloud;
441 
442  centers_pub.publish(to_send);
443  if (DEBUG) ROS_INFO("[LiDAR] Pattern centers published");
444 
445  if (save_to_file_) {
446  std::vector<pcl::PointXYZ> sorted_centers;
447  sortPatternCenters(centers_cloud, sorted_centers);
448  for (std::vector<pcl::PointXYZ>::iterator it = sorted_centers.begin();
449  it < sorted_centers.end(); ++it) {
450  savefile << it->x << ", " << it->y << ", " << it->z << ", ";
451  }
452  savefile << cumulative_cloud->width;
453  }
454  }
455 
456  if (save_to_file_) {
457  savefile << endl;
458  }
459 
460  // Clear cumulative cloud during warm-up phase
461  if (!WARMUP_DONE) {
462  cumulative_cloud->clear();
463  clouds_proc_ = 0;
464  clouds_used_ = 0;
465  }
466 }
467 
468 void param_callback(velo2cam_calibration::LidarConfig &config, uint32_t level) {
469  passthrough_radius_min_ = config.passthrough_radius_min;
470  ROS_INFO("[LiDAR] New passthrough_radius_min_ threshold: %f",
472  passthrough_radius_max_ = config.passthrough_radius_max;
473  ROS_INFO("[LiDAR] New passthrough_radius_max_ threshold: %f",
475  circle_radius_ = config.circle_radius;
476  ROS_INFO("[LiDAR] New pattern circle radius: %f", circle_radius_);
477  axis_[0] = config.x;
478  axis_[1] = config.y;
479  axis_[2] = config.z;
480  ROS_INFO("[LiDAR] New normal axis for plane segmentation: %f, %f, %f",
481  axis_[0], axis_[1], axis_[2]);
482  angle_threshold_ = config.angle_threshold;
483  ROS_INFO("[LiDAR] New angle threshold: %f", angle_threshold_);
484  centroid_distance_min_ = config.centroid_distance_min;
485  ROS_INFO("[LiDAR] New minimum distance between centroids: %f",
487  centroid_distance_max_ = config.centroid_distance_max;
488  ROS_INFO("[LiDAR] New maximum distance between centroids: %f",
490 }
491 
492 void warmup_callback(const std_msgs::Empty::ConstPtr &msg) {
494  if (WARMUP_DONE) {
495  ROS_INFO("[LiDAR] Warm up done, pattern detection started");
496  } else {
497  ROS_INFO("[LiDAR] Detection stopped. Warm up mode activated");
498  }
499 }
500 
501 int main(int argc, char **argv) {
502  ros::init(argc, argv, "lidar_pattern");
503  ros::NodeHandle nh; // GLOBAL
504  ros::NodeHandle nh_("~"); // LOCAL
505  ros::Subscriber sub = nh_.subscribe("cloud1", 1, callback);
506  pcl::console::setVerbosityLevel(pcl::console::L_ALWAYS);
507 
508  range_pub = nh_.advertise<PointCloud2>("range_filtered_cloud", 1);
509  if (DEBUG) {
510  pattern_pub = nh_.advertise<PointCloud2>("pattern_circles", 1);
511  rotated_pattern_pub = nh_.advertise<PointCloud2>("rotated_pattern", 1);
512  cumulative_pub = nh_.advertise<PointCloud2>("cumulative_cloud", 1);
513  }
514  centers_pub =
515  nh_.advertise<velo2cam_calibration::ClusterCentroids>("centers_cloud", 1);
516  coeff_pub = nh_.advertise<pcl_msgs::ModelCoefficients>("plane_model", 1);
517 
518  string csv_name;
519 
520  nh.param("delta_width_circles", delta_width_circles_, 0.5);
521  nh.param("delta_height_circles", delta_height_circles_, 0.4);
522  nh_.param("plane_threshold", plane_threshold_, 0.1);
523  nh_.param("gradient_threshold", gradient_threshold_, 0.1);
524  nh_.param("plane_distance_inliers", plane_distance_inliers_, 0.1);
525  nh_.param("circle_threshold", circle_threshold_, 0.05);
526  nh_.param("target_radius_tolerance", target_radius_tolerance_, 0.01);
527  nh_.param("cluster_tolerance", cluster_tolerance_, 0.05);
528  nh_.param("min_centers_found", min_centers_found_, TARGET_NUM_CIRCLES);
529  nh_.param("min_cluster_factor", min_cluster_factor_, 0.5);
530  nh_.param("rings_count", rings_count_, 64);
531  nh_.param("skip_warmup", skip_warmup_, false);
532  nh_.param("save_to_file", save_to_file_, false);
533  nh_.param("csv_name", csv_name,
534  "lidar_pattern_" + currentDateTime() + ".csv");
535 
537  pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
538 
539  dynamic_reconfigure::Server<velo2cam_calibration::LidarConfig> server;
540  dynamic_reconfigure::Server<velo2cam_calibration::LidarConfig>::CallbackType
541  f;
542  f = boost::bind(param_callback, _1, _2);
543  server.setCallback(f);
544 
545  ros::Subscriber warmup_sub =
546  nh.subscribe("warmup_switch", 1, warmup_callback);
547 
548  if (skip_warmup_) {
549  ROS_WARN("Skipping warmup");
550  WARMUP_DONE = true;
551  }
552 
553  // Just for statistics
554  if (save_to_file_) {
555  ostringstream os;
556  os << getenv("HOME") << "/v2c_experiments/" << csv_name;
557  if (save_to_file_) {
558  if (DEBUG) ROS_INFO("Opening %s", os.str().c_str());
559  savefile.open(os.str().c_str());
560  savefile << "det1_x, det1_y, det1_z, det2_x, det2_y, det2_z, det3_x, "
561  "det3_y, det3_z, det4_x, det4_y, det4_z, cent1_x, cent1_y, "
562  "cent1_z, cent2_x, cent2_y, cent2_z, cent3_x, cent3_y, "
563  "cent3_z, cent4_x, cent4_y, cent4_z, it"
564  << endl;
565  }
566  }
567 
568  ros::spin();
569  return 0;
570 }
int rings_count_
pcl::PointCloud< pcl::PointXYZ >::Ptr cumulative_cloud
bool WARMUP_DONE
ros::Publisher centers_pub
ros::Publisher coeff_pub
double passthrough_radius_min_
void publish(const boost::shared_ptr< M > &message) const
int min_centers_found_
f
float intensity
laser intensity reading
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
bool is_valid()
#define TARGET_NUM_CIRCLES
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
ros::Publisher range_pub
double angle_threshold_
double passthrough_radius_max_
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
ros::NodeHandle * nh_
#define ROS_WARN(...)
std::ofstream savefile
ros::Publisher pattern_pub
double threshold_
ROSCPP_DECL void spin(Spinner &spinner)
double cluster_tolerance_
#define DEBUG
void param_callback(velo2cam_calibration::LidarConfig &config, uint32_t level)
double delta_width_circles_
bool save_to_file_
double gradient_threshold_
#define ROS_INFO(...)
vector< vector< Point * > > getRings(pcl::PointCloud< Velodyne::Point > &pc, int rings_count)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
double plane_threshold_
void warmup_callback(const std_msgs::Empty::ConstPtr &msg)
double circle_threshold_
Eigen::Vector3f axis_
double min_cluster_factor_
double plane_distance_inliers_
void addRange(pcl::PointCloud< Velodyne::Point > &pc)
int clouds_proc_
ros::Publisher cumulative_pub
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
double centroid_distance_min_
double delta_height_circles_
bool skip_warmup_
Eigen::Affine3f getRotationMatrix(Eigen::Vector3f source, Eigen::Vector3f target)
void getCenterClusters(pcl::PointCloud< pcl::PointXYZ >::Ptr cloud_in, pcl::PointCloud< pcl::PointXYZ >::Ptr centers_cloud, double cluster_tolerance=0.10, int min_cluster_size=15, int max_cluster_size=200, bool verbosity=true)
int main(int argc, char **argv)
void sortPatternCenters(pcl::PointCloud< pcl::PointXYZ >::Ptr pc, vector< pcl::PointXYZ > &v)
ros::Publisher rotated_pattern_pub
double centroid_distance_max_
const std::string currentDateTime()
double target_radius_tolerance_
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
int clouds_used_
void callback(const PointCloud2::ConstPtr &laser_cloud)
void moveFromPCL(pcl::PCLImage &pcl_image, sensor_msgs::Image &image)
double max(double a, double b)
#define ROS_ERROR(...)
double circle_radius_
void comb(int N, int K, std::vector< std::vector< int >> &groups)


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