explore_frontier.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2008, Robert Bosch LLC.
00006  *  Copyright (c) 2015-2016, Jiri Horner.
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of the Jiri Horner nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  *********************************************************************/
00037 
00038 #include <explore/explore_frontier.h>
00039 
00040 #include <exception>
00041 #include <mutex>
00042 #include <boost/thread.hpp>
00043 
00044 namespace explore {
00045 
00046 ExploreFrontier::ExploreFrontier(Costmap2DClient* costmap, navfn::NavfnROS* planner) :
00047   costmap_client_(costmap),
00048   costmap_(costmap_client_->getCostmap()),
00049   planner_(planner),
00050   last_markers_count_(0)
00051   {
00052     if (!costmap || !planner) {
00053       throw std::invalid_argument("costmap and planner passed to constructor may not be NULL");
00054     }
00055   }
00056 
00057 bool ExploreFrontier::getFrontiers(std::vector<geometry_msgs::Pose>& frontiers)
00058 {
00059   findFrontiers();
00060   if (frontiers_.size() == 0)
00061     return false;
00062 
00063   frontiers.clear();
00064   for (auto& frontier : frontiers_) {
00065     frontiers.push_back(frontier.pose);
00066   }
00067 
00068   return true;
00069 }
00070 
00071 double ExploreFrontier::getFrontierCost(const Frontier& frontier) {
00072   ROS_DEBUG("cost of frontier: %f, at position: (%.2f, %.2f, %.2f)",
00073     planner_->getPointPotential(frontier.pose.position),
00074     frontier.pose.position.x, frontier.pose.position.y, tf::getYaw(frontier.pose.orientation));
00075 
00076   return planner_->getPointPotential(frontier.pose.position); // / 20000.0;
00077 }
00078 
00079 // TODO: what is this doing exactly?
00080 double ExploreFrontier::getOrientationChange(
00081   const Frontier& frontier,
00082   const tf::Stamped<tf::Pose>& robot_pose) const {
00083   double robot_yaw = tf::getYaw(robot_pose.getRotation());
00084   double robot_atan2 = atan2(robot_pose.getOrigin().y()
00085     + sin(robot_yaw), robot_pose.getOrigin().x() + cos(robot_yaw));
00086   double frontier_atan2 = atan2(frontier.pose.position.x, frontier.pose.position.y);
00087   double orientation_change = robot_atan2 - frontier_atan2;
00088 //  ROS_DEBUG("Orientation change: %.3f degrees, (%.3f radians)", orientation_change * (180.0 / M_PI), orientation_change);
00089   return orientation_change;
00090 }
00091 
00092 double ExploreFrontier::getFrontierGain(const Frontier& frontier) const {
00093   return frontier.size * costmap_->getResolution();
00094 }
00095 
00096 bool ExploreFrontier::getExplorationGoals(
00097   tf::Stamped<tf::Pose> robot_pose,
00098   std::vector<geometry_msgs::Pose>& goals,
00099   double potential_scale, double orientation_scale, double gain_scale)
00100 {
00101   findFrontiers();
00102 
00103   if (frontiers_.size() == 0) {
00104     ROS_DEBUG("no frontiers found");
00105     return false;
00106   }
00107 
00108   geometry_msgs::Point start;
00109   start.x = robot_pose.getOrigin().x();
00110   start.y = robot_pose.getOrigin().y();
00111   start.z = robot_pose.getOrigin().z();
00112   planner_->computePotential(start);
00113 
00114   //we'll make sure that we set goals for the frontier at least the circumscribed
00115   //radius away from unknown space
00116   // 2015: this may not be necessary
00117   // float step = -1.0 * costmap_resolution;
00118   // int c = ceil(costmap.getCircumscribedRadius() / costmap_resolution);
00119   // WeightedFrontier goal;
00120   std::vector<WeightedFrontier> weightedFrontiers;
00121   weightedFrontiers.reserve(frontiers_.size());
00122   for (auto& frontier: frontiers_) {
00123     WeightedFrontier weightedFrontier;
00124     weightedFrontier.frontier = frontier;
00125 
00126     tf::Point p(frontier.pose.position.x, frontier.pose.position.y, frontier.pose.position.z);
00127     tf::Quaternion bt;
00128     tf::quaternionMsgToTF(frontier.pose.orientation, bt);
00129     tf::Vector3 v(cos(bt.getAngle()), sin(bt.getAngle()), 0.0);
00130 
00131 //     for (int j=0; j <= c; j++) {
00132 //       tf::Vector3 check_point = p + (v * (step * j));
00133 //       weightedFrontier.frontier.pose.position.x = check_point.x();
00134 //       weightedFrontier.frontier.pose.position.y = check_point.y();
00135 //       weightedFrontier.frontier.pose.position.z = check_point.z();
00136 
00137 //       weightedFrontier.cost = potential_scale * getFrontierCost(weightedFrontier.frontier) + orientation_scale * getOrientationChange(weightedFrontier.frontier, robot_pose) - gain_scale * getFrontierGain(weightedFrontier.frontier, costmapResolution_);
00138 // //      weightedFrontier.cost = getFrontierCost(weightedFrontier.frontier) - getFrontierGain(weightedFrontier.frontier, costmapResolution_);
00139 // //      ROS_DEBUG("cost: %f (%f * %f + %f * %f - %f * %f)",
00140 // //          weightedFrontier.cost,
00141 // //          potential_scale,
00142 // //          getFrontierCost(weightedFrontier.frontier),
00143 // //          orientation_scale,
00144 // //          getOrientationChange(weightedFrontier.frontier, robot_pose),
00145 // //          gain_scale,
00146 // //          getFrontierGain(weightedFrontier.frontier, costmapResolution_) );
00147 //       weightedFrontiers.push_back(weightedFrontier);
00148 //     }
00149     weightedFrontier.cost =
00150       potential_scale * getFrontierCost(weightedFrontier.frontier)
00151       + orientation_scale * getOrientationChange(weightedFrontier.frontier, robot_pose)
00152       - gain_scale * getFrontierGain(weightedFrontier.frontier);
00153     weightedFrontiers.push_back(std::move(weightedFrontier));
00154   }
00155 
00156   goals.clear();
00157   goals.reserve(weightedFrontiers.size());
00158   std::sort(weightedFrontiers.begin(), weightedFrontiers.end());
00159   for (uint i = 0; i < weightedFrontiers.size(); i++) {
00160     goals.push_back(weightedFrontiers[i].frontier.pose);
00161   }
00162   return (goals.size() > 0);
00163 }
00164 
00165 void ExploreFrontier::findFrontiers() {
00166   frontiers_.clear();
00167 
00168   const size_t w = costmap_->getSizeInCellsX();
00169   const size_t h = costmap_->getSizeInCellsY();
00170   const size_t size = w * h;
00171 
00172   map_.info.width = w;
00173   map_.info.height = h;
00174   map_.data.resize(size);
00175   map_.info.resolution = costmap_->getResolution();
00176   map_.info.origin.position.x = costmap_->getOriginX();
00177   map_.info.origin.position.y = costmap_->getOriginY();
00178 
00179   // lock as we are accessing raw underlying map
00180   auto *mutex = costmap_->getMutex();
00181   std::unique_lock<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
00182 
00183   // Find all frontiers (open cells next to unknown cells).
00184   const unsigned char* map = costmap_->getCharMap();
00185   ROS_DEBUG_COND(!map, "no map available");
00186   for (size_t i = 0; i < size; ++i) {
00187 //    //get the world point for the index
00188 //    unsigned int mx, my;
00189 //    costmap.indexToCells(i, mx, my);
00190 //    geometry_msgs::Point p;
00191 //    costmap.mapToWorld(mx, my, p.x, p.y);
00192 //
00193     //check if the point has valid potential and is next to unknown space
00194 //    bool valid_point = planner_->validPointPotential(p);
00195     bool valid_point = (map[i] < costmap_2d::LETHAL_OBSTACLE);
00196     // ROS_DEBUG_COND(!valid_point, "invalid point %u", map[i]);
00197 
00198     if ((valid_point && map) &&
00199       (((i+1 < size) && (map[i+1] == costmap_2d::NO_INFORMATION)) ||
00200        ((i-1 < size) && (map[i-1] == costmap_2d::NO_INFORMATION)) ||
00201        ((i+w < size) && (map[i+w] == costmap_2d::NO_INFORMATION)) ||
00202        ((i-w < size) && (map[i-w] == costmap_2d::NO_INFORMATION))))
00203     {
00204       ROS_DEBUG_THROTTLE(30, "found suitable point");
00205       map_.data[i] = -128;
00206     } else {
00207       map_.data[i] = -127;
00208     }
00209   }
00210 
00211   // Clean up frontiers detected on separate rows of the map
00212   for (size_t y = 0, i = h-1; y < w; ++y) {
00213     ROS_DEBUG_THROTTLE(30, "cleaning cell %lu", i);
00214     map_.data[i] = -127;
00215     i += h;
00216   }
00217 
00218   // Group adjoining map_ pixels
00219   signed char segment_id = 127;
00220   std::vector<std::vector<FrontierPoint>> segments;
00221   for (size_t i = 0; i < size; i++) {
00222     if (map_.data[i] == -128) {
00223       ROS_DEBUG_THROTTLE(30, "adjoining on %lu", i);
00224       std::vector<size_t> neighbors;
00225       std::vector<FrontierPoint> segment;
00226       neighbors.push_back(i);
00227 
00228       // claim all neighbors
00229       while (!neighbors.empty()) {
00230         ROS_DEBUG_THROTTLE(30, "got %lu neighbors", neighbors.size());
00231         size_t idx = neighbors.back();
00232         neighbors.pop_back();
00233         map_.data[idx] = segment_id;
00234 
00235         tf::Vector3 tot(0,0,0);
00236         size_t c = 0;
00237         if (idx+1 < size && map[idx+1] == costmap_2d::NO_INFORMATION) {
00238           tot += tf::Vector3(1,0,0);
00239           ++c;
00240         }
00241         if (idx-1 < size && map[idx-1] == costmap_2d::NO_INFORMATION) {
00242           tot += tf::Vector3(-1,0,0);
00243           ++c;
00244         }
00245         if (idx+w < size && map[idx+w] == costmap_2d::NO_INFORMATION) {
00246           tot += tf::Vector3(0,1,0);
00247           ++c;
00248         }
00249         if (idx-w < size && map[idx-w] == costmap_2d::NO_INFORMATION) {
00250           tot += tf::Vector3(0,-1,0);
00251           ++c;
00252         }
00253 
00254         if(!(c > 0)) {
00255           ROS_ERROR("assertion failed. corrupted costmap?");
00256           ROS_DEBUG("c is %lu", c);
00257           continue;
00258         }
00259 
00260         segment.push_back(FrontierPoint(idx, tot / c));
00261 
00262         // consider 8 neighborhood
00263         if (idx-1 < size && map_.data[idx-1] == -128)
00264           neighbors.push_back(idx-1);
00265         if (idx+1 < size && map_.data[idx+1] == -128)
00266           neighbors.push_back(idx+1);
00267         if (idx-w < size && map_.data[idx-w] == -128)
00268           neighbors.push_back(idx-w);
00269         if (idx-w+1 < size && map_.data[idx-w+1] == -128)
00270           neighbors.push_back(idx-w+1);
00271         if (idx-w-1 < size && map_.data[idx-w-1] == -128)
00272           neighbors.push_back(idx-w-1);
00273         if (idx+w < size && map_.data[idx+w] == -128)
00274           neighbors.push_back(idx+w);
00275         if (idx+w+1 < size && map_.data[idx+w+1] == -128)
00276           neighbors.push_back(idx+w+1);
00277         if (idx+w-1 < size && map_.data[idx+w-1] == -128)
00278           neighbors.push_back(idx+w-1);
00279       }
00280 
00281       segments.push_back(std::move(segment));
00282       segment_id--;
00283       if (segment_id < -127)
00284         break;
00285     }
00286   }
00287 
00288   // no longer accessing map
00289   lock.unlock();
00290 
00291   int num_segments = 127 - segment_id;
00292   if (num_segments <= 0) {
00293     ROS_DEBUG("#segments is <0, no frontiers found");
00294     return;
00295   }
00296 
00297   for (auto& segment : segments) {
00298     Frontier frontier;
00299     size_t segment_size = segment.size();
00300     float x = 0, y = 0;
00301     tf::Vector3 d(0,0,0);
00302 
00303     for (const auto& frontier_point : segment) {
00304       d += frontier_point.d;
00305       size_t idx = frontier_point.idx;
00306       x += (idx % w);
00307       y += (idx / w);
00308     }
00309     d = d / segment_size;
00310     frontier.pose.position.x = map_.info.origin.position.x + map_.info.resolution * (x / segment_size);
00311     frontier.pose.position.y = map_.info.origin.position.y + map_.info.resolution * (y / segment_size);
00312     frontier.pose.position.z = 0.0;
00313 
00314     frontier.pose.orientation = tf::createQuaternionMsgFromYaw(atan2(d.y(), d.x()));
00315     frontier.size = segment_size;
00316 
00317     frontiers_.push_back(std::move(frontier));
00318   }
00319 
00320 }
00321 
00322 void ExploreFrontier::getVisualizationMarkers(visualization_msgs::MarkerArray& markers_msg)
00323 {
00324   ROS_DEBUG("visualising %lu frontiers", frontiers_.size());
00325   std::vector<visualization_msgs::Marker>& markers = markers_msg.markers;
00326   visualization_msgs::Marker m;
00327 
00328   m.header.frame_id = costmap_client_->getGlobalFrameID();
00329   m.header.stamp = ros::Time::now();
00330   m.ns = "frontiers";
00331   m.type = visualization_msgs::Marker::ARROW;
00332   m.scale.x = 1.0;
00333   m.scale.y = 1.0;
00334   m.scale.z = 1.0;
00335   m.color.r = 0;
00336   m.color.g = 0;
00337   m.color.b = 255;
00338   m.color.a = 255;
00339   // lives forever
00340   m.lifetime = ros::Duration(0);
00341   m.frame_locked = true;
00342 
00343   m.action = visualization_msgs::Marker::ADD;
00344   size_t id=0;
00345   for (auto& frontier : frontiers_) {
00346     m.id = id;
00347     m.pose = frontier.pose;
00348     markers.push_back(m);
00349     ++id;
00350   }
00351   size_t current_markers_count = markers.size();
00352 
00353   // delete previous markers, which are now unused
00354   m.action = visualization_msgs::Marker::DELETE;
00355   for (; id < last_markers_count_; ++id) {
00356     m.id = id;
00357     markers.push_back(m);
00358   }
00359 
00360   last_markers_count_ = current_markers_count;
00361 }
00362 
00363 }


explore
Author(s): Jiri Horner
autogenerated on Sun Mar 26 2017 03:48:06