-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
351 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Euler Angles" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"This notebook demonstrates how to use `clifford` to implement rotations in three dimensions using rotors. Specifically, the rotation is parameterized using Euler Angles. Conversion from the rotor form to a matrix reprentation is shown, and only takes three lines of code. " | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Euler Angles with Rotors" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"A common way to parameterize rotations in three dimensions is through [Euler Angles](https://en.wikipedia.org/wiki/Euler_angles). \n", | ||
"\n", | ||
"\"Any orientation can be achieved by composing three elemental rotations. The elemental rotations can either occur about the axes of the fixed coordinate system (*extrinsic rotations*) or about the axes of a rotating coordinate system, which is initially aligned with the fixed one, and modifies its orientation after each elemental rotation (*intrinsic rotations*). \"\n", | ||
"\n", | ||
"The animation below shows an intrinsic rotation model, as each elemental rotation is applied. Label the left,right, and vertical blue-axes are $e_1, e_2,$ and $e_3$, respectively. \n", | ||
"\n", | ||
"\n", | ||
"The series of rotations can be described:\n", | ||
"\n", | ||
"1. rotate about $e_3$\n", | ||
"2. rotate about the rotated $e_1$, call it $e_1^{'}$\n", | ||
"3. rotate about the twice rotated axis of $e_3$, call it $e_3^{''}$" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 93, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/html": [ | ||
"<img src=\"_static/Euler2a.gif\"/>" | ||
], | ||
"text/plain": [ | ||
"<IPython.core.display.Image object>" | ||
] | ||
}, | ||
"execution_count": 93, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"from IPython.display import Image,SVG\n", | ||
"Image(url='_static/Euler2a.gif')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"\n", | ||
"Following Sec. 2.7.5 from \"Geometric Algebra for Physicists\", we first rotate an angle $\\phi$ about the\n", | ||
"$e_3$-axis, which is equivalent to rotating in the $e_{12}$-plane. This is done with the rotor\n", | ||
"\n", | ||
"$$ R_{\\phi} = e^{-\\frac{\\phi}{2} e_{12}}$$\n", | ||
"\n", | ||
"\n", | ||
"Next we rotate about the rotated $e_1$-axis, which we label $e_1^{'}$\n", | ||
"\n", | ||
"$$ e_1^{'} =R_{\\phi} e_1 \\tilde{R_{\\phi}} $$\n", | ||
"\n", | ||
"The plane corresponding to this axis is found by taking the dual of $e_1^{'}$\n", | ||
"\n", | ||
"$$ I R_{\\phi} e_1 \\tilde{R_{\\phi}} = R_{\\phi} e_{23} \\tilde{R_{\\phi}} $$\n", | ||
"\n", | ||
"\n", | ||
"(where we have made use of the fact that the psuedo-scalar commutes in G3). The second roation by angle $\\theta$ about the $e_1^{'}$-axis is then ,\n", | ||
"\n", | ||
"\n", | ||
"$$ R_{\\theta} = e^{\\frac{\\theta}{2} R_{\\phi} e_{23} \\tilde{R_{\\phi}}} $$\n", | ||
"\n", | ||
"\n", | ||
"However, noting that \n", | ||
"\n", | ||
"$$ e^{R_{\\phi} e_{23} \\tilde{R_{\\phi}}} =R_{\\phi} e^{e_{23}} \\tilde{R_{\\phi}} $$\n", | ||
"\n", | ||
"Allows us to write the second rotation by angle $\\theta$ about the $e_1^{'}$-axis as \n", | ||
"\n", | ||
"$$ R_{\\theta} = R_{\\phi} e^{\\frac{\\theta}{2}e_{23}} \\tilde{R_{\\phi}} $$\n", | ||
"\n", | ||
"So, the combonation of the first two elemental rotations equals, \n", | ||
"\n", | ||
"\\begin{align*} \n", | ||
"R_{\\theta} R_{\\phi} &= R_{\\phi} e^{\\frac{\\theta}{2}e_{23}} \\tilde{R_{\\phi}} R_{\\phi} \\\\ \n", | ||
"&= e^{-\\frac{\\phi}{2} e_{12}}e^{-\\frac{\\theta}{2} e_{23}}\n", | ||
"\\end{align*}\n", | ||
"\n", | ||
"This pattern can be extended to the third elemental rotation of angle $\\psi$ in the twice-rotated $e_1$-axis, creating the total rotor \n", | ||
"\n", | ||
"$$ R = e^{-\\frac{\\phi}{2} e_{12}} e^{-\\frac{\\theta}{2} e_{23}} e^{-\\frac{\\psi}{2} e_{12}} $$" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Implementation" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"First, we initialize the algbera and assign the variables" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 94, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"from clifford import Cl\n", | ||
"\n", | ||
"layout, blades = Cl(3) # create a 3-dimensional clifford algebra\n", | ||
"\n", | ||
"locals().update(blades) # lazy way to put entire basis in the namespace" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"\n", | ||
"Next we define a function to produce a rotor given euler angles" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 95, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"def R_euler(phi, theta,psi):\n", | ||
" Rphi = e**(-phi/2.*e12)\n", | ||
" Rtheta = e**(-theta/2.*e23)\n", | ||
" Rpsi = e**(-psi/2.*e12)\n", | ||
" \n", | ||
" return Rphi*Rtheta*Rpsi" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"For example, using this to create the rotation shown in the animation above," | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 96, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"0.65328 - (0.65328^e12) - (0.38268^e23)" | ||
] | ||
}, | ||
"execution_count": 96, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"from numpy import pi \n", | ||
"\n", | ||
"R = R_euler(pi/4, pi/4, pi/4)\n", | ||
"R" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"To see how this rotor changes an ortho-normal frame " | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 97, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"[(0.14645^e1) + (0.85355^e2) + (0.5^e3),\n", | ||
" -(0.85355^e1) - (0.14645^e2) + (0.5^e3),\n", | ||
" (0.5^e1) - (0.5^e2) + (0.70711^e3)]" | ||
] | ||
}, | ||
"execution_count": 97, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"\n", | ||
"[R*a*~R for a in [e1,e2,e3]]" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Convert to Rotation Matrix" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The matrix representation for a rotation can defined as the result of rotating an ortho-normal frame. Rotating an ortho-normal frame can be done easily, " | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 98, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"[(0.14645^e1) + (0.85355^e2) + (0.5^e3),\n", | ||
" -(0.85355^e1) - (0.14645^e2) + (0.5^e3),\n", | ||
" (0.5^e1) - (0.5^e2) + (0.70711^e3)]" | ||
] | ||
}, | ||
"execution_count": 98, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"from numpy import array \n", | ||
"\n", | ||
"A = [e1,e2,e3] # initial ortho-normal frame\n", | ||
"B = [R*a*~R for a in A] # resultant frame after rotation\n", | ||
"\n", | ||
"B" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The components of this frame are equivalent to the rotation matrix. We could put this in a matrix easily by, " | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 99, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/plain": [ | ||
"array([[ 0.14644661, 0.85355339, 0.5 ],\n", | ||
" [-0.85355339, -0.14644661, 0.5 ],\n", | ||
" [ 0.5 , -0.5 , 0.70710678]])" | ||
] | ||
}, | ||
"execution_count": 99, | ||
"metadata": {}, | ||
"output_type": "execute_result" | ||
} | ||
], | ||
"source": [ | ||
"M = [float(b|a) for b in B for a in A] # you need float() due to bug in clifford\n", | ||
"array(M).reshape(3,3)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "IPython (Python 2)", | ||
"language": "python", | ||
"name": "python2" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 2 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython2", | ||
"version": "2.7.11" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 0 | ||
} |
Oops, something went wrong.