$search
00001 #include "hector_costmap.h" 00002 00003 // compute linear index for given map coords 00004 #define MAP_IDX(sx, i, j) ((sx) * (j) + (i)) 00005 00006 #define OCCUPIED_CELL 100 00007 #define FREE_CELL 0 00008 #define UNKNOWN_CELL -1 00009 00010 #define USE_GRID_MAP_ONLY 0 00011 #define USE_ELEVATION_MAP_ONLY 1 00012 #define USE_GRID_AND_ELEVATION_MAP 2 00013 00014 CostMapCalculation::CostMapCalculation() : nHandle("~") 00015 { 00016 int width, height; 00017 double grid_res_xy; 00018 00019 nHandle.param("elevation_resolution", grid_res_z, 0.01); //[m] 00020 00021 nHandle.param("max_grid_size_x", width, 1024); //[cell] 00022 cost_map.info.width = width; 00023 nHandle.param("max_grid_size_y", height, 1024); //[cell] 00024 cost_map.info.height = height; 00025 nHandle.param("map_resolution", grid_res_xy, 0.05); //[m] 00026 cost_map.info.resolution = grid_res_xy; 00027 nHandle.param("origin_x",cost_map.info.origin.position.x, -(double)cost_map.info.width*(double)cost_map.info.resolution/2.0); //[m] 00028 nHandle.param("origin_y",cost_map.info.origin.position.y, -(double)cost_map.info.height*(double)cost_map.info.resolution/2.0); //[m] 00029 nHandle.param("origin_z",cost_map.info.origin.position.z, 0.0); //[m] 00030 nHandle.param("orientation_x",cost_map.info.origin.orientation.x, 0.0); //[] 00031 nHandle.param("orientation_y",cost_map.info.origin.orientation.y, 0.0); //[] 00032 nHandle.param("orientation_z",cost_map.info.origin.orientation.z, 0.0); //[] 00033 nHandle.param("orientation_w",cost_map.info.origin.orientation.w, 1.0); //[] 00034 00035 nHandle.param("initial_free_cells_radius", initial_free_cells_radius, 0.30); //[m] 00036 nHandle.param("update_radius", update_radius_world, 4.0); //[m] 00037 nHandle.param("max_delta_elevation", max_delta_elevation, 0.07); //[m] 00038 00039 nHandle.param("map_frame_id", map_frame_id,std::string("/map")); 00040 nHandle.param("local_map_frame_id", local_map_frame_id,std::string("/base_footprint")); 00041 nHandle.param("cost_map_topic", cost_map_topic, std::string("/cost_map")); 00042 nHandle.param("elevation_map_topic", elevation_map_topic, std::string("/elevation_map_local")); 00043 nHandle.param("grid_map_topic", grid_map_topic, std::string("/scanmatcher_map")); 00044 00045 nHandle.param("use_elevation_map", use_elevation_map, true); 00046 nHandle.param("use_grid_map", use_grid_map, true); 00047 nHandle.param("allow_kinect_to_clear_occupied_cells", allow_kinect_to_clear_occupied_cells, true); 00048 nHandle.param("max_clear_size", max_clear_size, 2); 00049 00050 nHandle.param("costmap_pub_freq", costmap_pub_freq, 4.0); //[Hz] 00051 00052 nHandle.param("sysMsgTopic", paramSysMsgTopic, std::string("/syscommand")); 00053 00054 cost_map.data.assign(cost_map.info.width * cost_map.info.height,UNKNOWN_CELL); 00055 elevation_cost_map.data.assign(cost_map.info.width * cost_map.info.height,UNKNOWN_CELL); 00056 00057 world_map_transform.setTransforms(cost_map.info); 00058 00059 // loop through starting area 00060 min_index(0) = cost_map.info.width/2-floor(initial_free_cells_radius/cost_map.info.resolution); 00061 min_index(1) = cost_map.info.height/2-floor(initial_free_cells_radius/cost_map.info.resolution); 00062 max_index(0) = cost_map.info.width/2+floor(initial_free_cells_radius/cost_map.info.resolution); 00063 max_index(1) = cost_map.info.height/2+floor(initial_free_cells_radius/cost_map.info.resolution); 00064 for (int v = min_index(1); v < max_index(1); ++v) 00065 for (int u = min_index(0); u < max_index(0); ++u) 00066 cost_map.data[MAP_IDX(cost_map.info.width, u, v)] = FREE_CELL; 00067 00068 pub_cost_map = nHandle.advertise<nav_msgs::OccupancyGrid>(cost_map_topic, 1, true); 00069 00070 received_elevation_map = false; 00071 received_grid_map = false; 00072 00073 sub_elevation_map = nHandle.subscribe(elevation_map_topic,10,&CostMapCalculation::callbackElevationMap,this); 00074 sub_grid_map = nHandle.subscribe(grid_map_topic,10,&CostMapCalculation::callbackGridMap,this); 00075 00076 sub_map_info = nHandle.subscribe("/map_metadata",1,&CostMapCalculation::updateMapParamsCallback,this); 00077 00078 sub_sysMessage = nHandle.subscribe(paramSysMsgTopic, 10, &CostMapCalculation::sysMessageCallback, this); 00079 dyn_rec_server_.setCallback(boost::bind(&CostMapCalculation::dynRecParamCallback, this, _1, _2)); 00080 00081 timer = nHandle.createTimer(ros::Duration(1.0/costmap_pub_freq), &CostMapCalculation::timerCallback,this); 00082 00083 ROS_INFO("HectorCM: is up and running."); 00084 00085 ros::spin(); 00086 } 00087 00088 00089 CostMapCalculation::~CostMapCalculation() 00090 { 00091 ROS_INFO("HectorCM: Shutting down!"); 00092 } 00093 00094 00095 void CostMapCalculation::sysMessageCallback(const std_msgs::String& string) 00096 { 00097 ROS_DEBUG("HectorCM: sysMsgCallback, msg contents: %s", string.data.c_str()); 00098 00099 if (string.data == "reset") 00100 { 00101 ROS_INFO("HectorCM: reset"); 00102 00103 // set default values 00104 cost_map.data.assign(cost_map.info.width * cost_map.info.height,UNKNOWN_CELL); 00105 elevation_cost_map.data.assign(cost_map.info.width * cost_map.info.height,UNKNOWN_CELL); 00106 00107 // loop through starting area 00108 min_index(0) = cost_map.info.width/2-floor(initial_free_cells_radius/cost_map.info.resolution); 00109 min_index(1) = cost_map.info.height/2-floor(initial_free_cells_radius/cost_map.info.resolution); 00110 max_index(0) = cost_map.info.width/2+floor(initial_free_cells_radius/cost_map.info.resolution); 00111 max_index(1) = cost_map.info.height/2+floor(initial_free_cells_radius/cost_map.info.resolution); 00112 for (int v = min_index(1); v < max_index(1); ++v) 00113 for (int u = min_index(0); u < max_index(0); ++u) 00114 cost_map.data[MAP_IDX(cost_map.info.width, u, v)] = FREE_CELL; 00115 00116 received_elevation_map = false; 00117 received_grid_map = false; 00118 } 00119 } 00120 00121 00122 void CostMapCalculation::dynRecParamCallback(hector_costmap::CostMapCalculationConfig &config, uint32_t level) 00123 { 00124 00125 max_delta_elevation = config.max_delta_elevation; 00126 use_elevation_map = config.use_elevation_map; 00127 use_grid_map = config.use_grid_map; 00128 allow_kinect_to_clear_occupied_cells = config.allow_kinect_to_clear_occupied_cells; 00129 max_clear_size = config.max_clear_size; 00130 costmap_pub_freq = config.costmap_pub_freq; 00131 00132 if(use_elevation_map) 00133 sub_elevation_map = nHandle.subscribe(elevation_map_topic,10,&CostMapCalculation::callbackElevationMap,this); 00134 else 00135 sub_elevation_map.shutdown(); 00136 00137 if(use_grid_map) 00138 sub_grid_map = nHandle.subscribe(grid_map_topic,10,&CostMapCalculation::callbackGridMap,this); 00139 else 00140 sub_grid_map.shutdown(); 00141 } 00142 00143 00144 void CostMapCalculation::updateMapParamsCallback(const nav_msgs::MapMetaData& map_meta_data) 00145 { 00146 ROS_DEBUG("HectorCM: received new map meta data -> overwrite old parameters"); 00147 00148 // set new parameters 00149 cost_map.info.width = map_meta_data.width; 00150 cost_map.info.height = map_meta_data.height; 00151 cost_map.info.resolution = map_meta_data.resolution; 00152 00153 cost_map.info.origin.position.x = map_meta_data.origin.position.x; 00154 cost_map.info.origin.position.y = map_meta_data.origin.position.y; 00155 cost_map.info.origin.position.z = map_meta_data.origin.position.z; 00156 00157 cost_map.info.origin.orientation.x = map_meta_data.origin.orientation.x; 00158 cost_map.info.origin.orientation.y = map_meta_data.origin.orientation.y; 00159 cost_map.info.origin.orientation.z = map_meta_data.origin.orientation.z; 00160 cost_map.info.origin.orientation.w = map_meta_data.origin.orientation.w; 00161 00162 world_map_transform.setTransforms(cost_map.info); 00163 00164 // allocate memory 00165 cost_map.data.assign(cost_map.info.width * cost_map.info.height,UNKNOWN_CELL); 00166 elevation_cost_map.data.assign(cost_map.info.width * cost_map.info.height,UNKNOWN_CELL); 00167 00168 // loop through starting area 00169 min_index(0) = cost_map.info.width/2-floor(initial_free_cells_radius/cost_map.info.resolution); 00170 min_index(1) = cost_map.info.height/2-floor(initial_free_cells_radius/cost_map.info.resolution); 00171 max_index(0) = cost_map.info.width/2+floor(initial_free_cells_radius/cost_map.info.resolution); 00172 max_index(1) = cost_map.info.height/2+floor(initial_free_cells_radius/cost_map.info.resolution); 00173 for (int v = min_index(1); v < max_index(1); ++v) 00174 for (int u = min_index(0); u < max_index(0); ++u) 00175 cost_map.data[MAP_IDX(cost_map.info.width, u, v)] = FREE_CELL; 00176 00177 // update flags 00178 received_elevation_map = false; 00179 received_grid_map = false; 00180 } 00181 00182 void CostMapCalculation::timerCallback(const ros::TimerEvent& event) 00183 { 00184 if(received_elevation_map || received_grid_map) 00185 00186 ROS_DEBUG("HectorCM: published a new costmap"); 00187 00188 // set the header information on the map 00189 cost_map.header.stamp = ros::Time::now(); 00190 cost_map.header.frame_id = map_frame_id; 00191 00192 pub_cost_map.publish(cost_map); 00193 00194 } 00195 00196 00197 void CostMapCalculation::callbackElevationMap(const hector_elevation_msgs::ElevationGridConstPtr& elevation_map_msg) 00198 { 00199 ROS_DEBUG("HectorCM: received new elevation map"); 00200 00201 // check header 00202 if((int)(1000*elevation_map_msg->info.resolution_xy) != (int)(1000*cost_map.info.resolution) && // Magic number 1000 -> min grid size should not be less than 1mm 00203 elevation_map_msg->info.height != cost_map.info.height && 00204 elevation_map_msg->info.width != cost_map.info.width) 00205 { 00206 ROS_ERROR("HectorCM: elevation map resolution and or size incorrect!"); 00207 return; 00208 } 00209 00210 // store elevation_map_msg in local variable 00211 elevation_map_ = cv::Mat(elevation_map_msg->info.height, elevation_map_msg->info.width, CV_16S, const_cast<int16_t*>(&elevation_map_msg->data[0]), 2*(size_t)elevation_map_msg->info.width); 00212 00213 // store elevation map zero level 00214 elevation_zero_level = elevation_map_msg->info.zero_elevation; 00215 00216 // compute region of intereset 00217 if(!computeWindowIndices(elevation_map_msg->header.stamp, update_radius_world)) 00218 return; 00219 00220 // loop through each element 00221 int filtered_cell, filtered_cell_x, filtered_cell_y; 00222 for (int v = min_index(1); v < max_index(1); ++v) 00223 { 00224 for (int u = min_index(0); u < max_index(0); ++u) 00225 { 00226 // compute cost_map_index 00227 unsigned int cost_map_index = MAP_IDX(cost_map.info.width, u, v); 00228 00229 // check if neighbouring cells are known 00230 if(elevation_map_.at<int16_t>(v+1,u) == (-elevation_zero_level) || 00231 elevation_map_.at<int16_t>(v-1,u) == (-elevation_zero_level) || 00232 elevation_map_.at<int16_t>(v,u+1) == (-elevation_zero_level) || 00233 elevation_map_.at<int16_t>(v,u-1) == (-elevation_zero_level)) 00234 continue; 00235 00236 // edge filter 00237 filtered_cell_y = abs(elevation_map_.at<int16_t>(v,u-1) - elevation_map_.at<int16_t>(v,u+1)); 00238 filtered_cell_x = abs(elevation_map_.at<int16_t>(v-1,u) - elevation_map_.at<int16_t>(v+1,u)); 00239 00240 00241 if(filtered_cell_x > filtered_cell_y) 00242 filtered_cell = filtered_cell_x; 00243 else 00244 filtered_cell = filtered_cell_y; 00245 00246 // check if cell is traversable 00247 if(filtered_cell > max_delta_elevation/grid_res_z) 00248 { 00249 // cell is not traversable -> mark it as occupied 00250 elevation_cost_map.data[cost_map_index] = OCCUPIED_CELL; 00251 } 00252 else 00253 { 00254 // cell is traversable -> mark it as free 00255 elevation_cost_map.data[cost_map_index] = FREE_CELL; 00256 } 00257 } 00258 } 00259 00260 // set elevation map received flag 00261 received_elevation_map = true; 00262 00263 if(use_grid_map) 00264 { 00265 // calculate cost map 00266 if(received_grid_map) 00267 { 00268 calculateCostMap(USE_GRID_AND_ELEVATION_MAP); 00269 } 00270 else 00271 { 00272 ROS_WARN("HectorCM received no grid map, use only elevation map to compute costmap"); 00273 calculateCostMap(USE_ELEVATION_MAP_ONLY); 00274 } 00275 } 00276 else 00277 { 00278 calculateCostMap(USE_ELEVATION_MAP_ONLY); 00279 } 00280 } 00281 00282 void CostMapCalculation::callbackGridMap(const nav_msgs::OccupancyGridConstPtr& grid_map_msg) 00283 { 00284 ROS_DEBUG("HectorCM: received new grid map"); 00285 00286 // check header 00287 if((int)(1000*grid_map_msg->info.resolution) != (int)(1000*cost_map.info.resolution) && // Magic number 1000 -> min grid size should not be less than 1mm 00288 grid_map_msg->info.height != cost_map.info.height && 00289 grid_map_msg->info.width != cost_map.info.width) 00290 { 00291 ROS_ERROR("HectorCM: grid map resolution and or size incorrect!"); 00292 return; 00293 } 00294 00295 grid_map_msg_ = grid_map_msg; 00296 00297 // copy grid_map_msg to local variable 00298 grid_map_ = cv::Mat(grid_map_msg_->info.height, grid_map_msg_->info.width, CV_8S, const_cast<int8_t*>(&grid_map_msg_->data[0]), (size_t)grid_map_msg_->info.width); 00299 00300 // set grid map received flag 00301 received_grid_map = true; 00302 00303 // compute region of intereset 00304 if(!computeWindowIndices(grid_map_msg->header.stamp, update_radius_world)) 00305 return; 00306 00307 // calculate cost map 00308 if(use_elevation_map) 00309 { 00310 if(received_elevation_map) 00311 { 00312 calculateCostMap(USE_GRID_AND_ELEVATION_MAP); 00313 } 00314 else 00315 { 00316 ROS_DEBUG("HectorCM: received no elevation map, use only grid map to compute costmap"); 00317 calculateCostMap(USE_GRID_MAP_ONLY); 00318 } 00319 } 00320 else 00321 { 00322 calculateCostMap(USE_GRID_MAP_ONLY); 00323 } 00324 } 00325 00326 bool CostMapCalculation::calculateCostMap(char map_level) 00327 { 00328 switch(map_level) 00329 { 00330 case USE_GRID_MAP_ONLY: 00331 { 00332 // cost map based on grid map only 00333 00334 ROS_DEBUG("HectorCM: compute costmap based on grid map"); 00335 00336 // loop through each element 00337 for (int v = min_index(1); v < max_index(1); ++v) 00338 { 00339 for (int u = min_index(0); u < max_index(0); ++u) 00340 { 00341 unsigned int index = MAP_IDX(cost_map.info.width, u, v); 00342 00343 // check if cell is known 00344 if((char)grid_map_.data[index] != UNKNOWN_CELL) 00345 { 00346 if(grid_map_.data[index] == OCCUPIED_CELL) 00347 { 00348 // cell is occupied 00349 cost_map.data[index] = OCCUPIED_CELL; 00350 } 00351 else 00352 { 00353 // cell is not occupied 00354 cost_map.data[index] = FREE_CELL; 00355 } 00356 } 00357 } 00358 } 00359 00360 break; 00361 } 00362 case USE_ELEVATION_MAP_ONLY: 00363 { 00364 // cost map based on elevation map only 00365 00366 ROS_DEBUG("HectorCM: compute costmap based on elevation map"); 00367 00368 00369 // loop through each element 00370 for (int v = min_index(1); v < max_index(1); ++v) 00371 { 00372 for (int u = min_index(0); u < max_index(0); ++u) 00373 { 00374 unsigned int index = MAP_IDX(cost_map.info.width, u, v); 00375 00376 // check if cell is known 00377 if(elevation_cost_map.data[index] != UNKNOWN_CELL) 00378 { 00379 if(elevation_cost_map.data[index] == OCCUPIED_CELL) 00380 { 00381 // cell is occupied 00382 cost_map.data[index] = OCCUPIED_CELL; 00383 } 00384 else 00385 { 00386 // cell is not occupied 00387 cost_map.data[index] = FREE_CELL; 00388 } 00389 } 00390 } 00391 } 00392 00393 break; 00394 } 00395 case USE_GRID_AND_ELEVATION_MAP: 00396 { 00397 // cost map based on elevation and grid map 00398 00399 ROS_DEBUG("HectorCM: compute costmap based on grid and elevation map"); 00400 00401 int checksum_grid_map; 00402 00403 // loop through each element 00404 for (int v = min_index(1); v < max_index(1); ++v) 00405 { 00406 for (int u = min_index(0); u < max_index(0); ++u) 00407 { 00408 unsigned int index = MAP_IDX(cost_map.info.width, u, v); 00409 00410 // check if cell is known 00411 if(grid_map_.at<int8_t>(v,u) != UNKNOWN_CELL) 00412 { 00413 if(grid_map_.at<int8_t>(v,u) == OCCUPIED_CELL || elevation_cost_map.data[index] == OCCUPIED_CELL) 00414 { 00415 checksum_grid_map = 0; 00416 00417 checksum_grid_map += grid_map_.at<int8_t>(v-1, u); 00418 checksum_grid_map += grid_map_.at<int8_t>(v+1, u); 00419 checksum_grid_map += grid_map_.at<int8_t>(v, u-1); 00420 checksum_grid_map += grid_map_.at<int8_t>(v, u+1); 00421 checksum_grid_map += grid_map_.at<int8_t>(v+1, u+1); 00422 checksum_grid_map += grid_map_.at<int8_t>(v-1, u-1); 00423 checksum_grid_map += grid_map_.at<int8_t>(v+1, u-1); 00424 checksum_grid_map += grid_map_.at<int8_t>(v-1, u+1); 00425 00426 if(elevation_cost_map.data[index] == FREE_CELL && allow_kinect_to_clear_occupied_cells) 00427 { 00428 if(checksum_grid_map <= max_clear_size*OCCUPIED_CELL) 00429 { 00430 // cell is free 00431 cost_map.data[index] = FREE_CELL; 00432 } 00433 else 00434 { 00435 // cell is occupied 00436 cost_map.data[index] = OCCUPIED_CELL; 00437 } 00438 } 00439 else 00440 { 00441 // cell is occupied 00442 cost_map.data[index] = OCCUPIED_CELL; 00443 } 00444 } 00445 else 00446 { 00447 cost_map.data[index] = FREE_CELL; 00448 } 00449 } 00450 } 00451 } 00452 } 00453 break; 00454 } 00455 00456 ROS_DEBUG("HectorCM: computed a new costmap"); 00457 00458 return true; 00459 } 00460 00461 bool CostMapCalculation::computeWindowIndices(ros::Time time,double update_radius) 00462 { 00463 int update_radius_map; 00464 Eigen::Vector2f index_world, index_map; 00465 00466 // window update region 00467 // only update cells within the max a curtain radius of current robot position 00468 tf::StampedTransform local_map_transform; 00469 00470 // get local map transform 00471 try 00472 { 00473 listener.waitForTransform(map_frame_id, local_map_frame_id, time, ros::Duration(5)); 00474 listener.lookupTransform(map_frame_id, local_map_frame_id, time, local_map_transform); 00475 } 00476 catch (tf::TransformException ex) 00477 { 00478 ROS_ERROR("%s",ex.what()); 00479 return false; 00480 } 00481 00482 index_world(0) = local_map_transform.getOrigin().x(); 00483 index_world(1) = local_map_transform.getOrigin().y(); 00484 index_map = world_map_transform.getC2Coords(index_world); 00485 update_radius_map = floor(((double)update_radius/(double)cost_map.info.resolution)); 00486 00487 00488 // compute window min/max index 00489 if(index_map(1) < update_radius_map) 00490 min_index(1) = 0; 00491 else 00492 min_index(1) = index_map(1) - update_radius_map; 00493 00494 if(index_map(1) + update_radius_map > cost_map.info.height) 00495 max_index(1) = cost_map.info.height; 00496 else 00497 max_index(1) = index_map(1) + update_radius_map; 00498 00499 if(index_map(0) < update_radius_map) 00500 min_index(0) = 0; 00501 else 00502 min_index(0) = index_map(0) - update_radius_map; 00503 00504 if(index_map(0) + update_radius_map > cost_map.info.width) 00505 max_index(0) = cost_map.info.width; 00506 else 00507 max_index(0) = index_map(0) + update_radius_map; 00508 00509 return true; 00510 } 00511