Files
isaac/scripts/reverse_plow_mount.py
T
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

93 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Turn the plow round: pivot upstream, free end downstream at the discharge edge.
/home/whatevenif/isaacsim/python.sh scripts/reverse_plow_mount.py
Measured on the built scene, with goods travelling in **-X**:
pivot x = -7.050 <- DOWNSTREAM end
free tip x = -6.658 (at 42 deg) <- UPSTREAM end, y +0.353
That is a plough mounted backwards. A plough is an inclined plane: the belt drives the item
along the blade toward the blade's **downstream** end, and the item leaves there. With the
downstream end sitting at the pivot on the belt centreline (y = 0), goods are funnelled
*inward*, slip past the pivot and carry on down the line. They can never discharge.
It explains every symptom: the 0.39 m ceiling is the brief shove from the sweep, after
which the item slides back toward the centre; moving the lanes inboard changed nothing
because the lane edge was never what goods were failing to reach; and goods pile in the
wedge between the blade and the belt centre, which is what the viewport shows.
The fix is the mounting, not the length:
pivot x -7.050 -> -6.522 (upstream end of the same physical span)
arm extends -X instead of +X (rotateZ 180 -> 0)
so at 42 deg the free end lands near x -6.92, y +-0.353 - downstream of the pivot and out
at the discharge side. Goods now slide *outward and forward* along the blade.
**The swing sign flips with the mount.** `plow_sort.calibrate_mapping()` says to measure it
rather than reason about it; re-measure after running this.
"""
from __future__ import annotations
import math
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"
def main():
if not SCENE.exists():
sys.exit(f"{SCENE} not found")
stage = Usd.Stage.Open(str(SCENE))
prim = stage.GetPrimAtPath(DIVERTER)
if not prim.IsValid():
sys.exit(f"{DIVERTER} missing")
xc = UsdGeom.XformCache()
piv = xc.GetLocalToWorldTransform(stage.GetPrimAtPath(ARM)).ExtractTranslation()
r = UsdGeom.BBoxCache(0, ["default"]).ComputeWorldBound(
stage.GetPrimAtPath(ARM)).ComputeAlignedRange()
reach = r.GetMax()[0] - piv[0] # +0.528: arm points upstream today
print(f"before: pivot x={piv[0]:.3f}, arm reaches {reach:+.3f} m in X "
f"({'UPSTREAM - wrong way' if reach > 0 else 'downstream'})")
xf = UsdGeom.Xformable(prim)
moved = flipped = False
for op in xf.GetOrderedXformOps():
if op.GetOpType() == UsdGeom.XformOp.TypeTranslate and not moved:
t = op.Get()
op.Set(Gf.Vec3d(t[0] + reach, t[1], t[2])) # pivot to the upstream end
moved = True
elif op.GetOpType() == UsdGeom.XformOp.TypeRotateZ and not flipped:
op.Set(float((op.Get() or 0.0) + 180.0) % 360.0) # arm now points downstream
flipped = True
if not (moved and flipped):
sys.exit(f"{DIVERTER} needs both a translate and a rotateZ op "
f"(moved={moved}, flipped={flipped})")
stage.GetRootLayer().Save()
xc2 = UsdGeom.XformCache()
piv2 = xc2.GetLocalToWorldTransform(stage.GetPrimAtPath(ARM)).ExtractTranslation()
r2 = UsdGeom.BBoxCache(0, ["default"]).ComputeWorldBound(
stage.GetPrimAtPath(ARM)).ComputeAlignedRange()
L = abs(reach)
print(f"after : pivot x={piv2[0]:.3f}, arm spans x[{r2.GetMin()[0]:.3f}, "
f"{r2.GetMax()[0]:.3f}]")
for a in (0, 20, 42):
print(f" {a:2d} deg: free end x={piv2[0] - L * math.cos(math.radians(a)):+.3f} "
f"y={L * math.sin(math.radians(a)):+.3f} (downstream of pivot = correct)")
print(f"saved {SCENE}")
print("NOTE: the swing sign flips with the mount - re-measure calibrate_mapping()")
if __name__ == "__main__":
main()