00001
00024 #include "ccny_rgbd/registration/motion_estimation_icp.h"
00025
00026 namespace ccny_rgbd {
00027
00028 MotionEstimationICP::MotionEstimationICP(
00029 const ros::NodeHandle& nh,
00030 const ros::NodeHandle& nh_private):
00031 MotionEstimation(nh, nh_private)
00032 {
00033
00034
00035 int history_size;
00036
00037 if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
00038 fixed_frame_ = "/odom";
00039 if (!nh_private_.getParam ("base_frame", base_frame_))
00040 base_frame_ = "/camera_link";
00041
00042 if (!nh_private_.getParam ("reg/ICP/tf_epsilon_linear", tf_epsilon_linear_))
00043 tf_epsilon_linear_ = 1e-3;
00044 if (!nh_private_.getParam ("reg/ICP/tf_epsilon_angular", tf_epsilon_angular_))
00045 tf_epsilon_angular_ = 1.7e-2;
00046 if (!nh_private_.getParam ("reg/ICP/max_iterations", max_iterations_))
00047 max_iterations_ = 10;
00048 if (!nh_private_.getParam ("reg/ICP/min_correspondences", min_correspondences_))
00049 min_correspondences_ = 15;
00050 if (!nh_private_.getParam ("reg/ICP/max_corresp_dist_eucl", max_corresp_dist_eucl_))
00051 max_corresp_dist_eucl_ = 0.15;
00052 if (!nh_private_.getParam ("reg/ICP/publish_model_cloud", publish_model_))
00053 publish_model_ = false;
00054 if (!nh_private_.getParam ("reg/ICP/history_size", history_size))
00055 history_size = 5;
00056
00057 feature_history_.setCapacity(history_size);
00058
00059
00060
00061 max_corresp_dist_eucl_sq_ = max_corresp_dist_eucl_ * max_corresp_dist_eucl_;
00062
00063
00064
00065 f2b_.setIdentity();
00066
00067 model_ptr_.reset(new PointCloudFeature());
00068 model_ptr_->header.frame_id = fixed_frame_;
00069
00070
00071
00072 if (publish_model_)
00073 {
00074 model_publisher_ = nh_.advertise<PointCloudFeature>(
00075 "model/cloud", 1);
00076 }
00077 }
00078
00079 MotionEstimationICP::~MotionEstimationICP()
00080 {
00081
00082 }
00083
00084 bool MotionEstimationICP::getMotionEstimationImpl(
00085 RGBDFrame& frame,
00086 const tf::Transform& prediction,
00087 tf::Transform& motion)
00088 {
00090 bool result;
00091
00092
00093
00094 Vector3fVector data_means;
00095 removeInvalidMeans(frame.kp_means, frame.kp_valid, data_means);
00096 transformMeans(data_means, f2b_ * b2c_);
00097
00098
00099
00100 if (model_ptr_->points.empty())
00101 {
00102 ROS_INFO("No points in model: initializing from features.");
00103 motion.setIdentity();
00104 result = true;
00105 }
00106 else
00107 {
00108
00109 result = alignICPEuclidean(data_means, motion);
00110 }
00111
00112 if (result)
00113 {
00114 constrainMotion(motion);
00115 f2b_ = motion * f2b_;
00116 }
00117 else
00118 {
00119 feature_history_.reset();
00120 motion.setIdentity();
00121 }
00122
00123
00124
00125
00126 PointCloudFeature features;
00127 frame.constructFeaturePointCloud(features);
00128 pcl::transformPointCloud(features , features, eigenFromTf(f2b_* b2c_));
00129
00130
00131 features.header.frame_id = fixed_frame_;
00132 feature_history_.add(features);
00133
00134
00135 model_ptr_->points.clear();
00136 feature_history_.getAll(*model_ptr_);
00137 model_tree_.setInputCloud(model_ptr_);
00138
00139
00140
00141 if (publish_model_)
00142 {
00143
00144 model_ptr_->header.stamp = frame.header.stamp;
00145 model_ptr_->width = model_ptr_->points.size();
00146 model_publisher_.publish(model_ptr_);
00147 }
00148
00149 return result;
00150 }
00151
00152 bool MotionEstimationICP::alignICPEuclidean(
00153 const Vector3fVector& data_means,
00154 tf::Transform& correction)
00155 {
00156 TransformationEstimationSVD svd;
00157
00158
00159 PointCloudFeature data_cloud;
00160 pointCloudFromMeans(data_means, data_cloud);
00161
00162
00163 Eigen::Matrix4f final_transformation;
00164 final_transformation.setIdentity();
00165
00166 for (int iteration = 0; iteration < max_iterations_; ++iteration)
00167 {
00168
00169 IntVector data_indices, model_indices;
00170 getCorrespEuclidean(data_cloud, data_indices, model_indices);
00171
00172 if ((int)data_indices.size() < min_correspondences_)
00173 {
00174 ROS_WARN("[ICP] Not enough correspondences (%d of %d minimum). Leacing ICP loop",
00175 (int)data_indices.size(), min_correspondences_);
00176 return false;
00177 }
00178
00179
00180 Eigen::Matrix4f transformation;
00181 svd.estimateRigidTransformation (data_cloud, data_indices,
00182 *model_ptr_, model_indices,
00183 transformation);
00184
00185
00186 pcl::transformPointCloud(data_cloud, data_cloud, transformation);
00187
00188
00189 final_transformation = transformation * final_transformation;
00190
00191
00192 double linear, angular;
00193 getTfDifference(
00194 tfFromEigen(transformation), linear, angular);
00195 if (linear < tf_epsilon_linear_ &&
00196 angular < tf_epsilon_angular_)
00197 {
00198
00199
00200 break;
00201 }
00202 }
00203
00204 correction = tfFromEigen(final_transformation);
00205 return true;
00206 }
00207
00208 void MotionEstimationICP::getCorrespEuclidean(
00209 const PointCloudFeature& data_cloud,
00210 IntVector& data_indices,
00211 IntVector& model_indices)
00212 {
00213 for (unsigned int data_idx = 0; data_idx < data_cloud.size(); ++data_idx)
00214 {
00215 const PointFeature& data_point = data_cloud.points[data_idx];
00216
00217 int eucl_nn_idx;
00218 double eucl_dist_sq;
00219
00220 bool nn_result = getNNEuclidean(data_point, eucl_nn_idx, eucl_dist_sq);
00221
00222 if (nn_result && eucl_dist_sq < max_corresp_dist_eucl_sq_)
00223 {
00224 data_indices.push_back(data_idx);
00225 model_indices.push_back(eucl_nn_idx);
00226 }
00227 }
00228 }
00229
00230 bool MotionEstimationICP::getNNEuclidean(
00231 const PointFeature& data_point,
00232 int& eucl_nn_idx, double& eucl_dist_sq)
00233 {
00234
00235 IntVector indices;
00236 FloatVector dist_sq;
00237
00238 indices.resize(1);
00239 dist_sq.resize(1);
00240
00241 int n_retrieved = model_tree_.nearestKSearch(data_point, 1, indices, dist_sq);
00242
00243 if (n_retrieved != 0)
00244 {
00245 eucl_nn_idx = indices[0];
00246 eucl_dist_sq = dist_sq[0];
00247 return true;
00248 }
00249 else return false;
00250 }
00251
00252 }