Estoy buscando extraer de un eje angular de la representación de una unidad de cuaterniones. A partir de la definición, un ingenuo intento podría ser: $ q = \begin{bmatrix} cos(\theta/2) \\ \omega_x \sin(\theta/2) \\ \omega_y \sin(\theta/2) \\ \omega_z \sin(\theta/2)\end{bmatrix} = \begin{bmatrix} w \\ x \\ y \\ z\end{bmatrix}$
Y por lo tanto: $\theta = \operatorname{atan2}(\|\begin{bmatrix}x & y & z\end{bmatrix}\|, w)$
Lo que parece bastante inocente, pero el eje es más problemática: $ \omega = \frac{\begin{bmatrix} x & y & z\end{bmatrix}^T}{\|\begin{bmatrix}x & y & z\end{bmatrix}\|}$
Especialmente si el pecado del ángulo es muy pequeño - Considerar algo cerca de la unidad de cuaterniones, por ejemplo.
Una estrategia podría ser la de convertir el quaternion a una matriz de rotación, y $\omega = \operatorname{nullspace}(R - I)$, logrado a través de un SVD, pero esto parece un terrible desperdicio de cómputo y podría ser numéricamente dudosa como R enfoques I.
Así, hay una numéricamente buena estrategia? Especialmente uno documentada en la literatura, en alguna parte? Incluso Eigen, la que se hace referencia en Esta solución a una pregunta similar, parece comprobar simplemente por un pequeño delta:
/** Set \c *this from a \b unit quaternion.
* The axis is normalized.
*
* \warning As any other method dealing with quaternion, if the input quaternion
* is not normalized then the result is undefined.
*/
template<typename Scalar>
template<typename QuatDerived>
AngleAxis<Scalar>& AngleAxis<Scalar>::operator=(const QuaternionBase<QuatDerived>& q)
{
using std::acos;
using std::min;
using std::max;
Scalar n2 = q.vec().squaredNorm();
if (n2 < NumTraits<Scalar>::dummy_precision()*NumTraits<Scalar>::dummy_precision())
{
m_angle = 0;
m_axis << 1, 0, 0;
}
else
{
m_angle = Scalar(2)*acos((min)((max)(Scalar(-1),q.w()),Scalar(1)));
m_axis = q.vec() / internal::sqrt(n2);
}
return *this;
}