Сортировочная ячейка Isaac Sim: CV-пайплайн и меши товаров

Замкнутый контур "поток -> 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>
This commit is contained in:
dasha_f
2026-08-01 13:07:24 +00:00
parent 6ce460378a
commit 0d32f32db0
342 changed files with 18000 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
"""Smoke test for scene/plow_cell.usd: load the cell, run the belts, swing the plow under
script control and confirm the arm physically follows.
Run it inside a live Isaac Sim through the code editor's python server, e.g.
python isaacsim_send.py --context plow --file scripts/smoke_plow_cell.py
It asserts the things that were silent failures during the transfer: that the referenced
plow geometry actually composed (a stub resolves to 8 points, the real base to 72k), that
the belts got a surface velocity, and that commanding the drive moves the arm rather than
just setting an attribute nothing reads.
"""
import sys
REPO = "/home/dasha/robozon-sorter"
if REPO not in sys.path:
sys.path.insert(0, REPO)
import omni.timeline
import isaacsim.core.experimental.utils.app as app_utils
from pxr import PhysxSchema, UsdGeom
from robozon_sorter import config as C
from robozon_sorter.sim import plow_cell
from robozon_sorter.sim.plow import Plow
stage, info = plow_cell.load(script_control=True)
print("loaded:", info)
# --- geometry actually composed? --------------------------------------------------------
for path, label, floor in ((C.PLOW_BASE + "/Geom/Mesh", "plow base", 1000),
(C.PLOW_ARM + "/Geom/Mesh", "plow arm", 1000)):
pts = UsdGeom.Mesh(stage.GetPrimAtPath(path)).GetPointsAttr().Get()
n = len(pts) if pts else 0
print(f" {label:10s} {n:>7} points {'OK' if n > floor else 'FAIL (stub or missing)'}")
# --- belts driven? ----------------------------------------------------------------------
driven = 0
for b in plow_cell.BELTS + [plow_cell.BRANCH]:
p = stage.GetPrimAtPath(b)
if p.IsValid() and p.HasAPI(PhysxSchema.PhysxSurfaceVelocityAPI):
v = PhysxSchema.PhysxSurfaceVelocityAPI(p).GetSurfaceVelocityAttr().Get()
if v and any(abs(c) > 1e-6 for c in v):
driven += 1
print(f" belts driven: {driven}/{len(plow_cell.BELTS) + 1}")
# --- plow moves? ------------------------------------------------------------------------
tl = omni.timeline.get_timeline_interface()
tl.play()
await app_utils.update_app_async(steps=30)
plow = Plow(stage)
rest = plow.angle
print(f" rest angle {rest:+.2f} deg")
await plow.swing(app_utils, C.PLOW_SWING)
out = plow.angle
print(f" swung to {out:+.2f} deg (commanded {C.PLOW_SWING:+.1f})")
await plow.swing(app_utils, 0.0)
back = plow.angle
print(f" returned {back:+.2f} deg")
tl.stop()
moved = abs(out - rest) > 0.5 * C.PLOW_SWING
homed = abs(back) < 5.0
print(f"RESULT: arm moved={moved} returned_home={homed} "
f"{'PASS' if moved and homed else 'FAIL'}")