costmap_client.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2015-2016, Jiri Horner.
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 Jiri Horner 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  *********************************************************************/
00036 
00037 #include <explore/costmap_client.h>
00038 
00039 #include <string>
00040 #include <functional>
00041 #include <mutex>
00042 
00043 namespace explore {
00044 
00045 // static translation table to speed things up
00046 std::array<unsigned char, 256> init_translation_table();
00047 static const std::array<unsigned char, 256> cost_translation_table__ = init_translation_table();
00048 
00049 Costmap2DClient::Costmap2DClient(ros::NodeHandle& param_nh, ros::NodeHandle& subscription_nh,
00050   const tf::TransformListener* tf) :
00051     tf_(tf)
00052 {
00053   std::string costmap_topic;
00054   std::string footprint_topic;
00055   std::string costmap_updates_topic;
00056   param_nh.param("costmap_topic", costmap_topic, std::string("costmap"));
00057   param_nh.param("costmap_updates_topic", costmap_updates_topic, std::string("costmap_updates"));
00058   param_nh.param("robot_base_frame", robot_base_frame_, std::string("base_link"));
00059   // transform tolerance is used for all tf transforms here
00060   param_nh.param("transform_tolerance", transform_tolerance_, 0.3);
00061 
00062   /* initialize costmap */
00063   boost::function<void(const nav_msgs::OccupancyGrid::ConstPtr&)> costmap_cb =
00064     std::bind(&Costmap2DClient::updateFullMap, this, std::placeholders::_1);
00065   costmap_sub_ = subscription_nh.subscribe(costmap_topic, 1000, costmap_cb);
00066   ROS_INFO("Waiting for costmap to become available, topic: %s",
00067       costmap_topic.c_str());
00068   auto costmap_msg = ros::topic::waitForMessage<nav_msgs::OccupancyGrid>(costmap_topic, subscription_nh);
00069   updateFullMap(costmap_msg);
00070 
00071   /* subscribe to map updates */
00072   boost::function<void(const map_msgs::OccupancyGridUpdate::ConstPtr&)> costmap_updates_cb =
00073     std::bind(&Costmap2DClient::updatePartialMap, this, std::placeholders::_1);
00074   costmap_updates_sub_ = subscription_nh.subscribe(costmap_updates_topic, 1000, costmap_updates_cb);
00075 
00076   /* resolve tf prefix for robot_base_frame */
00077   std::string tf_prefix = tf::getPrefixParam(param_nh);
00078   robot_base_frame_ = tf::resolve(tf_prefix, robot_base_frame_);
00079 
00080   // we need to make sure that the transform between the robot base frame and the global frame is available
00081   /* tf transform is necessary for getRobotPose */
00082   ros::Time last_error = ros::Time::now();
00083   std::string tf_error;
00084   while (ros::ok()
00085       && !tf_->waitForTransform(global_frame_, robot_base_frame_, ros::Time(), ros::Duration(0.1), ros::Duration(0.01),
00086                                &tf_error))
00087   {
00088     ros::spinOnce();
00089     if (last_error + ros::Duration(5.0) < ros::Time::now())
00090     {
00091       ROS_WARN("Timed out waiting for transform from %s to %s to become available "
00092                 "before subscribing to costmap, tf error: %s",
00093                robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
00094       last_error = ros::Time::now();
00095     }
00096     // The error string will accumulate and errors will typically be the same, so the last
00097     // will do for the warning above. Reset the string here to avoid accumulation.
00098     tf_error.clear();
00099   }
00100 }
00101 
00102 void Costmap2DClient::updateFullMap(const nav_msgs::OccupancyGrid::ConstPtr& msg)
00103 {
00104   global_frame_ = msg->header.frame_id;
00105 
00106   unsigned int size_in_cells_x = msg->info.width;
00107   unsigned int size_in_cells_y = msg->info.height;
00108   double resolution = msg->info.resolution;
00109   double origin_x = msg->info.origin.position.x;
00110   double origin_y = msg->info.origin.position.y;
00111 
00112   ROS_DEBUG("received full new map, resizing to: %d, %d", size_in_cells_x, size_in_cells_y);
00113   costmap_.resizeMap(size_in_cells_x, size_in_cells_y, resolution, origin_x, origin_y);
00114 
00115   // lock as we are accessing raw underlying map
00116   auto *mutex = costmap_.getMutex();
00117   std::lock_guard<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
00118 
00119   // fill map with data
00120   unsigned char* costmap_data = costmap_.getCharMap();
00121   size_t costmap_size = costmap_.getSizeInCellsX() * costmap_.getSizeInCellsY();
00122   ROS_DEBUG("full map update, %lu values", costmap_size);
00123   for (size_t i = 0; i < costmap_size && i < msg->data.size(); ++i)
00124   {
00125     unsigned char cell_cost = static_cast<unsigned char>(msg->data[i]);
00126     costmap_data[i] = cost_translation_table__[cell_cost];
00127   }
00128   ROS_DEBUG("map updated, written %lu values", costmap_size);
00129 }
00130 
00131 void Costmap2DClient::updatePartialMap(const map_msgs::OccupancyGridUpdate::ConstPtr& msg)
00132 {
00133   ROS_DEBUG("received partial map update");
00134   global_frame_ = msg->header.frame_id;
00135 
00136   if (msg->x < 0 || msg->y < 0)
00137   {
00138     ROS_ERROR("negative coordinates, invalid update. x: %d, y: %d", msg->x, msg->y);
00139     return;
00140   }
00141 
00142   size_t x0 = static_cast<size_t>(msg->x);
00143   size_t y0 = static_cast<size_t>(msg->y);
00144   size_t xn = msg->width + x0;
00145   size_t yn = msg->height + y0;
00146 
00147   // lock as we are accessing raw underlying map
00148   auto *mutex = costmap_.getMutex();
00149   std::lock_guard<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
00150 
00151   size_t costmap_xn = costmap_.getSizeInCellsX();
00152   size_t costmap_yn = costmap_.getSizeInCellsY();
00153 
00154   if (xn > costmap_xn ||
00155       x0 > costmap_xn ||
00156       yn > costmap_yn ||
00157       y0 > costmap_yn)
00158   {
00159     ROS_WARN("received update doesn't fully fit into existing map, "
00160       "only part will be copied. received: [%lu, %lu], [%lu, %lu] "
00161       "map is: [0, %lu], [0, %lu]", x0, xn, y0, yn, costmap_xn, costmap_yn);
00162   }
00163 
00164   // update map with data
00165   unsigned char* costmap_data = costmap_.getCharMap();
00166   size_t i = 0;
00167   for (size_t y = y0; y < yn && y < costmap_yn; ++y)
00168   {
00169     for (size_t x = x0; x < xn && x < costmap_xn; ++x)
00170     {
00171       size_t idx = costmap_.getIndex(x, y);
00172       unsigned char cell_cost = static_cast<unsigned char>(msg->data[i]);
00173       costmap_data[idx] = cost_translation_table__[cell_cost];
00174       ++i;
00175     }
00176   }
00177 }
00178 
00179 bool Costmap2DClient::getRobotPose(tf::Stamped<tf::Pose>& global_pose) const
00180 {
00181   global_pose.setIdentity();
00182   tf::Stamped < tf::Pose > robot_pose;
00183   robot_pose.setIdentity();
00184   robot_pose.frame_id_ = robot_base_frame_;
00185   robot_pose.stamp_ = ros::Time();
00186   ros::Time current_time = ros::Time::now();  // save time for checking tf delay later
00187 
00188   // get the global pose of the robot
00189   try
00190   {
00191     tf_->transformPose(global_frame_, robot_pose, global_pose);
00192   }
00193   catch (tf::LookupException& ex)
00194   {
00195     ROS_ERROR_THROTTLE(1.0, "No Transform available Error looking up robot pose: %s\n", ex.what());
00196     return false;
00197   }
00198   catch (tf::ConnectivityException& ex)
00199   {
00200     ROS_ERROR_THROTTLE(1.0, "Connectivity Error looking up robot pose: %s\n", ex.what());
00201     return false;
00202   }
00203   catch (tf::ExtrapolationException& ex)
00204   {
00205     ROS_ERROR_THROTTLE(1.0, "Extrapolation Error looking up robot pose: %s\n", ex.what());
00206     return false;
00207   }
00208   // check global_pose timeout
00209   if (current_time.toSec() - global_pose.stamp_.toSec() > transform_tolerance_)
00210   {
00211     ROS_WARN_THROTTLE(1.0,
00212                       "Costmap2DClient transform timeout. Current time: %.4f, global_pose stamp: %.4f, tolerance: %.4f",
00213                       current_time.toSec(), global_pose.stamp_.toSec(), transform_tolerance_);
00214     return false;
00215   }
00216 
00217   return true;
00218 }
00219 
00220 std::array<unsigned char, 256> init_translation_table() {
00221   std::array<unsigned char, 256> cost_translation_table;
00222 
00223   // lineary mapped from [0..100] to [0..255]
00224   for(size_t i = 0; i < 256; ++i) {
00225     cost_translation_table[i] = static_cast<unsigned char>(1 + (251 * (i - 1)) / 97);
00226   }
00227 
00228   // special values:
00229   cost_translation_table[0] = 0;  // NO obstacle
00230   cost_translation_table[99] = 253;  // INSCRIBED obstacle
00231   cost_translation_table[100] = 254;  // LETHAL obstacle
00232   cost_translation_table[static_cast<unsigned char>(-1)] = 255;  // UNKNOWN
00233 
00234   return cost_translation_table;
00235 }
00236 
00237 } // namespace explore


explore
Author(s): Jiri Horner
autogenerated on Sun Mar 26 2017 03:48:06