Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
clear_costmap_service.cpp
1 // Copyright (c) 2018 Intel Corporation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <vector>
16 #include <string>
17 #include <algorithm>
18 #include <memory>
19 
20 #include "nav2_costmap_2d/clear_costmap_service.hpp"
21 #include "nav2_costmap_2d/costmap_2d_ros.hpp"
22 
23 namespace nav2_costmap_2d
24 {
25 
26 using std::vector;
27 using std::string;
28 using std::shared_ptr;
29 using std::any_of;
30 using ClearExceptRegion = nav2_msgs::srv::ClearCostmapExceptRegion;
31 using ClearAroundRobot = nav2_msgs::srv::ClearCostmapAroundRobot;
32 using ClearAroundPose = nav2_msgs::srv::ClearCostmapAroundPose;
33 using ClearEntirely = nav2_msgs::srv::ClearEntireCostmap;
34 
36  const nav2::LifecycleNode::WeakPtr & parent,
37  Costmap2DROS & costmap)
38 : costmap_(costmap)
39 {
40  auto node = parent.lock();
41  logger_ = node->get_logger();
42  reset_value_ = costmap_.getCostmap()->getDefaultValue();
43 
44  clear_except_service_ = node->create_service<ClearExceptRegion>(
45  std::string("clear_except_") + costmap_.getName(),
46  std::bind(
47  &ClearCostmapService::clearExceptRegionCallback, this,
48  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
49 
50  clear_around_service_ = node->create_service<ClearAroundRobot>(
51  std::string("clear_around_") + costmap_.getName(),
52  std::bind(
53  &ClearCostmapService::clearAroundRobotCallback, this,
54  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
55 
56  clear_around_pose_service_ = node->create_service<ClearAroundPose>(
57  std::string("clear_around_pose_") + costmap_.getName(),
58  std::bind(
59  &ClearCostmapService::clearAroundPoseCallback, this,
60  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
61 
62  clear_entire_service_ = node->create_service<ClearEntirely>(
63  std::string("clear_entirely_") + costmap_.getName(),
64  std::bind(
65  &ClearCostmapService::clearEntireCallback, this,
66  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
67 }
68 
70 {
71  // make sure services shutdown.
72  clear_except_service_.reset();
73  clear_around_service_.reset();
74  clear_around_pose_service_.reset();
75  clear_entire_service_.reset();
76 }
77 
78 void ClearCostmapService::clearExceptRegionCallback(
79  const shared_ptr<rmw_request_id_t>/*request_header*/,
80  const shared_ptr<ClearExceptRegion::Request> request,
81  const shared_ptr<ClearExceptRegion::Response> response)
82 {
83  RCLCPP_INFO_STREAM(
84  logger_,
85  "Received request to clear except a region for " <<
86  (request->plugins.empty() ? "all layers" : "specific layers") << " in " <<
87  costmap_.getName());
88 
89  response->success = clearRegion(request->reset_distance, true, request->plugins);
90 }
91 
92 void ClearCostmapService::clearAroundRobotCallback(
93  const shared_ptr<rmw_request_id_t>/*request_header*/,
94  const shared_ptr<ClearAroundRobot::Request> request,
95  const shared_ptr<ClearAroundRobot::Response> response)
96 {
97  RCLCPP_INFO_STREAM(
98  logger_,
99  "Received request to clear around robot for " <<
100  (request->plugins.empty() ? "all layers" : "specific layers") << " in " <<
101  costmap_.getName());
102 
103  response->success = clearRegion(request->reset_distance, false, request->plugins);
104 }
105 
106 void ClearCostmapService::clearAroundPoseCallback(
107  const shared_ptr<rmw_request_id_t>/*request_header*/,
108  const shared_ptr<ClearAroundPose::Request> request,
109  const shared_ptr<ClearAroundPose::Response> response)
110 {
111  RCLCPP_INFO_STREAM(
112  logger_,
113  "Received request to clear around pose for " <<
114  (request->plugins.empty() ? "all layers" : "specific layers") << " in " <<
115  costmap_.getName());
116 
117  response->success = clearAroundPose(request->pose, request->reset_distance, request->plugins);
118 }
119 
120 void ClearCostmapService::clearEntireCallback(
121  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
122  const std::shared_ptr<ClearEntirely::Request> request,
123  const std::shared_ptr<ClearEntirely::Response> response)
124 {
125  RCLCPP_INFO_STREAM(
126  logger_,
127  "Received request to clear entirely for " <<
128  (request->plugins.empty() ? "all layers" : "specific layers") << " in " <<
129  costmap_.getName());
130 
131  response->success = clearEntirely(request->plugins);
132 }
133 
135  const geometry_msgs::msg::PoseStamped & pose,
136  const double reset_distance,
137  const std::vector<std::string> & plugins)
138 {
139  double x, y;
140 
141  // Transform pose to costmap frame if necessary
142  geometry_msgs::msg::PoseStamped global_pose;
143  try {
144  if (pose.header.frame_id == costmap_.getGlobalFrameID()) {
145  global_pose = pose;
146  } else {
147  costmap_.getTfBuffer()->transform(pose, global_pose, costmap_.getGlobalFrameID());
148  }
149  } catch (tf2::TransformException & ex) {
150  RCLCPP_ERROR(
151  logger_,
152  "Cannot clear map around pose because pose cannot be transformed to costmap frame: %s",
153  ex.what());
154  return false;
155  }
156 
157  x = global_pose.pose.position.x;
158  y = global_pose.pose.position.y;
159 
160  auto layers = costmap_.getLayeredCostmap()->getPlugins();
161 
162  if (!plugins.empty()) {
163  return validateAndClearPlugins(
164  plugins, layers,
165  [this, x, y, reset_distance](std::shared_ptr<CostmapLayer> & layer) {
166  clearLayerRegion(layer, x, y, reset_distance, false);
167  },
168  "clear costmap around pose");
169  } else {
170  // Clear all clearable layers (default behavior)
171  for (auto & layer : *layers) {
172  if (layer->isClearable()) {
173  auto costmap_layer = std::static_pointer_cast<CostmapLayer>(layer);
174  clearLayerRegion(costmap_layer, x, y, reset_distance, false);
175  }
176  }
177  return true;
178  }
179 }
180 
182  const double reset_distance, bool invert,
183  const std::vector<std::string> & plugins)
184 {
185  double x, y;
186 
187  if (!getPosition(x, y)) {
188  RCLCPP_ERROR(
189  logger_, "%s",
190  "Cannot clear map because robot pose cannot be retrieved.");
191  return false;
192  }
193 
194  auto layers = costmap_.getLayeredCostmap()->getPlugins();
195 
196  if (!plugins.empty()) {
197  return validateAndClearPlugins(
198  plugins, layers,
199  [this, x, y, reset_distance, invert](std::shared_ptr<CostmapLayer> & layer) {
200  clearLayerRegion(layer, x, y, reset_distance, invert);
201  },
202  "clear costmap region");
203  } else {
204  // Clear all clearable layers (default behavior)
205  for (auto & layer : *layers) {
206  if (layer->isClearable()) {
207  auto costmap_layer = std::static_pointer_cast<CostmapLayer>(layer);
208  clearLayerRegion(costmap_layer, x, y, reset_distance, invert);
209  }
210  }
211  return true;
212  }
213 }
214 
215 void ClearCostmapService::clearLayerRegion(
216  shared_ptr<CostmapLayer> & costmap, double pose_x, double pose_y, double reset_distance,
217  bool invert)
218 {
219  std::unique_lock<Costmap2D::mutex_t> lock(*(costmap->getMutex()));
220 
221  double start_point_x = pose_x - reset_distance / 2;
222  double start_point_y = pose_y - reset_distance / 2;
223  double end_point_x = start_point_x + reset_distance;
224  double end_point_y = start_point_y + reset_distance;
225 
226  int start_x, start_y, end_x, end_y;
227  costmap->worldToMapNoBounds(start_point_x, start_point_y, start_x, start_y);
228  costmap->worldToMapNoBounds(end_point_x, end_point_y, end_x, end_y);
229 
230  costmap->clearArea(start_x, start_y, end_x, end_y, invert);
231 
232  double ox = costmap->getOriginX(), oy = costmap->getOriginY();
233  double width = costmap->getSizeInMetersX(), height = costmap->getSizeInMetersY();
234  costmap->addExtraBounds(ox, oy, ox + width, oy + height);
235 }
236 
237 bool ClearCostmapService::clearEntirely(const std::vector<std::string> & plugins)
238 {
239  if (plugins.empty()) {
240  // Default behavior: clear all layers
241  std::unique_lock<Costmap2D::mutex_t> lock(*(costmap_.getCostmap()->getMutex()));
242  RCLCPP_INFO(logger_, "Clearing all layers in costmap: %s", costmap_.getName().c_str());
243  costmap_.resetLayers();
244  return true;
245  } else {
246  // Clear only specified plugins
247  std::unique_lock<Costmap2D::mutex_t> lock(*(costmap_.getCostmap()->getMutex()));
248  auto layers = costmap_.getLayeredCostmap()->getPlugins();
249 
250  bool result = validateAndClearPlugins(
251  plugins, layers,
252  [](const std::shared_ptr<CostmapLayer> & layer) {
253  layer->resetMap(0, 0, layer->getSizeInCellsX(), layer->getSizeInCellsY());
254  },
255  "clear costmap entirely");
256 
257  if (result) {
258  RCLCPP_INFO(logger_, "Resetting master costmap after plugin clearing");
259  costmap_.getCostmap()->resetMap(0, 0,
260  costmap_.getCostmap()->getSizeInCellsX(),
261  costmap_.getCostmap()->getSizeInCellsY());
262  }
263 
264  return result;
265  }
266 }
267 
268 void ClearCostmapService::validatePlugins(
269  const std::vector<std::string> & requested_plugins,
270  const std::vector<std::shared_ptr<Layer>> * layers,
271  std::vector<std::string> & invalid_plugins) const
272 {
273  invalid_plugins.clear();
274 
275  for (const auto & requested_plugin : requested_plugins) {
276  bool found = false;
277  bool clearable = false;
278 
279  for (auto & layer : *layers) {
280  if (layer->getName() == requested_plugin) {
281  found = true;
282  clearable = layer->isClearable();
283  break;
284  }
285  }
286 
287  if (!found) {
288  invalid_plugins.push_back(requested_plugin + " (not found)");
289  } else if (!clearable) {
290  invalid_plugins.push_back(requested_plugin + " (not clearable)");
291  }
292  }
293 }
294 
295 bool ClearCostmapService::validateAndClearPlugins(
296  const std::vector<std::string> & plugins,
297  const std::vector<std::shared_ptr<Layer>> * layers,
298  std::function<void(std::shared_ptr<CostmapLayer> &)> clear_callback,
299  const std::string & operation_name) const
300 {
301  std::vector<std::string> invalid_plugins;
302 
303  validatePlugins(plugins, layers, invalid_plugins);
304 
305  if (!invalid_plugins.empty()) {
306  std::string error_msg = "Invalid plugin(s) requested for clearing: ";
307  for (size_t i = 0; i < invalid_plugins.size(); ++i) {
308  error_msg += invalid_plugins[i];
309  if (i < invalid_plugins.size() - 1) {
310  error_msg += ", ";
311  }
312  }
313  RCLCPP_ERROR(logger_, "%s", error_msg.c_str());
314  RCLCPP_ERROR(
315  logger_,
316  "Failed to %s: %zu invalid plugin(s) out of %zu requested. No layers were cleared.",
317  operation_name.c_str(), invalid_plugins.size(), plugins.size());
318  return false;
319  }
320 
321  for (auto & layer : *layers) {
322  if (std::find(plugins.begin(), plugins.end(),
323  layer->getName()) != plugins.end())
324  {
325  auto costmap_layer = std::static_pointer_cast<CostmapLayer>(layer);
326  clear_callback(costmap_layer);
327  RCLCPP_INFO(logger_, "Performed action '%s' on layer: %s", operation_name.c_str(),
328  layer->getName().c_str());
329  }
330  }
331 
332  return true;
333 }
334 
335 bool ClearCostmapService::getPosition(double & x, double & y) const
336 {
337  geometry_msgs::msg::PoseStamped pose;
338  if (!costmap_.getRobotPose(pose)) {
339  return false;
340  }
341 
342  x = pose.pose.position.x;
343  y = pose.pose.position.y;
344 
345  return true;
346 }
347 
348 } // namespace nav2_costmap_2d
bool clearAroundPose(const geometry_msgs::msg::PoseStamped &pose, double reset_distance, const std::vector< std::string > &plugins)
Clears the region around a specific pose.
bool clearEntirely(const std::vector< std::string > &plugins)
Clears the entire layer.
ClearCostmapService()=delete
A constructor.
bool clearRegion(double reset_distance, bool invert, const std::vector< std::string > &plugins)
Clears the region outside of a user-specified area reverting to the static map.
A ROS wrapper for a 2D Costmap. Handles subscribing to topics that provide observations about obstacl...
bool getRobotPose(geometry_msgs::msg::PoseStamped &global_pose)
Get the pose of the robot in the global frame of the costmap.
std::string getName() const
Returns costmap name.
LayeredCostmap * getLayeredCostmap()
Get the layered costmap object used in the node.
void resetLayers()
Reset each individual layer.
std::string getGlobalFrameID()
Returns the global frame of the costmap.
Costmap2D * getCostmap()
Return a pointer to the "master" costmap which receives updates from all the layers.
void resetMap(unsigned int x0, unsigned int y0, unsigned int xn, unsigned int yn)
Reset the costmap in bounds.
Definition: costmap_2d.cpp:131
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:553
unsigned char getDefaultValue()
Get the default background value of the costmap.
Definition: costmap_2d.hpp:309
std::vector< std::shared_ptr< Layer > > * getPlugins()
Get the vector of pointers to the costmap plugins.