Skip to main content

navigation-primitives

Shared mobile-robot substrate: footprints, A*, kinematics, cameras. Use before navigation/MobilityGen; not for map.yaml export (use occupancy-map).

Quellinformationen

Repository
isaac-sim/IsaacSim
Letzte Quellaktivität
18. September 2026 um 16:05
Erkannte Sprache von SKILL.md
Englisch
Sterne
4.169
Forks
560

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
4 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
navigation-primitives
description
Shared mobile-robot substrate: footprints, A*, kinematics, cameras. Use before navigation/MobilityGen; not for map.yaml export (use occupancy-map).
license
Apache-2.0
metadata
{"author":"Renato Gasoto <info@nvidia.com>"}
# Navigation Primitives — Shared Substrate ## Purpose Shared mobile-robot substrate: occupancy maps, A* planning, robot footprints, differential/holonomic kinematics, and chase-camera math. ## Prerequisites - Built Isaac Sim with `isaacsim.core.experimental`, `wheeled_robots` controllers, and `SimulationManager`. - A loaded robot articulation for footprint derivation; `numpy`, `scipy`, `cv2` for planning/collision helpers. - A `map.yaml`/`map.png` pair from `occupancy-map` when using pre-baked grids (optional for runtime projection). ## Limitations - This is the shared substrate only — it does not drive robots, record SDG, or publish ROS topics; jump to the specialization skills for those. - Footprints/kinematics assume authored collider geometry and correct scene units; garbage colliders yield garbage footprints. - Oriented-footprint PhysX checks require `SimulationManager.initialize()`; the grid-based check works offline but is coarser. ## Troubleshooting | Error / symptom | Cause | Solution | |---|---|---| | Robot falls through floor / feet float | Spawned without `z_offset` | Spawn at `z = ground + compute_robot_footprint(...)["z_offset"]` | | Path clips walls in render but not omap | Skipped oriented-footprint validation | Run `footprint_clips_grid` per waypoint (Validation Pipeline steps 6-7) | | Grid full of shell/signage obstacles | Visual-bbox projection without filtering | Prefer collider-driven filtering; apply the geometric filter list | Foundation layer for mobile robot navigation. Consumed by: - `isaac-sim-robot-navigation`: runtime navigation in custom scripts (RL policy, physics-vs-baked, GPU OOM). - `mobility-gen`: two-phase MobilityGen SDG (record -> replay+render). - `occupancy-map`: produces the `map.yaml` consumed here. Read this first for any mobile-robot work, then jump to the specialization. ## Available Scripts | Script | Purpose | Arguments | |---|---|---| | `scripts/occupancy_map_from_usd.py` | Rasterize a 2D occupancy grid directly from USD geometry (no PhysX required) | see script --help | | `scripts/robot_footprint.py` | Compute robot footprint dimensions and Z-offset from USD collision geometry | see script --help | ## Running scripts From agent runtimes that expose skill execution helpers, invoke helpers with `run_script()`: ```python run_script("scripts/occupancy_map_from_usd.py", args=["--help"]) ``` From a built Isaac Sim tree, run the same file with `./python.sh` (Linux) or `python.bat` (Windows) from `_build/*/release`, or execute shell helpers directly when they do not require the simulator. ## Shared NVIDIA APIs | Capability | Module | |---|---| | Occupancy maps | `isaacsim.replicator.mobility_gen.impl.occupancy_map.OccupancyMap` | | A* path planner | `isaacsim.replicator.mobility_gen.impl.path_planner.generate_paths` | | Runtime omap from stage | `isaacsim.asset.gen.omap.bindings._omap.Generator` | | Robot articulation | `isaacsim.core.experimental.prims.Articulation` | | Differential controller | `isaacsim.robot.experimental.wheeled_robots.controllers.DifferentialController` | | Holonomic controller | `isaacsim.robot.wheeled_robots.controllers.holonomic_controller.HolonomicController` | | Physics lifecycle | `isaacsim.core.simulation_manager.SimulationManager` | `OccupancyMap.from_ros_yaml(path)` loads a YAML+PNG pair (produced by `occupancy-map`). Both isaac-sim-robot-navigation and mobility-gen consume this same format. ## Robot Footprints & Z-Offsets — Derived at Runtime Do not hardcode footprints. Walk the articulation's collider prims and union their world-space AABBs. This handles every robot (Spot, Carter, VSVXL, Jetbot, Kaya, H1, custom) and stays correct when assets change. `compute_robot_footprint(stage, robot_root)` — walk CollisionAPI prims, union their AABBs, return size, z_offset, inscribed_radius, circumscribed_radius. See [`scripts/robot_footprint.py`](scripts/robot_footprint.py). Use `inscribed_radius` when the robot can rotate freely in place (over-conservative, zero clip). Use `circumscribed_radius` only when you require zero false negatives. For non-circular robots (Spot, VSVXL), prefer the oriented-footprint check below over a single radius. **Always spawn the robot at `z = ground + z_offset`**. Missing the Z-offset is the #1 cause of "robot falls through the floor" or "feet pop above ground" bugs. ### Reference Values (sanity check only) If your `compute_robot_footprint` output is far from these, your collider authoring or scene units are wrong: | Robot | Expected size (m) | Expected z_offset | Inscribed r | |---|---|---|---| | Spot | ~1.08 × 0.44 × 0.55 | ~0.69 | ~0.22 | | Spot + arm | ~1.10 × 0.40 × 1.20 | ~0.69 | ~0.20 | | Nova Carter | track_w=0.499, wheel_r=0.14 | ~0.0 | ~0.25 | | VSVXL | ~2.52 × 1.72, 6-wheel diff | ~0.0 | ~0.86 | | Jetbot | wheel_base=0.1125, wheel_r=0.03 | ~0.02 | ~0.06 | | Kaya (holonomic) | wheel_base=0.10, wheel_r=0.04 | ~0.02 | ~0.10 | | H1 (humanoid) | — | ~1.05 | ~0.20 | ## Occupancy Map from USD (Direct Projection) Use when you need a runtime omap and don't already have a `map.yaml`. For the canonical `map.yaml` workflow consumed by MobilityGen, use `occupancy-map` instead. `occupancy_map_from_usd(stage, x_range, y_range, resolution, z_cutoff, colliders_only)` — rasterize USD geometry into a 2D uint8 grid (0=free, 255=occupied). See [`scripts/occupancy_map_from_usd.py`](scripts/occupancy_map_from_usd.py). ### Obstacle Filtering (CRITICAL — learned 2026-03-13) Two strategies, in order of preference: **A. Collider-driven (preferred when assets have authored colliders).** Iterate only prims with `UsdPhysics.CollisionAPI`. This already excludes visual-only geometry (signage, decals, light cones, debug arrows) without a filter list. ```python from pxr import UsdPhysics for prim in Usd.PrimRange(stage.GetPrimAtPath("/World")): if not prim.HasAPI(UsdPhysics.CollisionAPI): continue enabled = prim.GetAttribute("physics:collisionEnabled") if enabled and enabled.Get() is False: continue # rasterize this prim's AABB into the grid ``` **B. Visual-bbox + filter list (fallback for scenes without colliders).** Naive bbox projection fills the grid with shell, zones, signage. Filter aggressively: ```python SKIP_SCOPES = {"GroundPlane", "Looks", "Lighting", "Render", "PushGraph", "DomeLight", "DemoCamera", "Spot_01", "Spot_02", "Obstacles_Anim"} SKIP_PREFIXES = ("Floor", "FL_", "FR_", "Exit_", "Hum", "SM_Deluxe") SKIP_CHILDREN = ("sm_warehouse_mega", "Zones", "signage") ``` Geometric filters (apply after either strategy): - Skip area > 3000 m² (shell, zone assemblies) - Skip height < 0.1 m (floor markings, safety tape) - Skip Z_min > 3.6 m (ceiling-only objects, ≈ 3× robot height) - Skip Z_max < `fp["z_offset"]` + 0.05 m (anything the robot can drive over) **Result on V4 KION**: 244 real obstacles (vs 5000+ before filtering), 70% free space. ### Buffer Sizing — Derived from Footprint The buffer is `circumscribed_radius + safety_margin`, not a magic constant. Empirical `safety_margin` defaults (validated 2026-03-15): | Context | Safety margin | |---|---| | Open corridor, smooth control | 0.10 m | | Aisle navigation, cluttered | 0.30 m | | Cluttered + non-zero yaw error | 0.50 m | ```python fp = compute_robot_footprint(stage, "/World/Robot") buffer_m = fp["circumscribed_radius"] + 0.30 # aisle default buffer_cells = int(round(buffer_m / RESOLUTION)) ``` For Spot (`circumscribed_radius ≈ 0.58 m`) this yields ~0.88–1.08 m, not the legacy 1.5 m blanket value. The legacy value was conservatively tuned against a circular proxy; with the oriented-footprint check (below) you recover the extra ~0.5 m of navigable space. ## A* Path Planning Erode by `inscribed_radius` (fast, conservative). Then validate the smoothed path with an oriented-footprint collision check, which recovers the navigable space the inscribed-radius erosion threw away. ```python from scipy.ndimage import binary_erosion import numpy as np fp = compute_robot_footprint(stage, "/World/Robot") kernel_r = int(fp["inscribed_radius"] / RESOLUTION) kernel = np.zeros((2*kernel_r+1, 2*kernel_r+1), dtype=bool) for dy in range(-kernel_r, kernel_r+1): for dx in range(-kernel_r, kernel_r+1): if dx*dx + dy*dy <= kernel_r*kernel_r: kernel[dy+kernel_r, dx+kernel_r] = True navigable = (grid == 0) eroded = binary_erosion(navigable, structure=kernel) # Standard A* over `eroded` (heapq-based) ``` ### Oriented-Footprint Collision Check (recommended for non-circular robots) Drives the same PhysX query the simulator uses. Works in two modes: **1. Against PhysX scene (after `SimulationManager.initialize()`):** use `get_physx_scene_query_interface().overlap_box`. Returns hit count; >0 means clip. ```python import carb from omni.physx import get_physx_scene_query_interface from pxr import Gf def footprint_clips(x: float, y: float, yaw: float, fp: dict, z_query: float = 0.2) -> bool: # half-extents of the robot footprint half = carb.Float3(fp["size"][0] / 2, fp["size"][1] / 2, fp["size"][2] / 2) origin = carb.Float3(x, y, z_query + fp["size"][2] / 2) # quaternion (x, y, z, w) for yaw about Z rot = Gf.Rotation(Gf.Vec3d(0, 0, 1), np.degrees(yaw)).GetQuat() quat = carb.Float4(*rot.GetImaginary(), rot.GetReal()) hits = get_physx_scene_query_interface().overlap_box( half, origin, quat, lambda h: True, anyHit=True, # early-exit on first hit ) return hits > 0 ``` **2. Against the rasterized omap (no PhysX needed):** stamp the rotated rectangle onto the obstacle grid and AND with the occupied mask. ```python import cv2 def footprint_clips_grid(px: int, py: int, yaw: float, fp: dict, grid: np.ndarray) -> bool: w_cells = fp["size"][0] / RESOLUTION h_cells = fp["size"][1] / RESOLUTION rect = ((px, py), (w_cells, h_cells), np.degrees(yaw)) pts = cv2.boxPoints(rect).astype(np.int32) mask = np.zeros_like(grid, dtype=np.uint8) cv2.fillPoly(mask, [pts], 1) return bool(np.any((grid > 0) & (mask > 0))) ``` ### Validation Pipeline (MANDATORY) 1. `compute_robot_footprint(stage, robot_root)` — get size, z_offset, radii. 2. Rasterize obstacles onto grid (0.25 m resolution typical). 3. Binary erode with circular kernel of radius = `inscribed_radius / resolution`. 4. A* pathfind on eroded grid. 5. Catmull-Rom smooth the raw path; assign yaw = `atan2(dy, dx)` along the curve. 6. **For every smoothed waypoint, run `footprint_clips_grid(px, py, yaw, fp, grid)`** (or `footprint_clips(...)` against PhysX). Reject the path on any hit. 7. If a single waypoint fails, snap to the nearest navigable cell and re-validate. If multiple fail, the inscribed-radius A* path is fundamentally bad — re-plan with a larger erosion kernel (`circumscribed_radius`). Skipping steps 6–7 produces paths that look fine on the omap but clip walls in render — especially on rectangular robots (Spot, VSVXL) cornering through aisles. ## Kinematics Helpers [`scripts/kinematics.py`](scripts/kinematics.py) — standalone differential-drive and holonomic kinematics with pure-pursuit path following. No Isaac Sim dependency; works offline for planning or inside a simulation loop. Key exports: - `differential_forward(v, ω, params)` → `(vL, vR)` wheel rad/s - `differential_inverse(vL, vR, params)` → `(v, ω)` body twist - `holonomic_forward(vx, vy, ω, params)` → 3 wheel velocities (120°-spaced) - `holonomic_inverse(wheel_vels, params)` → `(vx, vy, ω)` - `pure_pursuit_step(pos, yaw, path, config)` — PD-steered differential path follower - `holonomic_path_step(pos, yaw, path, config)` — strafing holonomic path follower ## Differential Drive Kinematics For `motion_generation` controller composition, see `motion-generation`; this section owns the geometry, wheel kinematics, and navigation tuning. ```python # Wheel velocities from body twist vL = (vx - omega * track_w / 2) / wheel_r vR = (vx + omega * track_w / 2) / wheel_r ``` PD steering (validated on Nova Carter / VSVXL): - KP=2.5, KD=1.2, MAX_W=1.5 rad/s - Waypoint tolerance: 4.0m - Speed reduction near waypoints and during large heading errors - Out-of-bounds: |Z| > 50 or |X|/|Y| > 500 → mark dead VSVXL validated parameters (LLM Advisor Grok-4, 2026-03-15): - ω = v / r (0.15m wheels → 10 rad/s for 1.5 m/s) - Physics: dt=1/120s, substeps=4 (effective 480Hz) - PID heading: Kp=2.0, Ki=0.1, Kd=0.5, max angular 1.0 rad/s - Aisle speed: 0.8 m/s; corridor speed: 1.5 m/s - Corridor buffer: 0.5m; aisle buffer: 0.2m + reduced speed ## Holonomic / Mecanum (Kaya, AMR) Use `HolonomicController` with `HolonomicRobotUsdSetup` to extract wheel positions, orientations, mecanum angles from the robot USD. Apply via `WheeledRobot.apply_wheel_actions`. 2D MobilityGen action `[lin, ang]` → 3D holonomic command `[forward, lateral=0, yaw]`. See `mobility-gen` for the `WheeledMobilityGenRobot.build()` override pattern. ## DifferentialController + Articulation (Kit 110) ```python from isaacsim.robot.experimental.wheeled_robots.controllers import DifferentialController from isaacsim.core.experimental.prims import Articulation import numpy as np robot = Articulation("/World/Robot") robot.initialize_cpp_data_view()
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen