00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #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
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
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
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
00095 robot_base_frame_ = tf::resolve(tf_prefix_, robot_base_frame_);
00096
00097
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
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
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
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
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
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
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
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
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
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
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
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
00232 if(marking)
00233 marking_buffers_.push_back(observation_buffers_.back());
00234
00235
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
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
00314 footprint_spec_ = loadRobotFootprint(private_nh, inscribed_radius, circumscribed_radius);
00315
00316 if(footprint_spec_.size() > 2){
00317
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
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
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
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
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
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
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
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
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
00459 config.restore_defaults = false;
00460 }
00461
00462
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
00474 boost::recursive_mutex::scoped_lock rel(configuration_mutex_);
00475
00476 transform_tolerance_ = config.transform_tolerance;
00477
00478
00479 map_update_thread_shutdown_ = true;
00480 map_update_thread_->join();
00481 boost::mutex::scoped_lock ml(map_update_mutex_);
00482
00483
00484
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
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
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
00557 if(footprint_spec_.size() > 2){
00558
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
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
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
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
00591
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
00634
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
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
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
00767 boost::recursive_mutex::scoped_lock lock(lock_);
00768
00769
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
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
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
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
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
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
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
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
00980 if(stopped_){
00981
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
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
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
01018 sensor_msgs::PointCloud2 cloud;
01019 cloud.header = message->header;
01020
01021
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
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
01040 float epsilon = 0.0001;
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
01052 sensor_msgs::PointCloud2 cloud;
01053 cloud.header = message.header;
01054
01055
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
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
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
01088 buffer->lock();
01089 buffer->bufferCloud(*message);
01090 buffer->unlock();
01091 }
01092
01093 void Costmap2DROS::mapUpdateLoop(double frequency){
01094
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
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
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
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
01160 current = current && getMarkingObservations(observations);
01161
01162
01163 current = current && getClearingObservations(clearing_observations);
01164
01165
01166 current_ = current;
01167
01168 boost::recursive_mutex::scoped_lock uml(configuration_mutex_);
01169 boost::recursive_mutex::scoped_lock lock(lock_);
01170
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
01179 clearRobotFootprint();
01180
01181 if(save_debug_pgm_)
01182 costmap_->saveMap(name_ + ".pgm");
01183
01184
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
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
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
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
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
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
01303 boost::recursive_mutex::scoped_lock uml(configuration_mutex_);
01304 boost::recursive_mutex::scoped_lock lock(lock_);
01305
01306
01307 costmap_->replaceFullMap(map_origin_x, map_origin_y, map_width, map_height, new_map_data);
01308
01309
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
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
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();
01367
01368
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
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
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
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
01466 if(footprint_spec_.size() < 3){
01467
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
01483 double x = global_pose.getOrigin().x();
01484 double y = global_pose.getOrigin().y();
01485 double theta = yaw;
01486
01487
01488 getOrientedFootprint(x, y, theta, oriented_footprint);
01489 }
01490
01491
01492 boost::recursive_mutex::scoped_lock lock(lock_);
01493
01494
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
01501 costmap_->clearNonLethal(global_pose.getOrigin().x(), global_pose.getOrigin().y(), max_inflation_dist, max_inflation_dist);
01502
01503
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 };