imu_filter.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010, CCNY Robotics Lab
3  * Ivan Dryanovski <ivan.dryanovski@gmail.com>
4  *
5  * http://robotics.ccny.cuny.edu
6  *
7  * Based on implementation of Madgwick's IMU and AHRS algorithms.
8  * http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
9  *
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include <cmath>
27 
28 // Fast inverse square-root
29 // See: http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Reciprocal_of_the_square_root
30 static float invSqrt(float x)
31 {
32  float xhalf = 0.5f * x;
33  union
34  {
35  float x;
36  int i;
37  } u;
38  u.x = x;
39  u.i = 0x5f3759df - (u.i >> 1);
40  /* The next line can be repeated any number of times to increase accuracy */
41  u.x = u.x * (1.5f - xhalf * u.x * u.x);
42  return u.x;
43 }
44 
45 template<typename T>
46 static inline void normalizeVector(T& vx, T& vy, T& vz)
47 {
48  T recipNorm = invSqrt (vx * vx + vy * vy + vz * vz);
49  vx *= recipNorm;
50  vy *= recipNorm;
51  vz *= recipNorm;
52 }
53 
54 template<typename T>
55 static inline void normalizeQuaternion(T& q0, T& q1, T& q2, T& q3)
56 {
57  T recipNorm = invSqrt (q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
58  q0 *= recipNorm;
59  q1 *= recipNorm;
60  q2 *= recipNorm;
61  q3 *= recipNorm;
62 }
63 
64 static inline void rotateAndScaleVector(
65  float q0, float q1, float q2, float q3,
66  float _2dx, float _2dy, float _2dz,
67  float& rx, float& ry, float& rz) {
68 
69  // result is half as long as input
70  rx = _2dx * (0.5f - q2 * q2 - q3 * q3)
71  + _2dy * (q0 * q3 + q1 * q2)
72  + _2dz * (q1 * q3 - q0 * q2);
73  ry = _2dx * (q1 * q2 - q0 * q3)
74  + _2dy * (0.5f - q1 * q1 - q3 * q3)
75  + _2dz * (q0 * q1 + q2 * q3);
76  rz = _2dx * (q0 * q2 + q1 * q3)
77  + _2dy * (q2 * q3 - q0 * q1)
78  + _2dz * (0.5f - q1 * q1 - q2 * q2);
79 }
80 
81 
82 static inline void compensateGyroDrift(
83  float q0, float q1, float q2, float q3,
84  float s0, float s1, float s2, float s3,
85  float dt, float zeta,
86  float& w_bx, float& w_by, float& w_bz,
87  float& gx, float& gy, float& gz)
88 {
89  // w_err = 2 q x s
90  float w_err_x = 2.0f * q0 * s1 - 2.0f * q1 * s0 - 2.0f * q2 * s3 + 2.0f * q3 * s2;
91  float w_err_y = 2.0f * q0 * s2 + 2.0f * q1 * s3 - 2.0f * q2 * s0 - 2.0f * q3 * s1;
92  float w_err_z = 2.0f * q0 * s3 - 2.0f * q1 * s2 + 2.0f * q2 * s1 - 2.0f * q3 * s0;
93 
94  w_bx += w_err_x * dt * zeta;
95  w_by += w_err_y * dt * zeta;
96  w_bz += w_err_z * dt * zeta;
97 
98  gx -= w_bx;
99  gy -= w_by;
100  gz -= w_bz;
101 }
102 
103 static inline void orientationChangeFromGyro(
104  float q0, float q1, float q2, float q3,
105  float gx, float gy, float gz,
106  float& qDot1, float& qDot2, float& qDot3, float& qDot4)
107 {
108  // Rate of change of quaternion from gyroscope
109  // See EQ 12
110  qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
111  qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
112  qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
113  qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
114 }
115 
116 static inline void addGradientDescentStep(
117  float q0, float q1, float q2, float q3,
118  float _2dx, float _2dy, float _2dz,
119  float mx, float my, float mz,
120  float& s0, float& s1, float& s2, float& s3)
121 {
122  float f0, f1, f2;
123 
124  // Gradient decent algorithm corrective step
125  // EQ 15, 21
126  rotateAndScaleVector(q0,q1,q2,q3, _2dx, _2dy, _2dz, f0, f1, f2);
127 
128  f0 -= mx;
129  f1 -= my;
130  f2 -= mz;
131 
132 
133  // EQ 22, 34
134  // Jt * f
135  s0 += (_2dy * q3 - _2dz * q2) * f0
136  + (-_2dx * q3 + _2dz * q1) * f1
137  + (_2dx * q2 - _2dy * q1) * f2;
138  s1 += (_2dy * q2 + _2dz * q3) * f0
139  + (_2dx * q2 - 2.0f * _2dy * q1 + _2dz * q0) * f1
140  + (_2dx * q3 - _2dy * q0 - 2.0f * _2dz * q1) * f2;
141  s2 += (-2.0f * _2dx * q2 + _2dy * q1 - _2dz * q0) * f0
142  + (_2dx * q1 + _2dz * q3) * f1
143  + (_2dx * q0 + _2dy * q3 - 2.0f * _2dz * q2) * f2;
144  s3 += (-2.0f * _2dx * q3 + _2dy * q0 + _2dz * q1) * f0
145  + (-_2dx * q0 - 2.0f * _2dy * q3 + _2dz * q2) * f1
146  + (_2dx * q1 + _2dy * q2) * f2;
147 }
148 
149 static inline void compensateMagneticDistortion(
150  float q0, float q1, float q2, float q3,
151  float mx, float my, float mz,
152  float& _2bxy, float& _2bz)
153 {
154  float hx, hy, hz;
155  // Reference direction of Earth's magnetic field (See EQ 46)
156  rotateAndScaleVector(q0, -q1, -q2, -q3, mx, my, mz, hx, hy, hz);
157 
158  _2bxy = 4.0f * sqrt (hx * hx + hy * hy);
159  _2bz = 4.0f * hz;
160 
161 }
162 
163 
165  q0(1.0), q1(0.0), q2(0.0), q3(0.0),
166  w_bx_(0.0), w_by_(0.0), w_bz_(0.0),
167  zeta_ (0.0), gain_ (0.0), world_frame_(WorldFrame::ENU)
168 {
169 }
170 
172 {
173 }
174 
176  float gx, float gy, float gz,
177  float ax, float ay, float az,
178  float mx, float my, float mz,
179  float dt)
180 {
181  float s0, s1, s2, s3;
182  float qDot1, qDot2, qDot3, qDot4;
183  float _2bz, _2bxy;
184 
185  // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation)
186  if (!std::isfinite(mx) || !std::isfinite(my) || !std::isfinite(mz))
187  {
188  madgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, dt);
189  return;
190  }
191 
192  // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
193  if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f)))
194  {
195  // Normalise accelerometer measurement
196  normalizeVector(ax, ay, az);
197 
198  // Normalise magnetometer measurement
199  normalizeVector(mx, my, mz);
200 
201  // Compensate for magnetic distortion
202  compensateMagneticDistortion(q0, q1, q2, q3, mx, my, mz, _2bxy, _2bz);
203 
204  // Gradient decent algorithm corrective step
205  s0 = 0.0; s1 = 0.0; s2 = 0.0; s3 = 0.0;
206  switch (world_frame_) {
207  case WorldFrame::NED:
208  // Gravity: [0, 0, -1]
209  addGradientDescentStep(q0, q1, q2, q3, 0.0, 0.0, -2.0, ax, ay, az, s0, s1, s2, s3);
210 
211  // Earth magnetic field: = [bxy, 0, bz]
212  addGradientDescentStep(q0,q1,q2,q3, _2bxy, 0.0, _2bz, mx, my, mz, s0, s1, s2, s3);
213  break;
214  case WorldFrame::NWU:
215  // Gravity: [0, 0, 1]
216  addGradientDescentStep(q0, q1, q2, q3, 0.0, 0.0, 2.0, ax, ay, az, s0, s1, s2, s3);
217 
218  // Earth magnetic field: = [bxy, 0, bz]
219  addGradientDescentStep(q0,q1,q2,q3, _2bxy, 0.0, _2bz, mx, my, mz, s0, s1, s2, s3);
220  break;
221  default:
222  case WorldFrame::ENU:
223  // Gravity: [0, 0, 1]
224  addGradientDescentStep(q0, q1, q2, q3, 0.0, 0.0, 2.0, ax, ay, az, s0, s1, s2, s3);
225 
226  // Earth magnetic field: = [0, bxy, bz]
227  addGradientDescentStep(q0, q1, q2, q3, 0.0, _2bxy, _2bz, mx, my, mz, s0, s1, s2, s3);
228  break;
229  }
230  normalizeQuaternion(s0, s1, s2, s3);
231 
232  // compute gyro drift bias
233  compensateGyroDrift(q0, q1, q2, q3, s0, s1, s2, s3, dt, zeta_, w_bx_, w_by_, w_bz_, gx, gy, gz);
234 
235  orientationChangeFromGyro(q0, q1, q2, q3, gx, gy, gz, qDot1, qDot2, qDot3, qDot4);
236 
237  // Apply feedback step
238  qDot1 -= gain_ * s0;
239  qDot2 -= gain_ * s1;
240  qDot3 -= gain_ * s2;
241  qDot4 -= gain_ * s3;
242  }
243  else
244  {
245  orientationChangeFromGyro(q0, q1, q2, q3, gx, gy, gz, qDot1, qDot2, qDot3, qDot4);
246  }
247 
248  // Integrate rate of change of quaternion to yield quaternion
249  q0 += qDot1 * dt;
250  q1 += qDot2 * dt;
251  q2 += qDot3 * dt;
252  q3 += qDot4 * dt;
253 
254  // Normalise quaternion
256 }
257 
259  float gx, float gy, float gz,
260  float ax, float ay, float az,
261  float dt)
262 {
263  float recipNorm;
264  float s0, s1, s2, s3;
265  float qDot1, qDot2, qDot3, qDot4;
266 
267  // Rate of change of quaternion from gyroscope
268  orientationChangeFromGyro (q0, q1, q2, q3, gx, gy, gz, qDot1, qDot2, qDot3, qDot4);
269 
270  // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
271  if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f)))
272  {
273  // Normalise accelerometer measurement
274  normalizeVector(ax, ay, az);
275 
276  // Gradient decent algorithm corrective step
277  s0 = 0.0; s1 = 0.0; s2 = 0.0; s3 = 0.0;
278  switch (world_frame_) {
279  case WorldFrame::NED:
280  // Gravity: [0, 0, -1]
281  addGradientDescentStep(q0, q1, q2, q3, 0.0, 0.0, -2.0, ax, ay, az, s0, s1, s2, s3);
282  break;
283  case WorldFrame::NWU:
284  // Gravity: [0, 0, 1]
285  addGradientDescentStep(q0, q1, q2, q3, 0.0, 0.0, 2.0, ax, ay, az, s0, s1, s2, s3);
286  break;
287  default:
288  case WorldFrame::ENU:
289  // Gravity: [0, 0, 1]
290  addGradientDescentStep(q0, q1, q2, q3, 0.0, 0.0, 2.0, ax, ay, az, s0, s1, s2, s3);
291  break;
292  }
293 
294  normalizeQuaternion(s0, s1, s2, s3);
295 
296  // Apply feedback step
297  qDot1 -= gain_ * s0;
298  qDot2 -= gain_ * s1;
299  qDot3 -= gain_ * s2;
300  qDot4 -= gain_ * s3;
301  }
302 
303  // Integrate rate of change of quaternion to yield quaternion
304  q0 += qDot1 * dt;
305  q1 += qDot2 * dt;
306  q2 += qDot3 * dt;
307  q3 += qDot4 * dt;
308 
309  // Normalise quaternion
311 }
static void compensateGyroDrift(float q0, float q1, float q2, float q3, float s0, float s1, float s2, float s3, float dt, float zeta, float &w_bx, float &w_by, float &w_bz, float &gx, float &gy, float &gz)
Definition: imu_filter.cpp:82
static float invSqrt(float x)
Definition: imu_filter.cpp:30
double q3
Definition: imu_filter.h:46
f
double q2
Definition: imu_filter.h:46
void madgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float dt)
Definition: imu_filter.cpp:175
static void normalizeVector(T &vx, T &vy, T &vz)
Definition: imu_filter.cpp:46
static void rotateAndScaleVector(float q0, float q1, float q2, float q3, float _2dx, float _2dy, float _2dz, float &rx, float &ry, float &rz)
Definition: imu_filter.cpp:64
double q0
Definition: imu_filter.h:46
void madgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, float dt)
Definition: imu_filter.cpp:258
static void orientationChangeFromGyro(float q0, float q1, float q2, float q3, float gx, float gy, float gz, float &qDot1, float &qDot2, float &qDot3, float &qDot4)
Definition: imu_filter.cpp:103
virtual ~ImuFilter()
Definition: imu_filter.cpp:171
float w_bx_
Definition: imu_filter.h:47
static void compensateMagneticDistortion(float q0, float q1, float q2, float q3, float mx, float my, float mz, float &_2bxy, float &_2bz)
Definition: imu_filter.cpp:149
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
float w_by_
Definition: imu_filter.h:47
double gain_
Definition: imu_filter.h:41
static void normalizeQuaternion(T &q0, T &q1, T &q2, T &q3)
Definition: imu_filter.cpp:55
double q1
Definition: imu_filter.h:46
float w_bz_
Definition: imu_filter.h:47
static void addGradientDescentStep(float q0, float q1, float q2, float q3, float _2dx, float _2dy, float _2dz, float mx, float my, float mz, float &s0, float &s1, float &s2, float &s3)
Definition: imu_filter.cpp:116
double zeta_
Definition: imu_filter.h:42
WorldFrame::WorldFrame world_frame_
Definition: imu_filter.h:43


imu_filter_madgwick
Author(s): Ivan Dryanovski
autogenerated on Tue May 7 2019 03:16:55