pxmlw6n2f/Gazebo_Distributed_MPI/gazebo/math/Quaternion.cc

654 lines
16 KiB
C++
Raw Normal View History

2019-04-18 10:27:54 +08:00
/*
* 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;
}