fix: rename migerate -> migrate (typo fix)
This commit is contained in:
159
migrate/fix_usd_metadata.py
Normal file
159
migrate/fix_usd_metadata.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
Batch fix USD metadata for IS 5.0.0 compatibility.
|
||||
|
||||
IS 4.5.0 auto-added 'Rotate:unitsResolve' and 'Scale:unitsResolve' xformOps
|
||||
to compensate for metersPerUnit/upAxis differences. IS 5.0.0 no longer does this,
|
||||
causing RigidObject physics to break (objects fly away).
|
||||
|
||||
Fix: Change metersPerUnit to 1.0 and upAxis to Z in USD metadata.
|
||||
Vertex data stays unchanged (already in correct scale for the scene).
|
||||
|
||||
Usage:
|
||||
# Scan only (dry run):
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --dry-run
|
||||
|
||||
# Fix all:
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets
|
||||
|
||||
# Fix specific pattern:
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --pattern "Aligned_obj.usd"
|
||||
|
||||
# Skip specific directories (e.g. robot models, curobo assets):
|
||||
python migrate/fix_usd_metadata.py --root . --skip curobo,robot
|
||||
|
||||
# Restore from backups:
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --restore
|
||||
"""
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from pxr import Usd, UsdGeom
|
||||
|
||||
|
||||
def fix_usd_files(root: str, pattern: str = "*.usd", target_mpu: float = 1.0,
|
||||
target_up: str = "Z", dry_run: bool = False, skip: list = None):
|
||||
usd_files = glob.glob(os.path.join(root, "**", pattern), recursive=True)
|
||||
usd_files = [f for f in usd_files if not f.endswith(".bak")]
|
||||
usd_files.sort()
|
||||
|
||||
skip = skip or []
|
||||
|
||||
print(f"Scanning {len(usd_files)} USD files under: {root}")
|
||||
if dry_run:
|
||||
print("[DRY RUN] No files will be modified.\n")
|
||||
print()
|
||||
|
||||
fixed = 0
|
||||
skipped = 0
|
||||
skipped_dir = 0
|
||||
errors = 0
|
||||
|
||||
for fpath in usd_files:
|
||||
rel = os.path.relpath(fpath, root)
|
||||
|
||||
# Check skip patterns
|
||||
if any(s in rel for s in skip):
|
||||
print(f" SKIP (dir filter): {rel}")
|
||||
skipped_dir += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
stage = Usd.Stage.Open(fpath)
|
||||
if stage is None:
|
||||
print(f" ERROR: Cannot open {rel}")
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
mpu = UsdGeom.GetStageMetersPerUnit(stage)
|
||||
up = UsdGeom.GetStageUpAxis(stage)
|
||||
|
||||
needs_fix = (mpu != target_mpu) or (up != target_up)
|
||||
if not needs_fix:
|
||||
print(f" OK: {rel} (mpu={mpu}, up={up})")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
print(f" WOULD FIX: {rel} (mpu={mpu}, up={up} -> mpu={target_mpu}, up={target_up})")
|
||||
fixed += 1
|
||||
continue
|
||||
|
||||
# Backup
|
||||
bak = fpath + ".bak"
|
||||
if not os.path.exists(bak):
|
||||
shutil.copy2(fpath, bak)
|
||||
|
||||
# Fix
|
||||
UsdGeom.SetStageMetersPerUnit(stage, target_mpu)
|
||||
UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.z if target_up == "Z" else UsdGeom.Tokens.y)
|
||||
stage.GetRootLayer().Save()
|
||||
|
||||
print(f" FIXED: {rel} (mpu={mpu}/{up} -> {target_mpu}/{target_up})")
|
||||
fixed += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f" ERROR: {rel} -> {e}")
|
||||
errors += 1
|
||||
|
||||
print(f"\nDone: {fixed} {'would fix' if dry_run else 'fixed'}, {skipped} already OK, "
|
||||
f"{skipped_dir} skipped (dir filter), {errors} errors.")
|
||||
|
||||
if not dry_run and fixed > 0:
|
||||
print("Backups saved as *.bak.")
|
||||
print("To restore: python migrate/fix_usd_metadata.py --root <DIR> --restore")
|
||||
|
||||
return fixed
|
||||
|
||||
|
||||
def restore_backups(root: str, pattern: str = "*.usd"):
|
||||
bak_files = glob.glob(os.path.join(root, "**", pattern + ".bak"), recursive=True)
|
||||
bak_files.sort()
|
||||
|
||||
if not bak_files:
|
||||
print(f"No .bak files found under: {root}")
|
||||
return
|
||||
|
||||
print(f"Found {len(bak_files)} backup files. Restoring...")
|
||||
for bak in bak_files:
|
||||
original = bak[:-4] # remove .bak
|
||||
shutil.copy2(bak, original)
|
||||
print(f" Restored: {os.path.relpath(original, root)}")
|
||||
print(f"\nDone: {len(bak_files)} files restored.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Batch fix USD metersPerUnit/upAxis metadata for IS 5.0.0 compatibility",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Dry run on example assets:
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --dry-run
|
||||
|
||||
# Fix all Aligned_obj.usd in example assets:
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --pattern "Aligned_obj.usd"
|
||||
|
||||
# Fix everything except robot/curobo dirs:
|
||||
python migrate/fix_usd_metadata.py --root . --skip curobo,robot
|
||||
|
||||
# Restore all backups:
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --restore
|
||||
""")
|
||||
parser.add_argument("--root", default="workflows/simbox/example_assets",
|
||||
help="Root directory to scan (default: workflows/simbox/example_assets)")
|
||||
parser.add_argument("--pattern", default="*.usd", help="Filename glob pattern (default: *.usd)")
|
||||
parser.add_argument("--target-mpu", type=float, default=1.0, help="Target metersPerUnit (default: 1.0)")
|
||||
parser.add_argument("--target-up", choices=["Y", "Z"], default="Z", help="Target upAxis (default: Z)")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Scan and report only, do not modify files")
|
||||
parser.add_argument("--skip", default="", help="Comma-separated dir substrings to skip (e.g. 'curobo,robot')")
|
||||
parser.add_argument("--restore", action="store_true", help="Restore all .bak files to originals")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.restore:
|
||||
restore_backups(args.root, args.pattern)
|
||||
else:
|
||||
skip_list = [s.strip() for s in args.skip.split(",") if s.strip()]
|
||||
fix_usd_files(args.root, args.pattern, args.target_mpu, args.target_up, args.dry_run, skip_list)
|
||||
199
migrate/migrate.md
Normal file
199
migrate/migrate.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Isaac Sim 4.5.0 -> 5.0.0 Migration Guide
|
||||
|
||||
## Background
|
||||
|
||||
- GPU: NVIDIA RTX PRO 6000 Blackwell (SM_120, compute capability 12.0)
|
||||
- IS 4.5.0 = Kit 106.5.0, DLSS Ray Reconstruction not supported on Blackwell (needs Kit >= 106.5.3), rendering has noise
|
||||
- IS 5.0.0 = Kit 107.x, fixes Blackwell rendering noise
|
||||
- IS 5.1.0 headless camera rendering completely broken (known bug GitHub #3250), abandoned
|
||||
|
||||
## Issues & Solutions
|
||||
|
||||
### 1. SimulationApp import path changed
|
||||
|
||||
**Error:** `ModuleNotFoundError: No module named 'omni.isaac.kit'`
|
||||
|
||||
**Cause:** IS 5.x changed the import path.
|
||||
|
||||
**Fix** (`nimbus_extension/components/load/env_loader.py`):
|
||||
```python
|
||||
try:
|
||||
from isaacsim import SimulationApp # IS 5.x
|
||||
except ImportError:
|
||||
from omni.isaac.kit import SimulationApp # IS 4.x
|
||||
```
|
||||
|
||||
### 2. USD metersPerUnit / upAxis not auto-compensated
|
||||
|
||||
**Error:** RigidObject Z coordinate flying to 109.75+ after `world.reset()`, curobo `plan_single()` fails with "Plan did not converge" (goal_pos Z = 25261 meters).
|
||||
|
||||
**Cause:** IS 4.5.0 auto-added `Rotate:unitsResolve` (X=90) and `Scale:unitsResolve` (0.01, 0.01, 0.01) xformOps to compensate for USD files with `metersPerUnit=0.01, upAxis=Y`. IS 5.0.0 no longer does this, causing PhysX to misinterpret RigidObject positions.
|
||||
|
||||
GeometryObjects (no physics) were unaffected — only RigidObjects with PhysX simulation had the issue.
|
||||
|
||||
**Fix:** Change USD metadata directly using pxr scripting. Run `fix_usd_metadata.py`:
|
||||
```python
|
||||
from pxr import Usd, UsdGeom
|
||||
stage = Usd.Stage.Open(usd_path)
|
||||
UsdGeom.SetStageMetersPerUnit(stage, 1.0) # was 0.01
|
||||
UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.z) # was Y
|
||||
stage.GetRootLayer().Save()
|
||||
```
|
||||
|
||||
All 14 `Aligned_obj.usd` files under `workflows/simbox/example_assets` have been batch-fixed. Backups saved as `.bak`.
|
||||
|
||||
**Verification:** After fix, pick_object_right Z went from 109.75 to 0.7968 (normal).
|
||||
|
||||
### 3. scipy `scalar_first` parameter not supported
|
||||
|
||||
**Error:** `Rotation.as_quat() takes no keyword arguments`
|
||||
|
||||
**Cause:** IS 5.0.0 ships scipy < 1.11 which doesn't support `scalar_first` parameter in `Rotation.from_quat()` / `Rotation.as_quat()`. Dozens of files use this parameter.
|
||||
|
||||
**Fix** (`launcher.py`): Monkey-patch via subclass at startup:
|
||||
```python
|
||||
try:
|
||||
from scipy.spatial.transform import Rotation as _R
|
||||
_R.from_quat([1, 0, 0, 0], scalar_first=True)
|
||||
except TypeError:
|
||||
# Install Rotation subclass that handles scalar_first
|
||||
# See launcher.py for full implementation
|
||||
pass
|
||||
```
|
||||
|
||||
### 4. `arena_file_path` is None on second reset
|
||||
|
||||
**Error:** `arena_file_path` becomes None after first episode.
|
||||
|
||||
**Cause:** `self.task_cfg.pop("arena_file", None)` on line 107 of `simbox_dual_workflow.py` deletes the key after first use.
|
||||
|
||||
**Fix** (`workflows/simbox_dual_workflow.py`): Cache the value:
|
||||
```python
|
||||
self._arena_file_path = None # in __init__
|
||||
|
||||
# in reset():
|
||||
arena_file_path = self.task_cfg.get("arena_file", None) or self._arena_file_path
|
||||
if arena_file_path:
|
||||
self._arena_file_path = arena_file_path
|
||||
```
|
||||
|
||||
### 5. "Task name should be unique in the world"
|
||||
|
||||
**Error:** `AssertionError` when calling `world.add_task()` on second episode.
|
||||
|
||||
**Cause:** World retains tasks from previous episode.
|
||||
|
||||
**Fix** (`workflows/simbox_dual_workflow.py`):
|
||||
```python
|
||||
self.world._current_tasks.clear()
|
||||
self.world.add_task(self.task)
|
||||
```
|
||||
|
||||
### 6. "A prim already exists at prim path"
|
||||
|
||||
**Error:** Scene setup tries to re-create existing prims on repeated `world.reset()` calls.
|
||||
|
||||
**Fix** (`workflows/simbox/core/tasks/banana.py`): Add `_scene_initialized` guard:
|
||||
```python
|
||||
def set_up_scene(self, scene):
|
||||
if getattr(self, '_scene_initialized', False):
|
||||
self._task_objects = {}
|
||||
self._task_objects |= (
|
||||
getattr(self, 'fixtures', {}) |
|
||||
getattr(self, 'objects', {}) |
|
||||
getattr(self, 'robots', {}) |
|
||||
getattr(self, 'cameras', {})
|
||||
)
|
||||
return
|
||||
self._scene_initialized = True
|
||||
super().set_up_scene(scene)
|
||||
```
|
||||
|
||||
### 7. Collision group prim already exists
|
||||
|
||||
**Error:** Collision group prims persist across resets.
|
||||
|
||||
**Fix** (`workflows/simbox/core/utils/collision_utils.py`): Remove existing collision prims before re-creating:
|
||||
```python
|
||||
collision_prim = stage.GetPrimAtPath(collision_root_path)
|
||||
if collision_prim.IsValid():
|
||||
stage.RemovePrim(collision_root_path)
|
||||
```
|
||||
|
||||
### 8. DomeLight environment map tilted
|
||||
|
||||
**Error:** HDR environment map appears rotated/tilted in viewport and rendered output.
|
||||
|
||||
**Cause:** DomeLight rotation was randomized on all 3 axes (X, Y, Z). Rotating X/Y tilts the environment.
|
||||
|
||||
**Fix** (`workflows/simbox/core/tasks/banana.py`): Only rotate Z axis:
|
||||
```python
|
||||
# Before:
|
||||
rotation = [random.uniform(r[0], r[1]) for _ in range(3)]
|
||||
# After:
|
||||
rotation = [0.0, 0.0, random.uniform(r[0], r[1])]
|
||||
```
|
||||
|
||||
## Files Modified
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `nimbus_extension/components/load/env_loader.py` | SimulationApp import compatibility |
|
||||
| `launcher.py` | scipy Rotation monkey-patch |
|
||||
| `workflows/simbox_dual_workflow.py` | arena_file caching, task clearing, task reuse |
|
||||
| `workflows/simbox/core/tasks/banana.py` | Scene re-init guard, DomeLight rotation fix |
|
||||
| `workflows/simbox/core/utils/collision_utils.py` | Collision group cleanup |
|
||||
| `workflows/simbox/example_assets/**/Aligned_obj.usd` | metersPerUnit=1.0, upAxis=Z |
|
||||
| `fix_usd_metadata.py` | Batch USD metadata fix script |
|
||||
|
||||
## Tools
|
||||
|
||||
Migration tools are located in the `migrate/` directory.
|
||||
|
||||
### scan_usd_metadata.py — Scan USD metadata
|
||||
|
||||
Scan all USD files and report their `metersPerUnit` / `upAxis`:
|
||||
|
||||
```bash
|
||||
conda activate banana500
|
||||
|
||||
# Scan entire project
|
||||
python migrate/scan_usd_metadata.py --root .
|
||||
|
||||
# Scan specific directory
|
||||
python migrate/scan_usd_metadata.py --root workflows/simbox/example_assets
|
||||
```
|
||||
|
||||
Exit code: 0 = all OK, 1 = found files with non-standard metadata.
|
||||
建议扫描后丢给大模型分析一下然后再做下一步
|
||||
### fix_usd_metadata.py — Batch fix USD metadata
|
||||
|
||||
Fix `metersPerUnit` and `upAxis` in USD files, with backup/restore support:
|
||||
|
||||
```bash
|
||||
conda activate banana500
|
||||
|
||||
# Dry run — see what would be fixed, no changes made
|
||||
python migrate/fix_usd_metadata.py --root . --dry-run --skip curobo,robot
|
||||
|
||||
# Fix example assets only (default)
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets
|
||||
|
||||
# Fix all USD in project, skip robot/curobo models
|
||||
python migrate/fix_usd_metadata.py --root . --skip curobo,robot
|
||||
|
||||
# Fix only Aligned_obj.usd files
|
||||
python migrate/fix_usd_metadata.py --root . --pattern "Aligned_obj.usd"
|
||||
|
||||
# Restore from backups (undo all fixes)
|
||||
python migrate/fix_usd_metadata.py --root workflows/simbox/example_assets --restore
|
||||
```
|
||||
|
||||
**Important:** Do NOT fix robot USD (curobo, split_aloha) — their metersPerUnit/upAxis are intentionally set for their own coordinate systems. Use `--skip` to exclude them.
|
||||
|
||||
## Notes
|
||||
|
||||
- `plan_single()` occasionally returns None for edge-case target poses (workspace boundary). This is normal and happens in IS 4.5.0 too.
|
||||
- Example HDR is only 1k resolution — production should use 4k/8k for better background quality.
|
||||
- Example `envmap_lib` only has 1 HDR file — add more for randomization diversity.
|
||||
- `non_recyclable_garbage/obj_0` does not exist in example assets (only obj_1~obj_11). Config changed to use `obj_1`.
|
||||
177
migrate/migrate_imports.py
Normal file
177
migrate/migrate_imports.py
Normal file
@@ -0,0 +1,177 @@
|
||||
"""
|
||||
Batch migrate omni.isaac.* imports to use core.compat module.
|
||||
|
||||
Usage:
|
||||
python migrate/migrate_imports.py --dry-run # preview changes
|
||||
python migrate/migrate_imports.py # apply changes
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
|
||||
# Files to process (relative to project root)
|
||||
SCAN_DIRS = [
|
||||
"workflows/simbox/core/cameras",
|
||||
"workflows/simbox/core/controllers",
|
||||
"workflows/simbox/core/objects",
|
||||
"workflows/simbox/core/robots",
|
||||
"workflows/simbox/core/skills",
|
||||
"workflows/simbox/core/tasks",
|
||||
"workflows/simbox/core/utils",
|
||||
"workflows/simbox/solver",
|
||||
"workflows/", # simbox_dual_workflow.py
|
||||
"nimbus_extension/components/load",
|
||||
]
|
||||
|
||||
# Skip these files/dirs
|
||||
SKIP = ["curobo", "__pycache__", "compat.py", "tools/art"]
|
||||
|
||||
# Mapping: old import pattern -> compat import
|
||||
# Format: (regex_pattern, replacement_imports_from_compat)
|
||||
IMPORT_MAP = {
|
||||
# Full module imports
|
||||
r"from omni\.isaac\.core import World": "from core.compat import World",
|
||||
r"from omni\.isaac\.core\.controllers import BaseController": "from core.compat import BaseController",
|
||||
r"from omni\.isaac\.core\.tasks import BaseTask": "from core.compat import BaseTask",
|
||||
r"from omni\.isaac\.core\.robots\.robot import Robot": "from core.compat import Robot",
|
||||
r"from omni\.isaac\.core\.robots import Robot": "from core.compat import Robot",
|
||||
r"from omni\.isaac\.core\.prims import XFormPrim": "from core.compat import XFormPrim",
|
||||
r"from omni\.isaac\.core\.prims import RigidPrim": "from core.compat import RigidPrim",
|
||||
r"from omni\.isaac\.core\.prims import GeometryPrim": "from core.compat import GeometryPrim",
|
||||
r"from omni\.isaac\.core\.prims import RigidContactView": "from core.compat import RigidContactView",
|
||||
r"from omni\.isaac\.core\.prims import RigidContactView, XFormPrim": "from core.compat import RigidContactView, XFormPrim",
|
||||
r"from omni\.isaac\.core\.articulations\.articulation import Articulation": "from core.compat import Articulation",
|
||||
r"from omni\.isaac\.core\.objects\.cylinder import VisualCylinder": "from core.compat import VisualCylinder",
|
||||
r"from omni\.isaac\.core\.objects import cuboid, sphere": "from core.compat import cuboid, sphere",
|
||||
r"from omni\.isaac\.core\.objects import cuboid": "from core.compat import cuboid",
|
||||
r"from omni\.isaac\.core\.objects import sphere": "from core.compat import sphere",
|
||||
r"from omni\.isaac\.core\.materials import PreviewSurface": "from core.compat import PreviewSurface",
|
||||
r"from omni\.isaac\.core\.materials import OmniPBR": "from core.compat import OmniPBR",
|
||||
r"from omni\.isaac\.core\.materials\.omni_pbr import OmniPBR.*": "from core.compat import OmniPBR",
|
||||
r"from omni\.isaac\.core\.materials import OmniGlass, OmniPBR": "from core.compat import OmniGlass, OmniPBR",
|
||||
r"from omni\.isaac\.core\.materials import OmniGlass": "from core.compat import OmniGlass",
|
||||
r"from omni\.isaac\.core\.scenes\.scene import Scene": "from core.compat import Scene",
|
||||
r"from omni\.isaac\.sensor import Camera": "from core.compat import Camera",
|
||||
|
||||
# Utils
|
||||
r"from omni\.isaac\.core\.utils\.prims import get_prim_at_path\b(?!,)": "from core.compat import get_prim_at_path",
|
||||
r"from omni\.isaac\.core\.utils\.prims import create_prim, get_prim_at_path": "from core.compat import create_prim, get_prim_at_path",
|
||||
r"from omni\.isaac\.core\.utils\.prims import create_prim, is_prim_path_valid": "from core.compat import create_prim, is_prim_path_valid",
|
||||
r"from omni\.isaac\.core\.utils\.prims import is_prim_path_valid": "from core.compat import is_prim_path_valid",
|
||||
r"from omni\.isaac\.core\.utils\.prims import create_prim\b": "from core.compat import create_prim",
|
||||
r"from omni\.isaac\.core\.utils\.transformations import get_relative_transform\b(?!,)": "from core.compat import get_relative_transform",
|
||||
r"from omni\.isaac\.core\.utils\.transformations import pose_from_tf_matrix": "from core.compat import pose_from_tf_matrix",
|
||||
r"from omni\.isaac\.core\.utils\.stage import get_current_stage": "from core.compat import get_current_stage",
|
||||
r"from omni\.isaac\.core\.utils\.stage import add_reference_to_stage": "from core.compat import add_reference_to_stage",
|
||||
r"from omni\.isaac\.core\.utils\.stage import get_stage_units": "from core.compat import get_stage_units",
|
||||
r"from omni\.isaac\.core\.utils\.xforms import get_world_pose": "from core.compat import get_world_pose",
|
||||
r"from omni\.isaac\.core\.utils\.types import ArticulationAction": "from core.compat import ArticulationAction",
|
||||
r"from omni\.isaac\.core\.utils\.viewports import set_camera_view": "from core.compat import set_camera_view",
|
||||
r"from omni\.isaac\.core\.utils\.semantics import add_update_semantics": "from core.compat import add_update_semantics",
|
||||
r"from omni\.isaac\.core\.utils\.string import find_unique_string_name": "from core.compat import find_unique_string_name",
|
||||
r"from omni\.isaac\.core\.utils\.extensions import enable_extension": "from core.compat import enable_extension",
|
||||
r"from omni\.isaac\.core\.utils\.nucleus import get_assets_root_path as nucleus_path": "from core.compat import nucleus_path",
|
||||
}
|
||||
|
||||
# Multi-line import blocks that need special handling
|
||||
MULTILINE_PATTERNS = [
|
||||
# get_relative_transform, pose_from_tf_matrix block
|
||||
(
|
||||
r"from omni\.isaac\.core\.utils\.transformations import \(\s*\n\s*get_relative_transform,\s*\n\s*pose_from_tf_matrix,?\s*\n\s*\)",
|
||||
"from core.compat import get_relative_transform, pose_from_tf_matrix",
|
||||
),
|
||||
# prims multi-import blocks
|
||||
(
|
||||
r"from omni\.isaac\.core\.utils\.prims import \(\s*\n\s*get_prim_at_path,\s*\n\s*create_prim,\s*\n\s*is_prim_path_valid,?\s*\n\s*\)",
|
||||
"from core.compat import get_prim_at_path, create_prim, is_prim_path_valid",
|
||||
),
|
||||
(
|
||||
r"from omni\.isaac\.core\.utils\.prims import \(\s*\n[^)]+\)",
|
||||
None, # skip complex blocks, handle manually
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def should_skip(filepath):
|
||||
for s in SKIP:
|
||||
if s in filepath:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def migrate_file(filepath, dry_run=False):
|
||||
with open(filepath, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
original = content
|
||||
changes = []
|
||||
|
||||
# Handle multi-line imports first
|
||||
for pattern, replacement in MULTILINE_PATTERNS:
|
||||
if replacement is None:
|
||||
continue
|
||||
matches = list(re.finditer(pattern, content))
|
||||
for m in matches:
|
||||
changes.append(f" MULTI: {m.group()[:60]}... -> {replacement}")
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
# Handle single-line imports
|
||||
for pattern, replacement in IMPORT_MAP.items():
|
||||
matches = list(re.finditer(pattern, content))
|
||||
for m in matches:
|
||||
changes.append(f" {m.group().strip()} -> {replacement}")
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
if content != original:
|
||||
if not dry_run:
|
||||
with open(filepath, "w") as f:
|
||||
f.write(content)
|
||||
return changes
|
||||
return []
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Migrate omni.isaac imports to core.compat")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Preview changes without modifying files")
|
||||
parser.add_argument("--root", default=".", help="Project root directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
total_files = 0
|
||||
total_changes = 0
|
||||
|
||||
for scan_dir in SCAN_DIRS:
|
||||
full_dir = os.path.join(args.root, scan_dir)
|
||||
if not os.path.exists(full_dir):
|
||||
continue
|
||||
|
||||
if os.path.isfile(full_dir):
|
||||
files = [full_dir]
|
||||
else:
|
||||
files = []
|
||||
for root, dirs, filenames in os.walk(full_dir):
|
||||
for fn in filenames:
|
||||
if fn.endswith(".py"):
|
||||
files.append(os.path.join(root, fn))
|
||||
|
||||
for filepath in sorted(files):
|
||||
if should_skip(filepath):
|
||||
continue
|
||||
|
||||
rel = os.path.relpath(filepath, args.root)
|
||||
changes = migrate_file(filepath, args.dry_run)
|
||||
if changes:
|
||||
total_files += 1
|
||||
total_changes += len(changes)
|
||||
print(f"\n{rel}:")
|
||||
for c in changes:
|
||||
print(c)
|
||||
|
||||
action = "would change" if args.dry_run else "changed"
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Done: {total_changes} imports {action} in {total_files} files.")
|
||||
if args.dry_run:
|
||||
print("Run without --dry-run to apply changes.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
222
migrate/runtime_warnings.md
Normal file
222
migrate/runtime_warnings.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# Runtime Warnings & Errors Analysis
|
||||
|
||||
Pipeline: `python launcher.py --config configs/simbox/de_plan_and_render_template.yaml`
|
||||
Environment: IS 5.0.0, RTX PRO 6000 Blackwell, conda `banana500`
|
||||
Date: 2026-04-03
|
||||
|
||||
---
|
||||
|
||||
## Errors
|
||||
|
||||
### 1. PhysicsUSD: triangle mesh collision on dynamic body
|
||||
|
||||
```
|
||||
[Error] PhysicsUSD: Parse collision - triangle mesh collision (approximation None/MeshSimplification)
|
||||
cannot be a part of a dynamic body, falling back to convexHull approximation:
|
||||
/World/task_0/split_aloha/.../lifting_link/collisions
|
||||
```
|
||||
|
||||
**Severity:** Medium (PhysX auto-fallback, but logs error every reset)
|
||||
**Root cause:** Robot USD `lifting_link/collisions` mesh 使用 `meshSimplification` 碰撞近似,PhysX 不支持动态物体使用 triangle mesh collision。
|
||||
**Fix:** 修改 robot USD 将 `MeshCollisionAPI.approximation` 从 `meshSimplification` 改为 `convexHull`。
|
||||
|
||||
```python
|
||||
from pxr import Usd, UsdPhysics
|
||||
stage = Usd.Stage.Open('workflows/simbox/example_assets/split_aloha_mid_360/robot.usd')
|
||||
prim = stage.GetPrimAtPath('/Root/.../lifting_link/collisions')
|
||||
UsdPhysics.MeshCollisionAPI(prim).GetApproximationAttr().Set('convexHull')
|
||||
stage.GetRootLayer().Save()
|
||||
```
|
||||
|
||||
**Status:** FIXED. Backup: `robot.usd.bak`
|
||||
|
||||
---
|
||||
|
||||
### 2. Physics scenes stepping mismatch
|
||||
|
||||
```
|
||||
[Error] Physics scenes stepping is not the same, step subscription will be send with later step,
|
||||
per scene step is not yet supported.
|
||||
```
|
||||
|
||||
**Severity:** Low (仅首次 reset 时出现一次)
|
||||
**Root cause:** IS 5.0.0 内部 physics scene 步进调度问题。physics_dt 和 rendering_dt 均为 1/30,配置一致。
|
||||
**Status:** IGNORED. IS 内部行为,不影响仿真结果。
|
||||
|
||||
---
|
||||
|
||||
## Warnings — Fixed
|
||||
|
||||
### 3. Camera aperture inconsistent with resolution
|
||||
|
||||
```
|
||||
[Warning] 'verticalAperture' (1.529) and 'horizontalAperture' (2.095) are inconsistent with
|
||||
the pixel resolution aspect ratio (1.333). Setting 'verticalAperture' to 1.572 to ensure square pixels.
|
||||
```
|
||||
|
||||
**Severity:** Low (IS 自动修正,但每个相机打 2 条 warning)
|
||||
**Root cause:** `custom_camera.py` 在 `self.initialize()` 之后才设置 aperture/focal_length,但 `initialize()` 时就用默认 aperture 做一致性检查。
|
||||
**Fix:** 重构 `custom_camera.py`,将 aperture/focal_length 设置移到 `initialize()` 之前。
|
||||
|
||||
```python
|
||||
# Before (wrong order):
|
||||
super().__init__(...)
|
||||
self.initialize() # <-- checks aperture here with default values
|
||||
# ... later ...
|
||||
self.set_horizontal_aperture(...) # too late, warning already printed
|
||||
|
||||
# After (correct order):
|
||||
super().__init__(...)
|
||||
self.set_horizontal_aperture(...) # set aperture first
|
||||
self.set_vertical_aperture(...)
|
||||
self.set_focal_length(...)
|
||||
self.initialize() # now checks with correct values
|
||||
```
|
||||
|
||||
**Status:** FIXED in `workflows/simbox/core/cameras/custom_camera.py`
|
||||
|
||||
---
|
||||
|
||||
### 4. Camera.set_projection_type deprecated
|
||||
|
||||
```
|
||||
[Warning] Camera.set_projection_type is deprecated and will be removed in a future release.
|
||||
Please use Camera.set_lens_distortion_model instead.
|
||||
```
|
||||
|
||||
**Severity:** Low
|
||||
**Fix:** 替换 API 调用,带 fallback 兼容旧版本。
|
||||
|
||||
```python
|
||||
# Before:
|
||||
self.set_projection_type("pinhole")
|
||||
# After:
|
||||
try:
|
||||
self.set_lens_distortion_model("pinhole")
|
||||
except AttributeError:
|
||||
self.set_projection_type("pinhole")
|
||||
```
|
||||
|
||||
**Status:** FIXED in `workflows/simbox/core/cameras/custom_camera.py`
|
||||
|
||||
---
|
||||
|
||||
## Warnings — Requires Manual Action
|
||||
|
||||
### 5. CPU powersave mode
|
||||
|
||||
```
|
||||
[Warning] CPU performance profile is set to powersave. This profile sets the CPU to the
|
||||
lowest frequency reducing performance.
|
||||
```
|
||||
|
||||
**Severity:** Medium (影响整体性能)
|
||||
**Fix:** 需要 sudo 权限手动设置。
|
||||
|
||||
```bash
|
||||
# 临时切换到 performance 模式 (重启后恢复)
|
||||
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||
|
||||
# 永久设置 (Ubuntu)
|
||||
sudo apt install cpufrequtils
|
||||
echo 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtils
|
||||
sudo systemctl restart cpufrequtils
|
||||
```
|
||||
|
||||
**Status:** MANUAL. 需要用户自行设置。
|
||||
|
||||
---
|
||||
|
||||
## Warnings — Ignored (Safe)
|
||||
|
||||
### 6. IS 5.0 API deprecation warnings (~100 条)
|
||||
|
||||
```
|
||||
[Warning] omni.isaac.core has been deprecated in favor of isaacsim.core.api.
|
||||
Please update your code accordingly.
|
||||
```
|
||||
|
||||
**Severity:** None
|
||||
**说明:** IS 5.0 对所有 `omni.isaac.*` 模块重命名为 `isaacsim.*`。旧 API 仍然可用,仅打印 deprecation 提示。不影响功能,无需修改。如果将来升级到 IS 6.x 可能需要迁移。
|
||||
|
||||
---
|
||||
|
||||
### 7. Curobo warp cache warnings
|
||||
|
||||
```
|
||||
[Warning] [curobo] Object already in warp cache, using existing instance for:
|
||||
/World/task_0/pick_object_left/Aligned/mesh
|
||||
```
|
||||
|
||||
**Severity:** None
|
||||
**说明:** Curobo 内部 warp kernel 缓存机制。同一 mesh 在多次 world update 中被重复加载时复用缓存。完全正常,无害。
|
||||
|
||||
---
|
||||
|
||||
### 8. Robot dummy_base inertia tensor warnings
|
||||
|
||||
```
|
||||
[Warning] The rigid body at .../dummy_base_rotate has a possibly invalid inertia tensor of
|
||||
{1.0, 1.0, 1.0} and a negative mass, small sphere approximated inertia was used.
|
||||
```
|
||||
|
||||
**Severity:** None
|
||||
**说明:** Robot USD 中 `dummy_base_rotate/x/y` 是虚拟关节(用于全局位移),不需要真实质量属性。PhysX 自动使用球体近似惯性张量,功能正常。
|
||||
|
||||
---
|
||||
|
||||
### 9. Table mesh corrupted normal primvar
|
||||
|
||||
```
|
||||
[Warning] Mesh '/World/task_0/table/Instance/Group_default_00/SM_08_component6_0' has corrupted
|
||||
data in primvar 'normal': buffer size 14880 doesn't match expected size 2482
|
||||
```
|
||||
|
||||
**Severity:** Low
|
||||
**说明:** Table USD 中一个子网格的 normal 数据大小不匹配。IS 会忽略损坏的 normal 并自动计算。不影响渲染和物理。如需修复,需在 DCC 工具(Blender/Maya)中重新导出 table USD。
|
||||
|
||||
---
|
||||
|
||||
### 10. GPU / Hardware warnings
|
||||
|
||||
```
|
||||
[Warning] Skipping unsupported non-NVIDIA GPU: AMD Ryzen 7 9800X3D (RADV RAPHAEL_MENDOCINO)
|
||||
[Warning] ECC is enabled on physical device 0
|
||||
[Warning] IOMMU is enabled.
|
||||
```
|
||||
|
||||
**Severity:** None
|
||||
**说明:** 系统有 AMD 集显(跳过正常)。RTX PRO 6000 默认开启 ECC(牺牲约 6% 显存带宽换数据可靠性)。IOMMU 已启用。均为信息提示。
|
||||
|
||||
---
|
||||
|
||||
### 11. SdRenderVarPtr missing LdrColorSDhost
|
||||
|
||||
```
|
||||
[Warning] SdRenderVarPtr missing valid input renderVar LdrColorSDhost
|
||||
```
|
||||
|
||||
**Severity:** None
|
||||
**说明:** Synthetic data pipeline 中 LDR color 渲染变量在 headless 模式下首帧未就绪。后续帧正常。不影响输出。
|
||||
|
||||
---
|
||||
|
||||
### 12. trimesh sampling warning
|
||||
|
||||
```
|
||||
[Warning] [trimesh] only got 3/4 samples!
|
||||
```
|
||||
|
||||
**Severity:** Low
|
||||
**说明:** Grasp pose 采样时某个物体只获得 3 个(期望 4 个)抓取采样点。不影响功能,pipeline 会使用获得的采样继续运行。
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Count | Fixed | Ignored | Manual |
|
||||
|----------|-------|-------|---------|--------|
|
||||
| Errors | 2 | 1 | 1 | 0 |
|
||||
| Warnings (actionable) | 3 | 2 | 0 | 1 |
|
||||
| Warnings (safe to ignore) | 7 | 0 | 7 | 0 |
|
||||
| **Total** | **12** | **3** | **8** | **1** |
|
||||
70
migrate/scan_usd_metadata.py
Normal file
70
migrate/scan_usd_metadata.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
Scan all USD files in the project and report their metersPerUnit / upAxis metadata.
|
||||
|
||||
Usage:
|
||||
python migrate/scan_usd_metadata.py [--root DIR]
|
||||
|
||||
Default root: current working directory (project root).
|
||||
"""
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pxr import Usd, UsdGeom
|
||||
|
||||
|
||||
def scan(root: str, target_mpu: float = 1.0, target_up: str = "Z"):
|
||||
usd_files = glob.glob(os.path.join(root, "**", "*.usd"), recursive=True)
|
||||
usd_files = [f for f in usd_files if not f.endswith(".bak")]
|
||||
usd_files.sort()
|
||||
|
||||
ok_files = []
|
||||
diff_files = []
|
||||
error_files = []
|
||||
|
||||
for fpath in usd_files:
|
||||
try:
|
||||
stage = Usd.Stage.Open(fpath)
|
||||
if stage is None:
|
||||
error_files.append((fpath, "Cannot open stage"))
|
||||
continue
|
||||
mpu = UsdGeom.GetStageMetersPerUnit(stage)
|
||||
up = UsdGeom.GetStageUpAxis(stage)
|
||||
rel = os.path.relpath(fpath, root)
|
||||
if mpu == target_mpu and up == target_up:
|
||||
ok_files.append((rel, mpu, up))
|
||||
else:
|
||||
diff_files.append((rel, mpu, up))
|
||||
except Exception as e:
|
||||
error_files.append((fpath, str(e)))
|
||||
|
||||
# Report
|
||||
print(f"Scanned {len(ok_files) + len(diff_files) + len(error_files)} USD files under: {root}")
|
||||
print(f" OK (mpu={target_mpu}, up={target_up}): {len(ok_files)}")
|
||||
print(f" Different: {len(diff_files)}")
|
||||
print(f" Errors: {len(error_files)}")
|
||||
|
||||
if diff_files:
|
||||
print(f"\n{'='*80}")
|
||||
print(f"Files with non-standard metadata (mpu != {target_mpu} or up != {target_up}):")
|
||||
print(f"{'='*80}")
|
||||
for rel, mpu, up in diff_files:
|
||||
print(f" mpu={mpu:<8} up={up:<4} {rel}")
|
||||
|
||||
if error_files:
|
||||
print(f"\n{'='*80}")
|
||||
print("Files with errors:")
|
||||
print(f"{'='*80}")
|
||||
for fpath, err in error_files:
|
||||
print(f" ERROR: {fpath} -> {err}")
|
||||
|
||||
return diff_files
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Scan USD files for metersPerUnit/upAxis metadata")
|
||||
parser.add_argument("--root", default=os.getcwd(), help="Root directory to scan (default: cwd)")
|
||||
args = parser.parse_args()
|
||||
diff = scan(args.root)
|
||||
sys.exit(1 if diff else 0)
|
||||
Reference in New Issue
Block a user