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
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
00051 sensor_msgs::PointCloud2 cur_cloud = *cloud;
00052
00053
00054 if(filter_map_)
00055 {
00056
00057 PointCloud pcl_cloud;
00058 pcl::fromROSMsg(cur_cloud, pcl_cloud);
00059
00060
00061 PointCloud::Ptr cloud_downsampled = filter(pcl_cloud.makeShared());
00062 pcl_cloud = *cloud_downsampled;
00063
00064
00065 pcl::toROSMsg(pcl_cloud, cur_cloud);
00066
00067 }
00068
00069 pointcloud_list_.push_back(cur_cloud);
00070 }
00071
00072 void timerCallback(const ros::WallTimerEvent& event)
00073 {
00074
00075 if (cloud_pub_.getNumSubscribers() > 0)
00076 {
00077
00078 PointCloud accumulated_cloud;
00079
00080
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
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
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
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
00135 for (unsigned int i=0; i<pointcloud_list_.size(); i++)
00136 {
00137
00138 sensor_msgs::PointCloud2 cloud = pointcloud_list_.at(i);
00139 double cloud_time = cloud.header.stamp.toSec();
00140
00141
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
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
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
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
00177 accumulated_cloud += transformed_cloud;
00178 }
00179 }
00180
00181
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
00192 PointCloud::Ptr cloud_filtered_ptr(new PointCloud);
00193 pcl::PassThrough<Point> pass_;
00194
00195
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
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
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
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
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
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
00265 std::string graph_vertices_file_;
00266 std::string graph_blocking_file_;
00267
00268
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