00001 #include <ros/ros.h>
00002 #include <sensor_msgs/PointCloud.h>
00003 #include <std_msgs/String.h>
00004 #include <sensor_msgs/PointCloud2.h>
00005 #include <tf/transform_listener.h>
00006 #include "globals.icc"
00007 #include <pcl_ros/transforms.h>
00008
00009 #include <fstream>
00010 using std::ofstream;
00011 #include <iostream>
00012 using std::cout;
00013 using std::endl;
00014
00015 tf::TransformListener *tl_;
00016
00017 bool needRequest_, requested_;
00018 std::string fixed_frame_;
00019 std::string robot_frame_;
00020
00021 bool getTransform(double *t, double *ti, double *rP, double *rPT, tf::TransformListener *listener, ros::Time time)
00022 {
00023 tf::StampedTransform transform;
00024
00025 std::string error_msg;
00026 bool success = listener->waitForTransform(fixed_frame_, robot_frame_, time, ros::Duration(3.0), ros::Duration(0.01),
00027 &error_msg);
00028
00029 if (!success)
00030 {
00031 ROS_WARN("Could not get transform, ignoring point cloud! %s", error_msg.c_str());
00032 return false;
00033 }
00034
00035 listener->lookupTransform(fixed_frame_, robot_frame_, time, transform);
00036
00037 double mat[9];
00038 double x = transform.getOrigin().getX() * 100;
00039 double y = transform.getOrigin().getY() * 100;
00040 double z = transform.getOrigin().getZ() * 100;
00041 mat[0] = transform.getBasis().getRow(0).getX();
00042 mat[1] = transform.getBasis().getRow(0).getY();
00043 mat[2] = transform.getBasis().getRow(0).getZ();
00044
00045 mat[3] = transform.getBasis().getRow(1).getX();
00046 mat[4] = transform.getBasis().getRow(1).getY();
00047 mat[5] = transform.getBasis().getRow(1).getZ();
00048
00049 mat[6] = transform.getBasis().getRow(2).getX();
00050 mat[7] = transform.getBasis().getRow(2).getY();
00051 mat[8] = transform.getBasis().getRow(2).getZ();
00052
00053 t[0] = mat[4];
00054 t[1] = -mat[7];
00055 t[2] = -mat[1];
00056 t[3] = 0.0;
00057
00058 t[4] = -mat[5];
00059 t[5] = mat[8];
00060 t[6] = mat[2];
00061 t[7] = 0.0;
00062
00063 t[8] = -mat[3];
00064 t[9] = mat[6];
00065 t[10] = mat[0];
00066 t[11] = 0.0;
00067
00068
00069 t[12] = -y;
00070 t[13] = z;
00071 t[14] = x;
00072 t[15] = 1;
00073 M4inv(t, ti);
00074 Matrix4ToEuler(t, rPT, rP);
00075
00076 return true;
00077 }
00078
00079 void writePose(int j, double rP[3], double rPT[3])
00080 {
00081 char pose_str[13];
00082 sprintf(pose_str, "scan%03d.pose", j);
00083 ofstream pose(pose_str);
00084 pose << rP[0] << " " << rP[1] << " " << rP[2] << endl << deg(rPT[0]) << " " << deg(rPT[1]) << " " << deg(rPT[2]);
00085 pose.close();
00086 }
00087
00088 void reqCallback(const std_msgs::String::ConstPtr& e)
00089 {
00090 ROS_INFO_STREAM("Request received: " << e->data);
00091 requested_ = true;
00092 }
00093
00094 void pcCallback(const sensor_msgs::PointCloud::ConstPtr& untransformed_cloud)
00095 {
00096
00097 static bool first = true;
00098 if (first)
00099 {
00100 first = false;
00101 return;
00102 }
00103
00104 if (needRequest_ && !requested_)
00105 {
00106 return;
00107 }
00108
00109 static int j = 0;
00110
00111
00112 double t[16], ti[16], rP[3], rPT[3];
00113 bool success = getTransform(t, ti, rP, rPT, tl_, untransformed_cloud->header.stamp);
00114 if (!success)
00115 return;
00116
00117 writePose(j, rP, rPT);
00118
00119
00120 boost::shared_ptr<sensor_msgs::PointCloud> cloud(new sensor_msgs::PointCloud);
00121 tl_->transformPointCloud(robot_frame_, *untransformed_cloud, *cloud);
00122
00123 if (!success)
00124 return;
00125
00126 char scan_str[11];
00127 sprintf(scan_str, "scan%03d.3d", j++);
00128 ofstream scan(scan_str);
00129
00130 size_t i;
00131 double p[3];
00132 for (i = 0; i < cloud->points.size(); i++)
00133 {
00134 p[0] = cloud->points[i].y * -100;
00135 p[1] = cloud->points[i].z * 100;
00136 p[2] = cloud->points[i].x * 100;
00137
00138
00139
00140 if (!isnan(p[0]) && !isnan(p[1]) && !isnan(p[2]))
00141 {
00142 scan << p[0] << " " << p[1] << " " << p[2] << endl;
00143 }
00144 }
00145 scan.close();
00146 ROS_INFO("wrote %zu points to file %s (backlog: %f s)", i, scan_str, (ros::Time::now() - cloud->header.stamp).toSec());
00147
00148 requested_ = false;
00149 }
00150
00151 inline int32_t findChannelIndex(const sensor_msgs::PointCloud2ConstPtr& cloud, const std::string& channel)
00152 {
00153 for (size_t i = 0; i < cloud->fields.size(); ++i)
00154 {
00155 if (cloud->fields[i].name == channel)
00156 {
00157 return i;
00158 }
00159 }
00160
00161 return -1;
00162 }
00163
00164 void pc2aCallback(const sensor_msgs::PointCloud2Ptr& untransformed_cloud)
00165 {
00166 static int j = 0;
00167
00168
00169 double t[16], ti[16], rP[3], rPT[3];
00170 bool success = getTransform(t, ti, rP, rPT, tl_, untransformed_cloud->header.stamp);
00171 if (!success)
00172 return;
00173
00174 writePose(j, rP, rPT);
00175
00176
00177 boost::shared_ptr<sensor_msgs::PointCloud2> cloud(new sensor_msgs::PointCloud2);
00178 success = pcl_ros::transformPointCloud(robot_frame_, *untransformed_cloud, *cloud, *tl_);
00179 if (!success)
00180 return;
00181
00182 char scan_str[11];
00183 sprintf(scan_str, "scan%03d.3d", j++);
00184 ofstream scan(scan_str);
00185
00186 int32_t xi = findChannelIndex(cloud, "x");
00187 int32_t yi = findChannelIndex(cloud, "y");
00188 int32_t zi = findChannelIndex(cloud, "z");
00189 int32_t rgbi = findChannelIndex(cloud, "rgb");
00190
00191 if (xi == -1 || yi == -1 || zi == -1)
00192 {
00193 return;
00194 }
00195
00196 const uint32_t xoff = cloud->fields[xi].offset;
00197 const uint32_t yoff = cloud->fields[yi].offset;
00198 const uint32_t zoff = cloud->fields[zi].offset;
00199 uint32_t rgboff = -1;
00200 if (rgbi != -1)
00201 rgboff = cloud->fields[rgbi].offset;
00202 const uint32_t point_step = cloud->point_step;
00203 const size_t point_count = cloud->width * cloud->height;
00204
00205 if (point_count == 0)
00206 {
00207 return;
00208 }
00209
00210 const uint8_t* ptr = &cloud->data.front();
00211 size_t i;
00212 double p[3];
00213 for (i = 0; i < point_count; ++i)
00214 {
00215 p[0] = *reinterpret_cast<const float*>(ptr + yoff) * -100;
00216 p[1] = *reinterpret_cast<const float*>(ptr + zoff) * 100;
00217 p[2] = *reinterpret_cast<const float*>(ptr + xoff) * 100;
00218
00219
00220
00221 if (!isnan(p[0]) && !isnan(p[1]) && !isnan(p[2]))
00222 {
00223 scan << p[0] << " " << p[1] << " " << p[2];
00224
00225 if (rgbi != -1)
00226 {
00227 uint32_t rgb = *reinterpret_cast<const uint32_t*>(ptr + rgboff);
00228 int r = ((rgb >> 16) & 0xff);
00229 int g = ((rgb >> 8) & 0xff);
00230 int b = (rgb & 0xff);
00231
00232 scan << " " << r << " " << g << " " << b;
00233 }
00234 scan << endl;
00235 }
00236
00237 ptr += point_step;
00238 }
00239
00240 scan.close();
00241 ROS_INFO("wrote %zu points to file %s (backlog: %f s)", i, scan_str, (ros::Time::now() - cloud->header.stamp).toSec());
00242 }
00243
00244 int main(int argc, char **argv)
00245 {
00246 ros::init(argc, argv, "slam_exporter");
00247
00248
00249 if (argc > 1 && strcmp(argv[1], "--withrequest") == 0)
00250 {
00251 ROS_INFO("Scan will only be exported when requested as defined by parameter.");
00252 needRequest_ = true;
00253 }
00254 else
00255 needRequest_ = false;
00256
00257 requested_ = false;
00258
00259 ros::NodeHandle n;
00260 ros::NodeHandle pn("~");
00261
00262 tl_ = new tf::TransformListener(ros::Duration(60.0));
00263
00264 bool use_point_cloud2;
00265 pn.param("fixed_frame", fixed_frame_, std::string("odom_combined"));
00266 pn.param("robot_frame", robot_frame_, std::string("base_link"));
00267 pn.param("use_point_cloud2", use_point_cloud2, true);
00268
00269 ros::Subscriber sub;
00270 if (use_point_cloud2)
00271 sub = n.subscribe("points2_in", 100, pc2aCallback);
00272 else
00273 sub = n.subscribe("points_in", 100, pcCallback);
00274
00275 ros::Subscriber scanRequest = n.subscribe("/request", 1, reqCallback);
00276
00277 ROS_INFO("slam_exporter initialized with fixed_frame = \"%s\", robot_frame = \"%s\", ", fixed_frame_.c_str(), robot_frame_.c_str());
00278 ros::spin();
00279 return 0;
00280 }