포스트

그래픽스에서 3D 회전을 표현하는 방법

그래픽스에서 3D 회전을 표현하는 방법

3D 공간에서 물체를 회전시키는 방법을 다뤄요. 오일러 각(Euler Angle)의 개념부터 시작해 짐벌락(Gimbal Lock) 문제를 살펴보고, 이를 해결하는 쿼터니언(Quaternion)과 로드리게스 공식(Rodrigues formula)까지 정리해요.

3D 회전은 2D 회전과 뭐가 다를까

2D 회전부터 생각해 봐요. 종이 위에 점 하나를 찍고 원점을 기준으로 돌린다고 하면, 정해야 할 건 각도 하나뿐이에요. “30° 돌려”라고 하면 끝이에요. 이때 회전 행렬은 아래와 같아요.

\[\mathbf{R} = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}\]

이 행렬을 좌표 벡터 $(x, y)^T$에 곱하면 회전된 좌표 $(x’, y’)^T$를 얻어요. 여기서 $\cos$과 $\sin$이 왜 등장하는지 궁금하다면, 단위원을 떠올리면 돼요.

단위원 시각화

단위원 위의 점은 $(\cos\theta, \sin\theta)$이고, 이는 $(1, 0)$을 $\theta$만큼 반시계 방향으로 회전한 결과예요. 회전 행렬의 각 열이 바로 회전된 x축, y축의 방향을 나타내요.

3D로 넘어오면 상황이 복잡해져요. “30° 돌려”라고만 하면 어느 축을 기준으로? 라는 질문이 따라와요. 같은 각도라도 축이 다르면 결과가 완전히 달라져요. 그래서 3D 회전에는 회전각(amount)과 회전축(axis) 두 가지 정보가 모두 필요해요.

각 축에 대한 회전 행렬은 어떻게 생겼을까

가장 간단한 경우부터 보죠. 회전축이 x, y, z 중 하나와 정확히 일치하는 경우예요. 이를 축 정렬(axis-aligned) 회전이라고 해요.

\[\mathbf{R}_x = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta \\ 0 & \sin\theta & \cos\theta \end{bmatrix}\] \[\mathbf{R}_y = \begin{bmatrix} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \end{bmatrix}\] \[\mathbf{R}_z = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}\]

패턴이 보이나요? 각 행렬에서 회전축에 해당하는 행과 열은 항등 행렬과 같아요. $\mathbf{R}_z$를 예로 들면, 3행 3열이 1이고 나머지 3행/3열 원소는 0이에요. z축 기준으로 회전하면 z 좌표는 변하지 않고, x-y 평면에서만 2D 회전이 일어나기 때문이에요. 나머지 2×2 블록에 익숙한 2D 회전 행렬이 들어가 있어요.

$\mathbf{R}_y$에서 $\sin$ 부호가 다른 이유는 오른손 법칙 때문이에요. y축 기준 회전은 z→x 방향이 양의 회전 방향인데, 이를 행렬로 표현하면 부호가 뒤집혀요.

Roll, Pitch, Yaw는 무엇일까

오일러 각(Euler Angle)은 세 축에 대한 회전을 순서대로 적용해서 임의의 3D 회전을 만드는 방식이에요. 항공기 자세 제어에서 유래한 Roll, Pitch, Yaw라는 이름으로 자주 불려요.

항공기 예시

비행기를 상상해 보세요. 기수(nose)가 앞을 향하고 있어요.

  • Roll(x축 회전): 기수 방향 축을 기준으로 좌우로 기울이는 동작이에요. 날개 한쪽이 올라가고 반대쪽이 내려가요.
  • Pitch(y축 회전): 날개를 관통하는 축을 기준으로 기수를 위아래로 드는 동작이에요. 받음각을 조절해서 고도를 바꿔요.
  • Yaw(z축 회전): 수직축을 기준으로 기수를 좌우로 돌리는 동작이에요. 방향을 전환할 때 사용해요.

세 회전을 행렬 곱으로 합치면 하나의 회전 행렬이 돼요.

\[\mathbf{R} = \mathbf{R}_z \cdot \mathbf{R}_y \cdot \mathbf{R}_x\]

여기서 주의할 점이 있어요. 행렬 곱은 교환법칙이 성립하지 않아요. 그래서 회전 순서에 대한 약속이 필요해요. 분야마다 관례가 달라요.

분야 순서 설명
항공기, 해양 Z-Y-X (Yaw-Pitch-Roll) 관성이 큰 축부터 회전
카메라, 게임 엔진 X-Y-Z (Pitch-Yaw-Roll) 카메라 팬/틸트/롤 순서
로보틱스, 수학적 모델링 Z-Y-Z (Proper Euler) 로봇 팔 끝단 자세 표현에 유리

같은 (30°, 45°, 60°)이라도 Z-Y-X 순서로 적용한 결과와 X-Y-Z 순서로 적용한 결과는 완전히 달라요. 라이브러리나 엔진을 사용할 때 어떤 회전 순서를 쓰는지 반드시 확인해야 해요.

오일러 각에는 치명적인 약점이 있다

오일러 각은 직관적이고 파라미터도 3개뿐이라 편리해 보이지만, 짐벌락(Gimbal Lock)이라는 구조적 문제가 있어요. 짐벌은 세 개의 동심원으로 이루어진 물리적 장치예요. 카메라 짐벌을 떠올리면 돼요. 바깥 고리, 중간 고리, 안쪽 고리가 각각 하나의 회전축(Yaw, Pitch, Roll)을 담당해요. 세 고리가 독립적으로 회전하면 3자유도(Degree of Freedom)를 가져요.

Gimbal Lock 예시 가운데 초록색 축이 90°로 고정되면 gimbal lock이 발생함

문제는 두 번째(가운데) 축이 ±90° 회전할 때 발생해요. Z-Y-X 순서를 예로 들어 볼게요. 정상 상태에서는 바깥 고리(Z축/Yaw), 중간 고리(Y축/Pitch), 안쪽 고리(X축/Roll)가 서로 다른 방향을 가리키고 있어요. 그런데 Pitch를 90°로 꺾으면, 바깥 고리(Yaw)와 안쪽 고리(Roll)의 회전축이 같은 방향을 가리키게 돼요. 두 슬라이더를 움직여도 같은 방향으로만 회전하니, 실질적으로 축 하나를 잃은 셈이에요. 자유도가 3에서 2로 줄어요.

회전 순서 짐벌락 조건 설명
ZYX Y축 = ±90° Yaw와 Roll이 겹침
XZY Z축 = ±90° 두 번째 축이 90°일 때 발생
XYZ Y축 = ±90° 두 번째 축이 90°일 때 발생

패턴이 보여요. 어떤 순서를 쓰든 가운데 축이 ±90°가 되면 짐벌락이 발생해요. 순서를 바꿔도 해결되지 않아요. 오일러 각이라는 표현 방식 자체의 구조적 한계예요.

쿼터니언은 왜 필요할까

짐벌락을 근본적으로 피하려면 회전을 표현하는 방식 자체를 바꿔야 해요. 그래서 등장한 것이 쿼터니언(Quaternion)이에요. 쿼터니언은 4개의 숫자(사원수)로 회전을 나타내요.

\[q = w + xi + yj + zk\]

여기서 $i, j, k$는 허수 단위예요. 복소수 $a + bi$가 2D 회전과 관련 있듯이, 쿼터니언은 복소수를 4차원으로 확장한 것으로 3D 회전을 다뤄요. $w$는 스칼라(실수부), $(x, y, z)$는 벡터 부분이에요. 회전을 올바르게 표현하려면 단위 쿼터니언 조건이 필요해요.

\[w^2 + x^2 + y^2 + z^2 = 1\]

이 조건은 쿼터니언이 4차원 공간의 단위 구면(hypersphere) 위에 있다는 뜻이에요. 3D 공간에서 단위 벡터가 단위 구(sphere) 표면 위의 점인 것과 같은 맥락이에요.

“축 $\hat{n}$을 기준으로 $\theta$만큼 회전”이라는 정보가 있을 때, 쿼터니언으로 변환하는 공식은 아래와 같아요.

\[w = \cos(\theta/2)\] \[x = n_x \cdot \sin(\theta/2)\] \[y = n_y \cdot \sin(\theta/2)\] \[z = n_z \cdot \sin(\theta/2)\]

여기서 $\hat{n} = (n_x, n_y, n_z)$는 단위 회전축이에요. 회전각의 절반인 $\theta/2$를 사용한다는 점이 독특해요. 이렇게 하면 $w^2 + x^2 + y^2 + z^2 = \cos^2(\theta/2) + \sin^2(\theta/2)(n_x^2 + n_y^2 + n_z^2) = 1$이 자연스럽게 만족돼요. $(n_x^2 + n_y^2 + n_z^2)$는 단위 회전축의 크기를 나타내기 때문에 합은 1이 돼요. 1을 대입하면 $\cos^2(\theta/2) + \sin^2(\theta/2)$이 남고, $\cos$, $\sin$ 그래프를 중첩해서 떠올려보면 합이 1이 된다는 것을 알 수 있어요.

cos-sin 그래프

임의의 축으로 회전하려면 어떻게 할까

지금까지는 x, y, z 축 정렬 회전을 다뤘어요. 하지만 현실에서는 $(1, 1, 1)$ 방향처럼 비스듬한 축을 기준으로 회전해야 할 때가 많아요. 이때 사용하는 것이 Rodrigues 회전 공식이에요. 회전축 $\hat{u}$와 회전각 $\theta$를 하나의 벡터로 합칠 수 있어요.

\[\mathbf{r} = \theta \hat{u}\]

벡터의 방향이 회전축, 크기 $\vert \mathbf{r} \vert = \theta$가 회전각이에요. 예를 들어 $\mathbf{r} = (0, 0, \pi/2)$는 z축 기준 90° 회전을 뜻해요. 3개의 숫자로 회전을 표현할 수 있지만, 행렬 곱으로 표현하기 위해서는 회전 행렬이나 쿼터니언으로 변환해야 해요. 이 변환을 exponential map이라고 불러요.

임의의 단위 축 $\hat{r}$을 기준으로 점 $\mathbf{x}$를 $\theta$만큼 회전한 결과 $\mathbf{x}’$를 구해 봐요. 핵심 아이디어는 벡터를 두 성분으로 분해하는 거예요.

\[\mathbf{x} = \mathbf{x}_{\parallel} + \mathbf{x}_\perp\]
  • $\mathbf{x}_{\parallel}$: 회전축과 평행한 성분. 축 위에 있으니 아무리 돌려도 변하지 않아요.
  • $\mathbf{x}_{\perp}$: 회전축에 수직인 성분. 이 성분만 회전 평면 위에서 원운동을 해요.

rodrigues 시각화

회전축 위에서 아래를 내려다본다고 상상해 보세요. $\mathbf{x}_ {\perp}$가 원 위를 따라 움직이는 모습이 보여요. 이건 결국 2D 회전이에요. 2D 회전에는 서로 직교하는 두 방향이 필요한데, 하나는 $\mathbf{x}_ {\perp}$ 자체이고, 다른 하나는 $\mathbf{x}_ {\vdash} = \hat{r} \times \mathbf{x}$ (외적)로 만들 수 있어요. 이 두 방향을 기저로 삼으면, 회전 결과는 2D 회전과 같은 형태가 돼요.

\[\mathbf{x}' = \mathbf{x}_ {\parallel} + \mathbf{x}_ {\perp} \cos\theta + \mathbf{x}_ {\vdash} \sin\theta\]

3D 회전의 본질이 보이나요? 축에 수직인 평면 위에서 $\cos\theta$만큼 원래 자리를 유지하고, $\sin\theta$만큼 직교 방향으로 이동하는 과정이에요. 축 방향 성분은 건드리지 않아요. 위 식에서 $\mathbf{x}_ {\parallel}, \mathbf{x}_ {\perp}, \mathbf{x}_ {\vdash}$를 모두 $\hat{r}$과 $\mathbf{x}$의 내적/외적으로 바꿀 수 있어요.

\[\mathbf{x}' = \hat{r}(\hat{r} \cdot \mathbf{x}) + \sin\theta(\hat{r} \times \mathbf{x}) - \cos\theta\big(\hat{r} \times (\hat{r} \times \mathbf{x})\big)\]

각 항의 역할을 정리하면 이래요.

  • $\hat{r}(\hat{r} \cdot \mathbf{x})$: 축 방향 성분(투영). 회전해도 그대로예요.
  • $\sin\theta(\hat{r} \times \mathbf{x})$: $\mathbf{x}_\perp$를 90° 돌린 방향 성분. 회전 평면에서 $\sin$ 역할이에요.
  • $-\cos\theta\big(\hat{r} \times (\hat{r} \times \mathbf{x})\big)$: 원래 수직 성분의 $\cos$ 크기만큼 남은 부분이에요. 이중 외적 $\hat{r} \times (\hat{r} \times \mathbf{x})$는 $\mathbf{x}_ \perp$를 180° 돌린 것, 즉 $-\mathbf{x}_ \perp$와 같아서 앞에 음수 부호가 붙어요.

외적을 행렬로 바꾸면 회전 행렬이 된다

위 벡터곱 형태의 Rodrigues 공식은 벡터 하나를 회전시킬 때는 잘 동작해요. 하지만 수천 개의 정점을 한꺼번에 회전시키려면, 매번 내적과 외적을 따로 계산하는 건 비효율적이에요. $\mathbf{x}’ = \mathbf{R}\mathbf{x}$ 형태의 행렬 곱 한 번으로 처리하고 싶어요. 그러려면 외적 연산을 행렬로 바꿔야 해요.

외적의 정의부터 다시 볼게요. $\hat{r} = (\hat{r}_x, \hat{r}_y, \hat{r}_z)$와 $\mathbf{v} = (v_x, v_y, v_z)$의 외적은 아래와 같아요.

\[\hat{r} \times \mathbf{v} = \begin{pmatrix} \hat{r}_y v_z - \hat{r}_z v_y \\ \hat{r}_z v_x - \hat{r}_x v_z \\ \hat{r}_x v_y - \hat{r}_y v_x \end{pmatrix}\]

이 결과를 잘 보면 $\mathbf{v}$의 각 성분에 대한 선형 결합이에요. 선형 결합은 행렬-벡터 곱으로 쓸 수 있어요. 실제로 위 식을 행렬 곱 형태로 풀어보면 이렇게 돼요.

\[\hat{r} \times \mathbf{v} = \begin{bmatrix} 0 & -\hat{r}_z & \hat{r}_y \\ \hat{r}_z & 0 & -\hat{r}_x \\ -\hat{r}_y & \hat{r}_x & 0 \end{bmatrix} \begin{pmatrix} v_x \\ v_y \\ v_z \end{pmatrix}\]

이 행렬을 skew-symmetric matrix라고 부르고, $\mathbf{K}$로 표기할게요.

\[\mathbf{K} = (\hat{r}\times) = \begin{bmatrix} 0 & -\hat{r}_z & \hat{r}_y \\ \hat{r}_z & 0 & -\hat{r}_x \\ -\hat{r}_y & \hat{r}_x & 0 \end{bmatrix}\]

“skew-symmetric”이라는 이름은 대각선을 기준으로 부호가 반대인 성질($K_{ij} = -K_{ji}$)에서 나와요. 대각 원소는 모두 0이에요.

이제 앞서 유도한 벡터곱 형태의 Rodrigues 공식으로 돌아가 봐요.

\[\mathbf{x}' = \hat{r}(\hat{r} \cdot \mathbf{x}) + \sin\theta(\hat{r} \times \mathbf{x}) - \cos\theta\big(\hat{r} \times (\hat{r} \times \mathbf{x})\big)\]

각 항을 행렬로 바꿀 수 있어요.

  • $\hat{r}(\hat{r} \cdot \mathbf{x}) = \hat{r}\hat{r}^T \mathbf{x}$: 외적 행렬 $\hat{r}\hat{r}^T$는 회전축 방향으로의 투영 행렬이에요.
  • $\hat{r} \times \mathbf{x} = \mathbf{K}\mathbf{x}$: 방금 유도한 skew-symmetric matrix예요.
  • $\hat{r} \times (\hat{r} \times \mathbf{x}) = \mathbf{K}(\mathbf{K}\mathbf{x}) = \mathbf{K}^2\mathbf{x}$: $\mathbf{K}$를 두 번 곱한 거예요.

대입하면 이렇게 돼요.

\[\mathbf{x}' = \hat{r}\hat{r}^T\mathbf{x} + \sin\theta \cdot \mathbf{K}\mathbf{x} - \cos\theta \cdot \mathbf{K}^2\mathbf{x}\]

$\mathbf{x}$를 공통으로 묶어내면 $\mathbf{x}’ = \mathbf{R}\mathbf{x}$ 형태가 되고, 회전 행렬 $\mathbf{R}$은 아래와 같아요.

\[\mathbf{R} = \hat{r}\hat{r}^T + \sin\theta \cdot \mathbf{K} - \cos\theta \cdot \mathbf{K}^2\]

여기서 $\hat{r}\hat{r}^T = \mathbf{I} + \mathbf{K}^2$ (skew-symmetric matrix의 성질)을 이용하면 더 깔끔하게 정리돼요.

\[\mathbf{R} = \mathbf{I}\cos\theta + (1 - \cos\theta)\hat{r}\hat{r}^T + \sin\theta \cdot \mathbf{K}\]

이것이 Rodrigues rotation formula의 행렬형이에요. 회전축 $\hat{r}$과 회전각 $\theta$만 알면, $\mathbf{K}$를 만들고 위 공식에 넣어서 임의의 3D 회전 행렬을 바로 구할 수 있어요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""Rodrigues rotation 구현"""
import numpy as np

def rodrigues_rotate(v, axis, theta):
    rx, ry, rz = axis
    I = np.eye(3)

    K = np.array([
        [0, -rz, ry],
        [rz, 0, -rx],
        [-ry, rx, 0]
    ])
    rrT = np.outer(axis, axis)
    cos_t = np.cos(theta)
    sin_t = np.sin(theta)

    R = I * cos_t + (1 - cos_t) * rrT + sin_t * K
    v_rotated = R @ v
    
    return v_rotated, R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""OpenCV Rodrigues 예제"""
import cv2
import numpy as np

theta = np.pi / 2
rotation_vector = np.array([0, 0, theta], dtype=np.float32)

# 벡터 -> 행렬 (3x3 Matrix 반환)
rotation_matrix, _ = cv2.Rodrigues(rotation_vector)

# 행렬 -> 벡터 (3x1 Vector 반환)
rotation_vector_back, _ = cv2.Rodrigues(rotation_matrix)

# 확인
is_equal = np.allclose(rotation_vector, rotation_vector_back.flatten())
assert is_equal

Exponential Map은 어디서 나온 걸까

앞에서 Rodrigues 공식으로 회전 행렬을 구했어요. 그런데 이 공식이 지수 함수(exponential function)와 깊은 관계가 있다는 사실이 있어요. 이 연결 고리를 이해하려면 먼저 테일러 급수라는 도구를 알아야 해요.

테일러 급수란

복잡한 함수를 덧셈과 곱셈만으로 근사하는 방법이에요.

어떤 함수의 정확한 값을 계산하기 어려울 때, 다항식으로 근사할 수 있어요. 예를 들어 $e^x$는 아래처럼 무한 다항식의 합으로 정확하게 표현돼요.

\[e^x = 1 + \frac{x}{1!} + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!} + \cdots\]

같은 방식으로 $\sin$과 $\cos$도 다항식으로 쓸 수 있어요.

\[\sin\theta = \frac{\theta}{1!} - \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \cdots\] \[\cos\theta = 1 - \frac{\theta^2}{2!} + \frac{\theta^4}{4!} - \cdots\]

$\sin$은 홀수 차수($\theta, \theta^3, \theta^5, \cdots$)만, $\cos$은 짝수 차수($1, \theta^2, \theta^4, \cdots$)만 나타나는 게 특징이에요. 부호는 번갈아 바뀌어요.

숫자 대신 행렬을 넣으면 어떻게 될까

테일러 급수에서 $x$ 자리에 숫자 대신 행렬을 넣을 수도 있어요. 이때 숫자 1은 항등 행렬 $\mathbf{I}$로 바꿔요. 회전축의 skew-symmetric matrix $\mathbf{K}$에 회전각 $\theta$를 곱한 $\mathbf{K}\theta$를 넣으면 이렇게 돼요.

\[e^{\mathbf{K}\theta} = \mathbf{I} + \frac{\mathbf{K}\theta}{1!} + \frac{(\mathbf{K}\theta)^2}{2!} + \frac{(\mathbf{K}\theta)^3}{3!} + \frac{(\mathbf{K}\theta)^4}{4!} + \cdots\]

$(\mathbf{K}\theta)^2 = \mathbf{K}^2\theta^2$이므로 정리하면 아래와 같아요.

\[e^{\mathbf{K}\theta} = \mathbf{I} + \frac{\mathbf{K}\theta}{1!} + \frac{\mathbf{K}^2\theta^2}{2!} + \frac{\mathbf{K}^3\theta^3}{3!} + \frac{\mathbf{K}^4\theta^4}{4!} + \cdots\]

이게 행렬 지수(matrix exponential)예요. 숫자에 대한 $e^x$를 행렬로 확장한 것이에요.

$\mathbf{K}$의 거듭제곱은 반복된다

이 무한 급수를 실제로 계산할 수 있을까요? 다행히 $\mathbf{K}$에는 특별한 성질이 있어요. 결론부터 말하면 $\mathbf{K}^3 = -\mathbf{K}$예요. 단계별로 왜 그런지 따라가 볼게요.

먼저 $\mathbf{K}^2$이 뭔지 알아야 해요. $\mathbf{K}^2 = \mathbf{K} \cdot \mathbf{K}$를 직접 계산하면 아래와 같은 항등식이 성립해요.

\[\mathbf{K}^2 = \hat{r}\hat{r}^T - \mathbf{I}\]

이제 양변에 $\mathbf{K}$를 한 번 더 곱해서 $\mathbf{K}^3$을 구해 봐요.

\[\mathbf{K}^3 = \mathbf{K} \cdot \mathbf{K}^2 = \mathbf{K}(\hat{r}\hat{r}^T - \mathbf{I}) = \mathbf{K}\hat{r}\hat{r}^T - \mathbf{K}\]

여기서 $\mathbf{K}\hat{r}$가 뭔지 생각해 보세요. $\mathbf{K}$는 $\hat{r}$의 외적을 행렬로 바꾼 것이므로, $\mathbf{K}\hat{r} = \hat{r} \times \hat{r}$이에요. 어떤 벡터를 자기 자신과 외적하면 항상 영벡터가 돼요. (외적의 결과는 두 벡터에 수직인 벡터인데, 같은 방향의 벡터 사이에는 “수직 방향”을 정의할 수 없기 때문이에요.)

\[\mathbf{K}\hat{r} = \hat{r} \times \hat{r} = \mathbf{0}\]

$\mathbf{K}\hat{r} = \mathbf{0}$이면 $\mathbf{K}\hat{r}\hat{r}^T$도 영행렬이에요. ($\mathbf{0}$에 $\hat{r}^T$를 곱해도 $\mathbf{0}$이에요.) 따라서 위 식에서 첫 번째 항이 사라지고 아래만 남아요.

\[\mathbf{K}^3 = \mathbf{0} - \mathbf{K} = -\mathbf{K}\]

이후로는 $\mathbf{K}^3 = -\mathbf{K}$를 반복 적용하면 돼요.

\[\mathbf{K}^4 = \mathbf{K} \cdot \mathbf{K}^3 = -\mathbf{K}^2\] \[\mathbf{K}^5 = \mathbf{K} \cdot \mathbf{K}^4 = -\mathbf{K}^3 = \mathbf{K}\] \[\mathbf{K}^6 = -\mathbf{K}, \quad \cdots\]

$\mathbf{K}$와 $\mathbf{K}^2$ 두 종류만 부호를 바꿔가며 반복돼요. 덕분에 무한 급수가 두 그룹으로 깔끔하게 나뉘어요.

$\mathbf{K}$가 들어간 항과 $\mathbf{K}^2$가 들어간 항을 각각 모아 볼게요.

\[e^{\mathbf{K}\theta} = \mathbf{I} + \mathbf{K}\underbrace{\left(\frac{\theta}{1!} - \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \cdots\right)}_{\sin\theta} + \mathbf{K}^2\underbrace{\left(\frac{\theta^2}{2!} - \frac{\theta^4}{4!} + \frac{\theta^6}{6!} - \cdots\right)}_{1 - \cos\theta}\]

괄호 안의 급수를 앞에서 본 $\sin$, $\cos$의 테일러 급수와 비교해 보세요. 첫 번째 괄호는 $\sin\theta$와 정확히 같고, 두 번째 괄호는 $1 - \cos\theta$와 정확히 같아요. 따라서

\[e^{\mathbf{K}\theta} = \mathbf{I} + \sin\theta \cdot \mathbf{K} + (1 - \cos\theta) \cdot \mathbf{K}^2\]

이 결과를 Rodrigues 공식의 행렬형과 나란히 놓으면

\[\mathbf{R} = \mathbf{I}\cos\theta + (1 - \cos\theta)\hat{r}\hat{r}^T + \sin\theta \cdot \mathbf{K}\]

$\hat{r}\hat{r}^T = \mathbf{I} + \mathbf{K}^2$을 대입하면 두 식이 완전히 동일해요. 즉, $e^{\mathbf{K}\theta} = \mathbf{R}$이에요.

이게 왜 Exponential Map일까

정리하면, 회전축 벡터 $\mathbf{r} = \theta\hat{u}$에서 skew-symmetric matrix $\mathbf{K}$를 만들고, 이를 지수 함수의 지수 자리에 올려 보내면 회전 행렬이 나와요.

\[\mathbf{r} = \theta\hat{u} \quad \xrightarrow{\text{exp}} \quad \mathbf{R} = e^{\mathbf{K}\theta}\]

이 과정을 exponential map이라고 불러요. “map”이라는 이름은 한 공간의 원소를 다른 공간의 원소로 대응시킨다는 뜻이에요. 여기서는 평평한 벡터 공간에서 곡면의 회전 공간으로 올려 보내는 과정이에요.

Exponential map 예시

비유하자면 이래요. 지구본 위의 한 점에서 종이를 접선으로 대고, 그 종이(평면) 위에 작은 화살표를 그려요. 이 화살표가 축-각도 벡터 $\mathbf{r}$이에요. Exponential map은 이 평면 위의 화살표를 지구본 표면(곡면) 위의 실제 이동으로 변환하는 과정이에요.

결국 어떤 표현을 써야 할까

이 글에서 다룬 회전 표현을 정리해 볼게요.

  • 오일러 각은 Roll, Pitch, Yaw 세 각도만으로 회전을 나타내요. 사람이 읽기 쉽고 파라미터도 3개뿐이에요. 하지만 가운데 축이 ±90°가 되면 짐벌락이 발생해서 자유도를 하나 잃어요. 항공기처럼 Pitch가 극단적인 값을 잘 넘지 않는 상황에서는 괜찮지만, 임의의 3D 회전을 안전하게 다루기엔 부족해요.
  • 회전 행렬은 3×3 행렬 $\mathbf{R}$로 회전을 표현해요. $\mathbf{x}’ = \mathbf{R}\mathbf{x}$ 한 번이면 회전이 끝나고, 짐벌락도 없어요. 하지만 숫자가 9개나 필요한데 실제 자유도는 3이에요. 나머지 6개는 직교 조건($\mathbf{R}^T\mathbf{R} = \mathbf{I}$)과 행렬식 조건($\det\mathbf{R} = 1$)이라는 제약으로 묶여 있어요. 반복 연산 중에 부동소수점 오차가 쌓이면 이 제약이 깨질 수 있어서 주기적으로 보정이 필요해요.
  • 축-각도 벡터 $\mathbf{r} = \theta\hat{u}$는 회전축과 회전각을 3개의 숫자로 담아요. 직관적이고 짐벌락도 없지만, 이 벡터를 직접 곱셈에 쓸 수는 없어요. Rodrigues 공식이나 exponential map을 거쳐 회전 행렬로 변환한 뒤에야 실제 회전 연산이 가능해요. 두 회전을 합성하려면 매번 행렬로 바꿔서 곱한 뒤 다시 축-각도로 돌아와야 하므로 불편해요.
  • 쿼터니언 $q = (w, x, y, z)$는 4개의 숫자로 회전을 나타내요. 단위 쿼터니언 조건 $w^2 + x^2 + y^2 + z^2 = 1$ 하나만 지키면 짐벌락 없이 모든 3D 회전을 표현할 수 있어요. 두 회전의 합성은 쿼터니언끼리 곱하면 돼요. 정규화도 4개의 숫자를 단위 길이로 나누기만 하면 되니까 회전 행렬보다 보정이 간단해요. 그래서 게임 엔진, 그래픽스, 로보틱스에서 내부 회전 표현으로 가장 많이 사용돼요.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.