calibrate-metropolis-camera
GitHub为已放置的相机生成Metropolis格式的calibration.json,包含内参、外参及视场多边形。需先运行place-camera-max-coverage,依赖Isaac Sim环境。
Trigger Scenarios
Install
npx skills add isaac-sim/IsaacSim --skill calibrate-metropolis-camera -g -y
SKILL.md
Frontmatter
{
"name": "calibrate-metropolis-camera",
"license": "Apache-2.0",
"metadata": {
"author": "NVIDIA Isaac Sim <isaac-sim@nvidia.com>"
},
"description": "Extract camera intrinsics, extrinsics, and FOV polygons to calibration.json. Use after cameras are placed; to place them first use place-camera-max-coverage."
}
Calibrate Placed Cameras
Purpose
Produce a Metropolis-format calibration.json — per-camera intrinsics, extrinsics, homography, and field-of-view polygons — plus a top-view reference image, using CameraCalibrationManager from isaacsim.sensors.rtx.calibration.
Prerequisites
- Isaac Sim 6 / Kit 110 running with the Python server (
isaacsim.code_editor.python_server, port 8226) — seeisaac-sim-remote. isaacsim.sensors.rtx.calibrationresolvable. It is pinned byisaacsim.exp.action_and_event_data_generation.base.kit; launch that app, or--enable isaacsim.sensors.rtx.calibration.- Cameras already placed under the camera parent prim (default
/World/Cameras) — useplace-camera-max-coverageorplace-camera-aim-atfirst. - A local output folder. Nucleus / cloud URIs are rejected.
- Shell env contract from
isaac-sim-orchestrator:$ISAAC_SIM_DIR,$ISAAC_LAB_DIR,$WORKSPACE_DIR.
Limitations
- This repo does not build the extension.
isaacsim.sensors.rtx.calibrationis authored in themetrosimrepo and consumed here as an exact registry pin declared insource/apps/isaacsim.exp.action_and_event_data_generation.base.kit(read the version there rather than from this skill). This skill drives it; API changes belong upstream. - Needs a live Kit session — the top view is a real orthographic render, so there is no offline path.
- Every camera must fall inside the top-view frustum. One camera outside it aborts the whole calibration.
- The top-view camera path is stored in a non-persistent setting, so the top-view stage must be re-run each session before calibration.
- Calibration describes cameras as they are now. Moving or re-placing a camera invalidates the file; re-run.
Troubleshooting
| Error / symptom | Cause | Solution |
|---|---|---|
CALIBRATE_PREFLIGHT: ext=unavailable |
App does not resolve the extension | Launch isaacsim.exp.action_and_event_data_generation.base.sh, or add --enable isaacsim.sensors.rtx.calibration |
cameras=0 in preflight |
Nothing under the camera parent prim | Place cameras first, or pass camera_parent_prim_path |
place_info=invalid |
No usable key=value pair |
Pass e.g. place_info=room=warehouse; pairs are joined with / |
scene_root=missing |
scene_root does not resolve |
Point it at a prim that bounds the area, usually /World |
| "camera … is not covered by top view!" | A camera sits outside the top-view frustum | Widen scene_root to a prim whose bbox contains every camera, or remove the stray camera |
calibration.json not written |
Pipeline aborted mid-run | Read the Isaac Sim console; the abort reason is logged via carb.log_error |
| Calibration silently does nothing | Called the sync generate_calibration() |
It is fire-and-forget; await generate_calibration_async() instead |
if is_ready_to_calibrate(): never true |
Returns None on success — see below |
Compare is not False, never truthiness |
When to use this skill
| You want | Skill |
|---|---|
calibration.json for cameras that already exist |
this skill |
| Decide where cameras go to cover an area | place-camera-max-coverage |
| Decide where cameras go to watch one object | place-camera-aim-at |
| Author one camera's intrinsics, AOVs, distortion by hand | isaac-camera |
The pipeline
Four stages, strictly ordered — each one depends on the previous having finished:
- Top-view camera. An orthographic camera framed on
scene_root. Setstop_view_camera_path, which every later stage validates. - Calibration dots.
dot_countreference points per camera under/World/Calibration_Dots, used to solve the world↔image mapping. - Calibration file. Per-camera intrinsics, extrinsics, homography, and FOV polygons →
calibration.json. - Top-view image. Orthographic render →
Top.pngplusimageMetadata.json.
Parameter semantics and settings are in
references/calibration-api.md; the output
schema and async/ordering rules are in
references/calibration-runtime-notes.md.
Available Scripts
| Script | Purpose | Arguments |
|---|---|---|
scripts/calibrate_camera.py |
Extract intrinsics, extrinsics, and FOV polygons for placed cameras | injected 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/calibrate_camera.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/calibrate-metropolis-camera/scripts/calibrate_camera.py \
--arg action=calibrate --arg output_dir=/tmp/calib
Workflow
-
Preflight.
action=preflight(the default) reports extension, stage, camera count,place_info,scene_root, and output folder without touching the stage. It never raises.run_script("scripts/calibrate_camera.py", args=["action=preflight"])Expect
CALIBRATE_PREFLIGHT: ext=enabled stage=ok cameras=5 place_info=ok scene_root=ok output_dir=.... Acameras=0here is the usual blocker — place cameras first. -
Calibrate. Runs all four stages and verifies the files landed.
run_script("scripts/calibrate_camera.py", args=["action=calibrate", "output_dir=/tmp/calib"])Expect
CALIBRATE_RESULT: action=calibrate cameras=5 top_camera=... output=/tmp/calibfollowed by awrote <file> (<n> bytes)line per artifact. -
Verify. Confirm
calibration.jsonhas onesensors[]entry per camera and thatTop.pngis not black — a black top view means the orthographic camera framed empty space, and the homographies derived from it are meaningless. -
Re-run after any camera moves. The file is a snapshot of the current poses.
Use action=topview to refresh only the top-view camera and image without
redoing the dots and calibration.
Calling the API directly
from isaacsim.sensors.rtx.calibration import CameraCalibrationManager, CameraCalibrationSettings
CameraCalibrationSettings.camera_calibration_output_folder_path = "/tmp/calib"
CameraCalibrationSettings.place_info = "room=warehouse"
CameraCalibrationSettings.scene_bounding_box_path = "/World"
manager = CameraCalibrationManager.get_instance()
manager.create_top_view_camera(root_prim_path="/World")
await manager.generate_calibration_dot_prim_async()
# NOT `if manager.is_ready_to_calibrate():` -- see below.
if manager.is_ready_to_calibrate() is False:
raise RuntimeError("stage is not ready; see the carb log")
await manager.generate_calibration_async()
await manager.capture_and_process_camera_view_image()
Three traps worth stating up front:
is_ready_to_calibrate()returnsNoneon success. It has threereturn Falsebranches and noreturn True, so it falls off the end. Testing it for truthiness means the guard never passes. Compareis not False.- The sync entry points are fire-and-forget.
generate_calibration()wrapsasyncio.ensure_future(...)and returns immediately, so the next stage races it. Await the_asyncvariants; python_server payloads support top-levelawait. top_view_camera_pathis non-persistent. It is only set bycreate_top_view_camera(), so stage 1 must run every session even if the top-view camera prim still exists on the stage.
Related skills
place-camera-max-coverage/place-camera-aim-at— produce the cameras this skill calibrates.isaac-sim-remote— the transport this skill's script rides on.isaac-camera— per-camera intrinsics, render products, AOVs, distortion.isaac-sim-sensor— attaching annotators and writers once cameras exist.
Version History
- 2469084 Current 2026-09-22 15:46


