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 #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     
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     
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     
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_;  
00241     try
00242     {
00243       
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; 
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     
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       
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     
00576     
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) { 
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     
00632     
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   
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     
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);