Skip to content

Commit 94450e2

Browse files
committed
add approximate extract common pointcloud function
Signed-off-by: Yukihito Saito <yukky.saito@gmail.com>
1 parent b404c8f commit 94450e2

File tree

3 files changed

+145
-2
lines changed

3 files changed

+145
-2
lines changed

perception/probabilistic_occupancy_grid_map/include/probabilistic_occupancy_grid_map/utils/utils.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ bool extractCommonPointCloud(
116116
const sensor_msgs::msg::PointCloud2 & obstacle_pc, const sensor_msgs::msg::PointCloud2 & raw_pc,
117117
sensor_msgs::msg::PointCloud2 & output_obstacle_pc);
118118

119+
bool extractApproximateCommonPointCloud(
120+
const sensor_msgs::msg::PointCloud2 & obstacle_pc, const sensor_msgs::msg::PointCloud2 & raw_pc,
121+
const float voxel_size, sensor_msgs::msg::PointCloud2 & output_obstacle_pc);
122+
119123
unsigned char getApproximateOccupancyState(const unsigned char & value);
120124
} // namespace utils
121125

perception/probabilistic_occupancy_grid_map/src/pointcloud_based_occupancy_grid_map/pointcloud_based_occupancy_grid_map_node.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ void PointcloudBasedOccupancyGridMapNode::onPointcloudWithObstacleAndRaw(
157157
// Filter obstacle pointcloud by raw pointcloud
158158
PointCloud2 filtered_obstacle_pc_common{};
159159
if (filter_obstacle_pointcloud_by_raw_pointcloud_) {
160-
if (!utils::extractCommonPointCloud(
161-
filtered_obstacle_pc, filtered_raw_pc, filtered_obstacle_pc_common)) {
160+
if (!utils::extractApproximateCommonPointCloud(
161+
filtered_obstacle_pc, filtered_raw_pc, 0.1f, filtered_obstacle_pc_common)) {
162162
filtered_obstacle_pc_common = filtered_obstacle_pc;
163163
}
164164
} else {

perception/probabilistic_occupancy_grid_map/src/utils/utils.cpp

+139
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,35 @@
1818

1919
#include <string>
2020

21+
namespace
22+
{
23+
/**
24+
* @brief Hash function for voxel keys.
25+
* Utilizes prime numbers to calculate a unique hash for each voxel key.
26+
*/
27+
struct VoxelKeyHash
28+
{
29+
std::size_t operator()(const std::array<int, 3> & k) const
30+
{
31+
// Primes based on the following paper: 'Investigating the Use of Primes in Hashing for
32+
// Volumetric Data'.
33+
return (k[0] * 73856093 ^ k[1] * 19349663 ^ k[2] * 83492791);
34+
}
35+
};
36+
37+
/**
38+
* @brief Equality function for voxel keys.
39+
* Checks if two voxel keys are equal.
40+
*/
41+
struct VoxelKeyEqual
42+
{
43+
bool operator()(const std::array<int, 3> & a, const std::array<int, 3> & b) const
44+
{
45+
return a == b;
46+
}
47+
};
48+
} // namespace
49+
2150
namespace utils
2251
{
2352

@@ -182,6 +211,116 @@ bool extractCommonPointCloud(
182211
return true;
183212
}
184213

214+
/**
215+
* @brief extract Common Pointcloud between obstacle pc and raw pc
216+
* @param obstacle_pc
217+
* @param raw_pc
218+
* @param voxel_size
219+
* @param output_obstacle_pc
220+
*/
221+
bool extractApproximateCommonPointCloud(
222+
const sensor_msgs::msg::PointCloud2 & obstacle_pc, const sensor_msgs::msg::PointCloud2 & raw_pc,
223+
const float voxel_size, sensor_msgs::msg::PointCloud2 & output_obstacle_pc)
224+
{
225+
using VoxelKey = std::array<int, 3>;
226+
std::unordered_map<VoxelKey, size_t, VoxelKeyHash, VoxelKeyEqual> obstacle_voxel_map;
227+
std::unordered_map<VoxelKey, size_t, VoxelKeyHash, VoxelKeyEqual> raw_voxel_map;
228+
229+
constexpr float large_num_offset = 100000.0;
230+
const float & voxel_size_x = voxel_size;
231+
const float & voxel_size_y = voxel_size;
232+
const float & voxel_size_z = voxel_size;
233+
const float inverse_voxel_size_x = 1.0 / voxel_size_x;
234+
const float inverse_voxel_size_y = 1.0 / voxel_size_y;
235+
const float inverse_voxel_size_z = 1.0 / voxel_size_z;
236+
237+
{
238+
const int x_offset = raw_pc.fields[pcl::getFieldIndex(raw_pc, "x")].offset;
239+
const int y_offset = raw_pc.fields[pcl::getFieldIndex(raw_pc, "y")].offset;
240+
const int z_offset = raw_pc.fields[pcl::getFieldIndex(raw_pc, "z")].offset;
241+
242+
// Process each point in the point cloud
243+
for (size_t global_offset = 0; global_offset + raw_pc.point_step <= raw_pc.data.size();
244+
global_offset += raw_pc.point_step) {
245+
const float & x = *reinterpret_cast<const float *>(&raw_pc.data[global_offset + x_offset]);
246+
const float & y = *reinterpret_cast<const float *>(&raw_pc.data[global_offset + y_offset]);
247+
const float & z = *reinterpret_cast<const float *>(&raw_pc.data[global_offset + z_offset]);
248+
249+
// The reason for adding a large value is that when converting from float to int, values
250+
// around -1 to 1 are all rounded down to 0. Therefore, to prevent the numbers from becoming
251+
// negative, a large value is added. It has been tuned to reduce computational costs, and
252+
// deliberately avoids using round or floor functions.
253+
VoxelKey key = {
254+
static_cast<int>((x + large_num_offset) * inverse_voxel_size_x),
255+
static_cast<int>((y + large_num_offset) * inverse_voxel_size_y),
256+
static_cast<int>((z + large_num_offset) * inverse_voxel_size_z)};
257+
258+
if (raw_voxel_map.find(key) == raw_voxel_map.end()) {
259+
raw_voxel_map[key] = global_offset;
260+
}
261+
}
262+
}
263+
{
264+
const int x_offset = obstacle_pc.fields[pcl::getFieldIndex(obstacle_pc, "x")].offset;
265+
const int y_offset = obstacle_pc.fields[pcl::getFieldIndex(obstacle_pc, "y")].offset;
266+
const int z_offset = obstacle_pc.fields[pcl::getFieldIndex(obstacle_pc, "z")].offset;
267+
268+
for (size_t global_offset = 0;
269+
global_offset + obstacle_pc.point_step <= obstacle_pc.data.size();
270+
global_offset += obstacle_pc.point_step) {
271+
const float & x =
272+
*reinterpret_cast<const float *>(&obstacle_pc.data[global_offset + x_offset]);
273+
const float & y =
274+
*reinterpret_cast<const float *>(&obstacle_pc.data[global_offset + y_offset]);
275+
const float & z =
276+
*reinterpret_cast<const float *>(&obstacle_pc.data[global_offset + z_offset]);
277+
278+
// The reason for adding a large value is that when converting from float to int, values
279+
// around -1 to 1 are all rounded down to 0. Therefore, to prevent the numbers from becoming
280+
// negative, a large value is added. It has been tuned to reduce computational costs, and
281+
// deliberately avoids using round or floor functions.
282+
VoxelKey key = {
283+
static_cast<int>((x + large_num_offset) * inverse_voxel_size_x),
284+
static_cast<int>((y + large_num_offset) * inverse_voxel_size_y),
285+
static_cast<int>((z + large_num_offset) * inverse_voxel_size_z)};
286+
287+
if (raw_voxel_map.find(key) == raw_voxel_map.end()) {
288+
obstacle_voxel_map[key] = global_offset;
289+
}
290+
}
291+
292+
// Populate the output point cloud
293+
size_t output_global_offset = 0;
294+
output_obstacle_pc.data.resize(obstacle_voxel_map.size() * obstacle_pc.point_step);
295+
for (const auto & kv : obstacle_voxel_map) {
296+
std::memcpy(
297+
&output_obstacle_pc.data[output_global_offset + x_offset],
298+
&obstacle_pc.data[kv.second + x_offset], sizeof(float));
299+
std::memcpy(
300+
&output_obstacle_pc.data[output_global_offset + y_offset],
301+
&obstacle_pc.data[kv.second + y_offset], sizeof(float));
302+
std::memcpy(
303+
&output_obstacle_pc.data[output_global_offset + z_offset],
304+
&obstacle_pc.data[kv.second + z_offset], sizeof(float));
305+
output_global_offset += obstacle_pc.point_step;
306+
}
307+
308+
// Set the output point cloud metadata
309+
output_obstacle_pc.header.frame_id = obstacle_pc.header.frame_id;
310+
output_obstacle_pc.height = 1;
311+
output_obstacle_pc.fields = obstacle_pc.fields;
312+
output_obstacle_pc.is_bigendian = obstacle_pc.is_bigendian;
313+
output_obstacle_pc.point_step = obstacle_pc.point_step;
314+
output_obstacle_pc.is_dense = obstacle_pc.is_dense;
315+
output_obstacle_pc.width = static_cast<uint32_t>(
316+
output_obstacle_pc.data.size() / output_obstacle_pc.height / output_obstacle_pc.point_step);
317+
output_obstacle_pc.row_step =
318+
static_cast<uint32_t>(output_obstacle_pc.data.size() / output_obstacle_pc.height);
319+
}
320+
321+
return true;
322+
}
323+
185324
/**
186325
* @brief Convert unsigned char value to closest cost value
187326
* @param cost Cost value

0 commit comments

Comments
 (0)