/* * MapsManager.cpp * * Created on: 2015-05-14 * Author: mathieu */ #include "MapsManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_OCTOMAP #include #endif using namespace rtabmap; MapsManager::MapsManager(bool usePublicNamespace) : cloudDecimation_(4), cloudMaxDepth_(4.0), // meters cloudMinDepth_(0.0), // meters cloudVoxelSize_(0.05), // meters cloudFloorCullingHeight_(0.0), cloudCeilingCullingHeight_(0.0), cloudOutputVoxelized_(false), cloudFrustumCulling_(false), cloudNoiseFilteringRadius_(0.0), cloudNoiseFilteringMinNeighbors_(5), scanDecimation_(0), scanVoxelSize_(0.0), scanOutputVoxelized_(false), projMaxGroundAngle_(45.0), // degrees projMinClusterSize_(20), projMaxObstaclesHeight_(2.0), // meters (<=0 disabled) projMaxGroundHeight_(0.0), // meters (<=0 disabled, only works if proj_detect_flat_obstacles is true) projDetectFlatObstacles_(false), gridCellSize_(0.05), // meters gridSize_(0), // meters gridEroded_(false), gridUnknownSpaceFilled_(false), gridMaxUnknownSpaceFilledRange_(6.0), mapFilterRadius_(0.5), mapFilterAngle_(30.0), // degrees mapCacheCleanup_(true), negativePosesIgnored(false) { ros::NodeHandle nh; ros::NodeHandle pnh("~"); // cloud map stuff pnh.param("cloud_decimation", cloudDecimation_, cloudDecimation_); pnh.param("cloud_max_depth", cloudMaxDepth_, cloudMaxDepth_); pnh.param("cloud_min_depth", cloudMinDepth_, cloudMinDepth_); pnh.param("cloud_voxel_size", cloudVoxelSize_, cloudVoxelSize_); pnh.param("cloud_floor_culling_height", cloudFloorCullingHeight_, cloudFloorCullingHeight_); pnh.param("cloud_ceiling_culling_height", cloudCeilingCullingHeight_, cloudCeilingCullingHeight_); if(cloudFloorCullingHeight_ > 0 && cloudCeilingCullingHeight_ > 0 && cloudCeilingCullingHeight_ < cloudFloorCullingHeight_) { ROS_WARN("\"cloud_floor_culling_height\" should be lower than \"cloud_ceiling_culling_height\", setting \"cloud_ceiling_culling_height\" to 0 (disabled)."); cloudCeilingCullingHeight_ = 0; } pnh.param("cloud_output_voxelized", cloudOutputVoxelized_, cloudOutputVoxelized_); pnh.param("cloud_frustum_culling", cloudFrustumCulling_, cloudFrustumCulling_); pnh.param("cloud_noise_filtering_radius", cloudNoiseFilteringRadius_, cloudNoiseFilteringRadius_); pnh.param("cloud_noise_filtering_min_neighbors", cloudNoiseFilteringMinNeighbors_, cloudNoiseFilteringMinNeighbors_); // scan map stuff pnh.param("scan_decimation", scanDecimation_, scanDecimation_); pnh.param("scan_voxel_size", scanVoxelSize_, scanVoxelSize_); pnh.param("scan_output_voxelized", scanOutputVoxelized_, scanOutputVoxelized_); //projection map stuff pnh.param("proj_max_ground_angle", projMaxGroundAngle_, projMaxGroundAngle_); pnh.param("proj_min_cluster_size", projMinClusterSize_, projMinClusterSize_); if(pnh.hasParam("proj_max_height") && !pnh.hasParam("proj_max_obstacles_height")) { ROS_WARN("Parameter \"proj_max_height\" has been renamed " "to \"proj_max_obstacles_height\"! Your value is still copied to " "corresponding parameter."); pnh.param("proj_max_height", projMaxObstaclesHeight_, projMaxObstaclesHeight_); } else { pnh.param("proj_max_obstacles_height", projMaxObstaclesHeight_, projMaxObstaclesHeight_); } pnh.param("proj_max_ground_height", projMaxGroundHeight_, projMaxGroundHeight_); pnh.param("proj_detect_flat_obstacles", projDetectFlatObstacles_, projDetectFlatObstacles_); // common grid map stuff pnh.param("grid_cell_size", gridCellSize_, gridCellSize_); // m if(gridCellSize_ <= 0) { ROS_FATAL("\"grid_cell_size\" (%f) should be greater than 0!", gridCellSize_); } pnh.param("grid_size", gridSize_, gridSize_); // m pnh.param("grid_eroded", gridEroded_, gridEroded_); pnh.param("grid_unknown_space_filled", gridUnknownSpaceFilled_, gridUnknownSpaceFilled_); pnh.param("grid_unknown_space_filled_max_range", gridMaxUnknownSpaceFilledRange_, gridMaxUnknownSpaceFilledRange_); // common map stuff pnh.param("map_filter_radius", mapFilterRadius_, mapFilterRadius_); pnh.param("map_filter_angle", mapFilterAngle_, mapFilterAngle_); pnh.param("map_cleanup", mapCacheCleanup_, mapCacheCleanup_); pnh.param("map_negative_poses_ignored", negativePosesIgnored, negativePosesIgnored); // If true, the last message published on // the map topics will be saved and sent to new subscribers when they // connect bool latch = true; pnh.param("latch", latch, latch); // mapping topics if(usePublicNamespace) { cloudMapPub_ = nh.advertise("cloud_map", 1, latch); projMapPub_ = nh.advertise("proj_map", 1, latch); gridMapPub_ = nh.advertise("grid_map", 1, latch); scanMapPub_ = nh.advertise("scan_map", 1, latch); } else { cloudMapPub_ = pnh.advertise("cloud_map", 1, latch); projMapPub_ = pnh.advertise("proj_map", 1, latch); gridMapPub_ = pnh.advertise("grid_map", 1, latch); scanMapPub_ = pnh.advertise("scan_map", 1, latch); } } MapsManager::~MapsManager() { clear(); } void MapsManager::clear() { clouds_.clear(); cameraModels_.clear(); projMaps_.clear(); gridMaps_.clear(); } bool MapsManager::hasSubscribers() const { return cloudMapPub_.getNumSubscribers() != 0 || projMapPub_.getNumSubscribers() != 0 || gridMapPub_.getNumSubscribers() != 0 || scanMapPub_.getNumSubscribers() != 0; } std::map MapsManager::getFilteredPoses(const std::map & poses) { if(mapFilterRadius_ > 0.0) { // filter nodes double angle = mapFilterAngle_ == 0.0?CV_PI+0.1:mapFilterAngle_*CV_PI/180.0; return rtabmap::graph::radiusPosesFiltering(poses, mapFilterRadius_, angle); } return std::map(); } std::map MapsManager::updateMapCaches( const std::map & poses, const rtabmap::Memory * memory, bool updateCloud, bool updateProj, bool updateGrid, bool updateScan, const std::map & signatures) { if(!updateCloud && !updateProj && !updateGrid && !updateScan) { // all false, udpate only those where we have subscribers updateCloud = cloudMapPub_.getNumSubscribers() != 0; updateProj = projMapPub_.getNumSubscribers() != 0; updateGrid = gridMapPub_.getNumSubscribers() != 0; updateScan = scanMapPub_.getNumSubscribers() != 0; } UDEBUG("Updating map caches..."); if(!memory && signatures.size() == 0) { ROS_ERROR("Memory and signatures should not be both null!?"); return std::map(); } std::map filteredPoses; // update cache if(updateCloud || updateProj || updateGrid || updateScan) { // filter nodes if(mapFilterRadius_ > 0.0) { UDEBUG("Filter nodes..."); double angle = mapFilterAngle_ == 0.0?CV_PI+0.1:mapFilterAngle_*CV_PI/180.0; filteredPoses = rtabmap::graph::radiusPosesFiltering(poses, mapFilterRadius_, angle); for(std::map::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter) { if(iter->first <=0) { // make sure to keep latest data filteredPoses.insert(*iter); } else { break; } } } else { filteredPoses = poses; } if(negativePosesIgnored) { for(std::map::iterator iter=filteredPoses.begin(); iter!=filteredPoses.end();) { if(iter->first <= 0) { filteredPoses.erase(iter++); } else { ++iter; } } } for(std::map::iterator iter=filteredPoses.begin(); iter!=filteredPoses.end(); ++iter) { if(!iter->second.isNull()) { rtabmap::SensorData data; bool rgbDepthRequired = updateCloud && (iter->first < 0 || !uContains(clouds_, iter->first)); bool depthRequired = updateProj && (iter->first < 0 || !uContains(projMaps_, iter->first)); bool gridRequired = updateGrid && (iter->first < 0 || !uContains(gridMaps_, iter->first)); bool scanRequired = updateScan && (iter->first < 0 || !uContains(scans_, iter->first)); if(rgbDepthRequired || depthRequired || scanRequired || gridRequired) { UDEBUG("Data required for %d", iter->first); std::map::const_iterator findIter = signatures.find(iter->first); if(findIter != signatures.end()) { data = findIter->second.sensorData(); } else if(memory) { data = memory->getSignatureDataConst(iter->first); } } if(data.id() != 0) { if(!(data.imageCompressed().empty() && data.imageRaw().empty()) && !(data.depthOrRightCompressed().empty() && data.depthOrRightRaw().empty()) && (data.cameraModels().size() || data.stereoCameraModel().isValidForProjection())) { // Which data should we decompress? cv::Mat image, depth, scan; data.uncompressData( (rgbDepthRequired||data.stereoCameraModel().isValidForProjection()) ? &image:0, (rgbDepthRequired||depthRequired) ? &depth:0, scanRequired||gridRequired?&scan:0); pcl::PointCloud::Ptr cloudRGB; pcl::PointCloud::Ptr cloudXYZ; if(rgbDepthRequired) { UDEBUG("rgbDepthRequired"); if(!image.empty() && !depth.empty()) { pcl::IndicesPtr validIndices(new std::vector); cloudRGB = util3d::cloudRGBFromSensorData( data, cloudDecimation_, cloudMaxDepth_, cloudMinDepth_, validIndices.get()); if(cloudVoxelSize_) { cloudRGB = util3d::voxelize(cloudRGB, validIndices, cloudVoxelSize_); } if(cloudRGB->size() && cloudNoiseFilteringRadius_ > 0.0 && cloudNoiseFilteringMinNeighbors_ > 0) { pcl::IndicesPtr indices = rtabmap::util3d::radiusFiltering(cloudRGB, cloudNoiseFilteringRadius_, cloudNoiseFilteringMinNeighbors_); pcl::PointCloud::Ptr tmp(new pcl::PointCloud); pcl::copyPointCloud(*cloudRGB, *indices, *tmp); cloudRGB = tmp; } } else { ROS_ERROR("RGB or Depth image not found (node=%d)!", iter->first); } } else if(depthRequired) { UDEBUG("depthRequired"); if( !depth.empty()) { pcl::IndicesPtr validIndices(new std::vector); cloudXYZ = util3d::cloudFromSensorData( data, cloudDecimation_, cloudMaxDepth_, cloudMinDepth_, validIndices.get()); // use gridCellSize since this cloud is only for the projection map UASSERT(gridCellSize_ > 0); cloudXYZ = util3d::voxelize(cloudXYZ, validIndices, gridCellSize_); if(cloudXYZ->size() && cloudNoiseFilteringRadius_ > 0.0 && cloudNoiseFilteringMinNeighbors_ > 0) { pcl::IndicesPtr indices = rtabmap::util3d::radiusFiltering(cloudXYZ, cloudNoiseFilteringRadius_, cloudNoiseFilteringMinNeighbors_); pcl::PointCloud::Ptr tmp(new pcl::PointCloud); pcl::copyPointCloud(*cloudXYZ, *indices, *tmp); cloudXYZ = tmp; } } else { ROS_ERROR("RGB or Depth image not found (node=%d)!", iter->first); } } if(cloudRGB.get()) { uInsert(clouds_, std::make_pair(iter->first, cloudRGB)); // Make sure that image size is set in camera models. // The camera models are used when cloud_frustum_culling=true. std::vector models; if(data.stereoCameraModel().isValidForProjection()) { //insert only the left camera model rtabmap::CameraModel model = data.stereoCameraModel().left(); model.setImageSize(cv::Size(data.imageRaw().cols, data.imageRaw().rows)); models.push_back(model); } else if(data.cameraModels().size()) { UASSERT_MSG(data.imageRaw().cols % data.cameraModels().size() == 0, uFormat("data.imageRaw().cols=%d data.cameraModels().size()=%d", data.imageRaw().cols, (int)data.cameraModels().size()).c_str()); models.resize(data.cameraModels().size()); for(unsigned int i=0; ifirst, models)); } if(depthRequired) { UDEBUG("Creating proj map for %d...", iter->first); cv::Mat ground, obstacles; if(cloudRGB.get()) { pcl::PointCloud::Ptr cloudClipped = cloudRGB; if(cloudClipped->size() && projMaxObstaclesHeight_ > 0) { cloudClipped = util3d::passThrough(cloudClipped, "z", std::numeric_limits::min(), projMaxObstaclesHeight_); } if(cloudClipped->size() && gridCellSize_ > cloudVoxelSize_) { cloudClipped = util3d::voxelize(cloudClipped, gridCellSize_); } if(cloudClipped->size()) { // add pose rotation without yaw float roll, pitch, yaw; iter->second.getEulerAngles(roll, pitch, yaw); cloudClipped = util3d::transformPointCloud(cloudClipped, Transform(0,0,0, roll, pitch, 0)); util3d::occupancy2DFromCloud3D(cloudClipped, ground, obstacles, gridCellSize_, projMaxGroundAngle_*M_PI/180.0, projMinClusterSize_, projDetectFlatObstacles_, projMaxGroundHeight_); } } else if(cloudXYZ.get()) { pcl::PointCloud::Ptr cloudClipped = cloudXYZ; if(cloudClipped->size() && projMaxObstaclesHeight_ > 0) { cloudClipped = util3d::passThrough(cloudClipped, "z", std::numeric_limits::min(), projMaxObstaclesHeight_); } if(cloudClipped->size()) { // add pose rotation without yaw float roll, pitch, yaw; iter->second.getEulerAngles(roll, pitch, yaw); cloudClipped = util3d::transformPointCloud(cloudClipped, Transform(0,0,0, roll, pitch, 0)); UDEBUG("util3d::occupancy2DFromCloud3D()"); util3d::occupancy2DFromCloud3D(cloudClipped, ground, obstacles, gridCellSize_, projMaxGroundAngle_*M_PI/180.0, projMinClusterSize_, projDetectFlatObstacles_, projMaxGroundHeight_); } } uInsert(projMaps_, std::make_pair(iter->first, std::make_pair(ground, obstacles))); } if(scanRequired || gridRequired) { if(scan.cols && (gridRequired || scanVoxelSize_ > 0.0)) { if(scanDecimation_ > 1) { scan = util3d::downsample(scan, scanDecimation_); } if(scanRequired || scanVoxelSize_ > 0.0) { pcl::PointCloud::Ptr scanCloud = util3d::laserScanToPointCloud(scan); if(scanVoxelSize_ > 0.0) { scanCloud = util3d::voxelize(scanCloud, scanVoxelSize_); if(gridRequired && scan.type() == CV_32FC2) { scan = util3d::laserScan2dFromPointCloud(*scanCloud); } } if(scanRequired) { uInsert(scans_, std::make_pair(iter->first, scanCloud)); } } } if(gridRequired && scan.type() == CV_32FC2) { cv::Mat ground, obstacles; util3d::occupancy2DFromLaserScan( scan, ground, obstacles, gridCellSize_, data.id() < 0 || gridUnknownSpaceFilled_, data.laserScanMaxRange()>gridMaxUnknownSpaceFilledRange_?gridMaxUnknownSpaceFilledRange_:data.laserScanMaxRange()); uInsert(gridMaps_, std::make_pair(iter->first, std::make_pair(ground, obstacles))); } } } else { ROS_ERROR("Some data missing for node %d to update the maps (image=%d, depth=%d, camera=%d)", iter->first, !(data.imageCompressed().empty() && data.imageRaw().empty())?1:0, !(data.depthOrRightCompressed().empty() && data.depthOrRightRaw().empty())?1:0, (data.cameraModels().size() || data.stereoCameraModel().isValidForProjection())?1:0); } } } else { ROS_ERROR("Pose null for node %d", iter->first); } } // cleanup not used nodes UDEBUG("Cleanup not used nodes"); for(std::map::Ptr >::iterator iter=clouds_.begin(); iter!=clouds_.end();) { if(!uContains(poses, iter->first)) { clouds_.erase(iter++); } else { ++iter; } } for(std::map >::iterator iter=projMaps_.begin(); iter!=projMaps_.end();) { if(!uContains(poses, iter->first)) { projMaps_.erase(iter++); } else { ++iter; } } for(std::map >::iterator iter=gridMaps_.begin(); iter!=gridMaps_.end();) { if(!uContains(poses, iter->first)) { gridMaps_.erase(iter++); } else { ++iter; } } for(std::map >::iterator iter=cameraModels_.begin(); iter!=cameraModels_.end();) { if(!uContains(poses, iter->first)) { cameraModels_.erase(iter++); } else { ++iter; } } } return filteredPoses; } void MapsManager::publishMaps( const std::map & poses, const ros::Time & stamp, const std::string & mapFrameId) { UDEBUG("Publishing maps..."); // publish maps if(cloudMapPub_.getNumSubscribers()) { // generate the assembled cloud! UTimer time; pcl::PointCloud::Ptr assembledCloud(new pcl::PointCloud); int count = 0; std::list > negativePoses; for(std::map::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter) { if(iter->first > 0) { std::map::Ptr >::iterator jter = clouds_.find(iter->first); if(jter != clouds_.end()) { pcl::PointCloud::Ptr transformed = util3d::transformPointCloud(jter->second, iter->second); *assembledCloud+=*transformed; ++count; } } else { negativePoses.push_back(*iter); } } if(assembledCloud->size()) { if(cloudFrustumCulling_ && negativePoses.size()) { for(std::list >::reverse_iterator iter=negativePoses.rbegin(); iter!=negativePoses.rend(); ++iter) { std::map::Ptr >::iterator jter = clouds_.find(iter->first); std::map >::iterator kter = cameraModels_.find(iter->first); if(jter != clouds_.end() && kter != cameraModels_.end()) { for(unsigned int i=0; isecond.size(); ++i) { if(kter->second[i].isValidForProjection()) { int size = assembledCloud->size(); assembledCloud = util3d::frustumFiltering( assembledCloud, iter->second, // FIXME: should include camera local transform kter->second[i].horizontalFOV(), kter->second[i].verticalFOV(), 0.0f, cloudMaxDepth_>0.0?cloudMaxDepth_:999999., true); //ROS_INFO("Frustum culling %d ->%d", size, (int)assembledCloud->size()); if(jter->second->size()) { pcl::PointCloud::Ptr transformed = util3d::transformPointCloud(jter->second, iter->second); *assembledCloud+=*transformed; } } } } } } if(assembledCloud->size() && (cloudFloorCullingHeight_ > 0.0 || cloudCeilingCullingHeight_ > 0.0)) { assembledCloud = util3d::passThrough(assembledCloud, "z", cloudFloorCullingHeight_>0.0?cloudFloorCullingHeight_:-999.0, cloudCeilingCullingHeight_>0.0 && (cloudFloorCullingHeight_<=0.0 || cloudCeilingCullingHeight_>cloudFloorCullingHeight_)?cloudCeilingCullingHeight_:999.0); } if(assembledCloud->size() && cloudVoxelSize_ > 0 && cloudOutputVoxelized_) { assembledCloud = util3d::voxelize(assembledCloud, cloudVoxelSize_); } ROS_INFO("Assembled %d clouds (%fs)", count, time.ticks()); sensor_msgs::PointCloud2::Ptr cloudMsg(new sensor_msgs::PointCloud2); pcl::toROSMsg(*assembledCloud, *cloudMsg); cloudMsg->header.stamp = stamp; cloudMsg->header.frame_id = mapFrameId; cloudMapPub_.publish(cloudMsg); } else if(poses.size()) { ROS_WARN("Cloud map is empty! (poses=%d clouds=%d)", (int)poses.size(), (int)clouds_.size()); } } else if(mapCacheCleanup_) { clouds_.clear(); cameraModels_.clear(); } if(scanMapPub_.getNumSubscribers()) { // generate the assembled scan cloud! UTimer time; pcl::PointCloud::Ptr assembledCloud(new pcl::PointCloud); int count = 0; std::list > negativePoses; for(std::map::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter) { if(iter->first > 0) { std::map::Ptr >::iterator jter = scans_.find(iter->first); if(jter != scans_.end() && jter->second->size()) { pcl::PointCloud::Ptr transformed = util3d::transformPointCloud(jter->second, iter->second); *assembledCloud+=*transformed; ++count; } } // negative poses are not used } if(assembledCloud->size()) { if(assembledCloud->size() && scanVoxelSize_ > 0 && scanOutputVoxelized_) { assembledCloud = util3d::voxelize(assembledCloud, scanVoxelSize_); } ROS_INFO("Assembled %d scans (%fs)", count, time.ticks()); sensor_msgs::PointCloud2::Ptr cloudMsg(new sensor_msgs::PointCloud2); pcl::toROSMsg(*assembledCloud, *cloudMsg); cloudMsg->header.stamp = stamp; cloudMsg->header.frame_id = mapFrameId; scanMapPub_.publish(cloudMsg); } else if(poses.size()) { ROS_WARN("Scan map is empty! (poses=%d, scans=%d)", (int)poses.size(), (int)scans_.size()); } } else if(mapCacheCleanup_) { scans_.clear(); } if(projMapPub_.getNumSubscribers()) { // create the projection map float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f; cv::Mat pixels = this->generateProjMap(poses, xMin, yMin, gridCellSize); if(!pixels.empty()) { //init nav_msgs::OccupancyGrid map; map.info.resolution = gridCellSize; map.info.origin.position.x = 0.0; map.info.origin.position.y = 0.0; map.info.origin.position.z = 0.0; map.info.origin.orientation.x = 0.0; map.info.origin.orientation.y = 0.0; map.info.origin.orientation.z = 0.0; map.info.origin.orientation.w = 1.0; map.info.width = pixels.cols; map.info.height = pixels.rows; map.info.origin.position.x = xMin; map.info.origin.position.y = yMin; map.data.resize(map.info.width * map.info.height); memcpy(map.data.data(), pixels.data, map.info.width * map.info.height); map.header.frame_id = mapFrameId; map.header.stamp = stamp; projMapPub_.publish(map); } else if(poses.size()) { ROS_WARN("Projection map is empty! (proj maps=%d)", (int)projMaps_.size()); } } else if(mapCacheCleanup_) { projMaps_.clear(); } if(gridMapPub_.getNumSubscribers()) { // create the grid map float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f; cv::Mat pixels = this->generateGridMap(poses, xMin, yMin, gridCellSize); if(!pixels.empty()) { //init nav_msgs::OccupancyGrid map; map.info.resolution = gridCellSize; map.info.origin.position.x = 0.0; map.info.origin.position.y = 0.0; map.info.origin.position.z = 0.0; map.info.origin.orientation.x = 0.0; map.info.origin.orientation.y = 0.0; map.info.origin.orientation.z = 0.0; map.info.origin.orientation.w = 1.0; map.info.width = pixels.cols; map.info.height = pixels.rows; map.info.origin.position.x = xMin; map.info.origin.position.y = yMin; map.data.resize(map.info.width * map.info.height); memcpy(map.data.data(), pixels.data, map.info.width * map.info.height); map.header.frame_id = mapFrameId; map.header.stamp = stamp; gridMapPub_.publish(map); } else if(poses.size()) { ROS_WARN("Grid map is empty! (local maps=%d)", (int)gridMaps_.size()); } } else if(mapCacheCleanup_) { gridMaps_.clear(); } } cv::Mat MapsManager::generateProjMap( const std::map & poses, float & xMin, float & yMin, float & gridCellSize) { gridCellSize = gridCellSize_; return util3d::create2DMapFromOccupancyLocalMaps( poses, projMaps_, gridCellSize_, xMin, yMin, gridSize_, gridEroded_); } cv::Mat MapsManager::generateGridMap( const std::map & poses, float & xMin, float & yMin, float & gridCellSize) { gridCellSize = gridCellSize_; cv::Mat map = util3d::create2DMapFromOccupancyLocalMaps( poses, gridMaps_, gridCellSize_, xMin, yMin, gridSize_, gridEroded_); return map; } #ifdef WITH_OCTOMAP // returned OcTree must be deleted // RTAB-Map optimizes the graph at almost each iteration, an octomap cannot // be updated online. Only available on service. To have an "online" octomap published as a topic, // you may want to subscribe an octomap_server to /rtabmap/cloud topic. // octomap::OcTree * MapsManager::createOctomap(const std::map & poses) { octomap::OcTree * octree = new octomap::OcTree(gridCellSize_); UTimer time; for(std::map::const_iterator posesIter = poses.begin(); posesIter!=poses.end(); ++posesIter) { std::map::Ptr >::iterator cloudsIter = clouds_.find(posesIter->first); if(cloudsIter != clouds_.end() && cloudsIter->second->size()) { octomap::Pointcloud * scan = new octomap::Pointcloud(); //octomap::pointcloudPCLToOctomap(*cloudsIter->second, *scan); // Not anymore in Indigo! scan->reserve(cloudsIter->second->size()); for(pcl::PointCloud::const_iterator it = cloudsIter->second->begin(); it != cloudsIter->second->end(); ++it) { // Check if the point is invalid if(pcl::isFinite(*it)) { scan->push_back(it->x, it->y, it->z); } } float x,y,z, r,p,w; posesIter->second.getTranslationAndEulerAngles(x,y,z,r,p,w); octomap::ScanNode node(scan, octomap::pose6d(x,y,z, r,p,w), posesIter->first); octree->insertPointCloud(node, cloudMaxDepth_, true, true); ROS_INFO("inserted %d pt=%d (%fs)", posesIter->first, (int)scan->size(), time.ticks()); } } octree->updateInnerOccupancy(); ROS_INFO("updated inner occupancy (%fs)", time.ticks()); // clear memory if no one subscribed if(mapCacheCleanup_ && cloudMapPub_.getNumSubscribers() == 0) { clouds_.clear(); cameraModels_.clear(); } return octree; } #endif