Auto OMap from USDs#

Occupancy maps gate collision-free pose sampling during training. Historically they were hand-authored in the Isaac Sim editor — slow, error-prone, and a blocker for SAGE-driven scene generation. The scripts/generate_omap_from_usd.py CLI generates an OMap directly from a USD scene in one shot.

Generate an OMap#

python scripts/generate_omap_from_usd.py \
    compass/rl_env/exts/mobility_es/mobility_es/usd/<scene>/<scene>.usd

(Inside an activated dev shell, python already points at Isaac Sim’s bundled Python; on bare-metal install, prefix with ${ISAACLAB_PATH}/isaaclab.sh -p.)

By default the script writes the PNG + ROS YAML pair to <usd_dir>/omap/ next to the input USD — exactly where OccupancyMapCollisionChecker auto-discovers it when no explicit OMAP_PATHS entry exists.

Origin convention#

There are two OMap conventions in the codebase:

  • Legacy bundled COMPASS maps use a custom top-left YAML origin. These are the maps already listed as plain string paths in OMAP_PATHS.

  • New maps generated by scripts/generate_omap_from_usd.py use the Isaac Sim / ROS occupancy-map convention: the YAML origin is the bottom-left map corner in world coordinates.

The generator follows Isaac Sim’s ROS export behavior. It rotates the raw Isaac OMap buffer by 180 degrees before writing the PNG:

np.flipud(np.fliplr(img))

The saved PNG therefore has columns increasing with world +X and rows increasing toward world -Y, while the YAML origin is written as (xmin, ymin, 0.0). This matches ROS map_server / Isaac Sim’s “ROS Occupancy Map Parameters File” convention.

OccupancyMapCollisionChecker keeps backward compatibility with the old maps:

  • OMAP_PATHS entries that are plain strings are interpreted as legacy top-left maps.

  • Auto-discovered maps at <usd_dir>/omap/occupancy_map.yaml are interpreted as generated ROS bottom-left maps.

  • If a ROS-convention map must be registered explicitly in OMAP_PATHS, use a dict entry:

OMAP_PATHS = {
    "MyScene": {
        "path": "/path/to/omap/occupancy_map.yaml",
        "origin_convention": "bottom-left",
    },
}

If you have an auto-discovered map generated by an older version of the script, regenerate it. Older generated maps used the legacy top-left interpretation and will be read incorrectly by the current auto-discovery path.

Common flags#

Flag

Default

Meaning

-o, --out-dir

<usd_dir>/omap/

Where the PNG + YAML land

--cell-size

0.05

meters / pixel

--z-min / --z-max

0.1 / 0.62

Height slab the rasterizer projects

--bounds

derived from USD bbox + 1 m padding

World-space xmin xmax ymin ymax

--padding

1.0

Padding (m) around the auto-derived bbox

--map-name

<usd_basename>

PNG stem

--seed-xy

0.0 0.0

World-space flood-fill seed; reachable cells from this point become free

Run python scripts/generate_omap_from_usd.py --help for the complete list.

Loader auto-discovery#

OccupancyMapCollisionChecker (compass/rl_env/exts/.../utils/occupancy_map.py) checks OMAP_PATHS first; if it has no entry for the scene, it falls back to <dirname(spawn.usd_path)>/omap/occupancy_map.yaml — the same path the generator writes to. No OMAP_PATHS registration is needed for new scenes.

Slab tuning for taller embodiments#

The default [0.1, 0.62] height slab works for Carter, Spot, G1. Tall humanoids (H1) clear about 1.3 m; bump --z-max 1.4 so the rasterizer captures higher obstacles. The slab is the only embodiment-specific knob and is exposed at the CLI rather than baked into per-embodiment maps.