Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
binary_filter.cpp
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2022 Samsung R&D Institute Russia
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 <ORGANIZATION> 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  * Author: Alexey Merzlyakov
36  *********************************************************************/
37 
38 #include "nav2_costmap_2d/costmap_filters/binary_filter.hpp"
39 
40 #include <cmath>
41 #include <utility>
42 #include <memory>
43 #include <string>
44 
45 #include "nav2_costmap_2d/costmap_filters/filter_values.hpp"
46 #include "nav2_util/occ_grid_values.hpp"
47 
48 namespace nav2_costmap_2d
49 {
50 
52 : filter_info_sub_(nullptr), mask_sub_(nullptr),
53  binary_state_pub_(nullptr), filter_mask_(nullptr), mask_frame_(""), global_frame_(""),
54  default_state_(false), binary_state_(default_state_)
55 {
56 }
57 
59  const std::string & filter_info_topic)
60 {
61  std::lock_guard<CostmapFilter::mutex_t> guard(*getMutex());
62 
63  rclcpp_lifecycle::LifecycleNode::SharedPtr node = node_.lock();
64  if (!node) {
65  throw std::runtime_error{"Failed to lock node"};
66  }
67 
68  // Declare parameters specific to BinaryFilter only
69  std::string binary_state_topic;
70  declareParameter("default_state", rclcpp::ParameterValue(false));
71  node->get_parameter(name_ + "." + "default_state", default_state_);
72  declareParameter("binary_state_topic", rclcpp::ParameterValue("binary_state"));
73  node->get_parameter(name_ + "." + "binary_state_topic", binary_state_topic);
74  declareParameter("flip_threshold", rclcpp::ParameterValue(50.0));
75  node->get_parameter(name_ + "." + "flip_threshold", flip_threshold_);
76 
77  filter_info_topic_ = filter_info_topic;
78  // Setting new costmap filter info subscriber
79  RCLCPP_INFO(
80  logger_,
81  "BinaryFilter: Subscribing to \"%s\" topic for filter info...",
82  filter_info_topic_.c_str());
83  filter_info_sub_ = node->create_subscription<nav2_msgs::msg::CostmapFilterInfo>(
84  filter_info_topic_, rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
85  std::bind(&BinaryFilter::filterInfoCallback, this, std::placeholders::_1));
86 
87  // Get global frame required for binary state publisher
88  global_frame_ = layered_costmap_->getGlobalFrameID();
89 
90  // Create new binary state publisher
91  binary_state_pub_ = node->create_publisher<std_msgs::msg::Bool>(
92  binary_state_topic, rclcpp::QoS(10));
93  binary_state_pub_->on_activate();
94 
95  // Reset parameters
96  base_ = BASE_DEFAULT;
97  multiplier_ = MULTIPLIER_DEFAULT;
98 
99  // Initialize state as "false" by-default
100  changeState(default_state_);
101 }
102 
103 void BinaryFilter::filterInfoCallback(
104  const nav2_msgs::msg::CostmapFilterInfo::SharedPtr msg)
105 {
106  std::lock_guard<CostmapFilter::mutex_t> guard(*getMutex());
107 
108  rclcpp_lifecycle::LifecycleNode::SharedPtr node = node_.lock();
109  if (!node) {
110  throw std::runtime_error{"Failed to lock node"};
111  }
112 
113  if (!mask_sub_) {
114  RCLCPP_INFO(
115  logger_,
116  "BinaryFilter: Received filter info from %s topic.", filter_info_topic_.c_str());
117  } else {
118  RCLCPP_WARN(
119  logger_,
120  "BinaryFilter: New costmap filter info arrived from %s topic. Updating old filter info.",
121  filter_info_topic_.c_str());
122  // Resetting previous subscriber each time when new costmap filter information arrives
123  mask_sub_.reset();
124  }
125 
126  if (msg->type != BINARY_FILTER) {
127  RCLCPP_ERROR(logger_, "BinaryFilter: Mode %i is not supported", msg->type);
128  return;
129  }
130 
131  // Set base_ and multiplier_
132  base_ = msg->base;
133  multiplier_ = msg->multiplier;
134  // Set topic name to receive filter mask from
135  mask_topic_ = msg->filter_mask_topic;
136 
137  // Setting new filter mask subscriber
138  RCLCPP_INFO(
139  logger_,
140  "BinaryFilter: Subscribing to \"%s\" topic for filter mask...",
141  mask_topic_.c_str());
142  mask_sub_ = node->create_subscription<nav_msgs::msg::OccupancyGrid>(
143  mask_topic_, rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
144  std::bind(&BinaryFilter::maskCallback, this, std::placeholders::_1));
145 }
146 
147 void BinaryFilter::maskCallback(
148  const nav_msgs::msg::OccupancyGrid::SharedPtr msg)
149 {
150  std::lock_guard<CostmapFilter::mutex_t> guard(*getMutex());
151 
152  if (!filter_mask_) {
153  RCLCPP_INFO(
154  logger_,
155  "BinaryFilter: Received filter mask from %s topic.", mask_topic_.c_str());
156  } else {
157  RCLCPP_WARN(
158  logger_,
159  "BinaryFilter: New filter mask arrived from %s topic. Updating old filter mask.",
160  mask_topic_.c_str());
161  filter_mask_.reset();
162  }
163 
164  filter_mask_ = msg;
165  mask_frame_ = msg->header.frame_id;
166 }
167 
169  nav2_costmap_2d::Costmap2D & /*master_grid*/,
170  int /*min_i*/, int /*min_j*/, int /*max_i*/, int /*max_j*/,
171  const geometry_msgs::msg::Pose2D & pose)
172 {
173  std::lock_guard<CostmapFilter::mutex_t> guard(*getMutex());
174 
175  if (!filter_mask_) {
176  // Show warning message every 2 seconds to not litter an output
177  RCLCPP_WARN_THROTTLE(
178  logger_, *(clock_), 2000,
179  "BinaryFilter: Filter mask was not received");
180  return;
181  }
182 
183  geometry_msgs::msg::Pose2D mask_pose; // robot coordinates in mask frame
184 
185  // Transforming robot pose from current layer frame to mask frame
186  if (!transformPose(global_frame_, pose, mask_frame_, mask_pose)) {
187  return;
188  }
189 
190  // Converting mask_pose robot position to filter_mask_ indexes (mask_robot_i, mask_robot_j)
191  unsigned int mask_robot_i, mask_robot_j;
192  if (!worldToMask(filter_mask_, mask_pose.x, mask_pose.y, mask_robot_i, mask_robot_j)) {
193  // Robot went out of mask range. Set "false" state by-default
194  RCLCPP_WARN(
195  logger_,
196  "BinaryFilter: Robot is outside of filter mask. Resetting binary state to default.");
197  changeState(default_state_);
198  return;
199  }
200 
201  // Getting filter_mask data from cell where the robot placed
202  int8_t mask_data = getMaskData(filter_mask_, mask_robot_i, mask_robot_j);
203  if (mask_data == nav2_util::OCC_GRID_UNKNOWN) {
204  // Corresponding filter mask cell is unknown.
205  // Warn and do nothing.
206  RCLCPP_WARN_THROTTLE(
207  logger_, *(clock_), 2000,
208  "BinaryFilter: Filter mask [%i, %i] data is unknown. Do nothing.",
209  mask_robot_i, mask_robot_j);
210  return;
211  }
212  // Check and flip binary state, if necessary
213  if (base_ + mask_data * multiplier_ > flip_threshold_) {
214  if (binary_state_ == default_state_) {
215  changeState(!default_state_);
216  }
217  } else {
218  if (binary_state_ != default_state_) {
219  changeState(default_state_);
220  }
221  }
222 }
223 
225 {
226  std::lock_guard<CostmapFilter::mutex_t> guard(*getMutex());
227 
228  RCLCPP_INFO(logger_, "BinaryFilter: Resetting the filter to default state");
229  changeState(default_state_);
230 
231  filter_info_sub_.reset();
232  mask_sub_.reset();
233  if (binary_state_pub_) {
234  binary_state_pub_->on_deactivate();
235  binary_state_pub_.reset();
236  }
237 }
238 
240 {
241  std::lock_guard<CostmapFilter::mutex_t> guard(*getMutex());
242 
243  if (filter_mask_) {
244  return true;
245  }
246  return false;
247 }
248 
249 void BinaryFilter::changeState(const bool state)
250 {
251  binary_state_ = state;
252  if (state) {
253  RCLCPP_INFO(logger_, "BinaryFilter: Switched on");
254  } else {
255  RCLCPP_INFO(logger_, "BinaryFilter: Switched off");
256  }
257 
258  // Forming and publishing new BinaryState message
259  std::unique_ptr<std_msgs::msg::Bool> msg =
260  std::make_unique<std_msgs::msg::Bool>();
261  msg->data = state;
262  binary_state_pub_->publish(std::move(msg));
263 }
264 
265 } // namespace nav2_costmap_2d
266 
267 #include "pluginlib/class_list_macros.hpp"
Reads in a speed restriction mask and enables a robot to dynamically adjust speed based on pose in ma...
void initializeFilter(const std::string &filter_info_topic)
Initialize the filter and subscribe to the info topic.
void process(nav2_costmap_2d::Costmap2D &master_grid, int min_i, int min_j, int max_i, int max_j, const geometry_msgs::msg::Pose2D &pose)
Process the keepout layer at the current pose / bounds / grid.
bool isActive()
If this filter is active.
void resetFilter()
Reset the costmap filter / topic / info.
A 2D costmap provides a mapping between points in the world and their associated "costs".
Definition: costmap_2d.hpp:68
int8_t getMaskData(nav_msgs::msg::OccupancyGrid::ConstSharedPtr filter_mask, const unsigned int mx, const unsigned int my) const
Get the data of a cell in the filter mask.
bool worldToMask(nav_msgs::msg::OccupancyGrid::ConstSharedPtr filter_mask, double wx, double wy, unsigned int &mx, unsigned int &my) const
: Convert from world coordinates to mask coordinates. Similar to Costmap2D::worldToMap() method but w...
bool transformPose(const std::string global_frame, const geometry_msgs::msg::Pose2D &global_pose, const std::string mask_frame, geometry_msgs::msg::Pose2D &mask_pose) const
: Transforms robot pose from current layer frame to mask frame
std::string filter_info_topic_
: Name of costmap filter info topic
std::string mask_topic_
: Name of filter mask topic
mutex_t * getMutex()
: returns pointer to a mutex
Abstract class for layered costmap plugin implementations.
Definition: layer.hpp:59
void declareParameter(const std::string &param_name, const rclcpp::ParameterValue &value)
Convenience functions for declaring ROS parameters.
Definition: layer.cpp:76