00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
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);
00077 }
00078
00079
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
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
00115
00116
00117
00118
00119
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
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
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
00180 auto *mutex = costmap_->getMutex();
00181 std::unique_lock<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
00182
00183
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
00188
00189
00190
00191
00192
00193
00194
00195 bool valid_point = (map[i] < costmap_2d::LETHAL_OBSTACLE);
00196
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
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
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
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
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
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
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
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 }