Skip to main content
Summary

물체를 찾는 것과 물체를 잡는 것은 다르다

카메라가 책상 위의 컵을 찾아냈다고 해 봅시다. 화면에는 컵 주위로 네모난 박스가 그려지고, 모델은 자신 있게 말합니다. "컵입니다. 확률은 0.92입니다."

사람에게는 이것만으로도 충분해 보입니다. 우리는 컵을 보면 자연스럽게 손을 뻗어 적당한 위치를 잡습니다. 하지만 로봇팔에게는 아직 정보가 부족합니다. 로봇팔은 이렇게 되묻습니다.

"컵의 어디를 잡아야 하나요?"

"위에서 내려가야 하나요, 옆에서 접근해야 하나요?"

"그리퍼를 얼마나 벌려야 하나요?"

"이 위치로 가도 충돌하지 않나요?"

이 장의 핵심은 바로 여기에 있습니다. 객체 검출은 "무엇이 어디에 있는지"를 알려 줍니다. 하지만 조작을 하려면 한 단계 더 나아가야 합니다. 로봇은 물체의 위치뿐 아니라 잡을 수 있는 지점, 접근 방향, 그리퍼 자세, 실패 가능성까지 고려해야 합니다.

앞 장에서 MoveIt2가 목표 자세를 받아 경로를 계획한다는 것을 배웠습니다. 그렇다면 이제 남은 질문은 이것입니다. 그 목표 자세는 누가 만들어 줄까요? 이 장에서는 카메라 인식 결과를 바탕으로 로봇팔이 실제로 사용할 수 있는 집기 후보를 만드는 과정을 다룹니다.

물체를 집기 위한 인식

객체 검출 결과에서 중심과 크기 계산하기

가장 단순한 출발점은 객체 검출 모델이 반환한 바운딩 박스입니다. 바운딩 박스는 이미지 안에서 물체를 감싸는 사각형입니다. 보통 다음과 같은 값으로 표현합니다.

class_name: cup
confidence: 0.92
box: (x_min, y_min, x_max, y_max)

여기서 (x_min, y_min)은 박스의 왼쪽 위 픽셀 좌표이고, (x_max, y_max)는 오른쪽 아래 픽셀 좌표입니다. 이 값만 있으면 물체의 이미지상 중심점과 크기를 계산할 수 있습니다.

center_x = (x_min + x_max) / 2
center_y = (y_min + y_max) / 2
width    = x_max - x_min
height   = y_max - y_min

예를 들어 바운딩 박스가 (320, 180, 420, 360)이라면 중심점은 (370, 270)입니다. 너비는 100픽셀, 높이는 180픽셀입니다. 이 중심점은 물체의 대표 위치로 사용할 수 있습니다.

하지만 여기서 조심해야 합니다. 바운딩 박스의 중심이 곧 좋은 집기 지점은 아닙니다. 컵의 경우 박스 중심이 빈 공간이나 손잡이 쪽에 가까울 수 있습니다. 병처럼 세로로 긴 물체는 박스 중심을 잡으면 안정적일 수 있지만, 가위나 집게처럼 모양이 복잡한 물체는 중심점이 실제 물체 표면이 아닌 곳을 가리킬 수도 있습니다.

그래도 바운딩 박스는 매우 유용한 첫 단서입니다. 로봇은 박스를 통해 "대략 이 영역 안에 물체가 있다"는 사실을 알 수 있습니다. 이후 세그멘테이션, 깊이 정보, 그리퍼 조건을 결합해 더 나은 집기 후보를 만들어 갑니다.

중심점은 출발점일 뿐이다

처음 로봇팔 프로젝트를 만들 때 흔히 하는 방식은 바운딩 박스 중심을 그대로 목표점으로 사용하는 것입니다. 화면에서 물체 중심 픽셀을 구하고, 깊이 카메라로 그 지점의 깊이를 읽고, 카메라 좌표에서 로봇 좌표로 변환한 뒤, 그 위치로 로봇팔을 이동시킵니다.

이 흐름은 단순하고 이해하기 좋습니다.

객체 검출 → 바운딩 박스 중심 계산 → 깊이 읽기 → 3D 위치 변환 → 로봇팔 목표점 생성

작은 블록이나 정육면체처럼 모양이 단순한 물체에서는 꽤 잘 동작합니다. 그러나 실제 환경에서는 금방 한계가 드러납니다. 중심 픽셀의 깊이가 튀거나, 박스 안에 배경이 섞이거나, 물체가 비스듬히 놓여 있을 수 있습니다. 반짝이는 물체나 투명한 컵은 깊이 카메라가 값을 제대로 주지 못하기도 합니다.

따라서 중심점은 "정답"이 아니라 "후보를 만들기 위한 시작점"으로 보는 것이 좋습니다. 사람도 물체를 잡을 때 한 점만 보고 손을 뻗지 않습니다. 전체 모양을 보고, 손가락이 닿을 곳을 상상하고, 미끄러지지 않을 방향을 고릅니다. 로봇도 비슷한 과정을 거쳐야 합니다.

세그멘테이션으로 진짜 물체 영역 찾기

바운딩 박스는 사각형입니다. 물체가 둥글든, 길쭉하든, 손잡이가 있든 일단 네모로 감쌉니다. 그래서 박스 안에는 실제 물체가 아닌 배경 픽셀도 포함됩니다. 물체를 집기 위해서는 이보다 더 정확한 정보가 필요합니다.

세그멘테이션은 픽셀 단위로 물체 영역을 분리합니다. 즉, 이미지의 각 픽셀에 대해 "이 픽셀은 컵이다", "이 픽셀은 배경이다"와 같은 판단을 합니다. 세그멘테이션 마스크가 있으면 로봇은 바운딩 박스 안에서도 실제 물체에 해당하는 픽셀만 골라낼 수 있습니다.

마스크가 주어졌을 때 물체의 중심을 계산하는 방법은 여러 가지입니다. 가장 간단한 방법은 마스크에 포함된 모든 픽셀 좌표의 평균을 구하는 것입니다.

mask_pixels = 물체에 해당하는 픽셀들의 집합
center_x = mask_pixels의 x 좌표 평균
center_y = mask_pixels의 y 좌표 평균

이렇게 구한 중심을 마스크 중심 또는 무게중심이라고 부를 수 있습니다. 바운딩 박스 중심보다 실제 물체 모양을 더 잘 반영합니다. 예를 들어 물체가 박스의 왼쪽에 치우쳐 있거나, 배경이 많이 포함되어 있는 경우에도 마스크 중심은 실제 물체 영역 쪽으로 이동합니다.

하지만 마스크 중심도 항상 좋은 집기 지점은 아닙니다. 컵처럼 속이 빈 물체는 중심이 빈 공간에 있을 수 있고, 손잡이가 있는 물체는 무게중심과 잡기 좋은 지점이 다를 수 있습니다. 그래서 마스크는 중심점 계산뿐 아니라 물체의 방향, 크기, 가장자리, 표면 후보를 찾는 데 함께 사용해야 합니다.

깊이 정보로 3D 표면 위치 추정하기

이미지 좌표는 2D입니다. 픽셀 (370, 270)은 화면에서의 위치일 뿐, 실제 책상 위에서 로봇 기준 몇 센티미터 앞에 있는지는 알려 주지 않습니다. 로봇팔이 움직이려면 3D 위치가 필요합니다. 여기서 깊이 정보가 들어옵니다.

RGB-D 카메라를 사용하면 컬러 이미지와 함께 각 픽셀의 깊이 값을 얻을 수 있습니다. 깊이 값은 카메라로부터 해당 지점까지의 거리입니다. 앞에서 배운 카메라 내부 파라미터를 사용하면 픽셀 좌표와 깊이 값을 3D 좌표로 바꿀 수 있습니다.

X = (u - cx) * Z / fx
Y = (v - cy) * Z / fy
Z = depth(u, v)

여기서 (u, v)는 픽셀 좌표이고, Z는 깊이 값입니다. fx, fy, cx, cy는 카메라 캘리브레이션으로 얻는 내부 파라미터입니다. 이렇게 계산한 (X, Y, Z)는 카메라 좌표계에서의 3D 위치입니다.

문제는 한 픽셀의 깊이에만 의존하면 불안정하다는 점입니다. 중심 픽셀의 깊이가 우연히 비어 있거나, 물체 가장자리와 배경 사이의 혼합값일 수 있습니다. 그래서 실전에서는 마스크 영역 안의 여러 픽셀을 함께 사용합니다.

절차는 다음과 같이 잡을 수 있습니다.

  1. 객체 검출 또는 세그멘테이션으로 물체 영역을 찾습니다.

  2. 마스크 내부 픽셀 중 깊이 값이 유효한 픽셀만 고릅니다.

  3. 너무 멀거나 너무 가까운 이상값을 제거합니다.

  4. 남은 깊이 값의 평균이나 중앙값을 사용합니다.

  5. 대표 픽셀 또는 여러 픽셀을 3D 점으로 변환합니다.

여기서 평균보다 중앙값이 더 안정적인 경우가 많습니다. 평균은 극단적인 값에 끌려갈 수 있지만, 중앙값은 일부 이상한 깊이 값이 섞여도 비교적 덜 흔들립니다. 로봇 비전에서는 이런 작은 안정성이 실제 성공률을 크게 바꿉니다.

마스크 내부의 여러 픽셀을 3D 점으로 변환하면 작은 포인트 클라우드를 얻을 수 있습니다. 포인트 클라우드는 3D 점들의 집합입니다. 이것을 보면 물체의 대략적인 표면 위치와 높이, 기울기를 추정할 수 있습니다. 복잡한 3D 재구성을 하지 않더라도, "이 물체의 윗면은 대략 어디인가", "가장 높은 지점은 어디인가", "옆면은 어느 방향을 향하는가" 정도는 판단할 수 있습니다.

로봇 좌표계로 옮겨야 조작할 수 있다

깊이 정보로 계산한 3D 좌표는 처음에는 카메라 기준입니다. 하지만 로봇팔이 사용하는 좌표계는 보통 base_link 같은 로봇 베이스 좌표계입니다. 카메라가 로봇 위에 고정되어 있든, 책상 옆에 따로 설치되어 있든, 카메라 좌표를 로봇 좌표로 변환해야 합니다.

이전 장에서 다룬 것처럼 이 변환에는 카메라-로봇 캘리브레이션이 필요합니다. 카메라에서 본 점 P_camera를 로봇 기준 점 P_robot으로 바꾸려면 회전과 이동을 함께 적용합니다.

P_robot = T_robot_camera × P_camera

여기서 T_robot_camera는 카메라 좌표계에서 로봇 좌표계로 가는 변환입니다. ROS2 환경에서는 TF를 통해 이런 변환을 관리하는 경우가 많습니다.

이 단계가 중요한 이유는 명확합니다. 카메라는 "내 앞쪽 0.6미터 지점에 물체가 있다"고 말합니다. 하지만 로봇팔은 "내 베이스 기준 x=0.35m, y=0.12m, z=0.08m 위치로 가라"는 식의 목표가 필요합니다. 둘 사이를 이어 주는 다리가 좌표 변환입니다.

이제 우리는 물체의 대략적인 3D 위치를 얻었습니다. 하지만 아직 끝이 아닙니다. 로봇팔은 점 하나를 잡는 것이 아니라, 그리퍼라는 도구로 물체를 감싸거나 눌러 잡습니다. 그래서 다음 질문은 그리퍼를 어떻게 배치할 것인가입니다.

그리퍼가 잡을 수 있는 물체인지 확인하기

로봇팔의 손에 해당하는 장치를 엔드 이펙터라고 부릅니다. 그중 가장 흔한 형태가 두 손가락 그리퍼입니다. 두 손가락 그리퍼는 양쪽 손가락을 벌렸다가 오므리면서 물체를 잡습니다. 단순해 보이지만, 집기 후보를 만들 때는 그리퍼의 물리적 조건을 반드시 고려해야 합니다.

예를 들어 그리퍼가 최대 8cm까지 벌어질 수 있다고 합시다. 그런데 물체의 폭이 12cm라면 아무리 좋은 위치를 찾아도 잡을 수 없습니다. 반대로 물체가 너무 얇거나 미끄럽다면 손가락이 닿아도 안정적으로 고정되지 않을 수 있습니다.

집기 후보를 만들 때 고려해야 할 기본 조건은 다음과 같습니다.

  • 그리퍼의 최대 벌림 폭보다 물체의 잡는 방향 폭이 작아야 합니다.

  • 손가락이 닿을 위치 주변에 충분한 공간이 있어야 합니다.

  • 접근 경로에 다른 물체나 책상, 벽이 없어야 합니다.

  • 잡은 뒤 들어 올릴 때 물체가 기울어지거나 미끄러질 가능성이 낮아야 합니다.

  • 로봇팔이 해당 자세에 도달할 수 있어야 합니다.

여기서 중요한 표현은 "물체의 전체 크기"가 아니라 "잡는 방향 폭"입니다. 예를 들어 긴 직사각형 블록이 있다고 합시다. 길이는 20cm이고 폭은 4cm입니다. 그리퍼 최대 폭이 8cm라면 길이 방향으로는 잡을 수 없지만, 폭 방향으로는 잡을 수 있습니다. 따라서 로봇은 물체가 어떤 방향으로 놓여 있는지 알아야 합니다.

물체의 방향 추정하기

마스크가 있으면 물체의 방향을 어느 정도 추정할 수 있습니다. 가장 쉬운 방법은 마스크 영역을 감싸는 회전된 사각형을 찾는 것입니다. 일반 바운딩 박스는 이미지 축에 맞춰진 사각형이지만, 회전된 사각형은 물체의 기울기에 맞춰 돌아갑니다.

예를 들어 책상 위에 길쭉한 상자가 30도 기울어져 놓여 있다면, 일반 바운딩 박스는 상자를 크게 감싸는 네모가 됩니다. 반면 회전된 사각형은 상자의 긴 방향을 따라 기울어진 형태가 됩니다. 이 회전 각도를 이용하면 그리퍼의 방향을 정할 수 있습니다.

또 다른 방법은 마스크 픽셀의 분포를 분석하는 것입니다. 픽셀들이 길게 퍼진 방향을 물체의 주축이라고 보고, 그에 수직인 방향을 짧은 축으로 봅니다. 두 손가락 그리퍼는 보통 물체의 짧은 축 방향으로 닫히도록 배치하는 것이 유리합니다. 그래야 필요한 벌림 폭이 작아지고, 양쪽 손가락이 안정적으로 물체를 누를 수 있습니다.

단순화하면 다음과 같은 판단을 할 수 있습니다.

물체가 길쭉하다 → 긴 축을 찾는다
그리퍼 손가락은 긴 축을 따라 배치한다
그리퍼가 닫히는 방향은 짧은 축이 되게 한다

조금 헷갈릴 수 있으니 손으로 상상해 봅시다. 책상 위에 연필이 놓여 있습니다. 연필을 두 손가락으로 잡을 때, 손가락 두 개가 연필의 양옆에서 다가와야 합니다. 손가락이 닫히는 방향은 연필의 두께 방향입니다. 손가락 자체는 연필의 길이 방향과 비슷하게 놓입니다. 로봇 그리퍼도 같은 원리입니다.

접근 방향 정하기

집기에서 접근 방향은 매우 중요합니다. 같은 물체라도 위에서 내려오는지, 옆에서 다가가는지에 따라 성공률이 달라집니다.

가장 기본적인 방식은 위에서 내려오는 탑다운 집기입니다. 로봇팔이 물체 위쪽으로 이동한 뒤, 수직으로 내려가서 물체를 잡고 들어 올립니다. 책상 위의 블록, 작은 상자, 스펀지, 부품 같은 물체에 적합합니다. 구현도 비교적 쉽습니다. 카메라가 위에서 물체를 보고 있다면 이미지상 위치와 로봇 작업 공간이 직관적으로 연결되기 때문입니다.

하지만 모든 물체를 위에서 잡을 수 있는 것은 아닙니다. 컵처럼 높이가 있는 물체는 옆면을 잡는 편이 더 안정적일 수 있습니다. 손잡이가 있는 컵이라면 손잡이를 피해야 할 수도 있고, 병처럼 원통형 물체는 옆에서 잡되 중심 높이를 잘 맞춰야 합니다.

접근 방향은 보통 다음 요소를 함께 보고 결정합니다.

  • 물체의 형태가 낮고 넓은지, 높고 세로로 긴지 확인합니다.

  • 주변 장애물 때문에 위쪽 또는 옆쪽 접근이 가능한지 확인합니다.

  • 그리퍼가 물체를 잡은 뒤 들어 올릴 공간이 있는지 확인합니다.

  • 물체 표면의 법선 방향, 즉 표면이 향하는 방향을 참고합니다.

여기서 표면 법선이라는 말이 나왔습니다. 표면 법선은 어떤 표면에서 수직으로 뻗어 나오는 방향입니다. 책상 윗면의 법선은 위쪽을 향합니다. 컵의 옆면 법선은 옆을 향합니다. 깊이 정보로 얻은 포인트 클라우드를 이용하면 표면이 어느 방향을 향하는지 대략 추정할 수 있습니다. 로봇은 그 법선 방향의 반대쪽에서 접근하도록 후보를 만들 수 있습니다.

처음에는 복잡한 법선 추정까지 구현하지 않아도 됩니다. 실습 단계에서는 탑다운 집기를 기본으로 두고, 물체의 높이와 크기 조건에 따라 옆잡기를 추가하는 식으로 발전시키면 충분합니다.

집기 후보는 하나가 아니라 여러 개 만든다

초보 프로젝트에서는 보통 하나의 목표점만 계산합니다. "컵 중심으로 이동해서 잡기" 같은 방식입니다. 하지만 실제 로봇 시스템은 하나의 후보에 모든 것을 걸지 않습니다. 이유는 간단합니다. 인식은 틀릴 수 있고, 깊이는 흔들릴 수 있고, 로봇팔은 어떤 자세에는 도달하지 못할 수 있습니다.

그래서 좋은 시스템은 여러 개의 집기 후보를 만듭니다. 후보 하나는 다음과 같은 정보를 포함할 수 있습니다.

grasp_candidate:
  position: 물체를 잡을 3D 위치
  approach_direction: 접근 방향
  gripper_orientation: 그리퍼 자세
  opening_width: 그리퍼 벌림 폭
  pre_grasp_position: 접근 전 대기 위치
  score: 후보 점수

여기서 position은 실제로 잡을 위치입니다. approach_direction은 그 지점으로 어느 방향에서 들어갈지를 뜻합니다. gripper_orientation은 그리퍼가 어느 각도로 회전해야 하는지 나타냅니다. opening_width는 물체를 잡기 전에 그리퍼를 얼마나 벌릴지 결정합니다. pre_grasp_position은 바로 집기 지점으로 가기 전에 잠시 머무는 안전한 위치입니다.

예를 들어 탑다운 집기에서는 집기 위치보다 z축으로 조금 위에 있는 점을 사전 접근 위치로 둘 수 있습니다.

pre_grasp_position = grasp_position + (0, 0, approach_offset)

approach_offset이 10cm라면 로봇은 먼저 물체 위 10cm 지점으로 이동한 뒤, 천천히 아래로 내려갑니다. 이렇게 하면 옆에서 갑자기 물체를 치는 위험을 줄일 수 있습니다.

그리퍼 폭 계산하기

그리퍼 폭은 단순히 물체 폭과 같게 설정하면 안 됩니다. 물체를 감싸기 위해서는 약간의 여유가 필요합니다. 예를 들어 물체 폭이 4cm라면 그리퍼를 4cm로 딱 맞춰 벌리는 것이 아니라 5cm나 6cm 정도로 벌린 뒤 접근하는 편이 안전합니다. 이후 닫을 때 힘이나 위치를 제어해 물체를 잡습니다.

기본적인 계산은 다음과 같이 할 수 있습니다.

opening_width = object_width_along_grasp_axis + margin

여기서 margin은 여유 폭입니다. 너무 작으면 접근 중에 손가락이 물체에 걸리고, 너무 크면 주변 물체와 충돌할 가능성이 커집니다. 그리퍼의 실제 크기, 손가락 두께, 카메라 오차를 고려해 적절히 정해야 합니다.

또한 물체 폭이 그리퍼 최대 폭에 너무 가까우면 실패 확률이 높습니다. 예를 들어 그리퍼 최대 폭이 8cm이고 물체 폭이 7.9cm라면 이론상 가능해 보입니다. 하지만 카메라 오차가 5mm만 있어도 손가락이 물체에 걸릴 수 있습니다. 실전에서는 안전 여유를 둡니다.

object_width + margin < gripper_max_width

이 조건을 만족하지 않으면 해당 방향의 집기 후보는 버리는 것이 좋습니다. 대신 물체를 다른 방향에서 잡을 수 있는지, 또는 다른 스킬을 사용해야 하는지 판단해야 합니다.

후보 점수 매기기

여러 집기 후보를 만들었다면 이제 어떤 후보가 가장 좋은지 골라야 합니다. 이때 후보마다 점수를 매깁니다. 점수는 복잡한 딥러닝 모델로 계산할 수도 있지만, 처음에는 규칙 기반으로도 충분히 유용한 시스템을 만들 수 있습니다.

집기 후보의 점수는 다음 요소를 반영할 수 있습니다.

  • 마스크 중심에 가까울수록 높은 점수를 줍니다.

  • 깊이 값이 안정적인 영역일수록 높은 점수를 줍니다.

  • 물체 가장자리보다 안쪽에 있는 지점일수록 높은 점수를 줍니다.

  • 그리퍼 폭에 충분한 여유가 있을수록 높은 점수를 줍니다.

  • MoveIt2가 충돌 없는 경로를 찾을 수 있으면 높은 점수를 줍니다.

  • 접근 방향 주변에 장애물이 적을수록 높은 점수를 줍니다.

물체 가장자리를 피하는 이유는 손가락이 빗나갈 가능성이 높기 때문입니다. 마스크 경계에 가까운 지점은 실제 물체와 배경의 경계일 수 있고, 깊이 값도 불안정할 수 있습니다. 반대로 마스크 안쪽 깊숙한 지점은 안정적일 가능성이 높습니다.

깊이 안정성도 중요합니다. 후보 지점 주변의 깊이 값이 들쭉날쭉하면 그 위치는 표면이 복잡하거나 센서가 불안정하게 측정한 곳일 수 있습니다. 이런 후보는 점수를 낮추는 편이 좋습니다.

마지막으로 MoveIt2 계획 가능성을 확인해야 합니다. 아무리 좋아 보이는 집기 후보라도 로봇팔이 그 자세에 도달할 수 없다면 의미가 없습니다. 따라서 후보를 만든 뒤에는 MoveIt2에 "이 자세로 갈 수 있는가?"를 물어보는 단계가 필요합니다. 실제 실행 전에 계획만 생성해 보고, 실패한 후보는 버릴 수 있습니다.

간단한 집기 후보 생성 파이프라인

이제 전체 흐름을 하나로 묶어 보겠습니다. 목표는 검출된 물체 하나에 대해 로봇팔이 시도할 수 있는 집기 후보 목록을 만드는 것입니다.

입력:
  RGB 이미지
  Depth 이미지
  객체 검출 결과
  세그멘테이션 마스크
  카메라-로봇 변환
  그리퍼 정보

출력:
  점수순으로 정렬된 집기 후보 목록

절차는 다음과 같습니다.

  1. 객체 검출 결과에서 대상 물체의 바운딩 박스를 선택합니다.

  2. 바운딩 박스와 클래스 정보를 이용해 대상 마스크를 얻습니다.

  3. 마스크 내부의 유효한 깊이 픽셀을 모읍니다.

  4. 깊이 이상값을 제거하고 대표 3D 위치를 계산합니다.

  5. 마스크 형태를 분석해 물체의 방향과 크기를 추정합니다.

  6. 그리퍼 폭 조건을 만족하는 접근 각도를 여러 개 만듭니다.

  7. 각 접근 각도에 대해 집기 위치, 사전 접근 위치, 그리퍼 자세를 만듭니다.

  8. 좌표를 카메라 좌표계에서 로봇 좌표계로 변환합니다.

  9. 충돌 가능성, 도달 가능성, 깊이 안정성으로 후보 점수를 계산합니다.

  10. 점수가 높은 후보부터 MoveIt2 계획을 시도합니다.

이 흐름의 장점은 각 단계가 분리되어 있다는 것입니다. 객체 검출 모델을 바꾸더라도 뒤쪽의 후보 평가 구조는 유지할 수 있습니다. 깊이 카메라를 바꾸더라도 마스크와 3D 위치를 만드는 인터페이스만 맞추면 됩니다. 나중에 학습 기반 grasp 모델을 추가하더라도 후보 생성과 평가라는 큰 틀은 그대로 사용할 수 있습니다.

예시: 작은 상자를 집는 경우

책상 위에 작은 직사각형 상자가 놓여 있다고 해 봅시다. 객체 검출 모델은 상자를 찾고 바운딩 박스를 반환합니다. 세그멘테이션 마스크를 보면 상자는 이미지에서 약 20도 정도 기울어져 있습니다. 깊이 이미지를 확인하니 상자 윗면의 깊이 값이 비교적 안정적입니다.

로봇은 먼저 마스크 중심을 계산합니다. 그리고 마스크의 긴 방향을 찾아 상자의 방향을 추정합니다. 상자의 긴 방향이 이미지 기준으로 20도 기울어져 있다면, 그리퍼 손가락도 그 방향과 비슷하게 맞춥니다. 그리퍼가 닫히는 방향은 상자의 짧은 폭 방향이 됩니다.

상자의 폭이 4cm로 추정되고, 그리퍼 최대 폭이 8cm라면 잡을 수 있습니다. 여유 폭 2cm를 더해 그리퍼를 6cm 정도 벌린 상태로 접근합니다. 집기 위치는 상자 윗면 중심 근처로 잡고, 사전 접근 위치는 그보다 10cm 위로 설정합니다.

이 후보가 바로 실행되는 것은 아닙니다. 먼저 MoveIt2에 사전 접근 위치와 집기 위치로 가는 경로를 계획하게 합니다. 경로가 충돌 없이 생성되면 후보 점수가 올라갑니다. 만약 로봇팔 관절 제한 때문에 해당 자세에 도달할 수 없다면, 로봇은 그리퍼 각도를 조금 바꾼 다른 후보를 시도합니다.

이렇게 하면 로봇은 "상자를 찾았다"에서 끝나지 않고, "상자를 이 방향에서, 이 폭으로, 이 경로를 통해 잡겠다"까지 결정할 수 있습니다.

예시: 컵을 집는 경우

컵은 상자보다 조금 더 까다롭습니다. 컵은 속이 비어 있고, 손잡이가 있을 수 있으며, 재질에 따라 깊이 값이 불안정할 수 있습니다. 바운딩 박스 중심이 컵의 빈 내부를 가리킬 수도 있습니다. 그래서 컵은 단순히 중심점으로 접근하면 실패하기 쉽습니다.

컵을 잡는 전략은 여러 가지가 있습니다. 실습 환경에서는 컵의 몸통 옆면을 잡는 방식을 생각할 수 있습니다. 먼저 세그멘테이션으로 컵의 전체 마스크를 얻습니다. 깊이 정보로 컵의 앞쪽 표면 위치를 추정하고, 컵의 높이 중간 정도를 잡기 지점으로 선택합니다.

만약 카메라가 위에서 내려다보고 있다면 컵의 원형 윗부분이 보일 수 있습니다. 이때 마스크 중심은 컵 내부에 있을 가능성이 있습니다. 그러므로 마스크 중심을 그대로 집기 지점으로 쓰기보다, 마스크의 좌우 가장자리 사이 거리와 컵의 외곽을 이용해 그리퍼가 닿을 양쪽 면을 추정해야 합니다.

컵 손잡이가 보인다면 손잡이 쪽은 피하는 것이 좋습니다. 손잡이는 모양이 복잡하고, 그리퍼가 걸릴 수 있으며, 컵이 회전할 수도 있습니다. 물론 고급 시스템에서는 손잡이를 일부러 잡는 전략도 가능하지만, 처음에는 몸통의 단순한 곡면을 잡는 편이 안정적입니다.

컵의 예시는 중요한 교훈을 줍니다. 물체 클래스에 따라 집기 전략이 달라질 수 있다는 점입니다. 상자는 위에서 집기 좋고, 컵은 옆에서 잡기 좋을 수 있습니다. 납작한 접시는 가장자리 접근이 필요하고, 공처럼 둥근 물체는 그리퍼 형상에 따라 성공률이 크게 달라집니다.

카메라 오차에 대비하는 방법

로봇 비전에는 항상 오차가 있습니다. 카메라 캘리브레이션이 완벽하지 않을 수 있고, 깊이 값이 흔들릴 수 있으며, 객체 검출 모델이 물체 영역을 조금 크게 또는 작게 잡을 수 있습니다. 로봇팔 자체의 반복 정밀도도 한계가 있습니다.

그래서 집기 후보를 만들 때는 오차를 전제로 설계해야 합니다. 가장 위험한 설계는 "정확히 이 점에 물체가 있을 것"이라고 가정하는 것입니다. 현실에서는 물체가 몇 밀리미터만 달라도 그리퍼가 빗나갈 수 있습니다.

오차에 대비하는 기본 전략은 다음과 같습니다.

  • 한 픽셀의 깊이보다 주변 영역의 깊이 통계를 사용합니다.

  • 물체 가장자리보다 안쪽의 안정적인 지점을 우선합니다.

  • 그리퍼 폭에 여유를 둡니다.

  • 하나의 후보가 아니라 여러 후보를 준비합니다.

  • 실행 후 카메라나 그리퍼 센서로 성공 여부를 확인합니다.

특히 여러 후보를 준비하는 방식은 매우 강력합니다. 첫 번째 후보가 실패하더라도 두 번째 후보를 시도할 수 있기 때문입니다. 로봇에게 중요한 것은 한 번에 완벽한 답을 맞히는 능력이 아니라, 실패를 감지하고 안전하게 다시 시도하는 능력입니다.

후보를 평가할 때 안전을 먼저 본다

집기 후보 평가에서 가장 높은 우선순위는 안전입니다. 물체를 잘 잡는 것보다 중요한 것은 사람, 로봇, 주변 환경을 해치지 않는 것입니다. 집기 후보가 아무리 좋아 보여도 접근 경로가 사람 쪽을 지나가거나, 책상 모서리에 그리퍼가 부딪힐 가능성이 있으면 선택해서는 안 됩니다.

MoveIt2의 충돌 검사 기능은 이때 큰 도움이 됩니다. 로봇팔의 링크와 주변 장애물, 작업대, 물체 모델을 고려해 경로가 안전한지 확인할 수 있습니다. 물론 모든 장애물이 완벽하게 모델링되어 있지는 않을 수 있습니다. 따라서 비전으로 본 장애물 정보를 계획 장면에 반영하고, 실행 중에도 속도 제한과 긴급 정지 조건을 두는 것이 좋습니다.

집기 후보 점수에서 안전 조건은 단순한 가산점이 아니라 통과 조건으로 두는 편이 낫습니다. 예를 들어 충돌 가능성이 있는 후보는 점수를 낮추는 것이 아니라 아예 제외합니다. 도달 불가능한 후보도 마찬가지입니다. 안전 조건을 통과한 후보들 사이에서 안정성, 효율성, 성공 가능성을 비교하는 구조가 좋습니다.

1단계: 안전하지 않은 후보 제거
2단계: 도달 불가능한 후보 제거
3단계: 그리퍼 조건을 만족하지 않는 후보 제거
4단계: 남은 후보에 점수 부여
5단계: 가장 좋은 후보부터 계획 및 실행

이런 구조를 사용하면 시스템이 예측하기 쉬워집니다. 나중에 LLM이 자연어 명령을 해석해 "빨간 컵을 집어 오른쪽에 놓아줘"라고 계획하더라도, 실제 집기 단계에서는 안전 필터를 통과한 스킬만 실행됩니다.

인식 결과를 MoveIt2 목표 자세로 바꾸기

마지막으로 집기 후보가 MoveIt2에서 사용할 수 있는 목표 자세가 되려면 위치와 방향이 모두 필요합니다. 위치는 (x, y, z)입니다. 방향은 보통 쿼터니언이나 회전 행렬로 표현합니다. 쿼터니언은 3D 회전을 안정적으로 표현하기 위한 숫자 묶음입니다. 처음에는 "로봇팔의 손목이 어느 방향을 바라보는지 나타내는 값" 정도로 이해해도 충분합니다.

집기 후보 하나는 보통 여러 개의 자세로 나뉩니다.

pre_grasp_pose  : 물체 근처의 안전한 접근 시작 자세
grasp_pose      : 실제로 그리퍼를 닫을 자세
post_grasp_pose : 물체를 잡은 뒤 들어 올린 자세

pre_grasp_pose에서 grasp_pose로 이동할 때는 천천히 직선에 가깝게 접근하는 것이 좋습니다. 그리퍼를 닫은 뒤에는 post_grasp_pose로 이동해 물체를 책상에서 들어 올립니다. 이 세 자세가 안정적으로 정의되어야 다음 장에서 Pick and Place 전체 동작을 구현할 수 있습니다.

예를 들어 탑다운 집기에서는 다음과 같은 구조를 만들 수 있습니다.

grasp_pose.position = object_surface_center
grasp_pose.orientation = 그리퍼가 아래를 향하는 자세

pre_grasp_pose.position = grasp_pose.position + 위쪽 방향 10cm
pre_grasp_pose.orientation = grasp_pose.orientation

post_grasp_pose.position = grasp_pose.position + 위쪽 방향 15cm
post_grasp_pose.orientation = grasp_pose.orientation

이때 실제 집기 위치는 물체 표면보다 약간 아래 또는 옆으로 조정될 수 있습니다. 그리퍼 손가락이 물체를 충분히 감싸야 하기 때문입니다. 단, 너무 깊게 들어가면 책상과 충돌할 수 있으므로 물체 높이와 손가락 길이를 함께 고려해야 합니다.

작은 의사코드로 정리하기

전체 개념을 간단한 의사코드로 표현하면 다음과 같습니다. 실제 코드에서는 ROS2 메시지, TF 변환, MoveIt2 인터페이스가 들어가지만, 큰 흐름은 이와 비슷합니다.

def generate_grasp_candidates(detection, mask, depth_image, camera_info, tf, gripper):
    # 1. 마스크 내부의 유효 깊이 수집
    pixels = get_valid_pixels(mask, depth_image)
    pixels = remove_depth_outliers(pixels)

    if len(pixels) == 0:
        return []

    # 2. 물체 중심과 방향 추정
    center_uv = compute_mask_centroid(mask)
    object_axes = estimate_object_axes(mask)
    object_size = estimate_object_size(mask, depth_image, camera_info)

    # 3. 대표 3D 위치 계산
    center_depth = median_depth_around(center_uv, depth_image, mask)
    center_xyz_camera = project_pixel_to_3d(center_uv, center_depth, camera_info)
    center_xyz_robot = transform_point(center_xyz_camera, tf)

    # 4. 여러 접근 각도 생성
    candidates = []
    for angle in make_grasp_angles(object_axes):
        width = estimate_width_along_axis(object_size, angle)

        if width + gripper.margin > gripper.max_width:
            continue

        grasp_pose = make_grasp_pose(center_xyz_robot, angle)
        pre_grasp_pose = offset_pose(grasp_pose, direction="up", distance=0.10)
        post_grasp_pose = offset_pose(grasp_pose, direction="up", distance=0.15)

        score = score_candidate(
            grasp_pose=grasp_pose,
            mask=mask,
            depth_image=depth_image,
            width=width,
            gripper=gripper,
        )

        candidates.append({
            "grasp_pose": grasp_pose,
            "pre_grasp_pose": pre_grasp_pose,
            "post_grasp_pose": post_grasp_pose,
            "opening_width": width + gripper.margin,
            "score": score,
        })

    return sorted(candidates, key=lambda c: c["score"], reverse=True)

이 코드는 완성된 구현이라기보다 생각의 뼈대입니다. 중요한 점은 인식 결과를 바로 실행 명령으로 바꾸지 않는다는 것입니다. 중간에 후보를 만들고, 조건을 확인하고, 점수를 매기고, 계획 가능성을 검사합니다. 이 완충 구간이 로봇을 훨씬 더 안전하고 똑똑하게 만듭니다.

좋은 집기 인식 시스템의 특징

좋은 집기 인식 시스템은 화려한 모델 하나로 결정되지 않습니다. 오히려 여러 작은 판단이 잘 연결될 때 강해집니다. 객체 검출은 대상 물체를 찾고, 세그멘테이션은 실제 물체 영역을 분리하고, 깊이 카메라는 3D 위치를 제공하고, 좌표 변환은 로봇 기준 위치를 만들어 줍니다. 여기에 그리퍼 조건과 경로 계획 가능성이 더해져야 비로소 실행 가능한 집기 후보가 됩니다.

다시 말해 로봇에게 "본다"는 것은 단순히 이미지를 분류하는 일이 아닙니다. 로봇의 시각은 행동을 위한 시각입니다. 물체를 본다는 것은 곧 그 물체와 상호작용할 준비를 한다는 뜻입니다.

이 장에서 우리는 검출된 물체 정보를 바탕으로 중심점, 크기, 표면 위치, 접근 방향, 그리퍼 폭, 후보 점수를 계산하는 방법을 살펴보았습니다. 이제 로봇팔은 단순히 "저기 컵이 있다"라고 말하는 수준을 넘어설 수 있습니다.

이제 다음 질문으로 넘어갈 차례입니다.

"좋다. 어디를 어떻게 잡을지 정했다. 그렇다면 실제 Pick and Place 동작은 어떤 순서로 실행해야 할까?"

다음 장에서는 접근, 집기, 들어 올리기, 이동, 내려놓기, 그리퍼 열기까지 이어지는 전체 Pick and Place 구현 흐름을 다룹니다.

책 목록

AI 로봇 에이전트 만들기: 비전·로봇팔·학습·LLM으로 자연어 명령을 실행하는 로봇

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기