Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Source: `~/nav2_ws/src/navigation2/nav2_costmap_2d/`
.claude/skills/harunkurtdev-nav2-costmap2d/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 79% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 185% | 0% |
Source: ~/nav2_ws/src/navigation2/nav2_costmap_2d/
Costmap2DROS (LifecycleNode)
└── LayeredCostmap
├── Plugin Layers (combined into master costmap)
│ ├── StaticLayer
│ ├── ObstacleLayer / VoxelLayer
│ ├── InflationLayer
│ ├── RangeSensorLayer
│ └── DenoiseLayer
└── Filter Layers (post-processing)
├── KeepoutFilter
├── SpeedFilter
└── BinaryFilterupdateBounds() on each layer → defines region to updateupdateCosts() on each layer → applies changes to master gridOverwrite, Max, MaxWithoutUnknownOverwritecpp// nav2_costmap_2d/cost_values.hpp FREE_SPACE = 0 // 1-252: Cost gradient (higher = more expensive) MAX_NON_OBSTACLE = 252 INSCRIBED_INFLATED_OBSTACLE = 253 LETHAL_OBSTACLE = 254 NO_INFORMATION = 255 // Unknown
Main 2D grid. Key methods:
getCost(mx, my) / setCost(mx, my, cost) - Cell accessworldToMap(wx, wy, mx, my) / mapToWorld(mx, my, wx, wy) - Coordinate conversiongetSizeInCellsX(), getSizeInCellsY(), getResolution()setConvexPolygonCost(polygon, cost) - Fill polygon with costupdateOrigin(new_ox, new_oy) - For rolling windowLifecycle node managing layers, footprint, TF. Key features:
cppclass Layer { virtual void onInitialize() = 0; virtual void updateBounds(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y) = 0; virtual void updateCosts(master_grid, min_x, min_y, max_x, max_y) = 0; virtual void reset() = 0; virtual bool isClearable() = 0; virtual void activate() = 0; virtual void deactivate() = 0; };
cpptemplate<typename CostmapT> class FootprintCollisionChecker { double footprintCost(const Footprint & footprint); double footprintCostAtPose(x, y, theta, footprint); double lineCost(x0, x1, y0, y1); double pointCost(x, y); };
Plugin: nav2_costmap_2d::StaticLayer Purpose: Load occupancy grid from map server (SLAM output) Clearable: No
yamlstatic_layer: plugin: "nav2_costmap_2d::StaticLayer" map_subscribe_transient_local: true map_topic: "/map" subscribe_to_updates: false track_unknown_space: true use_maximum: false lethal_threshold: 100 unknown_cost_value: -1 trinary_costmap: true
Plugin: nav2_costmap_2d::ObstacleLayer Purpose: Process LaserScan and PointCloud2 for dynamic obstacles Clearable: Yes
Maintains observation buffers for:
yamlobstacle_layer: plugin: "nav2_costmap_2d::ObstacleLayer" enabled: true observation_sources: scan pointcloud scan: topic: /scan sensor_frame: "" observation_persistence: 0.0 expected_update_rate: 0.0 data_type: LaserScan min_obstacle_height: 0.0 max_obstacle_height: 2.0 inf_is_valid: false marking: true clearing: true obstacle_max_range: 2.5 obstacle_min_range: 0.0 raytrace_max_range: 3.0 raytrace_min_range: 0.0 pointcloud: topic: /points data_type: PointCloud2 # Same params as scan...
Plugin: nav2_costmap_2d::VoxelLayer Purpose: 3D obstacle representation (extends ObstacleLayer) Clearable: Yes
Uses nav2_voxel_grid::VoxelGrid for 3D tracking. Projects to 2D costmap.
yamlvoxel_layer: plugin: "nav2_costmap_2d::VoxelLayer" enabled: true # All ObstacleLayer params plus: z_voxels: 16 # Vertical resolution z_resolution: 0.05 # Voxel height (meters) origin_z: 0.0 mark_threshold: 0 # Min voxels to mark unknown_threshold: 15 # Voxels for unknown publish_voxel_map: true
Plugin: nav2_costmap_2d::InflationLayer Purpose: Expand obstacle costs with exponential decay Clearable: No
Cost function: exponential falloff from lethal obstacles.
yamlinflation_layer: plugin: "nav2_costmap_2d::InflationLayer" cost_scaling_factor: 3.0 # Exponential decay rate inflation_radius: 0.55 # Max inflation distance (meters) inflate_unknown: false inflate_around_unknown: false
Cost formula:
cost = INSCRIBED (253) if distance <= inscribed_radius
cost = exp(-cost_scaling_factor * (distance - inscribed_radius)) * (252 - 1) + 1Higher cost_scaling_factor = steeper decay (costs drop faster from obstacles).
Plugin: nav2_costmap_2d::RangeSensorLayer Purpose: IR/Sonar/range sensor integration Clearable: Yes
yamlrange_layer: plugin: "nav2_costmap_2d::RangeSensorLayer" topics: ["/sonar0", "/sonar1"] input_sensor_type: ALL # or VARIABLE, FIXED
Plugin: nav2_costmap_2d::DenoiseLayer Purpose: Filter noise (standalone obstacle pixels or small groups) Clearable: No
yamldenoise_layer: plugin: "nav2_costmap_2d::DenoiseLayer" enabled: true minimal_group_size: 2
Applied as post-processing on combined costmap.
Enforces no-go zones from filter mask.
yamlkeepout_filter: plugin: "nav2_costmap_2d::KeepoutFilter" enabled: true filter_info_topic: "/costmap_filter_info"
Speed limitations by area from filter mask.
yamlspeed_filter: plugin: "nav2_costmap_2d::SpeedFilter" enabled: true filter_info_topic: "/costmap_filter_info" speed_limit_topic: "/speed_limit"
CostmapFilterInfo message types:
type: 0 → Keepout zonetype: 1 → Speed limit (percentage)type: 2 → Speed limit (m/s)yamllocal_costmap: local_costmap: ros__parameters: update_frequency: 5.0 publish_frequency: 2.0 global_frame: odom robot_base_frame: base_link rolling_window: true width: 3 height: 3 resolution: 0.05 footprint: "[[0.15,0.15],[0.15,-0.15],[-0.15,-0.15],[-0.15,0.15]]" # OR: robot_radius: 0.22 (for circular robots) plugins: ["voxel_layer", "inflation_layer"]
yamlglobal_costmap: global_costmap: ros__parameters: update_frequency: 1.0 publish_frequency: 1.0 global_frame: map robot_base_frame: base_link robot_radius: 0.22 resolution: 0.05 track_unknown_space: true plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
Buffers sensor data with TF transforms. Stores point clouds with origin for marking/clearing.
nav2_util)Bresenham line algorithm for raytracing (clearing free space).
Collision detection using oriented robot footprint against costmap.
nav2_smac_planner)Multi-resolution costmap support for planners.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 13,155 | 8,937 | -32% | 1 | 1 | 0% | 2,637 | 4,161 | +58% | 0 | 0 | — |
case-02 | fail→fail | 11,081 | 7,482 | -32% | 1 | 1 | 0% | 2,127 | 3,753 | +76% | 0 | 0 | — |
case-03 | pass→pass | 12,494 | 6,685 | -46% | 1 | 1 | 0% | 2,610 | 3,643 | +40% | 0 | 0 | — |
case-04 | fail→pass | 12,171 | 5,003 | -59% | 1 | 1 | 0% | 2,021 | 3,196 | +58% | 0 | 0 | — |
case-05 | pass→pass | 8,046 | 3,814 | -53% | 1 | 1 | 0% | 1,617 | 3,073 | +90% | 0 | 0 | — |
case-06 | pass→pass | 8,227 | 6,492 | -21% | 1 | 1 | 0% | 1,679 | 3,575 | +113% | 0 | 0 | — |
case-07 | pass→pass | 12,162 | 6,177 | -49% | 1 | 1 | 0% | 2,138 | 3,575 | +67% | 0 | 0 | — |
case-08 | fail→pass | 9,901 | 6,095 | -38% | 1 | 1 | 0% | 1,994 | 3,567 | +79% | 0 | 0 | — |
case-09 | pass→pass | 9,249 | 4,477 | -52% | 1 | 1 | 0% | 1,786 | 3,173 | +78% | 0 | 0 | — |
case-10 | pass→pass | 8,191 | 4,495 | -45% | 1 | 1 | 0% | 1,513 | 3,194 | +111% | 0 | 0 | — |
case-11 | fail→pass | 13,356 | 4,426 | -67% | 1 | 1 | 0% | 2,393 | 3,026 | +26% | 0 | 0 | — |
case-12 | pass→pass | 13,617 | 8,917 | -35% | 1 | 1 | 0% | 2,804 | 4,152 | +48% | 0 | 0 | — |
case-13 | pass→pass | 10,598 | 6,408 | -40% | 1 | 1 | 0% | 2,156 | 3,505 | +63% | 0 | 0 | — |
case-14 | pass→pass | 11,063 | 6,606 | -40% | 1 | 1 | 0% | 2,140 | 3,692 | +73% | 0 | 0 | — |
case-15 | pass→pass | 12,363 | 8,571 | -31% | 1 | 1 | 0% | 2,237 | 4,053 | +81% | 0 | 0 | — |
case-16 | pass→pass | 4,499 | 1,700 | -62% | 1 | 1 | 0% | 772 | 2,604 | +237% | 0 | 0 | — |
case-17 | fail→pass | 5,100 | 1,991 | -61% | 1 | 1 | 0% | 942 | 2,686 | +185% | 0 | 0 | — |
case-23 | pass→pass | 15,104 | 12,781 | -15% | 1 | 1 | 0% | 2,724 | 4,448 | +63% | 0 | 0 | — |
case-18 | fail→fail | 10,986 | 7,366 | -33% | 1 | 1 | 0% | 1,928 | 3,652 | +89% | 0 | 0 | — |
case-19 | pass→pass | 6,848 | 2,474 | -64% | 1 | 1 | 0% | 1,228 | 2,803 | +128% | 0 | 0 | — |
case-20 | fail→pass | 12,872 | 12,431 | -3% | 1 | 1 | 0% | 2,322 | 4,740 | +104% | 0 | 0 | — |
case-21 | pass→pass | 10,696 | 10,741 | +0% | 1 | 1 | 0% | 2,396 | 4,704 | +96% | 0 | 0 | — |
case-22 | pass→pass | 16,191 | 15,308 | -5% | 1 | 1 | 0% | 3,093 | 5,415 | +75% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +26 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.