0d32f32db0
Замкнутый контур "поток -> CV -> механика": товары идут по конвейеру с шагом 700 мм, класс определяется стереопайплайном во время движения, пушер и плуг реагируют физически. Состав: * control_test/ - ячейка и CV. run_sorting_cv.py + cv_worker.py (два процесса, потому что torch внутри Isaac роняет сцену), cell.py (физика лент, плуга, пушера), measure_plane.py (замер габаритов), README.md и .memory.md с замерами, проблемами и ловушками * robozon_sorter/ - модули симуляции, scripts/ - утилиты, scene/ - сцены * assets/ - меши товаров, плуг, объекты Objaverse Бейзлайн CV: DEFOM-Stereo vitl, вход 480, iters 24, кроп зоны осмотра, без сегментации. На потоке 700 мм - классы 8/9, габариты MAE 32.8 мм, 469 мс на товар при такте 700 мс. Веса моделей (4.5 ГБ) и пропсы конвейера NVIDIA (274 МБ) не включены - источники и команды скачивания в MODELS.md. Выход прогонов (captures/, runtime/) не включён: воспроизводится. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
3.4 KiB
Python
Executable File
84 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Fetch the conveyor art that scene/sorter.usd references.
|
|
|
|
python scripts/fetch_assets.py
|
|
|
|
~270 MB, too big for git. Downloads in resumable Range chunks: the Omniverse bucket is
|
|
slow enough that a plain GET truncates, and a half-written USD fails to compose with a
|
|
confusing "could not open asset" rather than an obvious size error.
|
|
"""
|
|
from __future__ import annotations
|
|
import os, sys, time, urllib.request
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
DEST = ROOT / "assets" / "conveyors"
|
|
BASE = ("https://omniverse-content-production.s3-us-west-2.amazonaws.com/"
|
|
"Assets/Isaac/6.0/Isaac/Props/Conveyors/")
|
|
CHUNK, TRIES = 1 << 21, 12
|
|
|
|
# the scene composes these; the Textures/ and Material Library/ folders come with them
|
|
FILES = ["ConveyorBelt_A06.usd", "ConveyorBelt_A24.usd"]
|
|
TEXTURES = [
|
|
"Textures/M_ConveyorBelt_A01_Belt.usd", "Textures/M_ConveyorBelt_A01_Decal.usd",
|
|
"Textures/MetalPainted_Blue_Glossy_A.usd", "Textures/Plastic_Orange_A.usd",
|
|
"Textures/Plastic_Rough_Black_A.usd", "Textures/Steel_A.usd",
|
|
"Textures/T_ConveyorBelt_A01_Belt_Albedo.png", "Textures/T_ConveyorBelt_A01_Belt_Normal.png",
|
|
"Textures/T_ConveyorBelt_A01_Belt_ORM.png",
|
|
"Textures/T_ConveyorsBelt_A01_Decal_Albedo.png", "Textures/T_ConveyorsBelt_A01_Decal_Alpha.png",
|
|
"Textures/T_ConveyorsBelt_A01_Decal_ORM.png",
|
|
"Material%20Library/physics_material.usd",
|
|
]
|
|
|
|
def size_of(url):
|
|
try:
|
|
req = urllib.request.Request(url, method="HEAD")
|
|
with urllib.request.urlopen(req, timeout=60) as r:
|
|
return int(r.headers.get("Content-Length", 0))
|
|
except Exception:
|
|
return 0
|
|
|
|
def grab(url, out: Path):
|
|
total = size_of(url)
|
|
have = out.stat().st_size if out.exists() else 0
|
|
if total and have == total:
|
|
print(f" {out.name}: present"); return True
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
if not total:
|
|
urllib.request.urlretrieve(url, out); return out.exists()
|
|
pos = have if have < total else 0
|
|
with open(out, "r+b" if pos else "wb") as fh:
|
|
fh.seek(pos); t0 = time.time()
|
|
while pos < total:
|
|
end = min(pos + CHUNK - 1, total - 1)
|
|
for a in range(TRIES):
|
|
try:
|
|
req = urllib.request.Request(url)
|
|
req.add_header("Range", f"bytes={pos}-{end}")
|
|
with urllib.request.urlopen(req, timeout=120) as r:
|
|
data = r.read()
|
|
if len(data) != end - pos + 1:
|
|
raise IOError("short chunk")
|
|
fh.write(data); fh.flush(); pos += len(data)
|
|
print(f"\r {out.name}: {100.0*pos/total:5.1f}%"
|
|
f" {(pos-have)/max(time.time()-t0,1e-6)/1024:6.0f} KB/s", end="", flush=True)
|
|
break
|
|
except Exception as exc:
|
|
if a == TRIES - 1:
|
|
print(f"\n {out.name}: FAILED at {pos}: {exc}"); return False
|
|
time.sleep(min(2 ** a, 20))
|
|
print()
|
|
return out.stat().st_size == total
|
|
|
|
def main():
|
|
print(f"fetching conveyor art into {DEST}")
|
|
ok = True
|
|
for rel in FILES + TEXTURES:
|
|
local = DEST / rel.replace("%20", " ")
|
|
ok &= grab(BASE + rel, local)
|
|
print("\nall present" if ok else "\nincomplete - re-run, downloads resume")
|
|
return 0 if ok else 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|