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 <explore/costmap_client.h>
00038
00039 #include <string>
00040 #include <functional>
00041 #include <mutex>
00042
00043 namespace explore {
00044
00045
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
00060 param_nh.param("transform_tolerance", transform_tolerance_, 0.3);
00061
00062
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
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
00077 std::string tf_prefix = tf::getPrefixParam(param_nh);
00078 robot_base_frame_ = tf::resolve(tf_prefix, robot_base_frame_);
00079
00080
00081
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
00097
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
00116 auto *mutex = costmap_.getMutex();
00117 std::lock_guard<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
00118
00119
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
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
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();
00187
00188
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
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
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
00229 cost_translation_table[0] = 0;
00230 cost_translation_table[99] = 253;
00231 cost_translation_table[100] = 254;
00232 cost_translation_table[static_cast<unsigned char>(-1)] = 255;
00233
00234 return cost_translation_table;
00235 }
00236
00237 }