OctomapServer.cpp
Go to the documentation of this file.
00001 
00009 /*
00010  * Copyright (c) 2010-2012, A. Hornung, University of Freiburg
00011  * All rights reserved.
00012  *
00013  * Redistribution and use in source and binary forms, with or without
00014  * modification, are permitted provided that the following conditions are met:
00015  *
00016  *     * Redistributions of source code must retain the above copyright
00017  *       notice, this list of conditions and the following disclaimer.
00018  *     * Redistributions in binary form must reproduce the above copyright
00019  *       notice, this list of conditions and the following disclaimer in the
00020  *       documentation and/or other materials provided with the distribution.
00021  *     * Neither the name of the University of Freiburg nor the names of its
00022  *       contributors may be used to endorse or promote products derived from
00023  *       this software without specific prior written permission.
00024  *
00025  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00026  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00027  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00028  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00029  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00030  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00031  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00032  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00033  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00034  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00035  * POSSIBILITY OF SUCH DAMAGE.
00036  */
00037 
00038 #include <octomap_server/OctomapServer.h>
00039 
00040 using namespace octomap;
00041 using octomap_msgs::Octomap;
00042 
00043 namespace octomap_server{
00044 
00045 OctomapServer::OctomapServer(ros::NodeHandle private_nh_)
00046 : m_nh(),
00047   m_pointCloudSub(NULL),
00048   m_tfPointCloudSub(NULL),
00049   m_octree(NULL),
00050   m_maxRange(-1.0),
00051   m_worldFrameId("/map"), m_baseFrameId("base_footprint"),
00052   m_useHeightMap(true),
00053   m_colorFactor(0.8),
00054   m_latchedTopics(true),
00055   m_publishFreeSpace(false),
00056   m_res(0.05),
00057   m_treeDepth(0),
00058   m_maxTreeDepth(0),
00059   m_probHit(0.7), m_probMiss(0.4),
00060   m_thresMin(0.12), m_thresMax(0.97),
00061   m_pointcloudMinZ(-std::numeric_limits<double>::max()),
00062   m_pointcloudMaxZ(std::numeric_limits<double>::max()),
00063   m_occupancyMinZ(-std::numeric_limits<double>::max()),
00064   m_occupancyMaxZ(std::numeric_limits<double>::max()),
00065   m_minSizeX(0.0), m_minSizeY(0.0),
00066   m_filterSpeckles(false), m_filterGroundPlane(false),
00067   m_groundFilterDistance(0.04), m_groundFilterAngle(0.15), m_groundFilterPlaneDistance(0.07),
00068   m_compressMap(true),
00069   m_incrementalUpdate(false)
00070 {
00071   ros::NodeHandle private_nh(private_nh_);
00072   private_nh.param("frame_id", m_worldFrameId, m_worldFrameId);
00073   private_nh.param("base_frame_id", m_baseFrameId, m_baseFrameId);
00074   private_nh.param("height_map", m_useHeightMap, m_useHeightMap);
00075   private_nh.param("color_factor", m_colorFactor, m_colorFactor);
00076 
00077   private_nh.param("pointcloud_min_z", m_pointcloudMinZ,m_pointcloudMinZ);
00078   private_nh.param("pointcloud_max_z", m_pointcloudMaxZ,m_pointcloudMaxZ);
00079   private_nh.param("occupancy_min_z", m_occupancyMinZ,m_occupancyMinZ);
00080   private_nh.param("occupancy_max_z", m_occupancyMaxZ,m_occupancyMaxZ);
00081   private_nh.param("min_x_size", m_minSizeX,m_minSizeX);
00082   private_nh.param("min_y_size", m_minSizeY,m_minSizeY);
00083 
00084   private_nh.param("filter_speckles", m_filterSpeckles, m_filterSpeckles);
00085   private_nh.param("filter_ground", m_filterGroundPlane, m_filterGroundPlane);
00086   // distance of points from plane for RANSAC
00087   private_nh.param("ground_filter/distance", m_groundFilterDistance, m_groundFilterDistance);
00088   // angular derivation of found plane:
00089   private_nh.param("ground_filter/angle", m_groundFilterAngle, m_groundFilterAngle);
00090   // distance of found plane from z=0 to be detected as ground (e.g. to exclude tables)
00091   private_nh.param("ground_filter/plane_distance", m_groundFilterPlaneDistance, m_groundFilterPlaneDistance);
00092 
00093   private_nh.param("sensor_model/max_range", m_maxRange, m_maxRange);
00094 
00095   private_nh.param("resolution", m_res, m_res);
00096   private_nh.param("sensor_model/hit", m_probHit, m_probHit);
00097   private_nh.param("sensor_model/miss", m_probMiss, m_probMiss);
00098   private_nh.param("sensor_model/min", m_thresMin, m_thresMin);
00099   private_nh.param("sensor_model/max", m_thresMax, m_thresMax);
00100   private_nh.param("compress_map", m_compressMap, m_compressMap);
00101   private_nh.param("incremental_2D_projection", m_incrementalUpdate, m_incrementalUpdate);
00102 
00103   if (m_filterGroundPlane && (m_pointcloudMinZ > 0.0 || m_pointcloudMaxZ < 0.0)){
00104           ROS_WARN_STREAM("You enabled ground filtering but incoming pointclouds will be pre-filtered in ["
00105                           <<m_pointcloudMinZ <<", "<< m_pointcloudMaxZ << "], excluding the ground level z=0. "
00106                           << "This will not work.");
00107 
00108   }
00109 
00110 
00111 
00112   // initialize octomap object & params
00113   m_octree = new OcTree(m_res);
00114   m_octree->setProbHit(m_probHit);
00115   m_octree->setProbMiss(m_probMiss);
00116   m_octree->setClampingThresMin(m_thresMin);
00117   m_octree->setClampingThresMax(m_thresMax);
00118   m_treeDepth = m_octree->getTreeDepth();
00119   m_maxTreeDepth = m_treeDepth;
00120   m_gridmap.info.resolution = m_res;
00121 
00122   double r, g, b, a;
00123   private_nh.param("color/r", r, 0.0);
00124   private_nh.param("color/g", g, 0.0);
00125   private_nh.param("color/b", b, 1.0);
00126   private_nh.param("color/a", a, 1.0);
00127   m_color.r = r;
00128   m_color.g = g;
00129   m_color.b = b;
00130   m_color.a = a;
00131 
00132   private_nh.param("color_free/r", r, 0.0);
00133   private_nh.param("color_free/g", g, 1.0);
00134   private_nh.param("color_free/b", b, 0.0);
00135   private_nh.param("color_free/a", a, 1.0);
00136   m_colorFree.r = r;
00137   m_colorFree.g = g;
00138   m_colorFree.b = b;
00139   m_colorFree.a = a;
00140 
00141   private_nh.param("publish_free_space", m_publishFreeSpace, m_publishFreeSpace);
00142 
00143   private_nh.param("latch", m_latchedTopics, m_latchedTopics);
00144   if (m_latchedTopics){
00145     ROS_INFO("Publishing latched (single publish will take longer, all topics are prepared)");
00146   } else
00147     ROS_INFO("Publishing non-latched (topics are only prepared as needed, will only be re-published on map change");
00148 
00149   m_markerPub = m_nh.advertise<visualization_msgs::MarkerArray>("occupied_cells_vis_array", 1, m_latchedTopics);
00150   m_binaryMapPub = m_nh.advertise<Octomap>("octomap_binary", 1, m_latchedTopics);
00151   m_fullMapPub = m_nh.advertise<Octomap>("octomap_full", 1, m_latchedTopics);
00152   m_pointCloudPub = m_nh.advertise<sensor_msgs::PointCloud2>("octomap_point_cloud_centers", 1, m_latchedTopics);
00153   m_collisionObjectPub = m_nh.advertise<arm_navigation_msgs::CollisionObject>("octomap_collision_object", 1, m_latchedTopics);
00154   m_mapPub = m_nh.advertise<nav_msgs::OccupancyGrid>("projected_map", 5, m_latchedTopics);
00155   m_cmapPub = m_nh.advertise<arm_navigation_msgs::CollisionMap>("collision_map_out", 1, m_latchedTopics);
00156   m_fmapPub = m_nh.advertise<arm_navigation_msgs::CollisionMap>("free_map_out", 1, m_latchedTopics);    
00157   m_fmarkerPub = m_nh.advertise<visualization_msgs::MarkerArray>("free_cells_vis_array", 1, m_latchedTopics);
00158 
00159   m_pointCloudSub = new message_filters::Subscriber<sensor_msgs::PointCloud2> (m_nh, "cloud_in", 5);
00160   m_tfPointCloudSub = new tf::MessageFilter<sensor_msgs::PointCloud2> (*m_pointCloudSub, m_tfListener, m_worldFrameId, 5);
00161   m_tfPointCloudSub->registerCallback(boost::bind(&OctomapServer::insertCloudCallback, this, _1));
00162 
00163   m_octomapBinaryService = m_nh.advertiseService("octomap_binary", &OctomapServer::octomapBinarySrv, this);
00164   m_octomapFullService = m_nh.advertiseService("octomap_full", &OctomapServer::octomapFullSrv, this);
00165   m_clearBBXService = private_nh.advertiseService("clear_bbx", &OctomapServer::clearBBXSrv, this);
00166   m_resetService = private_nh.advertiseService("reset", &OctomapServer::resetSrv, this);
00167 
00168   dynamic_reconfigure::Server<OctomapServerConfig>::CallbackType f;
00169 
00170   f = boost::bind(&OctomapServer::reconfigureCallback, this, _1, _2);
00171   m_reconfigureServer.setCallback(f);
00172 }
00173 
00174 OctomapServer::~OctomapServer(){
00175   if (m_tfPointCloudSub){
00176     delete m_tfPointCloudSub;
00177     m_tfPointCloudSub = NULL;
00178   }
00179 
00180   if (m_pointCloudSub){
00181     delete m_pointCloudSub;
00182     m_pointCloudSub = NULL;
00183   }
00184 
00185 
00186   if (m_octree){
00187     delete m_octree;
00188     m_octree = NULL;
00189   }
00190 
00191 }
00192 
00193 bool OctomapServer::openFile(const std::string& filename){
00194   if (filename.length() <= 3)
00195     return false;
00196 
00197   std::string suffix = filename.substr(filename.length()-3, 3);
00198   if (suffix== ".bt"){
00199     if (!m_octree->readBinary(filename)){
00200       return false;
00201     }
00202   } else if (suffix == ".ot"){
00203     AbstractOcTree* tree = AbstractOcTree::read(filename);
00204     if (!tree){
00205       return false;
00206     }
00207     if (m_octree){
00208       delete m_octree;
00209       m_octree = NULL;
00210     }
00211     m_octree = dynamic_cast<OcTree*>(tree);
00212     if (!m_octree){
00213       ROS_ERROR("Could not read OcTree in file, currently there are no other types supported in .ot");
00214       return false;
00215     }
00216 
00217   } else{
00218     return false;
00219   }
00220 
00221   ROS_INFO("Octomap file %s loaded (%zu nodes).", filename.c_str(),m_octree->size());
00222 
00223   m_treeDepth = m_octree->getTreeDepth();
00224   m_maxTreeDepth = m_treeDepth;
00225   m_res = m_octree->getResolution();
00226   m_gridmap.info.resolution = m_res;
00227   double minX, minY, minZ;
00228   double maxX, maxY, maxZ;
00229   m_octree->getMetricMin(minX, minY, minZ);
00230   m_octree->getMetricMax(maxX, maxY, maxZ);
00231 
00232   m_updateBBXMin[0] = m_octree->coordToKey(minX);
00233   m_updateBBXMin[1] = m_octree->coordToKey(minY);
00234   m_updateBBXMin[2] = m_octree->coordToKey(minZ);
00235   
00236   m_updateBBXMax[0] = m_octree->coordToKey(maxX);
00237   m_updateBBXMax[1] = m_octree->coordToKey(maxY);
00238   m_updateBBXMax[2] = m_octree->coordToKey(maxZ);
00239   
00240   publishAll();
00241 
00242   return true;
00243 
00244 }
00245 
00246 void OctomapServer::insertCloudCallback(const sensor_msgs::PointCloud2::ConstPtr& cloud){
00247   ros::WallTime startTime = ros::WallTime::now();
00248 
00249 
00250   //
00251   // ground filtering in base frame
00252   //
00253   PCLPointCloud pc; // input cloud for filtering and ground-detection
00254   pcl::fromROSMsg(*cloud, pc);
00255 
00256   tf::StampedTransform sensorToWorldTf;
00257   try {
00258     m_tfListener.lookupTransform(m_worldFrameId, cloud->header.frame_id, cloud->header.stamp, sensorToWorldTf);
00259   } catch(tf::TransformException& ex){
00260     ROS_ERROR_STREAM( "Transform error of sensor data: " << ex.what() << ", quitting callback");
00261     return;
00262   }
00263 
00264   Eigen::Matrix4f sensorToWorld;
00265   pcl_ros::transformAsMatrix(sensorToWorldTf, sensorToWorld);
00266 
00267 
00268   // set up filter for height range, also removes NANs:
00269   pcl::PassThrough<pcl::PointXYZ> pass;
00270   pass.setFilterFieldName("z");
00271   pass.setFilterLimits(m_pointcloudMinZ, m_pointcloudMaxZ);
00272 
00273   PCLPointCloud pc_ground; // segmented ground plane
00274   PCLPointCloud pc_nonground; // everything else
00275 
00276   if (m_filterGroundPlane){
00277     tf::StampedTransform sensorToBaseTf, baseToWorldTf;
00278     try{
00279       m_tfListener.waitForTransform(m_baseFrameId, cloud->header.frame_id, cloud->header.stamp, ros::Duration(0.2));
00280       m_tfListener.lookupTransform(m_baseFrameId, cloud->header.frame_id, cloud->header.stamp, sensorToBaseTf);
00281       m_tfListener.lookupTransform(m_worldFrameId, m_baseFrameId, cloud->header.stamp, baseToWorldTf);
00282 
00283 
00284     }catch(tf::TransformException& ex){
00285       ROS_ERROR_STREAM( "Transform error for ground plane filter: " << ex.what() << ", quitting callback.\n"
00286                         "You need to set the base_frame_id or disable filter_ground.");
00287     }
00288 
00289 
00290     Eigen::Matrix4f sensorToBase, baseToWorld;
00291     pcl_ros::transformAsMatrix(sensorToBaseTf, sensorToBase);
00292     pcl_ros::transformAsMatrix(baseToWorldTf, baseToWorld);
00293 
00294     // transform pointcloud from sensor frame to fixed robot frame
00295     pcl::transformPointCloud(pc, pc, sensorToBase);
00296     pass.setInputCloud(pc.makeShared());
00297     pass.filter(pc);
00298     filterGroundPlane(pc, pc_ground, pc_nonground);
00299 
00300     // transform clouds to world frame for insertion
00301     pcl::transformPointCloud(pc_ground, pc_ground, baseToWorld);
00302     pcl::transformPointCloud(pc_nonground, pc_nonground, baseToWorld);
00303   } else {
00304     // directly transform to map frame:
00305     pcl::transformPointCloud(pc, pc, sensorToWorld);
00306 
00307     // just filter height range:
00308     pass.setInputCloud(pc.makeShared());
00309     pass.filter(pc);
00310 
00311     pc_nonground = pc;
00312     // pc_nonground is empty without ground segmentation
00313     pc_ground.header = pc.header;
00314     pc_nonground.header = pc.header;
00315   }
00316 
00317 
00318   insertScan(sensorToWorldTf.getOrigin(), pc_ground, pc_nonground);
00319 
00320   double total_elapsed = (ros::WallTime::now() - startTime).toSec();
00321   ROS_DEBUG("Pointcloud insertion in OctomapServer done (%zu+%zu pts (ground/nonground), %f sec)", pc_ground.size(), pc_nonground.size(), total_elapsed);
00322 
00323   publishAll(cloud->header.stamp);
00324 }
00325 
00326 void OctomapServer::insertScan(const tf::Point& sensorOriginTf, const PCLPointCloud& ground, const PCLPointCloud& nonground){
00327   point3d sensorOrigin = pointTfToOctomap(sensorOriginTf);
00328 
00329   if (!m_octree->coordToKeyChecked(sensorOrigin, m_updateBBXMin)
00330     || !m_octree->coordToKeyChecked(sensorOrigin, m_updateBBXMax))
00331   {
00332     ROS_ERROR_STREAM("Could not generate Key for origin "<<sensorOrigin);
00333   }
00334 
00335 
00336 
00337   // instead of direct scan insertion, compute update to filter ground:
00338   KeySet free_cells, occupied_cells;
00339   // insert ground points only as free:
00340   for (PCLPointCloud::const_iterator it = ground.begin(); it != ground.end(); ++it){
00341     point3d point(it->x, it->y, it->z);
00342     // maxrange check
00343     if ((m_maxRange > 0.0) && ((point - sensorOrigin).norm() > m_maxRange) ) {
00344       point = sensorOrigin + (point - sensorOrigin).normalized() * m_maxRange;
00345     }
00346 
00347     // only clear space (ground points)
00348     if (m_octree->computeRayKeys(sensorOrigin, point, m_keyRay)){
00349       free_cells.insert(m_keyRay.begin(), m_keyRay.end());
00350     }
00351 
00352     octomap::OcTreeKey endKey;
00353     if (m_octree->coordToKeyChecked(point, endKey)){
00354       updateMinKey(endKey, m_updateBBXMin);
00355       updateMaxKey(endKey, m_updateBBXMax);
00356     } else{
00357       ROS_ERROR_STREAM("Could not generate Key for endpoint "<<point);
00358     }
00359   }
00360 
00361   // all other points: free on ray, occupied on endpoint:
00362   for (PCLPointCloud::const_iterator it = nonground.begin(); it != nonground.end(); ++it){
00363     point3d point(it->x, it->y, it->z);
00364     // maxrange check
00365     if ((m_maxRange < 0.0) || ((point - sensorOrigin).norm() <= m_maxRange) ) {
00366 
00367       // free cells
00368       if (m_octree->computeRayKeys(sensorOrigin, point, m_keyRay)){
00369         free_cells.insert(m_keyRay.begin(), m_keyRay.end());
00370       }
00371       // occupied endpoint
00372       OcTreeKey key;
00373       if (m_octree->coordToKeyChecked(point, key)){
00374         occupied_cells.insert(key);
00375 
00376         updateMinKey(key, m_updateBBXMin);
00377         updateMaxKey(key, m_updateBBXMax);
00378       }
00379     } else {// ray longer than maxrange:;
00380       point3d new_end = sensorOrigin + (point - sensorOrigin).normalized() * m_maxRange;
00381       if (m_octree->computeRayKeys(sensorOrigin, new_end, m_keyRay)){
00382         free_cells.insert(m_keyRay.begin(), m_keyRay.end());
00383 
00384         octomap::OcTreeKey endKey;
00385         if (m_octree->coordToKeyChecked(new_end, endKey)){
00386           updateMinKey(endKey, m_updateBBXMin);
00387           updateMaxKey(endKey, m_updateBBXMax);
00388         } else{
00389           ROS_ERROR_STREAM("Could not generate Key for endpoint "<<new_end);
00390         }
00391 
00392 
00393       }
00394     }
00395   }
00396 
00397   // mark free cells only if not seen occupied in this cloud
00398   for(KeySet::iterator it = free_cells.begin(), end=free_cells.end(); it!= end; ++it){
00399     if (occupied_cells.find(*it) == occupied_cells.end()){
00400       m_octree->updateNode(*it, false);
00401     }
00402   }
00403 
00404   // now mark all occupied cells:
00405   for (KeySet::iterator it = occupied_cells.begin(), end=free_cells.end(); it!= end; it++) {
00406     m_octree->updateNode(*it, true);
00407   }
00408 
00409   // TODO: eval lazy+updateInner vs. proper insertion
00410   // non-lazy by default (updateInnerOccupancy() too slow for large maps)
00411   //m_octree->updateInnerOccupancy();
00412   octomap::point3d minPt, maxPt;
00413   ROS_DEBUG_STREAM("Bounding box keys (before): " << m_updateBBXMin[0] << " " <<m_updateBBXMin[1] << " " << m_updateBBXMin[2] << " / " <<m_updateBBXMax[0] << " "<<m_updateBBXMax[1] << " "<< m_updateBBXMax[2]);
00414 
00415   // TODO: snap max / min keys to larger voxels by m_maxTreeDepth
00416 //   if (m_maxTreeDepth < 16)
00417 //   {
00418 //      OcTreeKey tmpMin = getIndexKey(m_updateBBXMin, m_maxTreeDepth); // this should give us the first key at depth m_maxTreeDepth that is smaller or equal to m_updateBBXMin (i.e. lower left in 2D grid coordinates)
00419 //      OcTreeKey tmpMax = getIndexKey(m_updateBBXMax, m_maxTreeDepth); // see above, now add something to find upper right
00420 //      tmpMax[0]+= m_octree->getNodeSize( m_maxTreeDepth ) - 1;
00421 //      tmpMax[1]+= m_octree->getNodeSize( m_maxTreeDepth ) - 1;
00422 //      tmpMax[2]+= m_octree->getNodeSize( m_maxTreeDepth ) - 1;
00423 //      m_updateBBXMin = tmpMin;
00424 //      m_updateBBXMax = tmpMax;
00425 //   }
00426 
00427   // TODO: we could also limit the bbx to be within the map bounds here (see publishing check)
00428   minPt = m_octree->keyToCoord(m_updateBBXMin);
00429   maxPt = m_octree->keyToCoord(m_updateBBXMax);
00430   ROS_DEBUG_STREAM("Updated area bounding box: "<< minPt << " - "<<maxPt);
00431   ROS_DEBUG_STREAM("Bounding box keys (after): " << m_updateBBXMin[0] << " " <<m_updateBBXMin[1] << " " << m_updateBBXMin[2] << " / " <<m_updateBBXMax[0] << " "<<m_updateBBXMax[1] << " "<< m_updateBBXMax[2]);
00432 
00433   if (m_compressMap)
00434     m_octree->prune();
00435 
00436 
00437 }
00438 
00439 
00440 
00441 void OctomapServer::publishAll(const ros::Time& rostime){
00442   ros::WallTime startTime = ros::WallTime::now();
00443   size_t octomapSize = m_octree->size();
00444   // TODO: estimate num occ. voxels for size of arrays (reserve)
00445   if (octomapSize <= 1){
00446     ROS_WARN("Nothing to publish, octree is empty");
00447     return;
00448   }
00449 
00450   bool publishFreeMap = m_publishFreeSpace && (m_latchedTopics || m_fmapPub.getNumSubscribers() > 0);
00451   bool publishFreeMarkerArray = m_publishFreeSpace && (m_latchedTopics || m_fmarkerPub.getNumSubscribers() > 0);
00452   bool publishCollisionMap = (m_latchedTopics || m_cmapPub.getNumSubscribers() > 0);
00453   bool publishCollisionObject = (m_latchedTopics || m_collisionObjectPub.getNumSubscribers() > 0);
00454   bool publishMarkerArray = (m_latchedTopics || m_markerPub.getNumSubscribers() > 0);
00455   bool publishPointCloud = (m_latchedTopics || m_pointCloudPub.getNumSubscribers() > 0);
00456   bool publishBinaryMap = (m_latchedTopics || m_binaryMapPub.getNumSubscribers() > 0);
00457   bool publishFullMap = (m_latchedTopics || m_fullMapPub.getNumSubscribers() > 0);
00458   m_publish2DMap = (m_latchedTopics || m_mapPub.getNumSubscribers() > 0);
00459 
00460   // init collision object:
00461   arm_navigation_msgs::CollisionObject collisionObject;
00462   collisionObject.header.frame_id = m_worldFrameId;
00463   collisionObject.header.stamp = rostime;
00464   collisionObject.id = "map";
00465   arm_navigation_msgs::OrientedBoundingBox collObjBox;
00466   collObjBox.axis.x = collObjBox.axis.y = 0.0;
00467   collObjBox.axis.z = 1.0;
00468   collObjBox.angle = 0.0;
00469 
00470   // init free map and box:
00471   arm_navigation_msgs::CollisionMap freeMap;
00472   freeMap.header.frame_id = m_worldFrameId;
00473   freeMap.header.stamp = rostime;
00474   arm_navigation_msgs::OrientedBoundingBox freeObjBox;
00475   freeObjBox.axis.x = freeObjBox.axis.y = 0.0;
00476   freeObjBox.axis.z = 1.0;
00477   freeObjBox.angle = 0.0;
00478 
00479   // init markers for free space:
00480   visualization_msgs::MarkerArray freeNodesVis;
00481   // each array stores all cubes of a different size, one for each depth level:
00482   freeNodesVis.markers.resize(m_treeDepth+1);
00483 
00484 
00485 
00486   //init collision map:
00487   arm_navigation_msgs::CollisionMap collisionMap;
00488   collisionMap.header.frame_id = m_worldFrameId;
00489   collisionMap.header.stamp = rostime;
00490   arm_navigation_msgs::Shape collObjShape;
00491   collObjShape.type = arm_navigation_msgs::Shape::BOX;
00492   collObjShape.dimensions.resize(3);
00493 
00494   geometry_msgs::Pose pose;
00495   pose.orientation = tf::createQuaternionMsgFromYaw(0.0);
00496 
00497   // init markers:
00498   visualization_msgs::MarkerArray occupiedNodesVis;
00499   // each array stores all cubes of a different size, one for each depth level:
00500   occupiedNodesVis.markers.resize(m_treeDepth+1);
00501 
00502   // init pointcloud:
00503   pcl::PointCloud<pcl::PointXYZ> pclCloud;
00504 
00505   // call pre-traversal hook:
00506   handlePreNodeTraversal(rostime);
00507 
00508   // now, traverse all leafs in the tree:
00509   for (OcTree::iterator it = m_octree->begin(m_maxTreeDepth),
00510       end = m_octree->end(); it != end; ++it)
00511   {
00512     bool inUpdateBBX = isInUpdateBBX(it);
00513 
00514     // call general hook:
00515     handleNode(it);
00516     if (inUpdateBBX)
00517       handleNodeInBBX(it);
00518 
00519     if (m_octree->isNodeOccupied(*it)){
00520       double z = it.getZ();
00521       if (z > m_occupancyMinZ && z < m_occupancyMaxZ)
00522       {
00523         double size = it.getSize();
00524         double x = it.getX();
00525         double y = it.getY();
00526 
00527         // Ignore speckles in the map:
00528         if (m_filterSpeckles && (it.getDepth() == m_treeDepth +1) && isSpeckleNode(it.getKey())){
00529           ROS_DEBUG("Ignoring single speckle at (%f,%f,%f)", x, y, z);
00530           continue;
00531         } // else: current octree node is no speckle, send it out
00532 
00533         handleOccupiedNode(it);
00534         if (inUpdateBBX)
00535           handleOccupiedNodeInBBX(it);
00536 
00537         // create collision object:
00538         if (publishCollisionObject){
00539           collObjShape.dimensions[0] = collObjShape.dimensions[1] = collObjShape.dimensions[2] = size;
00540           collisionObject.shapes.push_back(collObjShape);
00541           pose.position.x = x;
00542           pose.position.y = y;
00543           pose.position.z = z;
00544           collisionObject.poses.push_back(pose);
00545         }
00546 
00547         if (publishCollisionMap){
00548           collObjBox.extents.x = collObjBox.extents.y = collObjBox.extents.z = size;
00549 
00550           collObjBox.center.x = x;
00551           collObjBox.center.y = y;
00552           collObjBox.center.z = z;
00553           collisionMap.boxes.push_back(collObjBox);
00554 
00555         }
00556 
00557         //create marker:
00558         if (publishMarkerArray){
00559           unsigned idx = it.getDepth();
00560           assert(idx < occupiedNodesVis.markers.size());
00561 
00562           geometry_msgs::Point cubeCenter;
00563           cubeCenter.x = x;
00564           cubeCenter.y = y;
00565           cubeCenter.z = z;
00566 
00567           occupiedNodesVis.markers[idx].points.push_back(cubeCenter);
00568           if (m_useHeightMap){
00569             double minX, minY, minZ, maxX, maxY, maxZ;
00570             m_octree->getMetricMin(minX, minY, minZ);
00571             m_octree->getMetricMax(maxX, maxY, maxZ);
00572 
00573             double h = (1.0 - std::min(std::max((cubeCenter.z-minZ)/ (maxZ - minZ), 0.0), 1.0)) *m_colorFactor;
00574             occupiedNodesVis.markers[idx].colors.push_back(heightMapColor(h));
00575           }
00576         }
00577 
00578         // insert into pointcloud:
00579         if (publishPointCloud)
00580           pclCloud.push_back(pcl::PointXYZ(x, y, z));
00581 
00582       }
00583     } else{ // node not occupied => mark as free in 2D map if unknown so far
00584       double z = it.getZ();
00585       if (z > m_occupancyMinZ && z < m_occupancyMaxZ)
00586       {
00587         handleFreeNode(it);
00588         if (inUpdateBBX)
00589           handleFreeNodeInBBX(it);
00590 
00591         if (m_publishFreeSpace){
00592           double x = it.getX();
00593           double y = it.getY();
00594 
00595           if (publishFreeMap){
00596             freeObjBox.extents.x = freeObjBox.extents.y = freeObjBox.extents.z = it.getSize();
00597             freeObjBox.center.x = x;
00598             freeObjBox.center.y = y;
00599             freeObjBox.center.z = z;
00600             freeMap.boxes.push_back(freeObjBox);
00601           }
00602 
00603           //create marker for free space:
00604           if (publishFreeMarkerArray){
00605             unsigned idx = it.getDepth();
00606             assert(idx < freeNodesVis.markers.size());
00607 
00608             geometry_msgs::Point cubeCenter;
00609             cubeCenter.x = x;
00610             cubeCenter.y = y;
00611             cubeCenter.z = z;
00612 
00613             freeNodesVis.markers[idx].points.push_back(cubeCenter);
00614           }
00615         }
00616 
00617       }
00618     }
00619   }
00620 
00621   // call post-traversal hook:
00622   handlePostNodeTraversal(rostime);
00623 
00624   // finish MarkerArray:
00625   if (publishMarkerArray){
00626     for (unsigned i= 0; i < occupiedNodesVis.markers.size(); ++i){
00627       double size = m_octree->getNodeSize(i);
00628 
00629       occupiedNodesVis.markers[i].header.frame_id = m_worldFrameId;
00630       occupiedNodesVis.markers[i].header.stamp = rostime;
00631       occupiedNodesVis.markers[i].ns = "map";
00632       occupiedNodesVis.markers[i].id = i;
00633       occupiedNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00634       occupiedNodesVis.markers[i].scale.x = size;
00635       occupiedNodesVis.markers[i].scale.y = size;
00636       occupiedNodesVis.markers[i].scale.z = size;
00637       occupiedNodesVis.markers[i].color = m_color;
00638 
00639 
00640       if (occupiedNodesVis.markers[i].points.size() > 0)
00641         occupiedNodesVis.markers[i].action = visualization_msgs::Marker::ADD;
00642       else
00643         occupiedNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00644     }
00645 
00646     m_markerPub.publish(occupiedNodesVis);
00647   }
00648 
00649 
00650   // finish FreeMarkerArray:
00651   if (publishFreeMarkerArray){
00652     for (unsigned i= 0; i < freeNodesVis.markers.size(); ++i){
00653       double size = m_octree->getNodeSize(i);
00654 
00655       freeNodesVis.markers[i].header.frame_id = m_worldFrameId;
00656       freeNodesVis.markers[i].header.stamp = rostime;
00657       freeNodesVis.markers[i].ns = "map";
00658       freeNodesVis.markers[i].id = i;
00659       freeNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00660       freeNodesVis.markers[i].scale.x = size;
00661       freeNodesVis.markers[i].scale.y = size;
00662       freeNodesVis.markers[i].scale.z = size;
00663       freeNodesVis.markers[i].color = m_colorFree;
00664 
00665 
00666       if (freeNodesVis.markers[i].points.size() > 0)
00667         freeNodesVis.markers[i].action = visualization_msgs::Marker::ADD;
00668       else
00669         freeNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00670     }
00671 
00672     m_fmarkerPub.publish(freeNodesVis);
00673   }
00674 
00675 
00676   // finish pointcloud:
00677   if (publishPointCloud){
00678     sensor_msgs::PointCloud2 cloud;
00679     pcl::toROSMsg (pclCloud, cloud);
00680     cloud.header.frame_id = m_worldFrameId;
00681     cloud.header.stamp = rostime;
00682     m_pointCloudPub.publish(cloud);
00683   }
00684 
00685   if (publishCollisionObject)
00686     m_collisionObjectPub.publish(collisionObject);
00687 
00688   if (publishCollisionMap)
00689     m_cmapPub.publish(collisionMap);
00690 
00691   if (publishBinaryMap)
00692     publishBinaryOctoMap(rostime);
00693 
00694   if (publishFullMap)
00695     publishFullOctoMap(rostime);
00696 
00697   if (publishFreeMap)
00698     m_fmapPub.publish(freeMap);
00699 
00700 
00701 
00702   double total_elapsed = (ros::WallTime::now() - startTime).toSec();
00703   ROS_DEBUG("Map publishing in OctomapServer took %f sec", total_elapsed);
00704 
00705 }
00706 
00707 
00708 bool OctomapServer::octomapBinarySrv(OctomapSrv::Request  &req,
00709                                     OctomapSrv::Response &res)
00710 {
00711   ROS_INFO("Sending binary map data on service request");
00712   res.map.header.frame_id = m_worldFrameId;
00713   res.map.header.stamp = ros::Time::now();
00714   if (!octomap_msgs::binaryMapToMsg(*m_octree, res.map))
00715     return false;
00716 
00717   return true;
00718 }
00719 
00720 bool OctomapServer::octomapFullSrv(OctomapSrv::Request  &req,
00721                                     OctomapSrv::Response &res)
00722 {
00723   ROS_INFO("Sending full map data on service request");
00724   res.map.header.frame_id = m_worldFrameId;
00725   res.map.header.stamp = ros::Time::now();
00726 
00727 
00728   if (!octomap_msgs::fullMapToMsg(*m_octree, res.map))
00729     return false;
00730 
00731   return true;
00732 }
00733 
00734 bool OctomapServer::clearBBXSrv(BBXSrv::Request& req, BBXSrv::Response& resp){
00735   point3d min = pointMsgToOctomap(req.min);
00736   point3d max = pointMsgToOctomap(req.max);
00737 
00738   for(OcTree::leaf_bbx_iterator it = m_octree->begin_leafs_bbx(min,max),
00739       end=m_octree->end_leafs_bbx(); it!= end; ++it){
00740 
00741     it->setLogOdds(octomap::logodds(m_thresMin));
00742     //                  m_octree->updateNode(it.getKey(), -6.0f);
00743   }
00744   // TODO: eval which is faster (setLogOdds+updateInner or updateNode)
00745   m_octree->updateInnerOccupancy();
00746 
00747   publishAll(ros::Time::now());
00748 
00749   return true;
00750 }
00751 
00752 bool OctomapServer::resetSrv(std_srvs::Empty::Request& req, std_srvs::Empty::Response& resp) {
00753   visualization_msgs::MarkerArray occupiedNodesVis;
00754   occupiedNodesVis.markers.resize(m_treeDepth +1);
00755   ros::Time rostime = ros::Time::now();
00756   m_octree->clear();
00757   // clear 2D map:
00758   m_gridmap.data.clear();
00759   m_gridmap.info.height = 0.0;
00760   m_gridmap.info.width = 0.0;
00761   m_gridmap.info.resolution = 0.0;
00762   m_gridmap.info.origin.position.x = 0.0;
00763   m_gridmap.info.origin.position.y = 0.0;
00764 
00765   ROS_INFO("Cleared octomap");
00766   publishAll(rostime);
00767 
00768   publishBinaryOctoMap(rostime);
00769   for (unsigned i= 0; i < occupiedNodesVis.markers.size(); ++i){
00770 
00771     occupiedNodesVis.markers[i].header.frame_id = m_worldFrameId;
00772     occupiedNodesVis.markers[i].header.stamp = rostime;
00773     occupiedNodesVis.markers[i].ns = "map";
00774     occupiedNodesVis.markers[i].id = i;
00775     occupiedNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00776     occupiedNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00777   }
00778 
00779 
00780   m_markerPub.publish(occupiedNodesVis);
00781 
00782 
00783   visualization_msgs::MarkerArray freeNodesVis;
00784   freeNodesVis.markers.resize(m_treeDepth +1);
00785 
00786   for (unsigned i= 0; i < freeNodesVis.markers.size(); ++i){
00787 
00788     freeNodesVis.markers[i].header.frame_id = m_worldFrameId;
00789     freeNodesVis.markers[i].header.stamp = rostime;
00790     freeNodesVis.markers[i].ns = "map";
00791     freeNodesVis.markers[i].id = i;
00792     freeNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00793     freeNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00794   }
00795   m_fmarkerPub.publish(freeNodesVis);
00796 
00797   return true;
00798 }
00799 
00800 void OctomapServer::publishBinaryOctoMap(const ros::Time& rostime) const{
00801 
00802   Octomap map;
00803   map.header.frame_id = m_worldFrameId;
00804   map.header.stamp = rostime;
00805 
00806   if (octomap_msgs::binaryMapToMsg(*m_octree, map))
00807     m_binaryMapPub.publish(map);
00808   else
00809     ROS_ERROR("Error serializing OctoMap");
00810 }
00811 
00812 void OctomapServer::publishFullOctoMap(const ros::Time& rostime) const{
00813 
00814   Octomap map;
00815   map.header.frame_id = m_worldFrameId;
00816   map.header.stamp = rostime;
00817 
00818   if (octomap_msgs::fullMapToMsg(*m_octree, map))
00819     m_fullMapPub.publish(map);
00820   else
00821     ROS_ERROR("Error serializing OctoMap");
00822 
00823 }
00824 
00825 
00826 void OctomapServer::filterGroundPlane(const PCLPointCloud& pc, PCLPointCloud& ground, PCLPointCloud& nonground) const{
00827   ground.header = pc.header;
00828   nonground.header = pc.header;
00829 
00830   if (pc.size() < 50){
00831     ROS_WARN("Pointcloud in OctomapServer too small, skipping ground plane extraction");
00832     nonground = pc;
00833   } else {
00834     // plane detection for ground plane removal:
00835     pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
00836     pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
00837 
00838     // Create the segmentation object and set up:
00839     pcl::SACSegmentation<pcl::PointXYZ> seg;
00840     seg.setOptimizeCoefficients (true);
00841     // TODO: maybe a filtering based on the surface normals might be more robust / accurate?
00842     seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
00843     seg.setMethodType(pcl::SAC_RANSAC);
00844     seg.setMaxIterations(200);
00845     seg.setDistanceThreshold (m_groundFilterDistance);
00846     seg.setAxis(Eigen::Vector3f(0,0,1));
00847     seg.setEpsAngle(m_groundFilterAngle);
00848 
00849 
00850     PCLPointCloud cloud_filtered(pc);
00851     // Create the filtering object
00852     pcl::ExtractIndices<pcl::PointXYZ> extract;
00853     bool groundPlaneFound = false;
00854 
00855     while(cloud_filtered.size() > 10 && !groundPlaneFound){
00856       seg.setInputCloud(cloud_filtered.makeShared());
00857       seg.segment (*inliers, *coefficients);
00858       if (inliers->indices.size () == 0){
00859         ROS_INFO("PCL segmentation did not find any plane.");
00860 
00861         break;
00862       }
00863 
00864       extract.setInputCloud(cloud_filtered.makeShared());
00865       extract.setIndices(inliers);
00866 
00867       if (std::abs(coefficients->values.at(3)) < m_groundFilterPlaneDistance){
00868         ROS_DEBUG("Ground plane found: %zu/%zu inliers. Coeff: %f %f %f %f", inliers->indices.size(), cloud_filtered.size(),
00869                   coefficients->values.at(0), coefficients->values.at(1), coefficients->values.at(2), coefficients->values.at(3));
00870         extract.setNegative (false);
00871         extract.filter (ground);
00872 
00873         // remove ground points from full pointcloud:
00874         // workaround for PCL bug:
00875         if(inliers->indices.size() != cloud_filtered.size()){
00876           extract.setNegative(true);
00877           PCLPointCloud cloud_out;
00878           extract.filter(cloud_out);
00879           nonground += cloud_out;
00880           cloud_filtered = cloud_out;
00881         }
00882 
00883         groundPlaneFound = true;
00884       } else{
00885         ROS_DEBUG("Horizontal plane (not ground) found: %zu/%zu inliers. Coeff: %f %f %f %f", inliers->indices.size(), cloud_filtered.size(),
00886                   coefficients->values.at(0), coefficients->values.at(1), coefficients->values.at(2), coefficients->values.at(3));
00887         pcl::PointCloud<pcl::PointXYZ> cloud_out;
00888         extract.setNegative (false);
00889         extract.filter(cloud_out);
00890         nonground +=cloud_out;
00891         // debug
00892         //            pcl::PCDWriter writer;
00893         //            writer.write<pcl::PointXYZ>("nonground_plane.pcd",cloud_out, false);
00894 
00895         // remove current plane from scan for next iteration:
00896         // workaround for PCL bug:
00897         if(inliers->indices.size() != cloud_filtered.size()){
00898           extract.setNegative(true);
00899           cloud_out.points.clear();
00900           extract.filter(cloud_out);
00901           cloud_filtered = cloud_out;
00902         } else{
00903           cloud_filtered.points.clear();
00904         }
00905       }
00906 
00907     }
00908     // TODO: also do this if overall starting pointcloud too small?
00909     if (!groundPlaneFound){ // no plane found or remaining points too small
00910       ROS_WARN("No ground plane found in scan");
00911 
00912       // do a rough fitlering on height to prevent spurious obstacles
00913       pcl::PassThrough<pcl::PointXYZ> second_pass;
00914       second_pass.setFilterFieldName("z");
00915       second_pass.setFilterLimits(-m_groundFilterPlaneDistance, m_groundFilterPlaneDistance);
00916       second_pass.setInputCloud(pc.makeShared());
00917       second_pass.filter(ground);
00918 
00919       second_pass.setFilterLimitsNegative (true);
00920       second_pass.filter(nonground);
00921     }
00922 
00923     // debug:
00924     //        pcl::PCDWriter writer;
00925     //        if (pc_ground.size() > 0)
00926     //          writer.write<pcl::PointXYZ>("ground.pcd",pc_ground, false);
00927     //        if (pc_nonground.size() > 0)
00928     //          writer.write<pcl::PointXYZ>("nonground.pcd",pc_nonground, false);
00929 
00930   }
00931 
00932 
00933 }
00934 
00935 void OctomapServer::handlePreNodeTraversal(const ros::Time& rostime){
00936   if (m_publish2DMap){
00937     // init projected 2D map:
00938     m_gridmap.header.frame_id = m_worldFrameId;
00939     m_gridmap.header.stamp = rostime;
00940     nav_msgs::MapMetaData oldMapInfo = m_gridmap.info;
00941 
00942     // TODO: move most of this stuff into c'tor and init map only once (adjust if size changes)
00943     double minX, minY, minZ, maxX, maxY, maxZ;
00944     m_octree->getMetricMin(minX, minY, minZ);
00945     m_octree->getMetricMax(maxX, maxY, maxZ);
00946 
00947     octomap::point3d minPt(minX, minY, minZ);
00948     octomap::point3d maxPt(maxX, maxY, maxZ);
00949     octomap::OcTreeKey minKey = m_octree->coordToKey(minPt, m_maxTreeDepth);
00950     octomap::OcTreeKey maxKey = m_octree->coordToKey(maxPt, m_maxTreeDepth);
00951     
00952     ROS_DEBUG("MinKey: %d %d %d / MaxKey: %d %d %d", minKey[0], minKey[1], minKey[2], maxKey[0], maxKey[1], maxKey[2]);
00953 
00954     // add padding if requested (= new min/maxPts in x&y):
00955     double halfPaddedX = 0.5*m_minSizeX;
00956     double halfPaddedY = 0.5*m_minSizeY;
00957     minX = std::min(minX, -halfPaddedX);
00958     maxX = std::max(maxX, halfPaddedX);
00959     minY = std::min(minY, -halfPaddedY);
00960     maxY = std::max(maxY, halfPaddedY);
00961     minPt = octomap::point3d(minX, minY, minZ);
00962     maxPt = octomap::point3d(maxX, maxY, maxZ);
00963 
00964     OcTreeKey paddedMaxKey;
00965     if (!m_octree->coordToKeyChecked(minPt, m_maxTreeDepth, m_paddedMinKey)){
00966       ROS_ERROR("Could not create padded min OcTree key at %f %f %f", minPt.x(), minPt.y(), minPt.z());
00967       return;
00968     }
00969     if (!m_octree->coordToKeyChecked(maxPt, m_maxTreeDepth, paddedMaxKey)){
00970       ROS_ERROR("Could not create padded max OcTree key at %f %f %f", maxPt.x(), maxPt.y(), maxPt.z());
00971       return;
00972     }
00973 
00974     ROS_DEBUG("Padded MinKey: %d %d %d / padded MaxKey: %d %d %d", m_paddedMinKey[0], m_paddedMinKey[1], m_paddedMinKey[2], paddedMaxKey[0], paddedMaxKey[1], paddedMaxKey[2]);
00975     assert(paddedMaxKey[0] >= maxKey[0] && paddedMaxKey[1] >= maxKey[1]);
00976 
00977     m_multires2DScale = 1 << (m_treeDepth - m_maxTreeDepth);
00978     m_gridmap.info.width = (paddedMaxKey[0] - m_paddedMinKey[0])/m_multires2DScale +1;
00979     m_gridmap.info.height = (paddedMaxKey[1] - m_paddedMinKey[1])/m_multires2DScale +1;
00980 
00981     int mapOriginX = minKey[0] - m_paddedMinKey[0];
00982     int mapOriginY = minKey[1] - m_paddedMinKey[1];
00983     assert(mapOriginX >= 0 && mapOriginY >= 0);
00984 
00985     // might not exactly be min / max of octree:
00986     octomap::point3d origin = m_octree->keyToCoord(m_paddedMinKey, m_treeDepth);
00987     double gridRes = m_octree->getNodeSize(m_maxTreeDepth);
00988     m_projectCompleteMap = (!m_incrementalUpdate || (std::abs(gridRes-m_gridmap.info.resolution) > 1e-6));
00989     m_gridmap.info.resolution = gridRes;
00990     m_gridmap.info.origin.position.x = origin.x() - gridRes*0.5;
00991     m_gridmap.info.origin.position.y = origin.y() - gridRes*0.5;
00992     if (m_maxTreeDepth != m_treeDepth){
00993       m_gridmap.info.origin.position.x -= m_res/2.0;
00994       m_gridmap.info.origin.position.y -= m_res/2.0;
00995     }
00996     
00997     // workaround for  multires. projection not working properly for inner nodes:
00998     // force re-building complete map
00999     if (m_maxTreeDepth < m_treeDepth)
01000       m_projectCompleteMap = true;
01001 
01002 
01003     if(m_projectCompleteMap){
01004       ROS_DEBUG("Rebuilding complete 2D map");
01005       m_gridmap.data.clear();
01006       // init to unknown:
01007       m_gridmap.data.resize(m_gridmap.info.width * m_gridmap.info.height, -1);
01008 
01009     } else {
01010 
01011        if (mapChanged(oldMapInfo, m_gridmap.info)){
01012           ROS_DEBUG("2D grid map size changed to %dx%d", m_gridmap.info.width, m_gridmap.info.height);
01013           adjustMapData(m_gridmap, oldMapInfo);
01014        }
01015        nav_msgs::OccupancyGrid::_data_type::iterator startIt;
01016        size_t mapUpdateBBXMinX = std::max(0, (int(m_updateBBXMin[0]) - int(m_paddedMinKey[0]))/int(m_multires2DScale));
01017        size_t mapUpdateBBXMinY = std::max(0, (int(m_updateBBXMin[1]) - int(m_paddedMinKey[1]))/int(m_multires2DScale));
01018        size_t mapUpdateBBXMaxX = std::min(int(m_gridmap.info.width-1), (int(m_updateBBXMax[0]) - int(m_paddedMinKey[0]))/int(m_multires2DScale));
01019        size_t mapUpdateBBXMaxY = std::min(int(m_gridmap.info.height-1), (int(m_updateBBXMax[1]) - int(m_paddedMinKey[1]))/int(m_multires2DScale));
01020 
01021        assert(mapUpdateBBXMaxX > mapUpdateBBXMinX);
01022        assert(mapUpdateBBXMaxY > mapUpdateBBXMinY);
01023 
01024        size_t numCols = mapUpdateBBXMaxX-mapUpdateBBXMinX +1;
01025 
01026        // test for max idx:
01027        uint max_idx = m_gridmap.info.width*mapUpdateBBXMaxY + mapUpdateBBXMaxX;
01028        if (max_idx  >= m_gridmap.data.size())
01029          ROS_ERROR("BBX index not valid: %d (max index %zu for size %d x %d) update-BBX is: [%zu %zu]-[%zu %zu]", max_idx, m_gridmap.data.size(), m_gridmap.info.width, m_gridmap.info.height, mapUpdateBBXMinX, mapUpdateBBXMinY, mapUpdateBBXMaxX, mapUpdateBBXMaxY);
01030 
01031        // reset proj. 2D map in bounding box:
01032        for (unsigned int j = mapUpdateBBXMinY; j <= mapUpdateBBXMaxY; ++j){
01033           std::fill_n(m_gridmap.data.begin() + m_gridmap.info.width*j+mapUpdateBBXMinX,
01034                       numCols, -1);
01035        }
01036 
01037     }
01038        
01039 
01040 
01041   }
01042 
01043 }
01044 
01045 void OctomapServer::handlePostNodeTraversal(const ros::Time& rostime){
01046 
01047   if (m_publish2DMap)
01048     m_mapPub.publish(m_gridmap);
01049 }
01050 
01051 void OctomapServer::handleOccupiedNode(const OcTreeT::iterator& it){
01052 
01053   if (m_publish2DMap && m_projectCompleteMap){
01054     update2DMap(it, true);
01055   }
01056 }
01057 
01058 void OctomapServer::handleFreeNode(const OcTreeT::iterator& it){
01059 
01060   if (m_publish2DMap && m_projectCompleteMap){
01061     update2DMap(it, false);
01062   }
01063 }
01064 
01065 void OctomapServer::handleOccupiedNodeInBBX(const OcTreeT::iterator& it){
01066 
01067   if (m_publish2DMap && !m_projectCompleteMap){
01068     update2DMap(it, true);
01069   }
01070 }
01071 
01072 void OctomapServer::handleFreeNodeInBBX(const OcTreeT::iterator& it){
01073 
01074   if (m_publish2DMap && !m_projectCompleteMap){
01075     update2DMap(it, false);
01076   }
01077 }
01078 
01079 void OctomapServer::update2DMap(const OcTreeT::iterator& it, bool occupied){
01080 
01081   // update 2D map (occupied always overrides):
01082 
01083   if (it.getDepth() == m_maxTreeDepth){
01084     unsigned idx = mapIdx(it.getKey());
01085     if (occupied)
01086       m_gridmap.data[mapIdx(it.getKey())] = 100;
01087     else if (m_gridmap.data[idx] == -1){
01088       m_gridmap.data[idx] = 0;
01089     }
01090 
01091   } else{
01092     int intSize = 1 << (m_maxTreeDepth - it.getDepth());
01093     octomap::OcTreeKey minKey=it.getIndexKey();
01094     for(int dx=0; dx < intSize; dx++){
01095       int i = (minKey[0]+dx - m_paddedMinKey[0])/m_multires2DScale;
01096       for(int dy=0; dy < intSize; dy++){
01097         unsigned idx = mapIdx(i, (minKey[1]+dy - m_paddedMinKey[1])/m_multires2DScale);
01098         if (occupied)
01099           m_gridmap.data[idx] = 100;
01100         else if (m_gridmap.data[idx] == -1){
01101           m_gridmap.data[idx] = 0;
01102         }
01103       }
01104     }
01105   }
01106 
01107 
01108 }
01109 
01110 
01111 
01112 bool OctomapServer::isSpeckleNode(const OcTreeKey&nKey) const {
01113   OcTreeKey key;
01114   bool neighborFound = false;
01115   for (key[2] = nKey[2] - 1; !neighborFound && key[2] <= nKey[2] + 1; ++key[2]){
01116     for (key[1] = nKey[1] - 1; !neighborFound && key[1] <= nKey[1] + 1; ++key[1]){
01117       for (key[0] = nKey[0] - 1; !neighborFound && key[0] <= nKey[0] + 1; ++key[0]){
01118         if (key != nKey){
01119           OcTreeNode* node = m_octree->search(key);
01120           if (node && m_octree->isNodeOccupied(node)){
01121             // we have a neighbor => break!
01122             neighborFound = true;
01123           }
01124         }
01125       }
01126     }
01127   }
01128 
01129   return neighborFound;
01130 }
01131 
01132 void OctomapServer::reconfigureCallback(octomap_server::OctomapServerConfig& config, uint32_t level){
01133   if (m_maxTreeDepth != unsigned(config.max_depth)){
01134     m_maxTreeDepth = unsigned(config.max_depth);
01135 
01136     publishAll();
01137   }
01138 
01139 
01140 }
01141 
01142 void OctomapServer::adjustMapData(nav_msgs::OccupancyGrid& map, const nav_msgs::MapMetaData& oldMapInfo) const{
01143   if (map.info.resolution != oldMapInfo.resolution){
01144     ROS_ERROR("Resolution of map changed, cannot be adjusted");
01145     return;
01146   }
01147 
01148   int i_off = int((oldMapInfo.origin.position.x - map.info.origin.position.x)/map.info.resolution +0.5);
01149   int j_off = int((oldMapInfo.origin.position.y - map.info.origin.position.y)/map.info.resolution +0.5);
01150 
01151   if (i_off < 0 || j_off < 0
01152       || oldMapInfo.width  + i_off > map.info.width
01153       || oldMapInfo.height + j_off > map.info.height)
01154   {
01155     ROS_ERROR("New 2D map does not contain old map area, this case is not implemented");
01156     return;
01157   }
01158 
01159   nav_msgs::OccupancyGrid::_data_type oldMapData = map.data;
01160 
01161   map.data.clear();
01162   // init to unknown:
01163   map.data.resize(map.info.width * map.info.height, -1);
01164 
01165   nav_msgs::OccupancyGrid::_data_type::iterator fromStart, fromEnd, toStart;
01166 
01167   for (int j =0; j < int(oldMapInfo.height); ++j ){
01168     // copy chunks, row by row:
01169     fromStart = oldMapData.begin() + j*oldMapInfo.width;
01170     fromEnd = fromStart + oldMapInfo.width;
01171     toStart = map.data.begin() + ((j+j_off)*m_gridmap.info.width + i_off);
01172     copy(fromStart, fromEnd, toStart);
01173 
01174 //    for (int i =0; i < int(oldMapInfo.width); ++i){
01175 //      map.data[m_gridmap.info.width*(j+j_off) +i+i_off] = oldMapData[oldMapInfo.width*j +i];
01176 //    }
01177 
01178   }
01179 
01180 }
01181 
01182 
01183 std_msgs::ColorRGBA OctomapServer::heightMapColor(double h) {
01184 
01185   std_msgs::ColorRGBA color;
01186   color.a = 1.0;
01187   // blend over HSV-values (more colors)
01188 
01189   double s = 1.0;
01190   double v = 1.0;
01191 
01192   h -= floor(h);
01193   h *= 6;
01194   int i;
01195   double m, n, f;
01196 
01197   i = floor(h);
01198   f = h - i;
01199   if (!(i & 1))
01200     f = 1 - f; // if i is even
01201   m = v * (1 - s);
01202   n = v * (1 - s * f);
01203 
01204   switch (i) {
01205     case 6:
01206     case 0:
01207       color.r = v; color.g = n; color.b = m;
01208       break;
01209     case 1:
01210       color.r = n; color.g = v; color.b = m;
01211       break;
01212     case 2:
01213       color.r = m; color.g = v; color.b = n;
01214       break;
01215     case 3:
01216       color.r = m; color.g = n; color.b = v;
01217       break;
01218     case 4:
01219       color.r = n; color.g = m; color.b = v;
01220       break;
01221     case 5:
01222       color.r = v; color.g = m; color.b = n;
01223       break;
01224     default:
01225       color.r = 1; color.g = 0.5; color.b = 0.5;
01226       break;
01227   }
01228 
01229   return color;
01230 }
01231 }
01232 
01233 
01234 


octomap_server
Author(s): Armin Hornung
autogenerated on Mon Oct 6 2014 02:56:34