트러블해이팅 마인드

Navigation Toolbox: "Rotations, Orientation and Quaternions" 튜토리얼 본문

공학

Navigation Toolbox: "Rotations, Orientation and Quaternions" 튜토리얼

크래프트맨 2023. 3. 14. 11:44

MATLAB의 툴박스 중 하나인 Navigation Toolbox의 튜토리얼 내용을 정리한 것입니다.

 

Rotations in Three Dimensions
  • 3차원에서의 모든 회전은 회전축(the axis of rotation)과 이 회전축에 대한 회전각도(an angle of rotation)로 정의될 수 있다. 쿼터니언(quaternion)에는 이 두 가지 정보가 함축되어 있다.
  • quaternion 클래스는 회전을 정의하기 위해 오른손 법칙 (right-hand rule)을 따른다. 이 말은 양의 값의 회전은 원점에서 회전축을 보았을 때 시계방향으의 회전을 의미한다는 뜻이다.


Orientation
  • 정렬방향(orientation)이란 기준틀(a frame of reference)에 대한 어떤 물체의 각변위(angular displacement)를 말한다. 정렬방향은 기준틀의 정렬방향으로부터 특정한 각변위를 만드는 회전으로 흔히 묘사된다. 정렬방향을 틀회전(frame rotation)으로 이해하는 것이 도움이 된다. (Parent reference frame --(orientation)--> Child reference frame)
  • 정렬방향은 쿼터니언이나 회전행렬(rotation matrix), 오일러각도의 집합 혹은 회전벡터로 표현된다.

Quaternion
  • 쿼터니언은 〈a + bi + cj + dk〉 꼴의 수다. (이 때 〈i^2 = j^2 = k^2 = ijk = -1〉이다.)
  • 회전축의 단위벡터가 [x, y, z]이고 회전각도가 α일 때, 이 회전을 표현하는 쿼터니언은 〈cos(α/2) + sin(α/2)(xi + yj+ zk)〉이라고 할 수 있다.
  • 쿼터니언이 회전을 묘사하기 위해서는 단위 쿼터니언(unit quaternion)이어야 한다. 단위 쿼터니언은 노름(norm)의 크기가 1이므로 〈norm(q) = √(a^2 + b^2 + c^2 + d^2)〉라고 할 수 있다.

Quaternion Math
  • 쿼터니언의 덧셈과 뺄셈은 복소수의 덧·뺄셈과 유사하게 하면 된다. 하지만 쿼터니언의 곱셉은 가환이 아니다 (not commutative). 즉, 쿼터니언 p, q에 대하여 〈pq ≠ qp〉라는 것이다. 하지만 모든 쿼터니언은 곱셉이 가능한 역(multiplicative inverse; 〈1/q〉)을 가지므로 나눗셈을 할 수 있다.
  • p./q를 하면 곧 〈p(q-1)〉이고, p.\q를 하면 곧 〈(p-1)q〉다.
  • 쿼터니언의 켤레복소수(conjugate)는 일반적인 복소수와 같이 허수부의 부호만 반전시켜주면 된다.
  • MATLAB에서는 어떤 쿼터니언을 정규화(normalize)하여 노름의 크기가 1이 되도록 할 수 있다.

Point and Frame Rotations with Quaternions
  • 쿼터니언은 위 예시처럼 기준틀에서 점을 회전시키거나 기준틀 자체를 회전시킬 때 사용될 수 있다.
  • rotatepoint라는 함수는 한 점 〈v = (vx, vy, vz)〉을 쿼터니언 q에 대하여 〈pvquatp* (vquat = 0 + vxi + vyj + vzk)〉 공식을 이용하여 회전시킬 수 있다. 예시처럼 점 (0.7, 0.5)를 Z축에 대하여 30도 회전시킨다고 한다면 이 점은 Z값이 0이므로 (0.7, 0.5, 0)이고 회전축인 쿼터니언은 Z축으로 단위벡터가 [0 0 1]이므로 아래와 같은 코드를 통해 회전된 점의 좌표를 구할 수 있다. 이 값은 [0.3562 0.7830 0]으로 위 예시의 값과 같다.
%% 각도를 라디안으로 먼저 변환한다.
ang = deg2rad(30);

%% 회전축이 되는 쿼터니언의 단위벡터 [0 0 1]와 회전각도 α를 이용해 이 회전을 표현할 쿼터니언 q를 만든다.
q = quaternion(cos(ang/2), 0, 0, sin(ang/2))

%% 점 (0.7, 0.5)는 Z축의 좌표가 0이다.
pt = [0.7, 0.5, 0]
ptrot = rotatepoint(q, pt)

%% 결과로 ptrot = [0.3562 0.7830 0]이 출력될 것이다.
  • rotateframe라는 함수는 기준틀을 쿼터니언 q에 대하여 〈p*vquatp (vquat = 0 + vxi + vyj + vzk)〉 공식을 이용해 회전시켰을 때, 회전된 새로운 기준틀에 대한 한 점 〈v = (vx, vy, vz)〉의 좌표값을 구할 수 있다. (이 때는 순서가 rotatepoint와는 바뀌어 쿼터니언의 켤레복소수가 먼저 곱해진 것을 눈여겨보자.)  아래 코드를 이용해 예시 2번의 그림과 같은 값을 확인할 수 있다. 쿼터니언의 켤레복소수 값을 곱하면 회전했던 것을 다시 원래대로 되돌릴 수 있다.
%% 위 rotatepoint와 유사한 문법. 그러나 프레임 자체가 회전되었을 때의 해당 점의 좌표값을 뱉는다.
ptframerot = rotateframe(q, pt)

%% 결과로 ptframerot = [0.8562 0.0830 0]이 출력될 것이다.

%% 위처럼 회전된 틀을 다시 원래 상태로 되돌릴 수도 있다. 켤레복소수를 이용한다.
rotateframe(conj(q), ptframerot))

%% 회전되기 전의 틀에서의 좌표값인 (0.7, 0.5, 0)이 출력될 것이다.

Other Rotation Representations: Euler angles, rotation matrices, and/or rotation vectors
  • 오일러 각도를 이용해 자주 회전을 표현한다. 기준틀을 Z축에 대해서 30도, Y축에 대해서 20도, X축에 대해서 -50도 돌렸다고 했을 때 이 때 회전은 내부회전 (intrinsic rotation)이다. 이는 즉 Z축에 대해서 30도 돌리고 이후 돌릴 때에는 원래 틀이 아니라 변경된 틀에서 Y축에 대해서 20도를 돌린다는 것이다. 그러므로 돌리는 순서에 따라 결과가 달라진다.
figure;
euld = [30 20 -50];
dr.drawEulerRotation(gca, euld);
  • 오일러 각도로 표현된 회전을 쿼터니언으로 변환하고 싶다면 아래와 같이 한다. 이 때, 회전을 시킬 때 ZYX의 순서로 했으므로 'ZYX' flag를 달아주어야 한다. 'euler' flag는 오일러 각도가 라디안으로 표현되었다는 것이므로 오일러 각도가 degree라면 'eulerd' flag를 붙여준다. 또한 아래와 같이 쿼터니언과 오일러 각도, 회전행렬은 쉽게 변환이 가능하다. flag를 잘 달아주자.
%% 오일러각도를 쿼터니언으로 변환. ZYX 순서로 돌렸다는 것을 아는 상태다. 또한 라디안인지 각도인지에 따라 flag가 달라진다.
qeul = quaternion(deg2rad(euld), 'euler', 'ZYX', 'frame')
qeul = quaternion(euld, 'eulerd', 'ZYX', 'frame')

%% 쿼터니언을 오일러각도로 다시 원상복귀.
rad2deg(euler(qeul, 'ZYX', 'frame'))
eulerd(qeul, 'ZYX', 'frame')

%% 쿼터니언 값을 회전행렬로 변환 및 원상복귀.
rmat = rotmat(qeul, 'frame')
quaternion(rmat, 'rotmat', 'frame')
  • 쿼터니언을 이용해 한 점을 회전시키거나 틀을 회전시켰던 것처럼, 회전행렬로도 한 점이나 틀을 회전시킬 수 있다. 쿼터니언을 회전행렬로 변환할 때 'point'인지 'frame'인지 정해주자.
%% 점을 회전시킬 회전행렬을 생성. 이 회전행렬로 점 (0.7 0.5)를 회전했을 때의 결과값은 이 회전행렬의 오른쪽에 점을 transpose하여 곱해주면 된다.
rotmatPoint = rotmat(q, 'point')
rotmatPoint * (pt')
%% 결과값인 (0.3562 0.7830)이 도출.

%% 틀을 회전시킬 회전행렬을 생성. 이 회전행렬로 점 (0.7 0.5)를 회전했을 때 새로운 틀에서의 결과값은 이 회전행렬의 오른쪽에 점을 transpose하여 곱해주면 된다. 
rotmatFrame = rotmat(q, 'frame') 
rotmatFrame * (pt') 
%% 결과값인 (0.8562 0.0830)이 도출.
  • 회전 벡터(rotation vector)는 회전축의 단위벡터가 라디안으로 표현된 회전각에 의해 곱해진 것으로, 세 개의 원소를 가진 벡터다. 이 회전 벡터와 관련해서는 틀이나 점과 관련된 성질은 없다. 아래와 같이 쿼터니언으로부터 만들고 되돌릴 수 있다.
rv = rotvec(qeul)
quaternion(rv, 'rotvec')

Distance

  • 오일러 각도보다 쿼터니언이 좋은 이점 중 하나는 중단점(discontinuity)이 없다는 것이다. 오일러 각도는 사용되는 환경에 따라서 매우 다양한 중단이 존재한다. dist 함수를 통해 두 방식의 차이를 볼텐데, 이 때의 결과값은 0 ~ π 까지로 한정되어 있다. 아래 예시를 보면 eul1, eul2의 경우는 쿼터니언과 오일러 각도로 계산한 두 정렬방향 간의 차이가 같다. 그러나 eul3, eul4의 경우는 오일러 각도의 중단점까지 가기 때문에 두 방식에 차이가 있게 된다. 오일러 각도로 산출한 방식은 두 정렬방향 간의 차이가 매우 크다고 나오지만 쿼터니언은 2도에 불과하다고 나온다. 실제로는 거의 비슷한 방향을 향하고 있는 두 정렬방향이다.
%% 중단점까지는 가지 않는 경우.
eul1 = [0, 10, 0]; 
eul2 = [0, 15, 0]; 
qdist1 = quaternion(deg2rad(eul1), 'euler', 'ZYX', 'frame'); 
qdist2 = quaternion(deg2rad(eul2), 'euler', 'ZYX', 'frame');

%% 이 경우는 아래의 두 결과가 같다.
eul2 - eul1
rad2deg(dist(qdist1, qdist2))

%% 중단점까지 가는 경우.
eul3 = [0, 89, 0]; 
eul4 = [180, 89, 180]; 
qdist3 = quaternion(deg2rad(eul3), 'euler', 'ZYX', 'frame'); 
qdist4 = quaternion(deg2rad(eul4), 'euler', 'ZYX', 'frame');

%% 이 경우는 아래의 두 결과가 다르며, 쿼터니언으로 계산한 결과가 맞다.
euldiff = eul4 - eul3
euldist = rad2deg(dist(qdist3, qdist4))
  • 쿼터니언은 음의 값을 취했을 때 정확히 같은 정렬방향을 표현한다.
qpos = quaternion(-cos(pi/4), 0, 0, sin(pi/4))
qneg = -qpos

dist(qpos, qneg)
%% 위 결과 0이 나온다.

 

'공학' 카테고리의 다른 글

Rigid body와 euler angle  (0) 2019.12.03
Comments