$search
00001 /********************************************************************* 00002 * 00003 * Software License Agreement (BSD License) 00004 * 00005 * Copyright (c) 2010, Willow Garage, Inc. 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/or other materials provided 00017 * with the distribution. 00018 * * Neither the name of the Willow Garage 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 * \author Adam Harmat, Gil E. Jones, Kai M. Wurm, Armin Hornung 00036 *********************************************************************/ 00037 00038 #include "collider/collider.h" 00039 #include <planning_environment/monitors/monitor_utils.h> 00040 00041 00042 Collider::Collider(): root_handle_(""), pruning_counter_(0), transparent_freespace_(false) { 00043 00044 ros::NodeHandle priv("~"); 00045 00046 cm_ = new planning_environment::CollisionModels("robot_description"); 00047 fixed_frame_ = cm_->getWorldFrameId(); 00048 00049 attached_color_.a = 0.5; 00050 attached_color_.r = 0.6; 00051 attached_color_.g = 0.4; 00052 attached_color_.b = 0.3; 00053 00054 priv.param<double>("resolution", resolution_, 0.1); 00055 priv.param<double>("max_range", max_range_, -1.0); // default: full beam length 00056 priv.param<int>("pruning_period", pruning_period_, 5); 00057 00058 priv.param<bool>("publish_static_over_dynamic_map", publish_over_dynamic_map_, false); 00059 00060 // bounds of collision map in fixed frame 00061 priv.param<double>("dimension_x", workspace_box_.dimensionX, 0.0); 00062 priv.param<double>("dimension_y", workspace_box_.dimensionY, 0.0); 00063 priv.param<double>("dimension_z", workspace_box_.dimensionZ, 0.0); 00064 00065 // origin of collision map in the fixed frame 00066 priv.param<double>("origin_x", workspace_box_.originX, 0.0); 00067 priv.param<double>("origin_y", workspace_box_.originY, 0.0); 00068 priv.param<double>("origin_z", workspace_box_.originZ, 0.0); 00069 00070 // compute some useful values 00071 workspace_box_.real_minX = -workspace_box_.dimensionX + workspace_box_.originX; 00072 workspace_box_.real_maxX = workspace_box_.dimensionX + workspace_box_.originX; 00073 workspace_box_.real_minY = -workspace_box_.dimensionY + workspace_box_.originY; 00074 workspace_box_.real_maxY = workspace_box_.dimensionY + workspace_box_.originY; 00075 workspace_box_.real_minZ = -workspace_box_.dimensionZ + workspace_box_.originZ; 00076 workspace_box_.real_maxZ = workspace_box_.dimensionZ + workspace_box_.originZ; 00077 00078 // stereo cam params for sensor cone: 00079 priv.param<int>("camera_stereo_offset_left", camera_stereo_offset_left_, 128); 00080 priv.param<int>("camera_stereo_offset_right", camera_stereo_offset_right_, 0); 00081 00082 00083 collision_octree_ = new OcTreeType(resolution_); 00084 double probHit = 0.7; 00085 double probMiss = 0.4; 00086 double thresMin = 0.12; 00087 double thresMax = 0.97; 00088 priv.param("sensor_model_hit", probHit, probHit); 00089 priv.param("sensor_model_miss", probMiss, probMiss); 00090 priv.param("sensor_model_min", thresMin, thresMin); 00091 priv.param("sensor_model_max", thresMax, thresMax); 00092 collision_octree_->setProbHit(probHit); 00093 collision_octree_->setProbMiss(probMiss); 00094 collision_octree_->setClampingThresMin(thresMin); 00095 collision_octree_->setClampingThresMax(thresMax); 00096 00097 marker_pub_ = root_handle_.advertise<visualization_msgs::Marker>("visualization_marker", 10); 00098 octomap_visualization_pub_ = root_handle_.advertise<visualization_msgs::Marker>("occupied_cells", 10); 00099 octomap_visualization_free_pub_ = root_handle_.advertise<visualization_msgs::Marker>("free_cells", 10); 00100 octomap_visualization_attached_pub_ = root_handle_.advertise<visualization_msgs::Marker>("attached_objects", 10); 00101 octomap_visualization_attached_array_pub_ = root_handle_.advertise<visualization_msgs::MarkerArray>("attached_objects_array", 10); 00102 cmap_publisher_ = root_handle_.advertise<arm_navigation_msgs::CollisionMap>("collision_map_out", 1, true); 00103 static_map_publisher_ = root_handle_.advertise<arm_navigation_msgs::CollisionMap>("collision_map_occ_static", 1); 00104 pointcloud_publisher_ = root_handle_.advertise<sensor_msgs::PointCloud2>("point_cloud_out", 1, true); 00105 00106 color_occupied_.r = 0; 00107 color_occupied_.g = 0; 00108 color_occupied_.b = 1.0; 00109 color_occupied_.a = 0.5; 00110 00111 color_free_.r = 0; 00112 color_free_.g = 1.0; 00113 color_free_.b = 0.0; 00114 color_free_.a = 0.5; 00115 00116 // create a self mask with links: (see self_see_filter.h) 00117 double default_padding, default_scale; 00118 priv.param<double> ("self_see_default_padding", default_padding, .01); 00119 priv.param<double> ("self_see_default_scale", default_scale, 1.0); 00120 priv.param<double> ("min_sensor_dist", self_filter_min_dist_, 0.05); 00121 00122 std::vector<robot_self_filter::LinkInfo> links; 00123 if (!priv.hasParam ("self_see_links")) 00124 { 00125 ROS_WARN ("No links specified for self filtering."); 00126 } 00127 else 00128 { 00129 XmlRpc::XmlRpcValue ssl_vals;; 00130 00131 priv.getParam ("self_see_links", ssl_vals); 00132 if (ssl_vals.getType () != XmlRpc::XmlRpcValue::TypeArray) 00133 { 00134 ROS_WARN ("Self see links need to be an array"); 00135 } 00136 else 00137 { 00138 if (ssl_vals.size () == 0) 00139 { 00140 ROS_WARN ("No values in self see links array"); 00141 } 00142 else 00143 { 00144 for (int i = 0; i < ssl_vals.size (); ++i) 00145 { 00146 robot_self_filter::LinkInfo li; 00147 00148 if (ssl_vals[i].getType() != XmlRpc::XmlRpcValue::TypeStruct) 00149 { 00150 ROS_WARN ("Self see links entry %d is not a structure. Stopping processing of self see links", i); 00151 break; 00152 } 00153 if (!ssl_vals[i].hasMember ("name")) 00154 { 00155 ROS_WARN ("Self see links entry %d has no name. Stopping processing of self see links", i); 00156 break; 00157 } 00158 li.name = std::string (ssl_vals[i]["name"]); 00159 if (!ssl_vals[i].hasMember ("padding")) 00160 { 00161 ROS_DEBUG ("Self see links entry %d has no padding. Assuming default padding of %g", i, default_padding); 00162 li.padding = default_padding; 00163 } 00164 else 00165 { 00166 li.padding = ssl_vals[i]["padding"]; 00167 } 00168 if (!ssl_vals[i].hasMember ("scale")) 00169 { 00170 ROS_DEBUG ("Self see links entry %d has no scale. Assuming default scale of %g", i, default_scale); 00171 li.scale = default_scale; 00172 } 00173 else 00174 { 00175 li.scale = ssl_vals[i]["scale"]; 00176 } 00177 links.push_back (li); 00178 } 00179 } 00180 } 00181 } 00182 00183 00184 robot_mask_right_ = new robot_self_filter::SelfMask(tf_, links); 00185 robot_mask_left_ = new robot_self_filter::SelfMask(tf_, links); 00186 ROS_INFO_STREAM("Robot self mask initialized with " << links.size() << " links"); 00187 00188 00189 00190 cam_model_initialized_ = false; 00191 camera_info_subscriber_ = new ros::Subscriber; 00192 std::string camera_info_topic; 00193 priv.getParam("camera_info_topic", camera_info_topic); 00194 (*camera_info_subscriber_) = root_handle_.subscribe( camera_info_topic , 10, 00195 &Collider::cameraInfoCallback, this); 00196 00197 00198 00199 if(!priv.hasParam("cloud_sources")) { 00200 //ROS_WARN("No sensor sources specified, please set config correctly (e.g., config/sources.yaml)"); 00201 //return; 00202 } 00203 else 00204 { 00205 XmlRpc::XmlRpcValue cloud_sources; 00206 priv.getParam("cloud_sources", cloud_sources); 00207 00208 if(cloud_sources.getType() != XmlRpc::XmlRpcValue::TypeArray) { 00209 ROS_WARN("Cloud sources needs to be an array"); 00210 } 00211 else 00212 { 00213 for(int i = 0; i < cloud_sources.size(); ++i) 00214 { 00215 CloudInfo cinfo; 00216 if(cloud_sources[i].getType() != XmlRpc::XmlRpcValue::TypeStruct) 00217 { 00218 ROS_WARN("Cloud source entry %d is not a structure. Stopping processing of cloud sources",i); 00219 break; 00220 } 00221 00222 if(!cloud_sources[i].hasMember("name")) 00223 { 00224 ROS_WARN("Cloud sources entry %d has no name. Stopping processing of cloud sources",i); 00225 break; 00226 } 00227 00228 if(cloud_sources[i].hasMember("raw_name")) 00229 { 00230 cinfo.raw_cloud_name_ = std::string(cloud_sources[i]["raw_name"]); 00231 00232 if (!cloud_sources[i].hasMember("sensor_stereo_other_frame")){ 00233 ROS_WARN("Cloud sources entry %d has a raw_name but no sensor_stereo_other_frame. Stopping processing of cloud sources",i); 00234 break; 00235 } else{ 00236 cinfo.sensor_stereo_other_frame_ = std::string(cloud_sources[i]["sensor_stereo_other_frame"]); 00237 } 00238 00239 } 00240 00241 if(!cloud_sources[i].hasMember("sensor_frame")) 00242 { 00243 ROS_WARN("Cloud sources entry %d has no sensor_frame. Stopping processing of cloud sources",i); 00244 break; 00245 } 00246 00247 cinfo.cloud_name_ = std::string(cloud_sources[i]["name"]); 00248 cinfo.sensor_frame_ = std::string(cloud_sources[i]["sensor_frame"]); 00249 00250 if(!cloud_sources[i].hasMember("frame_subsample")) 00251 { 00252 ROS_DEBUG("Cloud sources entry %d has no frame subsample. Assuming default subsample of %g",i,1.0); 00253 cinfo.frame_subsample_ = 1.0;; 00254 } 00255 else 00256 { 00257 cinfo.frame_subsample_ = cloud_sources[i]["frame_subsample"]; 00258 } 00259 00260 if(!cloud_sources[i].hasMember("point_subsample")) 00261 { 00262 ROS_DEBUG("Cloud sources entry %d has no point subsample. Assuming default subsample of %g",i,1.0); 00263 cinfo.point_subsample_ = 1.0;; 00264 } 00265 else 00266 { 00267 cinfo.point_subsample_ = cloud_sources[i]["point_subsample"]; 00268 } 00269 00270 if(cloud_sources[i].hasMember("dynamic_buffer_size")) 00271 cinfo.dynamic_buffer_size_ = static_cast<unsigned int>(int(cloud_sources[i]["dynamic_buffer_size"])); 00272 00273 if(cloud_sources[i].hasMember("static_buffer_size")) 00274 cinfo.static_buffer_size_ = static_cast<unsigned int>(int(cloud_sources[i]["static_buffer_size"])); 00275 00276 if(cloud_sources[i].hasMember("dynamic_buffer_duration")) 00277 cinfo.dynamic_buffer_duration_ = ros::Duration(int(cloud_sources[i]["dynamic_buffer_duration"])); 00278 00279 if(cloud_sources[i].hasMember("static_buffer_duration")) 00280 cinfo.static_buffer_duration_ = ros::Duration(int(cloud_sources[i]["static_buffer_duration"])); 00281 00282 if(cloud_sources[i].hasMember("dynamic_publish")) 00283 cinfo.dynamic_publish_ = cloud_sources[i]["dynamic_publish"]; 00284 00285 if(cloud_sources[i].hasMember("static_publish")) 00286 cinfo.static_publish_ = cloud_sources[i]["static_publish"]; 00287 00288 if(cloud_sources[i].hasMember("dynamic_until_static_publish")) { 00289 cinfo.dynamic_until_static_publish_ = cloud_sources[i]["dynamic_until_static_publish"]; 00290 } 00291 00292 if(cloud_sources_.find(cinfo.cloud_name_) != cloud_sources_.end()) 00293 { 00294 ROS_WARN_STREAM("Already have a cloud defined with name " << cinfo.cloud_name_ << ", using prior values"); 00295 } 00296 else 00297 { 00298 message_filters::Subscriber<sensor_msgs::PointCloud2>* sub = new message_filters::Subscriber<sensor_msgs::PointCloud2>(); 00299 sub->subscribe(root_handle_, cinfo.cloud_name_, 3); 00300 sub_filtered_clouds_.push_back(sub); 00301 if(cinfo.raw_cloud_name_ != "") { 00302 message_filters::Subscriber<sensor_msgs::PointCloud2>* sub_raw = new message_filters::Subscriber<sensor_msgs::PointCloud2>(); 00303 sub_raw->subscribe(root_handle_,cinfo.raw_cloud_name_, 3); 00304 message_filters::Synchronizer<message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, sensor_msgs::PointCloud2> >* sync = 00305 new message_filters::Synchronizer<message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, sensor_msgs::PointCloud2> >(3); 00306 sub_raw_clouds_.push_back(sub_raw); 00307 sync->connectInput (*sub, *sub_raw); 00308 sync->registerCallback(bind (&Collider::cloudCombinedCallback, this, _1, _2, cinfo.cloud_name_)); 00309 sync_vector_.push_back(sync); 00310 ROS_INFO_STREAM("Adding synced callback for cloud " << cinfo.cloud_name_ << " raw " << cinfo.raw_cloud_name_); 00311 } else { 00312 sub->registerCallback(boost::bind(&Collider::cloudCallback, this, _1, cinfo.cloud_name_)); 00313 ROS_INFO_STREAM("Adding callback for " << cinfo.cloud_name_); 00314 } 00315 cloud_sources_[cinfo.cloud_name_] = cinfo; 00316 } 00317 } 00318 } 00319 } 00320 00321 attached_collision_object_subscriber_ = new message_filters::Subscriber<arm_navigation_msgs::AttachedCollisionObject>(root_handle_, "attached_collision_object", 1024); 00322 attached_collision_object_subscriber_->registerCallback(boost::bind(&Collider::attachedObjectCallback, this, _1)); 00323 00324 collision_object_subscriber_ = new message_filters::Subscriber<arm_navigation_msgs::CollisionObject>(root_handle_, "collision_object", 1024); 00325 collision_object_subscriber_->registerCallback(boost::bind(&Collider::objectCallback, this, _1)); 00326 00327 reset_service_ = priv.advertiseService("reset", &Collider::reset, this); 00328 dummy_reset_service_ = priv.advertiseService("dummy_reset", &Collider::dummyReset, this); 00329 00330 action_server_.reset( new actionlib::SimpleActionServer<arm_navigation_msgs::MakeStaticCollisionMapAction>(root_handle_, "make_static_collision_map", boost::bind(&Collider::makeStaticCollisionMap, this, _1), false)); 00331 action_server_->start(); 00332 00333 // queries on the map: 00334 get_octomap_service_ = root_handle_.advertiseService("octomap_binary", &Collider::octomapSrv, this); 00335 occupancy_point_service_ = root_handle_.advertiseService("occupancy_point", &Collider::occupancyPointSrv, this); 00336 occupancy_bbx_service_ = root_handle_.advertiseService("occupancy_in_bbx", &Collider::occupancyBBXSrv, this); 00337 occupancy_bbx_size_service_ = root_handle_.advertiseService("occupancy_in_bbx_size", &Collider::occupancyBBXSizeSrv, this); 00338 00339 } 00340 00341 00342 Collider::~Collider() { 00343 00344 delete collision_octree_; 00345 delete robot_mask_right_; 00346 delete robot_mask_left_; 00347 delete attached_collision_object_subscriber_; 00348 delete collision_object_subscriber_; 00349 00350 for(unsigned int i = 0; i < sub_filtered_clouds_.size(); ++i){ 00351 delete sub_filtered_clouds_[i]; 00352 } 00353 for(unsigned int i = 0; i < sub_raw_clouds_.size(); ++i) { 00354 delete sub_raw_clouds_[i]; 00355 } 00356 for(unsigned int i = 0; i < sync_vector_.size(); ++i) { 00357 delete sync_vector_[i]; 00358 } 00359 } 00360 00361 void Collider::attachedObjectCallback(const arm_navigation_msgs::AttachedCollisionObjectConstPtr& attached_object) { 00362 planning_environment::processAttachedCollisionObjectMsg(attached_object, tf_, cm_); 00363 } 00364 00365 void Collider::objectCallback(const arm_navigation_msgs::CollisionObjectConstPtr& object) { 00366 planning_environment::processCollisionObjectMsg(object, tf_, cm_); 00367 } 00368 00369 void Collider::cameraInfoCallback(const sensor_msgs::CameraInfo::ConstPtr &cam_info){ 00370 ROS_DEBUG("Got camera info: %d x %d\n", cam_info->height, cam_info->width); 00371 cam_model_.fromCameraInfo(*cam_info); 00372 cam_size_ = cam_model_.fullResolution(); 00373 cam_model_initialized_ = true; 00374 delete camera_info_subscriber_; 00375 } 00376 00377 void Collider::cloudCombinedCallback(const sensor_msgs::PointCloud2::ConstPtr &cloud, 00378 const sensor_msgs::PointCloud2::ConstPtr &cloud_raw, 00379 const std::string topic_name) { 00380 00381 CloudInfo settings = cloud_sources_[topic_name]; 00382 00383 ros::WallTime begin_cb = ros::WallTime::now(); 00384 00385 if (!cam_model_initialized_) { 00386 ROS_INFO ("ERROR: camera model not initialized."); 00387 return; 00388 } 00389 00390 // Use old callback with cloud (filtered) 00391 // cloudCallback (cloud, "full_cloud_filtered"); 00392 // For clearing use cloud_raw to check projections 00393 // (cloud_raw.{width,height}) 00394 00395 // transform pointcloud from sensor frame to fixed_frame_ 00396 if (!tf_.waitForTransform(fixed_frame_, cloud_raw->header.frame_id, cloud->header.stamp, ros::Duration(1.0))) { 00397 ROS_WARN_STREAM( "Timed out waiting for transform from " << cloud_raw->header.frame_id 00398 << " to " << fixed_frame_ << ", quitting callback"); 00399 return; 00400 } 00401 00402 // transform pointcloud from sensor frame to fixed_frame_ 00403 if (!tf_.waitForTransform(fixed_frame_, cloud->header.frame_id, cloud->header.stamp, ros::Duration(1.0))) { 00404 ROS_WARN_STREAM( "Timed out waiting for transform from " << cloud->header.frame_id 00405 << " to " << fixed_frame_ << ", quitting callback"); 00406 return; 00407 } 00408 00409 00410 tf::StampedTransform trans; 00411 tf_.lookupTransform (fixed_frame_, cloud->header.frame_id, cloud->header.stamp, trans); 00412 tf::Transform to_world = trans; 00413 00414 ros::WallTime begin_transform = ros::WallTime::now(); 00415 Eigen::Matrix4f eigen_transform; 00416 sensor_msgs::PointCloud2 transformed_cloud; 00417 // // cturtle: 00418 // pcl::transformAsMatrix (to_world, eigen_transform); 00419 // pcl::transformPointCloud (eigen_transform, *cloud, transformed_cloud); 00420 // // dturtle: 00421 pcl_ros::transformAsMatrix (to_world, eigen_transform); 00422 pcl_ros::transformPointCloud (eigen_transform, *cloud, transformed_cloud); 00423 00424 pcl::PointCloud<pcl::PointXYZ> pcl_cloud; 00425 pcl::fromROSMsg (transformed_cloud, pcl_cloud); 00426 00427 pcl_cloud.header.frame_id = cm_->getWorldFrameId(); 00428 pcl_cloud.header.stamp = cloud->header.stamp; 00429 00430 std::vector<int> inside_mask; 00431 //filtering out attached object inside points 00432 if(planning_environment::computeAttachedObjectPointCloudMask(pcl_cloud, 00433 cm_->getWorldFrameId(), 00434 cm_, 00435 tf_, 00436 inside_mask)) { 00437 pcl::PointCloud<pcl::PointXYZ>::iterator it = pcl_cloud.points.begin(); 00438 unsigned int i = 0; 00439 unsigned int count = 0; 00440 while(it != pcl_cloud.points.end()) { 00441 if(inside_mask[i++] == robot_self_filter::INSIDE) { 00442 it = pcl_cloud.points.erase(it); 00443 count++; 00444 } else { 00445 it++; 00446 } 00447 } 00448 ROS_DEBUG_STREAM("Filtering " << count << " points"); 00449 } 00450 00451 { 00452 planning_models::KinematicState state(cm_->getKinematicModel()); 00453 state.setKinematicStateToDefault(); 00454 00455 planning_environment::updateAttachedObjectBodyPoses(cm_, 00456 state, 00457 tf_); 00458 00459 visualization_msgs::MarkerArray arr; 00460 cm_->getAttachedCollisionObjectMarkers(state, 00461 arr, 00462 "filter_attached", 00463 attached_color_, 00464 ros::Duration(.2)); 00465 00466 octomap_visualization_attached_array_pub_.publish(arr); 00467 } 00468 00469 std_msgs::Header global_header = cloud->header; 00470 global_header.frame_id = fixed_frame_; 00471 00472 // pcl::transformPointCloud (fixed_frame_, *cloud, transformed_cloud, tf_); 00473 // pcl::PointCloud<pcl::PointXYZ> pcl_cloud; 00474 // pcl::fromROSMsg (transformed_cloud, pcl_cloud); 00475 00476 00477 pcl::PointCloud<pcl::PointXYZ> pcl_cloud_raw; 00478 pcl::fromROSMsg (*cloud_raw, pcl_cloud_raw); 00479 00480 // copy data to octomap pointcloud 00481 octomap::Pointcloud octo_pointcloud; 00482 octomap::pointcloudPCLToOctomap(pcl_cloud, octo_pointcloud); 00483 double elapsed_transform = (ros::WallTime::now() - begin_transform).toSec(); 00484 00485 00486 octomap::point3d sensor_origin = getSensorOrigin(cloud->header); 00487 tf::Point sensor_origin_tf = octomap::pointOctomapToTf(sensor_origin); 00488 00489 // integrate pointcloud into map 00490 ros::WallTime begin_insert = ros::WallTime::now(); 00491 00492 // make use of lazy update 00493 collision_octree_->insertScan(octo_pointcloud, sensor_origin, max_range_, false, true); 00494 collision_octree_->updateInnerOccupancy(); 00495 00496 double elapsed_insert = (ros::WallTime::now() - begin_insert).toSec(); 00497 00498 00499 // remove outdated occupied nodes ----- 00500 ros::WallTime begin_degrade = ros::WallTime::now(); 00501 degradeOutdatedRaw(cloud->header, sensor_origin_tf, settings.sensor_stereo_other_frame_, pcl_cloud_raw); 00502 double elapsed_degrade = (ros::WallTime::now() - begin_degrade).toSec(); 00503 00504 00505 // get all occupied nodes from map 00506 octomap::point3d_list node_centers; 00507 ros::WallTime begin_get_occupied = ros::WallTime::now(); 00508 00509 std::vector<geometry_msgs::Point> pointlist; 00510 getOccupiedPoints(pointlist); 00511 00512 //collision_octree_->getOccupied(node_centers); 00513 double elapsed_get_occupied = (ros::WallTime::now() - begin_get_occupied).toSec(); 00514 00515 00516 // publish occupied cells 00517 ros::WallTime begin_send = ros::WallTime::now(); 00518 if (settings.dynamic_publish_ && cmap_publisher_.getNumSubscribers() > 0) { 00519 publishCollisionMap(pointlist, global_header, cmap_publisher_); 00520 } 00521 if (pointcloud_publisher_.getNumSubscribers() > 0) { 00522 publishPointCloud(pointlist, global_header, pointcloud_publisher_); 00523 } 00524 if (octomap_visualization_pub_.getNumSubscribers() > 0){ 00525 publishMarkerArray(pointlist, global_header, color_occupied_, octomap_visualization_pub_); 00526 } 00527 if (octomap_visualization_free_pub_.getNumSubscribers() > 0){ 00528 std::vector<geometry_msgs::Point> freelist; 00529 getFreePoints(freelist); 00530 publishMarkerArray(freelist, global_header, color_free_, octomap_visualization_free_pub_); 00531 } 00532 double elapsed_send = (ros::WallTime::now() - begin_send).toSec(); 00533 00534 double total_elapsed = (ros::WallTime::now() - begin_cb).toSec(); 00535 ROS_DEBUG("Total cloudCombinedCB %d pts: %f (transf: %f, update: %f, clear: %f, get occ: %f, send map: %f)", 00536 int(pcl_cloud.size()), total_elapsed, elapsed_transform, elapsed_insert, elapsed_degrade, elapsed_get_occupied, elapsed_send); 00537 00538 } 00539 00540 00541 void Collider::cloudCallback(const sensor_msgs::PointCloud2::ConstPtr &cloud, const std::string topic_name) { 00542 00543 ros::WallTime begin_cb = ros::WallTime::now(); 00544 CloudInfo settings = cloud_sources_[topic_name]; 00545 00546 // transform pointcloud from sensor frame to fixed_frame_ 00547 if (!tf_.waitForTransform(fixed_frame_, cloud->header.frame_id, cloud->header.stamp, ros::Duration(1.0))) { 00548 ROS_WARN_STREAM( "Timed out waiting for transform from " << cloud->header.frame_id 00549 << " to " << fixed_frame_ << ", quitting callback"); 00550 return; 00551 } 00552 00553 // sensor_msgs::PointCloud2 transformed_cloud; 00554 // pcl::transformPointCloud (fixed_frame_, *cloud, transformed_cloud, tf_); 00555 // // tf::StampedTransform transform; 00556 // // tf_.lookupTransform (fixed_frame_, cloud->header.frame_id, cloud->header.stamp, transform); 00557 // // pcl::transformPointCloud (*cloud, transformed_cloud, transform); 00558 // pcl::PointCloud<pcl::PointXYZ> pcl_cloud; 00559 // pcl::fromROSMsg (transformed_cloud, pcl_cloud); 00560 00561 00562 00563 tf::StampedTransform trans; 00564 tf_.lookupTransform (fixed_frame_, cloud->header.frame_id, cloud->header.stamp, trans); 00565 tf::Transform to_world = trans; 00566 Eigen::Matrix4f eigen_transform; 00567 sensor_msgs::PointCloud2 transformed_cloud; 00568 // // cturtle: 00569 // pcl::transformAsMatrix (to_world, eigen_transform); 00570 // pcl::transformPointCloud (eigen_transform, *cloud, transformed_cloud); 00571 // // dturtle: 00572 pcl_ros::transformAsMatrix (to_world, eigen_transform); 00573 pcl_ros::transformPointCloud (eigen_transform, *cloud, transformed_cloud); 00574 00575 pcl::PointCloud<pcl::PointXYZ> pcl_cloud; 00576 pcl::fromROSMsg (transformed_cloud, pcl_cloud); 00577 transformed_cloud.header.frame_id = fixed_frame_; 00578 00579 std::vector<int> inside_mask; 00580 //filtering out attached object inside points 00581 if(planning_environment::computeAttachedObjectPointCloudMask(pcl_cloud, 00582 cm_->getWorldFrameId(), 00583 cm_, 00584 tf_, 00585 inside_mask)) { 00586 pcl::PointCloud<pcl::PointXYZ>::iterator it = pcl_cloud.points.begin(); 00587 unsigned int i = 0; 00588 while(it != pcl_cloud.end()) { 00589 if(inside_mask[i++] == robot_self_filter::INSIDE) { 00590 it = pcl_cloud.points.erase(it); 00591 } else { 00592 it++; 00593 } 00594 } 00595 } 00596 00597 { 00598 planning_models::KinematicState state(cm_->getKinematicModel()); 00599 state.setKinematicStateToDefault(); 00600 00601 planning_environment::updateAttachedObjectBodyPoses(cm_, 00602 state, 00603 tf_); 00604 00605 visualization_msgs::MarkerArray arr; 00606 cm_->getAttachedCollisionObjectMarkers(state, 00607 arr, 00608 "filter_attached", 00609 attached_color_, 00610 ros::Duration(.2)); 00611 00612 octomap_visualization_attached_array_pub_.publish(arr); 00613 } 00614 00615 // copy data to octomap pointcloud 00616 octomap::Pointcloud octo_pointcloud; 00617 octomap::pointcloudPCLToOctomap(pcl_cloud, octo_pointcloud); 00618 00619 00620 // // retrieve sensor pose from tf 00621 // tf::StampedTransform trans; 00622 // tf_.lookupTransform (fixed_frame_, settings.sensor_frame_, cloud->header.stamp, trans); 00623 // tf::Vector3 orig = trans.getOrigin(); 00624 // octomap::pose6d sensor_pose(orig.x(), orig.y(), orig.z(), 0, 0, 0); 00625 // octomap::point3d sensor_origin(orig.x(), orig.y(), orig.z()); 00626 00627 octomap::point3d sensor_origin = getSensorOrigin(cloud->header); 00628 octomap::pose6d sensor_pose(sensor_origin.x(), sensor_origin.y(), sensor_origin.z(), 0, 0, 0); 00629 /* 00630 fprintf(stderr, "sensor origin: %.2f , %.2f , %.2f\n (frame: %s)", 00631 sensor_origin.x(), sensor_origin.y(), sensor_origin.z(), 00632 cloud->header.frame_id.c_str()); 00633 */ 00634 // integrate pointcloud into map 00635 ros::WallTime begin_insert = ros::WallTime::now(); 00636 collision_octree_->insertScan(octo_pointcloud, sensor_origin, max_range_, false); 00637 00638 double elapsed_insert = (ros::WallTime::now() - begin_insert).toSec(); 00639 00640 00641 ros::WallTime begin_degrade = ros::WallTime::now(); 00642 // remove outdated occupied nodes 00643 //degradeOutdatedRaycasting(cloud->header, sensor_origin, *collision_octree_); 00644 00645 // remove single voxels with no neighbors (probably noise) 00646 degradeSingleSpeckles(); 00647 double elapsed_degrade = (ros::WallTime::now() - begin_degrade).toSec(); 00648 00649 00650 // get all occupied nodes from map 00651 octomap::point3d_list node_centers; 00652 ros::WallTime begin_get_occupied = ros::WallTime::now(); 00653 std::vector<geometry_msgs::Point> pointlist; 00654 getOccupiedPoints(pointlist); 00655 double elapsed_get_occupied = (ros::WallTime::now() - begin_get_occupied).toSec(); 00656 00657 00658 // publish occupied cells 00659 ros::WallTime begin_send = ros::WallTime::now(); 00660 if (settings.dynamic_publish_ && cmap_publisher_.getNumSubscribers() > 0) { 00661 publishCollisionMap(pointlist, transformed_cloud.header, cmap_publisher_); 00662 } 00663 if (pointcloud_publisher_.getNumSubscribers() > 0) { 00664 publishPointCloud(pointlist, transformed_cloud.header, pointcloud_publisher_); 00665 } 00666 if (octomap_visualization_pub_.getNumSubscribers() > 0){ 00667 publishMarkerArray(pointlist, transformed_cloud.header, color_occupied_, octomap_visualization_pub_); 00668 } 00669 00670 double elapsed_send = (ros::WallTime::now() - begin_send).toSec(); 00671 00672 double total_elapsed = (ros::WallTime::now() - begin_cb).toSec(); 00673 ROS_DEBUG("Total cloudCB: %f (Update %d pnts: %f, clearing: %f, get occupied: %f, send map: %f)", 00674 total_elapsed, int(pcl_cloud.size()), elapsed_insert, elapsed_degrade, elapsed_get_occupied, elapsed_send); 00675 00676 } 00677 00678 00679 void Collider::degradeOutdatedRaw(const std_msgs::Header& sensor_header, const tf::Point& sensor_origin, 00680 const std::string& other_stereo_frame, const pcl::PointCloud<pcl::PointXYZ>& pcl_cloud_raw) { 00681 00682 tf::StampedTransform trans; 00683 // tf_.lookupTransform (fixed_frame_, sensor_header.frame_id, sensor_header.stamp, trans); 00684 // tf::Transform to_world = trans; 00685 tf_.lookupTransform (sensor_header.frame_id, fixed_frame_, sensor_header.stamp, trans); 00686 tf::Transform to_sensor = trans; 00687 00688 robot_mask_right_->assumeFrame(fixed_frame_, sensor_header.stamp, sensor_header.frame_id, self_filter_min_dist_); 00689 robot_mask_left_->assumeFrame(fixed_frame_, sensor_header.stamp, other_stereo_frame, self_filter_min_dist_); 00690 00691 btVector3 sensor_pos_right, sensor_pos_left; 00692 planning_models::KinematicState state(cm_->getKinematicModel()); 00693 planning_environment::configureForAttachedBodyMask(state, 00694 cm_, 00695 tf_, 00696 sensor_header.frame_id, 00697 sensor_header.stamp, 00698 sensor_pos_right); 00699 00700 planning_environment::configureForAttachedBodyMask(state, 00701 cm_, 00702 tf_, 00703 other_stereo_frame, 00704 sensor_header.stamp, 00705 sensor_pos_left); 00706 00707 octomap::point3d min; 00708 octomap::point3d max; 00709 computeBBX(sensor_header, min, max); 00710 unsigned query_time = time(NULL); 00711 unsigned max_update_time = 3; 00712 for(OcTreeType::leaf_bbx_iterator it = collision_octree_->begin_leafs_bbx(min,max), 00713 end=collision_octree_->end_leafs_bbx(); it!= end; ++it) 00714 { 00715 if (collision_octree_->isNodeOccupied(*it) && 00716 ((query_time - it->getTimestamp()) > max_update_time)) 00717 { 00718 tf::Point pos(it.getX(), it.getY(), it.getZ()); 00719 tf::Point posRel = to_sensor(pos); 00720 cv::Point2d uv = cam_model_.project3dToPixel(cv::Point3d(posRel.x(), posRel.y(), posRel.z())); 00721 00722 // ignore point if not in sensor cone 00723 if (!inSensorCone(uv)) 00724 continue; 00725 00726 // ignore point if it is occluded in the map 00727 if (isOccludedRaw(uv, pos.distance(sensor_origin), pcl_cloud_raw)) 00728 continue; 00729 00730 // ignore point if it is in the shadow of the robot or attached object 00731 if (robot_mask_right_->getMaskIntersection(pos) == robot_self_filter::SHADOW 00732 || robot_mask_left_->getMaskIntersection(pos) == robot_self_filter::SHADOW 00733 || planning_environment::computeAttachedObjectPointMask(cm_, pos, sensor_pos_right) == robot_self_filter::SHADOW 00734 || planning_environment::computeAttachedObjectPointMask(cm_, pos, sensor_pos_left) == robot_self_filter::SHADOW) 00735 { 00736 continue; 00737 } 00738 00739 // otherwise: degrade node 00740 collision_octree_->integrateMissNoTime(&*it); 00741 00742 } 00743 } 00744 00745 // // query map for occupied leafs within a given BBX and time frame 00746 // std::list<std::pair<octomap::point3d, octomap::OcTreeNodeStamped*> > nodes; 00747 // tree.getOccupiedNodesUpdateTimeBBX(nodes, 3, time(NULL), min, max); 00748 // 00749 // 00750 // 00751 // std::list<std::pair<octomap::point3d, octomap::OcTreeNodeStamped*> >::iterator it = nodes.begin(); 00752 // for ( ; it != nodes.end(); ++it) { 00753 // 00754 // 00755 // // ignore point if not in sensor cone 00756 // if (!inSensorCone(to_sensor, it->first)) 00757 // continue; 00758 // 00759 // // ignore point if it is occluded in the map 00760 // if (isOccludedRaw(to_sensor, sensor_origin, it->first, pcl_cloud_raw)) 00761 // continue; 00762 // 00763 // // ignore point if it is in the shadow of the robot: 00764 // if (robot_mask_right_->getMaskIntersection(octomap::pointOctomapToTf(it->first)) == robot_self_filter::SHADOW 00765 // || robot_mask_left_->getMaskIntersection(octomap::pointOctomapToTf(it->first)) == robot_self_filter::SHADOW 00766 // || planning_environment::computeAttachedObjectPointMask(cm_, octomap::pointOctomapToTf(it->first), sensor_pos_right) == robot_self_filter::SHADOW 00767 // || planning_environment::computeAttachedObjectPointMask(cm_, octomap::pointOctomapToTf(it->first), sensor_pos_left) == robot_self_filter::SHADOW) 00768 // { 00769 // continue; 00770 // } 00771 // 00772 // // degrade node 00773 // collision_octree_->integrateMissNoTime(it->second); 00774 // } 00775 /* std_msgs::Header marker_header = sensor_header; 00776 marker_header.frame_id = fixed_frame_; 00777 publishMarkerArray(shadow_voxels, marker_header, octomap_debug_pub_);*/ 00778 00779 } 00780 00781 00782 00783 void Collider::degradeOutdatedRaycasting(const std_msgs::Header& sensor_header, const octomap::point3d& sensor_origin, 00784 octomap::OcTreeStamped& tree) { 00785 tf::StampedTransform trans; 00786 tf_.lookupTransform (sensor_header.frame_id, fixed_frame_, sensor_header.stamp, trans); 00787 tf::Transform to_sensor = trans; 00788 00789 // compute bbx from sensor cone 00790 octomap::point3d min; 00791 octomap::point3d max; 00792 computeBBX(sensor_header, min, max); 00793 00794 unsigned query_time = time(NULL); 00795 unsigned max_update_time = 1; 00796 for(OcTreeType::leaf_bbx_iterator it = collision_octree_->begin_leafs_bbx(min,max), 00797 end=collision_octree_->end_leafs_bbx(); it!= end; ++it) 00798 { 00799 if (collision_octree_->isNodeOccupied(*it) && 00800 ((query_time - it->getTimestamp()) > max_update_time)) 00801 { 00802 tf::Point pos(it.getX(), it.getY(), it.getZ()); 00803 tf::Point posRel = to_sensor(pos); 00804 cv::Point2d uv = cam_model_.project3dToPixel(cv::Point3d(posRel.x(), posRel.y(), posRel.z())); 00805 00806 // ignore point if not in sensor cone 00807 if (!inSensorCone(uv)) 00808 continue; 00809 00810 // ignore point if it is occluded in the map 00811 if (isOccludedMap(sensor_origin, it.getCoordinate())) 00812 continue; 00813 00814 // otherwise: degrade node 00815 collision_octree_->integrateMissNoTime(&*it); 00816 00817 } 00818 } 00819 } 00820 00821 void Collider::degradeSingleSpeckles(){ 00822 for(OcTreeType::leaf_iterator it = collision_octree_->begin_leafs(), 00823 end=collision_octree_->end_leafs(); it!= end; ++it) 00824 { 00825 if (collision_octree_->isNodeOccupied(*it)){ 00826 octomap::OcTreeKey nKey = it.getKey(); 00827 octomap::OcTreeKey key; 00828 bool neighborFound = false; 00829 for (key[2] = nKey[2] - 1; !neighborFound && key[2] <= nKey[2] + 1; ++key[2]){ 00830 for (key[1] = nKey[1] - 1; !neighborFound && key[1] <= nKey[1] + 1; ++key[1]){ 00831 for (key[0] = nKey[0] - 1; !neighborFound && key[0] <= nKey[0] + 1; ++key[0]){ 00832 if (key != nKey){ 00833 OcTreeType::NodeType* node = collision_octree_->search(key); 00834 if (node && collision_octree_->isNodeOccupied(node)){ 00835 // we have a neighbor => break! 00836 neighborFound = true; 00837 } 00838 } 00839 } 00840 } 00841 } 00842 // done with search, see if found and degrade otherwise: 00843 if (!neighborFound){ 00844 ROS_DEBUG("Degrading single speckle at (%f,%f,%f)", it.getX(), it.getY(), it.getZ()); 00845 collision_octree_->integrateMissNoTime(&*it); 00846 } 00847 00848 } 00849 } 00850 } 00851 00852 00853 octomap::point3d Collider::getSensorOrigin(const std_msgs::Header& sensor_header) { 00854 00855 geometry_msgs::PointStamped stamped_in; 00856 geometry_msgs::PointStamped stamped_out; 00857 stamped_in.header = sensor_header; 00858 00859 // HACK: laser origin 00860 if (sensor_header.frame_id == "base_footprint") { 00861 stamped_in.header.frame_id = "laser_tilt_link"; 00862 } 00863 00864 geometry_msgs::Point p; 00865 p.x=p.y=p.z=0; 00866 try { 00867 tf_.transformPoint(fixed_frame_, stamped_in, stamped_out); 00868 } catch(tf::TransformException& ex) { 00869 ros::Time t; 00870 std::string err_string; 00871 ROS_INFO_STREAM("Transforming sensor origin using latest common time because there's a tf problem"); 00872 if (tf_.getLatestCommonTime(fixed_frame_, stamped_in.header.frame_id, stamped_in.header.stamp, &err_string) == tf::NO_ERROR) { 00873 try { 00874 tf_.transformPoint(fixed_frame_, stamped_in, stamped_out); 00875 } catch(...) { 00876 ROS_WARN_STREAM("Still can't transform sensor origin between " << fixed_frame_ << " and " << stamped_in.header.frame_id); 00877 } 00878 } else { 00879 ROS_WARN_STREAM("No common time between " << fixed_frame_ << " and " << stamped_in.header.frame_id); 00880 } 00881 } 00882 octomap::point3d retval (stamped_out.point.x, stamped_out.point.y, stamped_out.point.z); 00883 00884 return retval; 00885 } 00886 00887 00888 void Collider::computeBBX(const std_msgs::Header& sensor_header, octomap::point3d& bbx_min, octomap::point3d& bbx_max) { 00889 00890 std::string sensor_frame = sensor_header.frame_id; 00891 00892 // transform sensor FOV 00893 geometry_msgs::PointStamped stamped_in; 00894 geometry_msgs::PointStamped stamped_out; 00895 stamped_in.header = sensor_header; 00896 stamped_in.header.frame_id = sensor_frame; 00897 00898 // get max 3d points from camera at 0.5m and 5m. 00899 geometry_msgs::Point p[8]; 00900 00901 // define min/max 2d points 00902 cv::Point2d uv [4]; 00903 uv[0].x = camera_stereo_offset_left_; 00904 uv[0].y = 0; 00905 uv[1].x = cam_size_.width + camera_stereo_offset_right_; 00906 uv[1].y = 0; 00907 uv[2].x = cam_size_.width + camera_stereo_offset_right_; 00908 uv[2].y = cam_size_.height; 00909 uv[3].x = camera_stereo_offset_left_; 00910 uv[3].y = cam_size_.height; 00911 00912 // transform to 3d space 00913 cv::Point3d xyz [4]; 00914 for (int i=0;i<4;i++) { 00915 xyz[i] = cam_model_.projectPixelTo3dRay(uv[i]); 00916 cv::Point3d xyz_05 = xyz[i] * 0.5; 00917 xyz[i] *= 5.; // 5meters 00918 p[i].x = xyz[i].x; 00919 p[i].y = xyz[i].y; 00920 p[i].z = xyz[i].z; 00921 p[i+4].x = xyz_05.x; 00922 p[i+4].y = xyz_05.y; 00923 p[i+4].z = xyz_05.z; 00924 } 00925 00926 // transform to world coodinates and find axis-aligned bbx 00927 bbx_min.x() = bbx_min.y() = bbx_min.z() = 1e6; 00928 bbx_max.x() = bbx_max.y() = bbx_max.z() = -1e6; 00929 for (int i=0; i<8; i++) { 00930 stamped_in.point = p[i]; 00931 tf_.transformPoint(fixed_frame_, stamped_in, stamped_out); 00932 p[i].x = stamped_out.point.x; 00933 p[i].y = stamped_out.point.y; 00934 p[i].z = stamped_out.point.z; 00935 if (p[i].x < bbx_min.x()) bbx_min.x() = p[i].x; 00936 if (p[i].y < bbx_min.y()) bbx_min.y() = p[i].y; 00937 if (p[i].z < bbx_min.z()) bbx_min.z() = p[i].z; 00938 if (p[i].x > bbx_max.x()) bbx_max.x() = p[i].x; 00939 if (p[i].y > bbx_max.y()) bbx_max.y() = p[i].y; 00940 if (p[i].z > bbx_max.z()) bbx_max.z() = p[i].z; 00941 } 00942 00943 00944 // // visualize axis-aligned querying bbx 00945 visualization_msgs::Marker bbx; 00946 bbx.header.frame_id = fixed_frame_; 00947 bbx.header.stamp = ros::Time::now(); 00948 bbx.ns = "collider"; 00949 bbx.id = 1; 00950 bbx.action = visualization_msgs::Marker::ADD; 00951 bbx.type = visualization_msgs::Marker::CUBE; 00952 bbx.pose.orientation.w = 1.0; 00953 bbx.pose.position.x = (bbx_min.x() + bbx_max.x()) / 2.; 00954 bbx.pose.position.y = (bbx_min.y() + bbx_max.y()) / 2.; 00955 bbx.pose.position.z = (bbx_min.z() + bbx_max.z()) / 2.; 00956 bbx.scale.x = bbx_max.x()-bbx_min.x(); 00957 bbx.scale.y = bbx_max.y()-bbx_min.y(); 00958 bbx.scale.z = bbx_max.z()-bbx_min.z(); 00959 bbx.color.g = 1; 00960 bbx.color.a = 0.3; 00961 marker_pub_.publish(bbx); 00962 00963 00964 // visualize sensor cone 00965 visualization_msgs::Marker bbx_points; 00966 bbx_points.header.frame_id = fixed_frame_; 00967 bbx_points.header.stamp = ros::Time::now(); 00968 bbx_points.ns = "collider"; 00969 bbx_points.id = 2; 00970 bbx_points.action = visualization_msgs::Marker::ADD; 00971 bbx_points.type = visualization_msgs::Marker::LINE_STRIP; 00972 bbx_points.pose.orientation.w = 1.0; 00973 bbx_points.scale.x = 0.02; 00974 bbx_points.scale.y = 0.02; 00975 bbx_points.color.g = 1; 00976 bbx_points.color.a = 0.3; 00977 bbx_points.points.push_back(p[0]); 00978 bbx_points.points.push_back(p[1]); 00979 bbx_points.points.push_back(p[2]); 00980 bbx_points.points.push_back(p[3]); 00981 bbx_points.points.push_back(p[0]); 00982 bbx_points.points.push_back(p[4]); 00983 bbx_points.points.push_back(p[5]); 00984 bbx_points.points.push_back(p[6]); 00985 bbx_points.points.push_back(p[7]); 00986 bbx_points.points.push_back(p[4]); 00987 bbx_points.points.push_back(p[7]); 00988 bbx_points.points.push_back(p[3]); 00989 bbx_points.points.push_back(p[2]); 00990 bbx_points.points.push_back(p[6]); 00991 bbx_points.points.push_back(p[5]); 00992 bbx_points.points.push_back(p[1]); 00993 marker_pub_.publish(bbx_points); 00994 } 00995 00996 00997 bool Collider::inSensorCone(const cv::Point2d& uv) const { 00998 // Check if projected 2D coordinate in pixel range. 00999 // This check is a little more restrictive than it should be by using 01000 // 1 pixel less to account for rounding / discretization errors. 01001 // Otherwise points on the corner are accounted to be in the sensor cone. 01002 return ( (uv.x > camera_stereo_offset_left_+1) 01003 && (uv.x < cam_size_.width + camera_stereo_offset_right_ - 2) 01004 && (uv.y > 1) 01005 && (uv.y < cam_size_.height-2) ); 01006 } 01007 01008 01009 01010 bool Collider::isOccludedRaw(const cv::Point2d& uv, double range, const pcl::PointCloud<pcl::PointXYZ>& pcl_cloud_raw) { 01011 01012 // out of image range? 01013 if ((uv.x < 0) || (uv.y < 0) || (uv.x > cam_size_.width) || (uv.y > cam_size_.height)) 01014 return false; 01015 01016 double sensor_range = pcl_cloud_raw(uv.x, uv.y).z; 01017 01018 return (sensor_range < range); 01019 } 01020 01021 01022 bool Collider::isOccludedMap(const octomap::point3d& sensor_origin, const octomap::point3d& p) const { 01023 01024 octomap::point3d direction (p-sensor_origin); 01025 octomap::point3d obstacle; 01026 double range = direction.norm() - resolution_; 01027 01028 if (collision_octree_->castRay(sensor_origin, direction, obstacle, true, range)) { 01029 // fprintf(stderr, "<%.2f , %.2f , %.2f> -> <%.2f , %.2f , %.2f> // obs at: <%.2f , %.2f , %.2f>, range: %.2f\n", 01030 // sensor_origin.x(), sensor_origin.y(), sensor_origin.z(), 01031 // p.x(), p.y(), p.z(), 01032 // obstacle.x(), obstacle.y(), obstacle.z(), (obstacle-p).norm()); 01033 return true; 01034 } 01035 return false; 01036 } 01037 01038 01039 01040 // publish map ---------------------------------------------------------------------- 01041 01042 void Collider::publishCollisionMap(const std::vector<geometry_msgs::Point>& pointlist, 01043 const std_msgs::Header &header, ros::Publisher &pub) { 01044 if(pointlist.size() <= 1) 01045 return; 01046 01047 arm_navigation_msgs::CollisionMap cmap; 01048 cmap.header = header; 01049 01050 arm_navigation_msgs::OrientedBoundingBox box; 01051 box.extents.x = box.extents.y = box.extents.z = collision_octree_->getResolution(); 01052 box.axis.x = box.axis.y = 0.0; box.axis.z = 1.0; 01053 box.angle = 0.0; 01054 cmap.boxes.reserve(pointlist.size()); 01055 01056 for (std::vector<geometry_msgs::Point>::const_iterator it = pointlist.begin(); it != pointlist.end(); ++it) { 01057 box.center.x = it->x; 01058 box.center.y = it->y; 01059 box.center.z = it->z; 01060 cmap.boxes.push_back(box); 01061 } 01062 pub.publish(cmap); 01063 } 01064 01065 01066 void Collider::publishPointCloud(const std::vector<geometry_msgs::Point>& pointlist, 01067 const std_msgs::Header &header, ros::Publisher &pub) { 01068 01069 if(pointlist.size() <= 1) 01070 return; 01071 01072 pcl::PointCloud<pcl::PointXYZ> pcl_cloud; 01073 pcl_cloud.points.reserve (pointlist.size ()); 01074 01075 for (std::vector<geometry_msgs::Point>::const_iterator it = pointlist.begin(); it != pointlist.end(); ++it) { 01076 pcl::PointXYZ point; 01077 point.x = it->x; 01078 point.y = it->y; 01079 point.z = it->z; 01080 pcl_cloud.points.push_back (point); 01081 } 01082 01083 sensor_msgs::PointCloud2 cloud; 01084 pcl::toROSMsg (pcl_cloud, cloud); 01085 cloud.header = header; 01086 pub.publish (cloud); 01087 01088 } 01089 01090 void Collider::publishMarkerArray(const std::vector<geometry_msgs::Point>& pointlist, 01091 const std_msgs::Header &header, const std_msgs::ColorRGBA& color, 01092 ros::Publisher &pub) { 01093 01094 if(pointlist.size() <= 1) 01095 return; 01096 01097 visualization_msgs::Marker occupiedCellsVis; 01098 occupiedCellsVis.header = header; 01099 occupiedCellsVis.ns = "map"; 01100 occupiedCellsVis.id = 0; 01101 occupiedCellsVis.type = visualization_msgs::Marker::CUBE_LIST; 01102 occupiedCellsVis.scale.x = collision_octree_->getResolution(); 01103 occupiedCellsVis.scale.y = collision_octree_->getResolution(); 01104 occupiedCellsVis.scale.z = collision_octree_->getResolution(); 01105 occupiedCellsVis.color = color; 01106 01107 01108 occupiedCellsVis.points = pointlist; 01109 01110 pub.publish (occupiedCellsVis); 01111 01112 } 01113 01114 01115 01116 // action server -------------------------------------------------------------------- 01117 01118 bool Collider::reset(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) { 01119 delete collision_octree_; 01120 collision_octree_ = new OcTreeType(resolution_); 01121 return true; 01122 } 01123 01124 01125 bool Collider::dummyReset(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) { 01126 // Dummy function that doesn't actually reset anything. Needed because some 01127 // parts of the grasping pipeline want to reset the collision map 01128 // on occasion, but with the octomap we don't want other nodes resetting the map sporadically. 01129 return true; 01130 } 01131 01132 01133 void Collider::makeStaticCollisionMap(const arm_navigation_msgs::MakeStaticCollisionMapGoalConstPtr& goal) { 01134 01135 std_msgs::Header head; 01136 head.stamp = ros::Time::now(); 01137 head.frame_id = fixed_frame_; 01138 01139 ROS_INFO("Making static collision map"); 01140 01141 //we first publish whatever we got 01142 std::vector<geometry_msgs::Point> pointlist; 01143 getOccupiedPoints(pointlist); 01144 if (pointcloud_publisher_.getNumSubscribers() > 0) { 01145 publishPointCloud(pointlist, head, pointcloud_publisher_); 01146 } 01147 01148 if(publish_over_dynamic_map_) 01149 { 01150 if (cmap_publisher_.getNumSubscribers() > 0) { 01151 publishCollisionMap(pointlist, head, cmap_publisher_); 01152 } 01153 // loop through cloud_source_map, set dynamic publish to false on all sources to preserve behavior of older collision_map_self_occ 01154 for(std::map<std::string,CloudInfo>::iterator it = cloud_sources_.begin(); 01155 it != cloud_sources_.end(); 01156 it++) 01157 { 01158 it->second.dynamic_publish_ = false; 01159 } 01160 ROS_INFO("Should stop publishing dynamic map"); 01161 } else { 01162 publishCollisionMap(pointlist, head, static_map_publisher_); 01163 } 01164 action_server_->setSucceeded(); 01165 } 01166 01167 bool Collider::octomapSrv(octomap_ros::GetOctomap::Request &req, octomap_ros::GetOctomap::Response &res){ 01168 ROS_DEBUG("Sending map data on service request"); 01169 01170 res.map.header.frame_id = fixed_frame_; 01171 res.map.header.stamp = ros::Time::now(); 01172 octomap::octomapMapToMsg(*collision_octree_, res.map); 01173 01174 return true; 01175 } 01176 01177 bool Collider::occupancyPointSrv(collider::OccupancyPointQuery::Request &req, 01178 collider::OccupancyPointQuery::Response &res){ 01179 01180 octomap::OcTreeNodeStamped* node = collision_octree_->search(req.point.x, req.point.y, req.point.z); 01181 if (node){ 01182 if (collision_octree_->isNodeOccupied(node)) 01183 res.occupancy=collider::OccupancyPointQueryResponse::OCCUPIED; 01184 else 01185 res.occupancy=collider::OccupancyPointQueryResponse::FREE; 01186 } else{ 01187 res.occupancy = collider::OccupancyPointQueryResponse::UNKNOWN; 01188 } 01189 01190 return true; 01191 } 01192 01193 bool Collider::occupancyBBXSrv(collider::OccupancyBBXQuery::Request &req, 01194 collider::OccupancyBBXQuery::Response &res){ 01195 01196 OcTreeType::leaf_bbx_iterator it = collision_octree_->begin_leafs_bbx(octomap::pointMsgToOctomap(req.min), 01197 octomap::pointMsgToOctomap(req.max)); 01198 OcTreeType::leaf_bbx_iterator end = collision_octree_->end_leafs_bbx(); 01199 01200 geometry_msgs::Point pt; 01201 for(; it!= end; ++it){ 01202 pt.x = it.getX(); 01203 pt.y = it.getY(); 01204 pt.z = it.getZ(); 01205 01206 if (collision_octree_->isNodeOccupied(*it)){ 01207 res.occupied.push_back(pt); 01208 } else { 01209 res.free.push_back(pt); 01210 } 01211 } 01212 res.resolution = collision_octree_->getResolution(); 01213 01214 return true; 01215 } 01216 01217 bool Collider::occupancyBBXSizeSrv(collider::OccupancyBBXSizeQuery::Request &req, 01218 collider::OccupancyBBXSizeQuery::Response &res){ 01219 01220 octomap::point3d center = octomap::pointMsgToOctomap(req.center); 01221 octomap::point3d size = octomap::pointMsgToOctomap(req.size); 01222 OcTreeType::leaf_bbx_iterator it = collision_octree_->begin_leafs_bbx(center - (size*0.5), center + (size*0.5)); 01223 OcTreeType::leaf_bbx_iterator end = collision_octree_->end_leafs_bbx(); 01224 01225 geometry_msgs::Point pt; 01226 for(; it!= end; ++it){ 01227 pt.x = it.getX(); 01228 pt.y = it.getY(); 01229 pt.z = it.getZ(); 01230 01231 if (collision_octree_->isNodeOccupied(*it)){ 01232 res.occupied.push_back(pt); 01233 } else { 01234 res.free.push_back(pt); 01235 } 01236 } 01237 res.resolution = collision_octree_->getResolution(); 01238 01239 return true; 01240 } 01241