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>
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
"""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'}")
|