Сортировочная ячейка 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
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Drop the plow so its blade sweeps at belt level instead of over the goods.
/home/whatevenif/isaacsim/python.sh scripts/lower_plow_to_belt.py
Measured on the built scene:
blade z 1.810 .. 1.890
belt top z 1.781
gap under the blade = 29 mm
29 mm is enough for flat goods to pass straight under the blade, and tall ones get caught
near their top edge and tipped rather than led across. It is the reason items reached only
y ~ 0.24 while the arm was correctly holding 42 deg with 402 mm of reach: the blade was not
touching them at all, so no amount of angle or lane geometry could have helped.
The whole ``DiverterEnd`` is moved, not just the arm: base and arm keep their relative
placement, so the hinge stays consistent whether the arm is driven kinematically or by its
joint. The pedestal sinks the same 27 mm, which is invisible - it stands on the floor.
Target clearance is 2 mm: enough that the blade is not grinding on the belt collider,
little enough that nothing rides under it.
"""
from __future__ import annotations
import sys
from pathlib import Path
from pxr import Gf, Usd, UsdGeom
SCENE = Path(__file__).resolve().parent.parent / "scene" / "plow_cell.usd"
DIVERTER = "/World/Diverters/DiverterEnd"
ARM = DIVERTER + "/Arm"
BELT = "/World/ConveyorTrack_04/Belt"
CLEARANCE = 0.002
def _range(stage, path):
return UsdGeom.BBoxCache(0, ["default"]).ComputeWorldBound(
stage.GetPrimAtPath(path)).ComputeAlignedRange()
def main():
if not SCENE.exists():
sys.exit(f"{SCENE} not found")
stage = Usd.Stage.Open(str(SCENE))
arm = _range(stage, ARM)
belt = _range(stage, BELT)
if arm.IsEmpty() or belt.IsEmpty():
sys.exit("arm or belt has no bounds - wrong scene?")
blade_bottom, belt_top = arm.GetMin()[2], belt.GetMax()[2]
gap = blade_bottom - belt_top
drop = gap - CLEARANCE
print(f"blade bottom z {blade_bottom:.4f} | belt top z {belt_top:.4f}")
print(f"gap {gap * 1000:.0f} mm -> target {CLEARANCE * 1000:.0f} mm, dropping {drop * 1000:.0f} mm")
if abs(drop) < 1e-4:
print("already at height, nothing to do")
return
prim = stage.GetPrimAtPath(DIVERTER)
xf = UsdGeom.Xformable(prim)
for op in xf.GetOrderedXformOps():
if op.GetOpType() == UsdGeom.XformOp.TypeTranslate:
t = op.Get()
op.Set(Gf.Vec3d(t[0], t[1], t[2] - drop))
break
else:
sys.exit(f"{DIVERTER} has no translate op to move")
stage.GetRootLayer().Save()
after = _range(stage, ARM)
print(f"blade now z[{after.GetMin()[2]:.4f}, {after.GetMax()[2]:.4f}] "
f"-> clearance {(after.GetMin()[2] - belt_top) * 1000:.0f} mm")
print(f"saved {SCENE}")
if __name__ == "__main__":
main()