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