Files
dasha_f 0d32f32db0 Сортировочная ячейка 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>
2026-08-01 13:07:24 +00:00

86 lines
3.5 KiB
Python

"""Item library for control_test: meshes are DISCOVERED in items/, classes are READ from
items/labels.json.
No geometric auto-measurement. Dimensions and k are taken from the labelling, which is the
ground truth this cell is verified against - measuring them from the mesh was tried and
the roundness estimate under-read handled/hollow bodies (bucket 0.737 vs 0.995, mug 0.731
vs 0.985), i.e. class D silently became B. Reading the label removes that whole class of
error from the mechanics test.
The folder is still the source of items: drop a .usd in, add one line to labels.json, and
it joins the next run. Anything in the folder without a label is reported and skipped
rather than guessed at.
The documented rules are kept in `classify()` so a labelling can be checked for internal
consistency (`verify_labels()`), not to derive it:
D "не подходит без доупаковки" габариты как у B, но k > 0.8 хотя бы в одном сечении
C "не подходит по габаритам" любой размер < 10 мм ИЛИ не влезает в 450x320x320 мм.
Форма не важна.
B "подходит для сортировки" всё от 10x10x10 до 450x320x320 мм и k <= 0.8
Fit is tested with the item's extents and the envelope both sorted descending - a parcel
may be presented on any face.
"""
from __future__ import annotations
import json
import pathlib
ENVELOPE_MM = sorted((450.0, 320.0, 320.0), reverse=True)
MIN_DIM_MM = 10.0
K_THRESHOLD = 0.8
LABELS_FILE = "labels.json"
def classify(dims_mm, k):
"""the documented decision, D checked first"""
d = sorted(dims_mm, reverse=True)
undersize = min(d) < MIN_DIM_MM
fits = all(a <= b + 1e-9 for a, b in zip(d, ENVELOPE_MM))
if undersize or not fits:
return "C" # shape irrelevant
return "D" if k > K_THRESHOLD else "B"
def load_library(items_dir):
"""every .usd in items_dir, sorted, paired with its label.
Returns dicts with name/path/cls/dims_mm/k, or name/path/error for meshes that have
no entry in labels.json - those are skipped by the runner, never guessed.
"""
items_dir = pathlib.Path(items_dir)
labels_path = items_dir / LABELS_FILE
if not labels_path.exists():
raise FileNotFoundError(f"{labels_path} missing - the item classes live there")
labels = json.loads(labels_path.read_text())
out = []
for f in sorted(items_dir.glob("*.usd")):
rec = labels.get(f.stem)
if rec is None:
out.append(dict(name=f.stem, path=str(f),
error="no entry in labels.json"))
continue
out.append(dict(name=f.stem, path=str(f), cls=rec["zone"],
dims_mm=rec.get("dims_mm"), k=rec.get("k")))
return out
def verify_labels(items_dir):
"""check each label against the documented rules; returns the rows that disagree.
A label whose own dims/k imply a different class is a labelling bug, and it would
otherwise show up as a mysterious mechanical failure.
"""
bad = []
for r in load_library(items_dir):
if "error" in r or r.get("dims_mm") is None or r.get("k") is None:
continue
implied = classify(r["dims_mm"], r["k"])
if implied != r["cls"]:
bad.append(dict(name=r["name"], labelled=r["cls"], implied=implied,
dims_mm=r["dims_mm"], k=r["k"]))
return bad