costmap_client.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2015-2016, Jiri Horner.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of the Jiri Horner nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  *********************************************************************/
36 
37 #include <explore/costmap_client.h>
38 
39 #include <functional>
40 #include <mutex>
41 #include <string>
42 
43 namespace explore
44 {
45 // static translation table to speed things up
46 std::array<unsigned char, 256> init_translation_table();
47 static const std::array<unsigned char, 256> cost_translation_table__ =
49 
51  ros::NodeHandle& subscription_nh,
53  : tf_(tf)
54 {
55  std::string costmap_topic;
56  std::string footprint_topic;
57  std::string costmap_updates_topic;
58  param_nh.param("costmap_topic", costmap_topic, std::string("costmap"));
59  param_nh.param("costmap_updates_topic", costmap_updates_topic,
60  std::string("costmap_updates"));
61  param_nh.param("robot_base_frame", robot_base_frame_,
62  std::string("base_link"));
63  // transform tolerance is used for all tf transforms here
64  param_nh.param("transform_tolerance", transform_tolerance_, 0.3);
65 
66  /* initialize costmap */
67  costmap_sub_ = subscription_nh.subscribe<nav_msgs::OccupancyGrid>(
68  costmap_topic, 1000,
69  [this](const nav_msgs::OccupancyGrid::ConstPtr& msg) {
70  updateFullMap(msg);
71  });
72  ROS_INFO("Waiting for costmap to become available, topic: %s",
73  costmap_topic.c_str());
74  auto costmap_msg = ros::topic::waitForMessage<nav_msgs::OccupancyGrid>(
75  costmap_topic, subscription_nh);
76  updateFullMap(costmap_msg);
77 
78  /* subscribe to map updates */
80  subscription_nh.subscribe<map_msgs::OccupancyGridUpdate>(
81  costmap_updates_topic, 1000,
82  [this](const map_msgs::OccupancyGridUpdate::ConstPtr& msg) {
83  updatePartialMap(msg);
84  });
85 
86  /* resolve tf prefix for robot_base_frame */
87  std::string tf_prefix = tf::getPrefixParam(param_nh);
89 
90  // we need to make sure that the transform between the robot base frame and
91  // the global frame is available
92  /* tf transform is necessary for getRobotPose */
93  ros::Time last_error = ros::Time::now();
94  std::string tf_error;
95  while (ros::ok() &&
97  ros::Duration(0.1), ros::Duration(0.01),
98  &tf_error)) {
99  ros::spinOnce();
100  if (last_error + ros::Duration(5.0) < ros::Time::now()) {
101  ROS_WARN(
102  "Timed out waiting for transform from %s to %s to become available "
103  "before subscribing to costmap, tf error: %s",
104  robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
105  last_error = ros::Time::now();
106  }
107  // The error string will accumulate and errors will typically be the same,
108  // so the last
109  // will do for the warning above. Reset the string here to avoid
110  // accumulation.
111  tf_error.clear();
112  }
113 }
114 
115 void Costmap2DClient::updateFullMap(const nav_msgs::OccupancyGrid::ConstPtr& msg)
116 {
117  global_frame_ = msg->header.frame_id;
118 
119  unsigned int size_in_cells_x = msg->info.width;
120  unsigned int size_in_cells_y = msg->info.height;
121  double resolution = msg->info.resolution;
122  double origin_x = msg->info.origin.position.x;
123  double origin_y = msg->info.origin.position.y;
124 
125  ROS_DEBUG("received full new map, resizing to: %d, %d", size_in_cells_x,
126  size_in_cells_y);
127  costmap_.resizeMap(size_in_cells_x, size_in_cells_y, resolution, origin_x,
128  origin_y);
129 
130  // lock as we are accessing raw underlying map
131  auto* mutex = costmap_.getMutex();
132  std::lock_guard<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
133 
134  // fill map with data
135  unsigned char* costmap_data = costmap_.getCharMap();
136  size_t costmap_size = costmap_.getSizeInCellsX() * costmap_.getSizeInCellsY();
137  ROS_DEBUG("full map update, %lu values", costmap_size);
138  for (size_t i = 0; i < costmap_size && i < msg->data.size(); ++i) {
139  unsigned char cell_cost = static_cast<unsigned char>(msg->data[i]);
140  costmap_data[i] = cost_translation_table__[cell_cost];
141  }
142  ROS_DEBUG("map updated, written %lu values", costmap_size);
143 }
144 
146  const map_msgs::OccupancyGridUpdate::ConstPtr& msg)
147 {
148  ROS_DEBUG("received partial map update");
149  global_frame_ = msg->header.frame_id;
150 
151  if (msg->x < 0 || msg->y < 0) {
152  ROS_ERROR("negative coordinates, invalid update. x: %d, y: %d", msg->x,
153  msg->y);
154  return;
155  }
156 
157  size_t x0 = static_cast<size_t>(msg->x);
158  size_t y0 = static_cast<size_t>(msg->y);
159  size_t xn = msg->width + x0;
160  size_t yn = msg->height + y0;
161 
162  // lock as we are accessing raw underlying map
163  auto* mutex = costmap_.getMutex();
164  std::lock_guard<costmap_2d::Costmap2D::mutex_t> lock(*mutex);
165 
166  size_t costmap_xn = costmap_.getSizeInCellsX();
167  size_t costmap_yn = costmap_.getSizeInCellsY();
168 
169  if (xn > costmap_xn || x0 > costmap_xn || yn > costmap_yn ||
170  y0 > costmap_yn) {
171  ROS_WARN("received update doesn't fully fit into existing map, "
172  "only part will be copied. received: [%lu, %lu], [%lu, %lu] "
173  "map is: [0, %lu], [0, %lu]",
174  x0, xn, y0, yn, costmap_xn, costmap_yn);
175  }
176 
177  // update map with data
178  unsigned char* costmap_data = costmap_.getCharMap();
179  size_t i = 0;
180  for (size_t y = y0; y < yn && y < costmap_yn; ++y) {
181  for (size_t x = x0; x < xn && x < costmap_xn; ++x) {
182  size_t idx = costmap_.getIndex(x, y);
183  unsigned char cell_cost = static_cast<unsigned char>(msg->data[i]);
184  costmap_data[idx] = cost_translation_table__[cell_cost];
185  ++i;
186  }
187  }
188 }
189 
190 geometry_msgs::Pose Costmap2DClient::getRobotPose() const
191 {
192  tf::Stamped<tf::Pose> global_pose;
193  global_pose.setIdentity();
194  tf::Stamped<tf::Pose> robot_pose;
195  robot_pose.setIdentity();
196  robot_pose.frame_id_ = robot_base_frame_;
197  robot_pose.stamp_ = ros::Time();
198  ros::Time current_time =
199  ros::Time::now(); // save time for checking tf delay later
200 
201  // get the global pose of the robot
202  try {
203  tf_->transformPose(global_frame_, robot_pose, global_pose);
204  } catch (tf::LookupException& ex) {
205  ROS_ERROR_THROTTLE(1.0, "No Transform available Error looking up robot "
206  "pose: %s\n",
207  ex.what());
208  return {};
209  } catch (tf::ConnectivityException& ex) {
210  ROS_ERROR_THROTTLE(1.0, "Connectivity Error looking up robot pose: %s\n",
211  ex.what());
212  return {};
213  } catch (tf::ExtrapolationException& ex) {
214  ROS_ERROR_THROTTLE(1.0, "Extrapolation Error looking up robot pose: %s\n",
215  ex.what());
216  return {};
217  }
218  // check global_pose timeout
219  if (current_time.toSec() - global_pose.stamp_.toSec() >
221  ROS_WARN_THROTTLE(1.0, "Costmap2DClient transform timeout. Current time: "
222  "%.4f, global_pose stamp: %.4f, tolerance: %.4f",
223  current_time.toSec(), global_pose.stamp_.toSec(),
225  return {};
226  }
227 
228  geometry_msgs::PoseStamped msg;
229  tf::poseStampedTFToMsg(global_pose, msg);
230  return msg.pose;
231 }
232 
233 std::array<unsigned char, 256> init_translation_table()
234 {
235  std::array<unsigned char, 256> cost_translation_table;
236 
237  // lineary mapped from [0..100] to [0..255]
238  for (size_t i = 0; i < 256; ++i) {
239  cost_translation_table[i] =
240  static_cast<unsigned char>(1 + (251 * (i - 1)) / 97);
241  }
242 
243  // special values:
244  cost_translation_table[0] = 0; // NO obstacle
245  cost_translation_table[99] = 253; // INSCRIBED obstacle
246  cost_translation_table[100] = 254; // LETHAL obstacle
247  cost_translation_table[static_cast<unsigned char>(-1)] = 255; // UNKNOWN
248 
249  return cost_translation_table;
250 }
251 
252 } // namespace explore
std::array< unsigned char, 256 > init_translation_table()
void resizeMap(unsigned int size_x, unsigned int size_y, double resolution, double origin_x, double origin_y)
#define ROS_WARN_THROTTLE(rate,...)
std::string getPrefixParam(ros::NodeHandle &nh)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
static void poseStampedTFToMsg(const Stamped< Pose > &bt, geometry_msgs::PoseStamped &msg)
double transform_tolerance_
timeout before transform errors
geometry_msgs::Pose getRobotPose() const
Get the pose of the robot in the global frame of the costmap.
void updatePartialMap(const map_msgs::OccupancyGridUpdate::ConstPtr &msg)
tf::TransformListener * tf_
TFSIMD_FORCE_INLINE const tfScalar & y() const
#define ROS_WARN(...)
unsigned char * getCharMap() const
ros::Subscriber costmap_updates_sub_
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
void updateFullMap(const nav_msgs::OccupancyGrid::ConstPtr &msg)
std::string resolve(const std::string &prefix, const std::string &frame_name)
#define ROS_ERROR_THROTTLE(rate,...)
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ROSCPP_DECL bool ok()
TFSIMD_FORCE_INLINE const tfScalar & x() const
unsigned int getSizeInCellsY() const
ros::Subscriber costmap_sub_
ros::Time stamp_
std::string robot_base_frame_
The frame_id of the robot base.
unsigned int getSizeInCellsX() const
costmap_2d::Costmap2D costmap_
std::string frame_id_
void transformPose(const std::string &target_frame, const geometry_msgs::PoseStamped &stamped_in, geometry_msgs::PoseStamped &stamped_out) const
Costmap2DClient(ros::NodeHandle &param_nh, ros::NodeHandle &subscription_nh, const tf::TransformListener *tf_listener)
Contructs client and start listening.
static Time now()
const tf::TransformListener *const tf_
Used for transforming point clouds.
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
std::string global_frame_
The global frame for the costmap.
unsigned int getIndex(unsigned int mx, unsigned int my) const
static const std::array< unsigned char, 256 > cost_translation_table__
#define ROS_DEBUG(...)


explore
Author(s): Jiri Horner
autogenerated on Mon Jun 10 2019 13:56:49