You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

405 lines
9.9 KiB

/**
* @file Quaternion.hpp
*
* All rotations and axis systems follow the right-hand rule.
*
* In order to rotate a vector v by a righthand rotation defined by the quaternion q
* one can use the following operation:
* v_rotated = q^(-1) * [0;v] * q
* where q^(-1) represents the inverse of the quaternion q.
* The product z of two quaternions z = q1 * q2 represents an intrinsic rotation
* in the order of first q1 followed by q2.
* The first element of the quaternion
* represents the real part, thus, a quaternion representing a zero-rotation
* is defined as (1,0,0,0).
*
* @author James Goppert <james.goppert@gmail.com>
*/
#pragma once
#include "math.hpp"
#include "helper_functions.hpp"
namespace matrix
{
template <typename Type>
class Dcm;
template <typename Type>
class Euler;
template <typename Type>
class AxisAngle;
/**
* Quaternion class
*
* The rotation between two coordinate frames is
* described by this class.
*/
template<typename Type>
class Quaternion : public Vector<Type, 4>
{
public:
virtual ~Quaternion() {};
typedef Matrix<Type, 4, 1> Matrix41;
typedef Matrix<Type, 3, 1> Matrix31;
/**
* Constructor from array
*
* @param data_ array
*/
Quaternion(const Type *data_) :
Vector<Type, 4>(data_)
{
}
/**
* Standard constructor
*/
Quaternion() :
Vector<Type, 4>()
{
Quaternion &q = *this;
q(0) = 1;
q(1) = 0;
q(2) = 0;
q(3) = 0;
}
/**
* Constructor from Matrix41
*
* @param other Matrix41 to copy
*/
Quaternion(const Matrix41 &other) :
Vector<Type, 4>(other)
{
}
/**
* Constructor from dcm
*
* Instance is initialized from a dcm representing coordinate transformation
* from frame 2 to frame 1.
*
* @param dcm dcm to set quaternion to
*/
Quaternion(const Dcm<Type> &dcm) :
Vector<Type, 4>()
{
Quaternion &q = *this;
q(0) = Type(0.5) * Type(sqrt(Type(1) + dcm(0, 0) +
dcm(1, 1) + dcm(2, 2)));
q(1) = Type((dcm(2, 1) - dcm(1, 2)) /
(Type(4) * q(0)));
q(2) = Type((dcm(0, 2) - dcm(2, 0)) /
(Type(4) * q(0)));
q(3) = Type((dcm(1, 0) - dcm(0, 1)) /
(Type(4) * q(0)));
}
/**
* Constructor from euler angles
*
* This sets the instance to a quaternion representing coordinate transformation from
* frame 2 to frame 1 where the rotation from frame 1 to frame 2 is described
* by a 3-2-1 intrinsic Tait-Bryan rotation sequence.
*
* @param euler euler angle instance
*/
Quaternion(const Euler<Type> &euler) :
Vector<Type, 4>()
{
Quaternion &q = *this;
Type cosPhi_2 = Type(cos(euler.phi() / (Type)2.0));
Type cosTheta_2 = Type(cos(euler.theta() / (Type)2.0));
Type cosPsi_2 = Type(cos(euler.psi() / (Type)2.0));
Type sinPhi_2 = Type(sin(euler.phi() / (Type)2.0));
Type sinTheta_2 = Type(sin(euler.theta() / (Type)2.0));
Type sinPsi_2 = Type(sin(euler.psi() / (Type)2.0));
q(0) = cosPhi_2 * cosTheta_2 * cosPsi_2 +
sinPhi_2 * sinTheta_2 * sinPsi_2;
q(1) = sinPhi_2 * cosTheta_2 * cosPsi_2 -
cosPhi_2 * sinTheta_2 * sinPsi_2;
q(2) = cosPhi_2 * sinTheta_2 * cosPsi_2 +
sinPhi_2 * cosTheta_2 * sinPsi_2;
q(3) = cosPhi_2 * cosTheta_2 * sinPsi_2 -
sinPhi_2 * sinTheta_2 * cosPsi_2;
}
/**
* Quaternion from AxisAngle
*
* @param aa axis-angle vector
*/
Quaternion(const AxisAngle<Type> &aa) :
Vector<Type, 4>()
{
Quaternion &q = *this;
Type angle = aa.norm();
Vector<Type, 3> axis = aa.unit();
if (angle < (Type)1e-10) {
q(0) = (Type)1.0;
q(1) = q(2) = q(3) = 0;
} else {
Type magnitude = sinf(angle / 2.0f);
q(0) = cosf(angle / 2.0f);
q(1) = axis(0) * magnitude;
q(2) = axis(1) * magnitude;
q(3) = axis(2) * magnitude;
}
}
/**
* Constructor from quaternion values
*
* Instance is initialized from quaternion values representing coordinate
* transformation from frame 2 to frame 1.
* A zero-rotation quaternion is represented by (1,0,0,0).
*
* @param a set quaternion value 0
* @param b set quaternion value 1
* @param c set quaternion value 2
* @param d set quaternion value 3
*/
Quaternion(Type a, Type b, Type c, Type d) :
Vector<Type, 4>()
{
Quaternion &q = *this;
q(0) = a;
q(1) = b;
q(2) = c;
q(3) = d;
}
/**
* Quaternion multiplication operator
*
* @param q quaternion to multiply with
* @return product
*/
Quaternion operator*(const Quaternion &q) const
{
const Quaternion &p = *this;
Quaternion r;
r(0) = p(0) * q(0) - p(1) * q(1) - p(2) * q(2) - p(3) * q(3);
r(1) = p(0) * q(1) + p(1) * q(0) - p(2) * q(3) + p(3) * q(2);
r(2) = p(0) * q(2) + p(1) * q(3) + p(2) * q(0) - p(3) * q(1);
r(3) = p(0) * q(3) - p(1) * q(2) + p(2) * q(1) + p(3) * q(0);
return r;
}
/**
* Self-multiplication operator
*
* @param other quaternion to multiply with
*/
void operator*=(const Quaternion &other)
{
Quaternion &self = *this;
self = self * other;
}
/**
* Scalar multiplication operator
*
* @param scalar scalar to multiply with
* @return product
*/
Quaternion operator*(Type scalar) const
{
const Quaternion &q = *this;
return scalar * q;
}
/**
* Scalar self-multiplication operator
*
* @param scalar scalar to multiply with
*/
void operator*=(Type scalar)
{
Quaternion &q = *this;
q = q * scalar;
}
/**
* Computes the derivative
*
* @param w direction
*/
Matrix41 derivative(const Matrix31 &w) const
{
const Quaternion &q = *this;
Quaternion<Type> v(0, w(0, 0), w(1, 0), w(2, 0));
return v * q * Type(0.5);
}
/**
* Invert quaternion
*/
void invert()
{
Quaternion &q = *this;
q(1) *= -1;
q(2) *= -1;
q(3) *= -1;
}
/**
* Invert quaternion
*
* @return inverted quaternion
*/
Quaternion inversed()
{
Quaternion &q = *this;
Quaternion ret;
ret(0) = q(0);
ret(1) = -q(1);
ret(2) = -q(2);
ret(3) = -q(3);
return ret;
}
/**
* Rotate quaternion from rotation vector
* TODO replace with AxisAngle call
*
* @param vec rotation vector
*/
void rotate(const Vector<Type, 3> &vec)
{
Quaternion res;
res.from_axis_angle(vec);
(*this) = (*this) * res;
}
Vector3f conjugate(const Vector3f &vec) {
Quaternion q = *this;
Quaternion v(0, vec(0), vec(1), vec(2));
Quaternion res = q*v*q.inversed();
return Vector3f(res(1), res(2), res(3));
}
Vector3f conjugate_inversed(const Vector3f &vec) {
Quaternion q = *this;
Quaternion v(0, vec(0), vec(1), vec(2));
Quaternion res = q.inversed()*v*q;
return Vector3f(res(1), res(2), res(3));
}
/**
* Rotation quaternion from vector
*
* The axis of rotation is given by vector direction and
* the angle is given by the norm.
*
* @param vec rotation vector
* @return quaternion representing the rotation
*/
void from_axis_angle(Vector<Type, 3> vec)
{
Quaternion &q = *this;
Type theta = vec.norm();
if (theta < (Type)1e-10) {
q(0) = (Type)1.0;
q(1) = q(2) = q(3) = 0;
return;
}
vec /= theta;
from_axis_angle(vec, theta);
}
/**
* Rotation quaternion from axis and angle
* XXX DEPRECATED, use AxisAngle class
*
* @param axis axis of rotation
* @param theta scalar describing angle of rotation
* @return quaternion representing the rotation
*/
void from_axis_angle(const Vector<Type, 3> &axis, Type theta)
{
Quaternion &q = *this;
if (theta < (Type)1e-10) {
q(0) = (Type)1.0;
q(1) = q(2) = q(3) = 0;
}
Type magnitude = sinf(theta / 2.0f);
q(0) = cosf(theta / 2.0f);
q(1) = axis(0) * magnitude;
q(2) = axis(1) * magnitude;
q(3) = axis(2) * magnitude;
}
/**
* Rotation vector from quaternion
* XXX DEPRECATED, use AxisAngle class
*
* The axis of rotation is given by vector direction and
* the angle is given by the norm.
*
* @return vector, direction representing rotation axis and norm representing angle
*/
Vector<Type, 3> to_axis_angle()
{
Quaternion &q = *this;
Type axis_magnitude = Type(sqrt(q(1) * q(1) + q(2) * q(2) + q(3) * q(3)));
Vector<Type, 3> vec;
vec(0) = q(1);
vec(1) = q(2);
vec(2) = q(3);
if (axis_magnitude >= (Type)1e-10) {
vec = vec / axis_magnitude;
vec = vec * wrap_pi((Type)2.0 * atan2f(axis_magnitude, q(0)));
}
return vec;
}
/**
* Imaginary components of quaternion
*/
Vector3<Type> imag()
{
Quaternion &q = *this;
return Vector3<Type>(q(1), q(2), q(3));
}
/**
* XXX DEPRECATED, can use assignment or ctor
*/
Quaternion from_dcm(Matrix<Type, 3, 3> dcm) {
return Quaternion(Dcmf(dcm));
}
/**
* XXX DEPRECATED, can use assignment or ctor
*/
Dcm<Type> to_dcm() {
return Dcm<Type>(*this);
}
};
typedef Quaternion<float> Quatf;
typedef Quaternion<float> Quaternionf;
} // namespace matrix
/* vim: set et fenc=utf-8 ff=unix sts=0 sw=4 ts=4 : */