Сортировочная ячейка 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,98 @@
|
||||
"""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}")
|
||||
Reference in New Issue
Block a user