cloud_accumulator.cc
Go to the documentation of this file.
00001 #define PCL_NO_PRECOMPILE
00002 #include "cloud_accumulator.h"
00003 #include <limits>
00004 #include <pcl_ros/point_cloud.h>
00005 #include <pcl/point_types.h>
00006 #include <pcl_conversions/pcl_conversions.h>
00007 #include <pcl/point_cloud.h>
00008 #include <pcl/visualization/pcl_visualizer.h>
00009 #include <pcl/common/transforms.h>
00010 #include <pcl/filters/passthrough.h>
00011 #include <pcl/filters/voxel_grid.h>
00012 #include <pcl/io/pcd_io.h>
00013 #include <boost/lexical_cast.hpp>
00014 
00015 namespace rc
00016 {
00017 
00018 /****************************** Utility functions ********************************************/
00019 
00020 inline geometry_msgs::TransformStamped myPoseStampedMsgToTF(const geometry_msgs::PoseStamped & msg)
00021 {
00022   geometry_msgs::TransformStamped bt;
00023   bt.transform.rotation = msg.pose.orientation;
00024   bt.transform.translation.x = msg.pose.position.x;
00025   bt.transform.translation.y = msg.pose.position.y;
00026   bt.transform.translation.z = msg.pose.position.z;
00027   bt.header.stamp = msg.header.stamp;
00028   bt.header.frame_id = msg.header.frame_id;
00029   bt.child_frame_id = "camera";
00030   return bt;
00031 }
00032 
00033 
00034 inline Eigen::Affine3f toAffine(const geometry_msgs::TransformStamped& tf_stamped)
00035 {
00036   const geometry_msgs::Vector3& t = tf_stamped.transform.translation;//just abbreviate
00037   const geometry_msgs::Quaternion& q = tf_stamped.transform.rotation;//just abbreviate
00038 
00039   Eigen::Affine3f result = Eigen::Affine3f::Identity();
00040   result.translation() << t.x, t.y, t.z;
00041   Eigen::Quaternionf rot(q.w, q.x, q.y, q.z);
00042   result.rotate(rot);
00043   return result;
00044 }
00045 
00046 
00048 void voxelGridFilter(double size, pointcloud_t::Ptr input, pointcloud_t::Ptr& output)
00049 {
00050   if(size <= 0.0){ output = input; }
00051   else
00052   {
00053     pcl::VoxelGrid<point_t> vgf;
00054     vgf.setInputCloud(input);
00055     vgf.setLeafSize(size, size, size);
00056     vgf.filter(*output);
00057   }
00058 }
00059 
00062 pointcloud_t::Ptr distanceFiltered(double min, double max, const pointcloud_t::ConstPtr input)
00063 {
00064   pointcloud_t::Ptr output(new pointcloud_t());
00065   if(min >= max){ *output = *input; }
00066   else
00067   {
00068     pcl::PassThrough<point_t> pass;
00069     pass.setInputCloud(input);
00070     pass.setFilterFieldName("z");
00071     pass.setFilterLimits(min, max);
00072     pass.filter(*output);
00073   }
00074   return output;
00075 }
00076 /****************************** CloudAccumulator ********************************************/
00077 
00078 CloudAccumulator::CloudAccumulator(double voxelgrid_size_display,
00079                                    double voxelgrid_size_save,
00080                                    double min_distance,
00081                                    double max_distance,
00082                                    std::string output_filename,
00083                                    bool keep_high_res,
00084                                    bool start_paused)
00085   : tf_buffer_(ros::Duration(std::numeric_limits<int>::max(), 0)),
00086     num_poses_(0),
00087     display_cloud_(new pointcloud_t()),
00088     voxelgrid_size_display_(voxelgrid_size_display),
00089     voxelgrid_size_save_(voxelgrid_size_save),
00090     min_distance_(min_distance),
00091     max_distance_(max_distance),
00092     output_filename_(output_filename),
00093     keep_high_res_(keep_high_res),
00094     pause_(start_paused),
00095     viewer_("Roboception Cloud Viewer"),
00096     viewer_thread_(boost::ref(viewer_))
00097 {
00098   viewer_.addCloudToViewer(display_cloud_);//so update can be called
00099   ROS_INFO("Distance filter range: %.2fm - %.2fm", min_distance_, max_distance_);
00100   ROS_INFO("Voxel grid filter for display: %.3fm", voxelgrid_size_display_);
00101   ROS_INFO("Voxel grid filter for saving:  %.3fm", voxelgrid_size_save_);
00102 }
00103 
00104 
00105 void CloudAccumulator::pointCloudCallback(const pointcloud_t::ConstPtr& pointcloud)
00106 {
00107   if(pause_){ return; }
00108   static int counter = 0;
00109   ++counter;
00110   ROS_INFO_THROTTLE(10, "Received %d point cloud%s", counter, counter == 1 ? "" : "s");
00111   double timestamp = pointcloud->header.stamp * 1e-6;//pcl stamps are in milliseconds
00112   ROS_DEBUG("Storing cloud (%.3f)", timestamp);
00113 
00114   pointcloud_t::Ptr cropped_cloud = distanceFiltered(min_distance_, max_distance_, pointcloud);
00115   if(keep_high_res_) { clouds_.push_back(cropped_cloud); }//store for later corrections & saving
00116 
00117   const Eigen::Affine3f transformation = lookupTransform(timestamp);
00118   if(transformation.matrix()(0,0) == transformation.matrix()(0,0)) //not nan
00119   {
00120     pointcloud_t::Ptr transformed_cloud(new pointcloud_t());
00121     pcl::transformPointCloud(*cropped_cloud, *transformed_cloud, transformation);
00122 
00123     boost::mutex::scoped_lock guard(display_cloud_mutex_);
00124     *transformed_cloud += *display_cloud_;
00125     //Voxelgridfilter to speedup display
00126     voxelGridFilter(voxelgrid_size_display_, transformed_cloud, display_cloud_);
00127     viewer_.updateCloudInViewer(display_cloud_);
00128     ROS_INFO_ONCE("Change view to start displaying the clouds.");
00129   }
00130   else if(num_poses_ == 0)
00131   {
00132     ROS_INFO_THROTTLE(5,"Cannot transform current point cloud: No poses were received yet. "
00133                       "Is Stereo INS or SLAM running?");
00134   }
00135   else {
00136     ROS_INFO_THROTTLE(5,"Cannot transform current point cloud: Pose not (yet) received\n"
00137                         "Current cloud stamp: %.3f\n"
00138                         "Latest pose stamp:   %.3f", timestamp, last_pose_stamp_.toSec());
00139   }
00140 }
00141 
00142 
00143 void CloudAccumulator::trajectoryCallback(const nav_msgs::PathConstPtr& path)
00144 {
00145   ROS_INFO("Received Trajectory with %zu poses", path->poses.size());
00146   boost::mutex::scoped_lock guard(tf_buffer_mutex_);
00147   tf_buffer_.clear();
00148   num_poses_ = path->poses.size();
00149   for(size_t i = 0; i < path->poses.size(); ++i)
00150   //for(const geometry_msgs::PoseStamped& pose : path->poses)
00151   {
00152     tf_buffer_.setTransform(myPoseStampedMsgToTF(path->poses[i]), "trajectory");
00153   }
00154 }
00155 
00156 
00157 void CloudAccumulator::poseCallback(const geometry_msgs::PoseStampedConstPtr& current_pose)
00158 {
00159   if(pause_){ return; }
00160   ROS_INFO_ONCE("Received first live pose");
00161   boost::mutex::scoped_lock guard(tf_buffer_mutex_);
00162   tf_buffer_.setTransform(myPoseStampedMsgToTF(*current_pose), "current_pose");
00163   last_pose_stamp_ = current_pose->header.stamp;
00164   ++num_poses_;
00165 }
00166 
00167 
00168 Eigen::Affine3f CloudAccumulator::lookupTransform(double timestamp)
00169 {
00170   ros::Time rosstamp; rosstamp.fromSec(timestamp);//pcl timestamp is in microsec
00171   //cannot use waitForTransform because of the concurrent use of the tf_buffer
00172   try
00173   {
00174     ros::Time endtime = ros::Time::now() + ros::Duration(0.25);
00175     do
00176     {
00177       {//lock-scope
00178         boost::mutex::scoped_lock guard(tf_buffer_mutex_);
00179         if(tf_buffer_.canTransform("world", "camera", rosstamp))
00180         {
00181           geometry_msgs::TransformStamped pose;
00182           Eigen::Affine3f result = toAffine(tf_buffer_.lookupTransform("world", "camera", rosstamp)); //target, source
00183           return result;
00184         }
00185       }
00186       usleep(10000);
00187     }
00188     while(endtime > ros::Time::now());
00189   }
00190   catch(tf2::LookupException ex) { ROS_WARN("%s", ex.what()); }
00191   catch(tf2::ExtrapolationException ex) {  ROS_WARN("%s", ex.what()); }
00192 
00193   Eigen::Affine3f invalid_result;
00194   invalid_result.matrix().setConstant(std::numeric_limits<double>::quiet_NaN());
00195   return invalid_result;
00196 }
00197 
00198 
00199 pointcloud_t::Ptr CloudAccumulator::rebuildCloud()
00200 {
00201   ROS_INFO("(Re-)merging all stored clouds with latest poses.");
00202   int counter = 0;
00203   pointcloud_t::Ptr merged(new pointcloud_t());
00204   for(size_t i = 0; i < clouds_.size(); ++i)
00205   //for(std::map<double, pointcloud_t::ConstPtr>::iterator it = cloud_map_.begin(); it != cloud_map_.end(); ++it)
00206   {
00207     double timestamp = clouds_[i]->header.stamp * 1e-6;//pcl stamps are in milliseconds
00208     const Eigen::Affine3f transformation = lookupTransform(timestamp);
00209     if(transformation.matrix()(0,0) == transformation.matrix()(0,0)) //not nan
00210     {
00211       ++counter;
00212       pointcloud_t transformed_cloud;
00213       pcl::transformPointCloud(*clouds_[i], transformed_cloud, transformation);
00214       *merged += transformed_cloud;
00215       if(counter % 10 == 0){ ROS_INFO("Merged %d of %zu clouds.", counter, clouds_.size()); }
00216     }
00217     else { ROS_DEBUG("Skipped cloud at %.3f in merge, cannot get its position", timestamp); }
00218   }
00219   ROS_INFO("Merged %d/%zu clouds.", counter, clouds_.size());
00220 
00221   pointcloud_t::Ptr voxel_filtered(new pointcloud_t());
00222   voxelGridFilter(voxelgrid_size_save_, merged, voxel_filtered);
00223   return voxel_filtered;
00224 }
00225 
00226 
00227 bool CloudAccumulator::saveCloud(std_srvs::Empty::Request &req, std_srvs::Empty::Request &resp)
00228 {
00229   if(keep_high_res_) // consider slam corrections
00230   {
00231     pointcloud_t::Ptr merged = rebuildCloud();
00232     pcl::io::savePCDFileASCII(output_filename_, *merged);
00233 
00234     //Now show the saved cloud
00235     //(it will quickly get downfiltered in the point cloud callback though if not paused)
00236     boost::mutex::scoped_lock guard(display_cloud_mutex_);
00237     display_cloud_ = merged;
00238     viewer_.addCloudToViewer(display_cloud_);
00239   }
00240   else { pcl::io::savePCDFileASCII(output_filename_, *display_cloud_); }//just save what we have
00241 
00242   ROS_INFO("Saved point cloud to %s", output_filename_.c_str());
00243   return true;
00244 }
00245 
00246 
00247 bool CloudAccumulator::togglePause(std_srvs::Empty::Request &req, std_srvs::Empty::Request &resp)
00248 {
00249   pause_ = !pause_;
00250   return true;
00251 }
00252 
00253 }//namespace rc


rc_cloud_accumulator
Author(s): Felix Endres
autogenerated on Thu Jun 6 2019 19:56:20