/* Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Universite de Sherbrooke nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include namespace rtabmap { ////////////////////////////////////// // RtabmapColorOcTree ////////////////////////////////////// RtabmapColorOcTreeNode* RtabmapColorOcTreeNode::getChild(unsigned int i) { #ifdef OCTOMAP_PRE_18 return static_cast (OcTreeNode::getChild(i)); #else UFATAL("This function should not be used with octomap >= 1.8"); return 0; #endif } const RtabmapColorOcTreeNode* RtabmapColorOcTreeNode::getChild(unsigned int i) const { #ifdef OCTOMAP_PRE_18 return static_cast (OcTreeNode::getChild(i)); #else UFATAL("This function should not be used with octomap >= 1.8"); return 0; #endif } //octomap <1.8 bool RtabmapColorOcTreeNode::pruneNode() { #ifdef OCTOMAP_PRE_18 // checks for equal occupancy only, color ignored if (!this->collapsible()) return false; // set occupancy value setLogOdds(getChild(0)->getLogOdds()); // set color to average color if (isColorSet()) color = getAverageChildColor(); // delete children for (unsigned int i=0;i<8;i++) { delete children[i]; } delete[] children; children = NULL; return true; #else UFATAL("This function should not be used with octomap >= 1.8"); return false; #endif } //octomap <1.8 void RtabmapColorOcTreeNode::expandNode() { #ifdef OCTOMAP_PRE_18 assert(!hasChildren()); for (unsigned int k=0; k<8; k++) { createChild(k); children[k]->setValue(value); getChild(k)->setColor(color); } #else UFATAL("This function should not be used with octomap >= 1.8"); #endif } //octomap <1.8 bool RtabmapColorOcTreeNode::createChild(unsigned int i) { #ifdef OCTOMAP_PRE_18 if (children == NULL) allocChildren(); children[i] = new RtabmapColorOcTreeNode(); return true; #else UFATAL("This function should not be used with octomap >= 1.8"); return false; #endif } void RtabmapColorOcTreeNode::updateOccupancyTypeChildren() { if (children != NULL){ int type = kTypeUnknown; for (int i=0; i<8 && type != kTypeObstacle; i++) { RtabmapColorOcTreeNode* child = static_cast(children[i]); if (child != NULL && child->getOccupancyType() >= kTypeEmpty) { if(type == kTypeUnknown) { type = child->getOccupancyType(); } } } type_ = type; } } RtabmapColorOcTree::RtabmapColorOcTree(double resolution) : OccupancyOcTreeBase(resolution) { RtabmapColorOcTreeMemberInit.ensureLinking(); }; RtabmapColorOcTreeNode* RtabmapColorOcTree::setNodeColor(const octomap::OcTreeKey& key, uint8_t r, uint8_t g, uint8_t b) { RtabmapColorOcTreeNode* n = search (key); if (n != 0) { n->setColor(r, g, b); } return n; } bool RtabmapColorOcTree::pruneNode(RtabmapColorOcTreeNode* node) { #ifndef OCTOMAP_PRE_18 if (!isNodeCollapsible(node)) return false; // set value to children's values (all assumed equal) node->copyData(*(getNodeChild(node, 0))); if (node->isColorSet()) // TODO check node->setColor(node->getAverageChildColor()); // delete children for (unsigned int i=0;i<8;i++) { deleteNodeChild(node, i); } delete[] node->children; node->children = NULL; return true; #else UFATAL("This function should not be used with octomap < 1.8"); return false; #endif } bool RtabmapColorOcTree::isNodeCollapsible(const RtabmapColorOcTreeNode* node) const{ #ifndef OCTOMAP_PRE_18 // all children must exist, must not have children of // their own and have the same occupancy probability if (!nodeChildExists(node, 0)) return false; const RtabmapColorOcTreeNode* firstChild = getNodeChild(node, 0); if (nodeHasChildren(firstChild)) return false; for (unsigned int i = 1; i<8; i++) { // compare nodes only using their occupancy, ignoring color for pruning if (!nodeChildExists(node, i) || nodeHasChildren(getNodeChild(node, i)) || !(getNodeChild(node, i)->getValue() == firstChild->getValue())) return false; } return true; #else UFATAL("This function should not be used with octomap < 1.8"); return false; #endif } RtabmapColorOcTreeNode* RtabmapColorOcTree::averageNodeColor(const octomap::OcTreeKey& key, uint8_t r, uint8_t g, uint8_t b) { RtabmapColorOcTreeNode* n = search(key); if (n != 0) { if (n->isColorSet()) { RtabmapColorOcTreeNode::Color prev_color = n->getColor(); n->setColor((prev_color.r + r)/2, (prev_color.g + g)/2, (prev_color.b + b)/2); } else { n->setColor(r, g, b); } } return n; } RtabmapColorOcTreeNode* RtabmapColorOcTree::integrateNodeColor(const octomap::OcTreeKey& key, uint8_t r, uint8_t g, uint8_t b) { RtabmapColorOcTreeNode* n = search (key); if (n != 0) { if (n->isColorSet()) { RtabmapColorOcTreeNode::Color prev_color = n->getColor(); double node_prob = n->getOccupancy(); uint8_t new_r = (uint8_t) ((double) prev_color.r * node_prob + (double) r * (0.99-node_prob)); uint8_t new_g = (uint8_t) ((double) prev_color.g * node_prob + (double) g * (0.99-node_prob)); uint8_t new_b = (uint8_t) ((double) prev_color.b * node_prob + (double) b * (0.99-node_prob)); n->setColor(new_r, new_g, new_b); } else { n->setColor(r, g, b); } } return n; } void RtabmapColorOcTree::updateInnerOccupancy() { this->updateInnerOccupancyRecurs(this->root, 0); } void RtabmapColorOcTree::updateInnerOccupancyRecurs(RtabmapColorOcTreeNode* node, unsigned int depth) { #ifndef OCTOMAP_PRE_18 // only recurse and update for inner nodes: if (nodeHasChildren(node)){ // return early for last level: if (depth < this->tree_depth){ for (unsigned int i=0; i<8; i++) { if (nodeChildExists(node, i)) { updateInnerOccupancyRecurs(getNodeChild(node, i), depth+1); } } } node->updateOccupancyChildren(); node->updateColorChildren(); node->updateOccupancyTypeChildren(); } #else // only recurse and update for inner nodes: if (node->hasChildren()){ // return early for last level: if (depth < this->tree_depth){ for (unsigned int i=0; i<8; i++) { if (node->childExists(i)) { updateInnerOccupancyRecurs(node->getChild(i), depth+1); } } } node->updateOccupancyChildren(); node->updateColorChildren(); node->updateOccupancyTypeChildren(); } #endif } RtabmapColorOcTree::StaticMemberInitializer::StaticMemberInitializer() { RtabmapColorOcTree* tree = new RtabmapColorOcTree(0.1); #ifndef OCTOMAP_PRE_18 tree->clearKeyRays(); #endif AbstractOcTree::registerTreeType(tree); } #ifndef _WIN32 // On Windows, the app freezes on start if the following is defined RtabmapColorOcTree::StaticMemberInitializer RtabmapColorOcTree::RtabmapColorOcTreeMemberInit; #endif ////////////////////////////////////// // OctoMap ////////////////////////////////////// OctoMap::OctoMap(const LocalGridCache * cache, const ParametersMap & parameters) : GlobalMap(cache, parameters), hasColor_(false), rangeMax_(Parameters::defaultGridRangeMax()), rayTracing_(Parameters::defaultGridRayTracing()), emptyFloodFillDepth_(Parameters::defaultGridGlobalFloodFillDepth()) { octree_ = new RtabmapColorOcTree(cellSize_); if(occupancyThr_ <= 0.0f) { UWARN("Cannot set %s to null for OctoMap, using default value %f instead.", Parameters::kGridGlobalOccupancyThr().c_str(), Parameters::defaultGridGlobalOccupancyThr()); occupancyThr_ = Parameters::defaultGridGlobalOccupancyThr(); } UDEBUG("occupancyThr_=%f", occupancyThr_); UDEBUG("probHit_=%f", probability(logOddsHit_)); UDEBUG("probMiss_=%f", probability(logOddsMiss_)); UDEBUG("probClampingMin_=%f", probability(logOddsClampingMin_)); UDEBUG("probClampingMax_=%f", probability(logOddsClampingMax_)); octree_->setOccupancyThres(occupancyThr_); octree_->setProbHit(probability(logOddsHit_)); octree_->setProbMiss(probability(logOddsMiss_)); octree_->setClampingThresMin(probability(logOddsClampingMin_)); octree_->setClampingThresMax(probability(logOddsClampingMax_)); Parameters::parse(parameters, Parameters::kGridRangeMax(), rangeMax_); Parameters::parse(parameters, Parameters::kGridRayTracing(), rayTracing_); Parameters::parse(parameters, Parameters::kGridGlobalFloodFillDepth(), emptyFloodFillDepth_); UASSERT(emptyFloodFillDepth_<=16); UDEBUG("rangeMax_ =%f", rangeMax_); UDEBUG("rayTracing_ =%s", rayTracing_?"true":"false"); UDEBUG("emptyFloodFillDepth_=%d", emptyFloodFillDepth_); } OctoMap::~OctoMap() { this->clear(); delete octree_; } void OctoMap::clear() { octree_->clear(); hasColor_ = false; GlobalMap::clear(); } unsigned long OctoMap::getMemoryUsed() const { unsigned long memoryUsage = GlobalMap::getMemoryUsed(); // Note: size of OctoMap object is missing. return memoryUsage; } bool OctoMap::isValidEmpty(RtabmapColorOcTree* octree_, unsigned int treeDepth,octomap::point3d startPosition) { auto nodePtr = octree_->search(startPosition.x(), startPosition.y(), startPosition.z(), treeDepth); if(nodePtr != NULL) { if(!octree_->isNodeOccupied(*nodePtr)) { return true; } } return false; } octomap::point3d OctoMap::findCloseEmpty(RtabmapColorOcTree* octree_, unsigned int treeDepth,octomap::point3d startPosition) { //try current position if(isValidEmpty(octree_,treeDepth,startPosition)) { return startPosition; } //x pos if(isValidEmpty(octree_,treeDepth,octomap::point3d(startPosition.x()+octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z()))) { return octomap::point3d(startPosition.x()+octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z()); } //x neg if(isValidEmpty(octree_,treeDepth,octomap::point3d(startPosition.x()-octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z()))) { return octomap::point3d(startPosition.x()-octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z()); } //y pos if(isValidEmpty(octree_,treeDepth,octomap::point3d(startPosition.x(), startPosition.y()+octree_->getNodeSize(treeDepth), startPosition.z()))) { return octomap::point3d(startPosition.x(), startPosition.y()+octree_->getNodeSize(treeDepth), startPosition.z()); } //y neg if(isValidEmpty(octree_,treeDepth,octomap::point3d(startPosition.x()-octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z()))) { return octomap::point3d(startPosition.x(), startPosition.y()-octree_->getNodeSize(treeDepth), startPosition.z()); } //z pos if(isValidEmpty(octree_,treeDepth,octomap::point3d(startPosition.x(), startPosition.y(), startPosition.z()+octree_->getNodeSize(treeDepth)))) { return octomap::point3d(startPosition.x(), startPosition.y(), startPosition.z()+octree_->getNodeSize(treeDepth)); } //z neg if(isValidEmpty(octree_,treeDepth,octomap::point3d(startPosition.x(), startPosition.y(), startPosition.z()-octree_->getNodeSize(treeDepth)))) { return octomap::point3d(startPosition.x(), startPosition.y(), startPosition.z()-octree_->getNodeSize(treeDepth)); } //no valid position return startPosition; } bool OctoMap::isNodeVisited(std::unordered_set const & EmptyNodes,octomap::OcTreeKey const key) { for(auto it = EmptyNodes.find(key);it != EmptyNodes.end();it++) { if(*it == key) { return true; } } return false; } void OctoMap::floodFill(RtabmapColorOcTree* octree_, unsigned int treeDepth,octomap::point3d startPosition, std::unordered_set & EmptyNodes,std::queue& positionToExplore) { auto key = octree_->coordToKey(startPosition,treeDepth); if(!isNodeVisited(EmptyNodes,key)) { if(isValidEmpty(octree_,treeDepth,startPosition)) { EmptyNodes.insert(key); positionToExplore.push(octomap::point3d(startPosition.x()+octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z())); positionToExplore.push(octomap::point3d(startPosition.x()-octree_->getNodeSize(treeDepth), startPosition.y(), startPosition.z())); positionToExplore.push(octomap::point3d(startPosition.x(), startPosition.y()+octree_->getNodeSize(treeDepth), startPosition.z())); positionToExplore.push(octomap::point3d(startPosition.x(), startPosition.y()-octree_->getNodeSize(treeDepth), startPosition.z())); positionToExplore.push(octomap::point3d(startPosition.x(), startPosition.y(), startPosition.z()+octree_->getNodeSize(treeDepth))); positionToExplore.push(octomap::point3d(startPosition.x(), startPosition.y(), startPosition.z()-octree_->getNodeSize(treeDepth))); } } } std::unordered_set OctoMap::findEmptyNode(RtabmapColorOcTree* octree_, unsigned int treeDepth, octomap::point3d startPosition) { std::unordered_set exploreNode; std::queue positionToExplore; startPosition = findCloseEmpty(octree_,treeDepth, startPosition); floodFill(octree_, treeDepth, startPosition, exploreNode, positionToExplore); while(!positionToExplore.empty()) { floodFill(octree_, treeDepth, positionToExplore.front(), exploreNode, positionToExplore); positionToExplore.pop(); } return exploreNode; } void OctoMap::assemble(const std::list > & newPoses) { // Original version from A. Hornung: // https://github.com/OctoMap/octomap_mapping/blob/jade-devel/octomap_server/src/OctomapServer.cpp#L356 // int lastId = assembledNodes().size()?assembledNodes().rbegin()->first:0; UDEBUG("Last id = %d", lastId); UDEBUG("newPoses = %d", (int)newPoses.size()); if(!newPoses.empty()) { float rangeMaxSqrd = rangeMax_*rangeMax_; float cellSize = octree_->getResolution(); int not3DCount = 0; int not3DFirstId = 0; int not3DGroundType = 0; int not3DObstaclesType = 0; int not3DEmptyType = 0; for(std::list >::const_iterator iter=newPoses.begin(); iter!=newPoses.end(); ++iter) { std::map::const_iterator localGridIter; localGridIter = cache().find(iter->first); if(localGridIter != cache().end()) { cv::Mat ground = localGridIter->second.groundCells; cv::Mat obstacles = localGridIter->second.obstacleCells; cv::Mat emptyCells = localGridIter->second.emptyCells; if(!localGridIter->second.is3D()) { if(++not3DCount == 1) { not3DFirstId = iter->first; not3DGroundType = ground.type(); not3DObstaclesType = obstacles.type(); not3DEmptyType = emptyCells.type(); } continue; } UDEBUG("Adding %d to octomap (resolution=%f)", iter->first, octree_->getResolution()); octomap::point3d sensorOrigin(iter->second.x(), iter->second.y(), iter->second.z()); sensorOrigin += octomap::point3d(localGridIter->second.viewPoint.x, localGridIter->second.viewPoint.y, localGridIter->second.viewPoint.z); updateMinMax(sensorOrigin); octomap::OcTreeKey tmpKey; if (!octree_->coordToKeyChecked(sensorOrigin, tmpKey)) { UERROR("Could not generate Key for origin (%f,%f,%f)", sensorOrigin.x(), sensorOrigin.y(), sensorOrigin.z()); } bool computeRays = rayTracing_ && emptyCells.empty(); // instead of direct scan insertion, compute update to filter ground: octomap::KeySet free_cells; // insert ground points only as free: unsigned int maxGroundPts = ground.cols; UDEBUG("%d: compute free cells (from %d ground points)", iter->first, (int)maxGroundPts); Eigen::Affine3f t = iter->second.toEigen3f(); LaserScan tmpGround = LaserScan::backwardCompatibility(ground); UASSERT(tmpGround.size() == (int)maxGroundPts); for (unsigned int i=0; i 0.0f) { octomap::point3d v(pt.x - cellSize - sensorOrigin.x(), pt.y - cellSize - sensorOrigin.y(), pt.z - cellSize - sensorOrigin.z()); if(v.norm_sq() > rangeMaxSqrd) { // compute new point to max range v.normalize(); v*=rangeMax_; point = sensorOrigin + v; ignoreOccupiedCell=true; } } if(!ignoreOccupiedCell) { // occupied endpoint octomap::OcTreeKey key; if (octree_->coordToKeyChecked(point, key)) { if(iter->first >0 && iter->firstsearch(key); if(n && n->getNodeRefId() > 0 && n->getNodeRefId() > iter->first) { // The cell has been updated from more recent node, don't update the cell continue; } } updateMinMax(point); RtabmapColorOcTreeNode * n = octree_->updateNode(key, true); if(n) { if(!hasColor_ && !(pt.r ==0 && pt.g == 0 && pt.b == 0) && !(pt.r ==255 && pt.g == 255 && pt.b == 255)) { hasColor_ = true; } octree_->averageNodeColor(key, pt.r, pt.g, pt.b); if(iter->first > 0) { n->setNodeRefId(iter->first); n->setPointRef(point); } n->setOccupancyType(RtabmapColorOcTreeNode::kTypeGround); } } } // only clear space (ground points) octomap::KeyRay keyRay; if (computeRays && (iter->first < 0 || iter->first>lastId) && octree_->computeRayKeys(sensorOrigin, point, keyRay)) { free_cells.insert(keyRay.begin(), keyRay.end()); } } UDEBUG("%d: ground cells=%d free cells=%d", iter->first, (int)maxGroundPts, (int)free_cells.size()); // all other points: free on ray, occupied on endpoint: unsigned int maxObstaclePts = obstacles.cols; UDEBUG("%d: compute occupied cells (from %d obstacle points)", iter->first, (int)maxObstaclePts); LaserScan tmpObstacle = LaserScan::backwardCompatibility(obstacles); UASSERT(tmpObstacle.size() == (int)maxObstaclePts); for (unsigned int i=0; i 0.0f) { octomap::point3d v(pt.x - cellSize - sensorOrigin.x(), pt.y - cellSize - sensorOrigin.y(), pt.z - cellSize - sensorOrigin.z()); if(v.norm_sq() > rangeMaxSqrd) { // compute new point to max range v.normalize(); v*=rangeMax_; point = sensorOrigin + v; ignoreOccupiedCell=true; } } if(!ignoreOccupiedCell) { // occupied endpoint octomap::OcTreeKey key; if (octree_->coordToKeyChecked(point, key)) { if(iter->first >0 && iter->firstsearch(key); if(n && n->getNodeRefId() > 0 && n->getNodeRefId() > iter->first) { // The cell has been updated from more recent node, don't update the cell continue; } } updateMinMax(point); RtabmapColorOcTreeNode * n = octree_->updateNode(key, true); if(n) { if(!hasColor_ && !(pt.r ==0 && pt.g == 0 && pt.b == 0) && !(pt.r ==255 && pt.g == 255 && pt.b == 255)) { hasColor_ = true; } octree_->averageNodeColor(key, pt.r, pt.g, pt.b); if(iter->first > 0) { n->setNodeRefId(iter->first); n->setPointRef(point); } n->setOccupancyType(RtabmapColorOcTreeNode::kTypeObstacle); } } } // free cells octomap::KeyRay keyRay; if (computeRays && (iter->first < 0 || iter->first>lastId) && octree_->computeRayKeys(sensorOrigin, point, keyRay)) { free_cells.insert(keyRay.begin(), keyRay.end()); } } UDEBUG("%d: occupied cells=%d free cells=%d", iter->first, (int)maxObstaclePts, (int)free_cells.size()); // mark free cells only if not seen occupied in this cloud for(octomap::KeySet::iterator it = free_cells.begin(), end=free_cells.end(); it!= end; ++it) { if(iter->first > 0) { RtabmapColorOcTreeNode * n = octree_->search(*it); if(n && n->getNodeRefId() > 0 && n->getNodeRefId() >= iter->first) { // The cell has been updated from current node or more recent node, don't update the cell continue; } } RtabmapColorOcTreeNode * n = octree_->updateNode(*it, false, true); if(n && n->getOccupancyType() == RtabmapColorOcTreeNode::kTypeUnknown) { n->setOccupancyType(RtabmapColorOcTreeNode::kTypeEmpty); if(iter->first > 0) { n->setNodeRefId(iter->first); } } } // all empty cells if(emptyCells.cols) { unsigned int maxEmptyPts = emptyCells.cols; UDEBUG("%d: compute free cells (from %d empty points)", iter->first, (int)maxEmptyPts); LaserScan tmpEmpty = LaserScan::backwardCompatibility(emptyCells); UASSERT(tmpEmpty.size() == (int)maxEmptyPts); for (unsigned int i=0; i 0.0f) { octomap::point3d v(pt.x - sensorOrigin.x(), pt.y - sensorOrigin.y(), pt.z - sensorOrigin.z()); if(v.norm_sq() > rangeMaxSqrd) { ignoreCell=true; } } if(!ignoreCell) { octomap::OcTreeKey key; if (octree_->coordToKeyChecked(point, key)) { if(iter->first >0) { RtabmapColorOcTreeNode * n = octree_->search(key); if(n && n->getNodeRefId() > 0 && n->getNodeRefId() >= iter->first) { // The cell has been updated from current node or more recent node, don't update the cell continue; } } updateMinMax(point); RtabmapColorOcTreeNode * n = octree_->updateNode(key, false, true); if(n && n->getOccupancyType() == RtabmapColorOcTreeNode::kTypeUnknown) { n->setOccupancyType(RtabmapColorOcTreeNode::kTypeEmpty); if(iter->first > 0) { n->setNodeRefId(iter->first); } } } } } } if(emptyCells.cols || !free_cells.empty()) { octree_->updateInnerOccupancy(); } // compress map //if(newPoses.size() > 1) //{ // octree_->prune(); //} addAssembledNode(iter->first, iter->second); UDEBUG("%d: end", iter->first); } else { UDEBUG("Did not find %d in cache", iter->first); } } if(not3DCount) { UWARN("It seems the local occupancy grids are not 3d, cannot update OctoMap! " "(%d local grid(s) ignored, first one (id=%d) had ground type=%d, obstacles type=%d, empty type=%d)", not3DCount, not3DFirstId, not3DGroundType, not3DObstaclesType, not3DEmptyType); } } if(emptyFloodFillDepth_>0) { UTimer t; auto key = octree_->coordToKey(0, 0, 0, emptyFloodFillDepth_); auto pos = octree_->keyToCoord(key); std::unordered_set EmptyNodes = findEmptyNode(octree_,emptyFloodFillDepth_, pos); std::vector nodeToDelete; for (RtabmapColorOcTree::iterator it = octree_->begin_leafs(emptyFloodFillDepth_); it != octree_->end_leafs(); ++it) { if(!octree_->isNodeOccupied(*it)) { if(!isNodeVisited(EmptyNodes,it.getKey())) { nodeToDelete.push_back(it.getKey()); } } } for(unsigned int y=0; y < nodeToDelete.size(); y++) { octree_->deleteNode(nodeToDelete[y],emptyFloodFillDepth_); } UDEBUG("Flood Fill: deleted %d empty cells (%fs)", (int)nodeToDelete.size(), t.ticks()); } } void OctoMap::updateMinMax(const octomap::point3d & point) { if(point.x() < minValues_[0]) { minValues_[0] = point.x(); } if(point.y() < minValues_[1]) { minValues_[1] = point.y(); } if(point.z() < minValues_[2]) { minValues_[2] = point.z(); } if(point.x() > maxValues_[0]) { maxValues_[0] = point.x(); } if(point.y() > maxValues_[1]) { maxValues_[1] = point.y(); } if(point.z() > maxValues_[2]) { maxValues_[2] = point.z(); } } pcl::PointCloud::Ptr OctoMap::createCloud( unsigned int treeDepth, std::vector * obstacleIndices, std::vector * emptyIndices, std::vector * groundIndices, bool originalRefPoints, std::vector * frontierIndices, std::vector * cloudProb) const { UASSERT(treeDepth <= octree_->getTreeDepth()); pcl::PointCloud::Ptr cloud(new pcl::PointCloud); if(cloudProb) { cloudProb->resize(octree_->size()); } UDEBUG("depth=%d (maxDepth=%d) octree = %d", (int)treeDepth, (int)octree_->getTreeDepth(), (int)octree_->size()); cloud->resize(octree_->size()); if(obstacleIndices) { obstacleIndices->resize(octree_->size()); } if(emptyIndices) { emptyIndices->resize(octree_->size()); } if(frontierIndices) { frontierIndices->resize(octree_->size()); } if(groundIndices) { groundIndices->resize(octree_->size()); } if(treeDepth == 0) { treeDepth = octree_->getTreeDepth(); } double minZ = minValues_[2]; double maxZ = maxValues_[2]; bool addAllPoints = obstacleIndices == 0 && groundIndices == 0 && emptyIndices == 0; int oi=0; int si=0; int ei=0; int fi=0; int gi=0; float halfCellSize = octree_->getNodeSize(treeDepth)/2.0f; for (RtabmapColorOcTree::iterator it = octree_->begin(treeDepth); it != octree_->end(); ++it) { if(octree_->isNodeOccupied(*it) && (obstacleIndices != 0 || groundIndices != 0 || addAllPoints)) { octomap::point3d pt = octree_->keyToCoord(it.getKey()); if(cloudProb) { (*cloudProb)[oi] = it->getOccupancy(); } if(octree_->getTreeDepth() == it.getDepth() && hasColor_) { (*cloud)[oi] = pcl::PointXYZRGB(it->getColor().r, it->getColor().g, it->getColor().b); } else { // Gradiant color on z axis float H = (maxZ - pt.z())*299.0f/(maxZ-minZ); float r,g,b; util2d::HSVtoRGB(&r, &g, &b, H, 1, 1); (*cloud)[oi].r = r*255.0f; (*cloud)[oi].g = g*255.0f; (*cloud)[oi].b = b*255.0f; } if(originalRefPoints && it->getOccupancyType() > 0) { const octomap::point3d & p = it->getPointRef(); (*cloud)[oi].x = p.x(); (*cloud)[oi].y = p.y(); (*cloud)[oi].z = p.z(); } else { (*cloud)[oi].x = pt.x()-halfCellSize; (*cloud)[oi].y = pt.y()-halfCellSize; (*cloud)[oi].z = pt.z(); } if(it->getOccupancyType() == RtabmapColorOcTreeNode::kTypeGround) { if(groundIndices) { groundIndices->at(gi++) = oi; } } else if(obstacleIndices) { obstacleIndices->at(si++) = oi; } ++oi; } else if(!octree_->isNodeOccupied(*it) && (emptyIndices != 0 || addAllPoints || frontierIndices !=0)) { octomap::point3d pt = octree_->keyToCoord(it.getKey()); if(cloudProb) { (*cloudProb)[oi] = it->getOccupancy(); } if(frontierIndices !=0 && (!octree_->search( pt.x()+octree_->getNodeSize(treeDepth), pt.y(), pt.z(), treeDepth) || !octree_->search( pt.x()-octree_->getNodeSize(treeDepth), pt.y(), pt.z(), treeDepth) || !octree_->search( pt.x(), pt.y()+octree_->getNodeSize(treeDepth), pt.z(), treeDepth) || !octree_->search( pt.x(), pt.y()-octree_->getNodeSize(treeDepth), pt.z(), treeDepth) || !octree_->search( pt.x(), pt.y(), pt.z()+octree_->getNodeSize(treeDepth), treeDepth) || !octree_->search( pt.x(), pt.y(), pt.z()-octree_->getNodeSize(treeDepth), treeDepth) )) //ajouter 1 au key ? { //unknown neighbor FACE cell frontierIndices->at(fi++) = oi; } (*cloud)[oi] = pcl::PointXYZRGB(it->getColor().r, it->getColor().g, it->getColor().b); (*cloud)[oi].x = pt.x()-halfCellSize; (*cloud)[oi].y = pt.y()-halfCellSize; (*cloud)[oi].z = pt.z(); if(emptyIndices) { emptyIndices->at(ei++) = oi; } ++oi; } } cloud->resize(oi); if(cloudProb) { cloudProb->resize(oi); } if(obstacleIndices) { obstacleIndices->resize(si); UDEBUG("obstacle=%d", si); } if(emptyIndices) { emptyIndices->resize(ei); UDEBUG("empty=%d", ei); } if(frontierIndices) { frontierIndices->resize(fi); UDEBUG("frontier=%d", fi); } if(groundIndices) { groundIndices->resize(gi); UDEBUG("ground=%d", gi); } UDEBUG(""); return cloud; } cv::Mat OctoMap::createProjectionMap(float & xMin, float & yMin, float & gridCellSize, float minGridSize, unsigned int treeDepth) { UDEBUG("minGridSize=%f, treeDepth=%d", minGridSize, (int)treeDepth); UASSERT(treeDepth <= octree_->getTreeDepth()); if(treeDepth == 0) { treeDepth = octree_->getTreeDepth(); } gridCellSize = octree_->getNodeSize(treeDepth); cv::Mat obstaclesMat = cv::Mat(1, (int)octree_->size(), CV_32FC2); cv::Mat groundMat = cv::Mat(1, (int)octree_->size(), CV_32FC2); int gi=0; int oi=0; cv::Vec2f * oPtr = obstaclesMat.ptr(0,0); cv::Vec2f * gPtr = groundMat.ptr(0,0); float halfCellSize = octree_->getNodeSize(treeDepth)/2.0f; for (RtabmapColorOcTree::iterator it = octree_->begin(treeDepth); it != octree_->end(); ++it) { octomap::point3d pt = octree_->keyToCoord(it.getKey()); if(octree_->isNodeOccupied(*it) && it->getOccupancyType() == RtabmapColorOcTreeNode::kTypeObstacle) { // projected on ground oPtr[oi][0] = pt.x()-halfCellSize; oPtr[oi][1] = pt.y()-halfCellSize; ++oi; } else { // projected on ground gPtr[gi][0] = pt.x()-halfCellSize; gPtr[gi][1] = pt.y()-halfCellSize; ++gi; } } obstaclesMat = obstaclesMat(cv::Range::all(), cv::Range(0, oi)); groundMat = groundMat(cv::Range::all(), cv::Range(0, gi)); std::map poses; poses.insert(std::make_pair(1, Transform::getIdentity())); std::map > maps; maps.insert(std::make_pair(1, std::make_pair(groundMat, obstaclesMat))); cv::Mat map = util3d::create2DMapFromOccupancyLocalMaps( poses, maps, gridCellSize, xMin, yMin, minGridSize, false); UDEBUG(""); return map; } bool OctoMap::writeBinary(const std::string & path) { return octree_->writeBinary(path); } } /* namespace rtabmap */