0d32f32db0
Замкнутый контур "поток -> 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>
99 lines
4.0 KiB
Python
99 lines
4.0 KiB
Python
"""SUPERSEDED - kept for history only.
|
|
|
|
SUPERSEDED by cam_configs.apply_config(stage, cam_configs.DEFAULT).
|
|
Hard-codes the old 600 mm standoff.
|
|
"""
|
|
|
|
"""Reposition the camera rigs:
|
|
* RealSense D435: Left and Right on OPPOSITE sides of the belt, facing each other.
|
|
* All three rigs: working distance exactly 600 mm to the inspection point.
|
|
Gemini 305/345 keep their azimuth and stay rectified (shared orientation, right eye
|
|
offset along the camera's local +X by the baseline).
|
|
"""
|
|
import json, math
|
|
import omni.usd
|
|
from pxr import Gf, Usd, UsdGeom
|
|
|
|
stage = omni.usd.get_context().get_stage()
|
|
TARGET = Gf.Vec3d(-0.750, 0.0, 1.781) # inspection point on the belt
|
|
STANDOFF = 0.60 # requested working distance, metres
|
|
SIDE_ELEV_DEG = 20.0 # D435 sits low, at the side
|
|
UP = Gf.Vec3d(0, 0, 1)
|
|
|
|
def find(name):
|
|
for p in stage.Traverse():
|
|
if p.IsA(UsdGeom.Camera) and p.GetName() == name:
|
|
return p
|
|
raise KeyError(name)
|
|
|
|
def look_at_matrix(pos, target):
|
|
"""USD camera convention: looks down local -Z, local +Y is up."""
|
|
fwd = (target - pos)
|
|
fwd = fwd / (fwd.GetLength() or 1.0)
|
|
zax = -fwd
|
|
up = UP
|
|
if abs(Gf.Dot(up, zax)) > 0.999:
|
|
up = Gf.Vec3d(0, 1, 0)
|
|
xax = Gf.Cross(up, zax); xax = xax / (xax.GetLength() or 1.0)
|
|
yax = Gf.Cross(zax, xax)
|
|
M = Gf.Matrix4d(1.0)
|
|
M.SetRow3(0, xax); M.SetRow3(1, yax); M.SetRow3(2, zax)
|
|
M.SetTranslateOnly(pos)
|
|
return M, xax, fwd
|
|
|
|
def set_pose(prim, M):
|
|
xf = UsdGeom.Xformable(prim)
|
|
xf.ClearXformOpOrder()
|
|
xf.AddTransformOp().Set(M)
|
|
|
|
def current_dir_from_target(prim):
|
|
p = UsdGeom.XformCache().GetLocalToWorldTransform(prim).ExtractTranslation()
|
|
d = Gf.Vec3d(p) - TARGET
|
|
return d / (d.GetLength() or 1.0)
|
|
|
|
report = {}
|
|
|
|
# ---- D435: opposing pair, one each side, both aimed at the belt --------------------
|
|
th = math.radians(SIDE_ELEV_DEG)
|
|
for side_name, sgn in (("Left", +1.0), ("Right", -1.0)):
|
|
prim = find(f"RealSense_D435_{side_name}")
|
|
pos = TARGET + Gf.Vec3d(0.0, sgn * STANDOFF * math.cos(th), STANDOFF * math.sin(th))
|
|
M, _, fwd = look_at_matrix(pos, TARGET)
|
|
set_pose(prim, M)
|
|
report[f"RealSense_D435_{side_name}"] = dict(
|
|
pos=[round(v, 4) for v in pos], fwd=[round(v, 4) for v in fwd],
|
|
dist_mm=round((TARGET - pos).GetLength() * 1000, 1))
|
|
|
|
# ---- Gemini pairs: same azimuth, distance forced to 600 mm, stay RECTIFIED ---------
|
|
for rig, baseline_m in (("Orbbec_Gemini305", 0.0265), ("Orbbec_Gemini345", 0.1294)):
|
|
left, right = find(f"{rig}_Left"), find(f"{rig}_Right")
|
|
# keep the direction the pair currently views from, measured at its midpoint
|
|
xc = UsdGeom.XformCache()
|
|
pl = Gf.Vec3d(xc.GetLocalToWorldTransform(left).ExtractTranslation())
|
|
pr = Gf.Vec3d(xc.GetLocalToWorldTransform(right).ExtractTranslation())
|
|
mid = (pl + pr) * 0.5
|
|
d = mid - TARGET
|
|
d = d / (d.GetLength() or 1.0)
|
|
centre = TARGET + d * STANDOFF
|
|
M, xax, fwd = look_at_matrix(centre, TARGET)
|
|
# both eyes share ONE orientation - that is what keeps the pair rectified; the right
|
|
# eye is displaced along the camera's own +X by the baseline
|
|
for prim, off in ((left, -baseline_m / 2.0), (right, +baseline_m / 2.0)):
|
|
Mi = Gf.Matrix4d(M)
|
|
Mi.SetTranslateOnly(centre + xax * off)
|
|
set_pose(prim, Mi)
|
|
report[prim.GetName()] = dict(
|
|
pos=[round(v, 4) for v in (centre + xax * off)],
|
|
fwd=[round(v, 4) for v in fwd],
|
|
dist_mm=round((TARGET - (centre + xax * off)).GetLength() * 1000, 1))
|
|
|
|
print(f"{'camera':>26} {'position':>26} {'dist to belt':>13}")
|
|
for k, v in report.items():
|
|
print(f"{k:>26} ({v['pos'][0]:+6.3f},{v['pos'][1]:+6.3f},{v['pos'][2]:+6.3f}) {v['dist_mm']:>10.1f} mm")
|
|
|
|
out = "/home/dasha/robozon-sorter/control_test/calib_rs_side.json"
|
|
json.dump(dict(target=[round(v, 4) for v in TARGET], standoff_m=STANDOFF,
|
|
side_elevation_deg=SIDE_ELEV_DEG, cameras=report),
|
|
open(out, "w"), indent=2)
|
|
print(f"\nextrinsics -> {out}")
|