hello, robot

팀 프로젝트에서 매니퓰레이션 파트를 맡게 됐어요. 요구사항은 한 줄이에요. 탑뷰 화면에서 물체를 클릭하면 로봇 팔이 그 물체를 집는 것.

처음 들으면 비전 AI가 있어야 하는 큰 문제처럼 보이는데, 회의에서 뜯어보니 카메라와 로봇의 위치 관계가 고정이라면 수학만으로 닫히는 문제였어요.

입력탑뷰 클릭 (u, v) 센서몸체 고정 depth 카메라 (eye-to-hand) 계산핀홀 역투영 + 좌표변환 출력grip(x,y,z) · place(x,y,z)

픽셀 하나로는 위치를 모릅니다

사진 속 한 점은 실제 공간의 한 점이 아니에요. 픽셀 하나에 대응하는 실제 위치는 렌즈 중심에서 그 픽셀 방향으로 뻗는 반직선 위 어디든 될 수 있어요. 가까운 작은 물체와 멀리 있는 큰 물체가 같은 픽셀에 찍히니까요.

역투영 — 2D 픽셀을 3D 좌표로 되돌리기
depth 카메라가 픽셀마다 거리를 알려주니 반직선 위에서 정확히 한 점이 정해져요.
렌즈 중심이미지 평면(u, v)가까운 작은 물체멀리 있는 큰 물체픽셀 하나는 이 반직선 위 어디든 될 수 있어요depth Z 가 정해 주는 한 점X = (u − cx) × Z / fxY = (v − cy) × Z / fyZ = depth(u, v)사진 속 한 점은 실제 공간의 한 점이 아니에요. depth가 그 모호함을 없애 줘요.fx·fy는 초점거리, cx·cy는 화면 중심이에요. 여기서 나온 좌표는 카메라 기준이라 로봇 베이스 기준으로 한 번 더 변환해요.

여기서 나온 좌표는 카메라 기준이라 로봇 베이스 기준으로 한 번 더 좌표변환을 해야 팔에 명령을 줄 수 있는데, 이 변환(extrinsics)은 카메라가 몸체에 고정돼 있으면 상수예요. 뷰가 고정이라는 조건 하나가 문제 전체를 계산 가능하게 만들어요.

depth가 정말 필요한지도 짚어봤어요.

조건depth이유
바닥이 평평하고 물체 높이가 모두 같음불필요Z를 상수로 두면 돼요
물체마다 높낮이가 다름필요같은 픽셀이라도 실제 위치가 달라져요

처음에는 필요 없어 보이던 센서가 조건 하나 바뀌면 필수가 되는 구조예요.

참고한 데모는 VLA가 아니었어요

비슷한 피킹 데모 영상을 찾아보니, VLA(Vision-Language-Action) 같은 최신 모델이 아니라 YOLO 디텍션과 depth의 조합이었어요. RGB 영상에서 물체를 찾아 바운딩 박스 중심 픽셀을 얻고, 그 픽셀의 depth를 읽어 역투영으로 3D 좌표를 만들고, 그 좌표로 팔을 보내는 구성이에요.

클릭과 디텍션은 같은 자리에 들어가요
사람의 클릭이 디텍터의 출력으로 바뀐 것뿐이에요.
사람의 클릭탑뷰 UI 콜백YOLO 디텍션바운딩 박스 중심좌표 공급원 — 여기만 갈아 끼워요(u, v)픽셀 좌표역투영depth로 3D 복원좌표변환카메라 → 로봇 베이스grip(x, y, z) · place(x, y, z)클릭 버전을 먼저 만들면 자동 피킹은 좌표 공급원만 갈아 끼우면 돼요.손으로 검증한 부분 위에 인식 모듈을 얹는 순서라, 어디서 틀렸는지도 분리해서 볼 수 있어요.

정석과 근사 사이

정석은 런타임에 픽셀별 depth를 읽어 매번 역투영하는 거예요. 그런데 임시 버전은 화면 XY에 따른 Z값 룩업 테이블로 대신하기로 했어요. 작업 영역의 경계 지점 몇 곳만 실측해 테이블에 넣고, 중간 지점은 선형 보간으로 채우는 방식이에요.

이 근사가 통하는 이유는 환경이 고정이기 때문이에요. 바닥 높이가 일정한 시뮬레이션 환경에서는 테이블 몇 칸이면 충분하고, 책상처럼 높낮이가 섞이는 환경 대응은 런타임 계산으로 확장하면 돼요.

기하가 결론을 정하는 이야기는 코스트맵 팽창 비용 253이 통로를 지우는 원리에서도 한 번 다뤘는데, 이번에도 같은 구조예요. 어려워 보이는 문제가 고정된 기하 앞에서 계산 문제로 내려와요.

카메라가 두 대인 이유

구성위치쓰임
eye-to-hand몸체 고정 depth 카메라피킹 좌표 계산 — 룰 베이스는 이것 하나로 충분
eye-in-hand팔 끝 RGB 카메라VLA 학습 — 집는 순간의 근접 시점이 없으면 파지 동작을 배우기 어려워요

초기 자세도 카메라와 얽혀 있어요. 팔의 기본 포즈가 ㄱ자로 서 있어 몸체 카메라 시야를 가리는 문제가 있었고, 움츠린 홈 포즈를 정의해 키 입력 하나로 복귀하게 해 뒀다고 해요. 이 홈 포즈는 나중에 VLA 학습 데이터의 기준 자세와 맞춰야 해요. 학습 데이터가 기준 자세에서 시작해 집고 다시 기준 자세로 돌아오는 사이클로 돌기 때문이에요.

목적지 좌표에도 같은 단계적 전략이 적용돼요. 물체를 버릴 통의 위치는 처음에는 config 파일에 적어 둔 고정 XYZ로 시작해요. 그런데 모바일 베이스가 내비게이션으로 이동해 오면 도착 위치에 오차가 생기니, 고정 좌표만 믿으면 통 옆에 떨어뜨리게 돼요. 최종 버전에서는 도착 후 통을 디텍션해서 좌표를 다시 뽑는 단계가 필요하고, 이것도 결국 위의 역투영 파이프라인을 한 번 더 쓰는 일이에요.

다음 단계는 단순해요. 탑뷰 UI에 마우스 클릭 콜백을 붙여 (u, v)를 받고, gripplace 두 명령으로 잇는 것부터예요. place는 목표보다 z를 조금 높게 줘서 물체를 떨어뜨리는 식으로 처리해요. 이 두 프리미티브만 있으면 집고, 옮기고, 버리는 시나리오는 전부 조합으로 나와요.