pointcloud_localization_nodelet.cpp
Go to the documentation of this file.
00001 // -*- mode: c++ -*-
00002 /*********************************************************************
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2015, JSK Lab
00006  *  All rights reserved.
00007  *
00008  *  Redistribution and use in source and binary forms, with or without
00009  *  modification, are permitted provided that the following conditions
00010  *  are met:
00011  *
00012  *   * Redistributions of source code must retain the above copyright
00013  *     notice, this list of conditions and the following disclaimer.
00014  *   * Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions and the following
00016  *     disclaimer in the documentation and/o2r other materials provided
00017  *     with the distribution.
00018  *   * Neither the name of the JSK Lab nor the names of its
00019  *     contributors may be used to endorse or promote products derived
00020  *     from this software without specific prior written permission.
00021  *
00022  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  *  POSSIBILITY OF SUCH DAMAGE.
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     // initialize localize_transform_ as identity
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     // always subscribe pointcloud...?
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     // timer to publish cloud
00078     cloud_timer_ = pnh_->createTimer(
00079       ros::Duration(1.0 / cloud_rate),
00080       boost::bind(&PointCloudLocalization::cloudTimerCallback, this, _1));
00081     // timer to publish tf
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     // dummy
00090   }
00091   
00092   void PointCloudLocalization::unsubscribe()
00093   {
00094     // dummy
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       // Update localize_transform_ to points initialize_tf
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     //JSK_NODELET_INFO("cloudCallback");
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             // run ICP
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               // Before running ICP, remove pointcloud where we cannot see
00195               // First, transform reference pointcloud, that is all_cloud_, into
00196               // sensor frame.
00197               // And after that, remove points which are x < 0.
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               // Remove negative-x points
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               // Convert the pointcloud to global frame again
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               // update *all_cloud
00268               applyDownsampling(concatenated_cloud, *all_cloud_);
00269               // update localize_transform_
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     // convert geometry_msgs::TransformStamped into tf::Transform
00323     tf::StampedTransform tf_transform;
00324     tf::transformStampedMsgToTF(next_pose, tf_transform);
00325     // TODO: resolve tf
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);


jsk_pcl_ros
Author(s): Yohei Kakiuchi
autogenerated on Wed Sep 16 2015 04:36:48