/* * 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 #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; }