10.20 쿼터니언과 오일러 각의 상호 변환
1. 변환의 동기
쿼터니언은 내부 자세 표현에 적합하지만, 사용자 인터페이스와 외부 통신에서는 오일러 각이 더 직관적이다. 따라서 쿼터니언과 오일러 각 사이의 변환이 빈번히 필요하다.
본 절에서는 가장 일반적으로 사용되는 ZYX 테이트-브라이언 규약(롤-피치-요)에 대한 변환 공식을 다룬다.
2. 오일러 각 규약의 명시
오일러 각의 다양한 규약(12가지) 중 어느 것을 사용하는지 명시해야 한다. 본 절에서는 다음을 사용한다.
- 규약: ZYX 테이트-브라이언, 이동 축
- 각도: 요(yaw) \psi, 피치(pitch) \theta, 롤(roll) \phi
- 순서: 먼저 z축 주위 \psi 회전, 다음 y'축 주위 \theta 회전, 마지막으로 x''축 주위 \phi 회전
이 규약은 항공 우주와 로봇 공학에서 가장 일반적이다.
3. 오일러 각에서 쿼터니언으로의 변환
3.1 단계별 변환
ZYX 이동 축 규약에서 회전이 다음과 같이 합성된다.
\mathbf{R} = \mathbf{R}_z(\psi)\mathbf{R}_y(\theta)\mathbf{R}_x(\phi)
쿼터니언으로 표현하면
\mathbf{q} = \mathbf{q}_z(\psi)\mathbf{q}_y(\theta)\mathbf{q}_x(\phi)
여기서 각 기본 회전 쿼터니언은 다음과 같다.
\mathbf{q}_x(\phi) = (\cos(\phi/2), \sin(\phi/2), 0, 0)
\mathbf{q}_y(\theta) = (\cos(\theta/2), 0, \sin(\theta/2), 0)
\mathbf{q}_z(\psi) = (\cos(\psi/2), 0, 0, \sin(\psi/2))
3.2 직접 공식
세 쿼터니언 곱을 전개하면 다음의 직접 공식이 얻어진다.
q_w = \cos(\psi/2)\cos(\theta/2)\cos(\phi/2) + \sin(\psi/2)\sin(\theta/2)\sin(\phi/2)
q_x = \cos(\psi/2)\cos(\theta/2)\sin(\phi/2) - \sin(\psi/2)\sin(\theta/2)\cos(\phi/2)
q_y = \cos(\psi/2)\sin(\theta/2)\cos(\phi/2) + \sin(\psi/2)\cos(\theta/2)\sin(\phi/2)
q_z = \sin(\psi/2)\cos(\theta/2)\cos(\phi/2) - \cos(\psi/2)\sin(\theta/2)\sin(\phi/2)
이 공식이 ZYX 이동 축 규약에 대한 변환이다. 다른 규약의 경우 각도와 곱의 순서가 다르다.
3.3 약식 표기
c_a = \cos(a/2), s_a = \sin(a/2)로 약식 표기하면
q_w = c_\psi c_\theta c_\phi + s_\psi s_\theta s_\phi
q_x = c_\psi c_\theta s_\phi - s_\psi s_\theta c_\phi
q_y = c_\psi s_\theta c_\phi + s_\psi c_\theta s_\phi
q_z = s_\psi c_\theta c_\phi - c_\psi s_\theta s_\phi
4. 쿼터니언에서 오일러 각으로의 변환
역방향 변환은 더 복잡하다. 쿼터니언으로부터 ZYX 오일러 각을 추출하는 공식은 다음과 같다.
4.1 롤 (Roll)
\phi = \mathrm{atan2}(2(q_w q_x + q_y q_z), 1 - 2(q_x^2 + q_y^2))
4.2 피치 (Pitch)
\theta = \arcsin(2(q_w q_y - q_z q_x))
또는 더 수치적으로 안정한 형태로
\theta = -\arcsin(\mathrm{clamp}(2(q_z q_x - q_w q_y), -1, 1))
부호와 클램핑은 정의의 일관성을 위해 필요하다.
4.3 요 (Yaw)
\psi = \mathrm{atan2}(2(q_w q_z + q_x q_y), 1 - 2(q_y^2 + q_z^2))
이 공식들은 쿼터니언으로부터 회전 행렬을 추출한 후 회전 행렬에서 오일러 각을 추출한 결과이다.
5. 짐벌 락의 처리
피치 각이 \pm \pi/2에 가까우면 짐벌 락이 발생하여 롤과 요가 독립적으로 결정되지 않는다.
5.1 짐벌 락 검출
sin_pitch = 2 * (qw * qy - qz * qx)
if abs(sin_pitch) >= 1 - epsilon:
# 짐벌 락
handle_gimbal_lock()
5.2 짐벌 락 처리
짐벌 락이 발생하면 롤과 요 중 하나를 0으로 고정하고 다른 하나를 결정한다.
if sin_pitch >= 1 - epsilon:
# 피치 = +pi/2
pitch = pi / 2
roll = 0
yaw = -2 * atan2(qx, qw)
elif sin_pitch <= -(1 - epsilon):
# 피치 = -pi/2
pitch = -pi / 2
roll = 0
yaw = 2 * atan2(qx, qw)
이는 짐벌 락 근처에서 일관된 결과를 보장한다.
6. 변환의 알고리즘
6.1 오일러 각 → 쿼터니언
function euler_to_quaternion(roll, pitch, yaw):
cr = cos(roll / 2)
sr = sin(roll / 2)
cp = cos(pitch / 2)
sp = sin(pitch / 2)
cy = cos(yaw / 2)
sy = sin(yaw / 2)
q.w = cr * cp * cy + sr * sp * sy
q.x = sr * cp * cy - cr * sp * sy
q.y = cr * sp * cy + sr * cp * sy
q.z = cr * cp * sy - sr * sp * cy
return q
6.2 쿼터니언 → 오일러 각
function quaternion_to_euler(q):
# 롤
sinr_cosp = 2 * (q.w * q.x + q.y * q.z)
cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y)
roll = atan2(sinr_cosp, cosr_cosp)
# 피치 (짐벌 락 처리)
sinp = 2 * (q.w * q.y - q.z * q.x)
if abs(sinp) >= 1:
pitch = sign(sinp) * pi / 2
else:
pitch = asin(sinp)
# 요
siny_cosp = 2 * (q.w * q.z + q.x * q.y)
cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z)
yaw = atan2(siny_cosp, cosy_cosp)
return roll, pitch, yaw
7. 부호 이중성과 변환
쿼터니언의 부호 이중성(\mathbf{q}와 -\mathbf{q}가 같은 회전)이 오일러 각으로의 변환에 영향을 준다. 두 부호 모두 같은 오일러 각을 산출해야 한다.
확인: 위 공식에서 \mathbf{q}의 모든 성분에 부호를 반전시키면, q_a q_b 형태의 모든 항도 부호가 유지된다(두 음수의 곱은 양수). 따라서 결과 오일러 각이 같다.
8. 변환의 정확도
8.1 일반적인 경우
피치 각이 \pm \pi/2에서 충분히 떨어져 있으면 변환이 정확하다. 부동 소수점 오차의 임계값 내에서 결과가 정확하다.
8.2 짐벌 락 근처
피치 각이 \pm \pi/2에 가까우면 \arcsin의 도함수가 발산하여 수치 오차가 증가한다. 위에서 제시한 클램핑과 특수 처리가 필요하다.
8.3 작은 회전
회전 각이 작으면 변환의 모든 단계가 안정적이다.
9. 다른 오일러 각 규약
본 절에서는 ZYX 이동 축 규약을 다루었지만, 다른 11가지 규약도 존재한다. 각 규약마다 변환 공식이 다르다. 일반적인 규약은 다음과 같다.
| 규약 | 분야 |
|---|---|
| ZYX 이동 축 | 항공 우주, 로봇 공학 |
| XYZ 고정 축 | ZYX 이동 축과 등가 |
| ZYZ | 매니퓰레이터 (PUMA 등) |
| ZXZ | 천체 역학, 고전 역학 |
각 규약에 대한 쿼터니언 변환 공식은 위와 비슷한 방법으로 유도된다. 라이브러리는 이러한 모든 규약을 지원할 수 있다.
10. 변환의 응용
10.1 사용자 인터페이스
사용자가 자세를 입력하거나 결과를 볼 때 오일러 각이 사용된다. 내부 처리는 쿼터니언으로 수행한다.
10.2 URDF/SDF
ROS의 URDF나 SDF 형식에서 링크의 자세는 RPY로 기술된다. 로봇 모델 로더가 이를 쿼터니언으로 변환한다.
10.3 텔레메트리
항공기나 드론의 자세 텔레메트리가 RPY로 전송된다. 수신 측에서는 쿼터니언으로 변환하여 처리한다.
10.4 시각화
3D 시각화 도구가 RPY를 입력으로 받을 수 있다. 내부 표현이 쿼터니언이면 변환이 필요하다.
11. 변환의 라이브러리 지원
대부분의 회전 라이브러리가 변환을 지원한다.
11.1 Eigen
Eigen::Quaterniond q = Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ()) *
Eigen::AngleAxisd(pitch, Eigen::Vector3d::UnitY()) *
Eigen::AngleAxisd(roll, Eigen::Vector3d::UnitX());
11.2 NumPy/SciPy
from scipy.spatial.transform import Rotation
q = Rotation.from_euler('zyx', [yaw, pitch, roll]).as_quat()
11.3 ROS의 tf2
tf2::Quaternion q;
q.setRPY(roll, pitch, yaw);
각 라이브러리는 다른 매개변수 순서와 규약을 사용하므로 문서를 참조한다.
12. 변환의 일관성
변환의 일관성을 검증할 때 유의할 점이 있다.
12.1 왕복 변환
쿼터니언 → 오일러 각 → 쿼터니언의 결과가 원래 쿼터니언(또는 부호 반전)과 같아야 한다.
\mathrm{euler\_to\_quat}(\mathrm{quat\_to\_euler}(\mathbf{q})) \approx \pm\mathbf{q}
12.2 회전 결과의 동등성
쿼터니언으로 회전한 결과와 오일러 각에서 변환된 쿼터니언으로 회전한 결과가 같아야 한다.
\mathbf{q}\mathbf{v}\mathbf{q}^* = \mathbf{q}'\mathbf{v}\mathbf{q}'^*
여기서 \mathbf{q}' = \mathrm{euler\_to\_quat}(\mathrm{quat\_to\_euler}(\mathbf{q}))이다.
13. 다가성과 표준 형태
쿼터니언에서 오일러 각으로의 변환은 다가적이다. 같은 회전을 다른 오일러 각 조합으로 표현할 수 있다.
13.1 표준 형태
관례적으로 다음의 범위로 결과를 제한한다.
\phi \in [-\pi, \pi), \quad \theta \in [-\pi/2, \pi/2], \quad \psi \in [-\pi, \pi)
위의 변환 공식이 이 범위 내의 결과를 산출한다.
13.2 짐벌 락 근처의 모호성
짐벌 락에서는 롤과 요가 독립적으로 결정되지 않으므로, 한 쪽을 0으로 고정하는 관례가 적용된다.
14. 참고 문헌
- Hamilton, W. R. (1844). “On Quaternions; or on a New System of Imaginaries in Algebra.” Philosophical Magazine, Vol. 25, 489–495.
- Diebel, J. (2006). “Representing Attitude: Euler Angles, Unit Quaternions, and Rotation Vectors.” Stanford University Technical Report.
- Shuster, M. D. (1993). “A Survey of Attitude Representations.” Journal of the Astronautical Sciences, 41(4), 439–517.
- Kuipers, J. B. (1999). Quaternions and Rotation Sequences. Princeton University Press.
- Sola, J. (2017). “Quaternion Kinematics for the Error-State Kalman Filter.” arXiv:1711.02508.
version: 1.0