costmap_2d_ros.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2008, Willow Garage, Inc.
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 Willow Garage 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: Eitan Marder-Eppstein
00036  *********************************************************************/
00037 #include <costmap_2d/costmap_2d_ros.h>
00038 
00039 #include <ros/console.h>
00040 
00041 #include <sensor_msgs/point_cloud_conversion.h>
00042 
00043 #include <map>
00044 #include <sstream>
00045 
00046 #include <boost/tokenizer.hpp>
00047 #include <boost/foreach.hpp>
00048 
00049 #include <XmlRpc.h>
00050 #include <costmap_2d/voxel_costmap_2d.h>
00051 #include <costmap_2d/VoxelGrid.h>
00052 #include <limits>
00053 
00054 using namespace std;
00055 
00056 namespace costmap_2d {
00057 
00058   double sign(double x){
00059     return x < 0.0 ? -1.0 : (x > 0.0 ? 1.0 : 0.0);
00060   }
00061 
00062   Costmap2DROS::Costmap2DROS(std::string name, tf::TransformListener& tf) : name_(name), tf_(tf), costmap_(NULL), 
00063                              map_update_thread_(NULL), costmap_publisher_(NULL), stop_updates_(false), 
00064                              initialized_(true), stopped_(false), map_update_thread_shutdown_(false), 
00065                              save_debug_pgm_(false), map_initialized_(false), costmap_initialized_(false), 
00066                              robot_stopped_(false), setup_(false){
00067     ros::NodeHandle private_nh("~/" + name);
00068     ros::NodeHandle g_nh;
00069 
00070     //get our tf prefix
00071     ros::NodeHandle prefix_nh;
00072     tf_prefix_ = tf::getPrefixParam(prefix_nh);
00073 
00074     std::string map_type;
00075     private_nh.param("map_type", map_type, std::string("voxel"));
00076 
00077     private_nh.param("publish_voxel_map", publish_voxel_, false);
00078 
00079     if(publish_voxel_ && map_type == "voxel")
00080       voxel_pub_ = private_nh.advertise<costmap_2d::VoxelGrid>("voxel_grid", 1);
00081     else
00082       publish_voxel_ = false;
00083 
00084     std::string topics_string;
00085     //get the topics that we'll subscribe to from the parameter server
00086     private_nh.param("observation_sources", topics_string, std::string(""));
00087     ROS_INFO("Subscribed to Topics: %s", topics_string.c_str());
00088 
00089     private_nh.param("global_frame", global_frame_, std::string("/map"));
00090     //make sure that we set the global frame appropriately based on the tf_prefix
00091     global_frame_ = tf::resolve(tf_prefix_, global_frame_);
00092 
00093     private_nh.param("robot_base_frame", robot_base_frame_, std::string("base_link"));
00094     //make sure that we set the base frame appropriately based on the tf_prefix
00095     robot_base_frame_ = tf::resolve(tf_prefix_, robot_base_frame_);
00096 
00097     //check if the user wants to save pgms of the costmap for debugging
00098     private_nh.param("save_debug_pgm", save_debug_pgm_, false);
00099 
00100     bool static_map;
00101     unsigned int map_width, map_height;
00102     double map_resolution;
00103     double map_origin_x, map_origin_y;
00104 
00105     private_nh.param("static_map", static_map, true);
00106 
00107     //check if we want a rolling window version of the costmap
00108     private_nh.param("rolling_window", rolling_window_, false);
00109 
00110     double map_width_meters, map_height_meters;
00111     std::string map_topic;
00112     private_nh.param("map_topic", map_topic, std::string("map"));
00113     private_nh.param("width", map_width_meters, 10.0);
00114     private_nh.param("height", map_height_meters, 10.0);
00115     private_nh.param("resolution", map_resolution, 0.05);
00116     private_nh.param("origin_x", map_origin_x, 0.0);
00117     private_nh.param("origin_y", map_origin_y, 0.0);
00118     map_width = (unsigned int)(map_width_meters / map_resolution);
00119     map_height = (unsigned int)(map_height_meters / map_resolution);
00120 
00121     if(static_map){
00122       //we'll subscribe to the latched topic that the map server uses
00123       ROS_INFO("Requesting the map...\n");
00124       map_sub_ = g_nh.subscribe(map_topic, 1, &Costmap2DROS::incomingMap, this);
00125 
00126       ros::Rate r(1.0);
00127       while(!map_initialized_ && ros::ok()){
00128         ros::spinOnce();
00129         ROS_INFO("Still waiting on map...\n");
00130         r.sleep();
00131       }
00132 
00133       //check if the user has set any parameters that will be overwritten
00134       bool user_map_params = false;
00135       user_map_params |= private_nh.hasParam("width");
00136       user_map_params |= private_nh.hasParam("height");
00137       user_map_params |= private_nh.hasParam("resolution");
00138       user_map_params |= private_nh.hasParam("origin_x");
00139       user_map_params |= private_nh.hasParam("origin_y");
00140 
00141       if(user_map_params)
00142         ROS_WARN("You have set map parameters, but also requested to use the static map. Your parameters will be overwritten by those given by the map server");
00143 
00144       {
00145         //lock just in case something weird is going on with the compiler or scheduler
00146         boost::recursive_mutex::scoped_lock lock(map_data_lock_);
00147         map_width = (unsigned int)map_meta_data_.width;
00148         map_height = (unsigned int)map_meta_data_.height;
00149         map_resolution = map_meta_data_.resolution;
00150         map_origin_x = map_meta_data_.origin.position.x;
00151         map_origin_y = map_meta_data_.origin.position.y;
00152 
00153         ROS_INFO("Received a %d X %d map at %f m/pix\n",
00154             map_width, map_height, map_resolution);
00155       }
00156 
00157     }
00158 
00159     ros::Time last_error = ros::Time::now();
00160     std::string tf_error;
00161     //we need to make sure that the transform between the robot base frame and the global frame is available
00162     while(ros::ok() && !tf_.waitForTransform(global_frame_, robot_base_frame_, ros::Time(), ros::Duration(0.1), ros::Duration(0.01), &tf_error)){
00163       ros::spinOnce();
00164       if(last_error + ros::Duration(5.0) < ros::Time::now()){
00165         ROS_WARN("Waiting on transform from %s to %s to become available before running costmap, tf error: %s", 
00166             robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
00167         last_error = ros::Time::now();
00168       }
00169     }
00170 
00171     private_nh.param("transform_tolerance", transform_tolerance_, 0.3);
00172 
00173     //now we need to split the topics based on whitespace which we can use a stringstream for
00174     std::stringstream ss(topics_string);
00175 
00176     double raytrace_range = 3.0;
00177     double obstacle_range = 2.5;
00178 
00179     std::string source;
00180     while(ss >> source){
00181       ros::NodeHandle source_node(private_nh, source);
00182       //get the parameters for the specific topic
00183       double observation_keep_time, expected_update_rate, min_obstacle_height, max_obstacle_height;
00184       std::string topic, sensor_frame, data_type;
00185       bool inf_is_valid;
00186       source_node.param("topic", topic, source);
00187       source_node.param("sensor_frame", sensor_frame, std::string(""));
00188       source_node.param("observation_persistence", observation_keep_time, 0.0);
00189       source_node.param("expected_update_rate", expected_update_rate, 0.0);
00190       source_node.param("data_type", data_type, std::string("PointCloud"));
00191       source_node.param("min_obstacle_height", min_obstacle_height, 0.0);
00192       source_node.param("max_obstacle_height", max_obstacle_height, 2.0);
00193       source_node.param("inf_is_valid", inf_is_valid, false);
00194 
00195       if(!(data_type == "PointCloud2" || data_type == "PointCloud" || data_type == "LaserScan")){
00196         ROS_FATAL("Only topics that use point clouds or laser scans are currently supported");
00197         throw std::runtime_error("Only topics that use point clouds or laser scans are currently supported");
00198       }
00199 
00200 
00201       bool clearing, marking;
00202       source_node.param("clearing", clearing, false);
00203       source_node.param("marking", marking, true);
00204 
00205       std::string raytrace_range_param_name, obstacle_range_param_name;
00206       double source_raytrace_range, source_obstacle_range;
00207 
00208       //get the obstacle range for the sensor
00209       if(!source_node.searchParam("obstacle_range", obstacle_range_param_name))
00210         source_obstacle_range = 2.5;
00211       else
00212         source_node.param(obstacle_range_param_name, source_obstacle_range, 2.5);
00213 
00214       //get the raytrace range for the sensor
00215       if(!source_node.searchParam("raytrace_range", raytrace_range_param_name))
00216         source_raytrace_range = 3.0;
00217       else
00218         source_node.param(raytrace_range_param_name, source_raytrace_range, 3.0);
00219 
00220 
00221       //keep track of the maximum raytrace range for the costmap to be able to inflate efficiently
00222       raytrace_range = std::max(raytrace_range, source_raytrace_range);
00223       obstacle_range = std::max(obstacle_range, source_obstacle_range);
00224 
00225       ROS_DEBUG("Creating an observation buffer for source %s, topic %s, frame %s", source.c_str(), topic.c_str(), sensor_frame.c_str());
00226 
00227       //create an observation buffer
00228       observation_buffers_.push_back(boost::shared_ptr<ObservationBuffer>(new ObservationBuffer(topic, observation_keep_time, 
00229               expected_update_rate, min_obstacle_height, max_obstacle_height, source_obstacle_range, source_raytrace_range, tf_, global_frame_, sensor_frame, transform_tolerance_)));
00230 
00231       //check if we'll add this buffer to our marking observation buffers
00232       if(marking)
00233         marking_buffers_.push_back(observation_buffers_.back());
00234 
00235       //check if we'll also add this buffer to our clearing observation buffers
00236       if(clearing)
00237         clearing_buffers_.push_back(observation_buffers_.back());
00238 
00239       ROS_DEBUG("Created an observation buffer for source %s, topic %s, global frame: %s, expected update rate: %.2f, observation persistence: %.2f", 
00240           source.c_str(), topic.c_str(), global_frame_.c_str(), expected_update_rate, observation_keep_time);
00241 
00242       //create a callback for the topic
00243       if(data_type == "LaserScan"){
00244         boost::shared_ptr<message_filters::Subscriber<sensor_msgs::LaserScan> > sub(
00245               new message_filters::Subscriber<sensor_msgs::LaserScan>(g_nh, topic, 50));
00246 
00247         boost::shared_ptr<tf::MessageFilter<sensor_msgs::LaserScan> > filter(
00248             new tf::MessageFilter<sensor_msgs::LaserScan>(*sub, tf_, global_frame_, 50));
00249 
00250         if( inf_is_valid )
00251         {
00252           filter->registerCallback(boost::bind(&Costmap2DROS::laserScanValidInfCallback, this, _1, observation_buffers_.back()));
00253         }
00254         else
00255         {
00256           filter->registerCallback(boost::bind(&Costmap2DROS::laserScanCallback, this, _1, observation_buffers_.back()));
00257         }
00258         observation_subscribers_.push_back(sub);
00259         observation_notifiers_.push_back(filter);
00260 
00261         observation_notifiers_.back()->setTolerance(ros::Duration(0.05));
00262       }
00263       else if(data_type == "PointCloud"){
00264         if( inf_is_valid )
00265         {
00266           ROS_WARN("costmap_2d: inf_is_valid option is not applicable to PointCloud observations.");
00267         }
00268         boost::shared_ptr<message_filters::Subscriber<sensor_msgs::PointCloud> > sub(
00269               new message_filters::Subscriber<sensor_msgs::PointCloud>(g_nh, topic, 50));
00270 
00271         boost::shared_ptr<tf::MessageFilter<sensor_msgs::PointCloud> > filter(
00272             new tf::MessageFilter<sensor_msgs::PointCloud>(*sub, tf_, global_frame_, 50));
00273         filter->registerCallback(boost::bind(&Costmap2DROS::pointCloudCallback, this, _1, observation_buffers_.back()));
00274 
00275         observation_subscribers_.push_back(sub);
00276         observation_notifiers_.push_back(filter);
00277       }
00278       else{
00279         if( inf_is_valid )
00280         {
00281           ROS_WARN("costmap_2d: inf_is_valid option is not applicable to PointCloud2 observations.");
00282         }
00283         boost::shared_ptr<message_filters::Subscriber<sensor_msgs::PointCloud2> > sub(
00284               new message_filters::Subscriber<sensor_msgs::PointCloud2>(g_nh, topic, 50));
00285 
00286         boost::shared_ptr<tf::MessageFilter<sensor_msgs::PointCloud2> > filter(
00287             new tf::MessageFilter<sensor_msgs::PointCloud2>(*sub, tf_, global_frame_, 50));
00288         filter->registerCallback(boost::bind(&Costmap2DROS::pointCloud2Callback, this, _1, observation_buffers_.back()));
00289 
00290         observation_subscribers_.push_back(sub);
00291         observation_notifiers_.push_back(filter);
00292       }
00293 
00294       if(sensor_frame != ""){
00295         std::vector<std::string> target_frames;
00296         target_frames.push_back(global_frame_);
00297         target_frames.push_back(sensor_frame);
00298         observation_notifiers_.back()->setTargetFrames(target_frames);
00299       }
00300 
00301     }
00302 
00303     double inscribed_radius, circumscribed_radius, inflation_radius;
00304     inscribed_radius = 0.46;
00305 
00306     if(private_nh.hasParam("robot_radius")){
00307       private_nh.param("robot_radius", inscribed_radius, 0.46);
00308     }
00309 
00310     circumscribed_radius = inscribed_radius;
00311     private_nh.param("inflation_radius", inflation_radius, 0.55);
00312 
00313     //load the robot footprint from the parameter server if its available in the global namespace
00314     footprint_spec_ = loadRobotFootprint(private_nh, inscribed_radius, circumscribed_radius);
00315 
00316     if(footprint_spec_.size() > 2){
00317       //now we need to compute the inscribed/circumscribed radius of the robot from the footprint specification
00318       double min_dist = std::numeric_limits<double>::max();
00319       double max_dist = 0.0;
00320 
00321       for(unsigned int i = 0; i < footprint_spec_.size() - 1; ++i){
00322         //check the distance from the robot center point to the first vertex
00323         double vertex_dist = distance(0.0, 0.0, footprint_spec_[i].x, footprint_spec_[i].y);
00324         double edge_dist = distanceToLine(0.0, 0.0, footprint_spec_[i].x, footprint_spec_[i].y, footprint_spec_[i+1].x, footprint_spec_[i+1].y);
00325         min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
00326         max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
00327       }
00328 
00329       //we also need to do the last vertex and the first vertex
00330       double vertex_dist = distance(0.0, 0.0, footprint_spec_.back().x, footprint_spec_.back().y);
00331       double edge_dist = distanceToLine(0.0, 0.0, footprint_spec_.back().x, footprint_spec_.back().y, footprint_spec_.front().x, footprint_spec_.front().y);
00332       min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
00333       max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
00334 
00335       inscribed_radius = min_dist;
00336       circumscribed_radius = max_dist;
00337     }
00338 
00339     if(inscribed_radius > inflation_radius || circumscribed_radius > inflation_radius){
00340       ROS_WARN("You have set an inflation radius (%f) that is less than the inscribed (%f) or circumscribed (%f) radius of the robot. This is dangerous and could casue the robot to hit obstacles. Please change your inflation radius setting appropriately.", inflation_radius, inscribed_radius, circumscribed_radius);
00341     }
00342 
00343     double max_obstacle_height;
00344     private_nh.param("max_obstacle_height", max_obstacle_height, 2.0);
00345 
00346     double cost_scale;
00347     private_nh.param("cost_scaling_factor", cost_scale, 10.0);
00348 
00349     int temp_lethal_threshold, temp_unknown_cost_value;
00350     private_nh.param("lethal_cost_threshold", temp_lethal_threshold, int(100));
00351     private_nh.param("unknown_cost_value", temp_unknown_cost_value, int(0));
00352 
00353     unsigned char lethal_threshold = std::max(std::min(temp_lethal_threshold, 255), 0);
00354     unsigned char unknown_cost_value = std::max(std::min(temp_unknown_cost_value, 255), 0);
00355 
00356     bool track_unknown_space;
00357     private_nh.param("track_unknown_space", track_unknown_space, false);
00358 
00359     struct timeval start, end;
00360     double start_t, end_t, t_diff;
00361     gettimeofday(&start, NULL);
00362     if(map_type == "costmap"){
00363       //make sure to lock the map data
00364       boost::recursive_mutex::scoped_lock lock(map_data_lock_);
00365       costmap_ = new Costmap2D(map_width, map_height,
00366           map_resolution, map_origin_x, map_origin_y, inscribed_radius, circumscribed_radius, inflation_radius,
00367           obstacle_range, max_obstacle_height, raytrace_range, cost_scale, input_data_, lethal_threshold, track_unknown_space, unknown_cost_value);
00368     }
00369     else if(map_type == "voxel"){
00370 
00371       int z_voxels;
00372       private_nh.param("z_voxels", z_voxels, 10);
00373 
00374       double z_resolution, map_origin_z;
00375       private_nh.param("z_resolution", z_resolution, 0.2);
00376       private_nh.param("origin_z", map_origin_z, 0.0);
00377 
00378       int unknown_threshold, mark_threshold;
00379       private_nh.param("unknown_threshold", unknown_threshold, z_voxels);
00380       private_nh.param("mark_threshold", mark_threshold, 0);
00381 
00382       if(!(z_voxels >= 0 && unknown_threshold >= 0 && mark_threshold >= 0)){
00383         ROS_FATAL("Values for z_voxels, unknown_threshold, and mark_threshold parameters must be positive.");
00384         throw std::runtime_error("Values for z_voxels, unknown_threshold, and mark_threshold parameters must be positive.");
00385       }
00386 
00387       boost::recursive_mutex::scoped_lock lock(map_data_lock_);
00388       costmap_ = new VoxelCostmap2D(map_width, map_height, z_voxels, map_resolution, z_resolution, map_origin_x, map_origin_y, map_origin_z, inscribed_radius,
00389           circumscribed_radius, inflation_radius, obstacle_range, raytrace_range, cost_scale, input_data_, lethal_threshold, unknown_threshold, mark_threshold,
00390           unknown_cost_value);
00391     }
00392     else{
00393       ROS_FATAL("Unsuported map type");
00394       throw std::runtime_error("Unsuported map type");
00395     }
00396 
00397     gettimeofday(&end, NULL);
00398     start_t = start.tv_sec + double(start.tv_usec) / 1e6;
00399     end_t = end.tv_sec + double(end.tv_usec) / 1e6;
00400     t_diff = end_t - start_t;
00401     ROS_DEBUG("New map construction time: %.9f", t_diff);
00402 
00403     double map_publish_frequency;
00404     private_nh.param("publish_frequency", map_publish_frequency, 0.0);
00405 
00406     //create a publisher for the costmap if desired
00407     costmap_publisher_ = new Costmap2DPublisher(private_nh, map_publish_frequency, global_frame_);
00408     if(costmap_publisher_->active()){
00409       std::vector<geometry_msgs::Point> oriented_footprint;
00410       getOrientedFootprint(oriented_footprint);
00411       tf::Stamped<tf::Pose> global_pose;
00412       getRobotPose(global_pose);
00413       costmap_publisher_->updateCostmapData(*costmap_, oriented_footprint, global_pose);
00414     }
00415 
00416     //create a thread to handle updating the map
00417     double map_update_frequency;
00418     private_nh.param("update_frequency", map_update_frequency, 5.0);
00419     map_update_thread_ = new boost::thread(boost::bind(&Costmap2DROS::mapUpdateLoop, this, map_update_frequency));
00420 
00421     costmap_initialized_ = true;
00422     
00423     //Create a time r to check if the robot is moving
00424     timer_ = private_nh.createTimer(ros::Duration(.1), &Costmap2DROS::movementCB, this);
00425     dsrv_ = new dynamic_reconfigure::Server<Costmap2DConfig>(ros::NodeHandle("~/"+name));
00426     dynamic_reconfigure::Server<Costmap2DConfig>::CallbackType cb = boost::bind(&Costmap2DROS::reconfigureCB, this, _1, _2);
00427     dsrv_->setCallback(cb);
00428   }
00429 
00430   void Costmap2DROS::movementCB(const ros::TimerEvent &event) {
00431     //don't allow configuration to happen while this check occurs
00432     boost::recursive_mutex::scoped_lock mcl(configuration_mutex_);
00433 
00434     tf::Stamped<tf::Pose> new_pose;
00435 
00436     if(!getRobotPose(new_pose)){
00437       ROS_WARN_THROTTLE(1.0, "Could not get robot pose, cancelling reconfiguration");
00438       robot_stopped_ = false;
00439     }
00440     //make sure that the robot is not moving 
00441     else if(fabs((old_pose_.getOrigin() - new_pose.getOrigin()).length()) < 1e-3 && fabs(old_pose_.getRotation().angle(new_pose.getRotation())) < 1e-3)
00442     {
00443       old_pose_ = new_pose;
00444       robot_stopped_ = true;
00445     }
00446     else
00447     {
00448       old_pose_ = new_pose; 
00449       robot_stopped_ = false;
00450     }
00451   }
00452 
00453   void Costmap2DROS::reconfigureCB(Costmap2DConfig &config, uint32_t level) {
00454     ros::NodeHandle nh = ros::NodeHandle("~/"+name_);
00455     
00456     if(setup_ && config.restore_defaults) {
00457       config = default_config_;
00458       //in case someone set restore_defaults on the parameter server, avoid looping
00459       config.restore_defaults = false;
00460     }
00461 
00462     //change the configuration defaults to match the param server
00463     if(!setup_) {
00464       boost::recursive_mutex::scoped_lock rel(configuration_mutex_);
00465 
00466       last_config_ = config;
00467       maptype_config_ = config;
00468       default_config_ = config;
00469 
00470       setup_ = true;
00471     }
00472     else if(setup_ && robot_stopped_) {
00473       //lock before modifying anything
00474       boost::recursive_mutex::scoped_lock rel(configuration_mutex_);
00475 
00476       transform_tolerance_ = config.transform_tolerance;
00477 
00478       // shutdown and restart the map update loop at a new frequency
00479       map_update_thread_shutdown_ = true;
00480       map_update_thread_->join();
00481       boost::mutex::scoped_lock ml(map_update_mutex_);
00482 
00483       //check and configure a new robot footprint
00484       //accepts a lis of points formatted [[x1, y1],[x2,y2],....[xn,yn]]
00485       string footprint_string = config.footprint;
00486       boost::erase_all(footprint_string, " ");
00487       boost::char_separator<char> sep("[]");
00488       boost::tokenizer<boost::char_separator<char> > tokens(footprint_string, sep);
00489       vector<string> points(tokens.begin(), tokens.end());
00490 
00491       //parse the input string into points
00492       vector<geometry_msgs::Point> footprint_spec;
00493       bool circular = false;
00494       bool valid_foot = true;
00495 
00496       if(points.size() >= 5) {
00497         BOOST_FOREACH(string t, tokens) {
00498           if (t != ",") {
00499             boost::erase_all(t, " ");
00500             boost::char_separator<char> pt_sep(",");
00501             boost::tokenizer<boost::char_separator<char> > pt_tokens(t, pt_sep);
00502             std::vector<string> point(pt_tokens.begin(), pt_tokens.end());
00503 
00504             if(point.size() != 2) {
00505               ROS_WARN("Each point must have exactly 2 coordinates");
00506               valid_foot = false;
00507               break;
00508             }
00509 
00510             vector<double>tmp_pt;
00511             BOOST_FOREACH(string p, point) {
00512               istringstream iss(p);
00513               double temp;
00514               if(iss >> temp) {
00515                 tmp_pt.push_back(temp);
00516               }
00517               else {
00518                 ROS_WARN("Each coordinate must convert to a double.");
00519                 valid_foot = false;
00520                 break;
00521               }
00522             }
00523             if(!valid_foot)
00524               break;
00525 
00526             geometry_msgs::Point pt;
00527             pt.x = tmp_pt[0];
00528             pt.y = tmp_pt[1];
00529 
00530             footprint_spec.push_back(pt);
00531           }
00532         }
00533         if (valid_foot) {
00534           footprint_spec_ = footprint_spec;
00535         }
00536       }
00537       //clear the footprint for a circular robot
00538       else if((footprint_string == "[]" || footprint_string == "") && config.robot_radius > 0.0) {
00539         footprint_spec_ = vector<geometry_msgs::Point>();
00540         circular = true;
00541       }
00542       else if(config.footprint != last_config_.footprint) {
00543         ROS_ERROR("You must specify at least three points for the robot footprint, reverting to previous footprint");
00544         config.footprint = last_config_.footprint;
00545       }
00546       
00547       if(!valid_foot) {
00548         ROS_FATAL("The footprint must be specified as a list of at least three points, you specified %s", footprint_string.c_str());
00549         config.footprint = last_config_.footprint;
00550       }
00551 
00552       double inscribed_radius, circumscribed_radius;
00553       inscribed_radius = config.robot_radius;
00554       circumscribed_radius = inscribed_radius;
00555 
00556       //if we have a footprint, recaclulate the radii
00557       if(footprint_spec_.size() > 2){
00558         //now we need to compute the inscribed/circumscribed radius of the robot from the footprint specification
00559         double min_dist = std::numeric_limits<double>::max();
00560         double max_dist = 0.0;
00561 
00562         for(unsigned int i = 0; i < footprint_spec_.size() - 1; ++i){
00563           //check the distance from the robot center point to the first vertex
00564           double vertex_dist = distance(0.0, 0.0, footprint_spec_[i].x, footprint_spec_[i].y);
00565           double edge_dist = distanceToLine(0.0, 0.0, footprint_spec_[i].x, footprint_spec_[i].y, footprint_spec_[i+1].x, footprint_spec_[i+1].y);
00566           min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
00567           max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
00568         }
00569 
00570 
00571         //we also need to do the last vertex and the first vertex
00572         double vertex_dist = distance(0.0, 0.0, footprint_spec_.back().x, footprint_spec_.back().y);
00573         double edge_dist = distanceToLine(0.0, 0.0, footprint_spec_.back().x, footprint_spec_.back().y, footprint_spec_.front().x, footprint_spec_.front().y);
00574         min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
00575         max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
00576 
00577         inscribed_radius = min_dist;
00578         circumscribed_radius = max_dist;
00579       }
00580       if(inscribed_radius > config.inflation_radius || circumscribed_radius > config.inflation_radius){
00581         ROS_WARN("You have set an inflation radius that is less than the inscribed and circumscribed radii of the robot. This is dangerous and could casue the robot to hit obstacles. Please change your inflation radius setting appropraitely.");
00582       }
00583 
00584       //if both static map and rolling window are set, we'll use static map and warn
00585       if(config.static_map && config.rolling_window){
00586         ROS_WARN("You have selected both rolling window and static map, using static_map");
00587         config.rolling_window = false;
00588       }
00589 
00590       //check to see if the map needs to be regenerated
00591       //TODO: Make sure that this check actually does the right things
00592       bool user_params = false;
00593       bool map_params = false;
00594       if(config.max_obstacle_height != last_config_.max_obstacle_height ||
00595          config.max_obstacle_range != last_config_.max_obstacle_range ||
00596          config.raytrace_range != last_config_.raytrace_range ||
00597          config.cost_scaling_factor != last_config_.cost_scaling_factor ||
00598          config.inflation_radius != last_config_.inflation_radius ||
00599          config.footprint != last_config_.footprint ||
00600          config.robot_radius != last_config_.robot_radius ||
00601          config.static_map != last_config_.static_map ||
00602          config.rolling_window != last_config_.rolling_window ||
00603          config.unknown_cost_value != last_config_.unknown_cost_value ||
00604          config.width != last_config_.width ||
00605          config.height != last_config_.height ||
00606          config.resolution != last_config_.resolution ||
00607          config.origin_x != last_config_.origin_x ||
00608          config.origin_y != last_config_.origin_y ||
00609          config.lethal_cost_threshold != last_config_.lethal_cost_threshold ||
00610          config.map_topic != last_config_.map_topic ||
00611          config.origin_z != last_config_.origin_z ||
00612          config.z_resolution != last_config_.z_resolution ||
00613          config.unknown_threshold != last_config_.unknown_threshold ||
00614          config.mark_threshold != last_config_.mark_threshold ||
00615          config.track_unknown_space != last_config_.track_unknown_space ||
00616          config.map_type != last_config_.map_type ||
00617          config.static_map != last_config_.static_map ||
00618          config.rolling_window != last_config_.rolling_window)
00619       {
00620         user_params = true;
00621       }
00622 
00623       if(config.width != last_config_.width ||
00624          config.height != last_config_.height ||
00625          config.resolution != last_config_.resolution ||
00626          config.origin_x != last_config_.origin_x ||
00627          config.origin_y != last_config_.origin_y)
00628       {
00629         map_params = true;
00630       }
00631 
00632       if(user_params) {
00633         //unmangle rolling_window and static map
00634         //both can be false but if one is true then the other is not
00635         if(!config.static_map && !config.rolling_window){
00636           rolling_window_ = false;
00637           static_map_ = false;
00638 
00639           if(last_config_.static_map)
00640           {
00641             map_sub_.shutdown();
00642             map_initialized_ = false;
00643           }
00644         }
00645         else if(config.static_map) {
00646           static_map_ = true;
00647           rolling_window_ = false;
00648 
00649           //we'll subscribe to the latched topic that the map server uses
00650           ros::NodeHandle g_nh;
00651           map_sub_.shutdown();
00652           map_initialized_ = false;
00653           map_sub_ = g_nh.subscribe(config.map_topic, 1, &Costmap2DROS::incomingMap, this);
00654           ROS_INFO("Requesting new map on topic %s\n", map_sub_.getTopic().c_str());
00655 
00656           ros::Rate r(1.0);
00657           while(!map_initialized_ && ros::ok()){
00658             ros::spinOnce();
00659             ROS_INFO("Still waiting on new map...\n");
00660             r.sleep();
00661           }
00662         }
00663         else if(config.rolling_window) {
00664           rolling_window_ = true;
00665           static_map_ = false;
00666 
00667           if(last_config_.static_map)
00668           {
00669             map_sub_.shutdown();
00670             map_initialized_ = false;
00671           }
00672         }
00673 
00674         //make sure to lock before we start messing with the map
00675         boost::recursive_mutex::scoped_lock mdl(map_data_lock_);
00676 
00677         if(!config.static_map && last_config_.static_map) {
00678           config.width = maptype_config_.width;
00679           config.height = maptype_config_.height;
00680           config.resolution = maptype_config_.resolution;
00681           config.origin_x = maptype_config_.origin_x;
00682           config.origin_y = maptype_config_.origin_y;
00683 
00684           maptype_config_ = last_config_;
00685         }
00686 
00687 
00688         unsigned int size_x, size_y;
00689         size_x = ceil(config.width/config.resolution);
00690         size_y = ceil(config.height/config.resolution);
00691         double resolution = config.resolution;
00692         double origin_x = config.origin_x;
00693         double origin_y = config.origin_y;
00694 
00695         if(config.static_map)
00696         {
00697           if(map_params)
00698           {
00699             ROS_WARN("You're trying to change parameters that will be overwritten since static_map is set. Your changes will have no effect");
00700           }
00701 
00702           size_x = (unsigned int)map_meta_data_.width;
00703           size_y = (unsigned int)map_meta_data_.height;
00704           resolution = map_meta_data_.resolution;
00705           origin_x = map_meta_data_.origin.position.x;
00706           origin_y = map_meta_data_.origin.position.y;
00707 
00708           config.width = size_x;
00709           config.height = size_y;
00710           config.resolution = resolution;
00711           config.origin_x = origin_x;
00712           config.origin_y = origin_y;
00713 
00714           ROS_INFO("Received a %d X %d map at %f m/pix\n",
00715               size_x, size_y, resolution);
00716         }
00717 
00718         if(config.map_type == "voxel") {
00719           unsigned int z_voxels;
00720           unsigned char lethal_threshold, unknown_cost_value;
00721           unsigned int unknown_threshold, mark_threshold;
00722 
00723           z_voxels = config.z_voxels;
00724 
00725           lethal_threshold = config.lethal_cost_threshold;
00726           unknown_threshold = config.unknown_threshold;
00727           mark_threshold = config.mark_threshold;
00728 
00729           unknown_cost_value = config.unknown_cost_value;
00730 
00731           VoxelCostmap2D *new_map = new VoxelCostmap2D(size_x, size_y, z_voxels, 
00732                          resolution, config.z_resolution, origin_x, origin_y, config.origin_z, 
00733                          inscribed_radius, circumscribed_radius, config.inflation_radius, 
00734                          config.max_obstacle_range, config.raytrace_range, config.cost_scaling_factor, 
00735                          input_data_, lethal_threshold, unknown_threshold, mark_threshold, unknown_cost_value);
00736           delete costmap_;
00737           costmap_ = new_map;
00738         }
00739         else if(config.map_type == "costmap") {
00740           unsigned char lethal_threshold, unknown_cost_value;
00741 
00742           lethal_threshold = config.lethal_cost_threshold;
00743           unknown_cost_value = config.unknown_cost_value;
00744          
00745           Costmap2D *new_map = new Costmap2D(size_x, size_y, resolution, origin_x, origin_y,
00746                   inscribed_radius, circumscribed_radius, config.inflation_radius,
00747                   config.max_obstacle_range, config.max_obstacle_height, config.raytrace_range, config.cost_scaling_factor, 
00748                   input_data_, lethal_threshold, config.track_unknown_space, unknown_cost_value);
00749           delete costmap_;
00750           costmap_ = new_map;
00751         }
00752       }
00753 
00754       if(config.map_type == "voxel" && config.publish_voxel_map) {
00755         publish_voxel_ = true;
00756         if(voxel_pub_ == NULL) {
00757           ros::NodeHandle nh("~/" + name_);
00758           voxel_pub_ = nh.advertise<costmap_2d::VoxelGrid>("voxel_grid", 1);
00759         }
00760       }
00761       else {
00762         publish_voxel_ = false;
00763         config.publish_voxel_map = false;
00764       }
00765 
00766       //make sure that nothing else is modifying the costmap while we reconfigure it
00767       boost::recursive_mutex::scoped_lock lock(lock_);
00768 
00769       //reconfigure the underlying costmap 
00770       costmap_->reconfigure(config, last_config_);
00771 
00772       if(config.publish_frequency != last_config_.publish_frequency)
00773       {
00774         double map_publish_frequency = config.publish_frequency;
00775         delete costmap_publisher_;
00776         costmap_publisher_ = new Costmap2DPublisher(ros::NodeHandle("~/"+name_), map_publish_frequency, global_frame_);
00777       }
00778 
00779       //once all configuration is done, restart the map update loop
00780       delete map_update_thread_;
00781       map_update_thread_shutdown_ = false;
00782       double map_update_frequency = config.update_frequency;
00783       map_update_thread_ = new boost::thread(boost::bind(&Costmap2DROS::mapUpdateLoop, this, map_update_frequency));
00784 
00785       last_config_ = config;
00786     }
00787     else {
00788       if(setup_)
00789       {
00790         ROS_WARN("You cannot reconfigure the costmap unless the robot is stopped");
00791       }
00792       config = last_config_;
00793     }
00794   }
00795 
00796   double Costmap2DROS::distanceToLine(double pX, double pY, double x0, double y0, double x1, double y1){
00797     double A = pX - x0;
00798     double B = pY - y0;
00799     double C = x1 - x0;
00800     double D = y1 - y0;
00801 
00802     double dot = A * C + B * D;
00803     double len_sq = C * C + D * D;
00804     double param = dot / len_sq;
00805 
00806     double xx,yy;
00807 
00808     if(param < 0)
00809     {
00810       xx = x0;
00811       yy = y0;
00812     }
00813     else if(param > 1)
00814     {
00815       xx = x1;
00816       yy = y1;
00817     }
00818     else
00819     {
00820       xx = x0 + param * C;
00821       yy = y0 + param * D;
00822     }
00823 
00824     return distance(pX,pY,xx,yy);
00825   }
00826 
00827   std::vector<geometry_msgs::Point> Costmap2DROS::loadRobotFootprint(ros::NodeHandle node, double inscribed_radius, double circumscribed_radius){
00828     std::vector<geometry_msgs::Point> footprint;
00829     geometry_msgs::Point pt;
00830     double padding;
00831 
00832     std::string padding_param, footprint_param;
00833     if(!node.searchParam("footprint_padding", padding_param))
00834       padding = 0.01;
00835     else
00836       node.param(padding_param, padding, 0.01);
00837 
00838     //grab the footprint from the parameter server if possible
00839     XmlRpc::XmlRpcValue footprint_list;
00840     std::string footprint_string;
00841     std::vector<string> footstring_list;
00842     if(node.searchParam("footprint", footprint_param)){
00843       node.getParam(footprint_param, footprint_list);
00844       if(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeString) {
00845         footprint_string = std::string(footprint_list);
00846 
00847         //if there's just an empty footprint up there, return
00848         if(footprint_string == "[]" || footprint_string == "")
00849           return footprint;
00850 
00851         boost::erase_all(footprint_string, " ");
00852 
00853         boost::char_separator<char> sep("[]");
00854         boost::tokenizer<boost::char_separator<char> > tokens(footprint_string, sep);
00855         footstring_list = std::vector<string>(tokens.begin(), tokens.end());
00856       }
00857       //make sure we have a list of lists
00858       if(!(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeArray && footprint_list.size() > 2) && !(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeString && footstring_list.size() > 5)){
00859         ROS_FATAL("The footprint must be specified as list of lists on the parameter server, %s was specified as %s", footprint_param.c_str(), std::string(footprint_list).c_str());
00860         throw std::runtime_error("The footprint must be specified as list of lists on the parameter server with at least 3 points eg: [[x1, y1], [x2, y2], ..., [xn, yn]]");
00861       }
00862 
00863       if(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeArray) {
00864         for(int i = 0; i < footprint_list.size(); ++i){
00865           //make sure we have a list of lists of size 2
00866           XmlRpc::XmlRpcValue point = footprint_list[i];
00867           if(!(point.getType() == XmlRpc::XmlRpcValue::TypeArray && point.size() == 2)){
00868             ROS_FATAL("The footprint must be specified as list of lists on the parameter server eg: [[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form");
00869             throw std::runtime_error("The footprint must be specified as list of lists on the parameter server eg: [[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form");
00870           }
00871        
00872           //make sure that the value we're looking at is either a double or an int
00873           if(!(point[0].getType() == XmlRpc::XmlRpcValue::TypeInt || point[0].getType() == XmlRpc::XmlRpcValue::TypeDouble)){
00874             ROS_FATAL("Values in the footprint specification must be numbers");
00875             throw std::runtime_error("Values in the footprint specification must be numbers");
00876           }
00877           pt.x = point[0].getType() == XmlRpc::XmlRpcValue::TypeInt ? (int)(point[0]) : (double)(point[0]);
00878           pt.x += sign(pt.x) * padding;
00879        
00880           //make sure that the value we're looking at is either a double or an int
00881           if(!(point[1].getType() == XmlRpc::XmlRpcValue::TypeInt || point[1].getType() == XmlRpc::XmlRpcValue::TypeDouble)){
00882             ROS_FATAL("Values in the footprint specification must be numbers");
00883             throw std::runtime_error("Values in the footprint specification must be numbers");
00884           }
00885           pt.y = point[1].getType() == XmlRpc::XmlRpcValue::TypeInt ? (int)(point[1]) : (double)(point[1]);
00886           pt.y += sign(pt.y) * padding;
00887        
00888           footprint.push_back(pt);
00889 
00890           node.deleteParam(footprint_param);
00891           ostringstream oss;
00892           bool first = true;
00893           BOOST_FOREACH(geometry_msgs::Point p, footprint) {
00894             if(first) {
00895               oss << "[[" << p.x << "," << p.y << "]";
00896               first = false;
00897             }
00898             else {
00899               oss << ",[" << p.x << "," << p.y << "]";
00900             }
00901           }
00902           oss << "]";
00903           node.setParam(footprint_param, oss.str().c_str());
00904           node.setParam("footprint", oss.str().c_str());
00905         }
00906       }
00907 
00908       else if(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeString) {
00909         std::vector<geometry_msgs::Point> footprint_spec;
00910         bool valid_foot = true;
00911         BOOST_FOREACH(string t, footstring_list) {
00912           if( t != "," ) {
00913             boost::erase_all(t, " ");
00914             boost::char_separator<char> pt_sep(",");
00915             boost::tokenizer<boost::char_separator<char> > pt_tokens(t, pt_sep);
00916             std::vector<string> point(pt_tokens.begin(), pt_tokens.end());
00917 
00918             if(point.size() != 2) {
00919               ROS_WARN("Each point must have exactly 2 coordinates");
00920               valid_foot = false;
00921               break;
00922             }
00923 
00924             vector<double>tmp_pt;
00925             BOOST_FOREACH(string p, point) {
00926               istringstream iss(p);
00927               double temp;
00928               if(iss >> temp) {
00929                 tmp_pt.push_back(temp);
00930               }
00931               else {
00932                 ROS_WARN("Each coordinate must convert to a double.");
00933                 valid_foot = false;
00934                 break;
00935               }
00936             }
00937             if(!valid_foot)
00938               break;
00939 
00940             geometry_msgs::Point pt;
00941             pt.x = tmp_pt[0];
00942             pt.y = tmp_pt[1];
00943             pt.x += sign(pt.x) * padding;
00944             pt.y += sign(pt.y) * padding;
00945 
00946             footprint_spec.push_back(pt);
00947           }
00948         }
00949         if (valid_foot) {
00950           footprint = footprint_spec;
00951           node.setParam("footprint", footprint_string);
00952         }
00953         else {
00954           ROS_FATAL("This footprint is not vaid it must be specified as a list of lists with at least 3 points, you specified %s", footprint_string.c_str());
00955           throw std::runtime_error("The footprint must be specified as list of lists on the parameter server with at least 3 points eg: [[x1, y1], [x2, y2], ..., [xn, yn]]");
00956         }
00957       }
00958     }
00959     return footprint;
00960   }
00961 
00962   Costmap2DROS::~Costmap2DROS(){
00963     delete dsrv_;
00964     map_update_thread_shutdown_ = true;
00965     if(map_update_thread_ != NULL){
00966       map_update_thread_->join();
00967       delete map_update_thread_;
00968     }
00969 
00970     if(costmap_publisher_ != NULL){
00971       delete costmap_publisher_;
00972     }
00973 
00974     if(costmap_ != NULL)
00975       delete costmap_;
00976   }
00977 
00978   void Costmap2DROS::start(){
00979     //check if we're stopped or just paused
00980     if(stopped_){
00981       //if we're stopped we need to re-subscribe to topics
00982       for(unsigned int i = 0; i < observation_subscribers_.size(); ++i){
00983         if(observation_subscribers_[i] != NULL)
00984           observation_subscribers_[i]->subscribe();
00985       }
00986       stopped_ = false;
00987     }
00988     for (unsigned int i=0; i < observation_buffers_.size(); ++i){
00989       if (observation_buffers_[i])
00990         observation_buffers_[i]->resetLastUpdated();
00991     } 
00992     stop_updates_ = false;
00993 
00994     //block until the costmap is re-initialized.. meaning one update cycle has run
00995     ros::Rate r(100.0);
00996     while(ros::ok() && !initialized_)
00997       r.sleep();
00998   }
00999 
01000   void Costmap2DROS::stop(){
01001     stop_updates_ = true;
01002     //unsubscribe from topics
01003     for(unsigned int i = 0; i < observation_subscribers_.size(); ++i){
01004       if(observation_subscribers_[i] != NULL)
01005         observation_subscribers_[i]->unsubscribe();
01006     }
01007     initialized_ = false;
01008     stopped_ = true;
01009   }
01010 
01011   void Costmap2DROS::addObservationBuffer(const boost::shared_ptr<ObservationBuffer>& buffer){
01012     if(buffer)
01013       observation_buffers_.push_back(buffer);
01014   }
01015 
01016   void Costmap2DROS::laserScanCallback(const sensor_msgs::LaserScanConstPtr& message, const boost::shared_ptr<ObservationBuffer>& buffer){
01017     //project the laser into a point cloud
01018     sensor_msgs::PointCloud2 cloud;
01019     cloud.header = message->header;
01020 
01021     //project the scan into a point cloud
01022     try
01023     {
01024       projector_.transformLaserScanToPointCloud(message->header.frame_id, *message, cloud, tf_);
01025     }
01026     catch (tf::TransformException &ex)
01027     {
01028       ROS_WARN ("High fidelity enabled, but TF returned a transform exception to frame %s: %s", global_frame_.c_str (), ex.what ());
01029       projector_.projectLaser(*message, cloud);
01030     }
01031 
01032     //buffer the point cloud
01033     buffer->lock();
01034     buffer->bufferCloud(cloud);
01035     buffer->unlock();
01036   }
01037 
01038   void Costmap2DROS::laserScanValidInfCallback(const sensor_msgs::LaserScanConstPtr& raw_message, const boost::shared_ptr<ObservationBuffer>& buffer){
01039     // Filter positive infinities ("Inf"s) to max_range.
01040     float epsilon = 0.0001; // a tenth of a millimeter
01041     sensor_msgs::LaserScan message = *raw_message;
01042     for( size_t i = 0; i < message.ranges.size(); i++ )
01043     {
01044       float range = message.ranges[ i ];
01045       if( !isfinite( range ) && range > 0 )
01046       {
01047         message.ranges[ i ] = message.range_max - epsilon;
01048       }
01049     }
01050 
01051     //project the laser into a point cloud
01052     sensor_msgs::PointCloud2 cloud;
01053     cloud.header = message.header;
01054 
01055     //project the scan into a point cloud
01056     try
01057     {
01058       projector_.transformLaserScanToPointCloud(message.header.frame_id, message, cloud, tf_);
01059     }
01060     catch (tf::TransformException &ex)
01061     {
01062       ROS_WARN ("High fidelity enabled, but TF returned a transform exception to frame %s: %s", global_frame_.c_str (), ex.what ());
01063       projector_.projectLaser(message, cloud);
01064     }
01065 
01066     //buffer the point cloud
01067     buffer->lock();
01068     buffer->bufferCloud(cloud);
01069     buffer->unlock();
01070   }
01071 
01072   void Costmap2DROS::pointCloudCallback(const sensor_msgs::PointCloudConstPtr& message, const boost::shared_ptr<ObservationBuffer>& buffer){
01073     sensor_msgs::PointCloud2 cloud2;
01074 
01075     if(!sensor_msgs::convertPointCloudToPointCloud2(*message, cloud2)){
01076       ROS_ERROR("Failed to convert a PointCloud to a PointCloud2, dropping message");
01077       return;
01078     }
01079 
01080     //buffer the point cloud
01081     buffer->lock();
01082     buffer->bufferCloud(cloud2);
01083     buffer->unlock();
01084   }
01085 
01086   void Costmap2DROS::pointCloud2Callback(const sensor_msgs::PointCloud2ConstPtr& message, const boost::shared_ptr<ObservationBuffer>& buffer){
01087     //buffer the point cloud
01088     buffer->lock();
01089     buffer->bufferCloud(*message);
01090     buffer->unlock();
01091   }
01092 
01093   void Costmap2DROS::mapUpdateLoop(double frequency){
01094     //the user might not want to run the loop every cycle
01095     if(frequency == 0.0)
01096       return;
01097 
01098     boost::mutex::scoped_lock ml(map_update_mutex_);
01099 
01100     ros::NodeHandle nh;
01101     ros::Rate r(frequency);
01102     while(nh.ok() && !map_update_thread_shutdown_){
01103       struct timeval start, end;
01104       double start_t, end_t, t_diff;
01105       gettimeofday(&start, NULL);
01106       if(!stop_updates_){
01107         updateMap();
01108         initialized_ = true;
01109       }
01110       gettimeofday(&end, NULL);
01111       start_t = start.tv_sec + double(start.tv_usec) / 1e6;
01112       end_t = end.tv_sec + double(end.tv_usec) / 1e6;
01113       t_diff = end_t - start_t;
01114       ROS_DEBUG("Map update time: %.9f", t_diff);
01115 
01116       r.sleep();
01117       //make sure to sleep for the remainder of our cycle time
01118       if(r.cycleTime() > ros::Duration(1 / frequency))
01119         ROS_WARN("Map update loop missed its desired rate of %.4fHz... the loop actually took %.4f seconds", frequency, r.cycleTime().toSec());
01120     }
01121   }
01122 
01123   bool Costmap2DROS::getMarkingObservations(std::vector<Observation>& marking_observations) const {
01124     bool current = true;
01125     //get the marking observations
01126     for(unsigned int i = 0; i < marking_buffers_.size(); ++i){
01127       marking_buffers_[i]->lock();
01128       marking_buffers_[i]->getObservations(marking_observations);
01129       current = marking_buffers_[i]->isCurrent() && current;
01130       marking_buffers_[i]->unlock();
01131     }
01132     return current;
01133   }
01134 
01135   bool Costmap2DROS::getClearingObservations(std::vector<Observation>& clearing_observations) const {
01136     bool current = true;
01137     //get the clearing observations
01138     for(unsigned int i = 0; i < clearing_buffers_.size(); ++i){
01139       clearing_buffers_[i]->lock();
01140       clearing_buffers_[i]->getObservations(clearing_observations);
01141       current = clearing_buffers_[i]->isCurrent() && current;
01142       clearing_buffers_[i]->unlock();
01143     }
01144     return current;
01145   }
01146 
01147 
01148   void Costmap2DROS::updateMap(){
01149     tf::Stamped<tf::Pose> global_pose;
01150     if(!getRobotPose(global_pose))
01151       return;
01152 
01153     double wx = global_pose.getOrigin().x();
01154     double wy = global_pose.getOrigin().y();
01155 
01156     bool current = true;
01157     std::vector<Observation> observations, clearing_observations;
01158 
01159     //get the marking observations
01160     current = current && getMarkingObservations(observations);
01161 
01162     //get the clearing observations
01163     current = current && getClearingObservations(clearing_observations);
01164 
01165     //update the global current status
01166     current_ = current;
01167 
01168     boost::recursive_mutex::scoped_lock uml(configuration_mutex_);
01169     boost::recursive_mutex::scoped_lock lock(lock_);
01170     //if we're using a rolling buffer costmap... we need to update the origin using the robot's position
01171     if(rolling_window_){
01172       double origin_x = wx - costmap_->getSizeInMetersX() / 2;
01173       double origin_y = wy - costmap_->getSizeInMetersY() / 2;
01174       costmap_->updateOrigin(origin_x, origin_y);
01175     }
01176     costmap_->updateWorld(wx, wy, observations, clearing_observations);
01177 
01178     //make sure to clear the robot footprint of obstacles at the end
01179     clearRobotFootprint();
01180     
01181     if(save_debug_pgm_)
01182       costmap_->saveMap(name_ + ".pgm");
01183 
01184     //if we have an active publisher... we'll update its costmap data
01185     if(costmap_publisher_->active()){
01186       std::vector<geometry_msgs::Point> oriented_footprint;
01187       getOrientedFootprint(oriented_footprint);
01188       tf::Stamped<tf::Pose> global_pose;
01189       getRobotPose(global_pose);
01190       costmap_publisher_->updateCostmapData(*costmap_, oriented_footprint, global_pose);
01191     }
01192 
01193     if(publish_voxel_){
01194       costmap_2d::VoxelGrid voxel_grid;
01195       ((VoxelCostmap2D*)costmap_)->getVoxelGridMessage(voxel_grid);
01196       voxel_grid.header.frame_id = global_frame_;
01197       voxel_grid.header.stamp = ros::Time::now();
01198       voxel_pub_.publish(voxel_grid);
01199     }
01200   }
01201 
01202   void Costmap2DROS::clearNonLethalWindow(double size_x, double size_y){
01203     tf::Stamped<tf::Pose> global_pose;
01204     if(!getRobotPose(global_pose))
01205       return;
01206 
01207     double wx = global_pose.getOrigin().x();
01208     double wy = global_pose.getOrigin().y();
01209     lock_.lock();
01210     ROS_DEBUG("Clearing map in window");
01211     costmap_->clearNonLethal(wx, wy, size_x, size_y, true);
01212     lock_.unlock();
01213 
01214     //make sure to force an update of the map to take in the latest sensor data
01215     updateMap();
01216   }
01217 
01218   void Costmap2DROS::resetMapOutsideWindow(double size_x, double size_y){
01219     tf::Stamped<tf::Pose> global_pose;
01220     if(!getRobotPose(global_pose))
01221       return;
01222 
01223     double wx = global_pose.getOrigin().x();
01224     double wy = global_pose.getOrigin().y();
01225     lock_.lock();
01226     ROS_DEBUG("Resetting map outside window");
01227     costmap_->resetMapOutsideWindow(wx, wy, size_x, size_y);
01228     lock_.unlock();
01229 
01230     //make sure to force an update of the map to take in the latest sensor data
01231     updateMap();
01232 
01233   }
01234 
01235   void Costmap2DROS::getCostmapCopy(Costmap2D& costmap) const {
01236     boost::recursive_mutex::scoped_lock lock(lock_);
01237     costmap = *costmap_;
01238   }
01239 
01240   void Costmap2DROS::incomingMap(const nav_msgs::OccupancyGridConstPtr& new_map){
01241     if(!map_initialized_){
01242       initFromMap(*new_map);
01243       map_initialized_ = true;
01244     }
01245     else if(costmap_initialized_)
01246       updateStaticMap(*new_map);
01247   }
01248 
01249   void Costmap2DROS::initFromMap(const nav_msgs::OccupancyGrid& map){
01250     boost::recursive_mutex::scoped_lock lock(map_data_lock_);
01251 
01252     // We need to cast to unsigned chars from int
01253     unsigned int numCells = map.info.width * map.info.height;
01254     for(unsigned int i = 0; i < numCells; i++){
01255       input_data_.push_back((unsigned char) map.data[i]);
01256     }
01257 
01258     map_meta_data_ = map.info;
01259     global_frame_ = tf::resolve(tf_prefix_, map.header.frame_id);
01260   }
01261 
01262   void Costmap2DROS::updateStaticMap(const nav_msgs::OccupancyGrid& new_map){
01263     std::vector<unsigned char> new_map_data;
01264     // We need to cast to unsigned chars from int
01265     unsigned int numCells = new_map.info.width * new_map.info.height;
01266     for(unsigned int i = 0; i < numCells; i++){
01267       new_map_data.push_back((unsigned char) new_map.data[i]);
01268     }
01269 
01270     double map_width = (unsigned int)new_map.info.width;
01271     double map_height = (unsigned int)new_map.info.height;
01272     double map_resolution = new_map.info.resolution;
01273     double map_origin_x = new_map.info.origin.position.x;
01274     double map_origin_y = new_map.info.origin.position.y;
01275 
01276     if(fabs(map_resolution - costmap_->getResolution()) > 1e-6){
01277       ROS_ERROR("You cannot update a map with resolution: %.4f, with a new map that has resolution: %.4f", 
01278           costmap_->getResolution(), map_resolution);
01279       return;
01280     }
01281 
01282     if(fabs(new_map.info.origin.orientation.x) > 1e-6 
01283        && fabs(new_map.info.origin.orientation.y) > 1e-6 
01284        && fabs(new_map.info.origin.orientation.z) > 1e-6 
01285        && (fabs(new_map.info.origin.orientation.w) > 1e-6 || fabs(new_map.info.origin.orientation.w - 1.0) > 1e-6)){
01286       ROS_ERROR("The costmap does not support origins that contain rotations. The origin must be aligned with the global_frame.");
01287       return;
01288     }
01289 
01290     if(tf::resolve(tf_prefix_, new_map.header.frame_id) != tf::resolve(tf_prefix_, global_frame_)){
01291       std::string new_global_frame = tf::resolve(tf_prefix_, new_map.header.frame_id);
01292 
01293       ROS_DEBUG("Map with a global_frame of: %s, updated with a new map that has a global frame of: %s, wiping map", global_frame_.c_str(), new_map.header.frame_id.c_str());
01294 
01295       //we'll update all the observation buffers we have associated with this map
01296       for(unsigned int i = 0; i < observation_buffers_.size(); ++i){
01297         observation_buffers_[i]->lock();
01298         observation_buffers_[i]->setGlobalFrame(new_global_frame);
01299         observation_buffers_[i]->unlock();
01300       }
01301 
01302       //make sure to lock the costmap
01303       boost::recursive_mutex::scoped_lock uml(configuration_mutex_);
01304       boost::recursive_mutex::scoped_lock lock(lock_);
01305 
01306       //if the map has a new global frame... we'll actually wipe the whole map rather than trying to be efficient about updating a potential window
01307       costmap_->replaceFullMap(map_origin_x, map_origin_y, map_width, map_height, new_map_data);
01308 
01309       //we'll also update the global frame id for this costmap
01310       global_frame_ = new_global_frame;
01311 
01312       return;
01313     }
01314 
01315     boost::recursive_mutex::scoped_lock lock(lock_);
01316     costmap_->updateStaticMapWindow(map_origin_x, map_origin_y, map_width, map_height, new_map_data);
01317   }
01318 
01319   void Costmap2DROS::getCostmapWindowCopy(double win_size_x, double win_size_y, Costmap2D& costmap) const {
01320     boost::recursive_mutex::scoped_lock lock(lock_);
01321     tf::Stamped<tf::Pose> global_pose;
01322     if(!getRobotPose(global_pose)){
01323       ROS_ERROR("Could not get a window of this costmap centered at the robot, because we failed to get the pose of the robot");
01324       return;
01325     }
01326     getCostmapWindowCopy(global_pose.getOrigin().x(), global_pose.getOrigin().y(), win_size_x, win_size_y, costmap);
01327   }
01328 
01329   void Costmap2DROS::getCostmapWindowCopy(double win_center_x, double win_center_y, double win_size_x, double win_size_y, Costmap2D& costmap) const {
01330     boost::recursive_mutex::scoped_lock lock(lock_);
01331 
01332     //we need to compute legal bounds for the window and shrink it if necessary
01333     double ll_x = std::min(std::max(win_center_x - win_size_x, costmap_->getOriginX()), costmap_->getSizeInMetersX());
01334     double ll_y = std::min(std::max(win_center_y - win_size_y, costmap_->getOriginY()), costmap_->getSizeInMetersY());
01335     double ur_x = std::min(std::max(win_center_x + win_size_x, costmap_->getOriginX()), costmap_->getSizeInMetersX());
01336     double ur_y = std::min(std::max(win_center_y + win_size_y, costmap_->getOriginY()), costmap_->getSizeInMetersY());
01337     double size_x = ur_x - ll_x;
01338     double size_y = ur_y - ll_y;
01339 
01340     //copy the appropriate window from our costmap into the one passed in by the user
01341     costmap.copyCostmapWindow(*costmap_, ll_x, ll_y, size_x, size_y);
01342   }
01343 
01344   unsigned int Costmap2DROS::getSizeInCellsX() const {
01345     boost::recursive_mutex::scoped_lock lock(lock_);
01346     return costmap_->getSizeInCellsX();
01347   }
01348 
01349   unsigned int Costmap2DROS::getSizeInCellsY() const {
01350     boost::recursive_mutex::scoped_lock lock(lock_);
01351     return costmap_->getSizeInCellsY();
01352   }
01353 
01354   double Costmap2DROS::getResolution() const {
01355     boost::recursive_mutex::scoped_lock lock(lock_);
01356     return costmap_->getResolution();
01357   }
01358 
01359   bool Costmap2DROS::getRobotPose(tf::Stamped<tf::Pose>& global_pose) const {
01360 
01361     global_pose.setIdentity();
01362     tf::Stamped<tf::Pose> robot_pose;
01363     robot_pose.setIdentity();
01364     robot_pose.frame_id_ = robot_base_frame_;
01365     robot_pose.stamp_ = ros::Time();
01366     ros::Time current_time = ros::Time::now(); // save time for checking tf delay later
01367 
01368     //get the global pose of the robot
01369     try{
01370       tf_.transformPose(global_frame_, robot_pose, global_pose);
01371     }
01372     catch(tf::LookupException& ex) {
01373       ROS_ERROR_THROTTLE(1.0, "No Transform available Error looking up robot pose: %s\n", ex.what());
01374       return false;
01375     }
01376     catch(tf::ConnectivityException& ex) {
01377       ROS_ERROR_THROTTLE(1.0, "Connectivity Error looking up robot pose: %s\n", ex.what());
01378       return false;
01379     }
01380     catch(tf::ExtrapolationException& ex) {
01381       ROS_ERROR_THROTTLE(1.0, "Extrapolation Error looking up robot pose: %s\n", ex.what());
01382       return false;
01383     }
01384     // check global_pose timeout
01385     if (current_time.toSec() - global_pose.stamp_.toSec() > transform_tolerance_) {
01386       ROS_WARN_THROTTLE(1.0, "Costmap2DROS transform timeout. Current time: %.4f, global_pose stamp: %.4f, tolerance: %.4f",
01387           current_time.toSec() ,global_pose.stamp_.toSec() ,transform_tolerance_);
01388       return false;
01389     }
01390 
01391     return true;
01392   }
01393 
01394   void Costmap2DROS::clearRobotFootprint(){
01395     tf::Stamped<tf::Pose> global_pose;
01396     if(!getRobotPose(global_pose))
01397       return;
01398 
01399     clearRobotFootprint(global_pose);
01400   }
01401 
01402   std::vector<geometry_msgs::Point> Costmap2DROS::getRobotFootprint() const {
01403     return footprint_spec_;
01404   }
01405 
01406   void Costmap2DROS::getOrientedFootprint(std::vector<geometry_msgs::Point>& oriented_footprint) const {
01407     tf::Stamped<tf::Pose> global_pose;
01408     if(!getRobotPose(global_pose))
01409       return;
01410 
01411     double yaw = tf::getYaw(global_pose.getRotation());
01412 
01413     getOrientedFootprint(global_pose.getOrigin().x(), global_pose.getOrigin().y(), yaw, oriented_footprint);
01414   }
01415 
01416   void Costmap2DROS::getOrientedFootprint(double x, double y, double theta, std::vector<geometry_msgs::Point>& oriented_footprint) const {
01417     //build the oriented footprint at the robot's current location
01418     double cos_th = cos(theta);
01419     double sin_th = sin(theta);
01420     for(unsigned int i = 0; i < footprint_spec_.size(); ++i){
01421       geometry_msgs::Point new_pt;
01422       new_pt.x = x + (footprint_spec_[i].x * cos_th - footprint_spec_[i].y * sin_th);
01423       new_pt.y = y + (footprint_spec_[i].x * sin_th + footprint_spec_[i].y * cos_th);
01424       oriented_footprint.push_back(new_pt);
01425     }
01426   }
01427 
01428   bool Costmap2DROS::setConvexPolygonCost(const std::vector<geometry_msgs::Point>& polygon, unsigned char cost_value){
01429     lock_.lock();
01430     bool success = costmap_->setConvexPolygonCost(polygon, cost_value);
01431     lock_.unlock();
01432 
01433     //make sure to take our active sensor data into account
01434     updateMap();
01435 
01436     return success;
01437   }
01438 
01439   std::string Costmap2DROS::getGlobalFrameID() const {
01440     return global_frame_;
01441   }
01442 
01443   std::string Costmap2DROS::getBaseFrameID() const {
01444     return robot_base_frame_;
01445   }
01446 
01447   double Costmap2DROS::getInscribedRadius() const {
01448     boost::recursive_mutex::scoped_lock lock(lock_);
01449     return costmap_->getInscribedRadius();
01450   }
01451 
01452   double Costmap2DROS::getCircumscribedRadius() const {
01453     boost::recursive_mutex::scoped_lock lock(lock_);
01454     return costmap_->getCircumscribedRadius();
01455   }
01456 
01457   double Costmap2DROS::getInflationRadius() const {
01458     boost::recursive_mutex::scoped_lock lock(lock_);
01459     return costmap_->getInflationRadius();
01460   }
01461 
01462   void Costmap2DROS::clearRobotFootprint(const tf::Stamped<tf::Pose>& global_pose){
01463     std::vector<geometry_msgs::Point> oriented_footprint;
01464 
01465     //check if we have a circular footprint or a polygon footprint
01466     if(footprint_spec_.size() < 3){
01467       //we'll build an approximation of the circle as the footprint and clear that
01468       double angle = 0;
01469       double step = 2 * M_PI / 72;
01470       while(angle < 2 * M_PI){
01471         geometry_msgs::Point pt;
01472         pt.x = getInscribedRadius() * cos(angle) + global_pose.getOrigin().x();
01473         pt.y = getInscribedRadius() * sin(angle) + global_pose.getOrigin().y();
01474         pt.z = 0.0;
01475         oriented_footprint.push_back(pt);
01476         angle += step;
01477       }
01478     }
01479     else{
01480       double yaw = tf::getYaw(global_pose.getRotation());
01481 
01482       //get the oriented footprint of the robot
01483       double x = global_pose.getOrigin().x();
01484       double y = global_pose.getOrigin().y();
01485       double theta = yaw;
01486 
01487       //build the oriented footprint at the robot's current location
01488       getOrientedFootprint(x, y, theta, oriented_footprint);
01489     }
01490 
01491     //lock the map if necessary
01492     boost::recursive_mutex::scoped_lock lock(lock_);
01493 
01494     //set the associated costs in the cost map to be free
01495     if(!costmap_->setConvexPolygonCost(oriented_footprint, costmap_2d::FREE_SPACE))
01496       return;
01497 
01498     double max_inflation_dist = 2 * (costmap_->getInflationRadius() + costmap_->getCircumscribedRadius());
01499 
01500     //clear all non-lethal obstacles out to the maximum inflation distance of an obstacle in the robot footprint
01501     costmap_->clearNonLethal(global_pose.getOrigin().x(), global_pose.getOrigin().y(), max_inflation_dist, max_inflation_dist);
01502 
01503     //make sure to re-inflate obstacles in the affected region... plus those obstalces that could inflate to have costs in the footprint
01504     costmap_->reinflateWindow(global_pose.getOrigin().x(), global_pose.getOrigin().y(), 
01505         max_inflation_dist + 2 * costmap_->getInflationRadius(), max_inflation_dist + 2 * costmap_->getInflationRadius(), false);
01506 
01507   }
01508 
01509 };


costmap_2d
Author(s): Eitan Marder-Eppstein
autogenerated on Mon Oct 6 2014 02:44:46