00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
00050 initParams();
00051
00052
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
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
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
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
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
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
00153
00154
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
00171
00172
00173
00174
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
00184
00185
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
00200
00201
00202 if (!nh_private_.getParam ("max_angular_correction_deg", input_.max_angular_correction_deg))
00203 input_.max_angular_correction_deg = 45.0;
00204
00205
00206 if (!nh_private_.getParam ("max_linear_correction", input_.max_linear_correction))
00207 input_.max_linear_correction = 0.50;
00208
00209
00210 if (!nh_private_.getParam ("max_iterations", input_.max_iterations))
00211 input_.max_iterations = 10;
00212
00213
00214 if (!nh_private_.getParam ("epsilon_xy", input_.epsilon_xy))
00215 input_.epsilon_xy = 0.000001;
00216
00217
00218 if (!nh_private_.getParam ("epsilon_theta", input_.epsilon_theta))
00219 input_.epsilon_theta = 0.000001;
00220
00221
00222 if (!nh_private_.getParam ("max_correspondence_dist", input_.max_correspondence_dist))
00223 input_.max_correspondence_dist = 0.3;
00224
00225
00226 if (!nh_private_.getParam ("sigma", input_.sigma))
00227 input_.sigma = 0.010;
00228
00229
00230 if (!nh_private_.getParam ("use_corr_tricks", input_.use_corr_tricks))
00231 input_.use_corr_tricks = 1;
00232
00233
00234 if (!nh_private_.getParam ("restart", input_.restart))
00235 input_.restart = 0;
00236
00237
00238 if (!nh_private_.getParam ("restart_threshold_mean_error", input_.restart_threshold_mean_error))
00239 input_.restart_threshold_mean_error = 0.01;
00240
00241
00242 if (!nh_private_.getParam ("restart_dt", input_.restart_dt))
00243 input_.restart_dt = 1.0;
00244
00245
00246 if (!nh_private_.getParam ("restart_dtheta", input_.restart_dtheta))
00247 input_.restart_dtheta = 0.1;
00248
00249
00250 if (!nh_private_.getParam ("clustering_threshold", input_.clustering_threshold))
00251 input_.clustering_threshold = 0.25;
00252
00253
00254 if (!nh_private_.getParam ("orientation_neighbourhood", input_.orientation_neighbourhood))
00255 input_.orientation_neighbourhood = 20;
00256
00257
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
00262 if (!nh_private_.getParam ("do_alpha_test", input_.do_alpha_test))
00263 input_.do_alpha_test = 0;
00264
00265
00266 if (!nh_private_.getParam ("do_alpha_test_thresholdDeg", input_.do_alpha_test_thresholdDeg))
00267 input_.do_alpha_test_thresholdDeg = 20.0;
00268
00269
00270
00271 if (!nh_private_.getParam ("outliers_maxPerc", input_.outliers_maxPerc))
00272 input_.outliers_maxPerc = 0.90;
00273
00274
00275
00276
00277
00278
00279
00280
00281
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
00289
00290
00291
00292 if (!nh_private_.getParam ("do_visibility_test", input_.do_visibility_test))
00293 input_.do_visibility_test = 0;
00294
00295
00296 if (!nh_private_.getParam ("outliers_remove_doubles", input_.outliers_remove_doubles))
00297 input_.outliers_remove_doubles = 1;
00298
00299
00300 if (!nh_private_.getParam ("do_compute_covariance", input_.do_compute_covariance))
00301 input_.do_compute_covariance = 0;
00302
00303
00304 if (!nh_private_.getParam ("debug_verify_tricks", input_.debug_verify_tricks))
00305 input_.debug_verify_tricks = 0;
00306
00307
00308
00309 if (!nh_private_.getParam ("use_ml_weights", input_.use_ml_weights))
00310 input_.use_ml_weights = 0;
00311
00312
00313
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
00343
00344 if (!initialized_)
00345 {
00346
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
00368
00369 if (!initialized_)
00370 {
00371 createCache(scan_msg);
00372
00373
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;
00395 gettimeofday(&start, NULL);
00396
00397
00398
00399
00400
00401
00402
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
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
00429
00430 tf::Transform pr_ch;
00431 createTfFromXYTheta(pr_ch_x, pr_ch_y, pr_ch_a, pr_ch);
00432
00433
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
00444
00445
00446
00447
00448
00449 sm_icp(&input_, &output_);
00450
00451 if (output_.valid)
00452 {
00453
00454
00455
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
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
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
00553
00554 ld_free(prev_ldp_scan_);
00555 prev_ldp_scan_ = curr_ldp_scan;
00556 last_icp_time_ = new_icp_time;
00557
00558
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
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;
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
00623
00624 double r = scan_msg->ranges[i];
00625
00626 if (r > scan_msg->range_min && r < scan_msg->range_max)
00627 {
00628
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;
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
00696
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
00703 pr_ch_x = 0.0;
00704 pr_ch_y = 0.0;
00705 pr_ch_a = 0.0;
00706
00707
00708 if (use_alpha_beta_)
00709 {
00710
00711 pr_ch_x = v_x_ * dt;
00712 pr_ch_y = v_y_ * dt;
00713 pr_ch_a = v_a_ * dt;
00714 }
00715
00716
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
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 }
00766