Skip to content

Commit a76d360

Browse files
authoredNov 15, 2023
fix(detected_object_validation): add 3d pointcloud based validator (#5327)
* fix: add 3d validation bind function Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: check validation point within shape Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: add 2d validator option param Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * chore: refactor Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * chore: refactor Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * Revert "chore: refactor" This reverts commit e3cbf6c. * chore: update docs and param file Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * refactor: change to abstract class Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * chore: add maintainer Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> --------- Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp>
1 parent b5d6c51 commit a76d360

File tree

6 files changed

+344
-138
lines changed

6 files changed

+344
-138
lines changed
 

‎perception/detected_object_validation/config/obstacle_pointcloud_based_validator.param.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@
1111
min_points_and_distance_ratio:
1212
#UNKNOWN, CAR, TRUCK, BUS, TRAILER, MOTORBIKE, BICYCLE, PEDESTRIAN
1313
[800.0, 800.0, 800.0, 800.0, 800.0, 800.0, 800.0, 800.0]
14+
15+
using_2d_validator: false

‎perception/detected_object_validation/include/obstacle_pointcloud_based_validator/debugger.hpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ class Debugger
7171
void addNeighborPointcloud(const pcl::PointCloud<pcl::PointXY>::Ptr & input)
7272
{
7373
pcl::PointCloud<pcl::PointXYZ>::Ptr input_xyz = toXYZ(input);
74-
for (const auto & point : *input_xyz) {
75-
neighbor_pointcloud_->push_back(point);
76-
}
74+
addNeighborPointcloud(input_xyz);
7775
}
7876

77+
void addNeighborPointcloud(const pcl::PointCloud<pcl::PointXYZ>::Ptr & input)
78+
{
79+
if (!input->empty()) {
80+
neighbor_pointcloud_->reserve(neighbor_pointcloud_->size() + input->size());
81+
for (const auto & point : *input) {
82+
neighbor_pointcloud_->push_back(point);
83+
}
84+
}
85+
}
7986
void addPointcloudWithinPolygon(const pcl::PointCloud<pcl::PointXYZ>::Ptr & input)
8087
{
8188
// pcl::PointCloud<pcl::PointXYZ>::Ptr input_xyz = toXYZ(input);

‎perception/detected_object_validation/include/obstacle_pointcloud_based_validator/obstacle_pointcloud_based_validator.hpp

+83-5
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@
2525
#include <message_filters/subscriber.h>
2626
#include <message_filters/sync_policies/approximate_time.h>
2727
#include <message_filters/synchronizer.h>
28+
#include <pcl/filters/crop_hull.h>
29+
#include <pcl/kdtree/kdtree_flann.h>
2830
#include <pcl/point_cloud.h>
2931
#include <pcl/point_types.h>
32+
#include <pcl/search/kdtree.h>
33+
#include <pcl/search/pcl_search.h>
34+
#include <pcl_conversions/pcl_conversions.h>
3035
#include <tf2_ros/buffer.h>
3136
#include <tf2_ros/transform_listener.h>
3237

@@ -35,12 +40,88 @@
3540
#include <vector>
3641
namespace obstacle_pointcloud_based_validator
3742
{
43+
3844
struct PointsNumThresholdParam
3945
{
4046
std::vector<int64_t> min_points_num;
4147
std::vector<int64_t> max_points_num;
4248
std::vector<double> min_points_and_distance_ratio;
4349
};
50+
51+
class Validator
52+
{
53+
private:
54+
PointsNumThresholdParam points_num_threshold_param_;
55+
56+
protected:
57+
pcl::PointCloud<pcl::PointXYZ>::Ptr cropped_pointcloud_;
58+
59+
public:
60+
explicit Validator(PointsNumThresholdParam & points_num_threshold_param);
61+
inline pcl::PointCloud<pcl::PointXYZ>::Ptr getDebugPointCloudWithinObject()
62+
{
63+
return cropped_pointcloud_;
64+
}
65+
66+
virtual bool setKdtreeInputCloud(
67+
const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_pointcloud) = 0;
68+
virtual bool validate_object(
69+
const autoware_auto_perception_msgs::msg::DetectedObject & transformed_object) = 0;
70+
virtual std::optional<float> getMaxRadius(
71+
const autoware_auto_perception_msgs::msg::DetectedObject & object) = 0;
72+
size_t getThresholdPointCloud(const autoware_auto_perception_msgs::msg::DetectedObject & object);
73+
virtual pcl::PointCloud<pcl::PointXYZ>::Ptr getDebugNeighborPointCloud() = 0;
74+
};
75+
76+
class Validator2D : public Validator
77+
{
78+
private:
79+
pcl::PointCloud<pcl::PointXY>::Ptr obstacle_pointcloud_;
80+
pcl::PointCloud<pcl::PointXY>::Ptr neighbor_pointcloud_;
81+
pcl::search::Search<pcl::PointXY>::Ptr kdtree_;
82+
83+
public:
84+
explicit Validator2D(PointsNumThresholdParam & points_num_threshold_param);
85+
86+
pcl::PointCloud<pcl::PointXYZ>::Ptr convertToXYZ(
87+
const pcl::PointCloud<pcl::PointXY>::Ptr & pointcloud_xy);
88+
inline pcl::PointCloud<pcl::PointXYZ>::Ptr getDebugNeighborPointCloud()
89+
{
90+
return convertToXYZ(neighbor_pointcloud_);
91+
}
92+
93+
bool setKdtreeInputCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_cloud);
94+
bool validate_object(
95+
const autoware_auto_perception_msgs::msg::DetectedObject & transformed_object);
96+
std::optional<float> getMaxRadius(
97+
const autoware_auto_perception_msgs::msg::DetectedObject & object);
98+
std::optional<size_t> getPointCloudWithinObject(
99+
const autoware_auto_perception_msgs::msg::DetectedObject & object,
100+
const pcl::PointCloud<pcl::PointXY>::Ptr neighbor_pointcloud);
101+
};
102+
class Validator3D : public Validator
103+
{
104+
private:
105+
pcl::PointCloud<pcl::PointXYZ>::Ptr obstacle_pointcloud_;
106+
pcl::PointCloud<pcl::PointXYZ>::Ptr neighbor_pointcloud_;
107+
pcl::search::Search<pcl::PointXYZ>::Ptr kdtree_;
108+
109+
public:
110+
explicit Validator3D(PointsNumThresholdParam & points_num_threshold_param);
111+
inline pcl::PointCloud<pcl::PointXYZ>::Ptr getDebugNeighborPointCloud()
112+
{
113+
return neighbor_pointcloud_;
114+
}
115+
bool setKdtreeInputCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_cloud);
116+
bool validate_object(
117+
const autoware_auto_perception_msgs::msg::DetectedObject & transformed_object);
118+
std::optional<float> getMaxRadius(
119+
const autoware_auto_perception_msgs::msg::DetectedObject & object);
120+
std::optional<size_t> getPointCloudWithinObject(
121+
const autoware_auto_perception_msgs::msg::DetectedObject & object,
122+
const pcl::PointCloud<pcl::PointXYZ>::Ptr neighbor_pointcloud);
123+
};
124+
44125
class ObstaclePointCloudBasedValidator : public rclcpp::Node
45126
{
46127
public:
@@ -61,16 +142,13 @@ class ObstaclePointCloudBasedValidator : public rclcpp::Node
61142
PointsNumThresholdParam points_num_threshold_param_;
62143

63144
std::shared_ptr<Debugger> debugger_;
145+
bool using_2d_validator_;
146+
std::unique_ptr<Validator> validator_;
64147

65148
private:
66149
void onObjectsAndObstaclePointCloud(
67150
const autoware_auto_perception_msgs::msg::DetectedObjects::ConstSharedPtr & input_objects,
68151
const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_obstacle_pointcloud);
69-
std::optional<size_t> getPointCloudNumWithinPolygon(
70-
const autoware_auto_perception_msgs::msg::DetectedObject & object,
71-
const pcl::PointCloud<pcl::PointXY>::Ptr pointcloud);
72-
std::optional<float> getMaxRadius(
73-
const autoware_auto_perception_msgs::msg::DetectedObject & object);
74152
};
75153
} // namespace obstacle_pointcloud_based_validator
76154

‎perception/detected_object_validation/obstacle-pointcloud-based-validator.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ In the debug image above, the red DetectedObject is the validated object. The bl
2828

2929
| Name | Type | Description |
3030
| ------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31+
| `using_2d_validator` | bool | The xy-plane projected (2D) obstacle point clouds will be used for validation |
3132
| `min_points_num` | int | The minimum number of obstacle point clouds in DetectedObjects |
3233
| `max_points_num` | int | The max number of obstacle point clouds in DetectedObjects |
3334
| `min_points_and_distance_ratio` | float | Threshold value of the number of point clouds per object when the distance from baselink is 1m, because the number of point clouds varies with the distance from baselink. |

‎perception/detected_object_validation/package.xml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<description>The ROS 2 detected_object_validation package</description>
77
<maintainer email="yukihiro.saito@tier4.jp">Yukihiro Saito</maintainer>
88
<maintainer email="shunsuke.miura@tier4.jp">Shunsuke Miura</maintainer>
9+
<maintainer email="dai.nguyen@tier4.jp">badai nguyen</maintainer>
910
<license>Apache License 2.0</license>
1011

1112
<buildtool_depend>ament_cmake_auto</buildtool_depend>

‎perception/detected_object_validation/src/obstacle_pointcloud_based_validator.cpp

+247-130
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@
1919

2020
#include <boost/geometry.hpp>
2121

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-
2822
#ifdef ROS_DISTRO_GALACTIC
2923
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
3024
#else
@@ -35,44 +29,247 @@
3529
#include <Eigen/Core>
3630
#include <Eigen/Geometry>
3731

38-
namespace
32+
namespace obstacle_pointcloud_based_validator
3933
{
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)
4139
{
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;
4644
}
4745

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)
4948
{
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;
5161
}
5262

53-
inline pcl::PointXYZ toXYZ(const pcl::PointXY & point)
63+
Validator2D::Validator2D(PointsNumThresholdParam & points_num_threshold_param)
64+
: Validator(points_num_threshold_param)
5465
{
55-
return pcl::PointXYZ(point.x, point.y, 0.0);
5666
}
5767

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)
6084
{
6185
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));
6589
}
6690
return pointcloud_xyz;
6791
}
6892

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;
70102

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)
72123
{
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+
}
76273

77274
ObstaclePointCloudBasedValidator::ObstaclePointCloudBasedValidator(
78275
const rclcpp::NodeOptions & node_options)
@@ -85,24 +282,32 @@ ObstaclePointCloudBasedValidator::ObstaclePointCloudBasedValidator(
85282
tf_listener_(tf_buffer_),
86283
sync_(SyncPolicy(10), objects_sub_, obstacle_pointcloud_sub_)
87284
{
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-
95285
points_num_threshold_param_.min_points_num =
96286
declare_parameter<std::vector<int64_t>>("min_points_num");
97287
points_num_threshold_param_.max_points_num =
98288
declare_parameter<std::vector<int64_t>>("max_points_num");
99289
points_num_threshold_param_.min_points_and_distance_ratio =
100290
declare_parameter<std::vector<double>>("min_points_and_distance_ratio");
101291

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+
102308
const bool enable_debugger = declare_parameter<bool>("enable_debugger", false);
103309
if (enable_debugger) debugger_ = std::make_shared<Debugger>(this);
104310
}
105-
106311
void ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud(
107312
const autoware_auto_perception_msgs::msg::DetectedObjects::ConstSharedPtr & input_objects,
108313
const sensor_msgs::msg::PointCloud2::ConstSharedPtr & input_obstacle_pointcloud)
@@ -119,61 +324,23 @@ void ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud(
119324
// objects_pub_->publish(*input_objects);
120325
return;
121326
}
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)) {
127328
RCLCPP_WARN_THROTTLE(this->get_logger(), *this->get_clock(), 5, "cannot receive pointcloud");
128-
// objects_pub_->publish(*input_objects);
129329
return;
130330
}
131331

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-
137332
for (size_t i = 0; i < transformed_objects.objects.size(); ++i) {
138333
const auto & transformed_object = transformed_objects.objects.at(i);
139-
const auto object_label_id = transformed_object.classification.front().label;
140334
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());
157339
}
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) {
176341
output.objects.push_back(object);
342+
} else {
343+
removed_objects.objects.push_back(object);
177344
}
178345
}
179346

@@ -185,56 +352,6 @@ void ObstaclePointCloudBasedValidator::onObjectsAndObstaclePointCloud(
185352
}
186353
}
187354

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-
238355
} // namespace obstacle_pointcloud_based_validator
239356

240357
#include <rclcpp_components/register_node_macro.hpp>

0 commit comments

Comments
 (0)
Please sign in to comment.