16
16
#include " rclcpp/rclcpp.hpp"
17
17
18
18
#include " grid_ground_filter.hpp"
19
+ #include " sanity_check.hpp"
19
20
20
21
#include < autoware_utils/geometry/geometry.hpp>
21
22
#include < autoware_utils/math/normalization.hpp>
26
27
#include < string>
27
28
#include < vector>
28
29
29
- namespace autoware ::pointcloud_filter
30
- {
31
- // For PointCloud2
32
- using PointCloud2 = sensor_msgs::msg::PointCloud2;
33
- using PointCloud2ConstPtr = sensor_msgs::msg::PointCloud2::ConstSharedPtr;
34
-
35
- using autoware::vehicle_info_utils::VehicleInfoUtils;
36
- using autoware_utils::calc_distance3d;
37
- using autoware_utils::deg2rad;
38
- using autoware_utils::normalize_degree;
39
- using autoware_utils::normalize_radian;
40
- using autoware_utils::ScopedTimeTrack;
41
-
42
30
namespace {
43
31
/* * \brief For parameter service callback */
44
32
template <typename T>
@@ -55,6 +43,19 @@ bool get_param(const std::vector<rclcpp::Parameter> & p, const std::string & nam
55
43
}
56
44
}
57
45
46
+ namespace autoware ::pointcloud_filter
47
+ {
48
+ // For PointCloud2
49
+ using PointCloud2 = sensor_msgs::msg::PointCloud2;
50
+ using PointCloud2ConstPtr = sensor_msgs::msg::PointCloud2::ConstSharedPtr;
51
+
52
+ using autoware::vehicle_info_utils::VehicleInfoUtils;
53
+ using autoware_utils::calc_distance3d;
54
+ using autoware_utils::deg2rad;
55
+ using autoware_utils::normalize_degree;
56
+ using autoware_utils::normalize_radian;
57
+ using autoware_utils::ScopedTimeTrack;
58
+
58
59
ScanGroundFilterComponent::ScanGroundFilterComponent (const rclcpp::NodeOptions & options)
59
60
: rclcpp::Node(" ScanGroundFilter" , options)
60
61
{
@@ -147,6 +148,229 @@ ScanGroundFilterComponent::ScanGroundFilterComponent(const rclcpp::NodeOptions &
147
148
grid_ground_filter_ptr_->setTimeKeeper (time_keeper_);
148
149
}
149
150
}
151
+
152
+ // pointcloud parameters
153
+ tf_input_frame_ = static_cast <std::string>(declare_parameter (" input_frame" , " " ));
154
+ tf_output_frame_ = static_cast <std::string>(declare_parameter (" output_frame" , " " ));
155
+ has_static_tf_only_ = static_cast <bool >(declare_parameter (" has_static_tf_only" , false ));
156
+ max_queue_size_ = static_cast <std::size_t >(declare_parameter (" max_queue_size" , 5 ));
157
+ use_indices_ = static_cast <bool >(declare_parameter (" use_indices" , false ));
158
+ latched_indices_ = static_cast <bool >(declare_parameter (" latched_indices" , false ));
159
+ approximate_sync_ = static_cast <bool >(declare_parameter (" approximate_sync" , false ));
160
+
161
+ RCLCPP_DEBUG_STREAM (
162
+ this ->get_logger (),
163
+ " Filter (as Component) successfully created with the following parameters:"
164
+ << std::endl
165
+ << " - approximate_sync : " << (approximate_sync_ ? " true" : " false" ) << std::endl
166
+ << " - use_indices : " << (use_indices_ ? " true" : " false" ) << std::endl
167
+ << " - latched_indices : " << (latched_indices_ ? " true" : " false" ) << std::endl
168
+ << " - max_queue_size : " << max_queue_size_);
169
+
170
+ // Set publisher
171
+ {
172
+ rclcpp::PublisherOptions pub_options;
173
+ pub_options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies ();
174
+ pub_output_ = this ->create_publisher <sensor_msgs::msg::PointCloud2>(
175
+ " output" , rclcpp::SensorDataQoS ().keep_last (max_queue_size_), pub_options);
176
+ }
177
+
178
+ subscribe ();
179
+
180
+ // Set tf_listener, tf_buffer.
181
+ setupTF ();
182
+
183
+ published_time_publisher_ = std::make_unique<autoware_utils::PublishedTimePublisher>(this );
184
+ RCLCPP_DEBUG (this ->get_logger (), " [Filter Constructor] successfully created." );
185
+
186
+ }
187
+
188
+ void ScanGroundFilterComponent::setupTF ()
189
+ {
190
+ // Always consider static TF if in & out frames are same
191
+ if (tf_input_frame_ == tf_output_frame_) {
192
+ if (!has_static_tf_only_) {
193
+ RCLCPP_INFO (
194
+ this ->get_logger (),
195
+ " Input and output frames are the same. Overriding has_static_tf_only to true." );
196
+ }
197
+ has_static_tf_only_ = true ;
198
+ }
199
+ managed_tf_buffer_ =
200
+ std::make_unique<autoware_utils::ManagedTransformBuffer>(this , has_static_tf_only_);
201
+ }
202
+
203
+ void ScanGroundFilterComponent::subscribe ()
204
+ {
205
+ if (use_indices_) {
206
+ // Subscribe to the input using a filter
207
+ sub_input_filter_.subscribe (
208
+ this , " input" , rclcpp::SensorDataQoS ().keep_last (max_queue_size_).get_rmw_qos_profile ());
209
+ sub_indices_filter_.subscribe (
210
+ this , " indices" , rclcpp::SensorDataQoS ().keep_last (max_queue_size_).get_rmw_qos_profile ());
211
+
212
+ if (approximate_sync_) {
213
+ sync_input_indices_a_ = std::make_shared<message_filters::Synchronizer<message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::PointCloud2, pcl_msgs::msg::PointIndices>>>(max_queue_size_);
214
+ sync_input_indices_a_->connectInput (sub_input_filter_, sub_indices_filter_);
215
+ sync_input_indices_a_->registerCallback (
216
+ std::bind (&ScanGroundFilterComponent::faster_input_indices_callback, this , std::placeholders::_1, std::placeholders::_2));
217
+ } else {
218
+ sync_input_indices_e_ = std::make_shared<message_filters::Synchronizer<message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, pcl_msgs::msg::PointIndices>>>(max_queue_size_);
219
+ sync_input_indices_e_->connectInput (sub_input_filter_, sub_indices_filter_);
220
+ sync_input_indices_e_->registerCallback (
221
+ std::bind (&ScanGroundFilterComponent::faster_input_indices_callback, this , std::placeholders::_1, std::placeholders::_2));
222
+ }
223
+ } else {
224
+ std::function<void (const sensor_msgs::msg::PointCloud2::ConstSharedPtr msg)> cb =
225
+ std::bind (&ScanGroundFilterComponent::faster_input_indices_callback, this , std::placeholders::_1, pcl_msgs::msg::PointIndices::ConstSharedPtr ());
226
+ sub_input_ = this ->create_subscription <sensor_msgs::msg::PointCloud2>(
227
+ " input" , rclcpp::SensorDataQoS ().keep_last (max_queue_size_), cb);
228
+ }
229
+ }
230
+
231
+ bool ScanGroundFilterComponent::calculate_transform_matrix (
232
+ const std::string & target_frame, const sensor_msgs::msg::PointCloud2 & from,
233
+ TransformInfo & transform_info /* output*/ )
234
+ {
235
+ transform_info.need_transform = false ;
236
+
237
+ if (target_frame.empty () || from.header .frame_id == target_frame) return true ;
238
+
239
+ RCLCPP_DEBUG (
240
+ this ->get_logger (), " [get_transform_matrix] Transforming input dataset from %s to %s." ,
241
+ from.header .frame_id .c_str (), target_frame.c_str ());
242
+
243
+ if (!managed_tf_buffer_->get_transform (
244
+ target_frame, from.header .frame_id , transform_info.eigen_transform )) {
245
+ return false ;
246
+ }
247
+
248
+ transform_info.need_transform = true ;
249
+ return true ;
250
+ }
251
+
252
+ bool ScanGroundFilterComponent::convert_output_costly (
253
+ std::unique_ptr<sensor_msgs::msg::PointCloud2> & output)
254
+ {
255
+ // In terms of performance, we should avoid using pcl_ros library function,
256
+ // but this code path isn't reached in the main use case of Autoware, so it's left as is for now.
257
+ if (!tf_output_frame_.empty () && output->header .frame_id != tf_output_frame_) {
258
+ RCLCPP_DEBUG (
259
+ this ->get_logger (), " [convert_output_costly] Transforming output dataset from %s to %s." ,
260
+ output->header .frame_id .c_str (), tf_output_frame_.c_str ());
261
+
262
+ // Convert the cloud into the different frame
263
+ auto cloud_transformed = std::make_unique<sensor_msgs::msg::PointCloud2>();
264
+
265
+ if (!managed_tf_buffer_->transform_pointcloud (tf_output_frame_, *output, *cloud_transformed)) {
266
+ RCLCPP_ERROR (
267
+ this ->get_logger (),
268
+ " [convert_output_costly] Error converting output dataset from %s to %s." ,
269
+ output->header .frame_id .c_str (), tf_output_frame_.c_str ());
270
+ return false ;
271
+ }
272
+
273
+ output = std::move (cloud_transformed);
274
+ }
275
+
276
+ // Same as the comment above
277
+ if (tf_output_frame_.empty () && output->header .frame_id != tf_input_orig_frame_) {
278
+ // No tf_output_frame given, transform the dataset to its original frame
279
+ RCLCPP_DEBUG (
280
+ this ->get_logger (), " [convert_output_costly] Transforming output dataset from %s back to %s." ,
281
+ output->header .frame_id .c_str (), tf_input_orig_frame_.c_str ());
282
+
283
+ auto cloud_transformed = std::make_unique<sensor_msgs::msg::PointCloud2>();
284
+
285
+ if (!managed_tf_buffer_->transform_pointcloud (
286
+ tf_input_orig_frame_, *output, *cloud_transformed)) {
287
+ return false ;
288
+ }
289
+
290
+ output = std::move (cloud_transformed);
291
+ }
292
+
293
+ return true ;
294
+ }
295
+
296
+ void ScanGroundFilterComponent::faster_input_indices_callback (
297
+ const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloud, const pcl_msgs::msg::PointIndices::ConstSharedPtr indices)
298
+ {
299
+ if (
300
+ !is_data_layout_compatible_with_point_xyzircaedt (*cloud) &&
301
+ !is_data_layout_compatible_with_point_xyzirc (*cloud)) {
302
+ RCLCPP_ERROR (
303
+ get_logger (),
304
+ " The pointcloud layout is not compatible with PointXYZIRCAEDT or PointXYZIRC. Aborting" );
305
+
306
+ if (is_data_layout_compatible_with_point_xyziradrt (*cloud)) {
307
+ RCLCPP_ERROR (
308
+ get_logger (),
309
+ " The pointcloud layout is compatible with PointXYZIRADRT. You may be using legacy "
310
+ " code/data" );
311
+ }
312
+
313
+ if (is_data_layout_compatible_with_point_xyzi (*cloud)) {
314
+ RCLCPP_ERROR (
315
+ get_logger (),
316
+ " The pointcloud layout is compatible with PointXYZI. You may be using legacy "
317
+ " code/data" );
318
+ }
319
+
320
+ return ;
321
+ }
322
+
323
+ if (!isValid (cloud)) {
324
+ RCLCPP_ERROR (this ->get_logger (), " [input_indices_callback] Invalid input!" );
325
+ return ;
326
+ }
327
+
328
+ if (!isValid (indices)) {
329
+ RCLCPP_ERROR (this ->get_logger (), " [input_indices_callback] Invalid indices!" );
330
+ return ;
331
+ }
332
+
333
+ if (indices) {
334
+ RCLCPP_DEBUG (
335
+ this ->get_logger (),
336
+ " [input_indices_callback]\n "
337
+ " - PointCloud with %d data points (%s), stamp %f, and frame %s on input topic received.\n "
338
+ " - PointIndices with %zu values, stamp %f, and frame %s on indices topic received." ,
339
+ cloud->width * cloud->height , pcl::getFieldsList (*cloud).c_str (),
340
+ rclcpp::Time (cloud->header .stamp ).seconds (), cloud->header .frame_id .c_str (),
341
+ indices->indices .size (), rclcpp::Time (indices->header .stamp ).seconds (),
342
+ indices->header .frame_id .c_str ());
343
+ } else {
344
+ RCLCPP_DEBUG (
345
+ this ->get_logger (),
346
+ " [input_indices_callback] PointCloud with %d data points and frame %s on input topic "
347
+ " received." ,
348
+ cloud->width * cloud->height , cloud->header .frame_id .c_str ());
349
+ }
350
+
351
+ tf_input_orig_frame_ = cloud->header .frame_id ;
352
+
353
+ // For performance reason, defer the transform computation.
354
+ // Do not use pcl_ros::transformPointCloud(). It's too slow due to the unnecessary copy.
355
+ TransformInfo transform_info;
356
+ if (!calculate_transform_matrix (tf_input_frame_, *cloud, transform_info)) return ;
357
+
358
+ // Need setInputCloud() here because we have to extract x/y/z
359
+ pcl::IndicesPtr vindices;
360
+ if (indices) {
361
+ vindices.reset (new std::vector<int >(indices->indices ));
362
+ }
363
+
364
+ auto output = std::make_unique<PointCloud2>();
365
+
366
+ // TODO(sykwer): Change to `filter()` call after when the filter nodes conform to new API.
367
+ faster_filter (cloud, vindices, *output, transform_info);
368
+
369
+ if (!convert_output_costly (output)) return ;
370
+
371
+ output->header .stamp = cloud->header .stamp ;
372
+ pub_output_->publish (std::move (output));
373
+ published_time_publisher_->publish_if_subscribed (pub_output_, cloud->header .stamp );
150
374
}
151
375
152
376
void ScanGroundFilterComponent::convertPointcloud (
@@ -356,8 +580,7 @@ void ScanGroundFilterComponent::faster_filter(
356
580
std::unique_ptr<autoware_utils::ScopedTimeTrack> st_ptr;
357
581
if (time_keeper_) st_ptr = std::make_unique<autoware_utils::ScopedTimeTrack>(__func__, *time_keeper_);
358
582
359
- // TODO(sasakisasaki): Understand why this mutex is defined before the porting. I guess this comes from pointcloud_preprocessor.
360
- // std::scoped_lock lock(mutex_);
583
+ std::scoped_lock lock (mutex_);
361
584
362
585
if (stop_watch_ptr_) stop_watch_ptr_->toc (" processing_time" , true );
363
586
@@ -463,13 +686,24 @@ rcl_interfaces::msg::SetParametersResult ScanGroundFilterComponent::onParameter(
463
686
this ->get_logger (),
464
687
" Setting use_recheck_ground_cluster to: " << std::boolalpha << use_recheck_ground_cluster_);
465
688
}
689
+
690
+ // For pointcloud
691
+ std::scoped_lock lock (mutex_);
692
+
693
+ if (get_param (param, " input_frame" , tf_input_frame_)) {
694
+ RCLCPP_DEBUG (this ->get_logger (), " Setting the input TF frame to: %s." , tf_input_frame_.c_str ());
695
+ }
696
+ if (get_param (param, " output_frame" , tf_output_frame_)) {
697
+ RCLCPP_DEBUG (this ->get_logger (), " Setting the output TF frame to: %s." , tf_output_frame_.c_str ());
698
+ }
699
+
700
+ // Finally return the result
466
701
rcl_interfaces::msg::SetParametersResult result;
467
702
result.successful = true ;
468
703
result.reason = " success" ;
469
704
470
705
return result;
471
706
}
472
-
473
707
} // namespace autoware::pointcloud_filter
474
708
475
709
#include < rclcpp_components/register_node_macro.hpp>
0 commit comments