Сортировочная ячейка 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:
@@ -0,0 +1,86 @@
|
||||
"""Self-running plow demo for watching over WebRTC.
|
||||
|
||||
Sent into the live streaming Kit. Unlike the test harness this does **not** block in a
|
||||
loop: it hooks the feeder and the plow onto the physics step, aims the viewport at the plow,
|
||||
presses Play and returns. The cell then runs on its own for as long as the session lives,
|
||||
which is what makes it watchable in the browser - a blocking script would hold the
|
||||
interpreter and the stream would show a frozen frame.
|
||||
|
||||
The subscriptions are stashed in the module namespace on purpose. A PhysX step
|
||||
subscription dies the moment its Python handle is garbage-collected, so a demo that forgets
|
||||
to keep a reference stops after the call returns and looks like the scene simply ignoring
|
||||
the Play button.
|
||||
"""
|
||||
import sys
|
||||
|
||||
REPO = "/home/dasha/robozon-sorter"
|
||||
if REPO not in sys.path:
|
||||
sys.path.insert(0, REPO)
|
||||
import importlib
|
||||
for _m in [k for k in list(sys.modules) if k.startswith("robozon_sorter")]:
|
||||
del sys.modules[_m]
|
||||
importlib.invalidate_caches()
|
||||
|
||||
import omni.timeline
|
||||
import isaacsim.core.experimental.utils.app as app_utils
|
||||
from isaacsim.core.rendering_manager import ViewportManager
|
||||
|
||||
from robozon_sorter import config as C
|
||||
from robozon_sorter.sim import lane_beams, plow_sort, plow_vision, staging
|
||||
from robozon_sorter.sim.mechanics import Cell
|
||||
from robozon_sorter.sim.spawner import AutoFeeder
|
||||
|
||||
SPEED = float(globals().get("speed", 0.8))
|
||||
PITCH = float(globals().get("pitch", 3.0))
|
||||
RATE = float(globals().get("rate", 300.0))
|
||||
N = int(globals().get("n", 8))
|
||||
|
||||
C.BELT_SPEED = SPEED
|
||||
C.PLOW_SWEEP_RATE = RATE
|
||||
|
||||
stage, info = plow_vision.load(belt_speed=SPEED, script_control=True,
|
||||
meshes_dir=f"{REPO}/assets/items")
|
||||
staging.stage_cell(stage, preset="bright", floor=True)
|
||||
plow_sort.keep_lanes_active(stage)
|
||||
lanes = plow_sort.configure_lanes(stage, SPEED)
|
||||
plow_sort.open_junction(stage)
|
||||
items = {k: v["zone"] for k, v in info["items"].items()}
|
||||
|
||||
await app_utils.update_app_async(steps=40)
|
||||
cell = Cell(stage, items.keys())
|
||||
cell.park_all()
|
||||
await app_utils.update_app_async(steps=15)
|
||||
|
||||
order = ([n for n in sorted(items) if items[n] == "B"][:3]
|
||||
+ [n for n in sorted(items) if items[n] == "C"][:3]
|
||||
+ [n for n in sorted(items) if items[n] == "D"][:2])[:N]
|
||||
sorter = plow_sort.PlowSorter(stage, cell, items, plow_sort.calibrate_mapping())
|
||||
beams = lane_beams.LaneBeams(stage, cell, plow=sorter.plow)
|
||||
|
||||
|
||||
def _step(dt):
|
||||
try:
|
||||
sorter.update(dt)
|
||||
beams.tick(dt)
|
||||
beams.poll(rate=RATE)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
from omni.physx import get_physx_interface
|
||||
# keep the handles alive in the namespace or the callbacks are collected and the cell stops
|
||||
STEP_SUB = get_physx_interface().subscribe_physics_step_events(_step)
|
||||
FEEDER = AutoFeeder(cell, order=order, pitch=PITCH, route=dict(items), loop=True).install()
|
||||
|
||||
# look at the plow from the discharge side so the sweep and both lanes are in frame
|
||||
ViewportManager.set_camera_view("/OmniverseKit_Persp",
|
||||
eye=[-5.2, -3.4, 3.2], target=[-7.0, 0.0, 1.9])
|
||||
await app_utils.update_app_async(steps=20)
|
||||
app_utils.play(commit=True)
|
||||
await app_utils.update_app_async(steps=20)
|
||||
|
||||
print(f"LIVE: {len(order)} items looping, pitch {PITCH} m @ {SPEED} m/s, "
|
||||
f"sweep {RATE} deg/s (tip {C.PLOW_ARM_LEN * RATE * 3.14159 / 180:.2f} m/s)")
|
||||
print(f"order: {order}")
|
||||
print(f"lanes/decks driven: {len(lanes)} | mapping {sorter.mapping}")
|
||||
print("running on the physics step - the stream stays live, nothing is blocking")
|
||||
Reference in New Issue
Block a user