Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
source.cpp
1 // Copyright (c) 2022 Samsung R&D Institute Russia
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 "nav2_collision_monitor/source.hpp"
16 
17 #include <algorithm>
18 #include <exception>
19 #include <iterator>
20 #include <string>
21 
22 #include "geometry_msgs/msg/transform_stamped.hpp"
23 
24 #include "nav2_ros_common/node_utils.hpp"
25 #include "nav2_util/robot_utils.hpp"
26 #include "nav2_ros_common/tf2_factories.hpp"
27 
28 namespace nav2_collision_monitor
29 {
30 
32  const nav2::LifecycleNode::WeakPtr & node,
33  const std::string & source_name,
34  const nav2::TransformBuffer::SharedPtr tf_buffer,
35  const std::string & base_frame_id,
36  const std::string & global_frame_id,
37  const tf2::Duration & transform_tolerance,
38  const rclcpp::Duration & source_timeout,
39  const bool base_shift_correction)
40 : node_(node), source_name_(source_name), tf_buffer_(tf_buffer),
41  base_frame_id_(base_frame_id), global_frame_id_(global_frame_id),
42  transform_tolerance_(transform_tolerance), source_timeout_(source_timeout),
43  base_shift_correction_(base_shift_correction)
44 {
45 }
46 
48 {
49  add_ez_service_.reset();
50  remove_ez_service_.reset();
51  auto node = node_.lock();
52  if (post_set_params_handler_ && node) {
53  node->remove_post_set_parameters_callback(post_set_params_handler_.get());
54  }
55  post_set_params_handler_.reset();
56  if (on_set_params_handler_ && node) {
57  node->remove_on_set_parameters_callback(on_set_params_handler_.get());
58  }
59  on_set_params_handler_.reset();
60 }
61 
63 {
64  auto node = node_.lock();
65  if (!node) {
66  throw std::runtime_error{"Failed to lock node"};
67  }
68 
69  // Configure the exclusion zones (if any) declared for this source
70  const std::vector<std::string> zone_names =
71  node->declare_or_get_parameter<std::vector<std::string>>(
72  source_name_ + ".exclusion_zones", std::vector<std::string>());
73 
74  for (const std::string & zone_name : zone_names) {
75  auto zone = std::make_shared<ExclusionZone>(
76  node, zone_name, tf_buffer_, base_frame_id_, global_frame_id_,
78  if (!zone->configure()) {
79  RCLCPP_ERROR(
80  logger_, "[%s]: Failed to configure exclusion zone '%s'",
81  source_name_.c_str(), zone_name.c_str());
82  return false;
83  }
84  exclusion_zones_.push_back(zone);
85  }
86 
87  // Add callback for dynamic parameters
88  post_set_params_handler_ = node->add_post_set_parameters_callback(
89  std::bind(
91  this, std::placeholders::_1));
92  on_set_params_handler_ = node->add_on_set_parameters_callback(
93  std::bind(
95  this, std::placeholders::_1));
96 
97  // Create services for runtime add/remove of exclusion zones
98  add_ez_service_ = node->create_service<nav2_msgs::srv::AddExclusionZone>(
99  "~/" + source_name_ + "/add_exclusion_zone",
100  std::bind(
102  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
103  remove_ez_service_ = node->create_service<nav2_msgs::srv::RemoveExclusionZone>(
104  "~/" + source_name_ + "/remove_exclusion_zone",
105  std::bind(
107  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
108 
109  return true;
110 }
111 
113  const rclcpp::Time & curr_time,
114  std::vector<Point> & data)
115 {
116  // Collect this source's points into a private buffer so exclusion-zone
117  // masking only ever considers data produced by this source.
118  std::vector<Point> source_data;
119  if (!getSourceData(curr_time, source_data)) {
120  return false;
121  }
122 
123  // Mask out points that fall inside any enabled exclusion zone.
124  if (!exclusion_zones_.empty() && !source_data.empty()) {
125  for (const auto & zone : exclusion_zones_) {
126  zone->apply(curr_time, source_data);
127  }
128  }
129 
130  // Append the surviving points to the caller's array.
131  data.insert(
132  data.end(),
133  std::make_move_iterator(source_data.begin()),
134  std::make_move_iterator(source_data.end()));
135 
136  return true;
137 }
138 
140 {
141  for (const auto & zone : exclusion_zones_) {
142  zone->activate();
143  }
144 }
145 
147 {
148  for (const auto & zone : exclusion_zones_) {
149  zone->deactivate();
150  }
151 }
152 
154 {
155  for (const auto & zone : exclusion_zones_) {
156  zone->publish();
157  }
158 }
159 
160 void Source::getCommonParameters(std::string & source_topic)
161 {
162  auto node = node_.lock();
163  if (!node) {
164  throw std::runtime_error{"Failed to lock node"};
165  }
166 
167  source_topic = node->declare_or_get_parameter(
168  source_name_ + ".topic", std::string("scan")); // Set default topic for laser scanner
169 
170  enabled_ = node->declare_or_get_parameter(
171  source_name_ + ".enabled", true);
172 
173  source_timeout_ = rclcpp::Duration::from_seconds(
174  node->declare_or_get_parameter(
175  source_name_ + ".source_timeout",
176  source_timeout_.seconds())); // node source_timeout by default
177 }
178 
180  const rclcpp::Time & source_time,
181  const rclcpp::Time & curr_time) const
182 {
183  // Source is considered as not valid, if latest received data timestamp is earlier
184  // than current time by source_timeout_ interval
185  const rclcpp::Duration dt = curr_time - source_time;
186  if (source_timeout_.seconds() != 0.0 && dt > source_timeout_) {
187  RCLCPP_WARN(
188  logger_,
189  "[%s]: Latest source and current collision monitor node timestamps differ on %f seconds. "
190  "Ignoring the source.",
191  source_name_.c_str(), dt.seconds());
192  return false;
193  }
194 
195  return true;
196 }
197 
198 bool Source::getEnabled() const
199 {
200  std::lock_guard<std::mutex> lock_reinit(mutex_);
201  return enabled_;
202 }
203 
204 std::string Source::getSourceName() const
205 {
206  return source_name_;
207 }
208 
209 rclcpp::Duration Source::getSourceTimeout() const
210 {
211  return source_timeout_;
212 }
213 
214 rcl_interfaces::msg::SetParametersResult Source::validateParameterUpdatesCallback(
215  const std::vector<rclcpp::Parameter> & /*parameters*/)
216 {
217  rcl_interfaces::msg::SetParametersResult result;
218  result.successful = true;
219  return result;
220 }
221 
223  const std::vector<rclcpp::Parameter> & parameters)
224 {
225  std::lock_guard<std::mutex> lock_reinit(mutex_);
226 
227  for (auto parameter : parameters) {
228  const auto & param_type = parameter.get_type();
229  const auto & param_name = parameter.get_name();
230  if (param_name.find(source_name_ + ".") != 0) {
231  continue;
232  }
233  if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
234  if (param_name == source_name_ + "." + "enabled") {
235  enabled_ = parameter.as_bool();
236  }
237  }
238  }
239 }
240 
242  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
243  const std::shared_ptr<nav2_msgs::srv::AddExclusionZone::Request> request,
244  std::shared_ptr<nav2_msgs::srv::AddExclusionZone::Response> response)
245 {
246  const auto & desc = request->zone;
247 
248  // Validate the description
249  bool duplicate = std::any_of(
250  exclusion_zones_.begin(), exclusion_zones_.end(),
251  [&](const auto & z) {return z->getName() == desc.zone_name;});
252  if (desc.zone_name.empty() || duplicate ||
253  (desc.type != "polygon" && desc.type != "circle") ||
254  (desc.type == "circle" && desc.radius <= 0.0) ||
255  (desc.type == "polygon" && desc.points.size() < 3))
256  {
257  response->success = false;
258  response->message = "Invalid exclusion zone description for '" + desc.zone_name +
259  "' on source '" + source_name_ + "'";
260  return;
261  }
262 
263  auto node = node_.lock();
264  auto zone = std::make_shared<ExclusionZone>(
265  node, desc.zone_name, tf_buffer_, base_frame_id_, global_frame_id_,
267  if (!zone->configure(desc)) {
268  response->success = false;
269  response->message = "Failed to configure zone '" + desc.zone_name + "'";
270  return;
271  }
272  zone->activate();
273  exclusion_zones_.push_back(zone);
274 
275  response->success = true;
276  response->message = "Added zone '" + desc.zone_name + "' to source '" + source_name_ + "'";
277  RCLCPP_INFO(logger_, "%s", response->message.c_str());
278 }
279 
281  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
282  const std::shared_ptr<nav2_msgs::srv::RemoveExclusionZone::Request> request,
283  std::shared_ptr<nav2_msgs::srv::RemoveExclusionZone::Response> response)
284 {
285  if (request->remove_all) {
286  for (auto & zone : exclusion_zones_) {
287  zone->deactivate();
288  }
289  exclusion_zones_.clear();
290  response->success = true;
291  response->message = std::string("Removed all zone(s) from source '") +
292  source_name_ + "'";
293  RCLCPP_INFO(logger_, "%s", response->message.c_str());
294  return;
295  }
296 
297  if (request->zone_name.empty()) {
298  response->success = false;
299  response->message = "Zone name must not be empty";
300  return;
301  }
302 
303  auto it = std::find_if(
304  exclusion_zones_.begin(), exclusion_zones_.end(),
305  [&](const std::shared_ptr<ExclusionZone> & z) {
306  return z->getName() == request->zone_name;
307  });
308 
309  if (it == exclusion_zones_.end()) {
310  response->success = false;
311  response->message = "Zone '" + request->zone_name +
312  "' not found on source '" + source_name_ + "'";
313  return;
314  }
315 
316  (*it)->deactivate();
317  exclusion_zones_.erase(it);
318 
319  response->success = true;
320  response->message = "Removed zone '" + request->zone_name +
321  "' from source '" + source_name_ + "'";
322  RCLCPP_INFO(logger_, "%s", response->message.c_str());
323 }
324 
326  const rclcpp::Time & curr_time,
327  const std_msgs::msg::Header & data_header,
328  tf2::Transform & tf_transform) const
329 {
331  if (
332  !nav2_util::getTransform(
333  data_header.frame_id, data_header.stamp,
334  base_frame_id_, curr_time, global_frame_id_,
335  transform_tolerance_, tf_buffer_, tf_transform))
336  {
337  return false;
338  }
339  } else {
340  if (
341  !nav2_util::getTransform(
342  data_header.frame_id, base_frame_id_,
343  transform_tolerance_, tf_buffer_, tf_transform))
344  {
345  return false;
346  }
347  }
348  return true;
349 }
350 
351 } // namespace nav2_collision_monitor
nav2::LifecycleNode::WeakPtr node_
Collision Monitor node.
Definition: source.hpp:205
rclcpp::Logger logger_
Collision monitor node logger stored for further usage.
Definition: source.hpp:207
std::vector< std::shared_ptr< ExclusionZone > > exclusion_zones_
Exclusion zones masking out points from this source.
Definition: source.hpp:234
void removeExclusionZoneCallback(const std::shared_ptr< rmw_request_id_t > request_header, const std::shared_ptr< nav2_msgs::srv::RemoveExclusionZone::Request > request, std::shared_ptr< nav2_msgs::srv::RemoveExclusionZone::Response > response)
Service callback to remove an exclusion zone at runtime.
Definition: source.cpp:280
void activate()
Activates the exclusion zone visualization publishers (if any)
Definition: source.cpp:139
std::string base_frame_id_
Robot base frame ID.
Definition: source.hpp:221
virtual ~Source()
Source destructor.
Definition: source.cpp:47
bool getTransform(const rclcpp::Time &curr_time, const std_msgs::msg::Header &data_header, tf2::Transform &tf_transform) const
Obtain the transform to get data from source frame and time where it was received to the base frame a...
Definition: source.cpp:325
void addExclusionZoneCallback(const std::shared_ptr< rmw_request_id_t > request_header, const std::shared_ptr< nav2_msgs::srv::AddExclusionZone::Request > request, std::shared_ptr< nav2_msgs::srv::AddExclusionZone::Response > response)
Service callback to add an exclusion zone at runtime.
Definition: source.cpp:241
bool enabled_
Whether source is enabled.
Definition: source.hpp:232
virtual bool getSourceData(const rclcpp::Time &curr_time, std::vector< Point > &data)=0
Adds latest data from source to the data array. Pure virtual method implemented by each concrete sour...
bool getEnabled() const
Obtains source enabled state.
Definition: source.cpp:198
std::string getSourceName() const
Obtains the name of the data source.
Definition: source.cpp:204
Source(const nav2::LifecycleNode::WeakPtr &node, const std::string &source_name, const nav2::TransformBuffer::SharedPtr tf_buffer, const std::string &base_frame_id, const std::string &global_frame_id, const tf2::Duration &transform_tolerance, const rclcpp::Duration &source_timeout, const bool base_shift_correction)
Source constructor.
Definition: source.cpp:31
nav2::ServiceServer< nav2_msgs::srv::AddExclusionZone >::SharedPtr add_ez_service_
Service to add an exclusion zone at runtime.
Definition: source.hpp:236
tf2::Duration transform_tolerance_
Transform tolerance.
Definition: source.hpp:225
void getCommonParameters(std::string &source_topic)
Supporting routine obtaining ROS-parameters common for all data sources.
Definition: source.cpp:160
bool getData(const rclcpp::Time &curr_time, std::vector< Point > &data)
Adds latest data from source to the data array, then removes any points falling inside an enabled exc...
Definition: source.cpp:112
bool configure()
Source configuration routine.
Definition: source.cpp:62
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
Definition: source.cpp:222
void publishExclusionZones() const
Publishes the source's exclusion zones for visualization.
Definition: source.cpp:153
std::string global_frame_id_
Global frame ID for correct transform calculation.
Definition: source.hpp:223
void deactivate()
Deactivates the exclusion zone visualization publishers (if any)
Definition: source.cpp:146
std::string source_name_
Name of data source.
Definition: source.hpp:215
nav2::ServiceServer< nav2_msgs::srv::RemoveExclusionZone >::SharedPtr remove_ez_service_
Service to remove an exclusion zone at runtime.
Definition: source.hpp:238
bool sourceValid(const rclcpp::Time &source_time, const rclcpp::Time &curr_time) const
Checks whether the source data might be considered as valid.
Definition: source.cpp:179
bool base_shift_correction_
Whether to correct source data towards to base frame movement, considering the difference between cur...
Definition: source.hpp:230
std::mutex mutex_
Dynamic parameters handler.
Definition: source.hpp:209
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
Definition: source.cpp:214
rclcpp::Duration source_timeout_
Maximum time interval in which data is considered valid.
Definition: source.hpp:227
nav2::TransformBuffer::SharedPtr tf_buffer_
TF buffer.
Definition: source.hpp:219
rclcpp::Duration getSourceTimeout() const
Obtains the source_timeout parameter of the data source.
Definition: source.cpp:209