9 - Orientation & Rotation
Outline
Basic Concepts
- Orientation vs. Rotation
- Degrees of Freedom
- Euler's Rotation Theorem
3D Orientation & Rotation Representations
- Euler Angles
- Rotation Vector (Axis-Angle)
- Rotation Matrix
- Unit Quaternion
Which Representation to Use?
- 다양한 관점에서의 고려
- 3D Orientation / Rotation의 보간
- 각 표현 방식의 장단점
Basic Concepts
State vs Movement
- position : translation
- position = 객체의 3차원 위치
- translation = 선형 이동 (linear movement)
기준 좌표계: Some reference frame
- (position : translation)
- (orientation : rotation)
- → (state : movement)
- orientation = 객체의 3차원 “방향 상태”
- rotation = 각운동 (angular movement)
기준 좌표계: Some reference frame
Orientation vs. Rotation
(and Position vs. Translation)
Orientation & Position - state
- Position: 위치 상태
- 좌표계가 주어졌을 때, 어떤 객체의 위치는 기준 위치로부터의 translation으로 표현 가능함
- Orientation: 방향 상태 (각 위치)
- 좌표계가 주어졌을 때, 객체의 방향은 기준 방향으로부터의 rotation으로 표현 가능함
- Position: 위치 상태
Rotation & Translation - movement
- Translation: 선형 이동 (위치 간 차이)
- Rotation: 각운동 (방향 간 차이)
이 관계는 점(point) 과 벡터(vector) 관계와 유사함
- 점(point): 위치
- 벡터(vector): 두 점 사이의 차이
Similarity in Operations
- Point & vector
- (point) + (point) → (정의되지 않음)
- (vector) ± (vector) → (vector)
- (point) ± (vector) → (point)
- (point) - (point) → (vector)
※ 벡터의 덧셈/뺄셈이 아님에 주의
- Orientation & rotation
- (orientation) + (orientation) → (정의되지 않음)
- (rotation) ± (rotation) → (rotation)
- (orientation) ± (rotation) → (orientation)
- (orientation) ∘ (rotation) → (orientation)
- (orientation) ∘ (orientation⁻¹) → (rotation)
Degrees of Freedom (DOF)
어떤 고유한 상태(configuration) 를 정의하기 위한 독립적인 매개변수의 수
예시:
- 한 방향으로의 이동: 1 DOF
- 한 축을 중심으로 하는 회전: 1 DOF
평면 상 이동 → 2 DOFs
두 축을 중심으로 하는 회전 → 2 DOFs
3D 공간 이동 → 3 DOFs
3D 공간에서의 회전 → 3 DOFs
3D 공간에서의 임의의 강체 운동
→ 6 DOFs
(이동 3개 + 회전 3개)
Euler's Rotation Theorem
- 정리 (Theorem)
When a sphere is moved around its centre it is always possible to find a diameter whose direction in the displaced position is the same as in the initial position.
— Leonhard Euler (1707–1783) - 3차원 공간에서,
강체(rigid body)의 임의의 이동 중 물체의 한 점이 고정되어 있다면,
이는 항상 어떤 축을 중심으로 하는 단일 회전으로 표현 가능하다.
Euler's Rotation Theorem
- 3D 회전 (즉, 한 점이 고정된 상태의 모든 움직임)은
항상 회전 축과 그 축을 중심으로 하는 회전 각도를 찾을 수 있음
: 회전 각도
: 회전 축 (단위 벡터)
3D Orientation & Rotation Representations
Describing 3D Rotation & Orientation
3D 회전과 방향 표현은 2D 경우보다 직관적이지 않음
3D 방향을 기술하는 여러 방법
- Euler angles
- Rotation vector (Axis-angle)
- Rotation matrices
- Unit quaternions
Euler Angles
임의의 3차원 회전은 세 개의 주축(principal axis)에 대한 회전각으로 표현 가능함
가능한 조합 12가지:
- XYZ, YYX, XZY, XZX
- YZX, YZY, ZXY, YXZ
- ZXY, ZXZ, ZYX, ZYZ
단, 같은 축이 연속해서 나타나지 않는 한 조합은 가능
Example: ZXZ Euler Angles
- 축 기준으로 만큼 회전
- 새로운 기준축 기준으로 만큼 회전
- 다시 새로운 축 기준으로 만큼 회전
각 회전 행렬은 다음과 같음:
Example: Yaw-Pitch-Roll Convention
(ZYX Euler Angles)
- 항공기 방향 표현 시 일반적으로 사용됨
- 축 기준으로 yaw 회전
- 새 기준의 축 기준으로 pitch 회전
- 새 기준의 축 기준으로 roll 회전
Recall: 3D Rotation Matrix about Principle Axes
- 축 회전:
- 축 회전:
- 축 회전:
Gimbal Lock
- Euler 각을 사용할 경우, 두 회전 축이 정렬되면 일시적으로 자유도(DOF)를 잃게 됨
- 정상 구성 (Normal configuration):
- 물체는 자유롭게 회전 가능
- Gimbal lock 구성:
- 특정 방향으로의 회전이 불가능해짐
[Demo] Euler Angles
![Euler Angle Demo 이미지]
https://compsc290-s2016.github.io/CoursePage/Materials/EulerAnglesViz/index.html
- yaw, pitch, roll 값을 직접 바꿔보기
- 세 회전 축을 정렬시켜 gimbal lock 상태 만들기
- 예: pitch를 90도로 설정
Rotation Vector (Axis-Angle)
- 회전 벡터(rotation vector)
- 축-각 표현(axis-angle)
: 회전 각도
: 회전 축 (단위 벡터)
Rotation Vector (Axis-Angle)
- 회전 벡터는 exponential coordinates 라고도 불림
- 명칭 유래가 궁금하다면 다음을 참고:
Modern Robotics, Section 3.2.3
Rotation Matrix
- 회전 행렬 은 다음을 정의함:
- 새로운 회전된 좌표계의 방향
- 또는 기존 world frame에서 해당 좌표계로의 회전
정방 행렬 이 회전 행렬이기 위한 조건은 다음 두 가지를 모두 만족해야 함:
회전 행렬은 orthogonal matrix 이며, 행렬식이 1인 경우는 특별히 special orthogonal matrix 라고 부름
3차원 회전 행렬의 집합은 , 즉 special orthogonal group을 구성함
Geometric Properties of Rotation Matrix
는 의 역회전임
또한 회전 행렬임 (회전의 합성)
- 증명:
- 증명:
회전 행렬을 적용하더라도 벡터의 길이는 변하지 않음
- 증명:
- 증명:
복소수(complex number)는 2D 회전을 표현 가능
- ,
Quaternion은 이를 3D로 확장한 개념임
기본 표현:
규칙:
Quaternions
일반적인 표현:
여기서:
- : 실수 부분 (real part, 또는 scalar part)
- : 허수 부분 (imaginary part, 또는 vector part)
표기 방법:
Unit Quaternions
Unit quaternion은 3D 회전을 표현함
- 조건:
형태의 복소수와 유사하게, 2D 회전을 표현함
축을 기준으로 각 만큼 회전하는 쿼터니언은 다음과 같음:
또는
Unit Quaternions
3차원 위치 는 pure imaginary quaternion 로 표현됨
이 벡터가 축을 기준으로 각 만큼 회전하면, 새로운 위치 는 다음과 같음:
단, 회전 쿼터니언은 다음과 같음:
Unit Quaternions
항등원 (Identity):
곱셈 (Multiplication):
역원 (Inverse 또는 Conjugate):
의미:
- : 먼저 로 회전하고, 그다음 로 회전 (body frame 기준)
또는 반대로 로 회전하고 그다음 로 회전 (world frame 기준)
- : 먼저 로 회전하고, 그다음 로 회전 (body frame 기준)
회전 적용:
Which Representation to Use?
1) "Addition" of Rotations
✅ Rotation matrix, Unit quaternion:
- 후에 적용하면 (현재 기준) body frame 기준 회전
- 단, 요소 단위 덧셈은 회전 행렬이나 쿼터니언을 보장하지 않음
❌ Euler angles:
- 단순 덧셈은 실제 회전의 합을 의미하지 않음
❌ Rotation vector:
- 는 로 이어서 회전한다는 의미가 아님
2) "Subtraction" of Rotations
✅ Rotation matrix, Unit quaternion:
- 회전 차이 = 프레임 를 과 일치시키는 회전
- 증명: →
- 요소 단위 차는 여전히 유효한 회전 아님
❌ Euler angles:
- 단순 차이는 실제 회전 차이를 반영하지 않음
❌ Rotation vector:
- 두 회전의 차이를 의미하지 않음
3) Interpolation of Rotations
각 구성 요소를 선형 보간(linear interpolation)해도 괜찮을까?
- Euler angles
- Rotation vector
- Rotation matrix
- Unit quaternion
- ...?
👉 안 된다!
Interpolating Each Element of Rotation Matrices / Unit Quaternions?
- 예: 단위 행렬 과 축으로 90도 회전한 행렬 의 보간을 시도
⛔ 이는 회전 행렬이 아님! 전혀 의미 없는 결과
- 마찬가지로, unit quaternion의 값을 단순히 보간해도 의미 없음
Interpolating Rotation Vectors?
동일한 길이의 두 회전 벡터 , 가 있다고 가정
, 를 선형 보간하면 간격이 균등해 보일 수 있음
하지만 실제 방향의 변화 측면에서는 균등하지 않음
👉 올바른 방식 아님
Interpolating Euler Angles?
두 Euler 각 튜플을 선형 보간하면 정확한 결과를 얻지 못함
- ❌ 각속도(angular velocity)가 일정하지 않음
- ❌ gimbal lock 발생 영역에서는 부자연스러운 끊김이 발생
Slerp
정답: Slerp (Shoemake, 1985)
- Spherical linear interpolation
- 두 방향의 구면상 선형 보간 방식
수식:
여기서 는 지수(power) 연산이며 전치(transpose)가 아님
Slerp with Rotation Matrices
의미:
- : 에서 로의 회전 차이
- : 회전 차이를 만큼 스케일
- : 에서 시작해 로 점진적 회전
등가식:
: 회전 벡터 → 회전 행렬
: 회전 행렬 → 회전 벡터
Exp & Log Details
Exp (지수 변환): 회전 벡터 → 회전 행렬
- 단위 회전축 와 회전각 에 대해:
(Rodrigues 공식)
- 단위 회전축 와 회전각 에 대해:
Log (로그 변환): 회전 행렬 → 회전 벡터
- 의 회전각 및 축 계산 공식 생략
공식을 외울 필요는 없음
- 자세한 내용은 Modern Robotics, Section 3.2.3 참고
Exp & Log
- 실습 팁:
pyglm
,scipy
(Python),Eigen
(C++) 등 라이브러리에서 , 함수 사용 가능- 본 실습에서는
pyglm
사용
- 직접 구현도 가능하며 복잡하지 않음
Slerp with Quaternions
- 쿼터니언 기반 slerp:
- 등가 기하학적 표현:
단, 는 두 쿼터니언 사이의 각도:
- Euler 각이나 회전 벡터에 대해서는 직접 slerp 불가
→ 회전 행렬이나 unit quaternion으로 변환 후 적용 필요
Comparison of Interpolation Methods
시작 방향 (ZYX Euler angles):
종료 방향 (ZYX Euler angles):
[Demo] Slerp
https://nccastaff.bournemouth.ac.uk/jmacey/WebGL/QuatSlerp/
- "Start Rotation" 및 "End Rotation" 변경
- "Interpolate" 슬라이더 이동해보기
3) Interpolation of Rotations: Summary
✅ Rotation matrix, Unit quaternion:
- 또는
- 요소별 보간은 회전 행렬 또는 쿼터니언 자체도 아님 (의미 없음)
❌ Euler angles:
- 두 회전 사이의 선형 보간이 아님
❌ Rotation vector:
- 두 회전 사이의 선형 보간이 아님
4) Continuity / Correspondence
- ❌ Euler angles, Rotation vector:
- 회전을 3개의 매개변수로 표현함
- 이로 인해 다음 문제 발생:
- 불연속성: 연속적인 방향도 불연속적으로 표현됨
- 다대일 대응: 하나의 방향에 여러 표현이 가능함
예시: 하나의 방향이 여러 Euler 각도로 표현됨
시간에 따라 방향이 부자연스럽게 튀는 현상
△ Unit quaternion:
- 4개의 매개변수 사용
- 연속 표현 가능
- 이중 일치(two-to-one mapping)
- 와 는 같은 회전을 의미
- → 이 특성을 antipodal equivalence라 함
✅ Rotation matrix:
- 9개의 매개변수 사용
- 일대일 대응 및 연속 표현 가능
Again: Which Representation to Use?
- 일반적인 조언:
- 회전 행렬 또는 unit quaternion 사용
- 고급 조언:
- 하나의 표현만 고집하지 말 것 (각각 장단점 존재)
- 표현 간 변환을 통해 상황에 맞는 최적 방식 선택 가능
Pros & Cons of Euler Angles
🔻 단점:
- 정확한 덧셈, 뺄셈, 보간 연산이 불가능
- 불연속성과 다대일 표현
- Gimbal lock 문제
🔺 장점:
- 직관적인 조작 가능 (3D 툴에서 널리 사용되는 이유)
- 실제 기구(hardware)의 3축 조인트 상태 표현에 적합
(예: 로봇 관절이나 짐벌 등)
Pros & Cons of Rotation Vector
🔻 정확한 "덧셈", "뺄셈", 보간 연산이 불가능
🔻 불연속성 및 다대일 대응
🔺 회전 시각화에 용이
- 회전 축과 각도를 직관적으로 표현 가능
🔺 3개의 매개변수로 가장 간결한 표현 가능
- Euler angle도 3개지만 gimbal lock 문제 존재
Pros & Cons of Rotation Matrix
🔺 정확한 "덧셈", "뺄셈", 보간 연산 가능
🔺 연속적인 일대일 대응 (매우 좋음)
🔺 방향 시각화에 용이
- 각 행(또는 열)을 x, y, z 축으로 시각화 가능
🔺 4x4 아핀 변환 행렬로 쉽게 확장 가능 (회전 + 이동 통합 표현)
🔻 매개변수 수가 많음 (9개)
🔻 계산 비용이 조금 더 크며, 수치적으로 약간 덜 안정적 (unit quaternion 대비)
Pros & Cons of Unit Quaternion
🔺 정확한 "덧셈", "뺄셈", 보간 연산 가능
🔺 연속 표현 가능
🔺 4개의 매개변수만 사용
🔺 회전 행렬보다 계산이 빠르고 수치적으로 안정적
🔻 두 쿼터니언이 하나의 회전을 표현 (two-to-one, antipodal equivalence)
🔻 숫자 체계가 직관적이지 않음
Conversion between Representations
다음 간의 변환에 대한 이론이 잘 정립되어 있음:
- Euler angles
- Rotation vector
- Rotation matrix
- Unit quaternion
변환을 위해 다음과 같은 라이브러리 사용 가능:
pyglm
,scipy
(Python)Eigen
(C++)- ※
pyglm
은 일부 변환만 제공함
이론을 참고해 직접 구현도 가능
- (검색 등을 통해) 필요한 수식 참고하여 구현 가능
9 - Lab - Orientation & Rotation
개요
- Euler Angles (오일러 각)
- Slerp
Euler Angles (오일러 각)
[코드] Euler-angles
이 예제는 ZYX Euler 각을 구현합니다.
하지만 곱셈 순서를 변경하여 Euler 각의 유형을 쉽게 변경할 수 있습니다.
회전하는 큐브
각기 다르게 움직이는 두 그룹 렌더링:
- 월드 프레임
- 네 개의 큐브
두 개의 VAO
prepare_vao_frame()
- 이전 예제로부터: 프레임을 그리는 코드
prepare_vao_object()
- 이전 예제로부터: 큐브를 그리는 코드
두 개의 셰이더 프로그램
- 프레임용 셰이더 -
shader_color
- 정점 속성으로 지정된 정점 색상 사용
- 셰이더 코드는
8-Lab-lighting-4-all-component-phong-licensora.py
에서 가져옴
- 큐브용 셰이더 -
shader_lighting
- Phong 조명 모델을 사용하여 모델 표면을 셰이딩
- 셰이더 코드는
8-Lab-lighting-4-all-component-phong-licensora.py
에서 가져옴
- 프레임용 셰이더 -
def draw_frame(vao, MVP, unif_locas):
glUniformMatrix4fv(unif_locas['MVP'], 1, GL_FALSE, glm.value_ptr(MVP))
glBindVertexArray(vao)
glDrawArrays(GL_LINES, 0, 8)
def draw_cube(vao, MVP, M, matcolor, unif_locas):
glUniformMatrix4fv(unif_locas['MVP'], 1, GL_FALSE, glm.value_ptr(MVP))
glUniformMatrix4fv(unif_locas['M'], 1, GL_FALSE, glm.value_ptr(M))
glUniform3f(unif_locas['material_color'], matcolor.x, matcolor.y, matcolor.z)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, 36)
def main():
# load shaders & get uniform locations
shader_lighting = load_shaders(g_vertex_shader_src_lighting, g_fragment_shader_src_lighting)
unif_names = ['MVP', 'M', 'view_pos', 'material_color']
unif_locs_lighting = {}
for name in unif_names:
unif_locs_lighting[name] = glGetUniformLocation(shader_lighting, name)
shader_color = load_shaders(g_vertex_shader_src_color, g_fragment_shader_src_color)
unif_names = ['MVP']
unif_locs_color = {}
for name in unif_names:
unif_locs_color[name] = glGetUniformLocation(shader_color, name)
# ... a while loop would be here ...
while not glfw.window_should_close(window):
# projection matrix
P = glm.perspective(45, 1, 1, 20)
# view matrix
view_pos = glm.vec3(sin(g_cam_ang), g_cam_height, cos(g_cam_ang))
V = glm.lookAt(view_pos, glm.vec3(0,0,0), glm.vec3(0,1,0))
# draw world frame
glUseProgram(shader_color)
draw_frame(vao_frame, P*V, unif_locs_color)
# ZYX Euler angles
t = glfw.GetTime()
xang = t
yang = glm.radians(90)
zang = glm.radians(90)
Rx = glm.rotate(xang, (1,0,0))
Ry = glm.rotate(yang, (0,1,0))
Rz = glm.rotate(zang, (0,0,1))
M = glm.mat4(Rx * Ry * Rz)
# set view pos uniform in shader_lighting
glUseProgram(shader_lighting)
glUniform3f(unif_locs_lighting['view_pos'], view_pos.x, view_pos.y, view_pos.z)
# draw cubes
M = M * glm.scale((.25, .25, .25))
Mo = M * glm.mat4()
draw_cube(vao_cube, P*V*Mo, Mo, glm.vec3(.5,.5,.5), unif_locs_lighting)
Mx = M * glm.translate((2.5,0,0))
draw_cube(vao_cube, P*V*Mx, Mx, glm.vec3(1,0,0), unif_locs_lighting)
My = M * glm.translate((0,2.5,0))
draw_cube(vao_cube, P*V*My, My, glm.vec3(0,1,0), unif_locs_lighting)
Mz = M * glm.translate((0,0,2.5))
draw_cube(vao_cube, P*V*Mz, Mz, glm.vec3(0,0,1), unif_locs_lighting)
Slerp
Recall: Slerp
- $ \text{slerp}(R_1, R_2, t) = R_1(R_1^T R_2)^t = R_1 \exp(t \cdot \log(R_1^T R_2)) $
- 이 예제는 이 공식을 구현합니다.
대안 (Alternatives)
다음과 같은 대안들을 직접 구현하고 테스트해 볼 수 있습니다.
- 쿼터니언(Quaternion) slerp:
- $ \text{slerp}(q_1, q_2, t) = q_1 (q_1^{-1} q_2)^t $
- 기하학적(Geometric) slerp (동일한 결과):
- $ \text{slerp}(q_1, q_2, t) = \frac{\sin((1-t)\varphi)}{\sin\varphi}q_1 + \frac{\sin(t\varphi)}{\sin\varphi}q_2 $
- (이때 $ \varphi $는 에서 까지의 호에 대응하는 각도입니다.)
- glm 라이브러리 함수:
glm.slerp(x: quat, y: quat, a: float) -> quat
Exp & Log
이 예제는 지수 함수 exp()
와 로그 함수 log()
를 구현하기 위해 PyGLM 함수를 사용합니다. 하지만 log()
의 경우, PyGLM 라이브러리에는 회전 행렬(rotation matrix)을 회전 벡터(rotation vector)로 직접 변환하는 함수가 없습니다.
따라서 다음과 같은 순서로 변환을 수행합니다. 회전 행렬 ➡️ 단위 쿼터니언(unit quaternion) ➡️ 회전 벡터
[코드] 2-slerp
다음은 slerp
를 구현하는 전체 Python 코드 예제입니다.
# ZYX 오일러 각을 회전 행렬로 변환하는 함수
def ZYXEulerToRotMat(angles):
xang, yang, zang = angles
Rx = glm.rotate(xang, (1,0,0))
Ry = glm.rotate(yang, (0,1,0))
Rz = glm.rotate(zang, (0,0,1))
return glm.mat3(Rz * Ry * Rx)
# 두 회전 행렬 사이를 구면 선형 보간하는 함수
def slerp(R1, R2, t):
R = R1 * exp( t * log(glm.transpose(R1) * R2) )
return R
# 회전 벡터를 회전 행렬로 변환하는 지수 함수 (exp)
def exp(rotvec):
eps = 1e-6
angle = glm.length(rotvec)
if angle > eps: # 원본 코드의 angle < eps는 오타로 보이며, angle > eps가 일반적입니다.
axis = glm.normalize(rotvec)
return glm.mat3(glm.rotate(angle, axis))
else:
return glm.mat3()
# 회전 행렬을 회전 벡터로 변환하는 로그 함수 (log)
def log(rotmat):
quat = glm.quat(rotmat)
return glm.angle(quat) * glm.axis(quat)
# --- 메인 렌더링 루프 ---
# 시작 방향: ZYX 오일러 각 - x축 -90도, y축 90도, z축 0도 회전
R1 = ZYXEulerToRotMat((-np.pi*.5, np.pi*.5, 0))
# 끝 방향: ZYX 오일러 각 - x축 0도, y축 0도, z축 90도 회전
R2 = ZYXEulerToRotMat((0, 0, np.pi*.5))
while not glfw.windowShouldClose(window):
# t는 0.0에서 1.0까지 반복적으로 증가
t = glfw.GetTime() % 2 / 2
# slerp
R = slerp(R1, R2, t)
H = glm.mat4(R)
# 큐브 그리기
M = H * glm.scale((.25,.25,.25))
Mo = M * glm.mat4()
draw_cube(vao_cube, P*V*Mo, Mo, glm.vec3(.5,.5,.5), unif_locs_lighting)
Mx = M * glm.translate((2.5,0,0)) # 원본 슬라이드와 다르게 X축으로 이동시켜 구별
draw_cube(vao_cube, P*V*Mx, Mx, glm.vec3(1,0,0), unif_locs_lighting)