laser_scan_matcher.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, Ivan Dryanovski, William Morris
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the CCNY Robotics Lab nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /* This package uses Canonical Scan Matcher [1], written by
31  * Andrea Censi
32  *
33  * [1] A. Censi, "An ICP variant using a point-to-line metric"
34  * Proceedings of the IEEE International Conference
35  * on Robotics and Automation (ICRA), 2008
36  */
37 
38 /* This package contains modifications that are copyright by Nokia
39  *
40  * Licensed under the BSD 3-Clause "New" or "Revised" License
41  * SPDX-License-Identifier: BSD-3-Clause
42  */
43 
45 #include <boost/assign.hpp>
46 
47 namespace scan_tools
48 {
49 
51  nh_(nh),
52  nh_private_(nh_private),
53  initialized_(false),
54  received_odom_(false),
55  have_map_(false),
56  initialpose_valid_(false),
57  theta_odom_(0.0),
58  skipped_poses_(0)
59 {
60  ROS_INFO("Starting LaserScanMatcher");
61 
62  // **** init parameters
63 
64  initParams();
65  if (debug_csm_)
66  sm_debug_write(1);
67 
68  // **** state variables
69 
70  // pre-allocate all matrices for speed
71  Sigma_odom_ = gsl_matrix_calloc(3, 3);
72  Sigma_odom_trans_ = gsl_matrix_alloc(3, 3);
73  Sigma_measured_ = gsl_matrix_calloc(3, 3);
74  B_odom_ = gsl_matrix_calloc(3, 5);
75  gsl_matrix_set(B_odom_, 2, 4, 1.0);
76  Sigma_u_ = gsl_matrix_calloc(5, 5);
77  trans_sigma_ = gsl_matrix_calloc(3, 3);
78  gsl_matrix_set(trans_sigma_, 2, 2, 1);
79  kalman_gain_ = gsl_matrix_alloc(3, 3);
80  kalman_gain_comp_ = gsl_matrix_alloc(3, 3);
81  // interim results
82  I1_ = gsl_matrix_alloc(5, 3);
83  I2_ = gsl_matrix_alloc(3, 3);
84  P2_ = gsl_permutation_alloc (3);
85  // pose vectors for kalman filter
86  xvec_ = gsl_vector_alloc(3);
87  yvec_ = gsl_vector_alloc(3);
88 
89  resetState();
95 
96  // **** publishers
98  {
99  constructed_scan_publisher_ = nh_.advertise<sensor_msgs::LaserScan>(
100  "lsm_localization/constructed_scan", 5);
101  }
102 
103  if (publish_pose_)
104  {
105  pose_publisher_ = nh_.advertise<geometry_msgs::Pose2D>(
106  "lsm_localization/pose2D", 5);
107  }
108 
110  {
111  pose_stamped_publisher_ = nh_.advertise<geometry_msgs::PoseStamped>(
112  "lsm_localization/pose_stamped", 5);
113  }
114 
116  {
117  pose_with_covariance_publisher_ = nh_.advertise<geometry_msgs::PoseWithCovariance>(
118  "lsm_localization/pose_with_covariance", 5);
119  }
120 
122  {
123  pose_with_covariance_stamped_publisher_ = nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>(
124  "lsm_localization/pose", 5);
125  }
126 
128  {
129  predicted_pose_publisher_ = nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("lsm_localization/predicted_pose", 5);
130  }
131 
133  {
134  measured_pose_publisher_ = nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("lsm_localization/measured_pose", 5);
135  }
136 
137  if (publish_debug_)
138  {
139  debug_odom_delta_publisher_ = nh_.advertise<geometry_msgs::PoseStamped>(
140  "lsm_localization/debug/odom_delta", 5);
141  debug_laser_delta_publisher_ = nh_.advertise<geometry_msgs::PoseStamped>(
142  "lsm_localization/debug/laser_delta", 5);
143  debug_odom_reference_publisher_ = nh_.advertise<geometry_msgs::PoseStamped>(
144  "lsm_localization/debug/odom_reference", 5);
145  debug_odom_current_publisher_ = nh_.advertise<geometry_msgs::PoseStamped>(
146  "lsm_localization/debug/odom_current", 5);
147  }
148 
149  // *** subscribers
150 
152  "/odom", 1, &LaserScanMatcher::odomCallback, this);
153 
156 
157  if (use_map_)
158  {
159  ROS_INFO("using map-based matching");
161  "map", 1, &LaserScanMatcher::mapCallback, this);
162  }
163  else
164  ROS_INFO("using frame-to-frame matching");
165 
167  "scan", 1, &LaserScanMatcher::scanCallback, this);
168 
169 }
170 
172 {
173  ROS_INFO("Destroying LaserScanMatcher");
174  gsl_matrix_free(Sigma_odom_);
175  gsl_matrix_free(Sigma_odom_trans_);
176  gsl_matrix_free(Sigma_measured_);
177  gsl_matrix_free(B_odom_);
178  gsl_matrix_free(I1_);
179  gsl_matrix_free(I2_);
180  gsl_permutation_free(P2_);
181  gsl_matrix_free(trans_sigma_);
182  gsl_matrix_free(kalman_gain_);
183  gsl_matrix_free(kalman_gain_comp_);
184  gsl_vector_free(xvec_);
185  gsl_vector_free(yvec_);
186 }
187 
189 {
190  ROS_INFO("LaserScanMatcher state cleared");
191 
192  // **** state variables
193 
194  constructed_intensities_.clear();
195  constructed_ranges_.clear();
196  initialpose_valid_ = false;
199 
200  f2b_.setIdentity();
202  input_.laser[0] = 0.0;
203  input_.laser[1] = 0.0;
204  input_.laser[2] = 0.0;
205 
206  // Initialize output_ vectors as Null for error-checking
207  output_.cov_x_m = 0;
208  output_.dx_dy1_m = 0;
209  output_.dx_dy2_m = 0;
210 }
211 
213 {
214  if (!nh_private_.getParam ("base_frame", base_frame_))
215  base_frame_ = "base_link";
216  if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
217  fixed_frame_ = "world";
218  if (!nh_private_.getParam ("initialpose_topic_name", initialpose_topic_))
219  initialpose_topic_ = "initialpose";
220  if (!nh_private_.getParam("scan_downsample_rate", scan_downsample_rate_))
222  if (scan_downsample_rate_ <= 0)
224 
225  // **** keyframe params: when to generate the keyframe scan
226  // if either is set to 0, reduces to frame-to-frame matching
227 
228  if (!nh_private_.getParam ("kf_dist_linear", kf_dist_linear_))
229  kf_dist_linear_ = 0.10;
230  if (!nh_private_.getParam ("kf_dist_angular", kf_dist_angular_))
231  kf_dist_angular_ = 10.0 * (M_PI / 180.0);
232 
234 
235  if (!nh_private_.getParam ("no_odom_fusing", no_odom_fusing_))
236  no_odom_fusing_ = false;
237 
238  // **** Parameters that control map-to-scan matching
239  // use_map must be true for map-to-scan matching to be used
240  // other parameters control how the initial-pose scan is constructed
241  if (!nh_private_.getParam ("use_map", use_map_))
242  use_map_ = true;
243  if (!nh_private_.getParam ("map_occupancy_threshold", map_occupancy_threshold_))
245  // min and max range for constructing the scan that initial pose
246  // would see
247  if (!nh_private_.getParam("max_allowed_range", max_allowed_range_))
248  max_allowed_range_ = -1;
249  if (!nh_private_.getParam ("max_variance_trans", max_variance_trans_))
250  max_variance_trans_ = 1e-5;
251  if (!nh_private_.getParam ("max_variance_rot", max_variance_rot_))
252  max_variance_rot_ = 1e-5;
253  if (!nh_private_.getParam ("max_pose_delta_yaw", max_pose_delta_yaw_))
254  max_pose_delta_yaw_ = 0.785; // 45 degrees
255 
256  // **** How to publish the output?
257  // tf (fixed_frame->base_frame),
258  // pose message (pose of base frame in the fixed frame)
259  if (!nh_private_.getParam ("publish_base_tf", publish_base_tf_))
260  publish_base_tf_ = false;
261  if (!nh_private_.getParam ("publish_odom_tf", publish_odom_tf_))
262  publish_odom_tf_ = true;
263  if (!nh_private_.getParam ("publish_pose", publish_pose_))
264  publish_pose_ = true;
265  if (!nh_private_.getParam ("publish_constructed_scan", publish_constructed_scan_))
267  if (!nh_private_.getParam ("publish_pose_stamped", publish_pose_stamped_))
268  publish_pose_stamped_ = false;
269  if (!nh_private_.getParam ("publish_pose_with_covariance", publish_pose_with_covariance_))
271  if (!nh_private_.getParam ("publish_pose_with_covariance_stamped", publish_pose_with_covariance_stamped_))
273  if (!nh_private_.getParam ("publish_predicted_pose", publish_predicted_pose_))
274  publish_predicted_pose_ = false;
275  if (!nh_private_.getParam ("publish_measured_pose", publish_measured_pose_))
276  publish_measured_pose_ = false;
277  if (!nh_private_.getParam ("publish_debug", publish_debug_))
278  publish_debug_ = false;
279  if (!nh_private_.getParam ("debug_csm", debug_csm_))
280  debug_csm_ = false;
281 
282  if (!nh_private_.getParam("position_covariance", position_covariance_))
283  {
284  position_covariance_.resize(3);
285  std::fill(position_covariance_.begin(), position_covariance_.end(), 1e-9);
286  }
287 
288  if (!nh_private_.getParam("orientation_covariance", orientation_covariance_))
289  {
290  orientation_covariance_.resize(3);
291  std::fill(orientation_covariance_.begin(), orientation_covariance_.end(), 1e-9);
292  }
293  // **** CSM parameters - comments copied from algos.h (by Andrea Censi)
294 
295  // Maximum angular displacement between scans
296  if (!nh_private_.getParam ("max_angular_correction_deg", input_.max_angular_correction_deg))
298 
299  // Maximum translation between scans (m)
300  if (!nh_private_.getParam ("max_linear_correction", input_.max_linear_correction))
302 
303  // Maximum ICP cycle iterations
304  if (!nh_private_.getParam ("max_iterations", input_.max_iterations))
305  input_.max_iterations = 10;
306 
307  // A threshold for stopping (m)
308  if (!nh_private_.getParam ("epsilon_xy", input_.epsilon_xy))
309  input_.epsilon_xy = 0.000001;
310 
311  // A threshold for stopping (rad)
312  if (!nh_private_.getParam ("epsilon_theta", input_.epsilon_theta))
313  input_.epsilon_theta = 0.000001;
314 
315  // Maximum distance for a correspondence to be valid
316  if (!nh_private_.getParam ("max_correspondence_dist", input_.max_correspondence_dist))
318 
319  // Noise in the scan (m)
320  if (!nh_private_.getParam ("sigma", input_.sigma))
321  input_.sigma = 0.010;
322 
323  // Use smart tricks for finding correspondences.
324  if (!nh_private_.getParam ("use_corr_tricks", input_.use_corr_tricks))
326 
327  // Restart: Restart if error is over threshold
328  if (!nh_private_.getParam ("restart", input_.restart))
329  input_.restart = 0;
330 
331  // Restart: Threshold for restarting
332  if (!nh_private_.getParam ("restart_threshold_mean_error", input_.restart_threshold_mean_error))
334 
335  // Restart: displacement for restarting. (m)
336  if (!nh_private_.getParam ("restart_dt", input_.restart_dt))
337  input_.restart_dt = 1.0;
338 
339  // Restart: displacement for restarting. (rad)
340  if (!nh_private_.getParam ("restart_dtheta", input_.restart_dtheta))
341  input_.restart_dtheta = 0.1;
342 
343  // Max distance for staying in the same clustering
344  if (!nh_private_.getParam ("clustering_threshold", input_.clustering_threshold))
346 
347  // Number of neighbour rays used to estimate the orientation
348  if (!nh_private_.getParam ("orientation_neighbourhood", input_.orientation_neighbourhood))
350 
351  // If 0, it's vanilla ICP
352  if (!nh_private_.getParam ("use_point_to_line_distance", input_.use_point_to_line_distance))
354 
355  // Discard correspondences based on the angles
356  if (!nh_private_.getParam ("do_alpha_test", input_.do_alpha_test))
357  input_.do_alpha_test = 0;
358 
359  // Discard correspondences based on the angles - threshold angle, in degrees
360  if (!nh_private_.getParam ("do_alpha_test_thresholdDeg", input_.do_alpha_test_thresholdDeg))
362 
363  // Percentage of correspondences to consider: if 0.9,
364  // always discard the top 10% of correspondences with more error
365  if (!nh_private_.getParam ("outliers_maxPerc", input_.outliers_maxPerc))
366  input_.outliers_maxPerc = 0.90;
367 
368  // Parameters describing a simple adaptive algorithm for discarding.
369  // 1) Order the errors.
370  // 2) Choose the percentile according to outliers_adaptive_order.
371  // (if it is 0.7, get the 70% percentile)
372  // 3) Define an adaptive threshold multiplying outliers_adaptive_mult
373  // with the value of the error at the chosen percentile.
374  // 4) Discard correspondences over the threshold.
375  // This is useful to be conservative; yet remove the biggest errors.
376  if (!nh_private_.getParam ("outliers_adaptive_order", input_.outliers_adaptive_order))
378 
379  if (!nh_private_.getParam ("outliers_adaptive_mult", input_.outliers_adaptive_mult))
381 
382  // If you already have a guess of the solution, you can compute the polar angle
383  // of the points of one scan in the new position. If the polar angle is not a monotone
384  // function of the readings index, it means that the surface is not visible in the
385  // next position. If it is not visible, then we don't use it for matching.
386  if (!nh_private_.getParam ("do_visibility_test", input_.do_visibility_test))
388 
389  // no two points in laser_sens can have the same corr.
390  if (!nh_private_.getParam ("outliers_remove_doubles", input_.outliers_remove_doubles))
392 
393  // Compute the covariance of ICP using the method http://purl.org/censi/2006/icpcov
395 
396  // Checks that find_correspondences_tricks gives the right answer
397  if (!nh_private_.getParam ("debug_verify_tricks", input_.debug_verify_tricks))
399 
400  // If 1, the field 'true_alpha' (or 'alpha') in the first scan is used to compute the
401  // incidence beta, and the factor (1/cos^2(beta)) used to weight the correspondence.");
402  if (!nh_private_.getParam ("use_ml_weights", input_.use_ml_weights))
404 
405  // If 1, the field 'readings_sigma' in the second scan is used to weight the
406  // correspondence by 1/sigma^2
407  if (!nh_private_.getParam ("use_sigma_weights", input_.use_sigma_weights))
409 }
410 
411 nav_msgs::Odometry* LaserScanMatcher::latestOdomBefore(const ros::Time& time)
412 {
413  for (auto o=odom_history_.begin(); o < odom_history_.end(); o++) {
414  if (o->header.stamp <= time)
415  return &(*o);
416  }
417  return NULL;
418 }
419 
420 nav_msgs::Odometry* LaserScanMatcher::earliestOdomAfter(const ros::Time& time)
421 {
422  for (auto o=odom_history_.begin(); o < odom_history_.end(); o++) {
423  if (o->header.stamp == time) {
424  // found exact match, that's it!
425  return &(*o);
426  } else if (o->header.stamp < time) {
427  if (o==odom_history_.begin()) {
428  // beginning of queue is already before (i.e. nothing after)
429  return NULL;
430  } else {
431  // found latest before, so earliest after is one before that
432  return &(*(o-1));
433  }
434  }
435  }
436  return &(*odom_history_.end());
437 }
438 
439 void LaserScanMatcher::addOdomToHistory(const nav_msgs::Odometry::ConstPtr& o)
440 {
441  if (odom_history_.size() >= MAX_ODOM_HISTORY)
442  odom_history_.pop_back();
443  odom_history_.push_front(*o);
444 }
445 
447 {
448  nav_msgs::Odometry* latest_before = latestOdomBefore(time);
449  if (!latest_before) {
450  ROS_WARN("missing latest odom before t=%.3f (is odom topic alive)",
451  time.toSec());
452  return -1.0;
453  }
454  nav_msgs::Odometry o = *latest_before;
455  o.header.stamp = time;
456  tf::Quaternion q;
457  // extrapolate because we don't have the second point
458  double delta_t = (time - latest_before->header.stamp).toSec();
459  if (delta_t > MAX_ODOM_AGE)
460  return -1.0;
461  o.pose.pose.position.x = latest_before->pose.pose.position.x +
462  delta_t * latest_before->twist.twist.linear.x;
463  o.pose.pose.position.y = latest_before->pose.pose.position.y +
464  delta_t * latest_before->twist.twist.linear.y;
465  o.pose.pose.position.z = latest_before->pose.pose.position.z +
466  delta_t * latest_before->twist.twist.linear.z;
467  tf::Quaternion delta_q;
468  delta_q.setRPY(delta_t * latest_before->twist.twist.angular.x,
469  delta_t * latest_before->twist.twist.angular.y,
470  delta_t * latest_before->twist.twist.angular.z);
471  tf::Quaternion qb(latest_before->pose.pose.orientation.x,
472  latest_before->pose.pose.orientation.y,
473  latest_before->pose.pose.orientation.z,
474  latest_before->pose.pose.orientation.w);
475  q = delta_q * qb;
476  o.pose.pose.orientation.x = q.x();
477  o.pose.pose.orientation.y = q.y();
478  o.pose.pose.orientation.z = q.z();
479  o.pose.pose.orientation.w = q.w();
480  current_odom_msg_ = o;
481  return delta_t;
482 }
483 
484 double LaserScanMatcher::getOdomDeltaT(const nav_msgs::Odometry::ConstPtr& o)
485 {
486  if (odom_history_.empty())
487  return -1.0;
488  return (o->header.stamp - odom_history_.front().header.stamp).toSec();
489 }
490 
492 {
493  tf::Transform current_odom_tf;
494  tf::Transform reference_odom_tf;
495  createTfFromXYTheta(current_odom_msg_.pose.pose.position.x,
496  current_odom_msg_.pose.pose.position.y,
497  tf::getYaw(current_odom_msg_.pose.pose.orientation),
498  current_odom_tf);
499  createTfFromXYTheta(reference_odom_msg_.pose.pose.position.x,
500  reference_odom_msg_.pose.pose.position.y,
501  tf::getYaw(reference_odom_msg_.pose.pose.orientation),
502  reference_odom_tf);
503  tf::Transform delta_odom_tf = reference_odom_tf.inverse() * current_odom_tf;
504 
505  // apply calculated delta to the reference pose
508 
509  // construct input covariance (see the paper)
510  // matrix is pre-cleared, so we don't have to touch what's always zero
511  gsl_matrix_set(Sigma_u_, 0, 0, current_odom_msg_.twist.covariance[0]);
512  gsl_matrix_set(Sigma_u_, 0, 1, current_odom_msg_.twist.covariance[1]);
513  gsl_matrix_set(Sigma_u_, 1, 0, current_odom_msg_.twist.covariance[6]);
514  gsl_matrix_set(Sigma_u_, 1, 1, current_odom_msg_.twist.covariance[7]);
515  gsl_matrix_set(Sigma_u_, 2, 2, gsl_matrix_get(Sigma_odom_, 2, 2));
516  gsl_matrix_set(Sigma_u_, 2, 3,
517  gsl_matrix_get(Sigma_odom_, 2, 2) *
518  current_odom_msg_.twist.twist.linear.x *
519  current_odom_msg_.twist.twist.linear.y);
520  gsl_matrix_set(Sigma_u_, 3, 2,
521  gsl_matrix_get(Sigma_odom_, 2, 2) *
522  current_odom_msg_.twist.twist.linear.x *
523  current_odom_msg_.twist.twist.linear.y);
524  gsl_matrix_set(Sigma_u_, 3, 3, gsl_matrix_get(Sigma_odom_, 2, 2));
525  gsl_matrix_set(Sigma_u_, 4, 4, current_odom_msg_.twist.covariance[35]);
526 
527  // construct B-matrix
528  theta_odom_ += delta_t * current_odom_msg_.twist.twist.angular.z;
529  double cos0 = cos(theta_odom_);
530  double sin0 = sin(theta_odom_);
531  double cos90 = cos(theta_odom_ + M_PI/2.0);
532  double sin90 = sin(theta_odom_ + M_PI/2.0);
533  gsl_matrix_set(B_odom_, 0, 0, cos0);
534  gsl_matrix_set(B_odom_, 0, 1, -sin0);
535  gsl_matrix_set(B_odom_, 1, 0, sin0);
536  gsl_matrix_set(B_odom_, 1, 1, cos0);
537  gsl_matrix_set(B_odom_, 0, 2, cos90);
538  gsl_matrix_set(B_odom_, 0, 3, -sin90);
539  gsl_matrix_set(B_odom_, 1, 2, sin90);
540  gsl_matrix_set(B_odom_, 1, 3, cos90);
541  // I1 = delta_t * Sigma_u * B_odom^T + 0 * I1
542  gsl_blas_dgemm(CblasNoTrans, CblasTrans, delta_t, Sigma_u_, B_odom_, 0.0, I1_);
543  // Sigma_odom = delta_t * B_odom * I1 + 1 * Sigma_odom (use beta = 1.0 to accumulate)
544  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, delta_t, B_odom_, I1_, 1.0, Sigma_odom_);
545 
546  if (publish_debug_) {
547  doPublishDebugTF(current_odom_msg_.header.stamp, delta_odom_tf, debug_odom_delta_publisher_, "");
548  doPublishDebugTF(current_odom_msg_.header.stamp, reference_odom_tf, debug_odom_reference_publisher_, "odom");
549  doPublishDebugTF(current_odom_msg_.header.stamp, current_odom_tf, debug_odom_current_publisher_, "odom");
550  }
551 }
552 
553 void LaserScanMatcher::odomCallback(const nav_msgs::Odometry::ConstPtr& odom_msg)
554 {
555  boost::mutex::scoped_lock(mutex_);
556  double delta_t = getOdomDeltaT(odom_msg);
557  addOdomToHistory(odom_msg);
558  if (!received_odom_)
559  {
560  if (!getBaseToFootprintTf(odom_msg->child_frame_id)) {
561  ROS_WARN("skipping odom");
562  return;
563  }
564  reference_odom_msg_ = *odom_msg;
565  current_odom_msg_ = *odom_msg;
566  gsl_matrix_set_zero(Sigma_odom_);
567  theta_odom_ = 0.0;
568  received_odom_ = true;
569  odom_frame_ = odom_msg->header.frame_id;
570  return;
571  }
572 
573  // if we got here, we have what we need to do the integration
574  assert (delta_t >= 0.0);
575  current_odom_msg_ = *odom_msg;
576  doPredictPose(delta_t);
577  doPublishOdomRate(odom_msg->header.stamp);
578 }
579 
581 {
582  gsl_matrix_set(trans_sigma_, 0, 0, cos(yaw));
583  gsl_matrix_set(trans_sigma_, 0, 1, -sin(yaw));
584  gsl_matrix_set(trans_sigma_, 1, 0, sin(yaw));
585  gsl_matrix_set(trans_sigma_, 1, 1, cos(yaw));
586 }
587 
589 {
591  params.range_max = observed_range_max_;
592  params.range_min = observed_range_min_;
593  params.angle_max = observed_angle_max_;
594  params.angle_min = observed_angle_min_;
595  params.angle_inc = observed_angle_inc_;
596 
597  params.max_allowed_range = max_allowed_range_;
598 
600  tf::Transform predicted_laser_pose_in_pcl = predicted_pose_in_pcl_ * base_to_laser_;
601  double laser_x = predicted_laser_pose_in_pcl.getOrigin().getX();
602  double laser_y = predicted_laser_pose_in_pcl.getOrigin().getY();
603  double laser_yaw = tf::getYaw(predicted_laser_pose_in_pcl.getRotation());
604  ROS_DEBUG("%s: laser_x=%f, laser_y=%f laser_yaw (deg)=%f", __func__,
605  laser_x, laser_y, 180.0 * laser_yaw / M_PI);
606  ROS_DEBUG("%s: range=[%f,%f], angle=[%f,%f]@%f", __func__,
607  params.range_min, params.range_max,
608  params.angle_min, params.angle_max, params.angle_inc);
609 
611  scan_constructor_.constructScan(laser_x, laser_y, laser_yaw, params);
612 
613  constructed_intensities_.clear();
615 
616  for (const auto r : constructed_ranges_)
617  constructed_intensities_.push_back(r > 0.0? 100.0:0.0);
618 
620  sensor_msgs::LaserScan::Ptr scan_msg;
621  scan_msg = boost::make_shared<sensor_msgs::LaserScan>();
622  scan_msg->range_min = params.range_min;
623  scan_msg->range_max = params.range_max;
624  scan_msg->angle_min = params.angle_min;
625  scan_msg->angle_max = params.angle_max;
626  scan_msg->angle_increment = params.angle_inc;
627  scan_msg->scan_time = observed_scan_time_;
628  scan_msg->time_increment = observed_time_inc_;
629  scan_msg->header.stamp = time;
630  scan_msg->header.frame_id = observed_scan_frame_;
631  scan_msg->ranges.assign(
633  scan_msg->intensities.assign(
635 
637  }
638 }
639 
640 void LaserScanMatcher::initialposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg)
641 {
642  boost::mutex::scoped_lock(mutex_);
643  tf::Transform pose;
644 
645  if (!have_map_) {
646  ROS_WARN_THROTTLE(30, "map not loaded, cannot process initial pose");
647  return;
648  }
649  if (!received_odom_) {
650  ROS_WARN_THROTTLE(30, "odom never received, cannot process initial pose");
651  return;
652  }
653  if (syncOdom(pose_msg->header.stamp) < 0.0) {
654  ROS_WARN_THROTTLE(30, "cannot sync odometry for initial pose timestamp");
655  return;
656  }
658  gsl_matrix_set_zero(Sigma_odom_);
659  theta_odom_ = 0.0;
660 
661  // convert the input pose (typically in 'map' frame into lsm_fixed frame)
662  pose.setOrigin(tf::Vector3(pose_msg->pose.pose.position.x,
663  pose_msg->pose.pose.position.y,
664  pose_msg->pose.pose.position.z));
665  pose.setRotation(tf::Quaternion(pose_msg->pose.pose.orientation.x,
666  pose_msg->pose.pose.orientation.y,
667  pose_msg->pose.pose.orientation.z,
668  pose_msg->pose.pose.orientation.w));
669  if (pose_msg->header.frame_id != fixed_frame_)
670  ROS_WARN("incorrect frame for initial pose: '%s' vs. '%s'",
671  pose_msg->header.frame_id.c_str(), fixed_frame_.c_str());
672 
673  // new initial pose came in, set the predicted pose to be equal
674  // to it and set the reference odom to be the current odom
675  initial_pose_ = pose;
677  initialpose_valid_ = true;
678  // reflect the incoming initial pose if set up to publish
679  // on compatible topic
682  }
683 }
684 
685 void LaserScanMatcher::mapCallback (const nav_msgs::OccupancyGrid::ConstPtr& map_msg)
686 {
687  boost::mutex::scoped_lock(mutex_);
688 
690 
692  map_params.map_res = map_msg->info.resolution;
693  map_params.map_width = map_msg->info.width;
694  map_params.map_height = map_msg->info.height;
695  f2pcl_.setOrigin(tf::Vector3(map_msg->info.origin.position.x,
696  map_msg->info.origin.position.y,
697  map_msg->info.origin.position.z));
698  f2pcl_.setRotation(tf::Quaternion(map_msg->info.origin.orientation.x,
699  map_msg->info.origin.orientation.y,
700  map_msg->info.origin.orientation.z,
701  map_msg->info.origin.orientation.w));
702  pcl2f_ = f2pcl_.inverse();
703 
704  ScanConstructor::grid_t map_grid;
705  map_grid.resize(map_params.map_height);
706 
707  int i = 0;
708  for (auto row=map_grid.begin(); row < map_grid.end(); row++) {
709  row->resize(map_params.map_width);
710  for (auto element=row->begin(); element < row->end(); element++)
711  *element = map_msg->data[i++];
712  }
713  ROS_INFO("got map: %dx%d@%f",
714  map_params.map_width, map_params.map_height, map_params.map_res);
715 
716  scan_constructor_ = ScanConstructor(std::move(map_grid), map_params);
717 
718  have_map_ = true;
719 }
720 
721 void LaserScanMatcher::scanCallback (const sensor_msgs::LaserScan::ConstPtr& scan_msg)
722 {
723  boost::mutex::scoped_lock(mutex_);
724 
725  // **** if first scan, cache the tf from base to the scanner
726  if (!initialized_)
727  {
728  ROS_INFO("first scan observed, initializing");
729 
730  // cache the static tf from base to laser
731  if (!getBaseToLaserTf(scan_msg->header.frame_id))
732  {
733  ROS_WARN("Skipping scan");
734  return;
735  }
736  input_.min_reading = scan_msg->range_min;
737  input_.max_reading = scan_msg->range_max;
738 
739  laserScanToLDP(scan_msg, prev_ldp_scan_);
740  last_icp_time_ = scan_msg->header.stamp;
741  observed_range_min_ = scan_msg->range_min;
742  observed_range_max_ = scan_msg->range_max;
743  observed_angle_min_ = scan_msg->angle_min;
744  observed_angle_max_ = scan_msg->angle_max;
745  observed_angle_inc_ = scan_msg->angle_increment;
746  observed_scan_time_ = scan_msg->scan_time;
747  observed_time_inc_ = scan_msg->time_increment;
748  observed_scan_frame_ = scan_msg->header.frame_id;
749  initialized_ = true;
750  }
751 
752  if (scan_msg->header.seq % scan_downsample_rate_ != 0)
753  return;
755  LDP curr_ldp_scan;
756  int r = 0;
757  double delta_t;
758  if ((delta_t = syncOdom(scan_msg->header.stamp)) < 0.0) {
759  ROS_WARN_THROTTLE(10, "odometry sync failed, skipping scan");
760  return;
761  }
762  // run prediction once again here, so that we end up using
763  // synced up odometry
764  doPredictPose(delta_t);
765  if (use_map_) {
766  if (initialpose_valid_) {
767  // if the reference frame comes from the map, replace it
768  LDP ref_pose_ldp_scan;
769  constructScan(scan_msg->header.stamp);
770  constructedScanToLDP(ref_pose_ldp_scan);
771  laserScanToLDP(scan_msg, curr_ldp_scan);
772  r = processScan(curr_ldp_scan, ref_pose_ldp_scan, scan_msg->header.stamp);
773  if (r) {
774  // if localization was successful, use the estimated pose
775  // as the reference for next time
776 
779  initialpose_valid_ = true;
781  gsl_matrix_set_zero(Sigma_odom_);
782  theta_odom_ = 0.0;
783  }
784  } else
786  "initial pose not received yet, scan processing skipped"
787  );
788  } else {
789  laserScanToLDP(scan_msg, curr_ldp_scan);
790  r = processScan(curr_ldp_scan, scan_msg->header.stamp);
791  }
792  if (r) {
793  double dur = (ros::WallTime::now() - start).toSec() * 1e3;
794  ROS_DEBUG("complete scan processing total duration: %.1f ms", dur);
795  }
796 }
797 
798 void LaserScanMatcher::doPublishDebugTF(const ros::Time& time, const tf::Transform& transform, const ros::Publisher &publisher, const std::string& frame)
799 {
800  geometry_msgs::PoseStamped::Ptr pose_stamped_msg;
801  pose_stamped_msg = boost::make_shared<geometry_msgs::PoseStamped>();
802  pose_stamped_msg->header.stamp = time;
803  pose_stamped_msg->header.frame_id = frame;
804  tf::poseTFToMsg(transform, pose_stamped_msg->pose);
805  publisher.publish(pose_stamped_msg);
806 }
807 
809 {
811  geometry_msgs::PoseWithCovarianceStamped::Ptr pose_with_covariance_stamped_msg;
812  pose_with_covariance_stamped_msg = boost::make_shared<geometry_msgs::PoseWithCovarianceStamped>();
813 
814  pose_with_covariance_stamped_msg->header.stamp = time;
815  pose_with_covariance_stamped_msg->header.frame_id = fixed_frame_;
816 
817  tf::poseTFToMsg(predicted_pose_, pose_with_covariance_stamped_msg->pose.pose);
818 
819  pose_with_covariance_stamped_msg->pose.covariance = boost::assign::list_of
820  (gsl_matrix_get(Sigma_odom_trans_, 0, 0)) (gsl_matrix_get(Sigma_odom_trans_, 0, 1)) (0) (0) (0) (0)
821  (gsl_matrix_get(Sigma_odom_trans_, 1, 0)) (gsl_matrix_get(Sigma_odom_trans_, 1, 1)) (0) (0) (0) (0)
822  (0) (0) (static_cast<double>(position_covariance_[2])) (0) (0) (0)
823  (0) (0) (0) (static_cast<double>(orientation_covariance_[0])) (0) (0)
824  (0) (0) (0) (0) (static_cast<double>(orientation_covariance_[1])) (0)
825  (0) (0) (0) (0) (0) (gsl_matrix_get(Sigma_odom_trans_, 2, 2));
826  predicted_pose_publisher_.publish(pose_with_covariance_stamped_msg);
827  }
828 }
829 
831 {
832  if (publish_pose_) {
833  // unstamped Pose2D message
834  geometry_msgs::Pose2D::Ptr pose_msg;
835  pose_msg = boost::make_shared<geometry_msgs::Pose2D>();
836  pose_msg->x = f2b_.getOrigin().getX();
837  pose_msg->y = f2b_.getOrigin().getY();
838  pose_msg->theta = tf::getYaw(f2b_.getRotation());
839  pose_publisher_.publish(pose_msg);
840  }
841  if (publish_pose_stamped_) {
842  // stamped Pose message
843  geometry_msgs::PoseStamped::Ptr pose_stamped_msg;
844  pose_stamped_msg = boost::make_shared<geometry_msgs::PoseStamped>();
845 
846  pose_stamped_msg->header.stamp = time;
847  pose_stamped_msg->header.frame_id = fixed_frame_;
848 
849  tf::poseTFToMsg(f2b_, pose_stamped_msg->pose);
850 
851  pose_stamped_publisher_.publish(pose_stamped_msg);
852  }
854  // unstamped PoseWithCovariance message
855  geometry_msgs::PoseWithCovariance::Ptr pose_with_covariance_msg;
856  pose_with_covariance_msg = boost::make_shared<geometry_msgs::PoseWithCovariance>();
857  tf::poseTFToMsg(f2b_, pose_with_covariance_msg->pose);
858 
859  pose_with_covariance_msg->covariance = boost::assign::list_of
860  (gsl_matrix_get(output_.cov_x_m, 0, 0)) (gsl_matrix_get(output_.cov_x_m, 0, 1)) (0) (0) (0) (0)
861  (gsl_matrix_get(output_.cov_x_m, 1, 0)) (gsl_matrix_get(output_.cov_x_m, 1, 1)) (0) (0) (0) (0)
862  (0) (0) (static_cast<double>(position_covariance_[2])) (0) (0) (0)
863  (0) (0) (0) (static_cast<double>(orientation_covariance_[0])) (0) (0)
864  (0) (0) (0) (0) (static_cast<double>(orientation_covariance_[1])) (0)
865  (0) (0) (0) (0) (0) (gsl_matrix_get(output_.cov_x_m, 2, 2));
866  pose_with_covariance_publisher_.publish(pose_with_covariance_msg);
867  }
869  // stamped Pose message
870  geometry_msgs::PoseWithCovarianceStamped::Ptr pose_with_covariance_stamped_msg;
871  pose_with_covariance_stamped_msg = boost::make_shared<geometry_msgs::PoseWithCovarianceStamped>();
872 
873  pose_with_covariance_stamped_msg->header.stamp = time;
874  pose_with_covariance_stamped_msg->header.frame_id = fixed_frame_;
875 
876  tf::poseTFToMsg(f2b_, pose_with_covariance_stamped_msg->pose.pose);
877 
878  pose_with_covariance_stamped_msg->pose.covariance = boost::assign::list_of
879  (gsl_matrix_get(output_.cov_x_m, 0, 0)) (gsl_matrix_get(output_.cov_x_m, 0, 1)) (0) (0) (0) (0)
880  (gsl_matrix_get(output_.cov_x_m, 1, 0)) (gsl_matrix_get(output_.cov_x_m, 1, 1)) (0) (0) (0) (0)
881  (0) (0) (static_cast<double>(position_covariance_[2])) (0) (0) (0)
882  (0) (0) (0) (static_cast<double>(orientation_covariance_[0])) (0) (0)
883  (0) (0) (0) (0) (static_cast<double>(orientation_covariance_[1])) (0)
884  (0) (0) (0) (0) (0) (gsl_matrix_get(output_.cov_x_m, 2, 2));
885  pose_with_covariance_stamped_publisher_.publish(pose_with_covariance_stamped_msg);
886  }
887 
889  geometry_msgs::PoseWithCovarianceStamped::Ptr pose_with_covariance_stamped_msg;
890  pose_with_covariance_stamped_msg = boost::make_shared<geometry_msgs::PoseWithCovarianceStamped>();
891 
892  pose_with_covariance_stamped_msg->header.stamp = time;
893  pose_with_covariance_stamped_msg->header.frame_id = fixed_frame_;
894 
895  tf::poseTFToMsg(measured_pose_, pose_with_covariance_stamped_msg->pose.pose);
896 
897  pose_with_covariance_stamped_msg->pose.covariance = boost::assign::list_of
898  (gsl_matrix_get(Sigma_measured_, 0, 0)) (gsl_matrix_get(Sigma_measured_, 0, 1)) (0) (0) (0) (0)
899  (gsl_matrix_get(Sigma_measured_, 1, 0)) (gsl_matrix_get(Sigma_measured_, 1, 1)) (0) (0) (0) (0)
900  (0) (0) (static_cast<double>(position_covariance_[2])) (0) (0) (0)
901  (0) (0) (0) (static_cast<double>(orientation_covariance_[0])) (0) (0)
902  (0) (0) (0) (0) (static_cast<double>(orientation_covariance_[1])) (0)
903  (0) (0) (0) (0) (0) (gsl_matrix_get(Sigma_measured_, 2, 2));
904  measured_pose_publisher_.publish(pose_with_covariance_stamped_msg);
905  }
906 
907  if (publish_base_tf_) {
908  tf::StampedTransform transform_msg (f2b_, time, fixed_frame_, base_frame_);
909  tf_broadcaster_.sendTransform (transform_msg);
910  }
911 
912  if (publish_odom_tf_) {
913  tf::Transform current_odom_tf;
914  tf::Transform m2o;
915  createTfFromXYTheta(current_odom_msg_.pose.pose.position.x,
916  current_odom_msg_.pose.pose.position.y,
917  tf::getYaw(current_odom_msg_.pose.pose.orientation),
918  current_odom_tf);
919  m2o = f2b_ * (current_odom_tf * footprint_to_base_).inverse();
920  tf::StampedTransform transform_msg (m2o, time, fixed_frame_, odom_frame_);
921  tf_broadcaster_.sendTransform (transform_msg);
922  }
923 
924 }
925 
926 tf::Vector3 LaserScanMatcher::fusePoses(const tf::Transform& pose_delta)
927 {
928  int s;
929 
931  gsl_matrix_memcpy(Sigma_measured_, output_.cov_x_m);
932  gsl_matrix_add(output_.cov_x_m, Sigma_odom_trans_);
933  // in gsl, matrix inversion goes via LU decomposition
934  gsl_linalg_LU_decomp(output_.cov_x_m, P2_, &s);
935  gsl_linalg_LU_invert(output_.cov_x_m, P2_, I2_);
936  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans,
937  1.0, Sigma_odom_trans_, I2_, 0.0, kalman_gain_);
938  gsl_matrix_set_identity(kalman_gain_comp_);
939  gsl_matrix_sub(kalman_gain_comp_, kalman_gain_);
940 
941  // Sigma = (I-K)Sigma_odom
942  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans,
944  double measured_pose_yaw = tf::getYaw(measured_pose_.getRotation());
945  double predicted_pose_yaw = tf::getYaw(predicted_pose_.getRotation());
946  // handle discontinuity around +/- PI
947  if (measured_pose_yaw < -M_PI/2.0 && predicted_pose_yaw > M_PI/2.0)
948  measured_pose_yaw += 2*M_PI;
949  else if (measured_pose_yaw > M_PI/2.0 && predicted_pose_yaw < -M_PI/2.0)
950  predicted_pose_yaw += 2*M_PI;
951  // y = K*measured
952  gsl_vector_set(xvec_, 0, measured_pose_.getOrigin().getX());
953  gsl_vector_set(xvec_, 1, measured_pose_.getOrigin().getY());
954  gsl_vector_set(xvec_, 2, measured_pose_yaw);
955  gsl_blas_dgemv(CblasNoTrans, 1.0, kalman_gain_, xvec_, 0.0, yvec_);
956  // y += (I-K)*predicted
957  gsl_vector_set(xvec_, 0, predicted_pose_.getOrigin().getX());
958  gsl_vector_set(xvec_, 1, predicted_pose_.getOrigin().getY());
959  gsl_vector_set(xvec_, 2, predicted_pose_yaw);
960  gsl_blas_dgemv(CblasNoTrans, 1.0, kalman_gain_comp_, xvec_, 1.0, yvec_);
961 
962  ROS_DEBUG("kalman_gain:\n%e %e %e\n%e %e %e\n%e %e %e",
963  gsl_matrix_get(kalman_gain_, 0, 0),
964  gsl_matrix_get(kalman_gain_, 0, 1),
965  gsl_matrix_get(kalman_gain_, 0, 2),
966  gsl_matrix_get(kalman_gain_, 1, 0),
967  gsl_matrix_get(kalman_gain_, 1, 1),
968  gsl_matrix_get(kalman_gain_, 1, 2),
969  gsl_matrix_get(kalman_gain_, 2, 0),
970  gsl_matrix_get(kalman_gain_, 2, 1),
971  gsl_matrix_get(kalman_gain_, 2, 2));
972  return tf::Vector3(
973  gsl_vector_get(yvec_, 0),
974  gsl_vector_get(yvec_, 1),
975  gsl_vector_get(yvec_, 2));
976 }
977 
978 int LaserScanMatcher::processScan(LDP& curr_ldp_scan, LDP& ref_ldp_scan, const ros::Time& time)
979 {
981 
982  ref_ldp_scan->odometry[0] = 0.0;
983  ref_ldp_scan->odometry[1] = 0.0;
984  ref_ldp_scan->odometry[2] = 0.0;
985  ref_ldp_scan->estimate[0] = 0.0;
986  ref_ldp_scan->estimate[1] = 0.0;
987  ref_ldp_scan->estimate[2] = 0.0;
988  ref_ldp_scan->true_pose[0] = 0.0;
989  ref_ldp_scan->true_pose[1] = 0.0;
990  ref_ldp_scan->true_pose[2] = 0.0;
991 
992  input_.laser_ref = ref_ldp_scan;
993  input_.laser_sens = curr_ldp_scan;
994  input_.first_guess[0] = 0.0;
995  input_.first_guess[1] = 0.0;
996  input_.first_guess[2] = 0.0;
997 
998  // clear old covariance gsl matrices
999  if (output_.cov_x_m)
1000  {
1001  gsl_matrix_free(output_.cov_x_m);
1002  output_.cov_x_m = 0;
1003  }
1004  if (output_.dx_dy1_m)
1005  {
1006  gsl_matrix_free(output_.dx_dy1_m);
1007  output_.dx_dy1_m = 0;
1008  }
1009  if (output_.dx_dy2_m)
1010  {
1011  gsl_matrix_free(output_.dx_dy2_m);
1012  output_.dx_dy2_m = 0;
1013  }
1014 
1015  // *** scan match - using point to line icp from CSM
1016  try {
1017  sm_icp(&input_, &output_);
1018  } catch(int e) {
1019  if (e == GSL_EDOM) {
1020  ROS_ERROR("sm_icp failed, probably singular matrix");
1021  output_.valid = false;
1022  } else
1023  throw e;
1024  }
1025  if (output_.valid) {
1026  if (gsl_matrix_get(output_.cov_x_m, 0, 0) < max_variance_trans_ &&
1027  gsl_matrix_get(output_.cov_x_m, 1, 1) < max_variance_trans_ &&
1028  gsl_matrix_get(output_.cov_x_m, 2, 2) < max_variance_rot_) {
1029  // the correction of the laser's position, in the laser frame
1030  ROS_DEBUG("found correlation transform: x=%f, y=%f, yaw=%f",
1031  output_.x[0], output_.x[1], 180.0 * output_.x[2] / M_PI);
1032  ROS_DEBUG("variances: %f, %f, %f",
1033  gsl_matrix_get(output_.cov_x_m, 0, 0),
1034  gsl_matrix_get(output_.cov_x_m, 1, 1),
1035  gsl_matrix_get(output_.cov_x_m, 2, 2));
1036  tf::Transform pose_delta_laser;
1038  pose_delta_laser);
1039  // predicted pose is pcl-to-base_link
1040  // delta is crrection of predicted pose, right two
1041  // operands get us corrected pcl-to-base_link
1042  // multiply by map-to-pcl on the left to get
1043  // map-to-base_link, which is the pose we are looking for
1044  if (output_.x[2] > max_pose_delta_yaw_ ||
1045  output_.x[2] < -max_pose_delta_yaw_) {
1046  ROS_WARN("laser pose delta max yaw exceeded %f", output_.x[2]);
1047  ld_free(curr_ldp_scan);
1048  ld_free(ref_ldp_scan);
1049  return 0;
1050  }
1051  tf::Transform pose_delta =
1052  base_to_laser_ * pose_delta_laser * laser_to_base_;
1053  if (publish_debug_)
1054  doPublishDebugTF(time, pose_delta, debug_laser_delta_publisher_, "");
1055  // Sigma_odom is the covariance of odometry-delta
1056  // we need covariance in the map frame, so apply transforms
1058  gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0,
1059  Sigma_odom_, trans_sigma_, 0.0, I2_);
1060  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, trans_sigma_,
1061  I2_, 0.0, Sigma_odom_trans_);
1062  gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0,
1063  output_.cov_x_m, trans_sigma_, 0.0, I2_);
1064  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, trans_sigma_,
1065  I2_, 0.0, output_.cov_x_m);
1066  if (!no_odom_fusing_) {
1067  tf::Vector3 pv = fusePoses(pose_delta);
1068  createTfFromXYTheta(pv.getX(), pv.getY(), pv.getZ(), f2b_);
1069  } else {
1070  f2b_ = f2pcl_ * predicted_pose_in_pcl_ * pose_delta;
1071  }
1072  doPublishScanRate(time);
1073  double dur = (ros::WallTime::now() - start).toSec() * 1e3;
1074  ROS_DEBUG("scan matcher duration: %.1f ms, iterations: %d", dur, output_.iterations);
1075  ld_free(curr_ldp_scan);
1076  ld_free(ref_ldp_scan);
1077  return 1;
1078  } else {
1079  skipped_poses_ += 1;
1080  ROS_WARN_THROTTLE(10,"Variance of measured pose exceeded limit, "
1081  "not publishing. (%d poses skipped)",
1082  skipped_poses_);
1083  ld_free(curr_ldp_scan);
1084  ld_free(ref_ldp_scan);
1085  return 0;
1086  }
1087  } else {
1088  ROS_WARN("Error in scan matching");
1089  ld_free(curr_ldp_scan);
1090  ld_free(ref_ldp_scan);
1091  return 0;
1092  }
1093 }
1094 
1095 int LaserScanMatcher::processScan(LDP& curr_ldp_scan, const ros::Time& time)
1096 {
1098  int success = 0;
1099 
1100  // CSM is used in the following way:
1101  // The scans are always in the laser frame
1102  // The reference scan (prevLDPcan_) has a pose of [0, 0, 0]
1103  // The new scan (currLDPScan) has a pose equal to the movement
1104  // of the laser in the laser frame since the last scan
1105  // The computed correction is then propagated using the tf machinery
1106 
1107  prev_ldp_scan_->odometry[0] = 0.0;
1108  prev_ldp_scan_->odometry[1] = 0.0;
1109  prev_ldp_scan_->odometry[2] = 0.0;
1110 
1111  prev_ldp_scan_->estimate[0] = 0.0;
1112  prev_ldp_scan_->estimate[1] = 0.0;
1113  prev_ldp_scan_->estimate[2] = 0.0;
1114 
1115  prev_ldp_scan_->true_pose[0] = 0.0;
1116  prev_ldp_scan_->true_pose[1] = 0.0;
1117  prev_ldp_scan_->true_pose[2] = 0.0;
1118 
1120  input_.laser_sens = curr_ldp_scan;
1121 
1122  // **** estimated change since last scan
1123 
1124  double dt = (time - last_icp_time_).toSec();
1125  double pr_ch_x, pr_ch_y, pr_ch_a;
1126  getPrediction(pr_ch_x, pr_ch_y, pr_ch_a, dt);
1127 
1128  // the predicted change of the laser's position, in the fixed frame
1129 
1130  tf::Transform pr_ch;
1131  createTfFromXYTheta(pr_ch_x, pr_ch_y, pr_ch_a, pr_ch);
1132 
1133  // account for the change since the last kf, in the fixed frame
1134 
1135  pr_ch = pr_ch * (f2b_ * f2b_kf_.inverse());
1136 
1137  // the predicted change of the laser's position, in the laser frame
1138 
1139  tf::Transform pr_ch_l;
1140  pr_ch_l = laser_to_base_ * f2b_.inverse() * pr_ch * f2b_ * base_to_laser_ ;
1141 
1142  input_.first_guess[0] = pr_ch_l.getOrigin().getX();
1143  input_.first_guess[1] = pr_ch_l.getOrigin().getY();
1144  input_.first_guess[2] = tf::getYaw(pr_ch_l.getRotation());
1145 
1146  // If they are non-Null, free covariance gsl matrices to avoid leaking memory
1147  if (output_.cov_x_m)
1148  {
1149  gsl_matrix_free(output_.cov_x_m);
1150  output_.cov_x_m = 0;
1151  }
1152  if (output_.dx_dy1_m)
1153  {
1154  gsl_matrix_free(output_.dx_dy1_m);
1155  output_.dx_dy1_m = 0;
1156  }
1157  if (output_.dx_dy2_m)
1158  {
1159  gsl_matrix_free(output_.dx_dy2_m);
1160  output_.dx_dy2_m = 0;
1161  }
1162 
1163  // *** scan match - using point to line icp from CSM
1164  sm_icp(&input_, &output_);
1165  tf::Transform corr_ch;
1166 
1167  if (output_.valid)
1168  {
1169  // the correction of the laser's position, in the laser frame
1170  tf::Transform corr_ch_l;
1171  createTfFromXYTheta(output_.x[0], output_.x[1], output_.x[2], corr_ch_l);
1172 
1173  // the correction of the base's position, in the base frame
1174  corr_ch = base_to_laser_ * corr_ch_l * laser_to_base_;
1175 
1176  // update the pose in the world frame
1177  f2b_ = f2b_kf_ * corr_ch;
1178 
1179  doPublishScanRate(time);
1180  success = 1;
1181  }
1182  else
1183  {
1184  corr_ch.setIdentity();
1185  ROS_WARN("Error in scan matching");
1186  }
1187 
1188  // **** swap old and new
1189 
1190  if (newKeyframeNeeded(corr_ch))
1191  {
1192  // generate a keyframe
1194  prev_ldp_scan_ = curr_ldp_scan;
1195  f2b_kf_ = f2b_;
1196  }
1197  else
1198  {
1199  ld_free(curr_ldp_scan);
1200  }
1201 
1202  last_icp_time_ = time;
1203 
1204  // **** statistics
1205 
1206  double dur = (ros::WallTime::now() - start).toSec() * 1e3;
1207  ROS_DEBUG("Scan matcher total duration: %.1f ms", dur);
1208  return success;
1209 }
1210 
1212 {
1213  if (fabs(tf::getYaw(d.getRotation())) > kf_dist_angular_) return true;
1214 
1215  double x = d.getOrigin().getX();
1216  double y = d.getOrigin().getY();
1217  if (x*x + y*y > kf_dist_linear_sq_) return true;
1218 
1219  return false;
1220 }
1221 
1222 void LaserScanMatcher::laserScanToLDP(const sensor_msgs::LaserScan::ConstPtr& scan_msg,
1223  LDP& ldp)
1224 {
1225  unsigned int n = scan_msg->ranges.size();
1226  ldp = ld_alloc_new(n);
1227 
1228  for (unsigned int i = 0; i < n; i++)
1229  {
1230  // calculate position in laser frame
1231 
1232  double r = scan_msg->ranges[i];
1233 
1234  if (r > scan_msg->range_min && r < scan_msg->range_max &&
1235  (max_allowed_range_ <= 0 || r <= max_allowed_range_))
1236  {
1237  // fill in laser scan data
1238 
1239  ldp->valid[i] = 1;
1240  ldp->readings[i] = r;
1241  }
1242  else
1243  {
1244  ldp->valid[i] = 0;
1245  ldp->readings[i] = -1; // for invalid range
1246  }
1247 
1248  ldp->theta[i] = scan_msg->angle_min + i * scan_msg->angle_increment;
1249 
1250  ldp->cluster[i] = -1;
1251  }
1252 
1253  ldp->min_theta = ldp->theta[0];
1254  ldp->max_theta = ldp->theta[n-1];
1255 
1256  ldp->odometry[0] = 0.0;
1257  ldp->odometry[1] = 0.0;
1258  ldp->odometry[2] = 0.0;
1259 
1260  ldp->true_pose[0] = 0.0;
1261  ldp->true_pose[1] = 0.0;
1262  ldp->true_pose[2] = 0.0;
1263 }
1264 
1266 {
1267  int n = constructed_intensities_.size();
1268  ldp = ld_alloc_new(n);
1269 
1270  for (int i = 0; i < n; i++) {
1271  if (constructed_ranges_[i] > 0.0) {
1272  ldp->valid[i] = 1;
1273  ldp->readings[i] = constructed_ranges_[i];
1274  } else {
1275  ldp->valid[i] = 0;
1276  ldp->readings[i] = -1;
1277  }
1279  ldp->cluster[i] = -1;
1280  }
1281  ldp->min_theta = ldp->theta[0];
1282  ldp->max_theta = ldp->theta[n-1];
1283  ldp->odometry[0] = 0.0;
1284  ldp->odometry[1] = 0.0;
1285  ldp->odometry[2] = 0.0;
1286  ldp->true_pose[0] = 0.0;
1287  ldp->true_pose[1] = 0.0;
1288  ldp->true_pose[2] = 0.0;
1289 }
1290 
1291 bool LaserScanMatcher::getBaseToLaserTf (const std::string& frame_id)
1292 {
1293  ros::Time t = ros::Time::now();
1294 
1295  tf::StampedTransform base_to_laser_tf;
1296  try
1297  {
1299  base_frame_, frame_id, t, ros::Duration(1.0));
1301  base_frame_, frame_id, t, base_to_laser_tf);
1302  }
1303  catch (tf::TransformException ex)
1304  {
1305  ROS_WARN("Could not get initial transform from base to laser frame, %s", ex.what());
1306  return false;
1307  }
1308  base_to_laser_ = base_to_laser_tf;
1310 
1311  return true;
1312 }
1313 
1314 bool LaserScanMatcher::getBaseToFootprintTf (const std::string& frame_id)
1315 {
1316  ros::Time t = ros::Time::now();
1317 
1318  tf::StampedTransform footprint_to_base_tf;
1319  try
1320  {
1322  frame_id, base_frame_, t, ros::Duration(1.0));
1324  frame_id, base_frame_, t, footprint_to_base_tf);
1325  }
1326  catch (tf::TransformException ex)
1327  {
1328  ROS_WARN("Could not get initial transform from footprint to base frame, %s", ex.what());
1329  return false;
1330  }
1331  footprint_to_base_ = footprint_to_base_tf;
1333 
1334  return true;
1335 }
1336 
1337 // returns the predicted change in pose (in fixed frame)
1338 // since the last time we did icp
1339 void LaserScanMatcher::getPrediction(double& pr_ch_x, double& pr_ch_y,
1340  double& pr_ch_a, double dt)
1341 {
1342  boost::mutex::scoped_lock(mutex_);
1343 
1344  // **** base case - no input available, use zero-motion model
1345  pr_ch_x = 0.0;
1346  pr_ch_y = 0.0;
1347  pr_ch_a = 0.0;
1348 
1349  if (received_odom_)
1350  {
1351  pr_ch_x = current_odom_msg_.pose.pose.position.x -
1352  reference_odom_msg_.pose.pose.position.x;
1353 
1354  pr_ch_y = current_odom_msg_.pose.pose.position.y -
1355  reference_odom_msg_.pose.pose.position.y;
1356 
1357  pr_ch_a = tf::getYaw(current_odom_msg_.pose.pose.orientation) -
1358  tf::getYaw(reference_odom_msg_.pose.pose.orientation);
1359 
1360  if (pr_ch_a >= M_PI) pr_ch_a -= 2.0 * M_PI;
1361  else if (pr_ch_a < -M_PI) pr_ch_a += 2.0 * M_PI;
1362 
1364  gsl_matrix_set_zero(Sigma_odom_);
1365  theta_odom_ = 0.0;
1366  }
1367 
1368 }
1369 
1371  double x, double y, double theta, tf::Transform& t)
1372 {
1373  t.setOrigin(tf::Vector3(x, y, 0.0));
1374  tf::Quaternion q;
1375  q.setRPY(0.0, 0.0, theta);
1376  t.setRotation(q);
1377 }
1378 
1379 } // namespace scan_tools
scan_tools::LaserScanMatcher::map_occupancy_threshold_
double map_occupancy_threshold_
Definition: laser_scan_matcher.h:125
scan_tools::LaserScanMatcher::Sigma_odom_
gsl_matrix * Sigma_odom_
Definition: laser_scan_matcher.h:187
scan_tools::LaserScanMatcher::predicted_pose_in_pcl_
tf::Transform predicted_pose_in_pcl_
Definition: laser_scan_matcher.h:169
tf::Transform::getRotation
Quaternion getRotation() const
scan_tools::LaserScanMatcher::debug_laser_delta_publisher_
ros::Publisher debug_laser_delta_publisher_
Definition: laser_scan_matcher.h:110
scan_tools::LaserScanMatcher::debug_odom_current_publisher_
ros::Publisher debug_odom_current_publisher_
Definition: laser_scan_matcher.h:112
M_PI
#define M_PI
scan_tools::LaserScanMatcher::measured_pose_publisher_
ros::Publisher measured_pose_publisher_
Definition: laser_scan_matcher.h:106
scan_tools::LaserScanMatcher::doPredictPose
void doPredictPose(double delta_t)
Definition: laser_scan_matcher.cpp:491
scan_tools::LaserScanMatcher::latestOdomBefore
nav_msgs::Odometry * latestOdomBefore(const ros::Time &time)
Definition: laser_scan_matcher.cpp:411
scan_tools::LaserScanMatcher::max_allowed_range_
double max_allowed_range_
Definition: laser_scan_matcher.h:123
scan_tools::LaserScanMatcher::mapCallback
void mapCallback(const nav_msgs::OccupancyGrid::ConstPtr &map_msg)
Definition: laser_scan_matcher.cpp:685
ros::Publisher
tf::TransformBroadcaster::sendTransform
void sendTransform(const geometry_msgs::TransformStamped &transform)
scan_tools::LaserScanMatcher::observed_angle_max_
double observed_angle_max_
Definition: laser_scan_matcher.h:160
sm_params::orientation_neighbourhood
int orientation_neighbourhood
scan_tools::LaserScanMatcher::kf_dist_angular_
double kf_dist_angular_
Definition: laser_scan_matcher.h:144
scan_tools::LaserScanMatcher::getBaseToFootprintTf
bool getBaseToFootprintTf(const std::string &frame_id)
Definition: laser_scan_matcher.cpp:1314
scan_tools::LaserScanMatcher::f2pcl_
tf::Transform f2pcl_
Definition: laser_scan_matcher.h:171
tf::Quaternion::setRPY
void setRPY(const tfScalar &roll, const tfScalar &pitch, const tfScalar &yaw)
scan_tools::LaserScanMatcher::initialpose_valid_
bool initialpose_valid_
Definition: laser_scan_matcher.h:127
scan_tools::LaserScanMatcher::odom_frame_
std::string odom_frame_
Definition: laser_scan_matcher.h:118
sm_params::max_angular_correction_deg
double max_angular_correction_deg
scan_tools::LaserScanMatcher::base_to_footprint_
tf::Transform base_to_footprint_
Definition: laser_scan_matcher.h:98
laser_data::theta
double *restrict theta
s
XmlRpcServer s
scan_tools::LaserScanMatcher::map_subscriber_
ros::Subscriber map_subscriber_
Definition: laser_scan_matcher.h:89
scan_tools::LaserScanMatcher::tf_listener_
tf::TransformListener tf_listener_
Definition: laser_scan_matcher.h:93
ros::NodeHandle::getParam
bool getParam(const std::string &key, bool &b) const
scan_tools::LaserScanMatcher::initialpose_topic_
std::string initialpose_topic_
Definition: laser_scan_matcher.h:119
scan_tools::LaserScanMatcher::I2_
gsl_matrix * I2_
Definition: laser_scan_matcher.h:193
scan_tools::LaserScanMatcher::constructedScanToLDP
void constructedScanToLDP(LDP &ldp)
Definition: laser_scan_matcher.cpp:1265
ROS_WARN_THROTTLE
#define ROS_WARN_THROTTLE(period,...)
laser_scan_matcher.h
scan_tools::LaserScanMatcher::laserScanToLDP
void laserScanToLDP(const sensor_msgs::LaserScan::ConstPtr &scan_msg, LDP &ldp)
Definition: laser_scan_matcher.cpp:1222
scan_tools::LaserScanMatcher::prev_ldp_scan_
LDP prev_ldp_scan_
Definition: laser_scan_matcher.h:204
scan_tools::LaserScanMatcher::kalman_gain_
gsl_matrix * kalman_gain_
Definition: laser_scan_matcher.h:196
scan_tools::ScanConstructor::scan_params_t
Definition: scan_constructor.h:32
scan_tools::LaserScanMatcher::base_to_laser_
tf::Transform base_to_laser_
Definition: laser_scan_matcher.h:96
scan_tools::LaserScanMatcher::kalman_gain_comp_
gsl_matrix * kalman_gain_comp_
Definition: laser_scan_matcher.h:197
MAX_ODOM_AGE
#define MAX_ODOM_AGE
Definition: laser_scan_matcher.h:70
scan_tools::LaserScanMatcher::constructed_intensities_
std::vector< double > constructed_intensities_
Definition: laser_scan_matcher.h:182
scan_tools::LaserScanMatcher::nh_private_
ros::NodeHandle nh_private_
Definition: laser_scan_matcher.h:86
scan_tools::ScanConstructor::map_params_t::map_occupancy_threshold
int map_occupancy_threshold
Definition: scan_constructor.h:29
sm_params::use_ml_weights
int use_ml_weights
scan_tools::LaserScanMatcher::use_map_
bool use_map_
Definition: laser_scan_matcher.h:126
laser_data::readings
double *restrict readings
sm_params::max_linear_correction
double max_linear_correction
scan_tools::LaserScanMatcher::resetState
void resetState()
Definition: laser_scan_matcher.cpp:188
TimeBase< Time, Duration >::toSec
double toSec() const
sm_params::use_corr_tricks
int use_corr_tricks
sm_params::clustering_threshold
double clustering_threshold
sm_params::epsilon_xy
double epsilon_xy
scan_tools::LaserScanMatcher::trans_sigma_
gsl_matrix * trans_sigma_
Definition: laser_scan_matcher.h:195
laser_data::odometry
double odometry[3]
scan_tools::LaserScanMatcher::publish_pose_stamped_
bool publish_pose_stamped_
Definition: laser_scan_matcher.h:133
scan_tools::LaserScanMatcher::f2b_
tf::Transform f2b_
Definition: laser_scan_matcher.h:174
sm_params::sigma
double sigma
false
#define false
scan_tools::LaserScanMatcher::doPublishDebugTF
void doPublishDebugTF(const ros::Time &time, const tf::Transform &odom_delta, const ros::Publisher &publisher, const std::string &frame)
Definition: laser_scan_matcher.cpp:798
sm_params::restart_dt
double restart_dt
tf::poseTFToMsg
static void poseTFToMsg(const Pose &bt, geometry_msgs::Pose &msg)
scan_tools::LaserScanMatcher::skipped_poses_
int skipped_poses_
Definition: laser_scan_matcher.h:165
ld_free
void ld_free(LDP ld)
scan_tools::LaserScanMatcher::observed_angle_inc_
double observed_angle_inc_
Definition: laser_scan_matcher.h:161
sm_params::outliers_remove_doubles
int outliers_remove_doubles
scan_tools::LaserScanMatcher::initial_pose_
tf::Transform initial_pose_
Definition: laser_scan_matcher.h:170
scan_tools::LaserScanMatcher::tf_broadcaster_
tf::TransformBroadcaster tf_broadcaster_
Definition: laser_scan_matcher.h:94
scan_tools::ScanConstructor
Definition: scan_constructor.h:19
scan_tools::LaserScanMatcher::publish_pose_with_covariance_
bool publish_pose_with_covariance_
Definition: laser_scan_matcher.h:132
scan_tools::LaserScanMatcher::addOdomToHistory
void addOdomToHistory(const nav_msgs::Odometry::ConstPtr &o)
Definition: laser_scan_matcher.cpp:439
sm_params::max_reading
double max_reading
tf::StampedTransform
scan_tools::LaserScanMatcher::scanCallback
void scanCallback(const sensor_msgs::LaserScan::ConstPtr &scan_msg)
Definition: laser_scan_matcher.cpp:721
scan_tools::LaserScanMatcher::predicted_pose_
tf::Transform predicted_pose_
Definition: laser_scan_matcher.h:167
scan_tools::LaserScanMatcher::publish_debug_
bool publish_debug_
Definition: laser_scan_matcher.h:137
scan_tools::LaserScanMatcher::observed_time_inc_
double observed_time_inc_
Definition: laser_scan_matcher.h:163
scan_tools::LaserScanMatcher::mutex_
boost::mutex mutex_
Definition: laser_scan_matcher.h:151
scan_tools::LaserScanMatcher::output_
sm_result output_
Definition: laser_scan_matcher.h:203
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
scan_tools::LaserScanMatcher::yvec_
gsl_vector * yvec_
Definition: laser_scan_matcher.h:199
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
scan_tools::ScanConstructor::map_params_t::map_height
int map_height
Definition: scan_constructor.h:28
scan_tools::LaserScanMatcher::scan_subscriber_
ros::Subscriber scan_subscriber_
Definition: laser_scan_matcher.h:88
scan_tools::LaserScanMatcher::initialposeCallback
void initialposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &pose_msg)
Definition: laser_scan_matcher.cpp:640
scan_tools::LaserScanMatcher::getOdomDeltaT
double getOdomDeltaT(const nav_msgs::Odometry::ConstPtr &o)
Definition: laser_scan_matcher.cpp:484
scan_tools::LaserScanMatcher::initialpose_subscriber_
ros::Subscriber initialpose_subscriber_
Definition: laser_scan_matcher.h:91
scan_tools::LaserScanMatcher::fusePoses
tf::Vector3 fusePoses(const tf::Transform &pose_delta)
Definition: laser_scan_matcher.cpp:926
scan_tools::ScanConstructor::grid_t
std::vector< std::vector< int > > grid_t
Definition: scan_constructor.h:22
scan_tools::LaserScanMatcher::reference_odom_msg_
nav_msgs::Odometry reference_odom_msg_
Definition: laser_scan_matcher.h:180
scan_tools::ScanConstructor::map_params_t::map_res
double map_res
Definition: scan_constructor.h:26
sm_params::max_iterations
int max_iterations
sm_result::iterations
int iterations
scan_tools::LaserScanMatcher::observed_scan_time_
double observed_scan_time_
Definition: laser_scan_matcher.h:162
sm_icp
void sm_icp(struct sm_params *input, struct sm_result *output)
sm_params::use_sigma_weights
int use_sigma_weights
tf::getYaw
static double getYaw(const geometry_msgs::Quaternion &msg_q)
sm_result::dx_dy2_m
gsl_matrix * dx_dy2_m
scan_tools::LaserScanMatcher::Sigma_odom_trans_
gsl_matrix * Sigma_odom_trans_
Definition: laser_scan_matcher.h:188
scan_tools::LaserScanMatcher::debug_odom_reference_publisher_
ros::Publisher debug_odom_reference_publisher_
Definition: laser_scan_matcher.h:111
scan_tools::LaserScanMatcher::observed_angle_min_
double observed_angle_min_
Definition: laser_scan_matcher.h:159
laser_data::cluster
int *restrict cluster
sm_params::max_correspondence_dist
double max_correspondence_dist
sm_params::outliers_adaptive_mult
double outliers_adaptive_mult
scan_tools::LaserScanMatcher::orientation_covariance_
std::vector< double > orientation_covariance_
Definition: laser_scan_matcher.h:140
scan_tools::LaserScanMatcher::footprint_to_base_
tf::Transform footprint_to_base_
Definition: laser_scan_matcher.h:99
tf::Transform::inverse
Transform inverse() const
sm_params::laser_ref
LDP laser_ref
tf::Transform::setRotation
TFSIMD_FORCE_INLINE void setRotation(const Quaternion &q)
scan_tools::LaserScanMatcher::debug_odom_delta_publisher_
ros::Publisher debug_odom_delta_publisher_
Definition: laser_scan_matcher.h:109
scan_tools::LaserScanMatcher::I1_
gsl_matrix * I1_
Definition: laser_scan_matcher.h:192
scan_tools::LaserScanMatcher::doPublishScanRate
void doPublishScanRate(const ros::Time &time)
Definition: laser_scan_matcher.cpp:830
ROS_DEBUG
#define ROS_DEBUG(...)
scan_tools::LaserScanMatcher::max_variance_rot_
double max_variance_rot_
Definition: laser_scan_matcher.h:122
scan_tools
Definition: laser_scan_matcher.h:72
sm_params::restart_threshold_mean_error
double restart_threshold_mean_error
sm_params::do_alpha_test_thresholdDeg
double do_alpha_test_thresholdDeg
ros::WallTime::now
static WallTime now()
sm_result::valid
int valid
scan_tools::LaserScanMatcher::syncOdom
double syncOdom(const ros::Time &time)
Definition: laser_scan_matcher.cpp:446
scan_tools::LaserScanMatcher::debug_csm_
bool debug_csm_
Definition: laser_scan_matcher.h:138
scan_tools::LaserScanMatcher::xvec_
gsl_vector * xvec_
Definition: laser_scan_matcher.h:198
scan_tools::LaserScanMatcher::have_map_
bool have_map_
Definition: laser_scan_matcher.h:154
scan_tools::LaserScanMatcher::odom_subscriber_
ros::Subscriber odom_subscriber_
Definition: laser_scan_matcher.h:90
sm_params::outliers_maxPerc
double outliers_maxPerc
scan_tools::LaserScanMatcher::setTransSigmaMatrix
void setTransSigmaMatrix(const double yaw)
Definition: laser_scan_matcher.cpp:580
scan_tools::LaserScanMatcher::getPrediction
void getPrediction(double &pr_ch_x, double &pr_ch_y, double &pr_ch_a, double dt)
Definition: laser_scan_matcher.cpp:1339
sm_params::restart_dtheta
double restart_dtheta
scan_tools::LaserScanMatcher::newKeyframeNeeded
bool newKeyframeNeeded(const tf::Transform &d)
Definition: laser_scan_matcher.cpp:1211
scan_tools::LaserScanMatcher::constructScan
void constructScan(const ros::Time &timestamp)
Definition: laser_scan_matcher.cpp:588
d
d
ROS_WARN
#define ROS_WARN(...)
scan_tools::LaserScanMatcher::theta_odom_
double theta_odom_
Definition: laser_scan_matcher.h:200
sm_params::do_visibility_test
int do_visibility_test
scan_tools::LaserScanMatcher::P2_
gsl_permutation * P2_
Definition: laser_scan_matcher.h:194
ros::NodeHandle::subscribe
Subscriber subscribe(const std::string &topic, uint32_t queue_size, const boost::function< void(C)> &callback, const VoidConstPtr &tracked_object=VoidConstPtr(), const TransportHints &transport_hints=TransportHints())
scan_tools::LaserScanMatcher::position_covariance_
std::vector< double > position_covariance_
Definition: laser_scan_matcher.h:139
tf::Transformer::waitForTransform
bool waitForTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
tf::Transform
scan_tools::LaserScanMatcher::max_pose_delta_yaw_
double max_pose_delta_yaw_
Definition: laser_scan_matcher.h:124
laser_data::estimate
double estimate[3]
scan_tools::LaserScanMatcher::getBaseToLaserTf
bool getBaseToLaserTf(const std::string &frame_id)
Definition: laser_scan_matcher.cpp:1291
ros::WallTime
scan_tools::LaserScanMatcher::current_odom_msg_
nav_msgs::Odometry current_odom_msg_
Definition: laser_scan_matcher.h:179
scan_tools::LaserScanMatcher::measured_pose_
tf::Transform measured_pose_
Definition: laser_scan_matcher.h:168
tf::Transform::setOrigin
TFSIMD_FORCE_INLINE void setOrigin(const Vector3 &origin)
scan_tools::LaserScanMatcher::LaserScanMatcher
LaserScanMatcher(ros::NodeHandle nh, ros::NodeHandle nh_private)
Definition: laser_scan_matcher.cpp:50
scan_tools::LaserScanMatcher::nh_
ros::NodeHandle nh_
Definition: laser_scan_matcher.h:85
scan_tools::LaserScanMatcher::laser_to_base_
tf::Transform laser_to_base_
Definition: laser_scan_matcher.h:97
transform
void transform(const gsl_vector *p, const gsl_vector *x, gsl_vector *res)
sm_params::restart
int restart
scan_tools::LaserScanMatcher::Sigma_measured_
gsl_matrix * Sigma_measured_
Definition: laser_scan_matcher.h:189
scan_tools::LaserScanMatcher::initParams
void initParams()
Definition: laser_scan_matcher.cpp:212
start
ROSCPP_DECL void start()
scan_tools::ScanConstructor::map_params_t::map_width
int map_width
Definition: scan_constructor.h:27
scan_tools::LaserScanMatcher::odomCallback
void odomCallback(const nav_msgs::Odometry::ConstPtr &odom_msg)
Definition: laser_scan_matcher.cpp:553
sm_params::laser
double laser[3]
scan_tools::LaserScanMatcher::predicted_pose_publisher_
ros::Publisher predicted_pose_publisher_
Definition: laser_scan_matcher.h:105
scan_tools::LaserScanMatcher::pcl2f_
tf::Transform pcl2f_
Definition: laser_scan_matcher.h:172
scan_tools::LaserScanMatcher::kf_dist_linear_sq_
double kf_dist_linear_sq_
Definition: laser_scan_matcher.h:143
scan_tools::LaserScanMatcher::observed_scan_frame_
std::string observed_scan_frame_
Definition: laser_scan_matcher.h:164
scan_tools::LaserScanMatcher::publish_pose_with_covariance_stamped_
bool publish_pose_with_covariance_stamped_
Definition: laser_scan_matcher.h:134
scan_tools::LaserScanMatcher::fixed_frame_
std::string fixed_frame_
Definition: laser_scan_matcher.h:117
sm_result::cov_x_m
gsl_matrix * cov_x_m
scan_tools::LaserScanMatcher::received_odom_
bool received_odom_
Definition: laser_scan_matcher.h:155
sm_params::laser_sens
LDP laser_sens
ros::Time
scan_tools::LaserScanMatcher::max_variance_trans_
double max_variance_trans_
Definition: laser_scan_matcher.h:121
scan_tools::LaserScanMatcher::observed_range_max_
double observed_range_max_
Definition: laser_scan_matcher.h:158
scan_tools::LaserScanMatcher::processScan
int processScan(LDP &curr_ldp_scan, const ros::Time &time)
Definition: laser_scan_matcher.cpp:1095
scan_tools::LaserScanMatcher::observed_range_min_
double observed_range_min_
Definition: laser_scan_matcher.h:157
scan_tools::LaserScanMatcher::publish_base_tf_
bool publish_base_tf_
Definition: laser_scan_matcher.h:129
scan_tools::LaserScanMatcher::earliestOdomAfter
nav_msgs::Odometry * earliestOdomAfter(const ros::Time &time)
Definition: laser_scan_matcher.cpp:420
scan_tools::LaserScanMatcher::odom_history_
std::deque< nav_msgs::Odometry > odom_history_
Definition: laser_scan_matcher.h:184
scan_tools::LaserScanMatcher::no_odom_fusing_
bool no_odom_fusing_
Definition: laser_scan_matcher.h:147
scan_tools::LaserScanMatcher::publish_constructed_scan_
bool publish_constructed_scan_
Definition: laser_scan_matcher.h:128
ROS_ERROR
#define ROS_ERROR(...)
ld_alloc_new
LDP ld_alloc_new(int nrays)
tf::Transform::setIdentity
void setIdentity()
sm_params::first_guess
double first_guess[3]
scan_tools::LaserScanMatcher::pose_publisher_
ros::Publisher pose_publisher_
Definition: laser_scan_matcher.h:101
scan_tools::ScanConstructor::constructScan
scan_t constructScan(double x, double y, double yaw, const scan_params_t &scan_params) const
Definition: scan_constructor.cpp:184
sm_params::outliers_adaptive_order
double outliers_adaptive_order
scan_tools::LaserScanMatcher::B_odom_
gsl_matrix * B_odom_
Definition: laser_scan_matcher.h:190
sm_params::epsilon_theta
double epsilon_theta
laser_data
sm_params::debug_verify_tricks
int debug_verify_tricks
scan_tools::LaserScanMatcher::pose_stamped_publisher_
ros::Publisher pose_stamped_publisher_
Definition: laser_scan_matcher.h:102
scan_tools::LaserScanMatcher::last_icp_time_
ros::Time last_icp_time_
Definition: laser_scan_matcher.h:177
sm_params::min_reading
double min_reading
scan_tools::LaserScanMatcher::constructed_scan_publisher_
ros::Publisher constructed_scan_publisher_
Definition: laser_scan_matcher.h:107
scan_tools::LaserScanMatcher::constructed_ranges_
std::vector< double > constructed_ranges_
Definition: laser_scan_matcher.h:183
inverse
TFSIMD_FORCE_INLINE Quaternion inverse(const Quaternion &q)
scan_tools::LaserScanMatcher::scan_constructor_
ScanConstructor scan_constructor_
Definition: laser_scan_matcher.h:156
scan_tools::LaserScanMatcher::pose_with_covariance_stamped_publisher_
ros::Publisher pose_with_covariance_stamped_publisher_
Definition: laser_scan_matcher.h:104
laser_data::max_theta
double max_theta
scan_tools::LaserScanMatcher::f2b_kf_
tf::Transform f2b_kf_
Definition: laser_scan_matcher.h:175
scan_tools::LaserScanMatcher::Sigma_u_
gsl_matrix * Sigma_u_
Definition: laser_scan_matcher.h:191
sm_result::x
double x[3]
scan_tools::LaserScanMatcher::createTfFromXYTheta
void createTfFromXYTheta(double x, double y, double theta, tf::Transform &t)
Definition: laser_scan_matcher.cpp:1370
tf::Transformer::lookupTransform
void lookupTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, StampedTransform &transform) const
scan_tools::LaserScanMatcher::~LaserScanMatcher
~LaserScanMatcher()
Definition: laser_scan_matcher.cpp:171
sm_params::do_alpha_test
int do_alpha_test
scan_tools::LaserScanMatcher::doPublishOdomRate
void doPublishOdomRate(const ros::Time &time)
Definition: laser_scan_matcher.cpp:808
scan_tools::LaserScanMatcher::publish_predicted_pose_
bool publish_predicted_pose_
Definition: laser_scan_matcher.h:135
tf2::TransformException
ROS_INFO
#define ROS_INFO(...)
laser_data::true_pose
double true_pose[3]
scan_tools::LaserScanMatcher::scan_downsample_rate_
int scan_downsample_rate_
Definition: laser_scan_matcher.h:120
MAX_ODOM_HISTORY
#define MAX_ODOM_HISTORY
Definition: laser_scan_matcher.h:69
scan_tools::LaserScanMatcher::base_frame_
std::string base_frame_
Definition: laser_scan_matcher.h:116
laser_data::valid
int *restrict valid
ros::Duration
scan_tools::ScanConstructor::map_params_t
Definition: scan_constructor.h:24
sm_result::dx_dy1_m
gsl_matrix * dx_dy1_m
scan_tools::LaserScanMatcher::publish_odom_tf_
bool publish_odom_tf_
Definition: laser_scan_matcher.h:130
scan_tools::LaserScanMatcher::publish_measured_pose_
bool publish_measured_pose_
Definition: laser_scan_matcher.h:136
scan_tools::LaserScanMatcher::input_
sm_params input_
Definition: laser_scan_matcher.h:202
scan_tools::LaserScanMatcher::pose_with_covariance_publisher_
ros::Publisher pose_with_covariance_publisher_
Definition: laser_scan_matcher.h:103
tf::Quaternion
scan_tools::LaserScanMatcher::initialized_
bool initialized_
Definition: laser_scan_matcher.h:153
sm_debug_write
void sm_debug_write(int flag)
sm_params::do_compute_covariance
int do_compute_covariance
scan_tools::LaserScanMatcher::kf_dist_linear_
double kf_dist_linear_
Definition: laser_scan_matcher.h:142
sm_params::use_point_to_line_distance
int use_point_to_line_distance
scan_tools::LaserScanMatcher::publish_pose_
bool publish_pose_
Definition: laser_scan_matcher.h:131
tf::Transform::getOrigin
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
laser_data::min_theta
double min_theta
ros::NodeHandle
params
ros::Time::now
static Time now()
ROS_INFO_THROTTLE
#define ROS_INFO_THROTTLE(period,...)


lsm_localization
Author(s): Ivan Dryanovski , Ilija Hadzic
autogenerated on Fri Dec 23 2022 03:09:11