카메라가 본 세계와 로봇이 움직이는 세계
로봇 앞에 빨간 공이 하나 놓여 있다고 해 봅시다. 카메라는 그 공을 봅니다. 이미지 안에서는 공이 화면의 오른쪽 아래에 보일 수도 있고, 중앙 근처에 보일 수도 있습니다. 사람은 사진만 봐도 대충 말할 수 있습니다.
'공이 저쪽에 있네.'
하지만 로봇에게는 이 말이 충분하지 않습니다. 로봇 팔이 공을 집으려면 훨씬 더 구체적인 정보가 필요합니다.
공이 로봇 베이스 기준으로 앞쪽 몇 미터에 있는지, 왼쪽인지 오른쪽인지, 손끝을 얼마나 움직여야 닿는지 알아야 합니다. 이동 로봇이라면 공을 피하기 위해 공이 주행 방향 기준 어디에 있는지도 알아야 합니다.
여기서 중요한 문제가 등장합니다. 카메라가 말하는 좌표와 로봇이 움직이는 좌표가 서로 다르다는 점입니다.
카메라는 보통 자기 자신을 기준으로 물체를 봅니다. 반면 로봇은 로봇 베이스, 바퀴 중심, 팔의 관절, 손끝 같은 여러 좌표계를 기준으로 움직입니다. 따라서 카메라가 관측한 물체 위치를 로봇이 이해하는 좌표로 바꿔 주어야 합니다.
이 장의 핵심은 한 문장으로 정리할 수 있습니다.
카메라가 본 물체 위치를 로봇이 움직일 수 있는 좌표계로 번역한다.
앞 장들에서 배운 회전 행렬과 동차 변환 행렬이 바로 이 번역기의 역할을 합니다.

카메라 좌표계는 어떻게 생겼을까
먼저 카메라가 세상을 어떻게 표현하는지 살펴보겠습니다. 카메라 좌표계는 카메라 렌즈 중심 근처에 원점을 두고, 카메라가 바라보는 방향을 하나의 축으로 삼습니다. 이 바라보는 중심 방향을 광학축이라고 부릅니다.
컴퓨터 비전에서 자주 쓰는 카메라 좌표계는 다음과 같이 잡는 경우가 많습니다.
x축: 이미지에서 오른쪽 방향
y축: 이미지에서 아래쪽 방향
z축: 카메라가 바라보는 앞쪽 방향, 즉 광학축 방향
이 약속을 따르면 카메라 바로 정면 1m 앞에 있는 물체는 대략 z = 1.0인 점으로 표현됩니다. 물체가 이미지 오른쪽에 보이면 x가 양수이고, 이미지 아래쪽에 보이면 y가 양수입니다.
단, 모든 로봇 시스템이 이 축 방향을 똑같이 쓰지는 않습니다. 어떤 로봇 소프트웨어는 z축을 위쪽으로 두고, x축을 전방으로 둡니다. 그래서 좌표 변환을 할 때는 항상 먼저 질문해야 합니다.
'이 좌표계에서 x, y, z는 각각 어느 방향인가?'
이 질문을 건너뛰면 코드가 아무리 멋져도 로봇은 엉뚱한 곳으로 손을 뻗습니다. 공이 오른쪽에 있는데 왼쪽으로 움직이거나, 앞에 있는 물체를 아래에 있다고 착각할 수 있습니다.
이미지 좌표와 카메라 3D 좌표
카메라가 처음 얻는 데이터는 보통 이미지입니다. 이미지 위의 한 점은 픽셀 좌표로 표현됩니다. 예를 들어 어떤 물체의 중심이 이미지에서 (u, v) = (420, 260)에 있다고 합시다. 여기서 u는 이미지의 가로 방향 픽셀 위치이고, v는 세로 방향 픽셀 위치입니다.
하지만 픽셀 좌표만으로는 물체가 얼마나 멀리 있는지 알 수 없습니다. 화면 중앙에 작게 보이는 공은 실제로 작은 공이 가까이 있는 것일 수도 있고, 큰 공이 멀리 있는 것일 수도 있습니다.
그래서 3D 위치를 얻으려면 깊이 정보가 필요합니다. 깊이는 카메라로부터 물체까지의 앞쪽 거리라고 생각하면 됩니다. 깊이 카메라, 스테레오 카메라, LiDAR, 또는 물체 크기 추정 같은 방법을 통해 깊이를 얻을 수 있습니다.
핀홀 카메라 모델이라는 간단한 모델에서는 픽셀 좌표와 깊이를 사용해 카메라 좌표계의 3D 점을 계산할 수 있습니다.
x_c = (u - c_x) * z_c / f_x
y_c = (v - c_y) * z_c / f_y
z_c = depth여기서 f_x, f_y는 초점거리 역할을 하는 값이고, c_x, c_y는 이미지 중심점입니다. 이 값들은 카메라 내부 파라미터라고 부릅니다. 내부 파라미터는 카메라 렌즈와 이미지 센서의 특성을 숫자로 나타낸 것입니다.
바로 Python으로 계산해 봅시다.
import numpy as np
# 이미지에서 관측한 물체 중심 픽셀
u = 420
v = 260
# 카메라 내부 파라미터 예시
fx = 600.0
fy = 600.0
cx = 320.0
cy = 240.0
# 깊이: 카메라 앞쪽 1.2m
depth = 1.2
x_c = (u - cx) * depth / fx
y_c = (v - cy) * depth / fy
z_c = depth
p_camera = np.array([x_c, y_c, z_c])
print(p_camera)출력은 대략 다음과 같습니다.
[0.2 0.04 1.2 ]이 결과는 물체가 카메라 기준으로 오른쪽 0.2m, 아래쪽 0.04m, 앞쪽 1.2m에 있다는 뜻입니다. 이제 카메라는 물체의 3D 위치를 말할 수 있게 되었습니다.
하지만 아직 로봇은 이 말을 바로 알아듣지 못합니다. 카메라가 말한 오른쪽, 아래쪽, 앞쪽은 카메라 기준입니다. 로봇 베이스 기준으로 다시 번역해야 합니다.
변환 행렬은 좌표계 사이의 번역기다
앞에서 배운 동차 변환 행렬을 다시 떠올려 봅시다. 동차 변환 행렬은 회전과 이동을 하나의 행렬에 담습니다.
T = [ R t ]
[ 0 1 ]R은 방향 차이를 나타내는 회전 행렬이고, t는 원점의 위치 차이를 나타내는 이동 벡터입니다.
이 장에서는 변환 행렬 이름을 아주 신중하게 붙이겠습니다. 다음 표기법을 사용합니다.
T_base_camera이 행렬은 카메라 좌표계의 점을 로봇 베이스 좌표계로 바꾸는 변환입니다.
p_base = T_base_camera @ p_camera이 표기법은 처음에는 조금 길어 보이지만, 실수를 줄이는 데 큰 도움이 됩니다. T_base_camera를 말로 풀면 다음과 같습니다.
'camera에 있는 좌표를 base로 가져오는 변환'
즉, 아래첨자처럼 읽으면 됩니다.
base <- camera로봇공학에서는 이 방향을 헷갈리는 일이 정말 많습니다. 행렬 하나를 반대로 쓰면 로봇은 물체 쪽으로 다가가는 대신 물체에서 멀어질 수 있습니다. 그래서 지금부터는 변환 행렬 이름에 도착 좌표계와 출발 좌표계를 함께 넣겠습니다.
카메라가 로봇 앞에 달려 있다면
간단한 이동 로봇을 생각해 봅시다. 로봇 베이스 좌표계는 다음처럼 정의하겠습니다.
x축: 로봇의 앞쪽
y축: 로봇의 왼쪽
z축: 위쪽
카메라는 로봇 앞부분에 달려 있고, 정면을 바라봅니다. 그런데 카메라 좌표계는 앞에서 정한 대로 x축이 오른쪽, y축이 아래쪽, z축이 앞쪽입니다.
두 좌표계의 방향을 비교하면 다음과 같습니다.
카메라의 앞쪽 z_c는 로봇의 앞쪽 x_b와 같습니다. 카메라의 오른쪽 x_c는 로봇의 오른쪽인데, 로봇 좌표계에서 오른쪽은 -y_b입니다. 카메라의 아래쪽 y_c는 로봇 좌표계에서 -z_b입니다.
따라서 카메라 좌표의 점 [x_c, y_c, z_c]는 로봇 베이스 좌표에서 대략 [z_c, -x_c, -y_c]가 됩니다. 여기에 카메라가 로봇 베이스 원점에서 얼마나 떨어져 달려 있는지 더하면 됩니다.
예를 들어 카메라가 로봇 베이스 기준으로 앞쪽 0.25m, 높이 0.35m 위치에 있다고 합시다.
import numpy as np
def make_T(R, t):
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = t
return T
def to_homogeneous(p):
return np.array([p[0], p[1], p[2], 1.0])
def from_homogeneous(p_h):
return p_h[:3] / p_h[3]
# 카메라 좌표계의 축을 로봇 베이스 좌표계로 표현한 회전 행렬
# p_base_rotation = R_base_camera @ p_camera
R_base_camera = np.array([
[ 0.0, 0.0, 1.0],
[-1.0, 0.0, 0.0],
[ 0.0, -1.0, 0.0],
])
# 카메라 원점은 로봇 베이스 기준으로 앞쪽 0.25m, 높이 0.35m
t_base_camera = np.array([0.25, 0.0, 0.35])
T_base_camera = make_T(R_base_camera, t_base_camera)
# 카메라가 본 물체 위치: 오른쪽 0.2m, 아래쪽 0.04m, 앞쪽 1.2m
p_camera = np.array([0.2, 0.04, 1.2])
p_camera_h = to_homogeneous(p_camera)
p_base_h = T_base_camera @ p_camera_h
p_base = from_homogeneous(p_base_h)
print(p_base)출력은 다음과 비슷합니다.
[ 1.45 -0.2 0.31]이제 물체는 로봇 베이스 기준으로 앞쪽 1.45m, 오른쪽 0.2m, 높이 0.31m에 있다고 해석할 수 있습니다. 로봇 좌표계에서 y축은 왼쪽이 양수이므로 y = -0.2는 오른쪽 0.2m라는 뜻입니다.
이 한 줄이 핵심입니다.
p_base_h = T_base_camera @ p_camera_h카메라가 본 점이 로봇이 이해하는 점으로 바뀌었습니다. 로봇에게는 이 좌표가 훨씬 유용합니다. 이제 앞쪽으로 얼마나 가야 하는지, 오른쪽으로 얼마나 회전해야 하는지, 로봇 팔을 어느 위치로 보내야 하는지 계산할 수 있습니다.
변환의 반대 방향이 필요할 때
때로는 반대 방향 변환도 필요합니다. 로봇 베이스 기준으로 알고 있는 점이 카메라 이미지에서 어디쯤 보일지 알고 싶을 수 있습니다. 이때는 T_base_camera의 역행렬을 사용합니다.
T_camera_base = inverse(T_base_camera)
p_camera = T_camera_base @ p_base동차 변환 행렬의 역행렬은 NumPy의 np.linalg.inv로 구할 수 있습니다.
T_camera_base = np.linalg.inv(T_base_camera)
p_base_again_h = to_homogeneous(np.array([1.45, -0.2, 0.31]))
p_camera_again_h = T_camera_base @ p_base_again_h
p_camera_again = from_homogeneous(p_camera_again_h)
print(p_camera_again)출력은 원래 카메라 좌표였던 [0.2, 0.04, 1.2]와 거의 같아야 합니다. 컴퓨터 계산에서는 아주 작은 소수 오차가 생길 수 있지만, 의미는 같습니다.
여기서 중요한 감각은 이것입니다. 변환 행렬은 단순한 숫자 표가 아니라 좌표계 사이를 오가는 문입니다. 한쪽 문으로 들어가면 카메라 좌표가 로봇 좌표가 되고, 반대쪽 문으로 들어가면 로봇 좌표가 카메라 좌표가 됩니다.
Eye-in-hand: 카메라가 손끝에 달린 경우
이제 로봇 팔로 가 봅시다. 로봇 팔에서 카메라를 다는 방식은 크게 두 가지로 나눌 수 있습니다. 첫 번째는 Eye-in-hand입니다. 말 그대로 눈이 손에 달려 있는 구성입니다. 카메라가 로봇 손끝, 즉 그리퍼 근처에 붙어 있습니다.
이 구성에서는 로봇 팔이 움직일 때 카메라도 함께 움직입니다. 손목을 돌리면 카메라 시야도 같이 돌아갑니다. 사람이 손에 든 스마트폰 카메라로 물체를 보면서 손을 움직이는 상황과 비슷합니다.
Eye-in-hand에서 자주 등장하는 좌표계는 다음과 같습니다.
base: 로봇 팔의 기준 좌표계ee: end-effector, 즉 손끝 좌표계camera: 손끝에 달린 카메라 좌표계object: 카메라가 관측한 물체 좌표계
물체 위치를 로봇 베이스 좌표로 바꾸려면 변환을 차례대로 연결합니다.
T_base_object = T_base_ee @ T_ee_camera @ T_camera_object이 식을 말로 읽으면 다음과 같습니다.
카메라가 본 물체를 먼저 손끝 기준으로 바꾸고, 그다음 로봇 베이스 기준으로 바꾼다.
여기서 T_base_ee는 로봇 팔의 현재 자세에 따라 계속 바뀝니다. 로봇 팔 관절이 움직이면 손끝 위치와 방향이 달라지기 때문입니다. 반면 T_ee_camera는 카메라가 손끝에 단단히 고정되어 있다면 거의 일정합니다. 이 고정된 관계를 정확히 알아내는 과정을 보통 손-눈 보정, 즉 hand-eye calibration이라고 부릅니다.
간단한 예제를 보겠습니다. 여기서는 회전을 복잡하게 만들지 않고, 손끝 앞쪽에 카메라가 조금 튀어나와 있다고 가정하겠습니다.
import numpy as np
def make_T(R, t):
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = t
return T
def transform_point(T, p):
p_h = np.array([p[0], p[1], p[2], 1.0])
result_h = T @ p_h
return result_h[:3] / result_h[3]
I = np.eye(3)
# 로봇 베이스 기준 손끝 위치
T_base_ee = make_T(I, np.array([0.6, 0.1, 0.4]))
# 손끝 기준 카메라 위치: 손끝보다 앞쪽 0.08m, 위쪽 0.03m
T_ee_camera = make_T(I, np.array([0.08, 0.0, 0.03]))
# 카메라 기준 물체 위치
T_camera_object = make_T(I, np.array([0.0, 0.02, 0.25]))
T_base_object = T_base_ee @ T_ee_camera @ T_camera_object
p_object_in_base = T_base_object[:3, 3]
print(p_object_in_base)이 코드는 물체의 원점이 로봇 베이스 기준 어디에 있는지 계산합니다. 예제를 단순하게 만들기 위해 회전을 모두 단위행렬로 두었지만, 실제 로봇에서는 손끝이 회전하고 카메라도 기울어져 있을 수 있습니다. 그래도 구조는 변하지 않습니다.
T_base_object = T_base_ee @ T_ee_camera @ T_camera_object행렬을 왼쪽부터 오른쪽으로 읽기보다, 좌표계 이름이 어떻게 이어지는지 보세요.
base <- ee <- camera <- object가운데 이름들이 자연스럽게 이어지면 체인이 맞는 것입니다.
Eye-to-hand: 카메라가 바깥에 고정된 경우
두 번째 구성은 Eye-to-hand입니다. 이번에는 카메라가 로봇 손에 붙어 있지 않습니다. 작업대 위쪽, 벽, 천장, 삼각대 같은 곳에 고정되어 로봇과 작업 공간을 바라봅니다.
공장 자동화 장면을 상상하면 쉽습니다. 컨베이어 벨트 위에 카메라가 고정되어 있고, 로봇 팔은 카메라가 본 물체 위치를 받아 집습니다. 카메라는 움직이지 않고, 로봇 팔만 움직입니다.
Eye-to-hand에서는 보통 다음 변환이 중요합니다.
T_base_camera카메라가 로봇 베이스 기준 어디에 있고 어느 방향을 바라보는지 나타내는 행렬입니다. 카메라가 고정되어 있으므로 이 행렬은 한 번 보정해 두면 대체로 일정합니다.
카메라가 물체를 관측하면 다음처럼 물체의 베이스 좌표를 구할 수 있습니다.
T_base_object = T_base_camera @ T_camera_objectEye-in-hand보다 체인이 짧습니다. 카메라가 손끝을 따라 움직이지 않기 때문입니다.
하지만 로봇 팔이 실제로 물체를 잡으려면 손끝 기준으로 물체가 어디에 있는지도 궁금할 수 있습니다. 그때는 로봇 베이스에서 손끝으로 가는 변환의 역행렬을 사용합니다.
T_ee_object = inverse(T_base_ee) @ T_base_object이 식은 이렇게 읽을 수 있습니다.
'베이스 기준 물체 위치를 손끝 기준 물체 위치로 다시 표현한다.'
Python으로 두 구성을 나란히 비교해 봅시다.
import numpy as np
def make_T(R, t):
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = t
return T
I = np.eye(3)
# 로봇 팔의 현재 손끝 자세
T_base_ee = make_T(I, np.array([0.5, 0.0, 0.35]))
# Eye-to-hand: 고정 카메라가 로봇 베이스 기준으로 놓인 위치
T_base_camera = make_T(I, np.array([0.2, -0.6, 0.8]))
# 카메라가 관측한 물체 위치
T_camera_object = make_T(I, np.array([0.3, 0.4, -0.5]))
# 물체를 로봇 베이스 기준으로 변환
T_base_object = T_base_camera @ T_camera_object
# 물체를 손끝 기준으로 보고 싶다면
T_ee_base = np.linalg.inv(T_base_ee)
T_ee_object = T_ee_base @ T_base_object
print('object in base:', T_base_object[:3, 3])
print('object in ee:', T_ee_object[:3, 3])이 예제의 숫자는 단순화를 위해 만든 값입니다. 실제 카메라 좌표계에서는 앞쪽 방향과 위아래 방향을 더 정확히 맞춰야 합니다. 그래도 전체 구조는 같습니다. 카메라가 손에 붙어 있으면 base <- ee <- camera <- object 체인을 쓰고, 카메라가 바깥에 고정되어 있으면 base <- camera <- object 체인을 씁니다.
손끝, 카메라, 물체를 하나의 체인으로 연결하기
이제 조금 더 로봇다운 예제를 만들어 보겠습니다. 목표는 카메라가 본 물체를 로봇 손끝 기준으로 바꾸는 것입니다. 이 값은 로봇 팔 제어에서 유용합니다. 손끝 기준으로 물체가 앞에 있는지, 왼쪽에 있는지, 얼마나 떨어져 있는지 알면 손끝을 어떻게 움직일지 결정할 수 있습니다.
이번에는 z축 회전도 넣어 보겠습니다. 3D 회전 전체를 깊게 다루지는 않지만, 앞 장에서 배운 회전 행렬의 감각을 그대로 사용하면 됩니다.
import numpy as np
def rot_z(theta):
c = np.cos(theta)
s = np.sin(theta)
return np.array([
[c, -s, 0.0],
[s, c, 0.0],
[0.0, 0.0, 1.0],
])
def make_T(R, t):
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = t
return T
# 로봇 베이스 기준 손끝 자세
# 손끝이 베이스 기준 (0.7, 0.2, 0.4)에 있고, z축으로 30도 회전했다고 하자.
theta_ee = np.deg2rad(30)
T_base_ee = make_T(rot_z(theta_ee), np.array([0.7, 0.2, 0.4]))
# 손끝 기준 카메라 자세
# 카메라가 손끝보다 앞에 0.1m 붙어 있다고 단순화한다.
T_ee_camera = make_T(np.eye(3), np.array([0.1, 0.0, 0.0]))
# 카메라 기준 물체 자세
# 물체가 카메라 앞쪽 0.35m, 오른쪽 0.04m에 있다고 하자.
T_camera_object = make_T(np.eye(3), np.array([0.35, -0.04, 0.0]))
# 전체 체인
T_base_object = T_base_ee @ T_ee_camera @ T_camera_object
# 손끝 기준 물체 위치도 계산해 보자.
T_ee_object = T_ee_camera @ T_camera_object
print('object position in base:', T_base_object[:3, 3])
print('object position in ee:', T_ee_object[:3, 3])여기서 눈여겨볼 부분은 두 가지입니다.
첫째, 베이스 기준 물체 위치는 손끝의 현재 자세 T_base_ee에 영향을 받습니다. 같은 카메라 관측값이라도 로봇 팔이 어디를 향하고 있느냐에 따라 베이스 기준 물체 위치는 달라집니다.
둘째, 손끝 기준 물체 위치는 T_ee_camera @ T_camera_object만으로 구할 수 있습니다. 손끝 기준에서만 생각한다면 베이스가 어디 있는지는 필요하지 않습니다.
로봇 제어에서는 두 관점이 모두 중요합니다. 작업 공간 전체에서 물체가 어디 있는지 알고 싶으면 베이스 좌표가 필요합니다. 반대로 지금 손끝에서 물체까지 어떻게 다가갈지 계산하려면 손끝 좌표가 더 직관적일 수 있습니다.
점만 바꾸는 것과 자세를 바꾸는 것
지금까지는 물체의 위치를 주로 다뤘습니다. 하지만 로봇이 물체를 잡으려면 위치만으로는 부족한 경우가 많습니다. 컵의 손잡이를 잡거나, 박스를 특정 방향으로 집거나, 드라이버를 나사 방향에 맞춰야 한다면 물체의 방향도 중요합니다.
점 하나만 변환할 때는 3D 점을 동차 좌표 [x, y, z, 1]로 만들어 변환 행렬을 곱하면 됩니다.
p_base = T_base_camera @ p_camera물체의 자세 전체를 변환할 때는 물체 좌표계 자체를 변환한다고 생각하면 됩니다.
T_base_object = T_base_camera @ T_camera_objectT_camera_object는 물체 좌표계가 카메라 좌표계에서 어디에 있고, 어느 방향을 향하는지를 나타냅니다. 여기에 T_base_camera를 곱하면 물체 좌표계가 베이스 좌표계에서 어디에 있고, 어느 방향을 향하는지 알 수 있습니다.
즉, 점 변환은 위치 하나를 옮기는 것이고, 자세 변환은 작은 좌표계 하나를 통째로 옮기는 것입니다. 로봇 손끝의 목표 자세를 만들 때는 보통 점이 아니라 자세 변환을 다룹니다.
좌표 변환에서 자주 나는 실수
카메라와 로봇 좌표 변환은 개념 자체보다 실수가 더 어렵습니다. 특히 축 방향과 행렬 순서가 문제를 자주 일으킵니다. 다음 항목들은 실제 코드를 작성할 때 꼭 확인해야 합니다.
T_base_camera와T_camera_base를 반대로 쓰지 않았는가행렬 곱 순서가
T_base_ee @ T_ee_camera @ T_camera_object처럼 좌표계 체인과 맞는가카메라의 z축이 앞쪽인지, 로봇의 x축이 앞쪽인지처럼 축 방향 약속을 확인했는가
픽셀 좌표
(u, v)와 3D 카메라 좌표(x, y, z)를 혼동하지 않았는가깊이 단위가 mm인지 m인지 확인했는가
이미지의 y축은 아래로 증가하지만 로봇의 z축은 위로 증가한다는 차이를 반영했는가
이 목록은 사소해 보이지만, 로봇에서는 사소하지 않습니다. 단위 하나를 잘못 쓰면 0.5m 움직여야 할 로봇이 500m를 목표로 삼을 수 있습니다. 축 하나를 반대로 쓰면 물체를 집는 대신 밀어 버릴 수 있습니다.
좌표 변환 코드를 디버깅할 때는 복잡한 장면부터 시작하지 않는 편이 좋습니다. 먼저 아주 단순한 점 하나를 둡니다. 예를 들어 카메라 정면 1m 앞의 점을 변환했을 때 로봇 기준으로 정말 앞쪽에 나오는지 확인합니다. 그다음 오른쪽 점, 위쪽 점을 하나씩 넣어 축 방향을 검증합니다.
작은 시뮬레이션: 카메라가 본 물체를 로봇 평면에 그리기
마지막으로 카메라가 본 물체 위치를 로봇 베이스 좌표로 바꾼 뒤, 위에서 내려다본 평면에 그려 보겠습니다. 3D 좌표를 모두 그리지 않아도, x-y 평면만 보면 로봇이 물체를 향해 어느 방향으로 가야 하는지 감이 옵니다.
import numpy as np
import matplotlib.pyplot as plt
def make_T(R, t):
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = t
return T
def transform_point(T, p):
p_h = np.array([p[0], p[1], p[2], 1.0])
result_h = T @ p_h
return result_h[:3] / result_h[3]
R_base_camera = np.array([
[ 0.0, 0.0, 1.0],
[-1.0, 0.0, 0.0],
[ 0.0, -1.0, 0.0],
])
t_base_camera = np.array([0.25, 0.0, 0.35])
T_base_camera = make_T(R_base_camera, t_base_camera)
# 카메라가 여러 물체를 보았다고 하자.
points_camera = np.array([
[ 0.00, 0.00, 1.0],
[ 0.20, 0.02, 1.2],
[-0.15, -0.01, 0.8],
[ 0.05, 0.05, 1.5],
])
points_base = np.array([
transform_point(T_base_camera, p) for p in points_camera
])
plt.figure(figsize=(6, 6))
plt.scatter(0, 0, c='blue', s=100, label='robot base')
plt.arrow(0, 0, 0.4, 0, head_width=0.05, color='blue')
plt.scatter(points_base[:, 0], points_base[:, 1], c='red', label='objects')
for i, p in enumerate(points_base):
plt.text(p[0], p[1], f'obj{i}')
plt.plot([0, p[0]], [0, p[1]], 'k--', alpha=0.3)
plt.axis('equal')
plt.grid(True)
plt.xlabel('base x: forward')
plt.ylabel('base y: left')
plt.legend()
plt.title('Objects observed by camera, expressed in robot base frame')
plt.show()이 그림에서 파란 점은 로봇 베이스이고, 빨간 점들은 카메라가 본 물체를 로봇 베이스 좌표로 변환한 결과입니다. 로봇 입장에서는 이제 물체들이 이미지 안의 픽셀이 아니라, 실제 평면 위의 목표점처럼 보입니다.
이 순간부터 카메라 정보는 로봇 제어와 연결됩니다. 물체가 오른쪽 앞에 있으면 로봇은 오른쪽으로 회전할 수 있고, 물체가 너무 가까우면 멈출 수 있으며, 로봇 팔은 손끝 목표 위치를 계산할 수 있습니다.
이번 장에서 얻은 좌표 감각
카메라 좌표와 로봇 좌표 변환은 로봇 비전의 입구입니다. 카메라가 아무리 선명한 이미지를 얻어도, 그 정보가 로봇의 좌표계로 바뀌지 않으면 로봇은 행동할 수 없습니다.
이번 장에서는 카메라 이미지의 픽셀 좌표가 깊이와 내부 파라미터를 만나 카메라 3D 좌표가 되는 과정을 살펴보았습니다. 그리고 카메라와 로봇 베이스 사이의 위치와 방향 관계를 동차 변환 행렬로 표현했습니다.
또한 Eye-in-hand와 Eye-to-hand의 차이를 비교했습니다. 카메라가 손끝에 붙어 있으면 base <- ee <- camera <- object 체인을 따라가고, 카메라가 바깥에 고정되어 있으면 base <- camera <- object 체인을 사용합니다. 어느 경우든 핵심은 같습니다. 좌표계 이름이 자연스럽게 이어지도록 변환 행렬을 곱하면 됩니다.
지금까지 우리는 로봇의 위치, 방향, 회전, 좌표 변환을 배웠습니다. 이제 로봇이 어디에 있고, 어느 방향을 보고 있으며, 카메라가 본 물체가 로봇 기준 어디에 있는지 말할 수 있습니다.
다음 부터는 로봇의 움직임을 시간으로 바라봅니다. 위치가 시간에 따라 변하면 속도가 되고, 속도가 변하면 가속도가 됩니다. 좌표와 행렬로 공간을 이해했다면, 이제 미분과 적분으로 움직임의 리듬을 이해할 차례입니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.