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
00050
00051 #include <ros/ros.h>
00052
00053 #include <geometry_msgs/Point.h>
00054 #include <sensor_msgs/PointCloud.h>
00055
00056 #include <Eigen/Core>
00057 #include <point_cloud_mapping/geometry/transforms.h>
00058
00059 #include <boost/thread/mutex.hpp>
00060
00061 #include <mapping_msgs/OrientedBoundingBox.h>
00062 #include <mapping_msgs/CollisionMap.h>
00063
00064 #include <tf/transform_listener.h>
00065 #include <sys/time.h>
00066
00067 using namespace std;
00068 using namespace std_msgs;
00069 using namespace mapping_msgs;
00070
00071 struct Leaf
00072 {
00073 int i_, j_, k_;
00074 int nr_points_;
00075 };
00076
00078 bool
00079 compareLeaf (const Leaf &l1, const Leaf &l2)
00080 {
00081 if (l1.i_ < l2.i_)
00082 return (true);
00083 else if (l1.i_ > l2.i_)
00084 return (false);
00085 else if (l1.j_ < l2.j_)
00086 return (true);
00087 else if (l1.j_ > l2.j_)
00088 return (false);
00089 else if (l1.k_ < l2.k_)
00090 return (true);
00091 else
00092 return (false);
00093 }
00094
00095 class CollisionMapper
00096 {
00097 protected:
00098 ros::NodeHandle& node_;
00099 public:
00100
00101
00102 sensor_msgs::PointCloud cloud_;
00103 CollisionMap c_map_;
00104 OrientedBoundingBox box_sub_obj_;
00105
00106 tf::TransformListener tf_;
00107
00108 vector<Leaf> leaves_;
00109
00110
00111 geometry_msgs::Point leaf_width_, robot_max_;
00112 bool only_updates_, subtract_object_;
00113
00114 int min_nr_points_;
00115
00116 boost::mutex m_lock_;
00117
00118 ros::Publisher collision_map_publisher_;
00119 ros::Subscriber cloud_subscriber_;
00120 ros::Subscriber subtract_object_subscriber_;
00121
00123 CollisionMapper (ros::NodeHandle& anode) : node_ (anode)
00124 {
00125 node_.param ("leaf_width_x", leaf_width_.x, 0.015);
00126 node_.param ("leaf_width_y", leaf_width_.y, 0.015);
00127 node_.param ("leaf_width_z", leaf_width_.z, 0.015);
00128
00129 node_.param ("robot_max_x", robot_max_.x, 1.5);
00130 node_.param ("robot_max_y", robot_max_.y, 1.5);
00131 node_.param ("robot_max_z", robot_max_.z, 1.5);
00132
00133 node_.param ("min_nr_points", min_nr_points_, 1);
00134
00135 node_.param ("only_updates", only_updates_, false);
00136 node_.param ("subtract_object", subtract_object_, false);
00137
00138 if (subtract_object_)
00139 subtract_object_subscriber_ = node_.subscribe ("collision_subtract_object", 1, &CollisionMapper::subtract_cb, this);
00140
00141 ROS_INFO ("Using a default leaf of size: %g,%g,%g.", leaf_width_.x, leaf_width_.y, leaf_width_.z);
00142 ROS_INFO ("Using a maximum bounding box around the robot of size: %g,%g,%g.", robot_max_.x, robot_max_.y, robot_max_.z);
00143
00144
00145 robot_max_.x = robot_max_.x * robot_max_.x;
00146 robot_max_.y = robot_max_.y * robot_max_.y;
00147 robot_max_.z = robot_max_.z * robot_max_.z;
00148
00149 string cloud_topic ("tilt_laser_cloud");
00150 std::vector<ros::master::TopicInfo> t_list;
00151 bool topic_found = false;
00152 ros::master::getTopics (t_list);
00153 for (vector<ros::master::TopicInfo>::iterator it = t_list.begin (); it != t_list.end (); it++)
00154 {
00155 if (it->name == cloud_topic)
00156 {
00157 topic_found = true;
00158 break;
00159 }
00160 }
00161 if (!topic_found)
00162 ROS_WARN ("Trying to subscribe to %s, but the topic doesn't exist!", cloud_topic.c_str ());
00163
00164 cloud_subscriber_ = node_.subscribe (cloud_topic, 1, &CollisionMapper::cloud_cb, this);
00165 collision_map_publisher_ = node_.advertise<CollisionMap> ("collision_map", 1);
00166 }
00167
00169 virtual ~CollisionMapper () { }
00170
00172 void
00173 updateParametersFromServer ()
00174 {
00175 if (node_.hasParam ("leaf_width_x")) node_.getParam ("leaf_width_x", leaf_width_.x);
00176 if (node_.hasParam ("leaf_width_y")) node_.getParam ("leaf_width_y", leaf_width_.y);
00177 if (node_.hasParam ("leaf_width_z")) node_.getParam ("leaf_width_z", leaf_width_.z);
00178
00179 if (node_.hasParam ("robot_max_x"))
00180 {
00181 double rx;
00182 node_.getParam ("robot_max_x", rx);
00183 robot_max_.x = rx * rx;
00184 }
00185 if (node_.hasParam ("robot_max_y"))
00186 {
00187 double ry;
00188 node_.getParam ("robot_max_y", ry);
00189 robot_max_.y = ry * ry;
00190 }
00191 if (node_.hasParam ("robot_max_z"))
00192 {
00193 double rz;
00194 node_.getParam ("robot_max_z", rz);
00195 robot_max_.z = rz * rz;
00196 }
00197
00198 if (node_.hasParam ("min_nr_points")) node_.getParam ("min_nr_points", min_nr_points_);
00199 if (node_.hasParam ("only_updates")) node_.getParam ("only_updates", only_updates_);
00200 }
00201
00203 void
00204 computeCollisionMap (sensor_msgs::PointCloud *points, vector<Leaf> &leaves, CollisionMap &cmap)
00205 {
00206
00207 cmap.header = cloud_.header;
00208 cmap.boxes.resize (cloud_.points.size ());
00209
00210 geometry_msgs::PointStamped base_origin, torso_lift_origin;
00211 base_origin.point.x = base_origin.point.y = base_origin.point.z = 0.0;
00212 base_origin.header.frame_id = "torso_lift_link";
00213 base_origin.header.stamp = ros::Time();
00214
00215 try
00216 {
00217 tf_.transformPoint ("base_link", base_origin, torso_lift_origin);
00218
00219 }
00220 catch (tf::ConnectivityException)
00221 {
00222 ROS_ERROR ("TF not running or wrong TF frame specified! Defaulting to 0,0,0.");
00223 torso_lift_origin = base_origin;
00224 }
00225
00226 vector<int> indices (cloud_.points.size ());
00227 int nr_p = 0;
00228
00229 geometry_msgs::Point32 minP, maxP;
00230 minP.x = minP.y = minP.z = FLT_MAX;
00231 maxP.x = maxP.y = maxP.z = FLT_MIN;
00232 double distance_sqr_x, distance_sqr_y, distance_sqr_z;
00233 for (unsigned int i = 0; i < cloud_.points.size (); i++)
00234 {
00235
00236 distance_sqr_x = fabs ((cloud_.points[i].x - torso_lift_origin.point.x) * (cloud_.points[i].x - torso_lift_origin.point.x));
00237 distance_sqr_y = fabs ((cloud_.points[i].y - torso_lift_origin.point.y) * (cloud_.points[i].y - torso_lift_origin.point.y));
00238 distance_sqr_z = fabs ((cloud_.points[i].z - torso_lift_origin.point.z) * (cloud_.points[i].z - torso_lift_origin.point.z));
00239
00240
00241 if (distance_sqr_x < robot_max_.x && distance_sqr_y < robot_max_.y && distance_sqr_z < robot_max_.z)
00242 {
00243 minP.x = (cloud_.points[i].x < minP.x) ? cloud_.points[i].x : minP.x;
00244 minP.y = (cloud_.points[i].y < minP.y) ? cloud_.points[i].y : minP.y;
00245 minP.z = (cloud_.points[i].z < minP.z) ? cloud_.points[i].z : minP.z;
00246
00247 maxP.x = (cloud_.points[i].x > maxP.x) ? cloud_.points[i].x : maxP.x;
00248 maxP.y = (cloud_.points[i].y > maxP.y) ? cloud_.points[i].y : maxP.y;
00249 maxP.z = (cloud_.points[i].z > maxP.z) ? cloud_.points[i].z : maxP.z;
00250 indices[nr_p] = i;
00251 nr_p++;
00252 }
00253 }
00254 indices.resize (nr_p);
00255
00256
00257 geometry_msgs::Point32 minB, maxB, divB;
00258
00259 minB.x = (int)(floor (minP.x / leaf_width_.x));
00260 maxB.x = (int)(floor (maxP.x / leaf_width_.x));
00261
00262 minB.y = (int)(floor (minP.y / leaf_width_.y));
00263 maxB.y = (int)(floor (maxP.y / leaf_width_.y));
00264
00265 minB.z = (int)(floor (minP.z / leaf_width_.z));
00266 maxB.z = (int)(floor (maxP.z / leaf_width_.z));
00267
00268
00269 divB.x = maxB.x - minB.x + 1;
00270 divB.y = maxB.y - minB.y + 1;
00271 divB.z = maxB.z - minB.z + 1;
00272
00273
00274 if (leaves.capacity () < divB.x * divB.y * divB.z)
00275 leaves.reserve (divB.x * divB.y * divB.z);
00276
00277 leaves.resize (divB.x * divB.y * divB.z);
00278
00279 for (unsigned int cl = 0; cl < leaves.size (); cl++)
00280 {
00281 if (leaves[cl].nr_points_ > 0)
00282 leaves[cl].i_ = leaves[cl].j_ = leaves[cl].k_ = leaves[cl].nr_points_ = 0;
00283 }
00284
00285
00286 int i = 0, j = 0, k = 0;
00287 for (unsigned int cp = 0; cp < indices.size (); cp++)
00288 {
00289 i = (int)(floor (cloud_.points[indices.at (cp)].x / leaf_width_.x));
00290 j = (int)(floor (cloud_.points[indices.at (cp)].y / leaf_width_.y));
00291 k = (int)(floor (cloud_.points[indices.at (cp)].z / leaf_width_.z));
00292
00293 int idx = ( (k - minB.z) * divB.y * divB.x ) + ( (j - minB.y) * divB.x ) + (i - minB.x);
00294 leaves[idx].i_ = i;
00295 leaves[idx].j_ = j;
00296 leaves[idx].k_ = k;
00297 leaves[idx].nr_points_++;
00298 }
00299
00300
00301 int nr_c = 0;
00302 for (unsigned int cl = 0; cl < leaves.size (); cl++)
00303 {
00304 if (leaves[cl].nr_points_ >= min_nr_points_)
00305 {
00306 cmap.boxes[nr_c].extents.x = leaf_width_.x / 2.0;
00307 cmap.boxes[nr_c].extents.y = leaf_width_.y / 2.0;
00308 cmap.boxes[nr_c].extents.z = leaf_width_.z / 2.0;
00309 cmap.boxes[nr_c].center.x = (leaves[cl].i_ + 1) * leaf_width_.x - cmap.boxes[nr_c].extents.x;
00310 cmap.boxes[nr_c].center.y = (leaves[cl].j_ + 1) * leaf_width_.y - cmap.boxes[nr_c].extents.y;
00311 cmap.boxes[nr_c].center.z = (leaves[cl].k_ + 1) * leaf_width_.z - cmap.boxes[nr_c].extents.z;
00312 cmap.boxes[nr_c].axis.x = cmap.boxes[nr_c].axis.y = cmap.boxes[nr_c].axis.z = 0.0;
00313 cmap.boxes[nr_c].angle = 0.0;
00314 nr_c++;
00315 }
00316 }
00317 cmap.boxes.resize (nr_c);
00318
00319 sort (leaves.begin (), leaves.end (), compareLeaf);
00320 }
00321
00323 void
00324 subtractCollisionMap (vector<Leaf> *prev_model, vector<Leaf> *cur_model, CollisionMap &map)
00325 {
00326 vector<Leaf> model_difference;
00327
00328
00329 set_difference (prev_model->begin (), prev_model->end (), cur_model->begin (), cur_model->end (),
00330 inserter (model_difference, model_difference.begin ()), compareLeaf);
00331
00332
00333 int nr_c = 0;
00334 for (unsigned int cl = 0; cl < model_difference.size (); cl++)
00335 {
00336 if (model_difference[cl].nr_points_ >= min_nr_points_)
00337 {
00338 map.boxes[nr_c].extents.x = leaf_width_.x / 2.0;
00339 map.boxes[nr_c].extents.y = leaf_width_.y / 2.0;
00340 map.boxes[nr_c].extents.z = leaf_width_.z / 2.0;
00341 map.boxes[nr_c].center.x = (model_difference[cl].i_ + 1) * leaf_width_.x - map.boxes[nr_c].extents.x;
00342 map.boxes[nr_c].center.y = (model_difference[cl].j_ + 1) * leaf_width_.y - map.boxes[nr_c].extents.y;
00343 map.boxes[nr_c].center.z = (model_difference[cl].k_ + 1) * leaf_width_.z - map.boxes[nr_c].extents.z;
00344 map.boxes[nr_c].axis.x = map.boxes[nr_c].axis.y = map.boxes[nr_c].axis.z = 0.0;
00345 map.boxes[nr_c].angle = 0.0;
00346 nr_c++;
00347 }
00348 }
00349 map.boxes.resize (nr_c);
00350 }
00351
00353
00354 void
00355 cloud_cb (const sensor_msgs::PointCloudConstPtr &msg)
00356 {
00357 cloud_ = *msg;
00358 ROS_INFO ("Received %u data points.", (unsigned int)cloud_.points.size ());
00359
00360 m_lock_.lock ();
00361 updateParametersFromServer ();
00362 m_lock_.unlock ();
00363
00364
00365 ros::Time t1, t2;
00366 double time_spent;
00367
00368
00369 ROS_WARN ("Did you transform your points into the map frame today?");
00370
00371
00372 t1 = ros::Time::now();
00373
00374 if (only_updates_ && leaves_.size () > 0)
00375 {
00376 CollisionMap new_c_map;
00377 vector<Leaf> new_leaves;
00378
00379 computeCollisionMap (&cloud_, new_leaves, new_c_map);
00380
00381 c_map_.header = cloud_.header;
00382 c_map_.boxes.resize (max (new_leaves.size (), leaves_.size ()));
00383
00384 m_lock_.lock ();
00385 subtractCollisionMap (&leaves_, &new_leaves, c_map_);
00386 m_lock_.unlock ();
00387 }
00388 else
00389 {
00390 m_lock_.lock ();
00391 computeCollisionMap (&cloud_, leaves_, c_map_);
00392 m_lock_.unlock ();
00393 }
00394
00395
00396 t2 = ros::Time::now();
00397
00398 time_spent = (t2 - t1).toSec();
00399 ROS_INFO ("Collision map computed in %g seconds. Number of boxes: %u.", time_spent, (unsigned int)c_map_.boxes.size ());
00400
00401 collision_map_publisher_.publish(c_map_);
00402 }
00403
00405 inline void
00406 getRotatedBoxBounds (OrientedBoundingBox *box, Eigen::Matrix3d rotation, Eigen::Vector3d &minB, Eigen::Vector3d &maxB)
00407 {
00408
00409 minB (0) = box->center.x - box->extents.x;
00410 minB (1) = box->center.y - box->extents.y;
00411 minB (2) = box->center.z - box->extents.z;
00412
00413 maxB (0) = box->center.x + box->extents.x;
00414 maxB (1) = box->center.y + box->extents.y;
00415 maxB (2) = box->center.z + box->extents.z;
00416
00417 minB = rotation * minB;
00418 maxB = rotation * maxB;
00419 }
00420
00422 bool
00423 isBoxInsideBounds (geometry_msgs::Point32 *center, geometry_msgs::Point32 *extents, Eigen::Vector3d minB, Eigen::Vector3d maxB)
00424 {
00425
00426 float ce_x = center->x - extents->x, ce_y = center->y - extents->y, ce_z = center->z - extents->z;
00427 float cex = center->x + extents->x, cey = center->y + extents->y, cez = center->z + extents->z;
00428
00429
00430 if ( (ce_x >= minB (0) && ce_x <= maxB (0)) && (ce_y >= minB (1) && ce_y <= maxB (1)) && (ce_z >= minB (2) && ce_z <= maxB (2)) )
00431 return (true);
00432
00433 if ( (ce_x >= minB (0) && ce_x <= maxB (0)) && (cey >= minB (1) && cey <= maxB (1)) && (ce_z >= minB (2) && ce_z <= maxB (2)) )
00434 return (true);
00435
00436 if ( (cex >= minB (0) && cex <= maxB (0)) && (cey >= minB (1) && cey <= maxB (1)) && (ce_z >= minB (2) && ce_z <= maxB (2)) )
00437 return (true);
00438
00439 if ( (cex >= minB (0) && cex <= maxB (0)) && (ce_y >= minB (1) && ce_y <= maxB (1)) && (ce_z >= minB (2) && ce_z <= maxB (2)) )
00440 return (true);
00441
00442 if ( (cex >= minB (0) && cex <= maxB (0)) && (ce_y >= minB (1) && ce_y <= maxB (1)) && (cez >= minB (2) && cez <= maxB (2)) )
00443 return (true);
00444
00445 if ( (cex >= minB (0) && cex <= maxB (0)) && (cey >= minB (1) && cey <= maxB (1)) && (cez >= minB (2) && cez <= maxB (2)) )
00446 return (true);
00447
00448 if ( (ce_x >= minB (0) && ce_x <= maxB (0)) && (cey >= minB (1) && cey <= maxB (1)) && (cez >= minB (2) && cez <= maxB (2)) )
00449 return (true);
00450
00451 if ( (ce_x >= minB (0) && ce_x <= maxB (0)) && (ce_y >= minB (1) && ce_y <= maxB (1)) && (cez >= minB (2) && cez <= maxB (2)) )
00452 return (true);
00453
00454 return (false);
00455 }
00456
00458
00459 void
00460 subtract_cb (const OrientedBoundingBoxConstPtr &msg)
00461 {
00462 box_sub_obj_ = *msg;
00463 ROS_INFO ("Received OBB with the following coordinates: center [%f, %f, %f], extents [%f, %f, %f], axis [%f, %f, %f], angle [%f]",
00464 box_sub_obj_.center.x, box_sub_obj_.center.y, box_sub_obj_.center.z,
00465 box_sub_obj_.extents.x, box_sub_obj_.extents.y, box_sub_obj_.extents.z,
00466 box_sub_obj_.axis.x, box_sub_obj_.axis.y, box_sub_obj_.axis.z,
00467 box_sub_obj_.angle);
00468
00469
00470 Eigen::Matrix3d rot_mat;
00471 cloud_geometry::transforms::convertAxisAngleToRotationMatrix (box_sub_obj_.axis, -box_sub_obj_.angle, rot_mat);
00472
00473 Eigen::Vector3d minB, maxB;
00474 getRotatedBoxBounds (&box_sub_obj_, rot_mat, minB, maxB);
00475
00476 m_lock_.lock ();
00477 updateParametersFromServer ();
00478 m_lock_.unlock ();
00479
00480
00481 ros::Time t1, t2;
00482 double time_spent;
00483
00484
00485 t1 = ros::Time::now();
00486
00487
00488 m_lock_.lock ();
00489 geometry_msgs::Point32 center, extents;
00490 if (leaves_.size () > 0)
00491 {
00492
00493 for (unsigned int cl = 0; cl < leaves_.size (); cl++)
00494 {
00495 if (leaves_[cl].nr_points_ >= min_nr_points_)
00496 {
00497
00498 extents.x = leaf_width_.x / 2.0;
00499 extents.y = leaf_width_.y / 2.0;
00500 extents.z = leaf_width_.z / 2.0;
00501 center.x = (leaves_[cl].i_ + 1) * leaf_width_.x - extents.x;
00502 center.y = (leaves_[cl].j_ + 1) * leaf_width_.y - extents.y;
00503 center.z = (leaves_[cl].k_ + 1) * leaf_width_.z - extents.z;
00504
00505 bool inside = isBoxInsideBounds (¢er, &extents, minB, maxB);
00506
00507 if (inside)
00508 {
00509
00510 leaves_[cl].nr_points_ = 0;
00511 }
00512 }
00513
00514 }
00515 }
00516 m_lock_.unlock ();
00517
00518
00519 t2 = ros::Time::now();
00520
00521 time_spent = (t2 - t1).toSec();
00522 ROS_INFO ("OBB subtracted from the map in %g seconds.", time_spent);
00523 }
00524 };
00525
00526
00527 int main (int argc, char** argv)
00528 {
00529 ros::init (argc, argv, "collision_map");
00530 ros::NodeHandle ros_node("~");
00531 CollisionMapper p (ros_node);
00532 ros::spin();
00533
00534 return (0);
00535 }
00536
00537