5
$\begingroup$

Have done lot of googling on this and am overwhelmed by the number of formulas; not a math major, just a developer struggling to understand quaternion rotations:)

Given a vector of: 0, 0, 0.15
And a quaternion: 0.86671, -0.40654, -0.21285, 0.19555 (s, x, y, z)
Vector should equal: -0.07919, 0.09322, 0.08683

Any help to understand what formulas should be applied to get to this result is appreciated.

$\endgroup$
3
  • $\begingroup$ Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. $\endgroup$
    – Community Bot
    Oct 27, 2022 at 1:12
  • $\begingroup$ There is not enough data to answer the question. To represent rotation in $\mathbb{R}^{3}$ using quaternions, you need at least one vector, a rotation axis and a rotation angle... Aka please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. $\endgroup$ Oct 27, 2022 at 2:40
  • $\begingroup$ Sorry was not able to provide more; the math for the results is done behind the scenes ... all I am given is the vector and quaternion doing (vector * rotation) to get the result, so was difficult to understand what was needed to math this. :< $\endgroup$
    – Nue Fox
    Oct 27, 2022 at 5:18

3 Answers 3

5
$\begingroup$

Formula

The formula to calculatise would be $$\mathrm{v}_{\text{rotated}} = \mathrm{q} \cdot \mathrm{v} \cdot \overline{\mathrm{q}}$$ with your vector $$\mathrm{v} = \left[\begin{matrix}0\\ 0\\ 0.15\end{matrix}\right] = 0 \cdot \mathrm{i} + 0 \cdot \mathrm{j} + 0.15 \cdot \mathrm{k}$$ and your quaternion $$\mathrm{q} = 0.86671 - 0.40654 \cdot \mathrm{i} - 0.21285 \cdot \mathrm{j} + 0.19555 \cdot \mathrm{k}$$ and $$\overline{\mathrm{q}} = 0.86671 + 0.40654 \cdot \mathrm{i} + 0.21285 \cdot \mathrm{j} - 0.19555 \cdot \mathrm{k}$$ with $\mathrm{i}^{2} = \mathrm{j}^{2} = \mathrm{k}^{2} = \mathrm{i} \cdot \mathrm{j} \cdot \mathrm{k} = -1$.

But don't forget that quaternions are not commutative with respect to multiplication.

Formula with roationangle and rotationaxis

If you use the vector / quaternion $r$ as the axis of rotation, $\varphi$ as the rotation angle, $\mathrm{v}$ as the vector to be rotated and the rotated vector $\mathrm{v}_{\text{rotated}}$, then the formula applies: $$ \begin{align*} \mathrm{v}_{\text{rotated}} &= \mathrm{q} \cdot \mathrm{v} \cdot \overline{\mathrm{q}}\\ \mathrm{v}_{\text{rotated}} &= \left( \cos\left( \frac{\varphi}{2} \right) + \mathrm{u} \cdot \sin\left( \frac{\varphi}{2} \right) \right) \cdot \mathrm{v} \cdot \left( \cos\left( \frac{\varphi}{2} \right) - \mathrm{u} \cdot \sin\left( \frac{\varphi}{2} \right) \right)\\ \mathrm{v}_{\text{rotated}} &= \left( \cos\left( \frac{\varphi}{2} \right) + \frac{\mathrm{r}}{||\mathrm{r}||} \cdot \sin\left( \frac{\varphi}{2} \right) \right) \cdot \mathrm{v} \cdot \left( \cos\left( \frac{\varphi}{2} \right) - \frac{\mathrm{r}}{||\mathrm{r}||} \cdot \sin\left( \frac{\varphi}{2} \right) \right)\\ \end{align*} $$

Code

Since you are a developer, my code for calculating rotation using quaternions might tell you more then the formulas:

HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rotation</title>

    <script src="myscript.js"></script>
    <link href = "style.css" rel = "stylesheet"><link>
</head>

<body style = "margin: 0%;">
    <h1 style = "left: 10pt;">Rotation in ℝ³</h1>
    
    <div>
        <form id = "QuaternionversorImput">
            <label for = "QuaternionversorZ">Quaternion q: </label><br><br>
            <label for = "i">i-Im(q): </label>
            <input type = "text" id = "i" name = "i"><br><br>
            <label for = "j">j-Im(q): </label>
            <input type = "text" id = "j" name = "j"><br><br>
            <label for = "k">k-Im(q): </label>
            <input type = "text" id = "k" name = "k"><br><br>
            <label for = "Rotationaxis">Rotation Axis: </label><br><br>
            <label for = "Rotationaxis">x (Euler Axis): </label>
            <input type = "text" id = "i-Rotationaxis" name = "i-Rotationaxis"><br><br>
            <label for = "Rotationaxis">y (Euler Axis): </label>
            <input type = "text" id = "j-Rotationaxis" name = "j-Rotationaxis"><br><br>
            <label for = "Rotationaxis">z (Euler Axis): </label>
            <input type = "text" id = "k-Rotationaxis" name = "k-Rotationaxis"><br><br>
            <input type = "button" onclick = "calculate()" onclick = "plot()" value="Calculate (φ in °)!">
            <input type = "button" onclick = "calculate2()" value="Calculate (φ in rad)!">
        </form>

        <form id = "Rotation">
            <label for = "Rotationangleφ">Rotationangle φ: </label><br><br>
            <label for = "Rotationangleindegree">Rotationangle: </label>
            <input type = "text" id = "Rotationangleindegree" name = "Rotationangleindegree"><br><br>
        </form>

        <div id = "RotatedQuaternionversor">
            Rotated Quaternion q':</div>
            <div id = "RotatedQuaternionversorImi">
            </div>
            <div id = "RotatedQuaternionversorImj">
            </div>
            <div id = "RotatedQuaternionversorImk">
            </div>
            <div id = "x">
                i-Im(q'): </div>
                <div id = "y">
                    j-Im(q'): </div>
                    <div id = "z">
                        k-Im(q'): </div>
    </div>
</body>
</html>

JavaScript

function calculate() {
    let q_rotatedi = document.getElementById('RotatedQuaternionversorImi');
    let q_rotatedj = document.getElementById('RotatedQuaternionversorImj');
    let q_rotatedk = document.getElementById('RotatedQuaternionversorImk');
    let y_0 = 0
    let y_1 = document.getElementById("i").value;
    let y_2 = document.getElementById("j").value;
    let y_3 = document.getElementById("k").value;
    let i_r = document.getElementById("i-Rotationaxis").value;
    let j_r = document.getElementById("j-Rotationaxis").value;
    let k_r = document.getElementById("k-Rotationaxis").value;
    let bet_r = Math.sqrt(Math.pow(i_r, 2) + Math.pow(j_r, 2) + Math.pow(k_r, 2));
    let Rotationangle = document.getElementById("Rotationangleindegree").value;
    let x_0 = Math.cos(Rotationangle / 2 * Math.PI / 180);
    let x_1 = Math.sin(Rotationangle / 2 * Math.PI / 180) * i_r / bet_r;
    let x_2 = Math.sin(Rotationangle / 2 * Math.PI / 180) * j_r / bet_r;
    let x_3 = Math.sin(Rotationangle / 2 * Math.PI / 180) * k_r / bet_r;
    let result1 = -(x_0 * y_0 - x_1 * y_1 - x_2 * y_2 - x_3 * y_3) * x_1 + (x_0 * y_1 + x_1 * y_0 + x_2 * y_3 - x_3 * y_2) * x_0 - (x_0 * y_2 - x_1 * y_3 + x_2 * y_0 + x_3 * y_1) * x_3 + (x_0 * y_3 + x_1 * y_2 - x_2 * y_1 + x_3 * y_0) * x_2;
    let result2 = -(x_0 * y_0 - x_1 * y_1 - x_2 * y_2 - x_3 * y_3) * x_2 + (x_0 * y_1 + x_1 * y_0 + x_2 * y_3 - x_3 * y_2) * x_3 + (x_0 * y_2 - x_1 * y_3 + x_2 * y_0 + x_3 * y_1) * x_0 - (x_0 * y_3 + x_1 * y_2 - x_2 * y_1 + x_3 * y_0) * x_1;
    let result3 = (x_0 * y_0 - x_1 * y_1 - x_2 * y_2 - x_3 * y_3) * x_3 - (x_0 * y_1 + x_1 * y_0 + x_2 * y_3 - x_3 * y_2) * x_2 + (x_0 * y_2 - x_1 * y_3 + x_2 * y_0 + x_3 * y_1) * x_1 + (x_0 * y_3 + x_1 * y_2 - x_2 * y_1 + x_3 * y_0) * x_0;
    q_rotatedi.textContent = result1;
    q_rotatedj.textContent = result2;
    q_rotatedk.textContent = result3;
}
function calculate2() {
    let q_rotatedi = document.getElementById('RotatedQuaternionversorImi');         // x-Rotationsvektorpart
    let q_rotatedj = document.getElementById('RotatedQuaternionversorImj');         // y-Rotationsvektorpart
    let q_rotatedk = document.getElementById('RotatedQuaternionversorImk');         // z-Rotationsvektorpart
    let y_0 = 0;                                                                    // da 3D keine (4-1)D-Zahl
    let y_1 = document.getElementById("i").value;                                   // x-Urvektorpart
    let y_2 = document.getElementById("j").value;                                   // y-Urvektorpart
    let y_3 = document.getElementById("k").value;                                   // z-Urvektorpart
    let i_r = document.getElementById("i-Rotationaxis").value;                      // x-Euler'sche-Achse
    let j_r = document.getElementById("j-Rotationaxis").value;                      // y-Euler'sche-Achse
    let k_r = document.getElementById("k-Rotationaxis").value;                      // z-Euler'sche-Achse
    let bet_r = Math.sqrt(Math.pow(i_r, 2) + Math.pow(j_r, 2) + Math.pow(k_r, 2));  // Betrah der Rotationsachse
    let Rotationangle = document.getElementById("Rotationangleindegree").value;     // Rotationswinkel
    let x_0 = Math.cos(Rotationangle / 2);                                          // w-Komponente
    let x_1 = Math.sin(Rotationangle / 2) * i_r / bet_r;                            // Einheitsquaternion x-Komponente (da x_1 = x-Euler'sche-Achse / ||Rotationsachse||)
    let x_2 = Math.sin(Rotationangle / 2) * j_r / bet_r;                            // Einheitsquaternion y-Komponente (da x_2 = y-Euler'sche-Achse / ||Rotationsachse||)
    let x_3 = Math.sin(Rotationangle / 2) * k_r / bet_r;                            // Einheitsquaternion z-Komponente (da x_3 = z-Euler'sche-Achse / ||Rotationsachse||)
    let result1 = -(x_0 * y_0 - x_1 * y_1 - x_2 * y_2 - x_3 * y_3) * x_1 + (x_0 * y_1 + x_1 * y_0 + x_2 * y_3 - x_3 * y_2) * x_0 - (x_0 * y_2 - x_1 * y_3 + x_2 * y_0 + x_3 * y_1) * x_3 + (x_0 * y_3 + x_1 * y_2 - x_2 * y_1 + x_3 * y_0) * x_2;
    let result2 = -(x_0 * y_0 - x_1 * y_1 - x_2 * y_2 - x_3 * y_3) * x_2 + (x_0 * y_1 + x_1 * y_0 + x_2 * y_3 - x_3 * y_2) * x_3 + (x_0 * y_2 - x_1 * y_3 + x_2 * y_0 + x_3 * y_1) * x_0 - (x_0 * y_3 + x_1 * y_2 - x_2 * y_1 + x_3 * y_0) * x_1;
    let result3 = (x_0 * y_0 - x_1 * y_1 - x_2 * y_2 - x_3 * y_3) * x_3 - (x_0 * y_1 + x_1 * y_0 + x_2 * y_3 - x_3 * y_2) * x_2 + (x_0 * y_2 - x_1 * y_3 + x_2 * y_0 + x_3 * y_1) * x_1 + (x_0 * y_3 + x_1 * y_2 - x_2 * y_1 + x_3 * y_0) * x_0;
    q_rotatedi.textContent = result1;
    q_rotatedj.textContent = result2;
    q_rotatedk.textContent = result3;
}

CSS

#QuaternionversorImput {
    position: absolute;
    width: 250pt;
    top:  52.5pt;
    left: 10pt;
}
#Rotation {
    width: 250pt;
    position: absolute;
    top:  52.5pt;
    left: 260pt;
}
#RotatedQuaternionversor {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top:  52.5pt;
    left: 510pt;
}
#RotatedQuaternionversorImi {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top: 82pt;
    left: 560pt;
    font-size: 12pt;
}
#RotatedQuaternionversorImj {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top: 110pt;
    left: 560pt;
    font-size: 12pt;
}
#RotatedQuaternionversorImk {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top: 138pt;
    left: 560pt;
    font-size: 12pt;
}

#x {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top: 82pt;
    left: 510pt;
    font-size: 12pt;
}
#y {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top: 110pt;
    left: 510pt;
    font-size: 12pt;
}
#z {
    height: 100pt;
    width: 125pt;
    position: absolute;
    top: 138pt;
    left: 510pt;
    font-size: 12pt;
}

free to use for everyone ;)

$\endgroup$
1
  • 1
    $\begingroup$ Thank you so much; this helped tremendously! <3 $\endgroup$
    – Nue Fox
    Oct 27, 2022 at 5:13
3
$\begingroup$

You are calculating $p\mathbf{x}p^{-1}$, the conjugation of $\mathbf{x}$ by $p$, where

$$ \mathbf{x}=0.15\mathbf{k}, \qquad p=0.86671-0.40654\mathbf{j}-0.21285\mathbf{j}+0.19555\mathbf{k}. $$

Since $p$ is a unit quaternion, $p^{-1}=\overline{p}=0.86671+0.40654\mathbf{j}+0.21285\mathbf{j}-0.19555\mathbf{k}$.

There are many packages in programming languages and computer algebra systems that let you do calculations like these with quaternions. I explain quaternions below.


A quaternion can be thought of as a scalar plus a 3D vector (also known as real and imaginary parts). The product of a scalar and a 3D vector is the usual scalar multiplication. The product of two vectors produces a quaternion with both scalar and vector components, given by (minus) the dot product and cross product respectively. In other words, for 3D vectors $\mathbf{u}$ and $\mathbf{v}$, their quaternion product is

$$ \mathbf{uv}=-\mathbf{u}\cdot\mathbf{v}+\mathbf{u}\times\mathbf{v} $$

A fun way to think about this is that each of $\mathbf{i},\mathbf{j},\mathbf{k}$ is a square root of $-1$, and that multiplying two of them in cyclic order yields the third (e.g. $\mathbf{ij}=\mathbf{k}$) or out of order is the opposite (e.g. $\mathbf{ji}=-\mathbf{k}$). As a good exercise, you can discover the only square roots of $+1$ are $\pm1$; the square roots of $-1$ are any unit vector; two quaternions commute ($pq=qp$) if and only if their vector parts are parallel (i.e. scalar multiples of each other); and two quaternions anticommute ($qp=-pq$) if and only if they are perpendicular vectors.

The norm $|a+b\mathbf{i}+c\mathbf{j}+d\mathbf{k}|=\sqrt{a^2+b^2+c^2+d^2}$ is multiplicative, i.e. $|pq|=|p||q|$. The quaternion conjugate of $p=a+b\mathbf{i}+c\mathbf{j}+d\mathbf{k}$ is $\overline{p}=a-b\mathbf{i}-c\mathbf{j}-d\mathbf{k}$, i.e. the vector part is negated. This is useful in calculating the multiplicative inverse of a quaternion, $p^{-1}=\overline{p}/|p|^2$.

Because all unit vectors are square roots of $-1$, we have Euler's formula $\exp(\theta\mathbf{u})=\cos\theta+\sin\theta\,\mathbf{u}$, which means every quaternion has a polar form $p=r\exp(\theta\mathbf{u})$ where $r=|p|\ge0$ is the magnitude, $\mathbf{u}$ is the vector part of $p$ normalized to be a unit vector, and $0\le\theta\le\pi$ is a convex angle. You calculate the polar form just as you would for a complex number (after normalizing the vector part for $\mathbf{u}$). For nonreal quaternions the polar form is unique with $0<\theta<\pi$, otherwise for real nonzero quaternions, $\theta$ is $0$ or $\pi$ for positive/negative reals respectively, and $\mathbf{u}$ is an arbitrary unit vector.

Given a quaternion $p$, left-multiplication $L_p(x):=px$ and right-multiplication $R_p(x):=xp$ are linear transformations when considered as functions of $x$, if we treat the quaternions as a 4D real vector space. Because the norm is multiplicative, these are orthogonal transformations (aka linear isometries, or 4D rotations) for unit quaternions $p$ (i.e. with $|p|=1$).

If $\mathbf{u}$ is a unit vector, it can be extended to an orthonormal basis $\{\mathbf{u},\mathbf{v},\mathbf{w}\}$ with the same orientation as the standard basis $\{\mathbf{i},\mathbf{j},\mathbf{k}\}$ (i.e. $\mathbf{w}=\mathbf{u}\times\mathbf{v}$ according to the right-hand rule, just as $\mathbf{k}=\mathbf{i}\times\mathbf{j}$). Then $\{1,\mathbf{u},\mathbf{v},\mathbf{w}\}$ is an orthonormal basis for the quaternions as a real vector space. We can calculate the $4\times4$ matrix representing $L_p$ and $R_p$ with respect to this basis fairly easily; both are block-diagonal with two $2\times2$ blocks which are rotation matrices with angle $\theta$, except the second block of $R_p$ uses $-\theta$ instead of $\theta$.

The cumulative effect is that "conjugating" a quaternion by $p$, i.e. $(L_p\circ R_p^{-1})(x)=pxp^{=1}$ rotates in the $\mathbf{vw}$-plane by $2\theta$ and fixed the $1\mathbf{u}$-plane pointwise. In other words, if $\mathbf{x}$ is a 3D vector, then $p\mathbf{x}p^{-1}$ is a 3D vector which is the rotation of $\mathbf{x}$ around $\mathbf{u}$ by an angle of $2\theta$. The fact there is a "$2$" in this formula is a manifestation of spin. It implies every 3D rotation is represented by two antipodal unit quaternions $\pm p$.

$\endgroup$
1
  • $\begingroup$ It would probably be good to add that if you have a quaternion $n$, wich is not an unit quaternion, can convert it to a unit quaternion with $u = \frac{n}{|n|}$ where $u$ is the unit quaternion ... ;) $\endgroup$ Oct 27, 2022 at 4:17
1
$\begingroup$

A quaternion can be faithfully represented by a real $4\times 4$ matrix, i.e. $$\eqalign{ \def\bbR#1{{\mathbb R}^{#1}} \def\o{{\tt1}}\def\p{\partial} \def\m#1{\left[\begin{array}{r}#1\end{array}\right]} \def\v#1{\begin{Bmatrix}#1\end{Bmatrix}} \def\qiq{\quad\implies\quad} \def\qif{\quad\iff\quad} \def\c#1{\color{red}{#1}} q &= \v{s\\x\\y\\z} \qif Q = \m{ s&-x&-y&-z \\ x&s&-z&y \\ y&z&s&-x \\ z&-y&x&s \\ } \\ }$$ Note that $q$ appears in the first column of $Q$ and the conjugate $\:\bar q\iff Q^T$

You can now use ordinary matrix multiplication to address your problem: $$\eqalign{ Q &= \m{ 0.86671 & 0.40654 & 0.21285 & -0.19555 \\ -0.40654 & 0.86671 & -0.19555 & -0.21285 \\ -0.21285 & 0.19555 & 0.86671 & 0.40654 \\ 0.19555 & 0.21285 & -0.40654 & 0.86671 \\ } \\ V &= \m{ 0 & 0 & 0 & -0.15 \\ 0 & 0 & -0.15 & 0 \\ 0 & 0.15 & 0 & 0 \\ 0.15 & 0 & 0 & 0 \\ } \\ V_{rot} &= QVQ^T = \m{ 0 & 0.07919 & -0.09322 & -0.08683 \\ \c{-0.07919} & 0 & -0.08683 & 0.09322 \\ \c{0.09322} & 0.08683 & 0 & 0.07919 \\ \c{0.08683} & -0.09322 & -0.07919 & 0 \\ } \\ }$$ The desired vector can be read from the $\c{\rm first}$ column of $V_{rot}$

A further advantage of the matrix representation becomes apparent when calculating quaternionic functions. Formulas for exp(q), log(q), sqrt(q), etc are seemingly bespoke one-offs, whereas the theory of matrix functions is very mature and is a standard feature of programming languages like Matlab and Julia.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .