Place Cameras for Maximum Coverage

SkillDev tools

Greedy CameraPlacementManager that fills a navmesh or XY scope until a patch-coverage ratio is met. Use for facility surveillance layouts that minimize camera count.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Place Cameras for Maximum Coverage skill

What this skill tells your AI

The instructions your AI receives, as published by isaac-sim/isaacsim in skills/place-camera-max-coverage/SKILL.md and read by ahel’s review.

Purpose

Lay out infrastructure cameras across a floor area so a target fraction of it is observed, using the CameraPlacementManager API of isaacsim.sensors.rtx.placement.

Prerequisites

  • Running Isaac Sim 6 / Kit 110 with python_server (port 8226); see isaac-sim-remote.
  • Extension isaacsim.sensors.rtx.placement enabled (action_and_event_data_generation app, or --enable).
  • Stage has the facility/environment loaded plus a baked navmesh or explicit scope=xmin,xmax,ymin,ymax.
  • Shell env: $ISAAC_SIM_DIR, $ISAAC_LAB_DIR, $WORKSPACE_DIR (isaac-sim-orchestrator).

Limitations

  • Does not build isaacsim.sensors.rtx.placement — consume the pin from source/apps/isaacsim.exp.action_and_event_data_generation.base.kit.
  • Live Kit only; coverage uses a single focus plane at focus_height (no multi-level model).
  • Greedy solver stops at ratio / budget / min_coverage_increase — not a proven minimum set.
  • Cost scales with area / patch_size² (tens of seconds on large stages).
  • For single-object unoccluded views (not floor coverage), use place-camera-aim-at.

Troubleshooting

Error / symptomCauseSolution
MAX_COVERAGE_PREFLIGHT: ext=unavailableApp does not resolve the extensionLaunch isaacsim.exp.action_and_event_data_generation.base.sh, or add --enable isaacsim.sensors.rtx.placement
scope=none in preflightNo navmesh baked and no explicit scopeBake a navmesh, or pass scope=xmin,xmax,ymin,ymax
Script raises "No cameras were authored"Solver produced nothingCheck the console; usually a scope that misses the walkable floor, or camera_on_navmesh with no navmesh
"input section distance … is too large" warningpatch_size exceeds shorter_span / 10Harmless — the value is auto-shrunk; pass a smaller patch_size to control it
Far fewer cameras than expectedCoverage target met early, or min_coverage_increase stopped the loopRaise target_coverage_ratio, or raise required_camera_per_patch for redundancy
Placement never finishes on a large stagepatch_size too small for the areaRaise patch_size; cost scales with area / patch_size²
camera_info_payload.json not writtenNo output folder configuredPass output_dir, which sets camera_placement_output_folder_path

When to use this skill

You wantSkill
Cover this whole floor area to a coverage ratiothis skill
N unobstructed views of this pallet / shelf / robotplace-camera-aim-at
Author a single camera's intrinsics, AOVs, distortionisaac-camera

How the algorithm works

  1. Resolve the target scope — either explicit [(x_min, x_max), (y_min, y_max)] bounds or the navmesh extents.
  2. Split the scope into patch_size cells on a focus plane at focus_height, and mark which cells are accessible.
  3. For each of four cardinal directions, estimate the ground-plane footprint of a candidate camera's frustum and greedily pick the pose that adds the most uncovered patches.
  4. Stop when target_coverage_ratio is reached, the num_cameras budget is spent, or a new camera would add less than min_coverage_increase patches.

Full parameter semantics and the carb-settings coupling live in references/coverage-api.md; scope resolution, coverage visualisation, and the JSON payload are in references/coverage-runtime-notes.md.

Available Scripts

ScriptPurposeArguments
scripts/place_camera_max_coverage.pyCover a floor area with cameras using isaacsim.sensors.rtx.placementinjected key=value args (see script docstring)

Running scripts

The script is a python_server payload, not a standalone program. From agent runtimes that expose skill execution helpers, invoke it with run_script():

run_script("scripts/place_camera_max_coverage.py", args=["action=preflight"])

From a built tree, send it through the isaac-sim-remote client:

python skills/isaac-sim-remote/scripts/isaacsim_send.py \
  --file skills/place-camera-max-coverage/scripts/place_camera_max_coverage.py \
  --arg action=place --arg num_cameras=5 --arg output_dir=/tmp/isp

Workflow

  1. Preflight. action=preflight (the default) reports extension, stage, and scope state without touching the stage. It never raises, so it is safe first.

    run_script("scripts/place_camera_max_coverage.py", args=["action=preflight"])
    

    Expect MAX_COVERAGE_PREFLIGHT: ext=enabled stage=ok scope=navmesh plus the resolved bounds. Resolve anything that is not ok before continuing.

  2. Confirm the scope. action=scope prints the navmesh-derived bounds without placing anything. Compare them against the facility you expect — a navmesh that leaked outside the building silently wastes cameras.

    run_script("scripts/place_camera_max_coverage.py", args=["action=scope"])
    
  3. Place. Either give a camera budget or let coverage drive the count.

    run_script(
        "scripts/place_camera_max_coverage.py",
        args=["action=place", "num_cameras=5", "height_range=5,7.5", "output_dir=/tmp/isp"],
    )
    

    num_cameras=-1 (the default) places until target_coverage_ratio is met. Expect MAX_COVERAGE_RESULT: placed=5 total=5 scope=... ratio=0.9 ... plus one line per new camera. The script counts prims on the stage rather than trusting the API return value.

  4. Verify coverage. action=coverage debug-draws the coverage frequency of the cameras currently under the parent prim and logs the achieved coverage and full-coverage ratios to the console.

    run_script("scripts/place_camera_max_coverage.py", args=["action=coverage"])
    
  5. Iterate. Vary random_seed for a different layout under the same constraints; raise required_camera_per_patch when downstream perception needs overlapping views.

Calling the API directly

from isaacsim.sensors.rtx.placement import CameraPlacementManager

manager = CameraPlacementManager.get_instance()
cameras = manager.place_camera_in_target_scope_explicit(
    target_scope=[(-9.5, 9.5), (-19.0, 0.0)],
    camera_num=5,
    target_coverage_ratio=0.9,
    camera_height_range=(5.0, 7.5),
    camera_distance_range=(8.0, 16.0),
    camera_look_down_angle_range=(20.0, 60.0),
    spawn_camera=True,
    output_camera_data=True,
)
for camera in cameras or []:
    print(camera["translate"], camera["rotation"])

Two traps worth stating up front:

  • output_camera_data defaults to False, and the call returns None in that case even on success. Pass True when you want the poses back.
  • Exceptions are swallowed into carb.log_error and the call then returns a partially filled list. Verify against the stage, not the return value — which is what scripts/place_camera_max_coverage.py does.

Use place_camera_in_target_scope_explicit, not place_camera_in_target_scope. The latter is the GUI path and reads every parameter from persistent carb settings.

Related skills

  • isaac-sim-remote — the transport this skill's script rides on.
  • place-camera-aim-at — the single-target sibling.
  • occupancy-map — an alternative source of floor bounds when no navmesh exists.
  • isaac-camera — per-camera intrinsics, render products, AOVs, distortion.
  • isaac-sim-sensor — attaching annotators and writers once cameras exist.

Signals

GitHub stars
4k
Forks
539
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
place-camera-max-coverage
Source
github.com/isaac-sim/isaacsim