654 lines
16 KiB
C++
654 lines
16 KiB
C++
/*
|
|
* Copyright (C) 2012 Open Source Robotics Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
/* Desc: External interfaces for Gazebo
|
|
* Author: Nate Koenig
|
|
* Date: 03 Apr 2007
|
|
*/
|
|
#include <math.h>
|
|
#include "gazebo/math/Helpers.hh"
|
|
#include "gazebo/math/Quaternion.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace math;
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion()
|
|
: w(1), x(0), y(0), z(0)
|
|
{
|
|
// quaternion not normalized, because that breaks
|
|
// Pose::CoordPositionAdd(...)
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion(const double &_w, const double &_x,
|
|
const double &_y, const double &_z)
|
|
: w(_w), x(_x), y(_y), z(_z)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion(const double &_roll, const double &_pitch,
|
|
const double &_yaw)
|
|
{
|
|
this->SetFromEuler(Vector3(_roll, _pitch, _yaw));
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion(const Vector3 &_rpy)
|
|
{
|
|
this->SetFromEuler(_rpy);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion(const Vector3 &_axis, const double &_angle)
|
|
{
|
|
this->SetFromAxis(_axis, _angle);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion(const Quaternion &_qt)
|
|
{
|
|
this->w = _qt.w;
|
|
this->x = _qt.x;
|
|
this->y = _qt.y;
|
|
this->z = _qt.z;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::Quaternion(const ignition::math::Quaterniond &_qt)
|
|
{
|
|
this->w = _qt.W();
|
|
this->x = _qt.X();
|
|
this->y = _qt.Y();
|
|
this->z = _qt.Z();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion::~Quaternion()
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion &Quaternion::operator =(const Quaternion &qt)
|
|
{
|
|
this->w = qt.w;
|
|
this->x = qt.x;
|
|
this->y = qt.y;
|
|
this->z = qt.z;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::SetToIdentity()
|
|
{
|
|
this->w = 1.0;
|
|
this->x = 0.0;
|
|
this->y = 0.0;
|
|
this->z = 0.0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::GetLog() const
|
|
{
|
|
// If q = cos(A)+sin(A)*(x*i+y*j+z*k) where (x, y, z) is unit length, then
|
|
// log(q) = A*(x*i+y*j+z*k). If sin(A) is near zero, use log(q) =
|
|
// sin(A)*(x*i+y*j+z*k) since sin(A)/A has limit 1.
|
|
|
|
Quaternion result;
|
|
result.w = 0.0;
|
|
|
|
if (std::fabs(this->w) < 1.0)
|
|
{
|
|
double fAngle = acos(this->w);
|
|
double fSin = sin(fAngle);
|
|
if (std::fabs(fSin) >= 1e-3)
|
|
{
|
|
double fCoeff = fAngle/fSin;
|
|
result.x = fCoeff*x;
|
|
result.y = fCoeff*y;
|
|
result.z = fCoeff*z;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result.x = x;
|
|
result.y = y;
|
|
result.z = z;
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::GetExp() const
|
|
{
|
|
// If q = A*(x*i+y*j+z*k) where (x, y, z) is unit length, then
|
|
// exp(q) = cos(A)+sin(A)*(x*i+y*j+z*k). If sin(A) is near zero,
|
|
// use exp(q) = cos(A)+A*(x*i+y*j+z*k) since A/sin(A) has limit 1.
|
|
|
|
double fAngle = sqrt(this->x*this->x+this->y*this->y+this->z*this->z);
|
|
double fSin = sin(fAngle);
|
|
|
|
Quaternion result;
|
|
result.w = cos(fAngle);
|
|
|
|
if (std::fabs(fSin) >= 1e-3)
|
|
{
|
|
double fCoeff = fSin/fAngle;
|
|
result.x = fCoeff*this->x;
|
|
result.y = fCoeff*this->y;
|
|
result.z = fCoeff*this->z;
|
|
}
|
|
else
|
|
{
|
|
result.x = this->x;
|
|
result.y = this->y;
|
|
result.z = this->z;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::Invert()
|
|
{
|
|
this->Normalize();
|
|
// this->w = this->w;
|
|
this->x = -this->x;
|
|
this->y = -this->y;
|
|
this->z = -this->z;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::Normalize()
|
|
{
|
|
double s = 0;
|
|
|
|
s = sqrt(this->w * this->w + this->x * this->x + this->y * this->y +
|
|
this->z * this->z);
|
|
|
|
if (math::equal(s, 0.0))
|
|
{
|
|
this->w = 1.0;
|
|
this->x = 0.0;
|
|
this->y = 0.0;
|
|
this->z = 0.0;
|
|
}
|
|
else
|
|
{
|
|
this->w /= s;
|
|
this->x /= s;
|
|
this->y /= s;
|
|
this->z /= s;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::SetFromAxis(double _ax, double _ay, double _az, double _aa)
|
|
{
|
|
double l;
|
|
|
|
l = _ax * _ax + _ay * _ay + _az * _az;
|
|
|
|
if (math::equal(l, 0.0))
|
|
{
|
|
this->w = 1;
|
|
this->x = 0;
|
|
this->y = 0;
|
|
this->z = 0;
|
|
}
|
|
else
|
|
{
|
|
_aa *= 0.5;
|
|
l = sin(_aa) / sqrt(l);
|
|
this->w = cos(_aa);
|
|
this->x = _ax * l;
|
|
this->y = _ay * l;
|
|
this->z = _az * l;
|
|
}
|
|
|
|
this->Normalize();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::SetFromAxis(const Vector3 &_axis, double _a)
|
|
{
|
|
this->SetFromAxis(_axis.x, _axis.y, _axis.z, _a);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::Set(double _w, double _x, double _y, double _z)
|
|
{
|
|
this->w = _w;
|
|
this->x = _x;
|
|
this->y = _y;
|
|
this->z = _z;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::SetFromEuler(const Vector3 &_vec)
|
|
{
|
|
this->SetFromEuler(_vec.x, _vec.y, _vec.z);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::SetFromEuler(double _roll, double _pitch, double _yaw)
|
|
{
|
|
double phi, the, psi;
|
|
|
|
phi = _roll / 2.0;
|
|
the = _pitch / 2.0;
|
|
psi = _yaw / 2.0;
|
|
|
|
this->w = cos(phi) * cos(the) * cos(psi) + sin(phi) * sin(the) * sin(psi);
|
|
this->x = sin(phi) * cos(the) * cos(psi) - cos(phi) * sin(the) * sin(psi);
|
|
this->y = cos(phi) * sin(the) * cos(psi) + sin(phi) * cos(the) * sin(psi);
|
|
this->z = cos(phi) * cos(the) * sin(psi) - sin(phi) * sin(the) * cos(psi);
|
|
|
|
this->Normalize();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Vector3 Quaternion::GetAsEuler() const
|
|
{
|
|
Vector3 vec;
|
|
|
|
Quaternion copy = *this;
|
|
double squ;
|
|
double sqx;
|
|
double sqy;
|
|
double sqz;
|
|
|
|
copy.Normalize();
|
|
|
|
squ = copy.w * copy.w;
|
|
sqx = copy.x * copy.x;
|
|
sqy = copy.y * copy.y;
|
|
sqz = copy.z * copy.z;
|
|
|
|
// Roll
|
|
vec.x = atan2(2 * (copy.y*copy.z + copy.w*copy.x), squ - sqx - sqy + sqz);
|
|
|
|
// Pitch
|
|
double sarg = -2 * (copy.x*copy.z - copy.w * copy.y);
|
|
vec.y = sarg <= -1.0 ? -0.5*M_PI : (sarg >= 1.0 ? 0.5*M_PI : asin(sarg));
|
|
|
|
// Yaw
|
|
vec.z = atan2(2 * (copy.x*copy.y + copy.w*copy.z), squ + sqx - sqy - sqz);
|
|
|
|
return vec;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::EulerToQuaternion(const Vector3 &_vec)
|
|
{
|
|
Quaternion result;
|
|
result.SetFromEuler(_vec);
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::EulerToQuaternion(double _x, double _y, double _z)
|
|
{
|
|
return EulerToQuaternion(Vector3(_x, _y, _z));
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
double Quaternion::GetRoll()
|
|
{
|
|
return this->GetAsEuler().x;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
double Quaternion::GetPitch()
|
|
{
|
|
return this->GetAsEuler().y;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
double Quaternion::GetYaw()
|
|
{
|
|
return this->GetAsEuler().z;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::GetAsAxis(Vector3 &_axis, double &_angle) const
|
|
{
|
|
double len = this->x*this->x + this->y*this->y + this->z*this->z;
|
|
if (math::equal(len, 0.0))
|
|
{
|
|
_angle = 0.0;
|
|
_axis.Set(1, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
_angle = 2.0 * acos(this->w);
|
|
double invLen = 1.0 / sqrt(len);
|
|
_axis.Set(this->x*invLen, this->y*invLen, this->z*invLen);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::Scale(double _scale)
|
|
{
|
|
Quaternion b;
|
|
Vector3 axis;
|
|
double angle;
|
|
|
|
// Convert to axis-and-angle
|
|
this->GetAsAxis(axis, angle);
|
|
angle *= _scale;
|
|
|
|
this->SetFromAxis(axis.x, axis.y, axis.z, angle);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator+(const Quaternion &qt) const
|
|
{
|
|
Quaternion result(this->w + qt.w, this->x + qt.x,
|
|
this->y + qt.y, this->z + qt.z);
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator+=(const Quaternion &qt)
|
|
{
|
|
*this = *this + qt;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator-=(const Quaternion &qt)
|
|
{
|
|
*this = *this - qt;
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator-(const Quaternion &qt) const
|
|
{
|
|
Quaternion result(this->w - qt.w, this->x - qt.x,
|
|
this->y - qt.y, this->z - qt.z);
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator*=(const Quaternion &qt)
|
|
{
|
|
*this = *this * qt;
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Vector3 Quaternion::operator*(const Vector3 &v) const
|
|
{
|
|
Vector3 uv, uuv;
|
|
Vector3 qvec(this->x, this->y, this->z);
|
|
uv = qvec.Cross(v);
|
|
uuv = qvec.Cross(uv);
|
|
uv *= (2.0f * this->w);
|
|
uuv *= 2.0f;
|
|
|
|
return v + uv + uuv;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator*(const double &_f) const
|
|
{
|
|
return Quaternion(this->w*_f, this->x*_f, this->y*_f, this->z*_f);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Vector3 Quaternion::RotateVectorReverse(Vector3 _vec) const
|
|
{
|
|
Quaternion tmp;
|
|
Vector3 result;
|
|
|
|
tmp.w = 0.0;
|
|
tmp.x = _vec.x;
|
|
tmp.y = _vec.y;
|
|
tmp.z = _vec.z;
|
|
|
|
tmp = this->GetInverse() * (tmp * (*this));
|
|
|
|
result.x = tmp.x;
|
|
result.y = tmp.y;
|
|
result.z = tmp.z;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Quaternion::IsFinite() const
|
|
{
|
|
return std::isfinite(this->w) && std::isfinite(this->x) &&
|
|
std::isfinite(this->y) && std::isfinite(this->z);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Vector3 Quaternion::GetXAxis() const
|
|
{
|
|
double fTy = 2.0f*this->y;
|
|
double fTz = 2.0f*this->z;
|
|
|
|
double fTwy = fTy*this->w;
|
|
double fTwz = fTz*this->w;
|
|
double fTxy = fTy*this->x;
|
|
double fTxz = fTz*this->x;
|
|
double fTyy = fTy*this->y;
|
|
double fTzz = fTz*this->z;
|
|
|
|
return Vector3(1.0f-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Vector3 Quaternion::GetYAxis() const
|
|
{
|
|
double fTx = 2.0f*this->x;
|
|
double fTy = 2.0f*this->y;
|
|
double fTz = 2.0f*this->z;
|
|
double fTwx = fTx*this->w;
|
|
double fTwz = fTz*this->w;
|
|
double fTxx = fTx*this->x;
|
|
double fTxy = fTy*this->x;
|
|
double fTyz = fTz*this->y;
|
|
double fTzz = fTz*this->z;
|
|
|
|
return Vector3(fTxy-fTwz, 1.0f-(fTxx+fTzz), fTyz+fTwx);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Vector3 Quaternion::GetZAxis() const
|
|
{
|
|
double fTx = 2.0f*this->x;
|
|
double fTy = 2.0f*this->y;
|
|
double fTz = 2.0f*this->z;
|
|
double fTwx = fTx*this->w;
|
|
double fTwy = fTy*this->w;
|
|
double fTxx = fTx*this->x;
|
|
double fTxz = fTz*this->x;
|
|
double fTyy = fTy*this->y;
|
|
double fTyz = fTz*this->y;
|
|
|
|
return Vector3(fTxz+fTwy, fTyz-fTwx, 1.0f-(fTxx+fTyy));
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Quaternion::operator ==(const Quaternion &_qt) const
|
|
{
|
|
return equal(this->x, _qt.x, 0.001) &&
|
|
equal(this->y, _qt.y, 0.001) &&
|
|
equal(this->z, _qt.z, 0.001) &&
|
|
equal(this->w, _qt.w, 0.001);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Quaternion::operator!=(const Quaternion &_qt) const
|
|
{
|
|
return !equal(this->x, _qt.x, 0.001) ||
|
|
!equal(this->y, _qt.y, 0.001) ||
|
|
!equal(this->z, _qt.z, 0.001) ||
|
|
!equal(this->w, _qt.w, 0.001);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::operator-() const
|
|
{
|
|
return Quaternion(-this->w, -this->x, -this->y, -this->z);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Matrix3 Quaternion::GetAsMatrix3() const
|
|
{
|
|
Quaternion q = *this;
|
|
q.Normalize();
|
|
return Matrix3(1 - 2*q.y*q.y - 2 *q.z*q.z,
|
|
2 * q.x*q.y - 2*q.z*q.w,
|
|
2 * q.x * q.z + 2 * q.y * q.w,
|
|
2 * q.x * q.y + 2 * q.z * q.w,
|
|
1 - 2*q.x*q.x - 2 * q.z*q.z,
|
|
2 * q.y * q.z - 2 * q.x * q.w,
|
|
2 * q.x * q.z - 2 * q.y * q.w,
|
|
2 * q.y * q.z + 2 * q.x * q.w,
|
|
1 - 2 * q.x*q.x - 2 * q.y*q.y);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Matrix4 Quaternion::GetAsMatrix4() const
|
|
{
|
|
Matrix4 result(Matrix4::IDENTITY);
|
|
result = this->GetAsMatrix3();
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Quaternion::Round(int _precision)
|
|
{
|
|
this->x = precision(this->x, _precision);
|
|
this->y = precision(this->y, _precision);
|
|
this->z = precision(this->z, _precision);
|
|
this->w = precision(this->w, _precision);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
double Quaternion::Dot(const Quaternion &_q) const
|
|
{
|
|
return this->w*_q.w + this->x * _q.x + this->y*_q.y + this->z*_q.z;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::Squad(double _fT, const Quaternion &_rkP,
|
|
const Quaternion &_rkA, const Quaternion &_rkB,
|
|
const Quaternion &_rkQ, bool _shortestPath)
|
|
{
|
|
double fSlerpT = 2.0f*_fT*(1.0f-_fT);
|
|
Quaternion kSlerpP = Slerp(_fT, _rkP, _rkQ, _shortestPath);
|
|
Quaternion kSlerpQ = Slerp(_fT, _rkA, _rkB);
|
|
return Slerp(fSlerpT, kSlerpP, kSlerpQ);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion Quaternion::Slerp(double _fT, const Quaternion &_rkP,
|
|
const Quaternion &_rkQ, bool _shortestPath)
|
|
{
|
|
double fCos = _rkP.Dot(_rkQ);
|
|
Quaternion rkT;
|
|
|
|
// Do we need to invert rotation?
|
|
if (fCos < 0.0f && _shortestPath)
|
|
{
|
|
fCos = -fCos;
|
|
rkT = -_rkQ;
|
|
}
|
|
else
|
|
{
|
|
rkT = _rkQ;
|
|
}
|
|
|
|
if (std::fabs(fCos) < 1 - 1e-03)
|
|
{
|
|
// Standard case (slerp)
|
|
double fSin = sqrt(1 - (fCos*fCos));
|
|
double fAngle = atan2(fSin, fCos);
|
|
// FIXME: should check if (std::fabs(fSin) >= 1e-3)
|
|
double fInvSin = 1.0f / fSin;
|
|
double fCoeff0 = sin((1.0f - _fT) * fAngle) * fInvSin;
|
|
double fCoeff1 = sin(_fT * fAngle) * fInvSin;
|
|
return _rkP * fCoeff0 + rkT * fCoeff1;
|
|
}
|
|
else
|
|
{
|
|
// There are two situations:
|
|
// 1. "rkP" and "rkQ" are very close (fCos ~= +1), so we can do a linear
|
|
// interpolation safely.
|
|
// 2. "rkP" and "rkQ" are almost inverse of each other (fCos ~= -1), there
|
|
// are an infinite number of possibilities interpolation. but we haven't
|
|
// have method to fix this case, so just use linear interpolation here.
|
|
Quaternion t = _rkP * (1.0f - _fT) + rkT * _fT;
|
|
// taking the complement requires renormalisation
|
|
t.Normalize();
|
|
return t;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// Implementation based on:
|
|
// http://physicsforgames.blogspot.com/2010/02/quaternions.html
|
|
Quaternion Quaternion::Integrate(const Vector3 &_angularVelocity,
|
|
const double _deltaT) const
|
|
{
|
|
Quaternion deltaQ;
|
|
Vector3 theta = _angularVelocity * _deltaT * 0.5;
|
|
double thetaMagSq = theta.GetSquaredLength();
|
|
double s;
|
|
if (thetaMagSq * thetaMagSq / 24.0 < GZ_DBL_MIN)
|
|
{
|
|
deltaQ.w = 1.0 - thetaMagSq / 2.0;
|
|
s = 1.0 - thetaMagSq / 6.0;
|
|
}
|
|
else
|
|
{
|
|
double thetaMag = sqrt(thetaMagSq);
|
|
deltaQ.w = cos(thetaMag);
|
|
s = sin(thetaMag) / thetaMag;
|
|
}
|
|
deltaQ.x = theta.x * s;
|
|
deltaQ.y = theta.y * s;
|
|
deltaQ.z = theta.z * s;
|
|
return deltaQ * (*this);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
ignition::math::Quaterniond Quaternion::Ign() const
|
|
{
|
|
return ignition::math::Quaterniond(this->w, this->x, this->y, this->z);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Quaternion &Quaternion::operator=(const ignition::math::Quaterniond &_v)
|
|
{
|
|
this->w = _v.W();
|
|
this->x = _v.X();
|
|
this->y = _v.Y();
|
|
this->z = _v.Z();
|
|
return *this;
|
|
}
|