stateless_orientation.cpp
Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) 2010, CCNY Robotics Lab
00003  *  Ivan Dryanovski <ivan.dryanovski@gmail.com>
00004  *
00005  *  http://robotics.ccny.cuny.edu
00006  *
00007  *  Based on implementation of Madgwick's IMU and AHRS algorithms.
00008  *  http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
00009  *
00010  *
00011  *  This program is free software: you can redistribute it and/or modify
00012  *  it under the terms of the GNU General Public License as published by
00013  *  the Free Software Foundation, either version 3 of the License, or
00014  *  (at your option) any later version.
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public License
00022  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00023  */
00024 
00025 #include "imu_filter_madgwick/stateless_orientation.h"
00026 #include <tf2/LinearMath/Matrix3x3.h>
00027 #include <tf2/convert.h>
00028 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
00029 
00030 template<typename T>
00031 static inline void crossProduct(
00032       T ax, T ay, T az,
00033       T bx, T by, T bz,
00034       T& rx, T& ry, T& rz) {
00035   rx = ay*bz - az*by;
00036   ry = az*bx - ax*bz;
00037   rz = ax*by - ay*bx;
00038 }
00039 
00040 
00041 template<typename T>
00042 static inline T normalizeVector(T& vx, T& vy, T& vz) {
00043   T norm = sqrt(vx*vx + vy*vy + vz*vz);
00044   T inv = 1.0 / norm;
00045   vx *= inv;
00046   vy *= inv;
00047   vz *= inv;
00048   return norm;
00049 
00050 }
00051 
00052 
00053 bool StatelessOrientation::computeOrientation(
00054   WorldFrame::WorldFrame frame,
00055   geometry_msgs::Vector3 A,
00056   geometry_msgs::Vector3 E,
00057   geometry_msgs::Quaternion& orientation) {
00058 
00059   float Hx, Hy, Hz;
00060   float Mx, My, Mz;
00061   float normH, invH, invA;
00062 
00063   // A: pointing up
00064   float Ax = A.x, Ay = A.y, Az = A.z;
00065 
00066   // E: pointing down/north
00067   float Ex = E.x, Ey = E.y, Ez = E.z;
00068 
00069   // H: vector horizontal, pointing east
00070   // H = E x A
00071   crossProduct(Ex, Ey, Ez, Ax, Ay, Az, Hx, Hy, Hz);
00072 
00073   // normalize H
00074   normH = normalizeVector(Hx, Hy, Hz);
00075   if (normH < 1E-7) {
00076     // device is close to free fall (or in space?), or close to
00077     // magnetic north pole.
00078     // mag in T => Threshold 1E-7, typical values are  > 1E-5.
00079     return false;
00080   }
00081 
00082   // normalize A
00083   normalizeVector(Ax, Ay, Az);
00084 
00085   // M: vector horizontal, pointing north
00086   // M = A x H
00087   crossProduct(Ax, Ay, Az, Hx, Hy, Hz, Mx, My, Mz);
00088 
00089   // Create matrix for basis transformation
00090   tf2::Matrix3x3 R;
00091   switch (frame) {
00092     case WorldFrame::NED:
00093       // vector space world W:
00094       // Basis: bwx (1,0,0) north, bwy (0,1,0) east, bwz (0,0,1) down
00095       // vector space local L:
00096       // Basis: M ,H, -A
00097       // W(1,0,0) => L(M)
00098       // W(0,1,0) => L(H)
00099       // W(0,0,1) => L(-A)
00100 
00101       // R: Transform Matrix local => world equals basis of L, because basis of W is I
00102       R[0][0] = Mx;     R[0][1] = Hx;     R[0][2] = -Ax;
00103       R[1][0] = My;     R[1][1] = Hy;     R[1][2] = -Ay;
00104       R[2][0] = Mz;     R[2][1] = Hz;     R[2][2] = -Az;
00105       break;
00106 
00107     case WorldFrame::NWU:
00108       // vector space world W:
00109       // Basis: bwx (1,0,0) north, bwy (0,1,0) west, bwz (0,0,1) up
00110       // vector space local L:
00111       // Basis: M ,H, -A
00112       // W(1,0,0) => L(M)
00113       // W(0,1,0) => L(-H)
00114       // W(0,0,1) => L(A)
00115 
00116       // R: Transform Matrix local => world equals basis of L, because basis of W is I
00117       R[0][0] = Mx;     R[0][1] = -Hx;     R[0][2] = Ax;
00118       R[1][0] = My;     R[1][1] = -Hy;     R[1][2] = Ay;
00119       R[2][0] = Mz;     R[2][1] = -Hz;     R[2][2] = Az;
00120       break;
00121 
00122     default:
00123     case WorldFrame::ENU:
00124       // vector space world W:
00125       // Basis: bwx (1,0,0) east, bwy (0,1,0) north, bwz (0,0,1) up
00126       // vector space local L:
00127       // Basis: H, M , A
00128       // W(1,0,0) => L(H)
00129       // W(0,1,0) => L(M)
00130       // W(0,0,1) => L(A)
00131 
00132       // R: Transform Matrix local => world equals basis of L, because basis of W is I
00133       R[0][0] = Hx;     R[0][1] = Mx;     R[0][2] = Ax;
00134       R[1][0] = Hy;     R[1][1] = My;     R[1][2] = Ay;
00135       R[2][0] = Hz;     R[2][1] = Mz;     R[2][2] = Az;
00136       break;
00137   }
00138 
00139   // Matrix.getRotation assumes vector rotation, but we're using
00140   // coordinate systems. Thus negate rotation angle (inverse).
00141   tf2::Quaternion q;
00142   R.getRotation(q);
00143   tf2::convert(q.inverse(), orientation);
00144   return true;
00145 }
00146 
00147 
00148 bool StatelessOrientation::computeOrientation(
00149   WorldFrame::WorldFrame frame,
00150   geometry_msgs::Vector3 A,
00151   geometry_msgs::Quaternion& orientation) {
00152 
00153   // This implementation could be optimized regarding speed.
00154 
00155   // magnetic Field E must not be parallel to A,
00156   // choose an arbitrary orthogonal vector
00157   geometry_msgs::Vector3 E;
00158   if (fabs(A.x) > 0.1 || fabs(A.y) > 0.1) {
00159       E.x = A.y;
00160       E.y = A.x;
00161       E.z = 0.0;
00162   } else if (fabs(A.z) > 0.1) {
00163       E.x = 0.0;
00164       E.y = A.z;
00165       E.z = A.y;
00166   } else {
00167       // free fall
00168       return false;
00169   }
00170 
00171   return computeOrientation(frame, A, E, orientation);
00172 }


imu_filter_madgwick
Author(s): Ivan Dryanovski
autogenerated on Tue May 23 2017 02:23:02