00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #define BOOST_PARAMETER_MAX_ARITY 7
00037 #include "jsk_pcl_ros/pointcloud_localization.h"
00038 #include <jsk_pcl_ros/ICPAlign.h>
00039 #include "jsk_pcl_ros/pcl_conversion_util.h"
00040 #include <pcl_ros/transforms.h>
00041 #include <pcl/filters/voxel_grid.h>
00042 #include <pcl/filters/passthrough.h>
00043
00044 namespace jsk_pcl_ros
00045 {
00046 void PointCloudLocalization::onInit()
00047 {
00048 pcl::console::setVerbosityLevel(pcl::console::L_ERROR);
00049 DiagnosticNodelet::onInit();
00050 tf_listener_ = TfListenerSingleton::getInstance();
00051
00052 localize_transform_.setIdentity();
00053 pnh_->param("global_frame", global_frame_, std::string("map"));
00054 pnh_->param("odom_frame", odom_frame_, std::string("odom"));
00055 pnh_->param("leaf_size", leaf_size_, 0.01);
00056 pnh_->param("initialize_from_tf", initialize_from_tf_, false);
00057 if (initialize_from_tf_) {
00058 pnh_->param("initialize_tf", initialize_tf_, std::string("odom_on_ground"));
00059 }
00060 pnh_->param("clip_unseen_pointcloud", clip_unseen_pointcloud_, false);
00061 if (clip_unseen_pointcloud_) {
00062 pnh_->param("sensor_frame", sensor_frame_, std::string("BODY"));
00063 }
00064 pnh_->param("use_normal", use_normal_, false);
00065 double cloud_rate;
00066 pnh_->param("cloud_rate", cloud_rate, 10.0);
00067 double tf_rate;
00068 pnh_->param("tf_rate", tf_rate, 20.0);
00069 pub_cloud_ = pnh_->advertise<sensor_msgs::PointCloud2>("output", 1);
00070
00071 sub_ = pnh_->subscribe("input", 1, &PointCloudLocalization::cloudCallback, this);
00072 localization_srv_ = pnh_->advertiseService(
00073 "localize", &PointCloudLocalization::localizationRequest, this);
00074 update_offset_srv_ = pnh_->advertiseService(
00075 "update_offset", &PointCloudLocalization::updateOffsetCallback, this);
00076
00077
00078 cloud_timer_ = pnh_->createTimer(
00079 ros::Duration(1.0 / cloud_rate),
00080 boost::bind(&PointCloudLocalization::cloudTimerCallback, this, _1));
00081
00082 tf_timer_ = pnh_->createTimer(
00083 ros::Duration(1.0 / tf_rate),
00084 boost::bind(&PointCloudLocalization::tfTimerCallback, this, _1));
00085 }
00086
00087 void PointCloudLocalization::subscribe()
00088 {
00089
00090 }
00091
00092 void PointCloudLocalization::unsubscribe()
00093 {
00094
00095 }
00096
00097 void PointCloudLocalization::applyDownsampling(
00098 pcl::PointCloud<pcl::PointNormal>::Ptr in_cloud,
00099 pcl::PointCloud<pcl::PointNormal>& out_cloud)
00100 {
00101 pcl::VoxelGrid<pcl::PointNormal> vg;
00102 vg.setInputCloud(in_cloud);
00103 vg.setLeafSize(leaf_size_, leaf_size_, leaf_size_);
00104 vg.filter(out_cloud);
00105 }
00106
00107 void PointCloudLocalization::cloudTimerCallback(
00108 const ros::TimerEvent& event)
00109 {
00110 boost::mutex::scoped_lock lock(mutex_);
00111 ros::Time stamp = event.current_real;
00112 if (all_cloud_) {
00113 sensor_msgs::PointCloud2 ros_cloud;
00114 pcl::toROSMsg(*all_cloud_, ros_cloud);
00115 ros_cloud.header.stamp = stamp;
00116 ros_cloud.header.frame_id = global_frame_;
00117 pub_cloud_.publish(ros_cloud);
00118 }
00119 }
00120
00121 void PointCloudLocalization::tfTimerCallback(
00122 const ros::TimerEvent& event)
00123 {
00124 boost::mutex::scoped_lock lock(tf_mutex_);
00125 try {
00126 ros::Time stamp = event.current_real;
00127 if (initialize_from_tf_ && first_time_) {
00128
00129 tf::StampedTransform transform = lookupTransformWithDuration(
00130 tf_listener_,
00131 initialize_tf_, odom_frame_, stamp, ros::Duration(1.0));
00132 localize_transform_ = transform;
00133
00134 }
00135 tf_broadcast_.sendTransform(tf::StampedTransform(localize_transform_,
00136 stamp,
00137 global_frame_,
00138 odom_frame_));
00139 }
00140 catch (tf2::TransformException& e) {
00141 JSK_NODELET_FATAL("Failed to lookup transformation: %s", e.what());
00142 }
00143 }
00144
00145 void PointCloudLocalization::cloudCallback(
00146 const sensor_msgs::PointCloud2::ConstPtr& cloud_msg)
00147 {
00148 vital_checker_->poke();
00149 boost::mutex::scoped_lock lock(mutex_);
00150
00151 latest_cloud_ = cloud_msg;
00152 if (localize_requested_){
00153 JSK_NODELET_INFO("localization is requested");
00154 try {
00155 pcl::PointCloud<pcl::PointNormal>::Ptr
00156 local_cloud (new pcl::PointCloud<pcl::PointNormal>);
00157 pcl::fromROSMsg(*latest_cloud_, *local_cloud);
00158 JSK_NODELET_INFO("waiting for tf transformation from %s tp %s",
00159 latest_cloud_->header.frame_id.c_str(),
00160 global_frame_.c_str());
00161 if (tf_listener_->waitForTransform(
00162 latest_cloud_->header.frame_id,
00163 global_frame_,
00164 latest_cloud_->header.stamp,
00165 ros::Duration(1.0))) {
00166 pcl::PointCloud<pcl::PointNormal>::Ptr
00167 input_cloud (new pcl::PointCloud<pcl::PointNormal>);
00168 if (use_normal_) {
00169 pcl_ros::transformPointCloudWithNormals(global_frame_,
00170 *local_cloud,
00171 *input_cloud,
00172 *tf_listener_);
00173 }
00174 else {
00175 pcl_ros::transformPointCloud(global_frame_,
00176 *local_cloud,
00177 *input_cloud,
00178 *tf_listener_);
00179 }
00180 pcl::PointCloud<pcl::PointNormal>::Ptr
00181 input_downsampled_cloud (new pcl::PointCloud<pcl::PointNormal>);
00182 applyDownsampling(input_cloud, *input_downsampled_cloud);
00183 if (isFirstTime()) {
00184 all_cloud_ = input_downsampled_cloud;
00185 first_time_ = false;
00186 }
00187 else {
00188
00189 ros::ServiceClient client
00190 = pnh_->serviceClient<jsk_pcl_ros::ICPAlign>("icp_align");
00191 jsk_pcl_ros::ICPAlign icp_srv;
00192
00193 if (clip_unseen_pointcloud_) {
00194
00195
00196
00197
00198 tf::StampedTransform global_sensor_tf_transform
00199 = lookupTransformWithDuration(
00200 tf_listener_,
00201 global_frame_,
00202 sensor_frame_,
00203 cloud_msg->header.stamp,
00204 ros::Duration(1.0));
00205 Eigen::Affine3f global_sensor_transform;
00206 tf::transformTFToEigen(global_sensor_tf_transform,
00207 global_sensor_transform);
00208 pcl::PointCloud<pcl::PointNormal>::Ptr sensor_cloud
00209 (new pcl::PointCloud<pcl::PointNormal>);
00210 pcl::transformPointCloudWithNormals(
00211 *all_cloud_,
00212 *sensor_cloud,
00213 global_sensor_transform.inverse());
00214
00215 pcl::PassThrough<pcl::PointNormal> pass;
00216 pass.setInputCloud(sensor_cloud);
00217 pass.setFilterFieldName("x");
00218 pass.setFilterLimits(0.0, 100.0);
00219 pcl::PointCloud<pcl::PointNormal>::Ptr filtered_cloud
00220 (new pcl::PointCloud<pcl::PointNormal>);
00221 pass.filter(*filtered_cloud);
00222 JSK_NODELET_INFO("clipping: %lu -> %lu", sensor_cloud->points.size(), filtered_cloud->points.size());
00223
00224 pcl::PointCloud<pcl::PointNormal>::Ptr global_filtered_cloud
00225 (new pcl::PointCloud<pcl::PointNormal>);
00226 pcl::transformPointCloudWithNormals(
00227 *filtered_cloud,
00228 *global_filtered_cloud,
00229 global_sensor_transform);
00230 pcl::toROSMsg(*global_filtered_cloud,
00231 icp_srv.request.target_cloud);
00232 }
00233 else {
00234 pcl::toROSMsg(*all_cloud_,
00235 icp_srv.request.target_cloud);
00236 }
00237 pcl::toROSMsg(*input_downsampled_cloud,
00238 icp_srv.request.reference_cloud);
00239
00240 if (client.call(icp_srv)) {
00241 Eigen::Affine3f transform;
00242 tf::poseMsgToEigen(icp_srv.response.result.pose, transform);
00243 Eigen::Vector3f transform_pos(transform.translation());
00244 float roll, pitch, yaw;
00245 pcl::getEulerAngles(transform, roll, pitch, yaw);
00246 JSK_NODELET_INFO("aligned parameter --");
00247 JSK_NODELET_INFO(" - pos: [%f, %f, %f]",
00248 transform_pos[0],
00249 transform_pos[1],
00250 transform_pos[2]);
00251 JSK_NODELET_INFO(" - rot: [%f, %f, %f]", roll, pitch, yaw);
00252 pcl::PointCloud<pcl::PointNormal>::Ptr
00253 transformed_input_cloud (new pcl::PointCloud<pcl::PointNormal>);
00254 if (use_normal_) {
00255 pcl::transformPointCloudWithNormals(*input_cloud,
00256 *transformed_input_cloud,
00257 transform);
00258 }
00259 else {
00260 pcl::transformPointCloud(*input_cloud,
00261 *transformed_input_cloud,
00262 transform);
00263 }
00264 pcl::PointCloud<pcl::PointNormal>::Ptr
00265 concatenated_cloud (new pcl::PointCloud<pcl::PointNormal>);
00266 *concatenated_cloud = *all_cloud_ + *transformed_input_cloud;
00267
00268 applyDownsampling(concatenated_cloud, *all_cloud_);
00269
00270 tf::Transform icp_transform;
00271 tf::transformEigenToTF(transform, icp_transform);
00272 {
00273 boost::mutex::scoped_lock tf_lock(tf_mutex_);
00274 localize_transform_ = localize_transform_ * icp_transform;
00275 }
00276 }
00277 else {
00278 JSK_NODELET_ERROR("Failed to call ~icp_align");
00279 return;
00280 }
00281 }
00282 localize_requested_ = false;
00283 }
00284 else {
00285 JSK_NODELET_WARN("No tf transformation is available");
00286 }
00287 }
00288 catch (tf2::ConnectivityException &e)
00289 {
00290 JSK_NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00291 return;
00292 }
00293 catch (tf2::InvalidArgumentException &e)
00294 {
00295 JSK_NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00296 return;
00297 }
00298 }
00299 }
00300
00301 bool PointCloudLocalization::isFirstTime()
00302 {
00303 return first_time_;
00304 }
00305
00306 bool PointCloudLocalization::localizationRequest(
00307 std_srvs::Empty::Request& req,
00308 std_srvs::Empty::Response& res)
00309 {
00310 JSK_NODELET_INFO("localize!");
00311 boost::mutex::scoped_lock lock(mutex_);
00312 localize_requested_ = true;
00313 return true;
00314 }
00315
00316 bool PointCloudLocalization::updateOffsetCallback(
00317 jsk_pcl_ros::UpdateOffset::Request& req,
00318 jsk_pcl_ros::UpdateOffset::Response& res)
00319 {
00320 boost::mutex::scoped_lock lock(mutex_);
00321 geometry_msgs::TransformStamped next_pose = req.transformation;
00322
00323 tf::StampedTransform tf_transform;
00324 tf::transformStampedMsgToTF(next_pose, tf_transform);
00325
00326 localize_transform_ = tf_transform;
00327 return true;
00328 }
00329 }
00330
00331 #include <pluginlib/class_list_macros.h>
00332 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::PointCloudLocalization, nodelet::Nodelet);