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>
113 lines
4.7 KiB
Python
113 lines
4.7 KiB
Python
"""SUPERSEDED - kept for history only.
|
|
|
|
SUPERSEDED by capture_roi.py. This version composes the item reference
|
|
onto the prim whose xformOpOrder it then clears, which destroys the mesh's own
|
|
placement and buries every item 0.6 m under the belt, and it never clears the
|
|
authored visibility=invisible the item layers carry. Both failures are silent:
|
|
the captures look like a normal empty belt. Do not use.
|
|
"""
|
|
|
|
"""STAGE 1 (inside Isaac, no torch): render a rectified L/R pair from every rig for every
|
|
item, for one named camera configuration.
|
|
|
|
Items are parked kinematic at the inspection point so the pair is perfectly consistent -
|
|
a moving object between the two eyes would fake a disparity that is not there.
|
|
"""
|
|
import json, os, sys
|
|
REPO = "/home/dasha/robozon-sorter"
|
|
for e in (REPO, f"{REPO}/control_test"):
|
|
if e not in sys.path:
|
|
sys.path.insert(0, e)
|
|
for _m in [k for k in list(sys.modules) if k.startswith(("cam_configs", "classify", "cell"))]:
|
|
del sys.modules[_m]
|
|
import importlib; importlib.invalidate_caches()
|
|
|
|
import asyncio
|
|
import omni.usd, omni.timeline
|
|
import omni.kit.viewport.utility as vp
|
|
import isaacsim.core.experimental.utils.app as app_utils
|
|
from pxr import Gf, Usd, UsdGeom, UsdLux, UsdPhysics
|
|
|
|
import cam_configs as CC
|
|
import classify as CL
|
|
|
|
CFG = globals().get("cfg", "A_original")
|
|
ITEMS = globals().get("items", ["bag", "backpack", "lunchbox", "helmet", "pillow",
|
|
"detergent", "bucket", "box_400x400x300", "box_300x200x200"])
|
|
OUT = f"/home/dasha/robozon-sorter/control_test/captures/{CFG}"
|
|
os.makedirs(OUT, exist_ok=True)
|
|
|
|
stage = omni.usd.get_context().get_stage()
|
|
tl = omni.timeline.get_timeline_interface()
|
|
if tl.is_playing():
|
|
tl.stop(); await app_utils.update_app_async(steps=10)
|
|
|
|
calib = CC.apply_config(stage, CFG)
|
|
print(f"config {CFG}: {len(calib)} cameras placed at {CC.STANDOFF*1000:.0f} mm")
|
|
|
|
# side views look at shadowed faces, so light the cell from several directions - the
|
|
# earlier study found texture matters far more than brightness, but a black frame has
|
|
# neither
|
|
for i, (nm, pos) in enumerate((("K0", (-0.75, 1.6, 2.6)), ("K1", (-0.75, -1.6, 2.6)),
|
|
("K2", (0.6, 0.0, 2.6)), ("K3", (-2.1, 0.0, 2.6)))):
|
|
p = f"/World/_CapLight_{nm}"
|
|
if not stage.GetPrimAtPath(p).IsValid():
|
|
sl = UsdLux.SphereLight.Define(stage, p)
|
|
sl.CreateRadiusAttr().Set(0.25)
|
|
sl.CreateIntensityAttr().Set(90000.0)
|
|
UsdGeom.Xformable(sl.GetPrim()).AddTranslateOp().Set(Gf.Vec3d(*pos))
|
|
dome = stage.GetPrimAtPath("/Environment/_BrightFill")
|
|
if dome.IsValid():
|
|
dome.GetAttribute("inputs:intensity").Set(3500.0)
|
|
|
|
lib = {r["name"]: r for r in CL.load_library(f"{REPO}/control_test/items") if "error" not in r}
|
|
ROOT = "/World/CapItems"
|
|
UsdGeom.Xform.Define(stage, ROOT)
|
|
|
|
def spawn(name):
|
|
path = f"{ROOT}/{name}"
|
|
if stage.GetPrimAtPath(path).IsValid():
|
|
stage.RemovePrim(path)
|
|
prim = UsdGeom.Xform.Define(stage, path).GetPrim()
|
|
prim.GetReferences().AddReference(lib[name]["path"])
|
|
# sit it ON the belt at the inspection point
|
|
bb = UsdGeom.BBoxCache(Usd.TimeCode.Default(), [UsdGeom.Tokens.default_, UsdGeom.Tokens.render])
|
|
r = bb.ComputeWorldBound(prim).ComputeAlignedRange()
|
|
drop = r.GetMin()[2]
|
|
xf = UsdGeom.Xformable(prim); xf.ClearXformOpOrder()
|
|
xf.AddTranslateOp(precision=UsdGeom.XformOp.PrecisionDouble).Set(
|
|
Gf.Vec3d(CC.TARGET[0], CC.TARGET[1], CC.TARGET[2] - drop + 0.001))
|
|
UsdGeom.Imageable(prim).MakeVisible()
|
|
return prim
|
|
|
|
w = vp.get_active_viewport()
|
|
orig_cam = w.camera_path
|
|
manifest = {"config": CFG, "target": [float(v) for v in CC.TARGET],
|
|
"standoff_m": CC.STANDOFF, "calib": calib, "items": {}}
|
|
|
|
for name in ITEMS:
|
|
if name not in lib:
|
|
print(f" skip {name} (not in library)"); continue
|
|
prim = spawn(name)
|
|
await app_utils.update_app_async(steps=20)
|
|
files = {}
|
|
for cam_name in calib:
|
|
w.camera_path = f"/RigRS/{cam_name}" if stage.GetPrimAtPath(f"/RigRS/{cam_name}").IsValid() \
|
|
else CC._cam(stage, cam_name).GetPath()
|
|
await app_utils.update_app_async(steps=22)
|
|
await asyncio.sleep(0)
|
|
f = f"{OUT}/{name}__{cam_name}.png"
|
|
vp.capture_viewport_to_file(w, file_path=f)
|
|
await app_utils.update_app_async(steps=12)
|
|
await asyncio.sleep(0)
|
|
files[cam_name] = f
|
|
manifest["items"][name] = dict(files=files, gt=dict(
|
|
cls=lib[name]["cls"], dims_mm=lib[name]["dims_mm"], k=lib[name]["k"]))
|
|
print(f" {name}: {len(files)} views")
|
|
UsdGeom.Imageable(prim).MakeInvisible()
|
|
|
|
w.camera_path = orig_cam
|
|
await app_utils.update_app_async(steps=10)
|
|
json.dump(manifest, open(f"{OUT}/manifest.json", "w"), indent=1)
|
|
print(f"\nmanifest -> {OUT}/manifest.json")
|