icp_registration_nodelet.cpp
Go to the documentation of this file.
00001 // -*- mode: c++; indent-tabs-mode: nil; -*-
00002 /*********************************************************************
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2014, 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 #include "jsk_pcl_ros/icp_registration.h"
00037 #include <pcl/registration/icp.h>
00038 #include <pcl/registration/gicp.h>
00039 #include <pcl/registration/ndt.h>
00040 #include <pcl/registration/transformation_estimation_lm.h>
00041 #include <pcl/registration/warp_point_rigid_3d.h>
00042 #include "jsk_recognition_utils/pcl_conversion_util.h"
00043 #include <eigen_conversions/eigen_msg.h>
00044 #include <pcl/common/transforms.h>
00045 #include <eigen_conversions/eigen_msg.h>
00046 #include "jsk_pcl_ros_utils/transform_pointcloud_in_bounding_box.h"
00047 #include <image_geometry/pinhole_camera_model.h>
00048 #include <pcl/registration/correspondence_estimation_organized_projection.h>
00049 #include <pcl/registration/correspondence_estimation_normal_shooting.h>
00050 #include <jsk_recognition_utils/pcl_ros_util.h>
00051 
00052 namespace jsk_pcl_ros
00053 {
00054   void ICPRegistration::onInit()
00055   {
00056     pcl::console::setVerbosityLevel(pcl::console::L_ERROR);
00057     ConnectionBasedNodelet::onInit();
00058     tf_listener_ = TfListenerSingleton::getInstance();
00060     // Dynamic Reconfigure
00062     srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
00063     dynamic_reconfigure::Server<Config>::CallbackType f =
00064       boost::bind(
00065         &ICPRegistration::configCallback, this, _1, _2);
00066     srv_->setCallback (f);
00067     pnh_->param("use_normal", use_normal_, false);
00068     pnh_->param("align_box", align_box_, false);
00069     pnh_->param("synchronize_reference", synchronize_reference_, false);
00070     pnh_->param("transform_3dof", transform_3dof_, false);
00071     pnh_->param("use_offset_pose", use_offset_pose_, false);
00073     // Publishers
00075     pub_result_pose_ = advertise<geometry_msgs::PoseStamped>(*pnh_,
00076       "output_pose", 1);
00077     pub_result_cloud_ = advertise<sensor_msgs::PointCloud2>(*pnh_,
00078       "output", 1);
00079     pub_debug_source_cloud_ = advertise<sensor_msgs::PointCloud2>(*pnh_,
00080       "debug/source", 1);
00081     pub_debug_target_cloud_ = advertise<sensor_msgs::PointCloud2>(*pnh_,
00082       "debug/target", 1);
00083     pub_debug_flipped_cloud_ = advertise<sensor_msgs::PointCloud2>(*pnh_,
00084       "debug/flipped", 1);
00085     pub_debug_result_cloud_ = advertise<sensor_msgs::PointCloud2>(*pnh_,
00086       "debug/result", 1);
00087     pub_icp_result = advertise<jsk_recognition_msgs::ICPResult>(*pnh_,
00088       "icp_result", 1);
00089     pub_latest_time_ = advertise<std_msgs::Float32>(
00090       *pnh_, "output/latest_time", 1);
00091     pub_average_time_ = advertise<std_msgs::Float32>(
00092       *pnh_, "output/average_time", 1);
00093     srv_icp_align_with_box_ = pnh_->advertiseService("icp_service", &ICPRegistration::alignWithBoxService, this);
00094     srv_icp_align_ = pnh_->advertiseService(
00095       "icp_align", &ICPRegistration::alignService, this);
00096     if (!synchronize_reference_) {
00097       sub_reference_ = pnh_->subscribe("input_reference", 1,
00098                                        &ICPRegistration::referenceCallback,
00099                                        this);
00100       sub_reference_array_ = pnh_->subscribe("input_reference_array", 1,
00101                                            &ICPRegistration::referenceArrayCallback,
00102                                              this);
00103       sub_reference_add = pnh_->subscribe("input_reference_add", 1,
00104                                           &ICPRegistration::referenceAddCallback,
00105                                           this);
00106     }
00107     done_init_ = true;
00108     onInitPostProcess();
00109   }
00110 
00111   void ICPRegistration::subscribe()
00112   {
00114     // Subscription
00116     sub_camera_info_ = pnh_->subscribe("input/camera_info", 1,
00117                                        &ICPRegistration::cameraInfoCallback,
00118                                        this);
00119     if (!synchronize_reference_) {
00120       if (align_box_) {
00121         sub_input_.subscribe(*pnh_, "input", 1);
00122         sub_box_.subscribe(*pnh_, "input_box", 1);
00123         sync_ = boost::make_shared<message_filters::Synchronizer<SyncPolicy> >(100);
00124         sync_->connectInput(sub_input_, sub_box_);
00125         sync_->registerCallback(boost::bind(
00126                                             &ICPRegistration::alignWithBox,
00127                                             this, _1, _2));
00128       }
00129       else if (use_offset_pose_) {
00130         sub_input_.subscribe(*pnh_, "input", 1);
00131         sub_offset_.subscribe(*pnh_, "input_offset", 1);
00132         sync_offset_ = boost::make_shared<message_filters::Synchronizer<OffsetSyncPolicy> >(100);
00133         sync_offset_->connectInput(sub_input_, sub_offset_);
00134         sync_offset_->registerCallback(boost::bind(&ICPRegistration::alignWithOffset, this, _1, _2));
00135       }
00136       else {
00137         sub_ = pnh_->subscribe("input", 1,
00138                                &ICPRegistration::align,
00139                                this);
00140       }
00141     }
00142     else {
00143       sub_sync_input_.subscribe(*pnh_, "input", 1);
00144       sub_sync_reference_.subscribe(*pnh_, "reference", 1);
00145       sync_reference_ = boost::make_shared<message_filters::Synchronizer<ReferenceSyncPolicy> >(100);
00146       sync_reference_->connectInput(sub_sync_input_, sub_sync_reference_);
00147       sync_reference_->registerCallback(boost::bind(&ICPRegistration::align, this, _1, _2));
00148     }
00149   }
00150 
00151   void ICPRegistration::unsubscribe()
00152   {
00153     sub_camera_info_.shutdown();
00154     if (!synchronize_reference_) {
00155       if (align_box_) {
00156         sub_input_.unsubscribe();
00157         sub_box_.unsubscribe();
00158       }
00159       else {
00160         sub_.shutdown();
00161       }
00162     }
00163     else {
00164       sub_sync_input_.unsubscribe();
00165       sub_sync_reference_.unsubscribe();
00166     }
00167   }
00168   
00169   void ICPRegistration::publishDebugCloud(
00170       ros::Publisher& pub,
00171       const pcl::PointCloud<PointT>& cloud)
00172   {
00173     if (pub.getNumSubscribers() > 0) {
00174       sensor_msgs::PointCloud2 ros_cloud;
00175       pcl::toROSMsg(cloud, ros_cloud);
00176       ros_cloud.header.frame_id = "base_link";
00177       ros_cloud.header.stamp = ros::Time::now();
00178       pub.publish(ros_cloud);
00179     }
00180   }
00181   
00182   void ICPRegistration::configCallback(Config &config, uint32_t level)
00183   {
00184     boost::mutex::scoped_lock lock(mutex_);
00185     algorithm_ = config.algorithm;
00186     correspondence_algorithm_ = config.correspondence_algorithm;
00187     use_flipped_initial_pose_ = config.use_flipped_initial_pose;
00188     max_iteration_ = config.max_iteration;
00189     correspondence_distance_ = config.correspondence_distance;
00190     transform_epsilon_ = config.transform_epsilon;
00191     euclidean_fittness_epsilon_ = config.euclidean_fittness_epsilon;
00192     rotation_epsilon_ = config.rotation_epsilon;
00193     ransac_iterations_ = config.ransac_iterations;
00194     ransac_outlier_threshold_ = config.ransac_outlier_threshold;
00195     maximum_optimizer_iterations_ = config.maximum_optimizer_iterations;
00196     ndt_resolution_ = config.ndt_resolution;
00197     ndt_step_size_ = config.ndt_step_size;
00198     ndt_outlier_ratio_ = config.ndt_outlier_ratio;
00199   }
00200 
00201   bool ICPRegistration::alignWithBoxService(
00202     jsk_recognition_msgs::ICPAlignWithBox::Request& req, 
00203     jsk_recognition_msgs::ICPAlignWithBox::Response& res)
00204   {
00205     boost::mutex::scoped_lock lock(mutex_);
00206     if (reference_cloud_list_.size() == 0) {
00207       NODELET_FATAL("no reference is specified");
00208       return false;
00209     }
00210     try
00211     {
00212       Eigen::Affine3f offset;
00213       pcl::PointCloud<PointT>::Ptr output (new pcl::PointCloud<PointT>);
00214       jsk_pcl_ros_utils::transformPointcloudInBoundingBox<PointT>(
00215         req.target_box, req.target_cloud,
00216         *output, offset,
00217         *tf_listener_);
00218       Eigen::Affine3f inversed_offset = offset.inverse();
00219       res.result = alignPointcloudWithReferences(output, inversed_offset, req.target_cloud.header);
00220     }
00221     catch (tf2::ConnectivityException &e)
00222     {
00223       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00224       return false;
00225     }
00226     catch (tf2::InvalidArgumentException &e)
00227     {
00228       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00229       return false;
00230     }
00231     return true;
00232   }
00233 
00234   bool ICPRegistration::alignService(
00235     jsk_recognition_msgs::ICPAlign::Request& req, 
00236     jsk_recognition_msgs::ICPAlign::Response& res)
00237   {
00238     boost::mutex::scoped_lock lock(mutex_);
00239     std::vector<pcl::PointCloud<PointT>::Ptr> tmp_reference_cloud_list
00240       = reference_cloud_list_;  // escape
00241     try
00242     {
00243       // first, update reference
00244       std::vector<pcl::PointCloud<PointT>::Ptr> new_references;
00245       pcl::PointCloud<PointT>::Ptr reference_cloud (new pcl::PointCloud<PointT>);
00246       pcl::fromROSMsg(req.reference_cloud, *reference_cloud);
00247       pcl::PointCloud<PointT>::Ptr non_nan_reference_cloud (new pcl::PointCloud<PointT>);
00248       for (size_t i = 0; i < reference_cloud->points.size(); i++) {
00249         PointT p = reference_cloud->points[i];
00250         if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00251           non_nan_reference_cloud->points.push_back(p);
00252         }
00253       }
00254       new_references.push_back(non_nan_reference_cloud);
00255       reference_cloud_list_ = new_references; // replace references
00256       NODELET_INFO("reference points: %lu/%lu",
00257                    non_nan_reference_cloud->points.size(),
00258                    reference_cloud->points.size());
00259       Eigen::Affine3f offset = Eigen::Affine3f::Identity();
00260       pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
00261       pcl::fromROSMsg(req.target_cloud, *cloud);
00262       res.result = alignPointcloudWithReferences(cloud,
00263                                                  offset,
00264                                                  req.target_cloud.header);
00265     }
00266     catch (tf2::ConnectivityException &e)
00267     {
00268       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00269       reference_cloud_list_ = tmp_reference_cloud_list;
00270       return false;
00271     }
00272     catch (tf2::InvalidArgumentException &e)
00273     {
00274       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00275       reference_cloud_list_ = tmp_reference_cloud_list;
00276       return false;
00277     }
00278     reference_cloud_list_ = tmp_reference_cloud_list;
00279     return true;
00280   }
00281 
00282   
00283   void ICPRegistration::alignWithBox(
00284       const sensor_msgs::PointCloud2::ConstPtr& msg,
00285       const jsk_recognition_msgs::BoundingBox::ConstPtr& box_msg)
00286   {
00287     boost::mutex::scoped_lock lock(mutex_);
00288     if (!done_init_) {
00289       NODELET_WARN("not yet initialized");
00290       return;
00291     }
00292     if (reference_cloud_list_.size() == 0) {
00293       NODELET_FATAL("no reference is specified");
00294       jsk_recognition_msgs::ICPResult result;
00295       result.name = std::string("NONE");
00296       result.score = DBL_MAX;
00297       result.header = box_msg->header;
00298       result.pose = box_msg->pose;
00299       pub_icp_result.publish(result);
00300       return;
00301     }
00302     try
00303     {
00304       Eigen::Affine3f offset;
00305       pcl::PointCloud<PointT>::Ptr output (new pcl::PointCloud<PointT>);
00306       jsk_pcl_ros_utils::transformPointcloudInBoundingBox<PointT>(
00307         *box_msg, *msg,
00308         *output, offset,
00309         *tf_listener_);
00310       Eigen::Affine3f inversed_offset = offset.inverse();
00311       jsk_recognition_msgs::ICPResult result = alignPointcloudWithReferences(output, inversed_offset, msg->header);
00312       pub_icp_result.publish(result);
00313     }
00314     catch (tf2::ConnectivityException &e)
00315     {
00316       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00317     }
00318     catch (tf2::InvalidArgumentException &e)
00319     {
00320       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00321     }
00322   }
00323 
00324   void ICPRegistration::alignWithOffset(
00325       const sensor_msgs::PointCloud2::ConstPtr& msg,
00326       const geometry_msgs::PoseStamped::ConstPtr& offset_msg)
00327   {
00328     boost::mutex::scoped_lock lock(mutex_);
00329     if (!done_init_) {
00330       NODELET_WARN("not yet initialized");
00331       return;
00332     }
00333     if (reference_cloud_list_.size() == 0) {
00334       NODELET_FATAL("no reference is specified");
00335       jsk_recognition_msgs::ICPResult result;
00336       result.name = std::string("NONE");
00337       result.score = DBL_MAX;
00338       result.header = offset_msg->header;
00339       result.pose = offset_msg->pose;
00340       pub_icp_result.publish(result);
00341       return;
00342     }
00343     try
00344     {
00345       if (!jsk_recognition_utils::isSameFrameId(msg->header.frame_id,
00346                                                 offset_msg->header.frame_id)) {
00347         NODELET_ERROR("frame_id does not match. cloud: %s, pose: %s",
00348                           msg->header.frame_id.c_str(),
00349                           offset_msg->header.frame_id.c_str());
00350         return;
00351       }
00352       Eigen::Affine3f offset;
00353       pcl::PointCloud<PointT>::Ptr input (new pcl::PointCloud<PointT>);
00354       pcl::fromROSMsg(*msg, *input);
00355       pcl::PointCloud<PointT>::Ptr output (new pcl::PointCloud<PointT>);
00356       tf::poseMsgToEigen(offset_msg->pose, offset);
00357       
00358       Eigen::Affine3f inversed_offset = offset.inverse();
00359       pcl::transformPointCloud(*input, *output, inversed_offset);
00360       jsk_recognition_msgs::ICPResult result = alignPointcloudWithReferences(output, offset, msg->header);
00361       pub_icp_result.publish(result);
00362     }
00363     catch (tf2::ConnectivityException &e)
00364     {
00365       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00366     }
00367     catch (tf2::InvalidArgumentException &e)
00368     {
00369       NODELET_ERROR("[%s] Transform error: %s", __PRETTY_FUNCTION__, e.what());
00370     }
00371   }
00372 
00373 
00374   void ICPRegistration::align(const sensor_msgs::PointCloud2::ConstPtr& msg)
00375   {
00376     boost::mutex::scoped_lock lock(mutex_);
00377     if (!done_init_) {
00378       NODELET_WARN("not yet initialized");
00379       return;
00380     }
00381     if (reference_cloud_list_.size() == 0) {
00382       NODELET_FATAL("no reference is specified");
00383       return;
00384     }
00385     
00386     pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
00387     pcl::fromROSMsg(*msg, *cloud);
00388     Eigen::Affine3f offset = Eigen::Affine3f::Identity();
00389     // remove nan
00390     pcl::PointCloud<PointT>::Ptr non_nan_cloud (new pcl::PointCloud<PointT>);
00391     for (size_t i = 0; i < cloud->points.size(); i++) {
00392       PointT p = cloud->points[i];
00393       if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00394         non_nan_cloud->points.push_back(p);
00395       }
00396     }
00397     jsk_recognition_msgs::ICPResult result = alignPointcloudWithReferences(non_nan_cloud, offset, msg->header);
00398     pub_icp_result.publish(result);
00399   }
00400   
00401   void ICPRegistration::align(const sensor_msgs::PointCloud2::ConstPtr& msg,
00402                               const sensor_msgs::PointCloud2::ConstPtr& reference_msg)
00403   {
00404     {
00405       boost::mutex::scoped_lock lock(mutex_);
00406       if (!done_init_) {
00407         NODELET_WARN("not yet initialized");
00408         return;
00409       }
00410       reference_cloud_list_.resize(0);
00411       pcl::PointCloud<PointT>::Ptr reference_cloud (new pcl::PointCloud<PointT>);
00412       pcl::fromROSMsg(*reference_msg, *reference_cloud);
00413       // remove nan
00414       pcl::PointCloud<PointT>::Ptr non_nan_reference_cloud (new pcl::PointCloud<PointT>);
00415       for (size_t i = 0; i < reference_cloud->points.size(); i++) {
00416         PointT p = reference_cloud->points[i];
00417         if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00418           non_nan_reference_cloud->points.push_back(p);
00419         }
00420       }
00421       reference_cloud_list_.push_back(non_nan_reference_cloud);
00422     }
00423     align(msg);
00424   }
00425 
00426   jsk_recognition_msgs::ICPResult ICPRegistration::alignPointcloudWithReferences(
00427     pcl::PointCloud<PointT>::Ptr& cloud,
00428     const Eigen::Affine3f& offset,
00429     const std_msgs::Header& header)
00430   {
00431     jsk_recognition_utils::ScopedWallDurationReporter r
00432       = timer_.reporter(pub_latest_time_, pub_average_time_);
00433     double min_score = DBL_MAX;
00434     size_t max_index = 0;
00435     pcl::PointCloud<PointT>::Ptr best_transformed_cloud;
00436     pcl::PointCloud<PointT>::Ptr best_reference;
00437     Eigen::Affine3d best_transform_result;
00438     Eigen::Affine3f best_offset_result;
00439     jsk_recognition_msgs::ICPResult result;
00440     if (cloud->empty()) {
00441       sensor_msgs::PointCloud2 empty_cloud;
00442       empty_cloud.header = header;
00443       pub_result_cloud_.publish(empty_cloud);
00444 
00445       pcl::PointCloud<PointT> empty_pcl_cloud;
00446       publishDebugCloud(pub_debug_source_cloud_, empty_pcl_cloud);
00447       publishDebugCloud(pub_debug_target_cloud_, empty_pcl_cloud);
00448       publishDebugCloud(pub_debug_result_cloud_, empty_pcl_cloud);
00449 
00450       geometry_msgs::PoseStamped empty_pose;
00451       empty_pose.header = header;
00452       pub_result_pose_.publish(empty_pose);
00453 
00454       result.header = header;
00455       result.score = min_score;
00456       result.name = "nan";
00457       return result;
00458     }
00459     for (size_t i = 0; i < reference_cloud_list_.size(); i++) {
00460       Eigen::Affine3f offset_result;
00461       pcl::PointCloud<PointT>::Ptr transformed_cloud(new pcl::PointCloud<PointT>);
00462       Eigen::Affine3d transform_result;
00463       double score = scorePointcloudAlignment(
00464         cloud,
00465         reference_cloud_list_[i],
00466         offset,
00467         offset_result,
00468         transformed_cloud,
00469         transform_result);
00470       if (score < min_score) {
00471         max_index = i;
00472         min_score = score;
00473         best_transform_result = transform_result;
00474         best_transformed_cloud = transformed_cloud;
00475         best_reference = reference_cloud_list_[i];
00476         best_offset_result = offset_result;
00477       }
00478     }
00479     
00480     NODELET_INFO("best score is: %f", min_score);
00481     if (pub_result_cloud_.getNumSubscribers() > 0) {
00482       sensor_msgs::PointCloud2 ros_final;
00483       pcl::toROSMsg(*best_transformed_cloud, ros_final);
00484       ros_final.header = header;
00485       pub_result_cloud_.publish(ros_final);
00486     }
00487     geometry_msgs::PoseStamped ros_result_pose;
00488     ros_result_pose.header = header;
00489     tf::poseEigenToMsg(best_transform_result, ros_result_pose.pose);
00490     pub_result_pose_.publish(ros_result_pose);
00491     publishDebugCloud(pub_debug_source_cloud_, *best_reference);
00492     publishDebugCloud(pub_debug_target_cloud_, *cloud);
00493     pcl::PointCloud<PointT>::Ptr transformed_cloud_for_debug_result
00494       (new pcl::PointCloud<PointT>);
00495     pcl::transformPointCloud(
00496       *best_transformed_cloud, *transformed_cloud_for_debug_result,
00497       best_offset_result.inverse());
00498     publishDebugCloud(pub_debug_result_cloud_,
00499                       *transformed_cloud_for_debug_result);
00500     result.header = ros_result_pose.header;
00501     result.pose = ros_result_pose.pose;
00502     result.score = min_score;
00503     std::stringstream ss;
00504     ss << max_index;
00505     result.name = ss.str();
00506     return result;
00507   }
00508 
00509   void ICPRegistration::cameraInfoCallback(
00510     const sensor_msgs::CameraInfo::ConstPtr& msg)
00511   {
00512     boost::mutex::scoped_lock lock(mutex_);
00513     camera_info_msg_ = msg;
00514   }
00515 
00516   double ICPRegistration::alignPointcloud(
00517     pcl::PointCloud<PointT>::Ptr& cloud,
00518     pcl::PointCloud<PointT>::Ptr& reference,
00519     const Eigen::Affine3f& offset,
00520     pcl::PointCloud<PointT>::Ptr& output_cloud,
00521     Eigen::Affine3d& output_transform)
00522   {
00523     if (algorithm_ == 0 || algorithm_ == 1) {
00524       return alignPointcloudWithICP(cloud, reference, offset, output_cloud, output_transform);
00525     }
00526     else {
00527       return alignPointcloudWithNDT(cloud, reference, offset, output_cloud, output_transform);
00528     }
00529   }
00530 
00531   
00532   double ICPRegistration::alignPointcloudWithNDT(
00533     pcl::PointCloud<PointT>::Ptr& cloud,
00534     pcl::PointCloud<PointT>::Ptr& reference,
00535     const Eigen::Affine3f& offset,
00536     pcl::PointCloud<PointT>::Ptr& output_cloud,
00537     Eigen::Affine3d& output_transform)
00538   {
00539     pcl::NormalDistributionsTransform<PointT, PointT> ndt;
00540     if (reference->points.empty ()) {
00541       NODELET_ERROR("Input Reference Cloud is empty!");
00542       return DBL_MAX;
00543     }
00544     ndt.setInputSource(reference);
00545     if (cloud->points.empty ()) {
00546       NODELET_ERROR("Input Target Cloud is empty!");
00547       return DBL_MAX;
00548     }
00549     ndt.setInputTarget(cloud);
00550     pcl::PointCloud<PointT> final;
00551     ndt.align(final);
00552     pcl::transformPointCloud(final, *output_cloud, offset);
00553     Eigen::Matrix4f transformation = ndt.getFinalTransformation ();
00554     Eigen::Matrix4d transformation_d;
00555     jsk_recognition_utils::convertMatrix4<Eigen::Matrix4f, Eigen::Matrix4d>(
00556       transformation, transformation_d);
00557     Eigen::Affine3d offsetd;
00558     convertEigenAffine3(offset, offsetd);
00559     output_transform = offsetd * Eigen::Affine3d(transformation_d);
00560     return ndt.getFitnessScore();
00561   }
00562   
00563   double ICPRegistration::alignPointcloudWithICP(
00564     pcl::PointCloud<PointT>::Ptr& cloud,
00565     pcl::PointCloud<PointT>::Ptr& reference,
00566     const Eigen::Affine3f& offset,
00567     pcl::PointCloud<PointT>::Ptr& output_cloud,
00568     Eigen::Affine3d& output_transform)
00569   {
00570     pcl::IterativeClosestPoint<PointT, PointT> icp;
00571     if (use_normal_) {
00572       pcl::IterativeClosestPointWithNormals<PointT, PointT> icp_with_normal;
00573       icp = icp_with_normal;
00574     }
00575     // icp.setInputSource(cloud);
00576     // icp.setInputTarget(reference_cloud_);
00577     if (algorithm_ == 1) {
00578       pcl::GeneralizedIterativeClosestPoint<PointT, PointT> gicp;
00579       gicp.setRotationEpsilon(rotation_epsilon_);
00580       gicp.setCorrespondenceRandomness(correspondence_randomness_);
00581       gicp.setMaximumOptimizerIterations(maximum_optimizer_iterations_);
00582       icp = gicp;
00583     }
00584     if (correspondence_algorithm_ == 1) { // Projective
00585       if (!camera_info_msg_) {
00586         NODELET_ERROR("no camera info is available yet");
00587         return DBL_MAX;
00588       }
00589       image_geometry::PinholeCameraModel model;
00590       bool model_success_p = model.fromCameraInfo(camera_info_msg_);
00591       if (!model_success_p) {
00592         NODELET_ERROR("failed to create camera model");
00593         return DBL_MAX;
00594       }
00595       pcl::registration::CorrespondenceEstimationOrganizedProjection<PointT, PointT, float>::Ptr
00596         corr_projection (new pcl::registration::CorrespondenceEstimationOrganizedProjection<PointT, PointT, float>);
00597       corr_projection->setFocalLengths(model.fx(), model.fy());
00598       corr_projection->setCameraCenters(model.cx(), model.cy());
00599       icp.setCorrespondenceEstimation(corr_projection);
00600     }
00601     
00602     if (reference->points.empty ()) {
00603       NODELET_ERROR("Input Reference Cloud is empty!");
00604       return DBL_MAX;
00605     }
00606     icp.setInputSource(reference);
00607     if (cloud->points.empty ()) {
00608       NODELET_ERROR("Input Target Cloud is empty!");
00609       return DBL_MAX;
00610     }
00611     icp.setInputTarget(cloud);
00612 
00613     if (transform_3dof_) {
00614       boost::shared_ptr<pcl::registration::WarpPointRigid3D<PointT, PointT> > warp_func
00615         (new pcl::registration::WarpPointRigid3D<PointT, PointT>);
00616       boost::shared_ptr<pcl::registration::TransformationEstimationLM<PointT, PointT> > te
00617         (new pcl::registration::TransformationEstimationLM<PointT, PointT>);
00618       te->setWarpFunction(warp_func);
00619       icp.setTransformationEstimation(te);
00620     }
00621 
00622     icp.setMaxCorrespondenceDistance (correspondence_distance_);
00623     icp.setMaximumIterations (max_iteration_);
00624     icp.setTransformationEpsilon (transform_epsilon_);
00625     icp.setEuclideanFitnessEpsilon (euclidean_fittness_epsilon_);
00626     icp.setRANSACIterations(ransac_iterations_);
00627     icp.setRANSACOutlierRejectionThreshold(ransac_outlier_threshold_);
00628     pcl::PointCloud<PointT> final;
00629     icp.align(final);
00630     pcl::transformPointCloud(final, *output_cloud, offset);
00631     // NODELET_INFO_STREAM("ICP converged: " << icp.hasConverged());
00632     // NODELET_INFO_STREAM("ICP score: " << icp.getFitnessScore());
00633     Eigen::Matrix4f transformation = icp.getFinalTransformation ();
00634     Eigen::Matrix4d transformation_d;
00635     jsk_recognition_utils::convertMatrix4<Eigen::Matrix4f, Eigen::Matrix4d>(
00636       transformation, transformation_d);
00637     Eigen::Affine3d offsetd;
00638     convertEigenAffine3(offset, offsetd);
00639     output_transform = offsetd * Eigen::Affine3d(transformation_d);
00640     return icp.getFitnessScore();
00641   }
00642 
00643   // compute ICP and return score
00644   double ICPRegistration::scorePointcloudAlignment(
00645     pcl::PointCloud<PointT>::Ptr& cloud,
00646     pcl::PointCloud<PointT>::Ptr& reference,
00647     const Eigen::Affine3f& offset,
00648     Eigen::Affine3f& offset_result,
00649     pcl::PointCloud<PointT>::Ptr transformed_cloud,
00650     Eigen::Affine3d& transform_result)
00651   {
00652     pcl::PointCloud<PointT>::Ptr transformed_cloud_for_debug_result
00653       (new pcl::PointCloud<PointT>);
00654     double score = alignPointcloud(cloud, reference, offset,
00655                                    transformed_cloud, transform_result);
00656     pcl::transformPointCloud(
00657       *transformed_cloud, *transformed_cloud_for_debug_result,
00658       offset.inverse());
00659     offset_result = offset;
00660     if (use_flipped_initial_pose_) {
00661       pcl::PointCloud<PointT>::Ptr flipped_transformed_cloud
00662         (new pcl::PointCloud<PointT>);
00663       Eigen::Affine3d flipped_transform_result;
00664       Eigen::Affine3f flipped_offset
00665       = offset * Eigen::AngleAxisf(M_PI, Eigen::Vector3f(0, 0, 1));
00666       pcl::PointCloud<PointT>::Ptr flipped_cloud (new pcl::PointCloud<PointT>);
00667       pcl::transformPointCloud(
00668         *cloud, *flipped_cloud,
00669         Eigen::Affine3f(Eigen::AngleAxisf(M_PI, Eigen::Vector3f(0, 0, 1))));
00670       double flipped_score
00671         = alignPointcloud(flipped_cloud, reference, flipped_offset,
00672                           flipped_transformed_cloud,
00673                           flipped_transform_result);
00674       NODELET_INFO("flipped score: %f", flipped_score);
00675       if (flipped_score < score) {
00676         score = flipped_score;
00677         transformed_cloud = flipped_transformed_cloud;
00678         transform_result = flipped_transform_result;
00679         pcl::transformPointCloud(
00680           *transformed_cloud, *transformed_cloud_for_debug_result,
00681           flipped_offset.inverse());
00682         offset_result = flipped_offset;
00683       }
00684     }
00685     return score;
00686   }
00687 
00688   void ICPRegistration::referenceCallback(
00689     const sensor_msgs::PointCloud2::ConstPtr& msg)
00690   {
00691     boost::mutex::scoped_lock lock(mutex_);
00692     if (!done_init_) {
00693       NODELET_WARN("not yet initialized");
00694       return;
00695     }
00696     reference_cloud_list_.resize(0);
00697     pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
00698     pcl::fromROSMsg(*msg, *cloud);
00699     pcl::PointCloud<PointT>::Ptr non_nan_cloud (new pcl::PointCloud<PointT>);
00700     for (size_t i = 0; i < cloud->points.size(); i++) {
00701       PointT p = cloud->points[i];
00702       if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00703         non_nan_cloud->points.push_back(p);
00704       }
00705     }
00706     reference_cloud_list_.push_back(non_nan_cloud);
00707   }
00708 
00709   void ICPRegistration::referenceArrayCallback(
00710     const jsk_recognition_msgs::PointsArray::ConstPtr& msg)
00711   {
00712     boost::mutex::scoped_lock lock(mutex_);
00713     if (!done_init_) {
00714       NODELET_WARN("not yet initialized");
00715       return;
00716     }
00717     reference_cloud_list_.resize(0);
00718     for (size_t i = 0; i < msg->cloud_list.size(); i++) {
00719       pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
00720       pcl::fromROSMsg(msg->cloud_list[i], *cloud);
00721       reference_cloud_list_.push_back(cloud);
00722     }
00723   }
00724   
00725   void ICPRegistration::referenceAddCallback(
00726     const sensor_msgs::PointCloud2::ConstPtr& msg)
00727   {
00728     boost::mutex::scoped_lock lock(mutex_);
00729     if (!done_init_) {
00730       NODELET_WARN("not yet initialized");
00731       return;
00732     }
00733     //reference_cloud_list_.resize(0); //not 0
00734     pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
00735     pcl::fromROSMsg(*msg, *cloud);
00736     reference_cloud_list_.push_back(cloud);
00737     ROS_INFO("reference_num: %zd", reference_cloud_list_.size()-1);
00738   }  
00739 }
00740 
00741 #include <pluginlib/class_list_macros.h>
00742 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::ICPRegistration, nodelet::Nodelet);


jsk_pcl_ros
Author(s): Yohei Kakiuchi
autogenerated on Tue Jul 2 2019 19:41:43