본문으로 건너뛰기

2.2 좌표계와 4×4 동차 변환

2.2.1 pose의 의미부터 고정한다

3차원 pose는 위치(position)와 방향(orientation)의 쌍이다. 회전행렬 ARB{}^{A}R_B는 B 좌표계의 벡터를 A 좌표계에서 표현하도록 바꾸고, 변위 AtB{}^{A}t_B는 A 원점에서 본 B 원점 위치다. 두 값을 합치면 다음 동차 변환을 얻는다.

ATB=[ARBAtB0  0  01] {}^{A}T_B = \begin{bmatrix} {}^{A}R_B & {}^{A}t_B \\ 0\;0\;0 & 1 \end{bmatrix}

B 좌표계 점 Bp{}^{B}p를 동차 좌표 [x,y,z,1]T[x,y,z,1]^T로 만들면 A 좌표계 표현은 다음과 같다.

Ap=ATB Bp {}^{A}p = {}^{A}T_B\,{}^{B}p

변환 체인의 곱셈 순서는 오른쪽부터 적용된다.

WTO=WTTTTO {}^{W}T_O = {}^{W}T_T {}^{T}T_O

여기서 W는 world, T는 table, O는 object 좌표계다. code/transforms.py의 Transform.compose()가 이 계산을 구현한다.

역변환은 일반 4×4 역행렬을 구하지 않고 회전행렬의 정규 직교 성질을 이용해 안정적으로 계산한다.

(ATB)−1=BTA=[RT−RTt01] ({}^{A}T_B)^{-1} = {}^{B}T_A = \begin{bmatrix} R^T & -R^Tt \\ 0 & 1 \end{bmatrix}

이해 도움말 — 변환 이름을 문장처럼 읽기

ATB{}^{A}T_B를 “B에서 A로 좌표를 표현하는 변환”이라고 읽는다. 점 Bp{}^{B}p 오른쪽에 가장 가까운 변환부터 적용되므로 WTT TTO Op{}^{W}T_T\,{}^{T}T_O\,{}^{O}p는 “Object의 점을 Table로, 다시 World로” 옮긴다. 첨자를 지워도 되는지 확인하려면 인접한 TT와 T{}^{T}, OO와 O{}^{O}가 연결되는지 본다. 코드 변수도 world_from_table처럼 같은 방향으로 읽히게 만들면 역변환 실수를 줄일 수 있다.

변환식을 오른쪽부터 읽는 방법

그림 2-2. 점에 가까운 변환부터 적용하고, 인접 프레임 이름이 이어지는지 확인한다.

2.2.2 active rotation과 passive rotation

같은 행렬이 두 문맥에서 사용되기 때문에 혼동이 생긴다.

  • Active rotation: 한 좌표계 안에서 물체 또는 벡터를 회전한다.
  • Passive rotation: 같은 물리 벡터를 다른 좌표계 성분으로 다시 표현한다.

두 연산은 서로 전치 관계다. API 문서가 rotate vector, transform pose, express in frame 중 어떤 의미를 쓰는지 확인해야 한다. 코드 리뷰에서는 변수명을 world_from_camera, camera_from_object처럼 읽을 수 있게 정한다. T_wc처럼 짧은 이름만 쓰면 첨자의 방향을 팀원이 다르게 해석할 수 있다.

2.2.3 Euler angle의 convention

Euler angle은 roll, pitch, yaw 세 값으로 방향을 설명해 사람이 이해하기 쉽다. 그러나 다음 정보를 함께 기록하지 않으면 값만으로 회전을 재현할 수 없다.

  • 축 순서: XYZ, ZYX 등
  • intrinsic 회전인지 extrinsic 회전인지
  • right-handed/left-handed 좌표계
  • radian/degree 단위

이 장은 오른손 좌표계와 열벡터 v′=Rvv'=Rv를 사용하고 R=Rz(yaw)Ry(pitch)Rx(roll)R=R_z(yaw)R_y(pitch)R_x(roll)로 행렬을 만든다. 벡터에 실제 적용되는 고정축 회전 순서는 오른쪽부터 X(roll)→Y(pitch)→Z(yaw)이며, extrinsic xyz라고 부른다. 이는 움직이는 몸체축을 사용하는 intrinsic Z→Y→X(yaw,pitch,roll)와 동치다. 코드의 euler_zyx_to_matrix 이름은 행렬을 왼쪽부터 Z·Y·X로 곱하는 관례를 보존한 것이며 고정축 적용 순서를 뜻하지 않는다. 회전 이름만 외우기보다 행렬·열벡터·적용 방향을 함께 적는다. SciPy의 공식 Euler 규약은 소문자 축을 extrinsic, 대문자 축을 intrinsic으로 구분한다. M02.02의 변환 방향·회전 순서 계산과 이어 확인한다. pitch가 정확히 ±90°일 때 Euler 표현의 두 회전축이 정렬되어 두 각도를 독립적으로 구분할 수 없게 된다. 이것이 짐벌 락(gimbal lock)이다. 그 근처에서는 조건이 나빠져 작은 회전·측정 오차에도 각도 표현이 크게 변할 수 있다. 실제 물체가 자유도를 잃는 것이 아니라 Euler 파라미터화가 특이해지는 것이다.

2.2.4 quaternion과 순서 문제

단위 쿼터니언은 네 성분으로 3차원 회전을 표현한다.

q=(x,y,z,w),x2+y2+z2+w2=1 q = (x,y,z,w), \qquad x^2+y^2+z^2+w^2=1

쿼터니언은 Euler angle처럼 특정 자세에서 자유도를 잃지 않고, spherical linear interpolation(SLERP)을 사용할 수 있다. 그러나 다음 함정이 있다.

  1. 라이브러리마다 (x,y,z,w)와 (w,x,y,z) 순서가 다르다.
  2. qq와 −q-q는 같은 회전을 나타낸다.
  3. 수치 적분 후 norm이 1에서 벗어날 수 있어 정규화가 필요하다.
  4. 두 쿼터니언의 선형 보간은 일정 각속도 회전을 보장하지 않는다.

code/transforms.py는 외부 인터페이스를 (x,y,z,w)로 고정하고 입력을 정규화한다. 실제 Isaac Sim/ROS/OpenCV 경계에서는 각 API의 순서를 어댑터 함수 하나로 격리하는 것이 좋다.

이 장의 예제 코드·설정 파일 (4)

이 장의 자가 점검·가상실습