laser_scan_matcher.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011, Ivan Dryanovski, William Morris
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the CCNY Robotics Lab nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 /*  This package uses Canonical Scan Matcher [1], written by
00031  *  Andrea Censi
00032  *
00033  *  [1] A. Censi, "An ICP variant using a point-to-line metric"
00034  *  Proceedings of the IEEE International Conference
00035  *  on Robotics and Automation (ICRA), 2008
00036  */
00037 
00038 #include "laser_scan_matcher/laser_scan_matcher.h"
00039 
00040 namespace scan_tools
00041 {
00042 
00043 LaserScanMatcher::LaserScanMatcher(ros::NodeHandle nh, ros::NodeHandle nh_private):
00044   nh_(nh),
00045   nh_private_(nh_private)
00046 {
00047   ROS_INFO("Starting LaserScanMatcher");
00048 
00049   // **** init parameters
00050   initParams();
00051 
00052   // **** state variables
00053 
00054   initialized_   = false;
00055   received_imu_  = false;
00056   received_odom_ = false;
00057 
00058   w2b_.setIdentity();
00059 
00060   v_x_ = 0;
00061   v_y_ = 0;
00062   v_a_ = 0;
00063 
00064   pose2d_msg_ = boost::make_shared<geometry_msgs::Pose2D>();
00065   pose_msg_ = boost::make_shared<geometry_msgs::PoseStamped>();
00066 
00067 
00068   input_.laser[0] = 0.0;
00069   input_.laser[1] = 0.0;
00070   input_.laser[2] = 0.0;
00071 
00072   // *** subscribers
00073 
00074   if (use_cloud_input_)
00075   {
00076     cloud_subscriber_ = nh_.subscribe(
00077       cloud_topic_, 1, &LaserScanMatcher::cloudCallback, this);
00078 
00079   }
00080   else
00081   {
00082     scan_subscriber_ = nh_.subscribe(
00083       scan_topic_, 1, &LaserScanMatcher::scanCallback, this);
00084   }
00085 
00086   if (use_imu_)
00087   {
00088     imu_subscriber_ = nh_.subscribe(
00089       imu_topic_, 1, &LaserScanMatcher::imuCallback, this);
00090   }
00091 
00092   if (use_odom_)
00093   {
00094     odom_subscriber_ = nh_.subscribe(
00095       odom_topic_, 1, &LaserScanMatcher::odomCallback, this);
00096   }
00097 
00098   // **** pose publisher
00099   if (publish_pose_)
00100   {
00101     pose_publisher_  = nh_.advertise<geometry_msgs::PoseStamped>(
00102       pose_topic_, 5);
00103   }
00104   if (publish_pose2d_)
00105   {
00106     pose2d_publisher_  = nh_.advertise<geometry_msgs::Pose2D>(
00107       pose2d_topic_, 5);
00108   }
00109 }
00110 
00111 LaserScanMatcher::~LaserScanMatcher()
00112 {
00113 
00114 }
00115 
00116 void LaserScanMatcher::initParams()
00117 {
00118   // inputs
00119   if (!nh_private_.getParam ("scan_topic", scan_topic_))
00120     scan_topic_  = "scan";
00121   if (!nh_private_.getParam ("cloud_topic", cloud_topic_))
00122     cloud_topic_ = "cloud";
00123   if (!nh_private_.getParam ("odom_topic", odom_topic_))
00124     odom_topic_  = "odom";
00125   if (!nh_private_.getParam ("imu_topic", imu_topic_))
00126     imu_topic_   = "imu";
00127   // outputs
00128   if (!nh_private_.getParam ("pose2d_topic", pose2d_topic_))
00129     pose2d_topic_ = "pose2D";
00130   if (!nh_private_.getParam ("pose_topic", pose_topic_))
00131     pose_topic_ = "poseICP";
00132   // tfs
00133   if (!nh_private_.getParam ("base_frame", base_frame_))
00134     base_frame_ = "base_link";
00135   if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
00136     fixed_frame_ = "world";
00137   if (!nh_private_.getParam ("frameid", frameid_))
00138     frameid_ = "icp_pose";
00139 
00140   ROS_INFO("scan_topic_ %s",scan_topic_.c_str());
00141   ROS_INFO("cloud_topic_ %s",cloud_topic_.c_str());
00142   ROS_INFO("odom_topic_ %s",odom_topic_.c_str());
00143   ROS_INFO("imu_topic_ %s",imu_topic_.c_str());
00144   ROS_INFO("pose2d_topic_ %s",pose2d_topic_.c_str());
00145   ROS_INFO("pose_topic_ %s",pose_topic_.c_str());
00146   ROS_INFO("base_frame_ %s",base_frame_.c_str());
00147   ROS_INFO("fixed_frame_ %s",fixed_frame_.c_str());
00148   ROS_INFO("frameid_ %s",frameid_.c_str());
00149 
00150 
00151 
00152   // **** input type - laser scan, or point clouds?
00153   // if false, will subscrive to LaserScan msgs on /scan.
00154   // if true, will subscrive to PointCloud2 msgs on /cloud
00155 
00156   if (!nh_private_.getParam ("use_cloud_input", use_cloud_input_))
00157     use_cloud_input_= false;
00158 
00159   if (use_cloud_input_)
00160   {
00161     if (!nh_private_.getParam ("cloud_range_min", cloud_range_min_))
00162       cloud_range_min_ = 0.1;
00163     if (!nh_private_.getParam ("cloud_range_max", cloud_range_max_))
00164       cloud_range_max_ = 50.0;
00165 
00166     input_.min_reading = cloud_range_min_;
00167     input_.max_reading = cloud_range_max_;
00168   }
00169 
00170   // **** What predictions are available to speed up the ICP?
00171   // 1) imu - [theta] from imu yaw angle - /odom topic
00172   // 2) odom - [x, y, theta] from wheel odometry - /imu topic
00173   // 3) alpha_beta - [x, y, theta] from simple tracking filter - no topic req.
00174   // If more than one is enabled, priority is imu > odom > alpha_beta
00175 
00176   if (!nh_private_.getParam ("use_imu", use_imu_))
00177     use_imu_ = true;
00178   if (!nh_private_.getParam ("use_odom", use_odom_))
00179     use_odom_ = true;
00180   if (!nh_private_.getParam ("use_alpha_beta", use_alpha_beta_))
00181     use_alpha_beta_ = false;
00182 
00183   // **** How to publish the output?
00184   // tf (fixed_frame->base_frame),
00185   // pose message (pose of base frame in the fixed frame)
00186 
00187   if (!nh_private_.getParam ("publish_tf", publish_tf_))
00188     publish_tf_ = true;
00189   if (!nh_private_.getParam ("publish_pose", publish_pose_))
00190     publish_pose_ = true;
00191   if (!nh_private_.getParam ("publish_pose2d", publish_pose2d_))
00192     publish_pose2d_ = true;
00193 
00194   if (!nh_private_.getParam ("alpha", alpha_))
00195     alpha_ = 1.0;
00196   if (!nh_private_.getParam ("beta", beta_))
00197     beta_ = 0.8;
00198 
00199  // **** CSM parameters - comments copied from algos.h (by Andrea Censi)
00200 
00201   // Maximum angular displacement between scans
00202   if (!nh_private_.getParam ("max_angular_correction_deg", input_.max_angular_correction_deg))
00203     input_.max_angular_correction_deg = 45.0;
00204 
00205   // Maximum translation between scans (m)
00206   if (!nh_private_.getParam ("max_linear_correction", input_.max_linear_correction))
00207     input_.max_linear_correction = 0.50;
00208 
00209   // Maximum ICP cycle iterations
00210   if (!nh_private_.getParam ("max_iterations", input_.max_iterations))
00211     input_.max_iterations = 10;
00212 
00213   // A threshold for stopping (m)
00214   if (!nh_private_.getParam ("epsilon_xy", input_.epsilon_xy))
00215     input_.epsilon_xy = 0.000001;
00216 
00217   // A threshold for stopping (rad)
00218   if (!nh_private_.getParam ("epsilon_theta", input_.epsilon_theta))
00219     input_.epsilon_theta = 0.000001;
00220 
00221   // Maximum distance for a correspondence to be valid
00222   if (!nh_private_.getParam ("max_correspondence_dist", input_.max_correspondence_dist))
00223     input_.max_correspondence_dist = 0.3;
00224 
00225   // Noise in the scan (m)
00226   if (!nh_private_.getParam ("sigma", input_.sigma))
00227     input_.sigma = 0.010;
00228 
00229   // Use smart tricks for finding correspondences.
00230   if (!nh_private_.getParam ("use_corr_tricks", input_.use_corr_tricks))
00231     input_.use_corr_tricks = 1;
00232 
00233   // Restart: Restart if error is over threshold
00234   if (!nh_private_.getParam ("restart", input_.restart))
00235     input_.restart = 0;
00236 
00237   // Restart: Threshold for restarting
00238   if (!nh_private_.getParam ("restart_threshold_mean_error", input_.restart_threshold_mean_error))
00239     input_.restart_threshold_mean_error = 0.01;
00240 
00241   // Restart: displacement for restarting. (m)
00242   if (!nh_private_.getParam ("restart_dt", input_.restart_dt))
00243     input_.restart_dt = 1.0;
00244 
00245   // Restart: displacement for restarting. (rad)
00246   if (!nh_private_.getParam ("restart_dtheta", input_.restart_dtheta))
00247     input_.restart_dtheta = 0.1;
00248 
00249   // Max distance for staying in the same clustering
00250   if (!nh_private_.getParam ("clustering_threshold", input_.clustering_threshold))
00251     input_.clustering_threshold = 0.25;
00252 
00253   // Number of neighbour rays used to estimate the orientation
00254   if (!nh_private_.getParam ("orientation_neighbourhood", input_.orientation_neighbourhood))
00255     input_.orientation_neighbourhood = 20;
00256 
00257   // If 0, it's vanilla ICP
00258   if (!nh_private_.getParam ("use_point_to_line_distance", input_.use_point_to_line_distance))
00259     input_.use_point_to_line_distance = 1;
00260 
00261   // Discard correspondences based on the angles
00262   if (!nh_private_.getParam ("do_alpha_test", input_.do_alpha_test))
00263     input_.do_alpha_test = 0;
00264 
00265   // Discard correspondences based on the angles - threshold angle, in degrees
00266   if (!nh_private_.getParam ("do_alpha_test_thresholdDeg", input_.do_alpha_test_thresholdDeg))
00267     input_.do_alpha_test_thresholdDeg = 20.0;
00268 
00269   // Percentage of correspondences to consider: if 0.9,
00270         // always discard the top 10% of correspondences with more error
00271   if (!nh_private_.getParam ("outliers_maxPerc", input_.outliers_maxPerc))
00272     input_.outliers_maxPerc = 0.90;
00273 
00274   // Parameters describing a simple adaptive algorithm for discarding.
00275         //  1) Order the errors.
00276         //      2) Choose the percentile according to outliers_adaptive_order.
00277         //         (if it is 0.7, get the 70% percentile)
00278         //      3) Define an adaptive threshold multiplying outliers_adaptive_mult
00279         //         with the value of the error at the chosen percentile.
00280         //      4) Discard correspondences over the threshold.
00281         //      This is useful to be conservative; yet remove the biggest errors.
00282   if (!nh_private_.getParam ("outliers_adaptive_order", input_.outliers_adaptive_order))
00283     input_.outliers_adaptive_order = 0.7;
00284 
00285   if (!nh_private_.getParam ("outliers_adaptive_mult", input_.outliers_adaptive_mult))
00286     input_.outliers_adaptive_mult = 2.0;
00287 
00288   //If you already have a guess of the solution, you can compute the polar angle
00289         //      of the points of one scan in the new position. If the polar angle is not a monotone
00290         //      function of the readings index, it means that the surface is not visible in the
00291         //      next position. If it is not visible, then we don't use it for matching.
00292   if (!nh_private_.getParam ("do_visibility_test", input_.do_visibility_test))
00293     input_.do_visibility_test = 0;
00294 
00295   // no two points in laser_sens can have the same corr.
00296   if (!nh_private_.getParam ("outliers_remove_doubles", input_.outliers_remove_doubles))
00297     input_.outliers_remove_doubles = 1;
00298 
00299   // If 1, computes the covariance of ICP using the method http://purl.org/censi/2006/icpcov
00300   if (!nh_private_.getParam ("do_compute_covariance", input_.do_compute_covariance))
00301     input_.do_compute_covariance = 0;
00302 
00303   // Checks that find_correspondences_tricks gives the right answer
00304   if (!nh_private_.getParam ("debug_verify_tricks", input_.debug_verify_tricks))
00305     input_.debug_verify_tricks = 0;
00306 
00307   // If 1, the field 'true_alpha' (or 'alpha') in the first scan is used to compute the
00308   // incidence beta, and the factor (1/cos^2(beta)) used to weight the correspondence.");
00309   if (!nh_private_.getParam ("use_ml_weights", input_.use_ml_weights))
00310     input_.use_ml_weights = 0;
00311 
00312   // If 1, the field 'readings_sigma' in the second scan is used to weight the
00313   // correspondence by 1/sigma^2
00314   if (!nh_private_.getParam ("use_sigma_weights", input_.use_sigma_weights))
00315     input_.use_sigma_weights = 0;
00316 }
00317 
00318 void LaserScanMatcher::imuCallback (const sensor_msgs::ImuPtr& imu_msg)
00319 {
00320   boost::mutex::scoped_lock(mutex_);
00321   latest_imu_yaw_ = getYawFromQuaternion(imu_msg->orientation);
00322   if (!received_imu_)
00323   {
00324     last_imu_yaw_ = getYawFromQuaternion(imu_msg->orientation);
00325     received_imu_ = true;
00326   }
00327 }
00328 
00329 void LaserScanMatcher::odomCallback (const nav_msgs::Odometry::ConstPtr& odom_msg)
00330 {
00331   boost::mutex::scoped_lock(mutex_);
00332   latest_odom_ = *odom_msg;
00333   if (!received_odom_)
00334   {
00335     last_odom_ = *odom_msg;
00336     received_odom_ = true;
00337   }
00338 }
00339 
00340 void LaserScanMatcher::cloudCallback (const PointCloudT::ConstPtr& cloud)
00341 {
00342   // **** if first scan, cache the tf from base to the scanner
00343 
00344   if (!initialized_)
00345   {
00346     // cache the static tf from base to laser
00347     if (!getBaseToLaserTf(cloud->header.frame_id))
00348     {
00349       ROS_WARN("ScanMatcher: Skipping scan");
00350       return;
00351     }
00352 
00353     PointCloudToLDP(cloud, prev_ldp_scan_);
00354     last_icp_time_ = cloud->header.stamp;
00355     last_imu_yaw_ = latest_imu_yaw_;
00356     last_odom_ = latest_odom_;
00357     initialized_ = true;
00358   }
00359 
00360   LDP curr_ldp_scan;
00361   PointCloudToLDP(cloud, curr_ldp_scan);
00362   processScan(curr_ldp_scan, cloud->header.stamp);
00363 }
00364 
00365 void LaserScanMatcher::scanCallback (const sensor_msgs::LaserScan::ConstPtr& scan_msg)
00366 {
00367   // **** if first scan, cache the tf from base to the scanner
00368 
00369   if (!initialized_)
00370   {
00371     createCache(scan_msg);    // caches the sin and cos of all angles
00372 
00373     // cache the static tf from base to laser
00374     if (!getBaseToLaserTf(scan_msg->header.frame_id))
00375     {
00376       ROS_WARN("ScanMatcher: Skipping scan");
00377       return;
00378     }
00379 
00380     laserScanToLDP(scan_msg, prev_ldp_scan_);
00381     last_icp_time_ = scan_msg->header.stamp;
00382     last_imu_yaw_ = latest_imu_yaw_;
00383     last_odom_ = latest_odom_;
00384     initialized_ = true;
00385   }
00386 
00387   LDP curr_ldp_scan;
00388   laserScanToLDP(scan_msg, curr_ldp_scan);
00389   processScan(curr_ldp_scan, scan_msg->header.stamp);
00390 }
00391 
00392 void LaserScanMatcher::processScan(LDP& curr_ldp_scan, const ros::Time& time)
00393 {
00394   struct timeval start, end;    // used for timing
00395   gettimeofday(&start, NULL);
00396 
00397   // CSM is used in the following way:
00398   // The scans are always in the laser frame
00399   // The reference scan (prevLDPcan_) has a pose of 0
00400   // The new scan (currLDPScan) has a pose equal to the movement
00401   // of the laser in the laser frame since the last scan
00402   // The computed correction is then propagated using the tf machinery
00403 
00404   prev_ldp_scan_->odometry[0] = 0;
00405   prev_ldp_scan_->odometry[1] = 0;
00406   prev_ldp_scan_->odometry[2] = 0;
00407 
00408   prev_ldp_scan_->estimate[0] = 0;
00409   prev_ldp_scan_->estimate[1] = 0;
00410   prev_ldp_scan_->estimate[2] = 0;
00411 
00412   prev_ldp_scan_->true_pose[0] = 0;
00413   prev_ldp_scan_->true_pose[1] = 0;
00414   prev_ldp_scan_->true_pose[2] = 0;
00415 
00416   input_.laser_ref  = prev_ldp_scan_;
00417   input_.laser_sens = curr_ldp_scan;
00418 
00419   // **** estimated change since last scan
00420 
00421   ros::Time new_icp_time = ros::Time::now();
00422   ros::Duration dur = new_icp_time - last_icp_time_;
00423   double dt = dur.toSec();
00424 
00425   double pr_ch_x, pr_ch_y, pr_ch_a;
00426   getPrediction(pr_ch_x, pr_ch_y, pr_ch_a, dt);
00427 
00428   // the predicted change of the laser's position, in the base frame
00429 
00430   tf::Transform pr_ch;
00431   createTfFromXYTheta(pr_ch_x, pr_ch_y, pr_ch_a, pr_ch);
00432 
00433   // the predicted change of the laser's position, in the laser frame
00434 
00435   tf::Transform pr_ch_l;
00436   pr_ch_l = laser_to_base_ * pr_ch * base_to_laser_;
00437 
00438   input_.first_guess[0] = pr_ch_l.getOrigin().getX();
00439   input_.first_guess[1] = pr_ch_l.getOrigin().getY();
00440   input_.first_guess[2] = getYawFromQuaternion(pr_ch_l.getRotation());
00441 
00442 /*
00443   printf("%f, %f, %f\n", input_.first_guess[0],
00444                          input_.first_guess[1],
00445                          input_.first_guess[2]);
00446 */
00447   // *** scan match - using point to line icp from CSM
00448 
00449   sm_icp(&input_, &output_);
00450 
00451   if (output_.valid)
00452   {
00453     // the correction of the laser's position, in the laser frame
00454 
00455     //printf("%f, %f, %f\n", output_.x[0], output_.x[1], output_.x[2]);
00456 
00457     float dx = output_.x[0] - pr_ch_l.getOrigin().getX();
00458     float dy = output_.x[1] - pr_ch_l.getOrigin().getY();
00459     float da = output_.x[2] - getYawFromQuaternion(pr_ch_l.getRotation());
00460 
00461     tf::Transform corr_ch_l;
00462     float epsilon_dist = 2.25;
00463     float epsilon_angle = 3.14159;
00464     float dist = dx*dx + dy*dy;
00465 
00466     if( dist <= epsilon_dist && abs(da) <= epsilon_angle )
00467     {
00468       createTfFromXYTheta(output_.x[0], output_.x[1], output_.x[2], corr_ch_l);
00469     }else
00470     {
00471       ROS_WARN("dist: %f, angle %f",dist,da);
00472       corr_ch_l = pr_ch_l;
00473     }
00474 
00475     // the correction of the base's position, in the world frame
00476 
00477     tf::Transform corr_ch = base_to_laser_ * corr_ch_l * laser_to_base_;
00478 
00479     if(use_alpha_beta_)
00480     {
00481       tf::Transform w2b_new = w2b_ * corr_ch;
00482 
00483       double dx = w2b_new.getOrigin().getX() - w2b_.getOrigin().getX();
00484       double dy = w2b_new.getOrigin().getY() - w2b_.getOrigin().getY();
00485       double da = getYawFromQuaternion(w2b_new.getRotation()) -
00486                   getYawFromQuaternion(w2b_.getRotation());
00487 
00488       double r_x = dx - pr_ch_x;
00489       double r_y = dy - pr_ch_y;
00490       double r_a = da - pr_ch_a;
00491 
00492       double x = w2b_.getOrigin().getX();
00493       double y = w2b_.getOrigin().getY();
00494       double a = getYawFromQuaternion(w2b_.getRotation());
00495 
00496       double x_new  = (x + pr_ch_x) + alpha_ * r_x;
00497       double y_new  = (y + pr_ch_y) + alpha_ * r_y;
00498       double a_new  = (a + pr_ch_a) + alpha_ * r_a;
00499 
00500       createTfFromXYTheta(x_new, y_new, a_new, w2b_);
00501 
00502       if (dt != 0.0)
00503       {
00504         v_x_ = v_x_ + (beta_ / dt) * r_x;
00505         v_y_ = v_y_ + (beta_ / dt) * r_y;
00506         v_a_ = v_a_ + (beta_ / dt) * r_a;
00507       }
00508     }
00509     else
00510     {
00511       w2b_ = w2b_ * corr_ch;
00512     }
00513 
00514     // **** publish
00515 
00516     if (publish_pose2d_)
00517     {
00518       pose2d_msg_->x = w2b_.getOrigin().getX();
00519       pose2d_msg_->y = w2b_.getOrigin().getY();
00520       pose2d_msg_->theta = getYawFromQuaternion(w2b_.getRotation());
00521       pose2d_publisher_.publish(pose2d_msg_);
00522     }
00523     if (publish_tf_)
00524     {
00525       tf::StampedTransform transform_msg (w2b_, time, fixed_frame_, base_frame_);
00526       tf_broadcaster_.sendTransform (transform_msg);
00527     }
00528     if(publish_pose_)
00529     {
00530       pose_msg_->header.stamp = time;
00531       pose_msg_->header.frame_id  = frameid_;
00532       pose_msg_->pose.position.x = w2b_.getOrigin().getX();
00533       pose_msg_->pose.position.y = w2b_.getOrigin().getY();
00534       pose_msg_->pose.position.z = 0.0;
00535       pose_msg_->pose.orientation.x = w2b_.getRotation().x();
00536       pose_msg_->pose.orientation.y = w2b_.getRotation().y();
00537       pose_msg_->pose.orientation.z = w2b_.getRotation().z();
00538       pose_msg_->pose.orientation.w = w2b_.getRotation().w();
00539       pose_publisher_.publish(pose_msg_);
00540     }
00541 
00542   }
00543   else
00544   {
00545     ROS_WARN("Error in scan matching");
00546 
00547     v_x_ = 0.0;
00548     v_y_ = 0.0;
00549     v_a_ = 0.0;
00550   }
00551 
00552   // **** swap old and new
00553 
00554   ld_free(prev_ldp_scan_);
00555   prev_ldp_scan_ = curr_ldp_scan;
00556   last_icp_time_ = new_icp_time;
00557 
00558   // **** statistics
00559 
00560   gettimeofday(&end, NULL);
00561   double dur_total = ((end.tv_sec   * 1000000 + end.tv_usec  ) -
00562                       (start.tv_sec * 1000000 + start.tv_usec)) / 1000.0;
00563   ROS_DEBUG("scan matcher total duration: %.1f ms", dur_total);
00564 }
00565 
00566 void LaserScanMatcher::PointCloudToLDP(const PointCloudT::ConstPtr& cloud,
00567                                              LDP& ldp)
00568 {
00569   unsigned int n = cloud->width * cloud->height ;
00570   ldp = ld_alloc_new(n);
00571 
00572   for (unsigned int i = 0; i < n; i++)
00573   {
00574     // calculate position in laser frame
00575 
00576     if (is_nan(cloud->points[i].x) || is_nan(cloud->points[i].y))
00577     {
00578       ROS_WARN("Laser Scan Matcher: Cloud input contains NaN values. \
00579                 Please use a filtered cloud input.");
00580     }
00581     else
00582     {
00583       double r = sqrt(cloud->points[i].x * cloud->points[i].x +
00584                       cloud->points[i].y * cloud->points[i].y);
00585 
00586       if (r > cloud_range_min_ && r < cloud_range_max_)
00587       {
00588         ldp->valid[i] = 1;
00589         ldp->readings[i] = r;
00590       }
00591       else
00592       {
00593         ldp->valid[i] = 0;
00594         ldp->readings[i] = -1;  // for invalid range
00595       }
00596     }
00597 
00598     ldp->theta[i] = atan2(cloud->points[i].y, cloud->points[i].x);
00599     ldp->cluster[i]  = -1;
00600   }
00601 
00602   ldp->min_theta = ldp->theta[0];
00603   ldp->max_theta = ldp->theta[n-1];
00604 
00605   ldp->odometry[0] = 0.0;
00606   ldp->odometry[1] = 0.0;
00607   ldp->odometry[2] = 0.0;
00608 
00609   ldp->true_pose[0] = 0.0;
00610   ldp->true_pose[1] = 0.0;
00611   ldp->true_pose[2] = 0.0;
00612 }
00613 
00614 void LaserScanMatcher::laserScanToLDP(const sensor_msgs::LaserScan::ConstPtr& scan_msg,
00615                                             LDP& ldp)
00616 {
00617   unsigned int n = scan_msg->ranges.size();
00618   ldp = ld_alloc_new(n);
00619 
00620   for (unsigned int i = 0; i < n; i++)
00621   {
00622     // calculate position in laser frame
00623 
00624     double r = scan_msg->ranges[i];
00625 
00626     if (r > scan_msg->range_min && r < scan_msg->range_max)
00627     {
00628       // fill in laser scan data
00629 
00630       ldp->valid[i] = 1;
00631       ldp->readings[i] = r;
00632     }
00633     else
00634     {
00635       ldp->valid[i] = 0;
00636       ldp->readings[i] = -1;  // for invalid range
00637     }
00638 
00639     ldp->theta[i]    = scan_msg->angle_min + i * scan_msg->angle_increment;
00640 
00641     ldp->cluster[i]  = -1;
00642   }
00643 
00644   ldp->min_theta = ldp->theta[0];
00645   ldp->max_theta = ldp->theta[n-1];
00646 
00647   ldp->odometry[0] = 0.0;
00648   ldp->odometry[1] = 0.0;
00649   ldp->odometry[2] = 0.0;
00650 
00651   ldp->true_pose[0] = 0.0;
00652   ldp->true_pose[1] = 0.0;
00653   ldp->true_pose[2] = 0.0;
00654 }
00655 
00656 void LaserScanMatcher::createCache (const sensor_msgs::LaserScan::ConstPtr& scan_msg)
00657 {
00658   a_cos_.clear();
00659   a_sin_.clear();
00660 
00661   for (unsigned int i = 0; i < scan_msg->ranges.size(); ++i)
00662   {
00663     double angle = scan_msg->angle_min + i * scan_msg->angle_increment;
00664     a_cos_.push_back(cos(angle));
00665     a_sin_.push_back(sin(angle));
00666   }
00667 
00668   input_.min_reading = scan_msg->range_min;
00669   input_.max_reading = scan_msg->range_max;
00670 }
00671 
00672 bool LaserScanMatcher::getBaseToLaserTf (const std::string& frame_id)
00673 {
00674   ros::Time t = ros::Time::now();
00675 
00676   tf::StampedTransform base_to_laser_tf;
00677   try
00678   {
00679     tf_listener_.waitForTransform(
00680       base_frame_, frame_id, t, ros::Duration(1.0));
00681     tf_listener_.lookupTransform (
00682       base_frame_, frame_id, t, base_to_laser_tf);
00683   }
00684   catch (tf::TransformException ex)
00685   {
00686     ROS_WARN("ScanMatcher: Could not get initial laser transform(%s)", ex.what());
00687     return false;
00688   }
00689   base_to_laser_ = base_to_laser_tf;
00690   laser_to_base_ = base_to_laser_.inverse();
00691 
00692   return true;
00693 }
00694 
00695 // returns the predicted change in pose (in fixed frame)
00696 // since the last time we did icp
00697 void LaserScanMatcher::getPrediction(double& pr_ch_x, double& pr_ch_y,
00698                                      double& pr_ch_a, double dt)
00699 {
00700   boost::mutex::scoped_lock(mutex_);
00701 
00702   // **** base case - no input available, use zero-motion model
00703   pr_ch_x = 0.0;
00704   pr_ch_y = 0.0;
00705   pr_ch_a = 0.0;
00706 
00707   // **** use alpha-beta tracking (const. vel. model)
00708   if (use_alpha_beta_)
00709   {
00710     // estmate change in fixed frame, using fixed velocity
00711     pr_ch_x = v_x_ * dt;     // in fixed frame
00712     pr_ch_y = v_y_ * dt;
00713     pr_ch_a = v_a_ * dt;
00714   }
00715 
00716   // **** use wheel odometry
00717   if (use_odom_ && received_odom_)
00718   {
00719     pr_ch_x = latest_odom_.pose.pose.position.x -
00720               last_odom_.pose.pose.position.x;
00721 
00722     pr_ch_y = latest_odom_.pose.pose.position.y -
00723               last_odom_.pose.pose.position.y;
00724 
00725     pr_ch_a = getYawFromQuaternion(latest_odom_.pose.pose.orientation) -
00726               getYawFromQuaternion(last_odom_.pose.pose.orientation);
00727 
00728     last_odom_ = latest_odom_;
00729   }
00730 
00731   // **** use imu
00732   if (use_imu_ && received_imu_)
00733   {
00734     pr_ch_a = latest_imu_yaw_ - last_imu_yaw_;
00735     last_imu_yaw_ = latest_imu_yaw_;
00736   }
00737 }
00738 
00739 double LaserScanMatcher::getYawFromQuaternion(
00740   const tf::Quaternion& quaternion)
00741 {
00742   double temp, yaw;
00743   btMatrix3x3 m(quaternion);
00744   m.getRPY(temp, temp, yaw);
00745   return yaw;
00746 }
00747 
00748 double LaserScanMatcher::getYawFromQuaternion(
00749   const geometry_msgs::Quaternion& quaternion)
00750 {
00751   tf::Quaternion q;
00752   tf::quaternionMsgToTF(quaternion, q);
00753   return getYawFromQuaternion(q);
00754 }
00755 
00756 void LaserScanMatcher::createTfFromXYTheta(
00757   double x, double y, double theta, tf::Transform& t)
00758 {
00759   t.setOrigin(btVector3(x, y, 0.0));
00760   tf::Quaternion q;
00761   q.setRPY(0.0, 0.0, theta);
00762   t.setRotation(q);
00763 }
00764 
00765 } //namespace
00766 


iri_laser_scan_matcher
Author(s): Ivan Dryanovski, William Morris
autogenerated on Fri Dec 6 2013 22:42:22