pointcloud_mapper_for_slam.cpp
Go to the documentation of this file.
00001 #include <ros/ros.h>
00002 #include <iostream>
00003 #include <fstream>
00004 #include <pcl_ros/point_cloud.h>
00005 #include <pcl_ros/transforms.h>
00006 #include <tf/transform_listener.h>
00007 #include <pcl/point_types.h>
00008 #include <pcl/filters/voxel_grid.h>
00009 #include <pcl/filters/passthrough.h>
00010 #include <pcl/filters/statistical_outlier_removal.h>
00011 #include <sys/stat.h>
00012 
00017 class PointCloudMapper
00018 {
00019 public:
00020 
00021   typedef pcl::PointXYZRGB                  Point;
00022   typedef pcl::PointCloud<pcl::PointXYZRGB> PointCloud;
00023 
00024   PointCloudMapper() :
00025     nh_(), nh_priv_("~")
00026   {
00027     // Read the parameters from the parameter server (set defaults)
00028     nh_priv_.param("graph_vertices_file", graph_vertices_file_, std::string("graph_vertices.txt"));
00029     nh_priv_.param("graph_blocking_file", graph_blocking_file_, std::string(".block.txt"));
00030     nh_priv_.param("x_filter_min", x_filter_min_, -2.0);
00031     nh_priv_.param("x_filter_max", x_filter_max_, 2.0);
00032     nh_priv_.param("y_filter_min", y_filter_min_, -2.0);
00033     nh_priv_.param("y_filter_max", y_filter_max_, 2.0);
00034     nh_priv_.param("z_filter_min", z_filter_min_, 0.2);
00035     nh_priv_.param("z_filter_max", z_filter_max_, 2.0);
00036     nh_priv_.param("voxel_size", voxel_size_, 0.1);
00037     nh_priv_.param("mean_k", mean_k_, 50);
00038     nh_priv_.param("std_dev_thresh", std_dev_thresh_, 1.0);
00039     nh_priv_.param("filter_map", filter_map_, true);
00040 
00041     cloud_sub_ = nh_.subscribe<sensor_msgs::PointCloud2>("input", 10, &PointCloudMapper::callback, this);
00042     bool latched = true;
00043     cloud_pub_ = nh_priv_.advertise<PointCloud>("output", 1, latched);
00044     pub_timer_ = nh_.createWallTimer(ros::WallDuration(3.0), 
00045                               &PointCloudMapper::timerCallback, this);
00046   }
00047 
00048   void callback(const sensor_msgs::PointCloud2ConstPtr& cloud)
00049   {
00050     // Copy the cloud message
00051     sensor_msgs::PointCloud2 cur_cloud = *cloud;
00052 
00053     // Filter map if required
00054     if(filter_map_)
00055     {
00056       // Get the cloud
00057       PointCloud pcl_cloud;
00058       pcl::fromROSMsg(cur_cloud, pcl_cloud);
00059 
00060       // Filter
00061       PointCloud::Ptr cloud_downsampled = filter(pcl_cloud.makeShared());
00062       pcl_cloud = *cloud_downsampled;
00063 
00064       // Convert again to 
00065       pcl::toROSMsg(pcl_cloud, cur_cloud);
00066       
00067     }
00068     // Insert the cloud into the list
00069     pointcloud_list_.push_back(cur_cloud);
00070   }
00071 
00072   void timerCallback(const ros::WallTimerEvent& event)
00073   {
00074     // Detect subscribers
00075     if (cloud_pub_.getNumSubscribers() > 0)
00076     {
00077       // Initialize point cloud
00078       PointCloud accumulated_cloud;
00079 
00080       // Wait until the blocking file disappear
00081       while(fileExists(graph_blocking_file_.c_str()))
00082       {
00083         ROS_WARN_THROTTLE(2, "[PointCloudMapper:] Graph blocking file detected, waiting...");
00084         ros::Duration(0.5).sleep();
00085       }
00086       ROS_INFO("[PointCloudMapper:] Processing SLAM graph and pointclouds.");
00087 
00088       // Read the file
00089       std::vector<OdomMsg> graph_vertices;
00090       std::ifstream file(graph_vertices_file_.c_str());
00091       for(std::string line; std::getline(file, line);)
00092       {
00093         // Get the line values
00094         std::string s;
00095         std::istringstream f(line);
00096         std::vector<std::string> line_values;
00097         while (std::getline(f, s, ','))
00098           line_values.push_back(s);
00099 
00100         // Convert string values to real
00101         if(line_values.size() == 12)
00102         {
00103           OdomMsg odom_msg;
00104 
00105           std::ostringstream s_t, s_x, s_y, s_z, s_qx, s_qy, s_qz, s_qw;
00106           s_t << std::fixed << std::setprecision(9) << line_values.at(0);
00107           s_x << std::fixed << std::setprecision(7) << line_values.at(5);
00108           s_y << std::fixed << std::setprecision(7) << line_values.at(6);
00109           s_z << std::fixed << std::setprecision(7) << line_values.at(7);
00110           s_qx << std::fixed << std::setprecision(7) << line_values.at(8);
00111           s_qy << std::fixed << std::setprecision(7) << line_values.at(9);
00112           s_qz << std::fixed << std::setprecision(7) << line_values.at(10);
00113           s_qw << std::fixed << std::setprecision(7) << line_values.at(11);
00114 
00115           odom_msg.timestamp = boost::lexical_cast<double>(s_t.str());
00116           odom_msg.id = boost::lexical_cast<int>(line_values.at(1));
00117           odom_msg.fixed_frame = line_values.at(3);
00118           odom_msg.base_link_frame = line_values.at(4);
00119           odom_msg.x = boost::lexical_cast<double>(s_x.str());
00120           odom_msg.y = boost::lexical_cast<double>(s_y.str());
00121           odom_msg.z = boost::lexical_cast<double>(s_z.str());
00122           odom_msg.qx = boost::lexical_cast<double>(s_qx.str());
00123           odom_msg.qy = boost::lexical_cast<double>(s_qy.str());
00124           odom_msg.qz = boost::lexical_cast<double>(s_qz.str());
00125           odom_msg.qw = boost::lexical_cast<double>(s_qw.str());
00126           graph_vertices.push_back(odom_msg);
00127         }
00128         else
00129         {
00130           ROS_WARN_STREAM("[PointCloudMapper:] Line not processed: " << line);
00131         }
00132       }
00133 
00134       // Transform the point clouds
00135       for (unsigned int i=0; i<pointcloud_list_.size(); i++)
00136       {
00137         // Get the current cloud
00138         sensor_msgs::PointCloud2 cloud = pointcloud_list_.at(i);
00139         double cloud_time = cloud.header.stamp.toSec();
00140 
00141         // Search the corresponding timestamp in the graph
00142         int idx_graph = -1;
00143         double min_time_diff = graph_vertices[0].timestamp;
00144         for (unsigned int j=0; j<graph_vertices.size(); j++)
00145         {
00146           double time_diff = fabs(graph_vertices[j].timestamp - cloud_time);
00147           if(time_diff < min_time_diff)
00148           {
00149             min_time_diff = time_diff;
00150             idx_graph = j;
00151           }
00152         }
00153 
00154         // Found sync?
00155         double eps = 1e-1;
00156         if (min_time_diff < eps)
00157         {
00158           ROS_DEBUG_STREAM("[PointCloudMapper:] Time sync found between pointcloud " << 
00159                             i << " and graph vertex " << idx_graph << ".");
00160 
00161           // Build the tf
00162           tf::Vector3 t(graph_vertices[idx_graph].x, 
00163                         graph_vertices[idx_graph].y,
00164                         graph_vertices[idx_graph].z);
00165           tf::Quaternion q(graph_vertices[idx_graph].qx,
00166                            graph_vertices[idx_graph].qy,
00167                            graph_vertices[idx_graph].qz,
00168                            graph_vertices[idx_graph].qw);
00169           tf::Transform transform(q, t);
00170 
00171           // Transform pointcloud
00172           PointCloud pcl_cloud, transformed_cloud;
00173           pcl::fromROSMsg(pointcloud_list_.at(i), pcl_cloud);
00174           pcl_ros::transformPointCloud(pcl_cloud, transformed_cloud, transform);
00175 
00176           // Accumulate points
00177           accumulated_cloud += transformed_cloud;
00178         }
00179       }
00180 
00181       // If points...
00182       if(accumulated_cloud.points.size() > 0)
00183         cloud_pub_.publish(accumulated_cloud);
00184       else
00185         ROS_INFO("[PointCloudMapper:] Nothing to add...");
00186     }
00187   }
00188 
00189   PointCloud::Ptr filter(PointCloud::Ptr cloud)
00190   {
00191     // NAN and limit filtering
00192     PointCloud::Ptr cloud_filtered_ptr(new PointCloud);
00193     pcl::PassThrough<Point> pass_;
00194 
00195     // X-filtering
00196     pass_.setFilterFieldName("x");
00197     pass_.setFilterLimits(x_filter_min_, x_filter_max_);
00198     pass_.setInputCloud(cloud);
00199     pass_.filter(*cloud_filtered_ptr);
00200 
00201     // Y-filtering
00202     pass_.setFilterFieldName("y");
00203     pass_.setFilterLimits(y_filter_min_, y_filter_max_);
00204     pass_.setInputCloud(cloud_filtered_ptr);
00205     pass_.filter(*cloud_filtered_ptr);
00206 
00207     // Z-filtering
00208     pass_.setFilterFieldName("z");
00209     pass_.setFilterLimits(z_filter_min_, z_filter_max_);
00210     pass_.setInputCloud(cloud);
00211     pass_.filter(*cloud_filtered_ptr);
00212 
00213     // Downsampling using voxel grid
00214     pcl::VoxelGrid<Point> grid_;
00215     PointCloud::Ptr cloud_downsampled_ptr(new PointCloud);
00216     double plane_detection_voxel_size_ = voxel_size_;
00217 
00218     grid_.setLeafSize(plane_detection_voxel_size_,
00219                       plane_detection_voxel_size_,
00220                       plane_detection_voxel_size_);
00221     grid_.setDownsampleAllData(true);
00222     grid_.setInputCloud(cloud_filtered_ptr);
00223     grid_.filter(*cloud_downsampled_ptr);
00224 
00225     // Statistical outlier removal
00226     pcl::StatisticalOutlierRemoval<Point> sor;
00227     sor.setInputCloud(cloud_downsampled_ptr);
00228     sor.setMeanK(mean_k_);
00229     sor.setStddevMulThresh(std_dev_thresh_);
00230     sor.filter(*cloud_downsampled_ptr);
00231 
00232     return cloud_downsampled_ptr;
00233   }
00234 
00235   bool fileExists(const char* filename) 
00236   {
00237     struct stat info;
00238     int ret = -1;
00239    
00240     //get the file attributes
00241     ret = stat(filename, &info);
00242     if(ret == 0)
00243       return true;
00244     else 
00245       return false;
00246   }
00247 
00248 private:
00249   struct OdomMsg 
00250   {
00251     double timestamp;
00252     int id;
00253     std::string fixed_frame;
00254     std::string base_link_frame;
00255     double x;
00256     double y;
00257     double z;
00258     double qx;
00259     double qy;
00260     double qz;
00261     double qw;
00262   };
00263 
00264   // Node parameters
00265   std::string graph_vertices_file_;
00266   std::string graph_blocking_file_;
00267 
00268   // Filter parameters
00269   double x_filter_min_;
00270   double x_filter_max_;
00271   double y_filter_min_;
00272   double y_filter_max_;
00273   double z_filter_min_;
00274   double z_filter_max_;
00275   double voxel_size_;
00276   double std_dev_thresh_;
00277   int mean_k_;
00278   bool filter_map_;
00279 
00280   ros::NodeHandle nh_;
00281   ros::NodeHandle nh_priv_;
00282 
00283   ros::Subscriber cloud_sub_;
00284   ros::Publisher cloud_pub_;
00285 
00286   ros::WallTimer pub_timer_;
00287 
00288   std::vector<sensor_msgs::PointCloud2> pointcloud_list_;
00289 };
00290 
00291 int main(int argc, char **argv)
00292 {
00293   ros::init(argc, argv, "cloud_mapper");
00294   PointCloudMapper mapper;
00295   ros::spin();
00296   return 0;
00297 }
00298 


pointcloud_tools
Author(s): Pep Lluis Negre
autogenerated on Fri Aug 28 2015 13:15:25