|
18 | 18 |
|
19 | 19 | #include <string>
|
20 | 20 |
|
| 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 | + |
21 | 50 | namespace utils
|
22 | 51 | {
|
23 | 52 |
|
@@ -182,6 +211,116 @@ bool extractCommonPointCloud(
|
182 | 211 | return true;
|
183 | 212 | }
|
184 | 213 |
|
| 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 | + |
185 | 324 | /**
|
186 | 325 | * @brief Convert unsigned char value to closest cost value
|
187 | 326 | * @param cost Cost value
|
|
0 commit comments