Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
pointcloud.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/pointcloud.hpp"
16 
17 #include <functional>
18 
19 #include "sensor_msgs/point_cloud2_iterator.hpp"
20 #include "tf2/transform_datatypes.hpp"
21 #include "nav2_ros_common/tf2_factories.hpp"
22 
23 #include "nav2_ros_common/node_utils.hpp"
24 #include "nav2_util/robot_utils.hpp"
25 
26 namespace nav2_collision_monitor
27 {
28 
30  const nav2::LifecycleNode::WeakPtr & node,
31  const std::string & source_name,
32  const nav2::TransformBuffer::SharedPtr tf_buffer,
33  const std::string & base_frame_id,
34  const std::string & global_frame_id,
35  const tf2::Duration & transform_tolerance,
36  const rclcpp::Duration & source_timeout,
37  const bool base_shift_correction)
38 : Source(
39  node, source_name, tf_buffer, base_frame_id, global_frame_id,
40  transform_tolerance, source_timeout, base_shift_correction),
41  data_(nullptr)
42 {
43  RCLCPP_INFO(logger_, "[%s]: Creating PointCloud", source_name_.c_str());
44 }
45 
47 {
48  RCLCPP_INFO(logger_, "[%s]: Destroying PointCloud", source_name_.c_str());
49  #if RCLCPP_VERSION_GTE(30, 0, 0)
50  data_sub_.shutdown();
51  #else
52  data_sub_.reset();
53  #endif
54  auto node = node_.lock();
55  if (post_set_params_handler_ && node) {
56  node->remove_post_set_parameters_callback(post_set_params_handler_.get());
57  }
58  post_set_params_handler_.reset();
59  if (on_set_params_handler_ && node) {
60  node->remove_on_set_parameters_callback(on_set_params_handler_.get());
61  }
62  on_set_params_handler_.reset();
63 }
64 
66 {
67  if (!Source::configure()) {
68  return false;
69  }
70  auto node = node_.lock();
71  if (!node) {
72  throw std::runtime_error{"Failed to lock node"};
73  }
74 
75  std::string source_topic;
76 
77  getParameters(source_topic);
78 
79  #if RCLCPP_VERSION_GTE(30, 0, 0)
80  const point_cloud_transport::TransportHints hint(transport_type_);
81  pct_ = std::make_shared<point_cloud_transport::PointCloudTransport>(*node);
82  data_sub_ = pct_->subscribe(
83  source_topic, nav2::qos::SensorDataQoS(),
84  std::bind(&PointCloud::dataCallback, this, std::placeholders::_1),
85  {}, &hint
86  );
87  #else
88  data_sub_ = node->create_subscription<sensor_msgs::msg::PointCloud2>(
89  source_topic,
90  std::bind(&PointCloud::dataCallback, this, std::placeholders::_1),
92  #endif
93 
94  // Add callback for dynamic parameters
95  post_set_params_handler_ = node->add_post_set_parameters_callback(
96  std::bind(
98  this, std::placeholders::_1));
99  on_set_params_handler_ = node->add_on_set_parameters_callback(
100  std::bind(
102  this, std::placeholders::_1));
103 
104  return true;
105 }
106 
108  const rclcpp::Time & curr_time,
109  std::vector<Point> & data)
110 {
111  std::lock_guard<std::mutex> lock_reinit(mutex_);
112  // Ignore data from the source if it is not being published yet or
113  // not published for a long time
114  if (data_ == nullptr) {
115  return false;
116  }
117  if (!sourceValid(data_->header.stamp, curr_time)) {
118  return false;
119  }
120 
121  tf2::Transform tf_transform;
122  if (!getTransform(curr_time, data_->header, tf_transform)) {
123  return false;
124  }
125 
126  sensor_msgs::PointCloud2ConstIterator<float> iter_x(*data_, "x");
127  sensor_msgs::PointCloud2ConstIterator<float> iter_y(*data_, "y");
128  sensor_msgs::PointCloud2ConstIterator<float> iter_z(*data_, "z");
129 
130  bool height_present = false;
131  for (const auto & field : data_->fields) {
132  if (field.name == "height") {
133  height_present = true;
134  }
135  }
136 
137 // Reference height field
138  std::string height_field{"z"};
139  if (use_global_height_ && height_present) {
140  height_field = "height";
141  } else if (use_global_height_) {
142  RCLCPP_ERROR(
143  logger_, "[%s]: 'use_global_height' parameter true but height field not in cloud",
144  source_name_.c_str());
145  return false;
146  }
147  sensor_msgs::PointCloud2ConstIterator<float> iter_height(*data_, height_field);
148 
149  // Refill data array with PointCloud points in base frame
150  for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
151  // Transform point coordinates from source frame -> to base frame
152  tf2::Vector3 p_v3_s(*iter_x, *iter_y, *iter_z);
153 
154  double data_height = *iter_z;
155  if (use_global_height_) {
156  data_height = *iter_height;
157  ++iter_height;
158  }
159 
160  // Check range from sensor origin before transformation
161  double range = p_v3_s.length();
162  if (range < min_range_) {
163  continue;
164  }
165 
166  tf2::Vector3 p_v3_b = tf_transform * p_v3_s;
167 
168  // Still need to transfer height from "z" field if not using global height
169  if (!use_global_height_) {
170  data_height = p_v3_b.z();
171  }
172 
173  // Refill data array
174  if (data_height >= min_height_ && data_height <= max_height_) {
175  data.push_back({p_v3_b.x(), p_v3_b.y(), p_v3_b.z(), source_name_});
176  }
177  }
178  return true;
179 }
180 
181 void PointCloud::getParameters(std::string & source_topic)
182 {
183  auto node = node_.lock();
184  if (!node) {
185  throw std::runtime_error{"Failed to lock node"};
186  }
187 
188  getCommonParameters(source_topic);
189 
190  min_height_ = node->declare_or_get_parameter(source_name_ + ".min_height", 0.05);
191  max_height_ = node->declare_or_get_parameter(source_name_ + ".max_height", 0.5);
192  min_range_ = node->declare_or_get_parameter(source_name_ + ".min_range", 0.0);
193  use_global_height_ = node->declare_or_get_parameter(
194  source_name_ + ".use_global_height", false);
195  transport_type_ = node->declare_or_get_parameter(
196  source_name_ + ".transport_type", std::string("raw"));
197 }
198 
199 void PointCloud::dataCallback(sensor_msgs::msg::PointCloud2::ConstSharedPtr msg)
200 {
201  data_ = msg;
202 }
203 
204 rcl_interfaces::msg::SetParametersResult PointCloud::validateParameterUpdatesCallback(
205  const std::vector<rclcpp::Parameter> & parameters)
206 {
207  rcl_interfaces::msg::SetParametersResult result;
208  result.successful = true;
209  for (const auto & parameter : parameters) {
210  const auto & param_type = parameter.get_type();
211  const auto & param_name = parameter.get_name();
212  if (param_name.find(source_name_ + ".") != 0) {
213  continue;
214  }
215  if (param_type == ParameterType::PARAMETER_DOUBLE) {
216  if (parameter.as_double() < 0.0) {
217  RCLCPP_WARN(
218  logger_, "The value of parameter '%s' is incorrectly set to %f, "
219  "it should be >=0. Ignoring parameter update.",
220  param_name.c_str(), parameter.as_double());
221  result.successful = false;
222  }
223  }
224  }
225  return result;
226 }
227 
229  const std::vector<rclcpp::Parameter> & parameters)
230 {
231  std::lock_guard<std::mutex> lock_reinit(mutex_);
232 
233  for (auto parameter : parameters) {
234  const auto & param_type = parameter.get_type();
235  const auto & param_name = parameter.get_name();
236  if (param_name.find(source_name_ + ".") != 0) {
237  continue;
238  }
239  if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
240  if (param_name == source_name_ + "." + "min_height") {
241  min_height_ = parameter.as_double();
242  } else if (param_name == source_name_ + "." + "max_height") {
243  max_height_ = parameter.as_double();
244  } else if (param_name == source_name_ + "." + "min_range") {
245  min_range_ = parameter.as_double();
246  }
247  } else if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
248  if (param_name == source_name_ + "." + "enabled") {
249  enabled_ = parameter.as_bool();
250  } else if (param_name == source_name_ + "." + "use_global_height") {
251  use_global_height_ = parameter.as_bool();
252  }
253  }
254  }
255 }
256 
257 } // namespace nav2_collision_monitor
A QoS profile for best-effort sensor data with a history of 10 messages.
bool configure()
Data source configuration routine. Obtains pointcloud related ROS-parameters and creates pointcloud s...
Definition: pointcloud.cpp:65
void getParameters(std::string &source_topic)
Getting sensor-specific ROS-parameters.
Definition: pointcloud.cpp:181
sensor_msgs::msg::PointCloud2::ConstSharedPtr data_
Latest data obtained from pointcloud.
Definition: pointcloud.hpp:136
PointCloud(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)
PointCloud constructor.
Definition: pointcloud.cpp:29
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: pointcloud.cpp:204
bool getSourceData(const rclcpp::Time &curr_time, std::vector< Point > &data) override
Adds latest data from pointcloud source to the data array.
Definition: pointcloud.cpp:107
nav2::Subscription< sensor_msgs::msg::PointCloud2 >::SharedPtr data_sub_
PointCloud data subscriber.
Definition: pointcloud.hpp:120
void dataCallback(sensor_msgs::msg::PointCloud2::ConstSharedPtr msg)
PointCloud data callback.
Definition: pointcloud.cpp:199
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
Definition: pointcloud.cpp:228
~PointCloud()
PointCloud destructor.
Definition: pointcloud.cpp:46
Basic data source class.
Definition: source.hpp:44
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
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
bool enabled_
Whether source is enabled.
Definition: source.hpp:232
void getCommonParameters(std::string &source_topic)
Supporting routine obtaining ROS-parameters common for all data sources.
Definition: source.cpp:160
bool configure()
Source configuration routine.
Definition: source.cpp:62
std::string source_name_
Name of data source.
Definition: source.hpp:215
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
std::mutex mutex_
Dynamic parameters handler.
Definition: source.hpp:209