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_footstep_planner/footstep_state.h"
00037
00038 #include <eigen_conversions/eigen_msg.h>
00039
00040 #include <jsk_recognition_utils/pcl_conversion_util.h>
00041 #include <jsk_recognition_utils/geo_util.h>
00042 #include "jsk_footstep_planner/line2d.h"
00043
00044 namespace jsk_footstep_planner
00045 {
00046
00047 std::string projectStateToString(unsigned int state)
00048 {
00049 if (state == projection_state::success) {
00050 return "success";
00051 }
00052 else if (state == projection_state::no_pointcloud) {
00053 return "no pointcloud";
00054 }
00055 else if (state == projection_state::no_enough_support) {
00056 return "no enough support";
00057 }
00058 else if (state == projection_state::no_plane) {
00059 return "no plane";
00060 }
00061 else if (state == projection_state::no_enough_inliers) {
00062 return "no enough inliers";
00063 }
00064 else if (state == projection_state::close_to_success) {
00065 return "close to success";
00066 }
00067 else {
00068 return "unknown error";
00069 }
00070 }
00071
00072 jsk_footstep_msgs::Footstep::Ptr
00073 FootstepState::toROSMsg()
00074 {
00075 jsk_footstep_msgs::Footstep::Ptr ret(new jsk_footstep_msgs::Footstep);
00076 tf::poseEigenToMsg(pose_, ret->pose);
00077 ret->dimensions.x = dimensions_[0];
00078 ret->dimensions.y = dimensions_[1];
00079 ret->dimensions.z = dimensions_[2];
00080 ret->leg = leg_;
00081 return ret;
00082 }
00083
00084 pcl::PointIndices::Ptr
00085 FootstepState::cropPointCloudExact(pcl::PointCloud<pcl::PointNormal>::Ptr cloud,
00086 pcl::PointIndices::Ptr near_indices)
00087 {
00088
00089 Eigen::Vector3f z(0, 0, 1);
00090 Eigen::Vector3f a = pose_ * Eigen::Vector3f(dimensions_[0]/2, dimensions_[1]/2, 0);
00091 Eigen::Vector3f b = pose_ * Eigen::Vector3f(-dimensions_[0]/2, dimensions_[1]/2, 0);
00092 Eigen::Vector3f c = pose_ * Eigen::Vector3f(-dimensions_[0]/2, -dimensions_[1]/2, 0);
00093 Eigen::Vector3f d = pose_ * Eigen::Vector3f(dimensions_[0]/2, -dimensions_[1]/2, 0);
00094 Eigen::Vector3f a_2d = a + (- z.dot(a)) * z;
00095 Eigen::Vector3f b_2d = b + (- z.dot(b)) * z;
00096 Eigen::Vector3f c_2d = c + (- z.dot(c)) * z;
00097 Eigen::Vector3f d_2d = d + (- z.dot(d)) * z;
00098
00099 Eigen::Vector2f a2d(a_2d[0], a_2d[1]);
00100 Eigen::Vector2f b2d(b_2d[0], b_2d[1]);
00101 Eigen::Vector2f c2d(c_2d[0], c_2d[1]);
00102 Eigen::Vector2f d2d(d_2d[0], d_2d[1]);
00103
00104 pcl::PointIndices::Ptr ret(new pcl::PointIndices);
00105 ret->indices.reserve(near_indices->indices.size());
00106 const std::vector<int> near_indices_indices = near_indices->indices;
00107 for (size_t i = 0; i < near_indices->indices.size(); i++) {
00108 const int index = near_indices_indices[i];
00109 const pcl::PointNormal point = cloud->points[index];
00110 const Eigen::Vector3f ep = point.getVector3fMap();
00111 const Eigen::Vector3f point_2d = ep + (-z.dot(ep)) * z;
00112 const Eigen::Vector2f p2d(point_2d[0], point_2d[1]);
00113 if (cross2d((b2d - a2d), (p2d - a2d)) > 0 &&
00114 cross2d((c2d - b2d), (p2d - b2d)) > 0 &&
00115 cross2d((d2d - c2d), (p2d - c2d)) > 0 &&
00116 cross2d((a2d - d2d), (p2d - d2d)) > 0) {
00117
00118 ret->indices.push_back(index);
00119 }
00120 }
00121
00122
00123 return ret;
00124 }
00125
00126 pcl::PointIndices::Ptr
00127 FootstepState::cropPointCloud(pcl::PointCloud<pcl::PointNormal>::Ptr cloud,
00128 ANNGrid::Ptr grid_search)
00129 {
00130 pcl::PointIndices::Ptr near_indices(new pcl::PointIndices);
00131 Eigen::Vector3f a = pose_ * Eigen::Vector3f(dimensions_[0]/2, dimensions_[1]/2, 0);
00132 Eigen::Vector3f b = pose_ * Eigen::Vector3f(-dimensions_[0]/2, dimensions_[1]/2, 0);
00133 Eigen::Vector3f c = pose_ * Eigen::Vector3f(-dimensions_[0]/2, -dimensions_[1]/2, 0);
00134 Eigen::Vector3f d = pose_ * Eigen::Vector3f(dimensions_[0]/2, -dimensions_[1]/2, 0);
00135 grid_search->approximateSearchInBox(a, b, c, d, *near_indices);
00136 return cropPointCloudExact(cloud, near_indices);
00137 }
00138
00139 pcl::PointIndices::Ptr
00140 FootstepState::cropPointCloud(pcl::PointCloud<pcl::PointNormal>::Ptr cloud,
00141 pcl::search::Octree<pcl::PointNormal>& tree)
00142 {
00143 pcl::PointNormal center;
00144 center.getVector3fMap() = Eigen::Vector3f(pose_.translation());
00145 center.z = 0.0;
00146 float r = 0.2;
00147 pcl::PointIndices::Ptr near_indices(new pcl::PointIndices);
00148 std::vector<float> distances;
00149 tree.radiusSearch(center, r, near_indices->indices, distances);
00150 return cropPointCloudExact(cloud, near_indices);
00151 }
00152
00153 bool FootstepState::crossCheck(FootstepState::Ptr other)
00154 {
00155 Eigen::Vector3f a0, a1, a2, a3;
00156 Eigen::Vector3f b0, b1, b2, b3;
00157 vertices(a0, a1, a2, a3);
00158 other->vertices(b0, b1, b2, b3);
00159 Line2D a_01(a0, a1), a_12(a1, a2), a_23(a2, a3), a_30(a3, a0);
00160 Line2D b_01(b0, b1), b_12(b1, b2), b_23(b2, b3), b_30(b3, b0);
00161 return !(a_01.isCrossing(b_01) ||
00162 a_01.isCrossing(b_12) ||
00163 a_01.isCrossing(b_23) ||
00164 a_01.isCrossing(b_30) ||
00165 a_12.isCrossing(b_01) ||
00166 a_12.isCrossing(b_12) ||
00167 a_12.isCrossing(b_23) ||
00168 a_12.isCrossing(b_30) ||
00169 a_23.isCrossing(b_01) ||
00170 a_23.isCrossing(b_12) ||
00171 a_23.isCrossing(b_23) ||
00172 a_23.isCrossing(b_30) ||
00173 a_30.isCrossing(b_01) ||
00174 a_30.isCrossing(b_12) ||
00175 a_30.isCrossing(b_23) ||
00176 a_30.isCrossing(b_30));
00177 }
00178
00179 FootstepState::Ptr
00180 FootstepState::projectToCloud(pcl::KdTreeFLANN<pcl::PointNormal>& tree,
00181 pcl::PointCloud<pcl::PointNormal>::Ptr cloud,
00182 ANNGrid::Ptr grid_search,
00183 pcl::search::Octree<pcl::PointNormal>& tree_2d,
00184 pcl::PointCloud<pcl::PointNormal>::Ptr cloud_2d,
00185 const Eigen::Vector3f& z,
00186 unsigned int& error_state,
00187 double outlier_threshold,
00188 int max_iterations,
00189 int min_inliers,
00190 int foot_x_sampling_num,
00191 int foot_y_sampling_num,
00192 double vertex_threshold,
00193 const bool skip_cropping)
00194 {
00195
00196
00197
00198
00199 pcl::PointIndices::Ptr indices;
00200 FootstepSupportState presupport_state;
00201 if (skip_cropping) {
00202 presupport_state = isSupportedByPointCloudWithoutCropping(
00203 pose_, cloud, tree,
00204 indices, foot_x_sampling_num, foot_y_sampling_num, vertex_threshold);
00205 }
00206 indices = cropPointCloud(cloud, grid_search);
00207 if (indices->indices.size() < min_inliers) {
00208 error_state = projection_state::no_enough_inliers;
00209 return FootstepState::Ptr();
00210 }
00211
00212 else {
00213 presupport_state = isSupportedByPointCloud(
00214 pose_, cloud, tree,
00215 indices, foot_x_sampling_num, foot_y_sampling_num, vertex_threshold);
00216 }
00217 if (presupport_state == projection_state::success) {
00218 return FootstepState::Ptr(new FootstepState(leg_, pose_, dimensions_,
00219 resolution_,
00220 index_x_,
00221 index_y_,
00222 index_yaw_));
00223 }
00224
00225 pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
00226 pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
00227 pcl::SACSegmentation<pcl::PointNormal> seg;
00228 seg.setOptimizeCoefficients (true);
00229 seg.setRadiusLimits(0.3, std::numeric_limits<double>::max ());
00230 seg.setMethodType(pcl::SAC_RANSAC);
00231 seg.setDistanceThreshold(outlier_threshold);
00232 seg.setModelType(pcl::SACMODEL_PLANE);
00233 seg.setInputCloud(cloud);
00234 seg.setIndices(indices);
00235 seg.setMaxIterations(max_iterations);
00236 seg.segment (*inliers, *coefficients);
00237 if (inliers->indices.size() == 0) {
00238 error_state = projection_state::no_plane;
00239 return FootstepState::Ptr();
00240 }
00241 else if (inliers->indices.size() < min_inliers) {
00242 error_state = projection_state::no_enough_inliers;
00243 return FootstepState::Ptr();
00244 }
00245 else {
00246 jsk_recognition_utils::Plane plane(coefficients->values);
00247 if (!plane.isSameDirection(z)) {
00248 plane = plane.flip();
00249 }
00250 Eigen::Vector3f n = plane.getNormal();
00251 Eigen::Vector3f x = pose_.matrix().block<3, 3>(0, 0) * Eigen::Vector3f::UnitX();
00252 if (acos(n.dot(x)) == 0) {
00253 error_state = projection_state::vertical_footstep;
00254 return FootstepState::Ptr();
00255 }
00256 Eigen::Vector3f rotation_axis = n.cross(x);
00257 Eigen::Vector3f new_x = Eigen::AngleAxisf(M_PI / 2.0, rotation_axis) * n;
00258 if (acos(new_x.dot(x)) > M_PI / 2.0) {
00259 new_x = - new_x;
00260 }
00261 Eigen::Vector3f new_y = n.cross(new_x);
00262 Eigen::Matrix3f new_rot_mat;
00263 new_rot_mat << new_x, new_y, n;
00264 Eigen::Quaternionf new_rot(new_rot_mat);
00265 Eigen::Vector3f p(pose_.translation());
00266 double alpha = (- plane.getD() - n.dot(p)) / (n.dot(z));
00267 Eigen::Vector3f q = p + alpha * z;
00268 Eigen::Affine3f new_pose = Eigen::Translation3f(q) * new_rot;
00269
00270 FootstepSupportState support_state;
00271 if (skip_cropping) {
00272 support_state = isSupportedByPointCloudWithoutCropping(
00273 new_pose, cloud, tree,
00274 inliers, foot_x_sampling_num, foot_y_sampling_num, vertex_threshold);
00275 }
00276 else {
00277 support_state = isSupportedByPointCloud(
00278 new_pose, cloud, tree,
00279 inliers, foot_x_sampling_num, foot_y_sampling_num, vertex_threshold);
00280 }
00281 if (support_state == NOT_SUPPORTED) {
00282 error_state = projection_state::no_enough_support;
00283 return FootstepState::Ptr();
00284 }
00285 else if (support_state == CLOSE_TO_SUPPORTED) {
00286 error_state = projection_state::close_to_success;
00287 return FootstepState::Ptr();
00288 }
00289 else {
00290 error_state = projection_state::success;
00291 return FootstepState::Ptr(new FootstepState(leg_, new_pose, dimensions_,
00292 resolution_,
00293 index_x_,
00294 index_y_,
00295 index_yaw_));
00296 }
00297 }
00298 }
00299
00300 FootstepSupportState
00301 FootstepState::isSupportedByPointCloud(const Eigen::Affine3f& pose,
00302 pcl::PointCloud<pcl::PointNormal>::Ptr cloud,
00303 pcl::KdTreeFLANN<pcl::PointNormal>& tree,
00304 pcl::PointIndices::Ptr inliers,
00305 const int foot_x_sampling_num,
00306 const int foot_y_sampling_num,
00307 const double vertex_threshold)
00308 {
00309 const double dx = dimensions_[0] / foot_x_sampling_num;
00310 const double dy = dimensions_[1] / foot_y_sampling_num;
00311
00312 const Eigen::Vector3f ux = Eigen::Vector3f::UnitX();
00313 const Eigen::Vector3f uy = Eigen::Vector3f::UnitY();
00314 const Eigen::Affine3f new_origin = pose *
00315 Eigen::Translation3f(- ux * dimensions_[0] / 2.0) *
00316 Eigen::Translation3f(- uy * dimensions_[1] / 2.0);
00317 const Eigen::Affine3f inv_pose = new_origin.inverse();
00318
00319 bool occupiedp[foot_x_sampling_num][foot_y_sampling_num];
00320
00321 for (size_t i = 0; i < foot_x_sampling_num; i++) {
00322 for (size_t j = 0; j < foot_y_sampling_num; j++) {
00323 occupiedp[i][j] = false;
00324 }
00325 }
00326
00327 for (size_t i = 0; i < inliers->indices.size(); i++) {
00328 pcl::PointNormal pp = cloud->points[inliers->indices[i]];
00329 const Eigen::Vector3f p = pp.getVector3fMap();
00330 const Eigen::Vector3f local_p = inv_pose * p;
00331 const int nx = floor(local_p[0] / dx);
00332 const int ny = floor(local_p[1] / dy);
00333
00334 if (0 <= nx && nx < foot_x_sampling_num &&
00335 0 <= ny && ny < foot_y_sampling_num) {
00336 occupiedp[nx][ny] = true;
00337 }
00338 }
00339 for (size_t i = 0; i < foot_x_sampling_num; i++) {
00340 for (size_t j = 0; j < foot_y_sampling_num; j++) {
00341 if (!occupiedp[i][j]) {
00342 return NOT_SUPPORTED;
00343 }
00344 }
00345 }
00346
00347 Eigen::Vector3f a((pose * Eigen::Translation3f(ux * dimensions_[0] / 2 + uy * dimensions_[1] / 2)).translation());
00348 Eigen::Vector3f b((pose * Eigen::Translation3f(- ux * dimensions_[0] / 2 + uy * dimensions_[1] / 2)).translation());
00349 Eigen::Vector3f c((pose * Eigen::Translation3f(- ux * dimensions_[0] / 2 - uy * dimensions_[1] / 2)).translation());
00350 Eigen::Vector3f d((pose * Eigen::Translation3f(ux * dimensions_[0] / 2 - uy * dimensions_[1] / 2)).translation());
00351 pcl::PointNormal pa, pb, pc, pd, p;
00352 pa.getVector3fMap() = a;
00353 pb.getVector3fMap() = b;
00354 pc.getVector3fMap() = c;
00355 pd.getVector3fMap() = d;
00356 p.getVector3fMap() = Eigen::Vector3f(pose.translation());
00357 std::vector<int> kdl_indices;
00358 std::vector<float> kdl_distances;
00359 if (tree.radiusSearch(p, vertex_threshold, kdl_indices, kdl_distances, 1) > 0 &&
00360 tree.radiusSearch(pa, vertex_threshold, kdl_indices, kdl_distances, 1) > 0 &&
00361 tree.radiusSearch(pb, vertex_threshold, kdl_indices, kdl_distances, 1) > 0 &&
00362 tree.radiusSearch(pc, vertex_threshold, kdl_indices, kdl_distances, 1) > 0 &&
00363 tree.radiusSearch(pd, vertex_threshold, kdl_indices, kdl_distances, 1) > 0) {
00364 return SUPPORTED;
00365 }
00366 else {
00367 return CLOSE_TO_SUPPORTED;
00368 }
00369 }
00370
00371 FootstepSupportState
00372 FootstepState::isSupportedByPointCloudWithoutCropping(
00373 const Eigen::Affine3f& pose,
00374 pcl::PointCloud<pcl::PointNormal>::Ptr cloud,
00375 pcl::KdTreeFLANN<pcl::PointNormal>& tree,
00376 pcl::PointIndices::Ptr inliers,
00377 const int foot_x_sampling_num,
00378 const int foot_y_sampling_num,
00379 const double vertex_threshold)
00380 {
00381 const Eigen::Vector3f ux = Eigen::Vector3f::UnitX();
00382 const Eigen::Vector3f uy = Eigen::Vector3f::UnitY();
00383 Eigen::Vector3f a((pose * Eigen::Translation3f(ux * dimensions_[0] / 2 + uy * dimensions_[1] / 2)).translation());
00384 Eigen::Vector3f b((pose * Eigen::Translation3f(- ux * dimensions_[0] / 2 + uy * dimensions_[1] / 2)).translation());
00385 Eigen::Vector3f c((pose * Eigen::Translation3f(- ux * dimensions_[0] / 2 - uy * dimensions_[1] / 2)).translation());
00386 Eigen::Vector3f d((pose * Eigen::Translation3f(ux * dimensions_[0] / 2 - uy * dimensions_[1] / 2)).translation());
00387 pcl::PointNormal pa, pb, pc, pd, p;
00388 pa.getVector3fMap() = a;
00389 pb.getVector3fMap() = b;
00390 pc.getVector3fMap() = c;
00391 pd.getVector3fMap() = d;
00392 p.getVector3fMap() = Eigen::Vector3f(pose.translation());
00393 std::vector<int> kdl_indices;
00394 std::vector<float> kdl_distances;
00395 size_t success_count = 0;
00396
00397 if (tree.radiusSearch(p, vertex_threshold, kdl_indices, kdl_distances, 1) > 0) {
00398 ++success_count;
00399 }
00400 if (tree.radiusSearch(pa, vertex_threshold, kdl_indices, kdl_distances, 1) > 0) {
00401 ++success_count;
00402 }
00403 if (tree.radiusSearch(pb, vertex_threshold, kdl_indices, kdl_distances, 1) > 0) {
00404 ++success_count;
00405 }
00406 if (tree.radiusSearch(pc, vertex_threshold, kdl_indices, kdl_distances, 1) > 0) {
00407 ++success_count;
00408 }
00409 if (tree.radiusSearch(pd, vertex_threshold, kdl_indices, kdl_distances, 1) > 0) {
00410 ++success_count;
00411 }
00412 if (success_count == 5) {
00413 return SUPPORTED;
00414 }
00415 else if (success_count > 0) {
00416 return CLOSE_TO_SUPPORTED;
00417 }
00418 else {
00419 return NOT_SUPPORTED;
00420 }
00421 }
00422
00423
00424
00425 FootstepState::Ptr FootstepState::fromROSMsg(const jsk_footstep_msgs::Footstep& f,
00426 const Eigen::Vector3f& size,
00427 const Eigen::Vector3f& resolution)
00428 {
00429 Eigen::Affine3f pose;
00430 tf::poseMsgToEigen(f.pose, pose);
00431 return FootstepState::Ptr(new FootstepState(
00432 f.leg, pose,
00433 size, resolution));
00434 }
00435
00436 }