Сортировочная ячейка 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
+52
View File
@@ -0,0 +1,52 @@
"""Вернуть перспективу вьюпорта на обзор ячейки.
Сдвинута оказалась только служебная камера /OmniverseKit_Persp - она уехала в
(-8.96, -5.49, -6.70), то есть ПОД пол, и смотрела снизу. Шесть стереокамер стенда в
/RigRS не тронуты (0.0 мм, 0.00°), их править нечего - а именно они участвуют в прогонах.
Перспектива вьюпорта в файл не пишется и на физику с замерами не влияет: это только то,
что видит человек. Поэтому здесь она ставится по фактическому габариту ячейки, а не по
запомненному числу - сцена менялась, и старая точка могла бы снова смотреть мимо.
"""
import sys
REPO = "/home/dasha/robozon-sorter"
if REPO not in sys.path:
sys.path.insert(0, REPO)
import omni.usd
import isaacsim.core.experimental.utils.app as app_utils
from isaacsim.core.rendering_manager import ViewportManager
from pxr import Usd, UsdGeom, Gf
stage = omni.usd.get_context().get_stage()
bb = UsdGeom.BBoxCache(Usd.TimeCode.Default(), [UsdGeom.Tokens.default_, UsdGeom.Tokens.render])
# габарит по дорожкам и плугу - то, на что осмысленно смотреть
lo = Gf.Vec3d(1e9, 1e9, 1e9); hi = Gf.Vec3d(-1e9, -1e9, -1e9)
for p in stage.Traverse():
n = p.GetName()
if not (n.startswith("ConveyorTrack") or n == "Diverters"):
continue
r = bb.ComputeWorldBound(p).ComputeAlignedRange()
a, b = r.GetMin(), r.GetMax()
for i in range(3):
lo[i] = min(lo[i], a[i]); hi[i] = max(hi[i], b[i])
ctr = Gf.Vec3d((lo[0]+hi[0])/2, (lo[1]+hi[1])/2, (lo[2]+hi[2])/2)
span = max(hi[0]-lo[0], hi[1]-lo[1])
print(f"габарит ячейки: x {lo[0]:+.2f}..{hi[0]:+.2f} y {lo[1]:+.2f}..{hi[1]:+.2f} "
f"z {lo[2]:+.2f}..{hi[2]:+.2f}")
print(f"центр ({ctr[0]:+.2f}, {ctr[1]:+.2f}, {ctr[2]:+.2f}), протяжённость {span:.1f} м")
# три четверти сверху-сбоку: вся линия в кадре, плуг и пушер видны не с торца
eye = Gf.Vec3d(ctr[0] + span * 0.45, ctr[1] - span * 0.65, ctr[2] + span * 0.55)
tgt = Gf.Vec3d(ctr[0], ctr[1], 1.60)
ViewportManager.set_camera_view("/OmniverseKit_Persp",
eye=[eye[0], eye[1], eye[2]],
target=[tgt[0], tgt[1], tgt[2]])
await app_utils.update_app_async(steps=30)
cache = UsdGeom.XformCache()
t = cache.GetLocalToWorldTransform(stage.GetPrimAtPath("/OmniverseKit_Persp")).ExtractTranslation()
print(f"\nперспектива возвращена: ({t[0]:+.2f}, {t[1]:+.2f}, {t[2]:+.2f}) "
f"-> смотрит на ({tgt[0]:+.2f}, {tgt[1]:+.2f}, {tgt[2]:+.2f})")
print("камеры стенда /RigRS не трогались")