19
19
20
20
#include < boost/geometry.hpp>
21
21
22
- #include < pcl/filters/crop_hull.h>
23
- #include < pcl/kdtree/kdtree_flann.h>
24
- #include < pcl/search/kdtree.h>
25
- #include < pcl/search/pcl_search.h>
26
- #include < pcl_conversions/pcl_conversions.h>
27
-
28
22
#ifdef ROS_DISTRO_GALACTIC
29
23
#include < tf2_geometry_msgs/tf2_geometry_msgs.h>
30
24
#else
35
29
#include < Eigen/Core>
36
30
#include < Eigen/Geometry>
37
31
38
- namespace
32
+ namespace obstacle_pointcloud_based_validator
39
33
{
40
- inline pcl::PointXY toPCL (const double x, const double y)
34
+ namespace bg = boost::geometry;
35
+ using Shape = autoware_auto_perception_msgs::msg::Shape;
36
+ using Polygon2d = tier4_autoware_utils::Polygon2d;
37
+
38
+ Validator::Validator (PointsNumThresholdParam & points_num_threshold_param)
41
39
{
42
- pcl::PointXY pcl_point ;
43
- pcl_point. x = x ;
44
- pcl_point. y = y;
45
- return pcl_point ;
40
+ points_num_threshold_param_. min_points_num = points_num_threshold_param. min_points_num ;
41
+ points_num_threshold_param_. max_points_num = points_num_threshold_param. max_points_num ;
42
+ points_num_threshold_param_. min_points_and_distance_ratio =
43
+ points_num_threshold_param. min_points_and_distance_ratio ;
46
44
}
47
45
48
- inline pcl::PointXY toPCL (const geometry_msgs::msg::Point & point)
46
+ size_t Validator::getThresholdPointCloud (
47
+ const autoware_auto_perception_msgs::msg::DetectedObject & object)
49
48
{
50
- return toPCL (point.x , point.y );
49
+ const auto object_label_id = object.classification .front ().label ;
50
+ const auto object_distance = std::hypot (
51
+ object.kinematics .pose_with_covariance .pose .position .x ,
52
+ object.kinematics .pose_with_covariance .pose .position .y );
53
+ size_t threshold_pc = std::clamp (
54
+ static_cast <size_t >(
55
+ points_num_threshold_param_.min_points_and_distance_ratio .at (object_label_id) /
56
+ object_distance +
57
+ 0 .5f ),
58
+ static_cast <size_t >(points_num_threshold_param_.min_points_num .at (object_label_id)),
59
+ static_cast <size_t >(points_num_threshold_param_.max_points_num .at (object_label_id)));
60
+ return threshold_pc;
51
61
}
52
62
53
- inline pcl::PointXYZ toXYZ (const pcl::PointXY & point)
63
+ Validator2D::Validator2D (PointsNumThresholdParam & points_num_threshold_param)
64
+ : Validator(points_num_threshold_param)
54
65
{
55
- return pcl::PointXYZ (point.x , point.y , 0.0 );
56
66
}
57
67
58
- inline pcl::PointCloud<pcl::PointXYZ>::Ptr toXYZ (
59
- const pcl::PointCloud<pcl::PointXY>::Ptr & pointcloud)
68
+ bool Validator2D::setKdtreeInputCloud (
69
+ const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_cloud)
70
+ {
71
+ obstacle_pointcloud_.reset (new pcl::PointCloud<pcl::PointXY>);
72
+ pcl::fromROSMsg (*input_cloud, *obstacle_pointcloud_);
73
+ if (obstacle_pointcloud_->empty ()) {
74
+ return false ;
75
+ }
76
+
77
+ kdtree_ = pcl::make_shared<pcl::search::KdTree<pcl::PointXY>>(false );
78
+ kdtree_->setInputCloud (obstacle_pointcloud_);
79
+
80
+ return true ;
81
+ }
82
+ pcl::PointCloud<pcl::PointXYZ>::Ptr Validator2D::convertToXYZ (
83
+ const pcl::PointCloud<pcl::PointXY>::Ptr & pointcloud_xy)
60
84
{
61
85
pcl::PointCloud<pcl::PointXYZ>::Ptr pointcloud_xyz (new pcl::PointCloud<pcl::PointXYZ>);
62
- pointcloud_xyz->reserve (pointcloud ->size ());
63
- for (const auto & point : *pointcloud ) {
64
- pointcloud_xyz->push_back (toXYZ (point));
86
+ pointcloud_xyz->reserve (pointcloud_xy ->size ());
87
+ for (const auto & point : *pointcloud_xy ) {
88
+ pointcloud_xyz->push_back (pcl::PointXYZ (point. x , point. y , 0.0 ));
65
89
}
66
90
return pointcloud_xyz;
67
91
}
68
92
69
- } // namespace
93
+ std::optional<size_t > Validator2D::getPointCloudWithinObject (
94
+ const autoware_auto_perception_msgs::msg::DetectedObject & object,
95
+ const pcl::PointCloud<pcl::PointXY>::Ptr pointcloud)
96
+ {
97
+ std::vector<pcl::Vertices> vertices_array;
98
+ pcl::Vertices vertices;
99
+ Polygon2d poly2d =
100
+ tier4_autoware_utils::toPolygon2d (object.kinematics .pose_with_covariance .pose , object.shape );
101
+ if (bg::is_empty (poly2d)) return std::nullopt;
70
102
71
- namespace obstacle_pointcloud_based_validator
103
+ pcl::PointCloud<pcl::PointXYZ>::Ptr poly3d (new pcl::PointCloud<pcl::PointXYZ>);
104
+
105
+ for (size_t i = 0 ; i < poly2d.outer ().size (); ++i) {
106
+ vertices.vertices .emplace_back (i);
107
+ vertices_array.emplace_back (vertices);
108
+ poly3d->emplace_back (poly2d.outer ().at (i).x (), poly2d.outer ().at (i).y (), 0.0 );
109
+ }
110
+ cropped_pointcloud_.reset (new pcl::PointCloud<pcl::PointXYZ>);
111
+ pcl::CropHull<pcl::PointXYZ> cropper; // don't be implemented PointXY by PCL
112
+ cropper.setInputCloud (convertToXYZ (pointcloud));
113
+ cropper.setDim (2 );
114
+ cropper.setHullIndices (vertices_array);
115
+ cropper.setHullCloud (poly3d);
116
+ cropper.setCropOutside (true );
117
+ cropper.filter (*cropped_pointcloud_);
118
+ return cropped_pointcloud_->size ();
119
+ }
120
+
121
+ bool Validator2D::validate_object (
122
+ const autoware_auto_perception_msgs::msg::DetectedObject & transformed_object)
72
123
{
73
- namespace bg = boost::geometry;
74
- using Shape = autoware_auto_perception_msgs::msg::Shape;
75
- using Polygon2d = tier4_autoware_utils::Polygon2d;
124
+ // get neighbor_pointcloud of object
125
+ neighbor_pointcloud_.reset (new pcl::PointCloud<pcl::PointXY>);
126
+ std::vector<int > indices;
127
+ std::vector<float > distances;
128
+ const auto search_radius = getMaxRadius (transformed_object);
129
+ if (!search_radius) {
130
+ return false ;
131
+ }
132
+ kdtree_->radiusSearch (
133
+ pcl::PointXY (
134
+ transformed_object.kinematics .pose_with_covariance .pose .position .x ,
135
+ transformed_object.kinematics .pose_with_covariance .pose .position .y ),
136
+ search_radius.value (), indices, distances);
137
+ for (const auto & index : indices) {
138
+ neighbor_pointcloud_->push_back (obstacle_pointcloud_->at (index ));
139
+ }
140
+ const auto num = getPointCloudWithinObject (transformed_object, neighbor_pointcloud_);
141
+ if (!num) return true ;
142
+
143
+ size_t threshold_pointcloud_num = getThresholdPointCloud (transformed_object);
144
+ if (num.value () > threshold_pointcloud_num) {
145
+ return true ;
146
+ }
147
+ return false ; // remove object
148
+ }
149
+
150
+ std::optional<float > Validator2D::getMaxRadius (
151
+ const autoware_auto_perception_msgs::msg::DetectedObject & object)
152
+ {
153
+ if (object.shape .type == Shape::BOUNDING_BOX || object.shape .type == Shape::CYLINDER) {
154
+ return std::hypot (object.shape .dimensions .x * 0 .5f , object.shape .dimensions .y * 0 .5f );
155
+ } else if (object.shape .type == Shape::POLYGON) {
156
+ float max_dist = 0.0 ;
157
+ for (const auto & point : object.shape .footprint .points ) {
158
+ const float dist = std::hypot (point.x , point.y );
159
+ max_dist = max_dist < dist ? dist : max_dist;
160
+ }
161
+ return max_dist;
162
+ } else {
163
+ return std::nullopt;
164
+ }
165
+ }
166
+
167
+ Validator3D::Validator3D (PointsNumThresholdParam & points_num_threshold_param)
168
+ : Validator(points_num_threshold_param)
169
+ {
170
+ }
171
+ bool Validator3D::setKdtreeInputCloud (
172
+ const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_cloud)
173
+ {
174
+ obstacle_pointcloud_.reset (new pcl::PointCloud<pcl::PointXYZ>);
175
+ pcl::fromROSMsg (*input_cloud, *obstacle_pointcloud_);
176
+ if (obstacle_pointcloud_->empty ()) {
177
+ return false ;
178
+ }
179
+ // setup kdtree_
180
+ kdtree_ = pcl::make_shared<pcl::search::KdTree<pcl::PointXYZ>>(false );
181
+ kdtree_->setInputCloud (obstacle_pointcloud_);
182
+ return true ;
183
+ }
184
+ std::optional<float > Validator3D::getMaxRadius (
185
+ const autoware_auto_perception_msgs::msg::DetectedObject & object)
186
+ {
187
+ if (object.shape .type == Shape::BOUNDING_BOX || object.shape .type == Shape::CYLINDER) {
188
+ auto square_radius = (object.shape .dimensions .x * 0 .5f ) * (object.shape .dimensions .x * 0 .5f ) +
189
+ (object.shape .dimensions .y * 0 .5f ) * (object.shape .dimensions .y * 0 .5f ) +
190
+ (object.shape .dimensions .z * 0 .5f ) * (object.shape .dimensions .z * 0 .5f );
191
+ return std::sqrt (square_radius);
192
+ } else if (object.shape .type == Shape::POLYGON) {
193
+ float max_dist = 0.0 ;
194
+ for (const auto & point : object.shape .footprint .points ) {
195
+ const float dist = std::hypot (point.x , point.y );
196
+ max_dist = max_dist < dist ? dist : max_dist;
197
+ }
198
+ return std::hypot (max_dist, object.shape .dimensions .z * 0 .5f );
199
+ } else {
200
+ return std::nullopt;
201
+ }
202
+ }
203
+
204
+ std::optional<size_t > Validator3D::getPointCloudWithinObject (
205
+ const autoware_auto_perception_msgs::msg::DetectedObject & object,
206
+ const pcl::PointCloud<pcl::PointXYZ>::Ptr neighbor_pointcloud)
207
+ {
208
+ std::vector<pcl::Vertices> vertices_array;
209
+ pcl::Vertices vertices;
210
+ auto const & object_position = object.kinematics .pose_with_covariance .pose .position ;
211
+ auto const object_height = object.shape .dimensions .x ;
212
+ auto z_min = object_position.z - object_height / 2 .0f ;
213
+ auto z_max = object_position.z + object_height / 2 .0f ;
214
+ Polygon2d poly2d =
215
+ tier4_autoware_utils::toPolygon2d (object.kinematics .pose_with_covariance .pose , object.shape );
216
+ if (bg::is_empty (poly2d)) return std::nullopt;
217
+
218
+ pcl::PointCloud<pcl::PointXYZ>::Ptr poly3d (new pcl::PointCloud<pcl::PointXYZ>);
219
+ for (size_t i = 0 ; i < poly2d.outer ().size (); ++i) {
220
+ vertices.vertices .emplace_back (i);
221
+ vertices_array.emplace_back (vertices);
222
+ poly3d->emplace_back (poly2d.outer ().at (i).x (), poly2d.outer ().at (i).y (), 0.0 );
223
+ }
224
+
225
+ pcl::PointCloud<pcl::PointXYZ>::Ptr cropped_pointcloud_2d (new pcl::PointCloud<pcl::PointXYZ>);
226
+ pcl::CropHull<pcl::PointXYZ> cropper;
227
+ cropper.setInputCloud (neighbor_pointcloud);
228
+ cropper.setDim (2 );
229
+ cropper.setHullIndices (vertices_array);
230
+ cropper.setHullCloud (poly3d);
231
+ cropper.setCropOutside (true );
232
+ cropper.filter (*cropped_pointcloud_2d);
233
+
234
+ cropped_pointcloud_.reset (new pcl::PointCloud<pcl::PointXYZ>);
235
+ cropped_pointcloud_->reserve (cropped_pointcloud_2d->size ());
236
+ for (const auto & point : *cropped_pointcloud_2d) {
237
+ if (point.z > z_min && point.z < z_max) {
238
+ cropped_pointcloud_->push_back (point);
239
+ }
240
+ }
241
+ return cropped_pointcloud_->size ();
242
+ }
243
+
244
+ bool Validator3D::validate_object (
245
+ const autoware_auto_perception_msgs::msg::DetectedObject & transformed_object)
246
+ {
247
+ neighbor_pointcloud_.reset (new pcl::PointCloud<pcl::PointXYZ>);
248
+ std::vector<int > indices;
249
+ std::vector<float > distances;
250
+ const auto search_radius = getMaxRadius (transformed_object);
251
+ if (!search_radius) {
252
+ return false ;
253
+ }
254
+ kdtree_->radiusSearch (
255
+ pcl::PointXYZ (
256
+ transformed_object.kinematics .pose_with_covariance .pose .position .x ,
257
+ transformed_object.kinematics .pose_with_covariance .pose .position .y ,
258
+ transformed_object.kinematics .pose_with_covariance .pose .position .z ),
259
+ search_radius.value (), indices, distances);
260
+ for (const auto & index : indices) {
261
+ neighbor_pointcloud_->push_back (obstacle_pointcloud_->at (index ));
262
+ }
263
+
264
+ const auto num = getPointCloudWithinObject (transformed_object, neighbor_pointcloud_);
265
+ if (!num) return true ;
266
+
267
+ size_t threshold_pointcloud_num = getThresholdPointCloud (transformed_object);
268
+ if (num.value () > threshold_pointcloud_num) {
269
+ return true ;
270
+ }
271
+ return false ; // remove object
272
+ }
76
273
77
274
ObstaclePointCloudBasedValidator::ObstaclePointCloudBasedValidator (
78
275
const rclcpp::NodeOptions & node_options)
@@ -85,24 +282,32 @@ ObstaclePointCloudBasedValidator::ObstaclePointCloudBasedValidator(
85
282
tf_listener_(tf_buffer_),
86
283
sync_(SyncPolicy(10 ), objects_sub_, obstacle_pointcloud_sub_)
87
284
{
88
- using std::placeholders::_1;
89
- using std::placeholders::_2;
90
- sync_.registerCallback (
91
- std::bind (&ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud, this , _1, _2));
92
- objects_pub_ = create_publisher<autoware_auto_perception_msgs::msg::DetectedObjects>(
93
- " ~/output/objects" , rclcpp::QoS{1 });
94
-
95
285
points_num_threshold_param_.min_points_num =
96
286
declare_parameter<std::vector<int64_t >>(" min_points_num" );
97
287
points_num_threshold_param_.max_points_num =
98
288
declare_parameter<std::vector<int64_t >>(" max_points_num" );
99
289
points_num_threshold_param_.min_points_and_distance_ratio =
100
290
declare_parameter<std::vector<double >>(" min_points_and_distance_ratio" );
101
291
292
+ using_2d_validator_ = declare_parameter<bool >(" using_2d_validator" );
293
+
294
+ using std::placeholders::_1;
295
+ using std::placeholders::_2;
296
+
297
+ sync_.registerCallback (
298
+ std::bind (&ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud, this , _1, _2));
299
+ if (using_2d_validator_) {
300
+ validator_ = std::make_unique<Validator2D>(points_num_threshold_param_);
301
+ } else {
302
+ validator_ = std::make_unique<Validator3D>(points_num_threshold_param_);
303
+ }
304
+
305
+ objects_pub_ = create_publisher<autoware_auto_perception_msgs::msg::DetectedObjects>(
306
+ " ~/output/objects" , rclcpp::QoS{1 });
307
+
102
308
const bool enable_debugger = declare_parameter<bool >(" enable_debugger" , false );
103
309
if (enable_debugger) debugger_ = std::make_shared<Debugger >(this );
104
310
}
105
-
106
311
void ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud (
107
312
const autoware_auto_perception_msgs::msg::DetectedObjects::ConstSharedPtr & input_objects,
108
313
const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_obstacle_pointcloud)
@@ -119,61 +324,23 @@ void ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud(
119
324
// objects_pub_->publish(*input_objects);
120
325
return ;
121
326
}
122
-
123
- // Convert to PCL
124
- pcl::PointCloud<pcl::PointXY>::Ptr obstacle_pointcloud (new pcl::PointCloud<pcl::PointXY>);
125
- pcl::fromROSMsg (*input_obstacle_pointcloud, *obstacle_pointcloud);
126
- if (obstacle_pointcloud->empty ()) {
327
+ if (!validator_->setKdtreeInputCloud (input_obstacle_pointcloud)) {
127
328
RCLCPP_WARN_THROTTLE (this ->get_logger (), *this ->get_clock (), 5 , " cannot receive pointcloud" );
128
- // objects_pub_->publish(*input_objects);
129
329
return ;
130
330
}
131
331
132
- // Create Kd-tree to search neighbor pointcloud to reduce cost.
133
- pcl::search::Search<pcl::PointXY>::Ptr kdtree =
134
- pcl::make_shared<pcl::search::KdTree<pcl::PointXY>>(false );
135
- kdtree->setInputCloud (obstacle_pointcloud);
136
-
137
332
for (size_t i = 0 ; i < transformed_objects.objects .size (); ++i) {
138
333
const auto & transformed_object = transformed_objects.objects .at (i);
139
- const auto object_label_id = transformed_object.classification .front ().label ;
140
334
const auto & object = input_objects->objects .at (i);
141
- const auto & transformed_object_position =
142
- transformed_object.kinematics .pose_with_covariance .pose .position ;
143
- const auto search_radius = getMaxRadius (transformed_object);
144
- if (!search_radius) {
145
- output.objects .push_back (object);
146
- continue ;
147
- }
148
-
149
- // Search neighbor pointcloud to reduce cost.
150
- pcl::PointCloud<pcl::PointXY>::Ptr neighbor_pointcloud (new pcl::PointCloud<pcl::PointXY>);
151
- std::vector<int > indices;
152
- std::vector<float > distances;
153
- kdtree->radiusSearch (
154
- toPCL (transformed_object_position), search_radius.value (), indices, distances);
155
- for (const auto & index : indices) {
156
- neighbor_pointcloud->push_back (obstacle_pointcloud->at (index ));
335
+ const auto validated = validator_->validate_object (transformed_object);
336
+ if (debugger_) {
337
+ debugger_->addNeighborPointcloud (validator_->getDebugNeighborPointCloud ());
338
+ debugger_->addPointcloudWithinPolygon (validator_->getDebugPointCloudWithinObject ());
157
339
}
158
- if (debugger_) debugger_->addNeighborPointcloud (neighbor_pointcloud);
159
-
160
- // Filter object that have few pointcloud in them.
161
- // TODO(badai-nguyen) add 3d validator option
162
- const auto num = getPointCloudNumWithinPolygon (transformed_object, neighbor_pointcloud);
163
- const auto object_distance =
164
- std::hypot (transformed_object_position.x , transformed_object_position.y );
165
- size_t min_pointcloud_num = std::clamp (
166
- static_cast <size_t >(
167
- points_num_threshold_param_.min_points_and_distance_ratio .at (object_label_id) /
168
- object_distance +
169
- 0 .5f ),
170
- static_cast <size_t >(points_num_threshold_param_.min_points_num .at (object_label_id)),
171
- static_cast <size_t >(points_num_threshold_param_.max_points_num .at (object_label_id)));
172
- if (num) {
173
- (min_pointcloud_num <= num.value ()) ? output.objects .push_back (object)
174
- : removed_objects.objects .push_back (object);
175
- } else {
340
+ if (validated) {
176
341
output.objects .push_back (object);
342
+ } else {
343
+ removed_objects.objects .push_back (object);
177
344
}
178
345
}
179
346
@@ -185,56 +352,6 @@ void ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud(
185
352
}
186
353
}
187
354
188
- std::optional<size_t > ObstaclePointCloudBasedValidator::getPointCloudNumWithinPolygon (
189
- const autoware_auto_perception_msgs::msg::DetectedObject & object,
190
- const pcl::PointCloud<pcl::PointXY>::Ptr pointcloud)
191
- {
192
- pcl::PointCloud<pcl::PointXYZ>::Ptr cropped_pointcloud (new pcl::PointCloud<pcl::PointXYZ>);
193
- std::vector<pcl::Vertices> vertices_array;
194
- pcl::Vertices vertices;
195
-
196
- Polygon2d poly2d =
197
- tier4_autoware_utils::toPolygon2d (object.kinematics .pose_with_covariance .pose , object.shape );
198
- if (bg::is_empty (poly2d)) return std::nullopt;
199
-
200
- pcl::PointCloud<pcl::PointXYZ>::Ptr poly3d (new pcl::PointCloud<pcl::PointXYZ>);
201
-
202
- for (size_t i = 0 ; i < poly2d.outer ().size (); ++i) {
203
- vertices.vertices .emplace_back (i);
204
- vertices_array.emplace_back (vertices);
205
- poly3d->emplace_back (poly2d.outer ().at (i).x (), poly2d.outer ().at (i).y (), 0.0 );
206
- }
207
-
208
- pcl::CropHull<pcl::PointXYZ> cropper; // don't be implemented PointXY by PCL
209
- cropper.setInputCloud (toXYZ (pointcloud));
210
- cropper.setDim (2 );
211
- cropper.setHullIndices (vertices_array);
212
- cropper.setHullCloud (poly3d);
213
- cropper.setCropOutside (true );
214
- cropper.filter (*cropped_pointcloud);
215
-
216
- if (debugger_) debugger_->addPointcloudWithinPolygon (cropped_pointcloud);
217
- return cropped_pointcloud->size ();
218
- }
219
-
220
- std::optional<float > ObstaclePointCloudBasedValidator::getMaxRadius (
221
- const autoware_auto_perception_msgs::msg::DetectedObject & object)
222
- {
223
- if (object.shape .type == Shape::BOUNDING_BOX || object.shape .type == Shape::CYLINDER) {
224
- return std::hypot (object.shape .dimensions .x * 0 .5f , object.shape .dimensions .y * 0 .5f );
225
- } else if (object.shape .type == Shape::POLYGON) {
226
- float max_dist = 0.0 ;
227
- for (const auto & point : object.shape .footprint .points ) {
228
- const float dist = std::hypot (point.x , point.y );
229
- max_dist = max_dist < dist ? dist : max_dist;
230
- }
231
- return max_dist;
232
- } else {
233
- RCLCPP_WARN_THROTTLE (this ->get_logger (), *this ->get_clock (), 5000 , " unknown shape type" );
234
- return std::nullopt;
235
- }
236
- }
237
-
238
355
} // namespace obstacle_pointcloud_based_validator
239
356
240
357
#include < rclcpp_components/register_node_macro.hpp>
0 commit comments