mpai227

laser_scan_matcher_odom.cpp

Jul 22nd, 2015
1,124
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2.  
  3. /*
  4. * Copyright (c) 2011, Ivan Dryanovski, William Morris
  5. * All rights reserved.
  6. *
  7. * *** Modified also to publish horizontal velocities: ***
  8. * *** WARNING: This is not the original laser_scan_matcher from CCNY, ***
  9. * *** it's been slightly modified in a dirty fashion. ***
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions are met:
  13. *
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the CCNY Robotics Lab nor the names of its
  20. * contributors may be used to endorse or promote products derived from
  21. * this software without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35.  
  36. /* This package uses Canonical Scan Matcher [1], written by
  37. * Andrea Censi
  38. *
  39. * [1] A. Censi, "An ICP variant using a point-to-line metric"
  40. * Proceedings of the IEEE International Conference
  41. * on Robotics and Automation (ICRA), 2008
  42. */
  43.  
  44. #include <laser_scan_matcher/laser_scan_matcher_odom.h>
  45. #include <pcl_conversions/pcl_conversions.h>
  46.  
  47. namespace scan_tools1
  48. {
  49.  
  50. LaserScanMatcher::LaserScanMatcher(ros::NodeHandle nh, ros::NodeHandle nh_private):
  51. nh_(nh),
  52. nh_private_(nh_private)
  53. {
  54. ROS_INFO("Starting LaserScanMatcher");
  55.  
  56. // **** init parameters
  57.  
  58. initParams();
  59.  
  60. // **** state variables
  61.  
  62. initialized_ = false;
  63. received_imu_ = false;
  64. received_odom_ = false;
  65.  
  66. w2b_.setIdentity();
  67.  
  68. v_x_ = 0;
  69. v_y_ = 0;
  70. v_a_ = 0;
  71.  
  72. vel_x_ = 0;
  73. vel_y_ = 0;
  74. vel_a_ = 0;
  75. last_vel_x_ = 0;
  76. last_vel_y_ = 0;
  77. last_vel_a_ = 0;
  78.  
  79. input_.laser[0] = 0.0;
  80. input_.laser[1] = 0.0;
  81. input_.laser[2] = 0.0;
  82.  
  83. // *** subscribers
  84.  
  85. if (use_cloud_input_)
  86. {
  87. cloud_subscriber_ = nh_.subscribe(
  88. cloud_topic_, 1, &LaserScanMatcher::cloudCallback, this);
  89.  
  90. }
  91. else
  92. {
  93. scan_subscriber_ = nh_.subscribe(
  94. scan_topic_, 1, &LaserScanMatcher::scanCallback, this);
  95. }
  96.  
  97. if (use_imu_)
  98. {
  99. imu_subscriber_ = nh_.subscribe(
  100. imu_topic_, 1, &LaserScanMatcher::imuCallback, this);
  101. }
  102.  
  103. if (use_odom_)
  104. {
  105. odom_subscriber_ = nh_.subscribe(
  106. odom_topic_, 1, &LaserScanMatcher::odomCallback, this);
  107. }
  108.  
  109. // **** pose publisher
  110. if (publish_pose_)
  111. {
  112. pose_publisher_ = nh_.advertise<geometry_msgs::Pose2D>(
  113. pose_topic_, 5);
  114. }
  115.  
  116. if (publish_vel_)
  117. {
  118. vel_publisher_ = nh_.advertise<nav_msgs::Odometry>(odom_topic_, 100);
  119. }
  120. }
  121.  
  122.  
  123. LaserScanMatcher::~LaserScanMatcher()
  124. {
  125.  
  126. }
  127.  
  128. void LaserScanMatcher::initParams()
  129. {
  130. if (!nh_private_.getParam ("base_frame", base_frame_))
  131. base_frame_ = "base_link";
  132. if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
  133. fixed_frame_ = "world";
  134.  
  135. // **** input type - laser scan, or point clouds?
  136. // if false, will subscrive to LaserScan msgs on /scan.
  137. // if true, will subscrive to PointCloud2 msgs on /cloud
  138.  
  139. if (!nh_private_.getParam ("use_cloud_input", use_cloud_input_))
  140. use_cloud_input_= false;
  141.  
  142. if (use_cloud_input_)
  143. {
  144. if (!nh_private_.getParam ("cloud_range_min", cloud_range_min_))
  145. cloud_range_min_ = 0.1;
  146. if (!nh_private_.getParam ("cloud_range_max", cloud_range_max_))
  147. cloud_range_max_ = 50.0;
  148.  
  149. input_.min_reading = cloud_range_min_;
  150. input_.max_reading = cloud_range_max_;
  151. }
  152.  
  153. // **** What predictions are available to speed up the ICP?
  154. // 1) imu - [theta] from imu yaw angle - /odom topic
  155. // 2) odom - [x, y, theta] from wheel odometry - /imu topic
  156. // 3) alpha_beta - [x, y, theta] from simple tracking filter - no topic req.
  157. // If more than one is enabled, priority is imu > odom > alpha_beta
  158.  
  159. if (!nh_private_.getParam ("use_imu", use_imu_))
  160. use_imu_ = false;
  161. if (!nh_private_.getParam ("use_odom", use_odom_))
  162. use_odom_ = false;
  163. if (!nh_private_.getParam ("use_alpha_beta", use_alpha_beta_))
  164. use_alpha_beta_ = true;
  165. if (!nh_private_.getParam ("publish_vel", publish_vel_))
  166. publish_vel_ = true;
  167.  
  168. // **** How to publish the output?
  169. // tf (fixed_frame->base_frame),
  170. // pose message (pose of base frame in the fixed frame)
  171.  
  172. if (!nh_private_.getParam ("publish_tf", publish_tf_))
  173. publish_tf_ = true;
  174. if (!nh_private_.getParam ("publish_pose", publish_pose_))
  175. publish_pose_ = true;
  176.  
  177. if (!nh_private_.getParam ("alpha", alpha_))
  178. alpha_ = 1.0;
  179. if (!nh_private_.getParam ("beta", beta_))
  180. beta_ = 0.8;
  181.  
  182. // **** CSM parameters - comments copied from algos.h (by Andrea Censi)
  183.  
  184. // Maximum angular displacement between scans
  185. if (!nh_private_.getParam ("max_angular_correction_deg", input_.max_angular_correction_deg))
  186. input_.max_angular_correction_deg = 45.0;
  187.  
  188. // Maximum translation between scans (m)
  189. if (!nh_private_.getParam ("max_linear_correction", input_.max_linear_correction))
  190. input_.max_linear_correction = 0.50;
  191.  
  192. // Maximum ICP cycle iterations
  193. if (!nh_private_.getParam ("max_iterations", input_.max_iterations))
  194. input_.max_iterations = 10;
  195.  
  196. // A threshold for stopping (m)
  197. if (!nh_private_.getParam ("epsilon_xy", input_.epsilon_xy))
  198. input_.epsilon_xy = 0.000001;
  199.  
  200. // A threshold for stopping (rad)
  201. if (!nh_private_.getParam ("epsilon_theta", input_.epsilon_theta))
  202. input_.epsilon_theta = 0.000001;
  203.  
  204. // Maximum distance for a correspondence to be valid
  205. if (!nh_private_.getParam ("max_correspondence_dist", input_.max_correspondence_dist))
  206. input_.max_correspondence_dist = 0.3;
  207.  
  208. // Noise in the scan (m)
  209. if (!nh_private_.getParam ("sigma", input_.sigma))
  210. input_.sigma = 0.010;
  211.  
  212. // Use smart tricks for finding correspondences.
  213. if (!nh_private_.getParam ("use_corr_tricks", input_.use_corr_tricks))
  214. input_.use_corr_tricks = 1;
  215.  
  216. // Restart: Restart if error is over threshold
  217. if (!nh_private_.getParam ("restart", input_.restart))
  218. input_.restart = 0;
  219.  
  220. // Restart: Threshold for restarting
  221. if (!nh_private_.getParam ("restart_threshold_mean_error", input_.restart_threshold_mean_error))
  222. input_.restart_threshold_mean_error = 0.01;
  223.  
  224. // Restart: displacement for restarting. (m)
  225. if (!nh_private_.getParam ("restart_dt", input_.restart_dt))
  226. input_.restart_dt = 1.0;
  227.  
  228. // Restart: displacement for restarting. (rad)
  229. if (!nh_private_.getParam ("restart_dtheta", input_.restart_dtheta))
  230. input_.restart_dtheta = 0.1;
  231.  
  232. // Max distance for staying in the same clustering
  233. if (!nh_private_.getParam ("clustering_threshold", input_.clustering_threshold))
  234. input_.clustering_threshold = 0.25;
  235.  
  236. // Number of neighbour rays used to estimate the orientation
  237. if (!nh_private_.getParam ("orientation_neighbourhood", input_.orientation_neighbourhood))
  238. input_.orientation_neighbourhood = 20;
  239.  
  240. // If 0, it's vanilla ICP
  241. if (!nh_private_.getParam ("use_point_to_line_distance", input_.use_point_to_line_distance))
  242. input_.use_point_to_line_distance = 1;
  243.  
  244. // Discard correspondences based on the angles
  245. if (!nh_private_.getParam ("do_alpha_test", input_.do_alpha_test))
  246. input_.do_alpha_test = 0;
  247.  
  248. // Discard correspondences based on the angles - threshold angle, in degrees
  249. if (!nh_private_.getParam ("do_alpha_test_thresholdDeg", input_.do_alpha_test_thresholdDeg))
  250. input_.do_alpha_test_thresholdDeg = 20.0;
  251.  
  252. // Percentage of correspondences to consider: if 0.9,
  253. // always discard the top 10% of correspondences with more error
  254. if (!nh_private_.getParam ("outliers_maxPerc", input_.outliers_maxPerc))
  255. input_.outliers_maxPerc = 0.90;
  256.  
  257. // Parameters describing a simple adaptive algorithm for discarding.
  258. // 1) Order the errors.
  259. // 2) Choose the percentile according to outliers_adaptive_order.
  260. // (if it is 0.7, get the 70% percentile)
  261. // 3) Define an adaptive threshold multiplying outliers_adaptive_mult
  262. // with the value of the error at the chosen percentile.
  263. // 4) Discard correspondences over the threshold.
  264. // This is useful to be conservative; yet remove the biggest errors.
  265. if (!nh_private_.getParam ("outliers_adaptive_order", input_.outliers_adaptive_order))
  266. input_.outliers_adaptive_order = 0.7;
  267.  
  268. if (!nh_private_.getParam ("outliers_adaptive_mult", input_.outliers_adaptive_mult))
  269. input_.outliers_adaptive_mult = 2.0;
  270.  
  271. //If you already have a guess of the solution, you can compute the polar angle
  272. // of the points of one scan in the new position. If the polar angle is not a monotone
  273. // function of the readings index, it means that the surface is not visible in the
  274. // next position. If it is not visible, then we don't use it for matching.
  275. if (!nh_private_.getParam ("do_visibility_test", input_.do_visibility_test))
  276. input_.do_visibility_test = 0;
  277.  
  278. // no two points in laser_sens can have the same corr.
  279. if (!nh_private_.getParam ("outliers_remove_doubles", input_.outliers_remove_doubles))
  280. input_.outliers_remove_doubles = 1;
  281.  
  282. // If 1, computes the covariance of ICP using the method http://purl.org/censi/2006/icpcov
  283. if (!nh_private_.getParam ("do_compute_covariance", input_.do_compute_covariance))
  284. input_.do_compute_covariance = 0;
  285.  
  286. // Checks that find_correspondences_tricks gives the right answer
  287. if (!nh_private_.getParam ("debug_verify_tricks", input_.debug_verify_tricks))
  288. input_.debug_verify_tricks = 0;
  289.  
  290. // If 1, the field 'true_alpha' (or 'alpha') in the first scan is used to compute the
  291. // incidence beta, and the factor (1/cos^2(beta)) used to weight the correspondence.");
  292. if (!nh_private_.getParam ("use_ml_weights", input_.use_ml_weights))
  293. input_.use_ml_weights = 0;
  294.  
  295. // If 1, the field 'readings_sigma' in the second scan is used to weight the
  296. // correspondence by 1/sigma^2
  297. if (!nh_private_.getParam ("use_sigma_weights", input_.use_sigma_weights))
  298. input_.use_sigma_weights = 0;
  299. }
  300.  
  301. void LaserScanMatcher::imuCallback (const sensor_msgs::ImuPtr& imu_msg)
  302. {
  303. boost::mutex::scoped_lock(mutex_);
  304. latest_imu_yaw_ = getYawFromQuaternion(imu_msg->orientation);
  305. if (!received_imu_)
  306. {
  307. last_imu_yaw_ = getYawFromQuaternion(imu_msg->orientation);
  308. received_imu_ = true;
  309. }
  310. }
  311.  
  312. void LaserScanMatcher::odomCallback (const nav_msgs::Odometry::ConstPtr& odom_msg)
  313. {
  314. boost::mutex::scoped_lock(mutex_);
  315. latest_odom_ = *odom_msg;
  316. if (!received_odom_)
  317. {
  318. last_odom_ = *odom_msg;
  319. received_odom_ = true;
  320. }
  321. }
  322.  
  323. void LaserScanMatcher::cloudCallback (const PointCloudT::ConstPtr& cloud)
  324. {
  325. // **** if first scan, cache the tf from base to the scanner
  326. std_msgs::Header cloud_header = pcl_conversions::fromPCL(cloud->header);
  327.  
  328. if (!initialized_)
  329. {
  330. // cache the static tf from base to laser
  331. if (!getBaseToLaserTf(cloud->header.frame_id))
  332. {
  333. ROS_WARN("ScanMatcher: Skipping scan");
  334. return;
  335. }
  336.  
  337. PointCloudToLDP(cloud, prev_ldp_scan_);
  338. last_icp_time_ = cloud_header.stamp;
  339. last_imu_yaw_ = latest_imu_yaw_;
  340. last_odom_ = latest_odom_;
  341. initialized_ = true;
  342. }
  343.  
  344. LDP curr_ldp_scan;
  345. PointCloudToLDP(cloud, curr_ldp_scan);
  346. processScan(curr_ldp_scan, cloud_header.stamp);
  347. }
  348.  
  349. void LaserScanMatcher::scanCallback (const sensor_msgs::LaserScan::ConstPtr& scan_msg)
  350. {
  351. // **** if first scan, cache the tf from base to the scanner
  352.  
  353. if (!initialized_)
  354. {
  355. createCache(scan_msg); // caches the sin and cos of all angles
  356.  
  357. // cache the static tf from base to laser
  358. if (!getBaseToLaserTf(scan_msg->header.frame_id))
  359. {
  360. ROS_WARN("ScanMatcher: Skipping scan");
  361. return;
  362. }
  363.  
  364. laserScanToLDP(scan_msg, prev_ldp_scan_);
  365. last_icp_time_ = scan_msg->header.stamp;
  366. last_imu_yaw_ = latest_imu_yaw_;
  367. last_odom_ = latest_odom_;
  368. initialized_ = true;
  369. }
  370.  
  371. LDP curr_ldp_scan;
  372. laserScanToLDP(scan_msg, curr_ldp_scan);
  373. processScan(curr_ldp_scan, scan_msg->header.stamp);
  374. }
  375.  
  376. void LaserScanMatcher::processScan(LDP& curr_ldp_scan, const ros::Time& time)
  377. {
  378. struct timeval start, end; // used for timing
  379. gettimeofday(&start, NULL);
  380.  
  381. // CSM is used in the following way:
  382. // The scans are always in the laser frame
  383. // The reference scan (prevLDPcan_) has a pose of 0
  384. // The new scan (currLDPScan) has a pose equal to the movement
  385. // of the laser in the laser frame since the last scan
  386. // The computed correction is then propagated using the tf machinery
  387.  
  388. prev_ldp_scan_->odometry[0] = 0;
  389. prev_ldp_scan_->odometry[1] = 0;
  390. prev_ldp_scan_->odometry[2] = 0;
  391.  
  392. prev_ldp_scan_->estimate[0] = 0;
  393. prev_ldp_scan_->estimate[1] = 0;
  394. prev_ldp_scan_->estimate[2] = 0;
  395.  
  396. prev_ldp_scan_->true_pose[0] = 0;
  397. prev_ldp_scan_->true_pose[1] = 0;
  398. prev_ldp_scan_->true_pose[2] = 0;
  399.  
  400. input_.laser_ref = prev_ldp_scan_;
  401. input_.laser_sens = curr_ldp_scan;
  402.  
  403. // **** estimated change since last scan
  404.  
  405. ros::Time new_icp_time = ros::Time::now();
  406. ros::Duration dur = new_icp_time - last_icp_time_;
  407. double dt = dur.toSec();
  408.  
  409. double pr_ch_x, pr_ch_y, pr_ch_a;
  410. getPrediction(pr_ch_x, pr_ch_y, pr_ch_a, dt);
  411.  
  412. // the predicted change of the laser's position, in the base frame
  413.  
  414. tf::Transform pr_ch;
  415. createTfFromXYTheta(pr_ch_x, pr_ch_y, pr_ch_a, pr_ch);
  416.  
  417. // the predicted change of the laser's position, in the laser frame
  418.  
  419. tf::Transform pr_ch_l;
  420. pr_ch_l = laser_to_base_ * pr_ch * base_to_laser_;
  421.  
  422. input_.first_guess[0] = pr_ch_l.getOrigin().getX();
  423. input_.first_guess[1] = pr_ch_l.getOrigin().getY();
  424. input_.first_guess[2] = getYawFromQuaternion(pr_ch_l.getRotation());
  425.  
  426. /*
  427. printf("%f, %f, %f\n", input_.first_guess[0],
  428. input_.first_guess[1],
  429. input_.first_guess[2]);
  430. */
  431. // *** scan match - using point to line icp from CSM
  432.  
  433. sm_icp(&input_, &output_);
  434.  
  435. if (output_.valid)
  436. {
  437. // the correction of the laser's position, in the laser frame
  438.  
  439. //printf("%f, %f, %f\n", output_.x[0], output_.x[1], output_.x[2]);
  440.  
  441. tf::Transform corr_ch_l;
  442. createTfFromXYTheta(output_.x[0], output_.x[1], output_.x[2], corr_ch_l);
  443.  
  444. // the correction of the base's position, in the world frame
  445.  
  446. tf::Transform corr_ch = base_to_laser_ * corr_ch_l * laser_to_base_;
  447.  
  448. if(use_alpha_beta_ || publish_vel_)
  449. {
  450. tf::Transform w2b_new = w2b_ * corr_ch;
  451.  
  452. double dx = w2b_new.getOrigin().getX() - w2b_.getOrigin().getX();
  453. double dy = w2b_new.getOrigin().getY() - w2b_.getOrigin().getY();
  454. double da = getYawFromQuaternion(w2b_new.getRotation()) -
  455. getYawFromQuaternion(w2b_.getRotation());
  456.  
  457. double r_x = dx - pr_ch_x;
  458. double r_y = dy - pr_ch_y;
  459. double r_a = da - pr_ch_a;
  460.  
  461. double x = w2b_.getOrigin().getX();
  462. double y = w2b_.getOrigin().getY();
  463. double a = getYawFromQuaternion(w2b_.getRotation());
  464.  
  465. double x_new = (x + pr_ch_x) + alpha_ * r_x;
  466. double y_new = (y + pr_ch_y) + alpha_ * r_y;
  467. double a_new = (a + pr_ch_a) + alpha_ * r_a;
  468.  
  469. createTfFromXYTheta(x_new, y_new, a_new, w2b_);
  470.  
  471. if (dt != 0.0)
  472. {
  473. v_x_ = v_x_ + (beta_ / dt) * r_x;
  474. v_y_ = v_y_ + (beta_ / dt) * r_y;
  475. v_a_ = v_a_ + (beta_ / dt) * r_a;
  476.  
  477. //TODO
  478. //Low-pass filter, 4.9Hz cutoff first order Butterworth
  479. //Warning: non-linear phase, be careful in control applications
  480. //http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
  481. double vx_raw = dx / dt / 1.031426266;
  482. double vy_raw = dy / dt / 1.031426266;
  483. double va_raw = da / dt / 1.031426266;
  484.  
  485. vel_x_ = vx_raw + last_vel_x_ - 0.9390625058 * vel_x_;
  486. vel_y_ = vy_raw + last_vel_y_ - 0.9390625058 * vel_y_;
  487. vel_a_ = va_raw + last_vel_a_ - 0.9390625058 * vel_a_;
  488.  
  489. last_vel_x_ = vx_raw;
  490. last_vel_y_ = vy_raw;
  491. last_vel_a_ = va_raw;
  492. }
  493. }
  494. else
  495. {
  496. w2b_ = w2b_ * corr_ch;
  497. }
  498.  
  499. // **** publish
  500.  
  501. if (publish_pose_)
  502. {
  503. geometry_msgs::Pose2D::Ptr pose_msg;
  504. pose_msg = boost::make_shared<geometry_msgs::Pose2D>();
  505. pose_msg->x = w2b_.getOrigin().getX();
  506. pose_msg->y = w2b_.getOrigin().getY();
  507. pose_msg->theta = getYawFromQuaternion(w2b_.getRotation());
  508. pose_publisher_.publish(pose_msg);
  509. }
  510. if (publish_tf_)
  511. {
  512. tf::StampedTransform transform_msg (w2b_, time, fixed_frame_, base_frame_);
  513. tf_broadcaster_.sendTransform (transform_msg);
  514. }
  515. if (publish_vel_)
  516. {
  517. odom.header.stamp = time;
  518. odom.header.frame_id = fixed_frame_;
  519.  
  520. odom.pose.pose.orientation.x = 0;
  521. odom.pose.pose.orientation.y = 0;
  522. odom.pose.pose.orientation.z = 0;
  523. odom.pose.pose.orientation.w = 1;
  524.  
  525. odom.child_frame_id = base_frame_;
  526. odom.twist.twist.linear.x = vel_x_;
  527. odom.twist.twist.linear.y = vel_y_;
  528. odom.twist.twist.angular.z = vel_a_;
  529.  
  530. vel_publisher_.publish(odom);
  531. }
  532. }
  533. else
  534. {
  535. ROS_WARN("Error in scan matching");
  536.  
  537. v_x_ = 0.0;
  538. v_y_ = 0.0;
  539. v_a_ = 0.0;
  540. }
  541.  
  542. // **** swap old and new
  543.  
  544. ld_free(prev_ldp_scan_);
  545. prev_ldp_scan_ = curr_ldp_scan;
  546. last_icp_time_ = new_icp_time;
  547.  
  548. // **** statistics
  549.  
  550. gettimeofday(&end, NULL);
  551. double dur_total = ((end.tv_sec * 1000000 + end.tv_usec ) -
  552. (start.tv_sec * 1000000 + start.tv_usec)) / 1000.0;
  553. ROS_DEBUG("scan matcher total duration: %.1f ms", dur_total);
  554. }
  555.  
  556. void LaserScanMatcher::PointCloudToLDP(const PointCloudT::ConstPtr& cloud,
  557. LDP& ldp)
  558. {
  559. unsigned int n = cloud->width * cloud->height ;
  560. ldp = ld_alloc_new(n);
  561.  
  562. for (unsigned int i = 0; i < n; i++)
  563. {
  564. // calculate position in laser frame
  565.  
  566. if (is_nan(cloud->points[i].x) || is_nan(cloud->points[i].y))
  567. {
  568. ROS_WARN("Laser Scan Matcher: Cloud input contains NaN values. \
  569. Please use a filtered cloud input.");
  570. }
  571. else
  572. {
  573. double r = sqrt(cloud->points[i].x * cloud->points[i].x +
  574. cloud->points[i].y * cloud->points[i].y);
  575.  
  576. if (r > cloud_range_min_ && r < cloud_range_max_)
  577. {
  578. ldp->valid[i] = 1;
  579. ldp->readings[i] = r;
  580. }
  581. else
  582. {
  583. ldp->valid[i] = 0;
  584. ldp->readings[i] = -1; // for invalid range
  585. }
  586. }
  587.  
  588. ldp->theta[i] = atan2(cloud->points[i].y, cloud->points[i].x);
  589. ldp->cluster[i] = -1;
  590. }
  591.  
  592. ldp->min_theta = ldp->theta[0];
  593. ldp->max_theta = ldp->theta[n-1];
  594.  
  595. ldp->odometry[0] = 0.0;
  596. ldp->odometry[1] = 0.0;
  597. ldp->odometry[2] = 0.0;
  598.  
  599. ldp->true_pose[0] = 0.0;
  600. ldp->true_pose[1] = 0.0;
  601. ldp->true_pose[2] = 0.0;
  602. }
  603.  
  604. void LaserScanMatcher::laserScanToLDP(const sensor_msgs::LaserScan::ConstPtr& scan_msg,
  605. LDP& ldp)
  606. {
  607. unsigned int n = scan_msg->ranges.size();
  608. ldp = ld_alloc_new(n);
  609.  
  610. for (unsigned int i = 0; i < n; i++)
  611. {
  612. // calculate position in laser frame
  613.  
  614. double r = scan_msg->ranges[i];
  615.  
  616. if (r > scan_msg->range_min && r < scan_msg->range_max)
  617. {
  618. // fill in laser scan data
  619.  
  620. ldp->valid[i] = 1;
  621. ldp->readings[i] = r;
  622. }
  623. else
  624. {
  625. ldp->valid[i] = 0;
  626. ldp->readings[i] = -1; // for invalid range
  627. }
  628.  
  629. ldp->theta[i] = scan_msg->angle_min + i * scan_msg->angle_increment;
  630.  
  631. ldp->cluster[i] = -1;
  632. }
  633.  
  634. ldp->min_theta = ldp->theta[0];
  635. ldp->max_theta = ldp->theta[n-1];
  636.  
  637. ldp->odometry[0] = 0.0;
  638. ldp->odometry[1] = 0.0;
  639. ldp->odometry[2] = 0.0;
  640.  
  641. ldp->true_pose[0] = 0.0;
  642. ldp->true_pose[1] = 0.0;
  643. ldp->true_pose[2] = 0.0;
  644. }
  645.  
  646. void LaserScanMatcher::createCache (const sensor_msgs::LaserScan::ConstPtr& scan_msg)
  647. {
  648. a_cos_.clear();
  649. a_sin_.clear();
  650.  
  651. for (unsigned int i = 0; i < scan_msg->ranges.size(); ++i)
  652. {
  653. double angle = scan_msg->angle_min + i * scan_msg->angle_increment;
  654. a_cos_.push_back(cos(angle));
  655. a_sin_.push_back(sin(angle));
  656. }
  657.  
  658. input_.min_reading = scan_msg->range_min;
  659. input_.max_reading = scan_msg->range_max;
  660. }
  661.  
  662. bool LaserScanMatcher::getBaseToLaserTf (const std::string& frame_id)
  663. {
  664. ros::Time t = ros::Time::now();
  665.  
  666. tf::StampedTransform base_to_laser_tf;
  667. try
  668. {
  669. tf_listener_.waitForTransform(
  670. base_frame_, frame_id, t, ros::Duration(1.0));
  671. tf_listener_.lookupTransform (
  672. base_frame_, frame_id, t, base_to_laser_tf);
  673. }
  674. catch (tf::TransformException ex)
  675. {
  676. ROS_WARN("ScanMatcher: Could not get initial laser transform(%s)", ex.what());
  677. return false;
  678. }
  679. base_to_laser_ = base_to_laser_tf;
  680. laser_to_base_ = base_to_laser_.inverse();
  681.  
  682. return true;
  683. }
  684.  
  685. // returns the predicted change in pose (in fixed frame)
  686. // since the last time we did icp
  687. void LaserScanMatcher::getPrediction(double& pr_ch_x, double& pr_ch_y,
  688. double& pr_ch_a, double dt)
  689. {
  690. boost::mutex::scoped_lock(mutex_);
  691.  
  692. // **** base case - no input available, use zero-motion model
  693. pr_ch_x = 0.0;
  694. pr_ch_y = 0.0;
  695. pr_ch_a = 0.0;
  696.  
  697. // **** use alpha-beta tracking (const. vel. model)
  698. if (use_alpha_beta_)
  699. {
  700. // estmate change in fixed frame, using fixed velocity
  701. pr_ch_x = v_x_ * dt; // in fixed frame
  702. pr_ch_y = v_y_ * dt;
  703. pr_ch_a = v_a_ * dt;
  704. }
  705.  
  706. // **** use wheel odometry
  707. if (use_odom_ && received_odom_)
  708. {
  709. pr_ch_x = latest_odom_.pose.pose.position.x -
  710. last_odom_.pose.pose.position.x;
  711.  
  712. pr_ch_y = latest_odom_.pose.pose.position.y -
  713. last_odom_.pose.pose.position.y;
  714.  
  715. pr_ch_a = getYawFromQuaternion(latest_odom_.pose.pose.orientation) -
  716. getYawFromQuaternion(last_odom_.pose.pose.orientation);
  717.  
  718. last_odom_ = latest_odom_;
  719. }
  720.  
  721. // **** use imu
  722. if (use_imu_ && received_imu_)
  723. {
  724. pr_ch_a = latest_imu_yaw_ - last_imu_yaw_;
  725. last_imu_yaw_ = latest_imu_yaw_;
  726. }
  727. }
  728.  
  729. double LaserScanMatcher::getYawFromQuaternion(
  730. const tf::Quaternion& quaternion)
  731. {
  732. double temp, yaw;
  733. tf::Matrix3x3 m(quaternion);
  734. m.getRPY(temp, temp, yaw);
  735. return yaw;
  736. }
  737.  
  738. double LaserScanMatcher::getYawFromQuaternion(
  739. const geometry_msgs::Quaternion& quaternion)
  740. {
  741. tf::Quaternion q;
  742. tf::quaternionMsgToTF(quaternion, q);
  743. return getYawFromQuaternion(q);
  744. }
  745.  
  746. void LaserScanMatcher::createTfFromXYTheta(
  747. double x, double y, double theta, tf::Transform& t)
  748. {
  749. t.setOrigin(tf::Vector3(x, y, 0.0));
  750. tf::Quaternion q;
  751. q.setRPY(0.0, 0.0, theta);
  752. t.setRotation(q);
  753. }
  754.  
  755. } //namespace
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×