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 단위

이 장의 예제는 오른손 좌표계에서 고정 world 축에 대한 extrinsic Z-Y-X 합성, 즉 R=Rz(yaw)Ry(pitch)Rx(roll)R=R_z(yaw)R_y(pitch)R_x(roll)을 사용한다. pitch가 ±90° 근처가 되면 서로 다른 두 회전축이 정렬되어 자유도 하나를 구분할 수 없게 된다. 이것이 짐벌 락(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)

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