Skip to content

Commit 7da383d

Browse files
amadeuszszpre-commit-ci[bot]knzo25
authored andcommitted
feat(lidar_transfusion): intensity as uint8 and tests (autowarefoundation#7745)
* feat(lidar_transfusion): intensity as uint8 and tests Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * style(pre-commit): autofix * fix(lidar_transfusion): headers include Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * feat(lidar_transfusion): use autoware_point_types for validation & refactor Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * style(lidar_transfusion): remove deprecated function Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * test(lidar_transfusion): point_step validation is not required Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * docs(lidar_transfusion): update assumptions / known limits Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * refactor(lidar_transfusion): redundant points structure Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * docs(lidar_transfusion): update assumptions / known limits Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> * test(lidar_transfusion): temporary disable CUDA tests Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> --------- Signed-off-by: Amadeusz Szymko <amadeusz.szymko.2@tier4.jp> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kenzo Lobos Tsunekawa <kenzo.lobos@tier4.jp>
1 parent dc446be commit 7da383d

20 files changed

+1517
-105
lines changed

perception/lidar_transfusion/CMakeLists.txt

+52
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,58 @@ if(TRT_AVAIL AND CUDA_AVAIL AND CUDNN_AVAIL)
137137
if(BUILD_TESTING)
138138
find_package(ament_lint_auto REQUIRED)
139139
ament_lint_auto_find_test_dependencies()
140+
141+
find_package(ament_cmake_gtest REQUIRED)
142+
ament_auto_add_gtest(test_detection_class_remapper
143+
test/test_detection_class_remapper.cpp
144+
)
145+
ament_auto_add_gtest(test_ros_utils
146+
test/test_ros_utils.cpp
147+
)
148+
ament_auto_add_gtest(test_nms
149+
test/test_nms.cpp
150+
)
151+
152+
# Temporary disabled, tracked by:
153+
# https://github.com/autowarefoundation/autoware.universe/issues/7724
154+
# ament_auto_add_gtest(test_voxel_generator
155+
# test/test_voxel_generator.cpp
156+
# )
157+
158+
# # preprocess kernel test
159+
# add_executable(test_preprocess_kernel
160+
# test/test_preprocess_kernel.cpp
161+
# )
162+
# target_include_directories(test_preprocess_kernel PUBLIC
163+
# ${test_preprocess_kernel_SOURCE_DIR}
164+
# )
165+
# target_link_libraries(test_preprocess_kernel
166+
# transfusion_cuda_lib
167+
# gtest
168+
# gtest_main
169+
# )
170+
# ament_add_test(test_preprocess_kernel
171+
# GENERATE_RESULT_FOR_RETURN_CODE_ZERO
172+
# COMMAND "$<TARGET_FILE:test_preprocess_kernel>"
173+
# )
174+
175+
# # postprocess kernel test
176+
# add_executable(test_postprocess_kernel
177+
# test/test_postprocess_kernel.cpp
178+
# )
179+
# target_include_directories(test_postprocess_kernel PUBLIC
180+
# ${test_postprocess_kernel_SOURCE_DIR}
181+
# )
182+
# target_link_libraries(test_postprocess_kernel
183+
# transfusion_cuda_lib
184+
# gtest
185+
# gtest_main
186+
# )
187+
# ament_add_test(test_postprocess_kernel
188+
# GENERATE_RESULT_FOR_RETURN_CODE_ZERO
189+
# COMMAND "$<TARGET_FILE:test_postprocess_kernel>"
190+
# )
191+
140192
endif()
141193

142194
ament_auto_package(

perception/lidar_transfusion/README.md

+18
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ ros2 launch lidar_transfusion lidar_transfusion.launch.xml log_level:=debug
5959

6060
## Assumptions / Known limits
6161

62+
This library operates on raw cloud data (bytes). It is assumed that the input pointcloud message has following format:
63+
64+
```python
65+
[
66+
sensor_msgs.msg.PointField(name='x', offset=0, datatype=7, count=1),
67+
sensor_msgs.msg.PointField(name='y', offset=4, datatype=7, count=1),
68+
sensor_msgs.msg.PointField(name='z', offset=8, datatype=7, count=1),
69+
sensor_msgs.msg.PointField(name='intensity', offset=12, datatype=2, count=1)
70+
]
71+
```
72+
73+
This input may consist of other fields as well - shown format is required minimum.
74+
For debug purposes, you can validate your pointcloud topic using simple command:
75+
76+
```bash
77+
ros2 topic echo <input_topic> --field fields
78+
```
79+
6280
## Trained Models
6381

6482
You can download the onnx format of trained models by clicking on the links below.

perception/lidar_transfusion/include/lidar_transfusion/preprocess/pointcloud_densification.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class DensificationParam
5151

5252
struct PointCloudWithTransform
5353
{
54-
cuda::unique_ptr<float[]> points_d{nullptr};
54+
cuda::unique_ptr<uint8_t[]> data_d{nullptr};
5555
std_msgs::msg::Header header;
5656
size_t num_points{0};
5757
Eigen::Affine3f affine_past2world;

perception/lidar_transfusion/include/lidar_transfusion/preprocess/preprocess_kernel.hpp

+1-5
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,8 @@ class PreprocessCuda
5656
unsigned int * mask, float * voxels, unsigned int * pillar_num, float * voxel_features,
5757
unsigned int * voxel_num, unsigned int * voxel_idxs);
5858

59-
// cudaError_t generateVoxelsInput_launch(
60-
// uint8_t * cloud_data, CloudInfo & cloud_info, unsigned int points_agg, unsigned int
61-
// points_size, float time_lag, float * affine_transform, float * points);
62-
6359
cudaError_t generateSweepPoints_launch(
64-
const float * input_points, size_t points_size, int input_point_step, float time_lag,
60+
const uint8_t * input_data, size_t points_size, int input_point_step, float time_lag,
6561
const float * transform, float * output_points);
6662

6763
private:

perception/lidar_transfusion/include/lidar_transfusion/preprocess/voxel_generator.hpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@
1818
#include "lidar_transfusion/cuda_utils.hpp"
1919
#include "lidar_transfusion/preprocess/pointcloud_densification.hpp"
2020
#include "lidar_transfusion/preprocess/preprocess_kernel.hpp"
21+
#include "lidar_transfusion/ros_utils.hpp"
2122
#include "lidar_transfusion/transfusion_config.hpp"
22-
#include "lidar_transfusion/utils.hpp"
2323

2424
#ifdef ROS_DISTRO_GALACTIC
2525
#include <tf2_eigen/tf2_eigen.h>
2626
#else
2727
#include <tf2_eigen/tf2_eigen.hpp>
2828
#endif
2929

30+
#include <autoware_point_types/types.hpp>
31+
3032
#include <sensor_msgs/msg/point_cloud2.hpp>
3133

3234
#include <memory>
@@ -36,8 +38,8 @@
3638

3739
namespace lidar_transfusion
3840
{
39-
constexpr size_t AFF_MAT_SIZE = 16; // 4x4 matrix
40-
constexpr size_t MAX_CLOUD_STEP_SIZE = 35; // PointXYZIRCADT
41+
constexpr size_t AFF_MAT_SIZE = 16; // 4x4 matrix
42+
constexpr size_t MAX_CLOUD_STEP_SIZE = sizeof(autoware_point_types::PointXYZIRCAEDT);
4143

4244
class VoxelGenerator
4345
{

perception/lidar_transfusion/include/lidar_transfusion/ros_utils.hpp

+68
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,84 @@
1717

1818
#include "lidar_transfusion/utils.hpp"
1919

20+
#include <autoware_point_types/types.hpp>
21+
2022
#include <autoware_perception_msgs/msg/detected_object_kinematics.hpp>
2123
#include <autoware_perception_msgs/msg/detected_objects.hpp>
2224
#include <autoware_perception_msgs/msg/object_classification.hpp>
2325
#include <autoware_perception_msgs/msg/shape.hpp>
26+
#include <sensor_msgs/msg/point_field.hpp>
2427

28+
#include <cstdint>
2529
#include <string>
2630
#include <vector>
2731

32+
#define CHECK_OFFSET(offset, structure, field) \
33+
static_assert( \
34+
offsetof(structure, field) == offset, \
35+
"Offset of " #field " in " #structure " does not match expected offset.")
36+
#define CHECK_TYPE(type, structure, field) \
37+
static_assert( \
38+
std::is_same<decltype(structure::field), type>::value, \
39+
"Type of " #field " in " #structure " does not match expected type.")
40+
#define CHECK_FIELD(offset, type, structure, field) \
41+
CHECK_OFFSET(offset, structure, field); \
42+
CHECK_TYPE(type, structure, field)
43+
2844
namespace lidar_transfusion
2945
{
46+
using sensor_msgs::msg::PointField;
47+
48+
CHECK_FIELD(0, float, autoware_point_types::PointXYZIRCAEDT, x);
49+
CHECK_FIELD(4, float, autoware_point_types::PointXYZIRCAEDT, y);
50+
CHECK_FIELD(8, float, autoware_point_types::PointXYZIRCAEDT, z);
51+
CHECK_FIELD(12, uint8_t, autoware_point_types::PointXYZIRCAEDT, intensity);
52+
53+
struct CloudInfo
54+
{
55+
uint32_t x_offset{0};
56+
uint32_t y_offset{sizeof(float)};
57+
uint32_t z_offset{sizeof(float) * 2};
58+
uint32_t intensity_offset{sizeof(float) * 3};
59+
uint8_t x_datatype{PointField::FLOAT32};
60+
uint8_t y_datatype{PointField::FLOAT32};
61+
uint8_t z_datatype{PointField::FLOAT32};
62+
uint8_t intensity_datatype{PointField::UINT8};
63+
uint8_t x_count{1};
64+
uint8_t y_count{1};
65+
uint8_t z_count{1};
66+
uint8_t intensity_count{1};
67+
uint32_t point_step{sizeof(autoware_point_types::PointXYZIRCAEDT)};
68+
bool is_bigendian{false};
69+
70+
bool operator!=(const CloudInfo & rhs) const
71+
{
72+
return x_offset != rhs.x_offset || y_offset != rhs.y_offset || z_offset != rhs.z_offset ||
73+
intensity_offset != rhs.intensity_offset || x_datatype != rhs.x_datatype ||
74+
y_datatype != rhs.y_datatype || z_datatype != rhs.z_datatype ||
75+
intensity_datatype != rhs.intensity_datatype || x_count != rhs.x_count ||
76+
y_count != rhs.y_count || z_count != rhs.z_count ||
77+
intensity_count != rhs.intensity_count || is_bigendian != rhs.is_bigendian;
78+
}
79+
80+
friend std::ostream & operator<<(std::ostream & os, const CloudInfo & info)
81+
{
82+
os << "x_offset: " << static_cast<int>(info.x_offset) << std::endl;
83+
os << "y_offset: " << static_cast<int>(info.y_offset) << std::endl;
84+
os << "z_offset: " << static_cast<int>(info.z_offset) << std::endl;
85+
os << "intensity_offset: " << static_cast<int>(info.intensity_offset) << std::endl;
86+
os << "x_datatype: " << static_cast<int>(info.x_datatype) << std::endl;
87+
os << "y_datatype: " << static_cast<int>(info.y_datatype) << std::endl;
88+
os << "z_datatype: " << static_cast<int>(info.z_datatype) << std::endl;
89+
os << "intensity_datatype: " << static_cast<int>(info.intensity_datatype) << std::endl;
90+
os << "x_count: " << static_cast<int>(info.x_count) << std::endl;
91+
os << "y_count: " << static_cast<int>(info.y_count) << std::endl;
92+
os << "z_count: " << static_cast<int>(info.z_count) << std::endl;
93+
os << "intensity_count: " << static_cast<int>(info.intensity_count) << std::endl;
94+
os << "is_bigendian: " << static_cast<int>(info.is_bigendian) << std::endl;
95+
return os;
96+
}
97+
};
3098

3199
void box3DToDetectedObject(
32100
const Box3D & box3d, const std::vector<std::string> & class_names,

perception/lidar_transfusion/include/lidar_transfusion/utils.hpp

-46
Original file line numberDiff line numberDiff line change
@@ -36,52 +36,6 @@ struct Box3D
3636
float yaw;
3737
};
3838

39-
struct CloudInfo
40-
{
41-
uint32_t x_offset;
42-
uint32_t y_offset;
43-
uint32_t z_offset;
44-
uint32_t intensity_offset;
45-
uint8_t x_datatype;
46-
uint8_t y_datatype;
47-
uint8_t z_datatype;
48-
uint8_t intensity_datatype;
49-
uint8_t x_count;
50-
uint8_t y_count;
51-
uint8_t z_count;
52-
uint8_t intensity_count;
53-
uint32_t point_step;
54-
bool is_bigendian;
55-
56-
CloudInfo()
57-
: x_offset(0),
58-
y_offset(4),
59-
z_offset(8),
60-
intensity_offset(12),
61-
x_datatype(7),
62-
y_datatype(7),
63-
z_datatype(7),
64-
intensity_datatype(7),
65-
x_count(1),
66-
y_count(1),
67-
z_count(1),
68-
intensity_count(1),
69-
point_step(16),
70-
is_bigendian(false)
71-
{
72-
}
73-
74-
bool operator!=(const CloudInfo & rhs) const
75-
{
76-
return x_offset != rhs.x_offset || y_offset != rhs.y_offset || z_offset != rhs.z_offset ||
77-
intensity_offset != rhs.intensity_offset || x_datatype != rhs.x_datatype ||
78-
y_datatype != rhs.y_datatype || z_datatype != rhs.z_datatype ||
79-
intensity_datatype != rhs.intensity_datatype || x_count != rhs.x_count ||
80-
y_count != rhs.y_count || z_count != rhs.z_count ||
81-
intensity_count != rhs.intensity_count || is_bigendian != rhs.is_bigendian;
82-
}
83-
};
84-
8539
enum NetworkIO { voxels = 0, num_points, coors, cls_score, dir_pred, bbox_pred, ENUM_SIZE };
8640

8741
// cspell: ignore divup

perception/lidar_transfusion/lib/preprocess/pointcloud_densification.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,15 @@ void PointCloudDensification::enqueue(
9393
affine_world2current_ = affine_world2current;
9494
current_timestamp_ = rclcpp::Time(msg.header.stamp).seconds();
9595

96-
assert(sizeof(uint8_t) * msg.width * msg.height * msg.point_step % sizeof(float) == 1);
97-
auto points_d = cuda::make_unique<float[]>(
98-
sizeof(uint8_t) * msg.width * msg.height * msg.point_step / sizeof(float));
96+
auto data_d = cuda::make_unique<uint8_t[]>(
97+
sizeof(uint8_t) * msg.width * msg.height * msg.point_step / sizeof(uint8_t));
9998

10099
CHECK_CUDA_ERROR(cudaMemcpyAsync(
101-
points_d.get(), msg.data.data(), sizeof(uint8_t) * msg.width * msg.height * msg.point_step,
100+
data_d.get(), msg.data.data(), sizeof(uint8_t) * msg.width * msg.height * msg.point_step,
102101
cudaMemcpyHostToDevice, stream_));
103102

104103
PointCloudWithTransform pointcloud = {
105-
std::move(points_d), msg.header, msg.width * msg.height, affine_world2current.inverse()};
104+
std::move(data_d), msg.header, msg.width * msg.height, affine_world2current.inverse()};
106105

107106
pointcloud_cache_.push_front(std::move(pointcloud));
108107
}

perception/lidar_transfusion/lib/preprocess/preprocess_kernel.cu

+37-24
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "lidar_transfusion/cuda_utils.hpp"
3232
#include "lidar_transfusion/preprocess/preprocess_kernel.hpp"
3333

34+
#include <cstdint>
35+
3436
namespace lidar_transfusion
3537
{
3638

@@ -99,9 +101,12 @@ __global__ void generateVoxels_random_kernel(
99101
cudaError_t PreprocessCuda::generateVoxels_random_launch(
100102
float * points, unsigned int points_size, unsigned int * mask, float * voxels)
101103
{
102-
int threadNum = config_.threads_for_voxel_;
103-
dim3 blocks((points_size + threadNum - 1) / threadNum);
104-
dim3 threads(threadNum);
104+
if (points_size == 0) {
105+
return cudaGetLastError();
106+
}
107+
dim3 blocks(divup(points_size, config_.threads_for_voxel_));
108+
dim3 threads(config_.threads_for_voxel_);
109+
105110
generateVoxels_random_kernel<<<blocks, threads, 0, stream_>>>(
106111
points, points_size, config_.min_x_range_, config_.max_x_range_, config_.min_y_range_,
107112
config_.max_y_range_, config_.min_z_range_, config_.max_z_range_, config_.voxel_x_size_,
@@ -165,40 +170,48 @@ cudaError_t PreprocessCuda::generateBaseFeatures_launch(
165170
}
166171

167172
__global__ void generateSweepPoints_kernel(
168-
const float * input_points, size_t points_size, int input_point_step, float time_lag,
173+
const uint8_t * input_data, size_t points_size, int input_point_step, float time_lag,
169174
const float * transform_array, int num_features, float * output_points)
170175
{
171176
int point_idx = blockIdx.x * blockDim.x + threadIdx.x;
172177
if (point_idx >= points_size) return;
173178

174-
const float input_x = input_points[point_idx * input_point_step + 0];
175-
const float input_y = input_points[point_idx * input_point_step + 1];
176-
const float input_z = input_points[point_idx * input_point_step + 2];
177-
const float intensity = input_points[point_idx * input_point_step + 3];
178-
179-
output_points[point_idx * num_features] = transform_array[0] * input_x +
180-
transform_array[4] * input_y +
181-
transform_array[8] * input_z + transform_array[12];
182-
output_points[point_idx * num_features + 1] = transform_array[1] * input_x +
183-
transform_array[5] * input_y +
184-
transform_array[9] * input_z + transform_array[13];
185-
output_points[point_idx * num_features + 2] = transform_array[2] * input_x +
186-
transform_array[6] * input_y +
187-
transform_array[10] * input_z + transform_array[14];
188-
output_points[point_idx * num_features + 3] = intensity;
179+
union {
180+
uint32_t raw{0};
181+
float value;
182+
} input_x, input_y, input_z;
183+
184+
#pragma unroll
185+
for (int i = 0; i < 4; i++) { // 4 bytes for float32
186+
input_x.raw |= input_data[point_idx * input_point_step + i] << i * 8;
187+
input_y.raw |= input_data[point_idx * input_point_step + i + 4] << i * 8;
188+
input_z.raw |= input_data[point_idx * input_point_step + i + 8] << i * 8;
189+
}
190+
191+
float input_intensity = static_cast<float>(input_data[point_idx * input_point_step + 12]);
192+
193+
output_points[point_idx * num_features] =
194+
transform_array[0] * input_x.value + transform_array[4] * input_y.value +
195+
transform_array[8] * input_z.value + transform_array[12];
196+
output_points[point_idx * num_features + 1] =
197+
transform_array[1] * input_x.value + transform_array[5] * input_y.value +
198+
transform_array[9] * input_z.value + transform_array[13];
199+
output_points[point_idx * num_features + 2] =
200+
transform_array[2] * input_x.value + transform_array[6] * input_y.value +
201+
transform_array[10] * input_z.value + transform_array[14];
202+
output_points[point_idx * num_features + 3] = input_intensity;
189203
output_points[point_idx * num_features + 4] = time_lag;
190204
}
191205

192206
cudaError_t PreprocessCuda::generateSweepPoints_launch(
193-
const float * input_points, size_t points_size, int input_point_step, float time_lag,
207+
const uint8_t * input_data, size_t points_size, int input_point_step, float time_lag,
194208
const float * transform_array, float * output_points)
195209
{
196-
int threadNum = config_.threads_for_voxel_;
197-
dim3 blocks((points_size + threadNum - 1) / threadNum);
198-
dim3 threads(threadNum);
210+
dim3 blocks(divup(points_size, config_.threads_for_voxel_));
211+
dim3 threads(config_.threads_for_voxel_);
199212

200213
generateSweepPoints_kernel<<<blocks, threads, 0, stream_>>>(
201-
input_points, points_size, input_point_step, time_lag, transform_array,
214+
input_data, points_size, input_point_step, time_lag, transform_array,
202215
config_.num_point_feature_size_, output_points);
203216

204217
cudaError_t err = cudaGetLastError();

0 commit comments

Comments
 (0)