|
| 1 | +/** |
| 2 | + * @file |
| 3 | + * @brief Functions related to 3D quaternions and Euler angles. |
| 4 | + * @author Krishna Vedala |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#ifdef __arm__ // if compiling for ARM-Cortex processors |
| 9 | +#define LIBQUAT_ARM |
| 10 | +#include <arm_math.h> |
| 11 | +#else |
| 12 | +#include <math.h> |
| 13 | +#endif |
| 14 | +#include <assert.h> |
| 15 | + |
| 16 | +#include "geometry_datatypes.h" |
| 17 | + |
| 18 | +/** |
| 19 | + * @addtogroup quats 3D Quaternion operations |
| 20 | + * @{ |
| 21 | + */ |
| 22 | + |
| 23 | +/** |
| 24 | + * Function to convert given Euler angles to a quaternion. |
| 25 | + * \f{eqnarray*}{ |
| 26 | + * q_{0} & = |
| 27 | + * &\cos\left(\frac{\phi}{2}\right)\cos\left(\frac{\theta}{2}\right)\cos\left(\frac{\psi}{2}\right) |
| 28 | + * + |
| 29 | + * \sin\left(\frac{\phi}{2}\right)\sin\left(\frac{\theta}{2}\right)\sin\left(\frac{\psi}{2}\right)\\ |
| 30 | + * q_{1} & = |
| 31 | + * &\sin\left(\frac{\phi}{2}\right)\cos\left(\frac{\theta}{2}\right)\cos\left(\frac{\psi}{2}\right) |
| 32 | + * - |
| 33 | + * \cos\left(\frac{\phi}{2}\right)\sin\left(\frac{\theta}{2}\right)\sin\left(\frac{\psi}{2}\right)\\ |
| 34 | + * q_{2} & = |
| 35 | + * &\cos\left(\frac{\phi}{2}\right)\sin\left(\frac{\theta}{2}\right)\cos\left(\frac{\psi}{2}\right) |
| 36 | + * + |
| 37 | + * \sin\left(\frac{\phi}{2}\right)\cos\left(\frac{\theta}{2}\right)\sin\left(\frac{\psi}{2}\right)\\ |
| 38 | + * q_{3} & = |
| 39 | + * &\cos\left(\frac{\phi}{2}\right)\cos\left(\frac{\theta}{2}\right)\sin\left(\frac{\psi}{2}\right) |
| 40 | + * - |
| 41 | + * \sin\left(\frac{\phi}{2}\right)\sin\left(\frac{\theta}{2}\right)\cos\left(\frac{\psi}{2}\right)\\ |
| 42 | + * \f} |
| 43 | + * |
| 44 | + * @param [in] in_euler input Euler angles instance |
| 45 | + * @returns converted quaternion |
| 46 | + */ |
| 47 | +quaternion quat_from_euler(const euler *in_euler) |
| 48 | +{ |
| 49 | + quaternion out_quat; |
| 50 | + |
| 51 | + if (!in_euler) // if null |
| 52 | + { |
| 53 | + fprintf(stderr, "%s: Invalid input.", __func__); |
| 54 | + return out_quat; |
| 55 | + } |
| 56 | + |
| 57 | + quaternion temp; |
| 58 | + |
| 59 | + float cy = cosf(in_euler->yaw * 0.5f); |
| 60 | + float sy = sinf(in_euler->yaw * 0.5f); |
| 61 | + float cp = cosf(in_euler->pitch * 0.5f); |
| 62 | + float sp = sinf(in_euler->pitch * 0.5f); |
| 63 | + float cr = cosf(in_euler->roll * 0.5f); |
| 64 | + float sr = sinf(in_euler->roll * 0.5f); |
| 65 | + |
| 66 | + temp.w = cr * cp * cy + sr * sp * sy; |
| 67 | + temp.q1 = sr * cp * cy - cr * sp * sy; |
| 68 | + temp.q2 = cr * sp * cy + sr * cp * sy; |
| 69 | + temp.q3 = cr * cp * sy - sr * sp * cy; |
| 70 | + |
| 71 | + return temp; |
| 72 | +} |
| 73 | + |
| 74 | +/** |
| 75 | + * Function to convert given quaternion to Euler angles. |
| 76 | + * \f{eqnarray*}{ |
| 77 | + * \phi & = & |
| 78 | + * \tan^{-1}\left[\frac{2\left(q_0q_1+q_2q_3\right)}{1-2\left(q_1^2+q_2^2\right)}\right]\\ |
| 79 | + * \theta & = |
| 80 | + * &-\sin^{-1}\left[2\left(q_0q_2-q_3q_1\right)\right]\\ |
| 81 | + * \psi & = & |
| 82 | + * \tan^{-1}\left[\frac{2\left(q_0q_3+q_1q_2\right)}{1-2\left(q_2^2+q_3^2\right)}\right]\\ |
| 83 | + * \f} |
| 84 | + * |
| 85 | + * @param [in] in_quat input quaternion instance |
| 86 | + * @returns converted euler angles |
| 87 | + */ |
| 88 | +euler euler_from_quat(const quaternion *in_quat) |
| 89 | +{ |
| 90 | + euler out_euler; |
| 91 | + if (!in_quat) // if null |
| 92 | + { |
| 93 | + fprintf(stderr, "%s: Invalid input.", __func__); |
| 94 | + return out_euler; |
| 95 | + } |
| 96 | + |
| 97 | + out_euler.roll = atan2f( |
| 98 | + 2.f * (in_quat->w * in_quat->q1 + in_quat->q2 * in_quat->q3), |
| 99 | + 1.f - 2.f * (in_quat->q1 * in_quat->q1 + in_quat->q2 * in_quat->q2)); |
| 100 | + out_euler.pitch = |
| 101 | + asinf(2.f * (in_quat->w * in_quat->q2 + in_quat->q1 * in_quat->q3)); |
| 102 | + out_euler.yaw = atan2f( |
| 103 | + 2.f * (in_quat->w * in_quat->q3 + in_quat->q1 * in_quat->q2), |
| 104 | + 1.f - 2.f * (in_quat->q2 * in_quat->q2 + in_quat->q3 * in_quat->q3)); |
| 105 | + |
| 106 | + return out_euler; |
| 107 | +} |
| 108 | + |
| 109 | +/** |
| 110 | + * Function to multiply two quaternions. |
| 111 | + * \f{eqnarray*}{ |
| 112 | + * \mathbf{c} & = & \mathbf{a}\otimes\mathbf{b}\\ |
| 113 | + * & = & \begin{bmatrix}a_{0} & a_{1} & a_{2} & |
| 114 | + * a_{3}\end{bmatrix}\otimes\begin{bmatrix}b_{0} & b_{1} & b_{2} & |
| 115 | + * b_{3}\end{bmatrix}\\ |
| 116 | + * & = & |
| 117 | + * \begin{bmatrix} |
| 118 | + * a_{0}b_{0}-a_{1}b_{1}-a_{2}b_{2}-a_{3}b_{3}\\ |
| 119 | + * a_{0}b_{1}+a_{1}b_{0}+a_{2}b_{3}-a_{3}b_{2}\\ |
| 120 | + * a_{0}b_{2}-a_{1}b_{3}+a_{2}b_{0}+a_{3}b_{1}\\ |
| 121 | + * a_{0}b_{3}+a_{1}b_{2}-a_{2}b_{1}+a_{3}b_{0} |
| 122 | + * \end{bmatrix}^{T} |
| 123 | + * \f} |
| 124 | + * |
| 125 | + * @param [in] in_quat1 first input quaternion instance |
| 126 | + * @param [in] in_quat2 second input quaternion instance |
| 127 | + * @returns resultant quaternion |
| 128 | + */ |
| 129 | +quaternion quaternion_multiply(const quaternion *in_quat1, |
| 130 | + const quaternion *in_quat2) |
| 131 | +{ |
| 132 | + quaternion out_quat; |
| 133 | + if (!in_quat1 || !in_quat2) // if null |
| 134 | + { |
| 135 | + fprintf(stderr, "%s: Invalid input.", __func__); |
| 136 | + return out_quat; |
| 137 | + } |
| 138 | + |
| 139 | + out_quat.w = in_quat1->w * in_quat2->w - in_quat1->q1 * in_quat2->q1 - |
| 140 | + in_quat1->q2 * in_quat2->q2 - in_quat1->q3 * in_quat2->q3; |
| 141 | + out_quat.q1 = in_quat1->w * in_quat2->q1 + in_quat1->q1 * in_quat2->w + |
| 142 | + in_quat1->q2 * in_quat2->q3 - in_quat1->q3 * in_quat2->q2; |
| 143 | + out_quat.q2 = in_quat1->w * in_quat2->q2 - in_quat1->q1 * in_quat2->q3 + |
| 144 | + in_quat1->q2 * in_quat2->w + in_quat1->q3 * in_quat2->q1; |
| 145 | + out_quat.q3 = in_quat1->w * in_quat2->q3 + in_quat1->q1 * in_quat2->q2 - |
| 146 | + in_quat1->q2 * in_quat2->q1 + in_quat1->q3 * in_quat2->w; |
| 147 | + |
| 148 | + return out_quat; |
| 149 | +} |
| 150 | + |
| 151 | +/** @} */ |
| 152 | + |
| 153 | +static void test() |
| 154 | +{ |
| 155 | + quaternion quat = {0.7071f, 0.7071f, 0.f, 0.f}; |
| 156 | + euler eul = euler_from_quat(&quat); |
| 157 | + printf("Euler: %.4g, %.4g, %.4g\n", eul.pitch, eul.roll, eul.yaw); |
| 158 | + |
| 159 | + quaternion test_quat = quat_from_euler(&eul); |
| 160 | + printf("Quaternion: %.4g %+.4g %+.4g %+.4g\n", test_quat.w, |
| 161 | + test_quat.dual.x, test_quat.dual.y, test_quat.dual.z); |
| 162 | + |
| 163 | + assert(fabsf(test_quat.w - quat.w) < .01); |
| 164 | + assert(fabsf(test_quat.q1 - quat.q1) < .01); |
| 165 | + assert(fabsf(test_quat.q2 - quat.q2) < .01); |
| 166 | + assert(fabsf(test_quat.q3 - quat.q3) < .01); |
| 167 | +} |
| 168 | + |
| 169 | +int main() |
| 170 | +{ |
| 171 | + test(); |
| 172 | + return 0; |
| 173 | +} |
0 commit comments