PPExplorer.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2010, ISR University of Coimbra.
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 ISR University of Coimbra 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 * Author: Gonçalo Cabrita on 5/1/2011
00036 *********************************************************************/
00037 #include "PPExplorer.h"
00038 
00039 #include <cstdlib>
00040 #include <math.h>
00041 
00042 particle_plume::PPExplorer::PPExplorer() : n_(), pn_("~")
00043 {
00044         // Get the private parameters...
00045         pn_.param<std::string>("global_frame_id", global_frame_id_, "map");
00046 
00047         pn_.param("max_visited_cells_coef", max_visited_cells_coef_, 0.35);
00048         if(max_visited_cells_coef_ < 0.0)
00049         {
00050                 ROS_WARN("PPExplorer -- You set max_visited_cells_coef to %lf, yet the minimum value is 0, setting max_visited_cells_coef to 0...", max_visited_cells_coef_);
00051                 max_visited_cells_coef_ = 0.0;
00052         }
00053         if(max_visited_cells_coef_ > 1.0)
00054         {
00055                 ROS_WARN("PPExplorer -- You set max_visited_cells_coef to %lf, yet the maximum value is 1, setting max_visited_cells_coef to 1...", max_visited_cells_coef_);
00056                 max_visited_cells_coef_ = 1;
00057         }
00058         
00059         pn_.param("max_odor_visited_cells_coef", max_odor_visited_cells_coef_, 0.50);
00060         if(max_odor_visited_cells_coef_ < 0.0)
00061         {
00062                 ROS_WARN("PPExplorer -- You set max_odor_visited_cells_coef to %lf, yet the minimum value is 0, setting max_odor_visited_cells_coef to 0...", max_odor_visited_cells_coef_);
00063                 max_odor_visited_cells_coef_ = 0.0;
00064         }
00065         if(max_odor_visited_cells_coef_ > 1.0)
00066         {
00067                 ROS_WARN("PPExplorer -- You set max_odor_visited_cells_coef to %lf, yet the maximum value is 1, setting max_odor_visited_cells_coef to 1...", max_odor_visited_cells_coef_);
00068                 max_odor_visited_cells_coef_ = 1;
00069         }
00070         
00071         pn_.param("pie_radius", pie_radius_, 0.60);
00072         if(pie_radius_ < 0) pie_radius_ = 0.0;
00073         
00074         pn_.param("pie_slices", pie_slices_, 16);
00075         if(pie_slices_ < 4)
00076         {
00077                 ROS_WARN("PPExplorer -- You set pie_slices to %d, yet the minimum value is 4, setting pie_slices to 4...", pie_slices_);
00078                 pie_slices_ = 4;
00079         }
00080         if(pie_slices_ > 36)
00081         {
00082                 ROS_WARN("PPExplorer -- You set pie_slices to %d, yet the maximum value is 36, setting pie_slices to 36...", pie_slices_);
00083                 pie_slices_ = 36;
00084         }
00085         
00086         pn_.param("find_clearing_sim_steps", find_clearing_sim_steps_, 3);
00087         if(find_clearing_sim_steps_ < 1)
00088         {
00089                 ROS_WARN("PPExplorer -- You set find_clearing_sim_steps to %d, yet the minimum value is 1, setting find_clearing_sim_steps to 1...", find_clearing_sim_steps_);
00090                 find_clearing_sim_steps_ = 1;
00091         }
00092         
00093         pn_.param("visited_cells_weight", visited_cells_weight_, 1.00);
00094         pn_.param("current_heading_weight", current_heading_weight_, 0.01);
00095         pn_.param("odor_weight", odor_weight_, 0.10);
00096         pn_.param("other_robots_weight", other_robots_weight_, 1.00);
00097         
00098         pn_.param("bubble_radius", bubble_radius_, 0.10);
00099         pn_.param("min_particle_count_difference", min_particle_count_difference_, 5);
00100         if(min_particle_count_difference_ < 1)
00101         {
00102                 ROS_WARN("PPExplorer -- You set min_particle_count_difference to %d, yet the minimum value is 1, setting min_particle_count_difference to 1...", min_particle_count_difference_);
00103                 min_particle_count_difference_ = 1;
00104         }
00105         
00106         multi_robot_ = pn_.getParam("unique_id", unique_id_);
00107         
00108         pn_.param("debug_slices", debug_slices_, false);
00109         
00110         n_.param("move_base/local_costmap/inflation_radius", inflation_radius_, 0.20);
00111         
00112         // Subscribe to topics
00113         odom_sub_.subscribe(n_, "odom", 10);
00114         tf_filter_ = new tf::MessageFilter<nav_msgs::Odometry>(odom_sub_, tf_, global_frame_id_, 10);
00115     tf_filter_->registerCallback( boost::bind(&PPExplorer::odomCallback, this, _1) );
00116     
00117         map_sub_ = n_.subscribe("/map", 1, &PPExplorer::mapCallback, this);
00118         map_meta_sub_ = n_.subscribe("/map_metadata", 1, &PPExplorer::mapMetaCallback, this);
00119         cells_sub_ = n_.subscribe("/visited_cells", 1, &PPExplorer::cellsCallback, this);
00120         plume_sub_ = n_.subscribe("/plume", 1, &PPExplorer::ppCallback, this);
00121         //plume_sub_.subscribe(n_, "plume", 1, boost::bind(&PPExplorer::ppCallback, this, _1));
00122         obstacles_sub_ = n_.subscribe("move_base/local_costmap/obstacles", 1, &PPExplorer::obstaclesCallback, this);
00123         inflated_sub_ = n_.subscribe("move_base/local_costmap/inflated_obstacles", 1, &PPExplorer::inflatedCallback, this);
00124         
00125         // Multi-robot stuff
00126         if(multi_robot_)
00127         {
00128                 ROS_INFO("PPExplorer -- Going multi-robot, my unique id is %s", unique_id_.c_str());
00129                 
00130                 finish_on_next_recovery_ = false;
00131                 
00132                 recovery_sub_ = n_.subscribe("/pp_explorer/recovery_cells", 100, &PPExplorer::recoveryCellsCallback, this);
00133                 recovery_pub_ = n_.advertise<pp_explorer::GridCellsIdentified>("/pp_explorer/recovery_cells", 10, true);
00134                 
00135                 chatter_sub_ = n_.subscribe("/pp_explorer/chatter", 100, &PPExplorer::chatterCallback, this);
00136                 chatter_pub_ = n_.advertise<pp_explorer::StringIdentified>("/pp_explorer/chatter", 10, true);
00137                 
00138                 poses_sub_ = n_.subscribe("/pp_explorer/poses", 100, &PPExplorer::posesCallback, this);
00139                 poses_pub_ = n_.advertise<pp_explorer::PoseStampedIdentified>("/pp_explorer/poses", 10, true);
00140         }
00141         
00142         debug_pub_ = n_.advertise<nav_msgs::GridCells>("debug_cells", 1, true);
00143         debug_goal_pub_ = n_.advertise<visualization_msgs::Marker>("pp_goal", 1);
00144         
00145         got_map_ = false;
00146         got_odom_ = false;      
00147 }
00148 
00149 particle_plume::PPExplorer::~PPExplorer()
00150 {
00151         // Do destruction stuff here!
00152 }
00153 
00154 bool particle_plume::PPExplorer::isReady()
00155 {
00156         return (got_map_ && got_odom_);
00157 }
00158 
00159 
00160 void particle_plume::PPExplorer::ppCallback(const sensor_msgs::PointCloud2::ConstPtr& msg)
00161 {
00162         ROS_DEBUG("PPExplorer - %s - Got a plume msg!", __FUNCTION__);
00163         fromROSMsg(*msg, plume_);
00164 }
00165 
00166 void particle_plume::PPExplorer::cellsCallback(const nav_msgs::GridCells::ConstPtr& msg)
00167 {
00168         ROS_DEBUG("PPExplorer - %s - Got a visited cells msg!", __FUNCTION__);
00169         cells_ = *msg;
00170 }
00171 
00172 void particle_plume::PPExplorer::mapCallback(const nav_msgs::OccupancyGrid::ConstPtr& msg)
00173 {
00174         ROS_DEBUG("PPExplorer - %s - Got the map!", __FUNCTION__);
00175         map_ = *msg;
00176         got_map_ = true;
00177 }
00178 
00179 void particle_plume::PPExplorer::mapMetaCallback(const nav_msgs::MapMetaData::ConstPtr& msg)
00180 {
00181         ROS_DEBUG("PPExplorer - %s - Got a map metadata msg!", __FUNCTION__);
00182         map_metadata_ = *msg;
00183 }
00184 
00185 void particle_plume::PPExplorer::odomCallback(const boost::shared_ptr<const nav_msgs::Odometry>& msg)
00186 {
00187         ROS_DEBUG("PPExplorer - %s - Got an odom msg!", __FUNCTION__);
00188 
00189         geometry_msgs::PoseStamped odom;
00190         odom.header.frame_id = msg->header.frame_id;
00191         odom.header.stamp = msg->header.stamp;
00192         odom.pose.position.x = msg->pose.pose.position.x;       
00193         odom.pose.position.y = msg->pose.pose.position.y;
00194         odom.pose.orientation = msg->pose.pose.orientation;
00195         
00196         try 
00197         {
00198                 // Transform the odom into a pose in the map frame
00199                 tf_.transformPose(global_frame_id_, odom, robot_pose_);
00200         }
00201         catch(tf::TransformException &ex) 
00202         {
00203                 ROS_ERROR("PPExplorer - %s - Error: %s", __FUNCTION__, ex.what());
00204                 return;
00205         }
00206         
00207         /*pp_explorer::PoseStampedIdentified my_pose;
00208         my_pose.sender = unique_id_;
00209         my_pose.pose = robot_pose_;
00210         poses_pub_.publish(my_pose);*/
00211         
00212         got_odom_ = true;
00213 }
00214 
00215 void particle_plume::PPExplorer::obstaclesCallback(const nav_msgs::GridCells::ConstPtr& msg)
00216 {
00217         ROS_DEBUG("PPExplorer - %s - Got an obstacles msg!", __FUNCTION__);
00218         obstacles_ = *msg;
00219 }
00220 
00221 void particle_plume::PPExplorer::inflatedCallback(const nav_msgs::GridCells::ConstPtr& msg)
00222 {
00223         ROS_DEBUG("PPExplorer - %s - Got an inflated obstacles msg!", __FUNCTION__);
00224         inflated_ = *msg;
00225 }
00226 
00227 void particle_plume::PPExplorer::recoveryCellsCallback(const pp_explorer::GridCellsIdentified::ConstPtr& msg)
00228 {
00229         if(msg->sender.compare(unique_id_) == 0) return;
00230         
00231         ROS_DEBUG("PPExplorer - %s - Got a recovery cells msg from %s with %d cells.", __FUNCTION__, msg->sender.c_str(), msg->cells.cells.size());
00232         
00233         if(recovery_cells_.size() == 0)
00234         {
00235                 recovery_cells_.push_back(*msg);
00236         }
00237         else
00238         {
00239                 std::vector<pp_explorer::GridCellsIdentified>::iterator cell;
00240                 for(cell = recovery_cells_.begin() ; cell != recovery_cells_.end() ; cell++)
00241                 {
00242                         if(msg->sender.compare(cell->sender) == 0)
00243                         {
00244                                 if(msg->cells.cells.size() > 0)
00245                                 {
00246                                         cell->cells = msg->cells;
00247                                 }
00248                                 else
00249                                 {
00250                                         recovery_cells_.erase(cell);
00251                                 }
00252                                 break;
00253                         }
00254                         else if(cell == recovery_cells_.end() && msg->cells.cells.size() > 0)
00255                         {
00256                                 recovery_cells_.push_back(*msg);
00257                         }
00258                 }
00259         }
00260 }
00261 
00262 void particle_plume::PPExplorer::chatterCallback(const pp_explorer::StringIdentified::ConstPtr& msg)
00263 {
00264         if(msg->sender.compare(unique_id_) == 0) return;
00265 
00266         ROS_INFO("PPExplorer - %s - Got a chatter msg from %s with the following: \"%s\"", __FUNCTION__, msg->sender.c_str(), msg->string.data.c_str());
00267         
00268         if(msg->string.data.compare("Exploration complete")==0) finish_on_next_recovery_ = true;
00269 }
00270 
00271 void particle_plume::PPExplorer::posesCallback(const pp_explorer::PoseStampedIdentified::ConstPtr& msg)
00272 {
00273         if(msg->sender.compare(unique_id_) == 0) return;
00274         
00275         ROS_DEBUG("PPExplorer - %s - Got a pose msg from %s.", __FUNCTION__, msg->sender.c_str());
00276         
00277         if(other_robots_poses_.size() == 0)
00278         {
00279                 other_robots_poses_.push_back(*msg);
00280         }
00281         else
00282         {
00283                 std::vector<pp_explorer::PoseStampedIdentified>::iterator pose;
00284                 for(pose = other_robots_poses_.begin() ; pose != other_robots_poses_.end() ; pose++)
00285                 {
00286                         if(msg->sender.compare(pose->sender) == 0)
00287                         {
00288                                 pose->pose = msg->pose;
00289                                 break;
00290                         }
00291                         else if(pose == other_robots_poses_.end())
00292                         {
00293                                 other_robots_poses_.push_back(*msg);
00294                         }
00295                 }
00296         }       
00297 }
00298 
00299 bool particle_plume::PPExplorer::findYumiestSlice(geometry_msgs::PoseStamped * goal)
00300 {
00301         return yumiestSlice(goal, &robot_pose_);
00302 }
00303 
00304 bool particle_plume::PPExplorer::yumiestSlice(geometry_msgs::PoseStamped * goal, geometry_msgs::PoseStamped * robot_pose)
00305 {
00306         ROS_DEBUG("PPExplorer - %s - Yum yum!", __FUNCTION__);  
00307         
00308         nav_msgs::GridCells debug_cells;
00309         
00310         if(debug_slices_)
00311         {
00312                 debug_cells.header.frame_id = global_frame_id_;
00313                 debug_cells.cell_width = map_.info.resolution;
00314                 debug_cells.cell_height = map_.info.resolution;
00315         }
00316 
00317         int robot_cell_x = (int)( robot_pose->pose.position.x / map_.info.resolution );
00318         int robot_cell_y = (int)( robot_pose->pose.position.y / map_.info.resolution );
00319         
00320         ROS_DEBUG("PPExplorer - %s - Map %dx%d @%lf   Robot is at %d %d", __FUNCTION__, map_.info.width, map_.info.height, map_.info.resolution, robot_cell_x, robot_cell_y);
00321         
00322         // If our robot is out of the map... not good, not good.
00323         if(!isInsideTheMap(robot_cell_x, robot_cell_y))
00324         {
00325                 ROS_ERROR("PPExplorer - %s - Robot is out of bounds!", __FUNCTION__);
00326                 return false;
00327         }
00328         
00329         // Empty the plate with the best slices :D
00330         best_slices_.clear();
00331         
00332         // Calculate the slice area
00333         double slice_area = (M_PI*pie_radius_*pie_radius_)/pie_slices_;         //m^2
00334         // And the angle for half a slice
00335         double half_slice = 2*M_PI/(2*pie_slices_); // rad
00336         
00337         ROS_DEBUG("PPExplorer - %s - Half slice is %lf", __FUNCTION__, half_slice);
00338         
00339         int slice_cells;
00340         int visited_cells_count;
00341         bool skip_slice;
00342         int cell_x, cell_y;
00343         double pos_x, pos_y;
00344         float slice_goal_x, slice_goal_y;
00345         double sigma;
00346         
00347         double odor_yaw = 0.0;
00348         double odor_weight = 0.0;
00349         
00350         geometry_msgs::PointStamped max_odor;
00351         max_odor.header.frame_id = global_frame_id_;
00352         geometry_msgs::PointStamped min_odor;
00353         min_odor.header.frame_id = global_frame_id_;
00354         
00355         int max_particle_count = 0;
00356         int min_particle_count = plume_.points.size();
00357 
00358         // 1. Check if there is odor present in the pie or not
00359         bool odor_found = false;
00360         if(max_visited_cells_coef_ > 0.0)
00361         {
00362                 BOOST_FOREACH(const pcl::PointXYZI& pt, plume_.points)
00363                 {
00364                         if(isInsidePieRadius(pt.x, pt.y, robot_pose))
00365                         {
00366                                 int particle_count = 0;
00367                                 BOOST_FOREACH(const pcl::PointXYZI& pti, plume_.points)
00368                                 {
00369                                         if(sqrt((pt.x-pti.x)*(pt.x-pti.x) + (pt.y-pti.y)*(pt.y-pti.y) + (pt.z-pti.z)*(pt.z-pti.z)) <= bubble_radius_)
00370                                         {
00371                                                 particle_count++;
00372                                         }
00373                                 }
00374                                 if(particle_count > max_particle_count)
00375                                 {
00376                                         max_particle_count = particle_count;
00377                                         max_odor.point.x = pt.x;
00378                                         max_odor.point.y = pt.y;
00379                                         max_odor.point.z = pt.z;
00380                                 }
00381                                 if(particle_count < min_particle_count)
00382                                 {
00383                                         min_particle_count = particle_count;
00384                                         min_odor.point.x = pt.x;
00385                                         min_odor.point.y = pt.y;
00386                                         min_odor.point.z = pt.z;
00387                                 }
00388                                 odor_found = true;
00389                         }
00390                 }
00391         }
00392         double max_visited_cells;
00393         
00394         // If odor was found and our odor gradient provided enought information...
00395         if(odor_found)
00396         {
00397                 max_visited_cells = max_odor_visited_cells_coef_;
00398                 
00399                 if(!(max_particle_count == 0 || min_particle_count == plume_.points.size()) && max_particle_count-min_particle_count > min_particle_count_difference_)
00400                 {
00401                         odor_weight = odor_weight_;
00402                         //TODO: Verify if the odor vector resulting from max_odor and min_odor is valid. 
00403                         odor_yaw = atan2(max_odor.point.x-min_odor.point.x, max_odor.point.y-min_odor.point.y);
00404                 }
00405                 else
00406                 {
00407                         odor_weight = 0.0;
00408                 }
00409         }
00410         else
00411         {
00412                 max_visited_cells = max_visited_cells_coef_;
00413                 odor_weight = 0.0;
00414         }
00415         
00416         // For each slice...
00417         for(int slice=0 ; slice<pie_slices_ ; slice++)
00418         {       
00419                 skip_slice = false;
00420                 
00421                 // 2. Make a rough estimate of where the slice is in terms of map cells
00422                 int upper_bound_x = robot_cell_x;
00423                 int upper_bound_y = robot_cell_y;
00424                 int lower_bound_x = robot_cell_x;
00425                 int lower_bound_y = robot_cell_y;
00426                 
00427                 double start_angle = angles::normalize_angle(tf::getYaw(robot_pose->pose.orientation) + slice*2*half_slice - half_slice);
00428                 
00429                 slice_goal_x = robot_pose->pose.position.x + pie_radius_*cos(start_angle+half_slice);
00430                 slice_goal_y = robot_pose->pose.position.y + pie_radius_*sin(start_angle+half_slice);
00431                 
00432                 ROS_DEBUG("PPExplorer - %s - Slice %d with angle of %lf", __FUNCTION__, slice+1, start_angle);
00433                 
00434                 for(int i=0 ; i<3 ; i++)
00435                 {
00436                         // Cell indexes
00437                         cell_x = (int)((robot_pose->pose.position.x + pie_radius_*cos(start_angle + i*half_slice)) / map_.info.resolution );
00438                         cell_y = (int)((robot_pose->pose.position.y + pie_radius_*sin(start_angle + i*half_slice)) / map_.info.resolution );
00439                 
00440                         if(cell_x > upper_bound_x) upper_bound_x = cell_x;
00441                         else if(cell_x < lower_bound_x) lower_bound_x = cell_x;
00442                         
00443                         if(cell_y > upper_bound_y) upper_bound_y = cell_y;
00444                         else if(cell_y < lower_bound_y) lower_bound_y = cell_y;
00445                 }
00446                 
00447                 // 3. If slice is out of bounds, discard it!
00448                 if(upper_bound_x > map_.info.width-1 || lower_bound_x < 0 || upper_bound_y > map_.info.height-1 || lower_bound_y < 0)
00449                 {
00450                         skip_slice = true;
00451                 }
00452                 
00453                 // 4. If the slice sends the robot to an obstacle or too close to one (inflated obstacle) discard it!
00454                 if(!skip_slice)
00455                 {
00456                         if(!isViableLocalMapGoal(slice_goal_x, slice_goal_y)) skip_slice = true;
00457                 }
00458                 
00459                 // 5. If there are obstacle or unknown cells in the slice, skip this slice
00460                 if(!skip_slice)
00461                 {
00462                         slice_cells = 0;
00463                         for(int i=lower_bound_x ; i<upper_bound_x ; i++)
00464                         {
00465                                 for(int j=lower_bound_y ; j<upper_bound_y ; j++)
00466                                 {       
00467                                         // Cell in map coordinates
00468                                         pos_x = i * map_.info.resolution + map_.info.resolution/2;
00469                                         pos_y = j * map_.info.resolution + map_.info.resolution/2;
00470                                 
00471                                         sigma = atan2(pos_y - robot_pose->pose.position.y, pos_x - robot_pose->pose.position.x);
00472                                         if(sigma < 0.0) sigma += 2*M_PI;
00473                                         sigma = angles::normalize_angle(sigma - (start_angle < 0.0 ? start_angle+2*M_PI : start_angle));
00474                                 
00475                                         if(isInsidePieRadius(pos_x, pos_y, robot_pose) && sigma >= 0.0 && sigma <= half_slice*2)
00476                                         {
00477                                                 geometry_msgs::Point point;
00478                                                 point.x = i * map_.info.resolution;
00479                                                 point.y = j * map_.info.resolution;
00480                                         
00481                                                 // If slice has an obstacle, unknown cell or too close to either one
00482                                                 if(!isViableGlobalMapCell(i,j))
00483                                                 {
00484                                                         skip_slice = true;
00485                                                 }
00486                                                 
00487                                                 if(debug_slices_)
00488                                                 {
00489                                                         debug_cells.cells.push_back(point);
00490                                                         debug_cells.header.stamp = ros::Time::now();
00491                                                         debug_pub_.publish(debug_cells);
00492                                                         
00493                                                         visualization_msgs::Marker marker;
00494                                                         marker.header.frame_id = "/map";
00495                                                         marker.header.stamp = ros::Time::now();
00496                                                         marker.ns = "pp_goal";
00497                                                         marker.id = 0;
00498                                                         marker.type = visualization_msgs::Marker::ARROW;
00499                                                         marker.action = visualization_msgs::Marker::ADD;
00500                                                         marker.pose.position.x = slice_goal_x;
00501                                                         marker.pose.position.y = slice_goal_y;
00502                                                         marker.pose.position.z = 0.25;
00503                                                         marker.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(0.0, M_PI/2, 0.0);
00504                                                         marker.scale.x = 0.20;
00505                                                         marker.scale.y = 0.5;
00506                                                         marker.scale.z = 0.5;
00507                                                         marker.color.a = 1.0;
00508                                                         marker.color.r = 1.0;
00509                                                         marker.color.g = 0.0;
00510                                                         marker.color.b = 0.0;
00511                                                         debug_goal_pub_.publish(marker);
00512                                                 }
00513                                                 slice_cells++;
00514                                         }
00515                                         if(skip_slice) break;
00516                                 }
00517                                 if(skip_slice) break;
00518                         }
00519                 }
00520                 
00521                 if(!skip_slice)
00522                 {
00523                         // 6. Count the number of visited cells inside the slice
00524                         visited_cells_count = 0;
00525                         std::vector<geometry_msgs::Point>::iterator cell;
00526                         for(cell = cells_.cells.begin() ; cell != cells_.cells.end() ; cell++)
00527                         {
00528                                 sigma = atan2(cell->y+cells_.cell_height/2 - robot_pose->pose.position.y, cell->x+cells_.cell_width/2 - robot_pose->pose.position.x);
00529                                 if(sigma < 0.0) sigma += 2*M_PI;
00530                                 sigma = angles::normalize_angle(sigma - (start_angle < 0.0 ? start_angle+2*M_PI : start_angle));
00531                         
00532                                 if(isInsidePieRadius(cell->x, cell->y, robot_pose) && sigma >= 0.0 && sigma <= half_slice*2)
00533                                 {
00534                                         visited_cells_count++;
00535                                 }
00536                         }
00537                         
00538                         double slice_yaw = atan2(slice_goal_y-robot_pose->pose.position.y, slice_goal_x-robot_pose->pose.position.x);
00539                         
00540                         SliceOfPie new_slice;
00541                         new_slice.goal.position.x = slice_goal_x;
00542                         new_slice.goal.position.y = slice_goal_y;
00543                         new_slice.goal.orientation = tf::createQuaternionMsgFromYaw(slice_yaw);
00544                         
00545                         double visited_cells_coef = cells_.cell_width*cells_.cell_height*visited_cells_count/slice_area;
00546                         
00547                         if(visited_cells_coef <= max_visited_cells)
00548                         {
00549                                 // Cost function
00550                                 new_slice.yuminess = visited_cells_weight_ * (visited_cells_coef) + current_heading_weight_ * (fabs(angles::shortest_angular_distance(slice_yaw, tf::getYaw(robot_pose->pose.orientation)))) + odor_weight * (fabs(angles::shortest_angular_distance(slice_yaw, odor_yaw)));
00551                                 
00552                                 // Multi-robot cost function
00553                                 if(multi_robot_)
00554                                 {
00555                                         std::vector<pp_explorer::PoseStampedIdentified>::iterator pose_it;
00556                                         for(pose_it = other_robots_poses_.begin() ; pose_it != other_robots_poses_.end() ; pose_it++)
00557                                         {
00558                                                 if(pose_it->pose.header.stamp - ros::Time::now() > ros::Duration(0.1))
00559                                                 {
00560                                                         ROS_WARN("PPExplorer - %s - Discarding %s pose for being too old...", __FUNCTION__, pose_it->sender.c_str());
00561                                                 }
00562                                                 else
00563                                                 {
00564                                                         //TODO: Finish this!!!
00565                                                         double robots_delta_x = pose_it->pose.pose.position.x - robot_pose->pose.position.x;
00566                                                         double robots_delta_y = pose_it->pose.pose.position.y - robot_pose->pose.position.y;
00567                                                         double other_robot_yaw = atan2(robots_delta_y, robots_delta_x);
00568                                                         double distance_between_robots = sqrt(robots_delta_x*robots_delta_x + robots_delta_y*robots_delta_y);
00569                                                         
00570                                                         new_slice.yuminess += other_robots_weight_*exp(-1*distance_between_robots)*(fabs(angles::shortest_angular_distance(slice_yaw, other_robot_yaw)));
00571                                                 }
00572                                         }
00573                                 }
00574                                 
00575                                 best_slices_.push_back(new_slice);
00576                         }
00577                         else
00578                         {
00579                                 ROS_DEBUG("PPExplorer - %s - Discarding this slice %lf %lf due to high percentage of visited cells.", __FUNCTION__, robot_pose->pose.position.x + (pie_radius_ - 0.20)*cos(start_angle+half_slice), robot_pose->pose.position.y + (pie_radius_ - 0.20)*sin(start_angle+half_slice));
00580                         }
00581                         
00582                         ROS_DEBUG("PPExplorer - %s - Found %d visited cells", __FUNCTION__, visited_cells_count);
00583                 }
00584                 else
00585                 {
00586                         ROS_DEBUG("PPExplorer - %s - Discarding this slice %lf %lf due to presence of obstacles.", __FUNCTION__, robot_pose->pose.position.x + (pie_radius_ - 0.20)*cos(start_angle+half_slice), robot_pose->pose.position.y + (pie_radius_ - 0.20)*sin(start_angle+half_slice));
00587                 }
00588                 
00589                 if(debug_slices_)
00590                 {
00591                         ros::Duration(0.1).sleep();
00592                         //debug_cells.cells.clear();
00593                 }
00594         }
00595         
00596         // Ops... None of the slices are any good!!!
00597         if(best_slices_.size() == 0)
00598         {
00599                 ROS_DEBUG("PPExplorer - %s - All the slices were discarded!", __FUNCTION__);
00600                 return false;
00601         }
00602         
00603         // 7. Order the slices by yuminess!
00604         CompareSlices cmp;
00605         best_slices_.sort(cmp);
00606         
00607         // 8. Set the goal!
00608         this->setGoal(goal);
00609         
00610         ROS_DEBUG("PPExplorer - %s - Setting goal to %lf %lf", __FUNCTION__, goal->pose.position.x, goal->pose.position.y);
00611         
00612         if(debug_slices_)
00613         {
00614                 ros::Duration(1.0).sleep();
00615                 debug_cells.cells.clear();
00616                 debug_pub_.publish(debug_cells);
00617         }
00618         
00619         return true;
00620 }
00621 
00622 bool particle_plume::PPExplorer::nextYumiestSlice(geometry_msgs::PoseStamped * goal)
00623 {
00624         best_slices_.pop_front();
00625         
00626         if(best_slices_.size() == 0) return false;
00627         
00628         this->setGoal(goal);
00629         
00630         return true;
00631 }
00632 
00633 bool particle_plume::PPExplorer::findClearing(geometry_msgs::PoseStamped * goal)
00634 {
00635         if(multi_robot_ && finish_on_next_recovery_)
00636         {
00637                 explorationComplete();
00638                 return false;
00639         }
00640 
00641         ROS_DEBUG("PPExplorer - %s - Looking for a clearing to go to...", __FUNCTION__);
00642         
00643         pp_explorer::GridCellsIdentified recovery_cells;
00644         recovery_cells.sender = unique_id_;
00645         recovery_cells.cells.header.frame_id = global_frame_id_;
00646         
00647         nav_msgs::GridCells debug_cells;
00648         
00649         if(debug_slices_)
00650         {
00651                 debug_cells.header.frame_id = global_frame_id_;
00652                 debug_cells.cell_width = map_.info.resolution;
00653                 debug_cells.cell_height = map_.info.resolution;
00654         }
00655         
00656         int robot_cell_x = (int)( robot_pose_.pose.position.x / map_.info.resolution );
00657         int robot_cell_y = (int)( robot_pose_.pose.position.y / map_.info.resolution );
00658         
00659         // If our robot is out of the map... not good, not good.
00660         if(!isInsideTheMap(robot_cell_x, robot_cell_y))
00661         {
00662                 ROS_ERROR("PPExplorer - %s - Robot is out of bounds!", __FUNCTION__);
00663                 return false;
00664         }
00665         
00666         std::vector<geometry_msgs::Point> last_cells;
00667         std::vector<geometry_msgs::Point> current_cells;
00668         std::vector<geometry_msgs::Point> next_cells;
00669         
00670         geometry_msgs::Point cell;
00671         cell.x = robot_cell_x * map_.info.resolution + map_.info.resolution/2;
00672         cell.y = robot_cell_y * map_.info.resolution + map_.info.resolution/2;
00673         current_cells.push_back(cell);
00674         
00675         while(true)
00676         {
00677                 ros::spinOnce();
00678         
00679                 next_cells.clear();
00680         
00681                 std::vector<geometry_msgs::Point>::iterator cell_it;
00682                 for(cell_it = current_cells.begin() ; cell_it != current_cells.end() ; cell_it++)
00683                 {
00684                         for(int i=(int)(cell_it->x/map_.info.resolution)-1 ; i<(int)(cell_it->x/map_.info.resolution)+2 ; i++)
00685                         {
00686                                 for(int j=(int)(cell_it->y/map_.info.resolution)-1 ; j<(int)(cell_it->y/map_.info.resolution)+2 ; j++)
00687                                 {
00688                                         geometry_msgs::Point new_cell;
00689                                         new_cell.x = i * map_.info.resolution;
00690                                         new_cell.y = j * map_.info.resolution;
00691                                         
00692                                         // 1. If cell is itself skip
00693                                         if(new_cell.x==cell_it->x && new_cell.y==cell_it->y) continue;
00694                                         
00695                                         // 2. If cell is obstacle or unknown, or too close to one, skip
00696                                         if(!isViableGlobalMapCell(i,j)) continue;
00697                                         
00698                                         // 3. If cell is already a next cell skip
00699                                         if(testCell(&new_cell, &next_cells)) continue; 
00700                                         
00701                                         // 4. If cell is a current cell skip
00702                                         if(testCell(&new_cell, &current_cells)) continue; 
00703                                         
00704                                         // 5. If cell is a last cell skip
00705                                         if(testCell(&new_cell, &last_cells)) continue;
00706                                         
00707                                         // Multi-robot cell sharing
00708                                         if(multi_robot_)
00709                                         {
00710                                                 bool is_recovery_cell = false;
00711                                                 std::vector<pp_explorer::GridCellsIdentified>::iterator recovery_it;
00712                                                 for(recovery_it = recovery_cells_.begin() ; recovery_it != recovery_cells_.end() ; recovery_it++)
00713                                                 {
00714                                                         if(testCell(&new_cell, &recovery_it->cells.cells))
00715                                                         {
00716                                                                 is_recovery_cell = true;
00717                                                                 break;
00718                                                         }
00719                                                 }
00720                                                 if(is_recovery_cell) continue;
00721                                         }
00722                                         
00723                                         // 6. We only test cells that are outside the pie!
00724                                         if(!isInsidePieRadius(new_cell.x, new_cell.y, &robot_pose_))
00725                                         {
00726                                                 geometry_msgs::PoseStamped dummy_goal;
00727                                                 dummy_goal.pose.position.x = new_cell.x + map_.info.resolution/2;
00728                                                 dummy_goal.pose.position.y = new_cell.y + map_.info.resolution/2;
00729                                                 dummy_goal.pose.orientation = tf::createQuaternionMsgFromYaw(atan2(dummy_goal.pose.position.y - robot_pose_.pose.position.y, dummy_goal.pose.position.x - robot_pose_.pose.position.x));
00730                                         
00731                                                 // Turn debuf off
00732                                                 bool turn_debug_back_on = false;
00733                                                 if(debug_slices_)
00734                                                 {
00735                                                         debug_slices_ = false;
00736                                                         turn_debug_back_on = true;
00737                                                 }
00738                                                 
00739                                                 // Change the visited cells coefficient...
00740                                                 double temp_visited_cells_coef = max_visited_cells_coef_;
00741                                                 max_visited_cells_coef_ = 0.0;
00742                                                 
00743                                                 nav_msgs::Path dummy_path;
00744                                                 bool found_clearing = true;
00745                                                 for(int t=0 ; t<find_clearing_sim_steps_ ; t++)
00746                                                 {
00747                                                         if(!yumiestSlice(&dummy_goal, &dummy_goal))
00748                                                         {
00749                                                                 found_clearing = false;
00750                                                                 break;
00751                                                         }
00752                                                         if(dummy_path.poses.size()==0) dummy_path.poses.push_back(dummy_goal);
00753                                                         else
00754                                                         {
00755                                                                 // Don't let it go back and forth between two points
00756                                                                 bool nearby_pose = false;
00757                                                                 std::vector<geometry_msgs::PoseStamped>::iterator pose_it;
00758                                                                 for(pose_it=dummy_path.poses.begin() ; pose_it!=dummy_path.poses.end() ; pose_it++)
00759                                                                 {
00760                                                                         double dx = pose_it->pose.position.x - dummy_goal.pose.position.x;
00761                                                                         double dy = pose_it->pose.position.y - dummy_goal.pose.position.y;
00762                                                                         if(sqrt((dx*dx+dy*dy)) < pie_radius_*0.5)
00763                                                                         {
00764                                                                                 nearby_pose = true;
00765                                                                                 break;
00766                                                                         }
00767                                                                 }
00768                                                                 if(nearby_pose)
00769                                                                 {
00770                                                                         found_clearing = false;
00771                                                                         break;
00772                                                                 }
00773                                                                 else
00774                                                                 {
00775                                                                         dummy_path.poses.push_back(dummy_goal);
00776                                                                 }
00777                                                         }
00778                                                 }
00779                                                 dummy_path.poses.clear();
00780                                                 
00781                                                 // And then put it back!
00782                                                 max_visited_cells_coef_ = temp_visited_cells_coef;
00783                                                 
00784                                                 // Turn debug back on
00785                                                 if(turn_debug_back_on) debug_slices_ = true;
00786                                         
00787                                                 // Yay we found a suitable goal!
00788                                                 if(found_clearing)
00789                                                 {
00790                                                         goal->header.stamp = ros::Time::now();
00791                                                         goal->header.frame_id = global_frame_id_;
00792                                                         goal->pose.position.x = new_cell.x + map_.info.resolution/2;
00793                                                         goal->pose.position.y = new_cell.y + map_.info.resolution/2;
00794                                                         goal->pose.orientation = tf::createQuaternionMsgFromYaw(atan2(goal->pose.position.y - robot_pose_.pose.position.y, goal->pose.position.x - robot_pose_.pose.position.x));
00795                                                         
00796                                                         if(debug_slices_)
00797                                                         {
00798                                                                 visualization_msgs::Marker marker;
00799                                                                 marker.header.frame_id = "/map";
00800                                                                 marker.header.stamp = ros::Time::now();
00801                                                                 marker.ns = "pp_goal";
00802                                                                 marker.id = 0;
00803                                                                 marker.type = visualization_msgs::Marker::ARROW;
00804                                                                 marker.action = visualization_msgs::Marker::ADD;
00805                                                                 marker.pose.position.x = goal->pose.position.x;
00806                                                                 marker.pose.position.y = goal->pose.position.y;
00807                                                                 marker.pose.position.z = 0.2;
00808                                                                 marker.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(0.0, M_PI/2, 0.0);
00809                                                                 marker.scale.x = 0.19;
00810                                                                 marker.scale.y = 0.5;
00811                                                                 marker.scale.z = 0.5;
00812                                                                 marker.color.a = 1.0;
00813                                                                 marker.color.r = 1.0;
00814                                                                 marker.color.g = 1.0;
00815                                                                 marker.color.b = 0.0;
00816                                                                 debug_goal_pub_.publish(marker);
00817                                                         }
00818                                                         
00819                                                         if(multi_robot_)
00820                                                         {
00821                                                                 recovery_cells.cells.cells.empty();
00822                                                                 recovery_pub_.publish(recovery_cells);
00823                                                         }
00824                                                         return true;
00825                                                 }
00826                                         }
00827                                         
00828                                         // 7. It was not a match, bag and tag
00829                                         next_cells.push_back(new_cell);
00830                                 }
00831                         }
00832                 }
00833                 
00834                 if(next_cells.size()==0)
00835                 {
00836                         if(multi_robot_)
00837                         {
00838                                 recovery_cells.cells.cells.empty();
00839                                 recovery_pub_.publish(recovery_cells);
00840                                 
00841                                 explorationComplete();
00842                         }
00843                         return false;
00844                 }
00845                 
00846                 if(multi_robot_)
00847                 {
00848                         std::vector<geometry_msgs::Point>::iterator recovery_cell_it;
00849                         for(recovery_cell_it = current_cells.begin() ; recovery_cell_it != current_cells.end() ; recovery_cell_it++) recovery_cells.cells.cells.push_back(*recovery_cell_it);
00850                         
00851                         recovery_cells.cells.header.stamp = ros::Time::now();
00852                         
00853                         recovery_pub_.publish(recovery_cells);
00854                 }
00855                 
00856                 if(debug_slices_)
00857                 {
00858                         std::vector<geometry_msgs::Point>::iterator debug_cell_it;
00859                         for(debug_cell_it = current_cells.begin() ; debug_cell_it != current_cells.end() ; debug_cell_it++) debug_cells.cells.push_back(*debug_cell_it);
00860                         
00861                         debug_pub_.publish(debug_cells);
00862                         ros::Duration(0.1).sleep();
00863                 }
00864                 
00865                 last_cells = current_cells;
00866                 current_cells = next_cells;
00867         }
00868 }
00869 
00870 
00871 // ******* Helper functions *******
00872 
00873 bool particle_plume::PPExplorer::testCell(geometry_msgs::Point * cell, std::vector<geometry_msgs::Point> * cells)
00874 {
00875         std::vector<geometry_msgs::Point>::iterator cell_it;
00876         
00877         for(cell_it = cells->begin() ; cell_it != cells->end() ; cell_it++)
00878         {
00879                 if(cell->x == cell_it->x && cell->y == cell_it->y) return true;
00880         }
00881         return false;
00882 }
00883 
00884 void particle_plume::PPExplorer::setGoal(geometry_msgs::PoseStamped * goal)
00885 {
00886         goal->header.stamp = ros::Time::now();
00887         goal->header.frame_id = global_frame_id_;
00888         goal->pose = best_slices_.front().goal;
00889 
00890         if(debug_slices_)
00891         {
00892                 visualization_msgs::Marker marker;
00893                 marker.header.frame_id = "/map";
00894                 marker.header.stamp = ros::Time::now();
00895                 marker.ns = "pp_goal";
00896                 marker.id = 0;
00897                 marker.type = visualization_msgs::Marker::ARROW;
00898                 marker.action = visualization_msgs::Marker::ADD;
00899                 marker.pose.position.x = goal->pose.position.x;
00900                 marker.pose.position.y = goal->pose.position.y;
00901                 marker.pose.position.z = 0.2;
00902                 marker.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(0.0, M_PI/2, 0.0);
00903                 marker.scale.x = 0.19;
00904                 marker.scale.y = 0.5;
00905                 marker.scale.z = 0.5;
00906                 marker.color.a = 1.0;
00907                 marker.color.r = 1.0;
00908                 marker.color.g = 1.0;
00909                 marker.color.b = 0.0;
00910                 debug_goal_pub_.publish(marker);
00911         }
00912 }
00913 
00914 bool particle_plume::PPExplorer::isInsidePieRadius(double x, double y, geometry_msgs::PoseStamped * robot)
00915 {
00916         return sqrt((x - robot->pose.position.x)*(x - robot->pose.position.x) + (y - robot->pose.position.y)*(y - robot->pose.position.y)) <= pie_radius_;
00917 }
00918 
00919 bool particle_plume::PPExplorer::isInsideTheMap(int x, int y)
00920 {
00921         return (x<=map_.info.width-1 && x>=0 && y<=map_.info.height-1 && y>=0);
00922 }
00923 
00924 bool particle_plume::PPExplorer::isViableGlobalMapCell(int x, int y)
00925 {
00926         if(!isInsideTheMap(x,y)) return false;
00927         
00928         if(map_.data[y*map_.info.width+x] != 0) return false;
00929                                         
00930         int cir = ceil(inflation_radius_/map_.info.resolution)+1;
00931         for(int i=(x-cir<0 ? 0 : x-cir) ; i<(x+cir>map_.info.width ? map_.info.width : x+cir) ; i++)
00932         {
00933                 for(int j=(y-cir<0 ? 0 : y-cir) ; j<(y+cir>map_.info.height ? map_.info.height : y+cir) ; j++)
00934                 {
00935                         if(map_.data[j*map_.info.width+i] != 0 && sqrt((x-i)*(x-i)+(y-j)*(y-j)) <= (float)cir-1.0)
00936                         {
00937                                 return false;
00938                         }
00939                 }
00940         }
00941         return true;
00942 }
00943 
00944 bool particle_plume::PPExplorer::isViableLocalMapGoal(double x, double y)
00945 {
00946         std::vector<geometry_msgs::Point>::iterator obstacle;
00947         
00948         for(obstacle = obstacles_.cells.begin() ; obstacle != obstacles_.cells.end() ; obstacle++)
00949         {
00950                 if(x >= obstacle->x && x <= obstacle->x+obstacles_.cell_width && y >= obstacle->y && y <= obstacle->y+obstacles_.cell_height)
00951                         return false;
00952         }
00953         
00954         for(obstacle = inflated_.cells.begin() ; obstacle != inflated_.cells.end() ; obstacle++)
00955         {
00956                 if(x >= obstacle->x && x <= obstacle->x+obstacles_.cell_width && y >= obstacle->y && y <= obstacle->y+obstacles_.cell_height)
00957                         return false;
00958         }
00959         return true;    
00960 }
00961 
00962 double particle_plume::PPExplorer::getExploredArea()
00963 {
00964         int map_cell_count = 0;
00965 
00966         for(int i=0 ; i<map_.info.width ; i++)
00967         {
00968                 for(int j=0 ; j<map_.info.height ; j++)
00969                 {
00970                         if(map_.data[j*map_.info.width+i] == 0) map_cell_count++;
00971                 }
00972         }
00973         
00974         return (cells_.cell_height*cells_.cell_width*cells_.cells.size())/(map_.info.resolution*map_.info.resolution*map_cell_count);
00975 }
00976 
00977 void particle_plume::PPExplorer::explorationComplete()
00978 {
00979         pp_explorer::StringIdentified finished_msg;
00980         finished_msg.sender = unique_id_;
00981         finished_msg.string.data = "Exploration complete";
00982         chatter_pub_.publish(finished_msg);
00983 }
00984 
00985 // EOF
00986 


pp_explorer
Author(s): Gonçalo Cabrita and Pedro Sousa
autogenerated on Mon Jan 6 2014 11:27:11