|
41 | 41 | #include <hydra/common/global_info.h> |
42 | 42 |
|
43 | 43 | #include <cmath> |
| 44 | +#include <queue> |
44 | 45 | #include <set> |
45 | 46 |
|
46 | 47 | namespace hydra { |
@@ -80,6 +81,7 @@ void declare_config(TsdfGradientOccupancyPublisher::Config& config) { |
80 | 81 | field(config.min_confidence, "min_confidence"); |
81 | 82 | field(config.smoothing, "smoothing"); |
82 | 83 | field(config.probabilistic, "probabilistic"); |
| 84 | + field(config.filter_disjoint, "filter_disjoint"); |
83 | 85 |
|
84 | 86 | checkCondition(config.gradient_threshold > 0.0f, |
85 | 87 | "gradient_threshold must be positive"); |
@@ -141,6 +143,9 @@ void TsdfGradientOccupancyPublisher::call(uint64_t timestamp_ns, |
141 | 143 | msg.info.map_load_time = msg.header.stamp; |
142 | 144 |
|
143 | 145 | fillOccupancyGrid(gradient_map, world_T_body, tsdf_layer, msg); |
| 146 | + if (config.filter_disjoint) { |
| 147 | + filterDisjointFreeSpace(msg, world_T_body); |
| 148 | + } |
144 | 149 | pub_->publish(msg); |
145 | 150 | } |
146 | 151 |
|
@@ -354,6 +359,85 @@ void TsdfGradientOccupancyPublisher::fillOccupancyGrid( |
354 | 359 | } |
355 | 360 | } |
356 | 361 |
|
| 362 | +void TsdfGradientOccupancyPublisher::filterDisjointFreeSpace( |
| 363 | + nav_msgs::msg::OccupancyGrid& msg, |
| 364 | + const Eigen::Isometry3d& world_T_body) const { |
| 365 | + if (msg.data.empty()) { |
| 366 | + return; |
| 367 | + } |
| 368 | + |
| 369 | + const int width = static_cast<int>(msg.info.width); |
| 370 | + const int height = static_cast<int>(msg.info.height); |
| 371 | + |
| 372 | + // Convert robot world position to grid coordinates |
| 373 | + const auto robot_pos_world = world_T_body.translation(); |
| 374 | + const float rel_x = |
| 375 | + static_cast<float>(robot_pos_world.x() - msg.info.origin.position.x); |
| 376 | + const float rel_y = |
| 377 | + static_cast<float>(robot_pos_world.y() - msg.info.origin.position.y); |
| 378 | + const int robot_r = static_cast<int>(std::floor(rel_y / msg.info.resolution)); |
| 379 | + const int robot_c = static_cast<int>(std::floor(rel_x / msg.info.resolution)); |
| 380 | + |
| 381 | + // Check if robot is within grid bounds |
| 382 | + if (robot_r < 0 || robot_r >= height || robot_c < 0 || robot_c >= width) { |
| 383 | + return; // Robot outside grid - skip filtering |
| 384 | + } |
| 385 | + |
| 386 | + const size_t robot_index = robot_r * width + robot_c; |
| 387 | + |
| 388 | + // Only filter if robot is in free space |
| 389 | + if (msg.data[robot_index] != 0) { |
| 390 | + return; // Robot not in free space - skip filtering |
| 391 | + } |
| 392 | + |
| 393 | + // BFS flood fill from robot position |
| 394 | + std::vector<bool> visited(msg.data.size(), false); |
| 395 | + std::queue<size_t> to_visit; |
| 396 | + |
| 397 | + to_visit.push(robot_index); |
| 398 | + visited[robot_index] = true; |
| 399 | + |
| 400 | + while (!to_visit.empty()) { |
| 401 | + const size_t current_index = to_visit.front(); |
| 402 | + to_visit.pop(); |
| 403 | + |
| 404 | + const int r = current_index / width; |
| 405 | + const int c = current_index % width; |
| 406 | + |
| 407 | + // Check 4-connected neighbors (up, down, left, right) |
| 408 | + const std::array<std::pair<int, int>, 4> neighbors = {{ |
| 409 | + {r - 1, c}, {r + 1, c}, {r, c - 1}, {r, c + 1} |
| 410 | + }}; |
| 411 | + |
| 412 | + for (const auto& [nr, nc] : neighbors) { |
| 413 | + // Check bounds |
| 414 | + if (nr < 0 || nr >= height || nc < 0 || nc >= width) { |
| 415 | + continue; |
| 416 | + } |
| 417 | + |
| 418 | + const size_t neighbor_index = nr * width + nc; |
| 419 | + |
| 420 | + // Skip if already visited |
| 421 | + if (visited[neighbor_index]) { |
| 422 | + continue; |
| 423 | + } |
| 424 | + |
| 425 | + // Only expand through free cells (value 0) |
| 426 | + if (msg.data[neighbor_index] == 0) { |
| 427 | + visited[neighbor_index] = true; |
| 428 | + to_visit.push(neighbor_index); |
| 429 | + } |
| 430 | + } |
| 431 | + } |
| 432 | + |
| 433 | + // Mark unvisited free cells as occupied |
| 434 | + for (size_t i = 0; i < msg.data.size(); ++i) { |
| 435 | + if (msg.data[i] == 0 && !visited[i]) { |
| 436 | + msg.data[i] = 100; // Mark as occupied |
| 437 | + } |
| 438 | + } |
| 439 | +} |
| 440 | + |
357 | 441 | float TsdfGradientOccupancyPublisher::computeHorizontalDistance( |
358 | 442 | const Index2D& offset, float voxel_size) const { |
359 | 443 | // Diagonal: sqrt(2) * voxel_size |
|
0 commit comments