豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

ObstacleLayer marks spurious LETHAL cells due to cell-distance discretization mismatch after PR #5697 #6084

@Cheny5863

Description

@Cheny5863

Bug report

Required Info:

  • Operating System:
    • Ubuntu 24.04.3 LTS (x86_64)
  • Computer:
    • x86_64 desktop
  • ROS2 Version:
    • Jazzy (from source)
  • Version or commit hash:
  • DDS implementation:
    • rmw_fastrtps_cpp 8.4.3

Steps to reproduce issue

  1. Configure a depth camera sensor converted to LaserScan with inf_is_valid: true (or any sensor where inf readings are clamped to range_max - epsilon).
  2. Set obstacle_max_range smaller than the sensor's range_max. For example: obstacle_max_range: 0.70, sensor range_max: 0.80.
  3. Use a typical costmap resolution: 0.05.
  4. Run the robot in an empty environment (no real obstacles). Rotate the robot in place.

Expected behavior

The local costmap should remain clear — no LETHAL_OBSTACLE cells should appear because all sensor readings are inf (no real obstacles), and the projected points at range_max - epsilon ≈ 0.7999 m exceed obstacle_max_range = 0.70 m.

Actual behavior

Spurious LETHAL_OBSTACLE (254) cells appear on the local costmap, especially during rotation. The noise pattern follows the sensor's arc at the boundary of its projected range.

Root cause

PR #5697 (commit 80b72d27, backported to Jazzy as 3320ebf7) replaced the original world-distance obstacle range check with a cell-space distance check. The original code was:

double sq_obstacle_max_range = obs.obstacle_max_range_ * obs.obstacle_max_range_;
// ...
double sq_dist = (px - obs.origin_.x) * (px - obs.origin_.x)
               + (py - obs.origin_.y) * (py - obs.origin_.y)
               + (pz - obs.origin_.z) * (pz - obs.origin_.z);
if (sq_dist >= sq_obstacle_max_range) {
    continue;
}

It was changed to:

const unsigned int max_range_cells = cellDistance(obs.obstacle_max_range_);
// ...
const int dx = static_cast<int>(mx) - static_cast<int>(x0);
const int dy = static_cast<int>(my) - static_cast<int>(y0);
const unsigned int dist = static_cast<unsigned int>(
    std::hypot(static_cast<double>(dx), static_cast<double>(dy)));
if (dist > max_range_cells) {
    continue;
}

The cell-space check alone does not guarantee consistency with the meter-based obstacle_max_range parameter due to discretization boundary effects:

Quantity Value Note
World distance (sensor origin → hit, 2D) 0.79990 m Well above obstacle_max_range = 0.70 m
dx, dy (grid offsets) 10, −11 Diagonal direction on the grid
cell_dist 14 hypot(10, 11) = √221 ≈ 14.866 → truncated to unsigned int14
max_range_cells 14 cellDistance(0.70) = ceil(0.70 / 0.05) = ceil(14.0) = 14

The filter evaluates 14 > 14FALSE → the point passes and is marked LETHAL_OBSTACLE, even though the actual world distance (0.7999 m) clearly exceeds obstacle_max_range (0.70 m).

The truncation from double to unsigned int in hypot loses the fractional part (14.866 → 14), while cellDistance uses ceil which rounds up, creating a systematic boundary mismatch.

Proposed fix

Add a world-distance pre-filter before the cell-distance checks, restoring the meter-consistent semantics that existed prior to PR #5697 while keeping the cell-space checks for alignment with raytrace/clearing behavior:

// Pre-filter by world distance to avoid cell discretization boundary
// effects where hypot(dx,dy) truncation makes far points appear in range
const double wdx = px - obs.origin_.x;
const double wdy = py - obs.origin_.y;
const double world_dist_sq = wdx * wdx + wdy * wdy;
if (world_dist_sq > obs.obstacle_max_range_ * obs.obstacle_max_range_) {
    continue;
}
if (world_dist_sq < obs.obstacle_min_range_ * obs.obstacle_min_range_) {
    continue;
}

Additional information

  • ROS1's costmap_2d ObstacleLayer uses a continuous squared-distance check (sq_dist >= sq_obstacle_range) and does not have this issue.
  • The bug is most easily triggered when obstacle_max_range is small relative to sensor range_max, with inf_is_valid: true (so inf readings are clamped to range_max - epsilon and projected into the point cloud).
  • Introduced by: PR Update obstacle layer usage of max ranges #5697 / commit 80b72d27 (main), backported as 3320ebf7 (Jazzy) and 0412cea6 (Humble).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions