bounded_explore_layer.cpp
Go to the documentation of this file.
2 
4 #include <geometry_msgs/PolygonStamped.h>
6 #include <costmap_2d/footprint.h>
7 #include <sensor_msgs/PointCloud2.h>
8 #include <pcl_ros/point_cloud.h>
9 #include <pcl/point_cloud.h>
10 #include <pcl/point_types.h>
11 #include <boost/foreach.hpp>
12 
13 #include <frontier_exploration/Frontier.h>
14 #include <frontier_exploration/UpdateBoundaryPolygon.h>
15 #include <frontier_exploration/GetNextFrontier.h>
18 
20 
21 namespace frontier_exploration
22 {
23 
27 
28  BoundedExploreLayer::BoundedExploreLayer(){}
29 
30  BoundedExploreLayer::~BoundedExploreLayer(){
31  polygonService_.shutdown();
32  frontierService_.shutdown();
33  delete dsrv_;
34  dsrv_ = 0;
35  }
36 
37  void BoundedExploreLayer::onInitialize(){
38 
39  ros::NodeHandle nh_("~/" + name_);
40  frontier_cloud_pub = nh_.advertise<sensor_msgs::PointCloud2>("frontiers",5);
41  configured_ = false;
42  marked_ = false;
43 
44  bool explore_clear_space;
45  nh_.param("explore_clear_space", explore_clear_space, true);
46  if(explore_clear_space){
47  default_value_ = NO_INFORMATION;
48  }else{
49  default_value_ = FREE_SPACE;
50  }
51 
52  matchSize();
53 
54  nh_.param<bool>("resize_to_boundary", resize_to_boundary_, true);
55  nh_.param<std::string>("frontier_travel_point", frontier_travel_point_, "closest");
56 
57  polygonService_ = nh_.advertiseService("update_boundary_polygon", &BoundedExploreLayer::updateBoundaryPolygonService, this);
58  frontierService_ = nh_.advertiseService("get_next_frontier", &BoundedExploreLayer::getNextFrontierService, this);
59 
60  dsrv_ = new dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>(nh_);
61  dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>::CallbackType cb = boost::bind(
62  &BoundedExploreLayer::reconfigureCB, this, _1, _2);
63  dsrv_->setCallback(cb);
64 
65  }
66 
67 
68  void BoundedExploreLayer::matchSize(){
69  Costmap2D* master = layered_costmap_->getCostmap();
70  resizeMap(master->getSizeInCellsX(), master->getSizeInCellsY(), master->getResolution(),
71  master->getOriginX(), master->getOriginY());
72  }
73 
74 
75  void BoundedExploreLayer::reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level){
76  enabled_ = config.enabled;
77  }
78 
79  bool BoundedExploreLayer::getNextFrontierService(frontier_exploration::GetNextFrontier::Request &req, frontier_exploration::GetNextFrontier::Response &res){
80  return getNextFrontier(req.start_pose, res.next_frontier);
81  }
82 
83  bool BoundedExploreLayer::getNextFrontier(geometry_msgs::PoseStamped start_pose, geometry_msgs::PoseStamped &next_frontier){
84 
85  //wait for costmap to get marked with boundary
86  ros::Rate r(10);
87  while(!marked_){
88  ros::spinOnce();
89  r.sleep();
90  }
91 
92  if(start_pose.header.frame_id != layered_costmap_->getGlobalFrameID()){
93  //error out if no transform available
94  if(!tf_listener_.waitForTransform(layered_costmap_->getGlobalFrameID(), start_pose.header.frame_id,ros::Time::now(),ros::Duration(10))) {
95  ROS_ERROR_STREAM("Couldn't transform from "<<layered_costmap_->getGlobalFrameID()<<" to "<< start_pose.header.frame_id);
96  return false;
97  }
98  geometry_msgs::PoseStamped temp_pose = start_pose;
99  tf_listener_.transformPose(layered_costmap_->getGlobalFrameID(),temp_pose,start_pose);
100  }
101 
102  //initialize frontier search implementation
103  FrontierSearch frontierSearch(*(layered_costmap_->getCostmap()));
104  //get list of frontiers from search implementation
105  std::list<Frontier> frontier_list = frontierSearch.searchFrom(start_pose.pose.position);
106 
107  if(frontier_list.size() == 0){
108  ROS_DEBUG("No frontiers found, exploration complete");
109  return false;
110  }
111 
112  //create placeholder for selected frontier
113  Frontier selected;
114  selected.min_distance = std::numeric_limits<double>::infinity();
115 
116  //pointcloud for visualization purposes
117  pcl::PointCloud<pcl::PointXYZI> frontier_cloud_viz;
118  pcl::PointXYZI frontier_point_viz(50);
119  int max;
120 
121  BOOST_FOREACH(Frontier frontier, frontier_list){
122  //load frontier into visualization poitncloud
123  frontier_point_viz.x = frontier.initial.x;
124  frontier_point_viz.y = frontier.initial.y;
125  frontier_cloud_viz.push_back(frontier_point_viz);
126 
127  //check if this frontier is the nearest to robot
128  if (frontier.min_distance < selected.min_distance){
129  selected = frontier;
130  max = frontier_cloud_viz.size()-1;
131  }
132  }
133 
134  //color selected frontier
135  frontier_cloud_viz[max].intensity = 100;
136 
137  //publish visualization point cloud
138  sensor_msgs::PointCloud2 frontier_viz_output;
139  pcl::toROSMsg(frontier_cloud_viz,frontier_viz_output);
140  frontier_viz_output.header.frame_id = layered_costmap_->getGlobalFrameID();
141  frontier_viz_output.header.stamp = ros::Time::now();
142  frontier_cloud_pub.publish(frontier_viz_output);
143 
144  //set goal pose to next frontier
145  next_frontier.header.frame_id = layered_costmap_->getGlobalFrameID();
146  next_frontier.header.stamp = ros::Time::now();
147 
148  //
149  if(frontier_travel_point_ == "closest"){
150  next_frontier.pose.position = selected.initial;
151  }else if(frontier_travel_point_ == "middle"){
152  next_frontier.pose.position = selected.middle;
153  }else if(frontier_travel_point_ == "centroid"){
154  next_frontier.pose.position = selected.centroid;
155  }else{
156  ROS_ERROR("Invalid 'frontier_travel_point' parameter, falling back to 'closest'");
157  next_frontier.pose.position = selected.initial;
158  }
159 
160  next_frontier.pose.orientation = tf::createQuaternionMsgFromYaw( yawOfVector(start_pose.pose.position, next_frontier.pose.position) );
161  return true;
162 
163  }
164 
165  bool BoundedExploreLayer::updateBoundaryPolygonService(frontier_exploration::UpdateBoundaryPolygon::Request &req, frontier_exploration::UpdateBoundaryPolygon::Response &res){
166 
167  return updateBoundaryPolygon(req.explore_boundary);
168 
169  }
170 
171  void BoundedExploreLayer::reset(){
172 
173  //reset costmap_ char array to default values
174  marked_ = false;
175  configured_ = false;
176  memset(costmap_, default_value_, size_x_ * size_y_ * sizeof(unsigned char));
177 
178  }
179 
180  bool BoundedExploreLayer::updateBoundaryPolygon(geometry_msgs::PolygonStamped polygon_stamped){
181 
182  //clear existing boundary, if any
183  polygon_.points.clear();
184 
185  //error if no transform available between polygon and costmap
186  if(!tf_listener_.waitForTransform(layered_costmap_->getGlobalFrameID(), polygon_stamped.header.frame_id,ros::Time::now(),ros::Duration(10))) {
187  ROS_ERROR_STREAM("Couldn't transform from "<<layered_costmap_->getGlobalFrameID()<<" to "<< polygon_stamped.header.frame_id);
188  return false;
189  }
190 
191  //Transform all points of boundary polygon into costmap frame
192  geometry_msgs::PointStamped in, out;
193  in.header = polygon_stamped.header;
194  BOOST_FOREACH(geometry_msgs::Point32 point32, polygon_stamped.polygon.points){
195  in.point = costmap_2d::toPoint(point32);
196  tf_listener_.transformPoint(layered_costmap_->getGlobalFrameID(),in,out);
197  polygon_.points.push_back(costmap_2d::toPoint32(out.point));
198  }
199 
200  //if empty boundary provided, set to whole map
201  if(polygon_.points.empty()){
202  geometry_msgs::Point32 temp;
203  temp.x = getOriginX();
204  temp.y = getOriginY();
205  polygon_.points.push_back(temp);
206  temp.y = getSizeInMetersY();
207  polygon_.points.push_back(temp);
208  temp.x = getSizeInMetersX();
209  polygon_.points.push_back(temp);
210  temp.y = getOriginY();
211  polygon_.points.push_back(temp);
212  }
213 
214  if(resize_to_boundary_){
215  updateOrigin(0,0);
216 
217  //Find map size and origin by finding min/max points of polygon
218  double min_x = std::numeric_limits<double>::infinity();
219  double min_y = std::numeric_limits<double>::infinity();
220  double max_x = -std::numeric_limits<double>::infinity();
221  double max_y = -std::numeric_limits<double>::infinity();
222 
223  BOOST_FOREACH(geometry_msgs::Point32 point, polygon_.points){
224  min_x = std::min(min_x,(double)point.x);
225  min_y = std::min(min_y,(double)point.y);
226  max_x = std::max(max_x,(double)point.x);
227  max_y = std::max(max_y,(double)point.y);
228  }
229 
230  //resize the costmap to polygon boundaries, don't change resolution
231  int size_x, size_y;
232  worldToMapNoBounds(max_x - min_x, max_y - min_y, size_x, size_y);
233  layered_costmap_->resizeMap(size_x, size_y, layered_costmap_->getCostmap()->getResolution(), min_x, min_y);
234  matchSize();
235  }
236 
237  configured_ = true;
238  marked_ = false;
239  return true;
240  }
241 
242 
243  void BoundedExploreLayer::updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x,
244  double* min_y, double* max_x, double* max_y){
245 
246  //check if layer is enabled and configured with a boundary
247  if (!enabled_ || !configured_){ return; }
248 
249  //update the whole costmap
250  *min_x = getOriginX();
251  *min_y = getOriginY();
252  *max_x = getSizeInMetersX()+getOriginX();
253  *max_y = getSizeInMetersY()+getOriginY();
254 
255  }
256 
257  void BoundedExploreLayer::updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j){
258  //check if layer is enabled and configured with a boundary
259  if (!enabled_ || !configured_){ return; }
260 
261  //draw lines between each point in polygon
262  MarkCell marker(costmap_, LETHAL_OBSTACLE);
263 
264  //circular iterator
265  for(int i = 0, j = polygon_.points.size()-1; i < polygon_.points.size(); j = i++){
266 
267  int x_1, y_1, x_2, y_2;
268  worldToMapEnforceBounds(polygon_.points[i].x, polygon_.points[i].y, x_1,y_1);
269  worldToMapEnforceBounds(polygon_.points[j].x, polygon_.points[j].y, x_2,y_2);
270 
271  raytraceLine(marker,x_1,y_1,x_2,y_2);
272  }
273  //update the master grid from the internal costmap
274  mapUpdateKeepObstacles(master_grid, min_i, min_j, max_i, max_j);
275 
276 
277  }
278 
279  void BoundedExploreLayer::mapUpdateKeepObstacles(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j){
280  if (!enabled_)
281  return;
282 
283  unsigned char* master = master_grid.getCharMap();
284  unsigned int span = master_grid.getSizeInCellsX();
285 
286  for (int j = min_j; j < max_j; j++)
287  {
288  unsigned int it = span*j+min_i;
289  for (int i = min_i; i < max_i; i++)
290  {
291  //only update master grid if local costmap cell is lethal/higher value, and is not overwriting a lethal obstacle in the master grid
292  if(master[it] != LETHAL_OBSTACLE && (costmap_[it] == LETHAL_OBSTACLE || costmap_[it] > master[it])){
293  master[it] = costmap_[it];
294  }
295  it++;
296  }
297  }
298  marked_ = true;
299  }
300 }
geometry_msgs::Point32 toPoint32(geometry_msgs::Point pt)
PLUGINLIB_EXPORT_CLASS(BAGReader, nodelet::Nodelet)
costmap_2d layer plugin that holds the state for a bounded frontier exploration task. Manages the boundary polygon, superimposes the polygon on the overall exploration costmap, and processes costmap to find next frontier to explore.
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
static const unsigned char FREE_SPACE
unsigned char * getCharMap() const
bool param(const std::string &param_name, T &param_val, const T &default_val) const
std::list< Frontier > searchFrom(geometry_msgs::Point position)
Runs search implementation, outward from the start position.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
Thread-safe implementation of a frontier-search task for an input costmap.
bool sleep()
unsigned int getSizeInCellsX() const
static geometry_msgs::Quaternion createQuaternionMsgFromYaw(double yaw)
geometry_msgs::Point toPoint(geometry_msgs::Point32 pt)
static const unsigned char LETHAL_OBSTACLE
static const unsigned char NO_INFORMATION
double yawOfVector(const T &origin, const S &end)
Calculate the yaw of vector defined by origin and end points.
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
static Time now()
#define ROS_ERROR_STREAM(args)
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
#define ROS_DEBUG(...)


frontier_exploration
Author(s): Paul Bovbel
autogenerated on Mon Jun 10 2019 13:25:00