4 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 #ifdef _ORIENTATION_FILTER_H_
23 #include "orientation_filter.h"
25 //Windowing is used for buffering of previous samples for statistical analysis
26 #define MOVING_AVERAGE_WINDOW_LENGTH 20
28 #define GRAVITY 9.80665
30 //Needed for non-zero initialization for statistical analysis
31 #define NON_ZERO_VAL 0.1
32 //microseconds to seconds
33 #define US2S (1.0 / 1000000.0)
34 //Initialize sampling interval to 100000microseconds
35 #define SAMPLE_INTV 100000
37 // constants for computation of covariance and transition matrices
38 #define ZIGMA_W (0.05 * DEG2RAD)
40 #define QWB_CONST ((2 * (ZIGMA_W * ZIGMA_W)) / TAU_W)
41 #define F_CONST (-1 / TAU_W)
43 #define NEGLIGIBLE_VAL 0.0000001
45 #define ABS(val) (((val) < 0) ? -(val) : (val))
48 template <typename TYPE>
49 orientation_filter<TYPE>::orientation_filter()
51 TYPE arr[MOVING_AVERAGE_WINDOW_LENGTH];
53 std::fill_n(arr, MOVING_AVERAGE_WINDOW_LENGTH, NON_ZERO_VAL);
55 vect<TYPE, MOVING_AVERAGE_WINDOW_LENGTH> vec(arr);
64 m_pitch_phase_compensation = 1;
65 m_roll_phase_compensation = 1;
66 m_azimuth_phase_compensation = 1;
67 m_magnetic_alignment_factor = 1;
69 m_gyro.m_time_stamp = 0;
72 template <typename TYPE>
73 orientation_filter<TYPE>::~orientation_filter()
77 template <typename TYPE>
78 inline void orientation_filter<TYPE>::init_accel_gyro_mag_data(const sensor_data<TYPE> accel,
79 const sensor_data<TYPE> gyro, const sensor_data<TYPE> magnetic)
81 unsigned long long sample_interval_gyro = SAMPLE_INTV;
83 m_accel.m_data = accel.m_data;
84 m_magnetic.m_data = magnetic.m_data;
86 if (m_gyro.m_time_stamp != 0 && gyro.m_time_stamp != 0)
87 sample_interval_gyro = gyro.m_time_stamp - m_gyro.m_time_stamp;
89 m_gyro_dt = sample_interval_gyro * US2S;
91 m_accel.m_time_stamp = accel.m_time_stamp;
92 m_gyro.m_time_stamp = gyro.m_time_stamp;
93 m_magnetic.m_time_stamp = magnetic.m_time_stamp;
95 m_gyro.m_data = gyro.m_data - m_bias_correction;
98 template <typename TYPE>
99 inline void orientation_filter<TYPE>::init_accel_mag_data(const sensor_data<TYPE> accel,
100 const sensor_data<TYPE> magnetic)
102 m_accel.m_data = accel.m_data;
103 m_magnetic.m_data = magnetic.m_data;
105 m_accel.m_time_stamp = accel.m_time_stamp;
106 m_magnetic.m_time_stamp = magnetic.m_time_stamp;
109 template <typename TYPE>
110 inline void orientation_filter<TYPE>::init_accel_gyro_data(const sensor_data<TYPE> accel,
111 const sensor_data<TYPE> gyro)
113 unsigned long long sample_interval_gyro = SAMPLE_INTV;
115 m_accel.m_data = accel.m_data;
117 if (m_gyro.m_time_stamp != 0 && gyro.m_time_stamp != 0)
118 sample_interval_gyro = gyro.m_time_stamp - m_gyro.m_time_stamp;
120 m_gyro_dt = sample_interval_gyro * US2S;
122 m_accel.m_time_stamp = accel.m_time_stamp;
123 m_gyro.m_time_stamp = gyro.m_time_stamp;
125 m_gyro.m_data = gyro.m_data - m_bias_correction;
128 template <typename TYPE>
129 inline void orientation_filter<TYPE>::orientation_triad_algorithm()
131 TYPE arr_acc_e[V1x3S] = {0.0, 0.0, 1.0};
132 TYPE arr_mag_e[V1x3S] = {0.0, (TYPE) m_magnetic_alignment_factor, 0.0};
134 vect<TYPE, V1x3S> acc_e(arr_acc_e);
135 vect<TYPE, V1x3S> mag_e(arr_mag_e);
137 vect<TYPE, SENSOR_DATA_SIZE> acc_b_x_mag_b = cross(m_accel.m_data, m_magnetic.m_data);
138 vect<TYPE, V1x3S> acc_e_x_mag_e = cross(acc_e, mag_e);
140 vect<TYPE, SENSOR_DATA_SIZE> cross1 = cross(acc_b_x_mag_b, m_accel.m_data);
141 vect<TYPE, V1x3S> cross2 = cross(acc_e_x_mag_e, acc_e);
143 matrix<TYPE, M3X3R, M3X3C> mat_b;
144 matrix<TYPE, M3X3R, M3X3C> mat_e;
146 for(int i = 0; i < M3X3R; i++)
148 mat_b.m_mat[i][0] = m_accel.m_data.m_vec[i];
149 mat_b.m_mat[i][1] = acc_b_x_mag_b.m_vec[i];
150 mat_b.m_mat[i][2] = cross1.m_vec[i];
151 mat_e.m_mat[i][0] = acc_e.m_vec[i];
152 mat_e.m_mat[i][1] = acc_e_x_mag_e.m_vec[i];
153 mat_e.m_mat[i][2] = cross2.m_vec[i];
156 matrix<TYPE, M3X3R, M3X3C> mat_b_t = tran(mat_b);
157 rotation_matrix<TYPE> rot_mat(mat_e * mat_b_t);
159 m_quat_aid = rot_mat2quat(rot_mat);
162 template <typename TYPE>
163 inline void orientation_filter<TYPE>::compute_accel_orientation()
165 TYPE arr_acc_e[V1x3S] = {0.0, 0.0, 1.0};
167 vect<TYPE, V1x3S> acc_e(arr_acc_e);
169 m_quat_aid = sensor_data2quat(m_accel, acc_e);
172 template <typename TYPE>
173 inline void orientation_filter<TYPE>::compute_covariance()
175 TYPE var_gyr_x, var_gyr_y, var_gyr_z;
176 TYPE var_roll, var_pitch, var_azimuth;
178 insert_end(m_var_gyr_x, m_gyro.m_data.m_vec[0]);
179 insert_end(m_var_gyr_y, m_gyro.m_data.m_vec[1]);
180 insert_end(m_var_gyr_z, m_gyro.m_data.m_vec[2]);
181 insert_end(m_var_roll, m_orientation.m_ang.m_vec[0]);
182 insert_end(m_var_pitch, m_orientation.m_ang.m_vec[1]);
183 insert_end(m_var_azimuth, m_orientation.m_ang.m_vec[2]);
185 var_gyr_x = var(m_var_gyr_x);
186 var_gyr_y = var(m_var_gyr_y);
187 var_gyr_z = var(m_var_gyr_z);
188 var_roll = var(m_var_roll);
189 var_pitch = var(m_var_pitch);
190 var_azimuth = var(m_var_azimuth);
192 m_driv_cov.m_mat[0][0] = var_gyr_x;
193 m_driv_cov.m_mat[1][1] = var_gyr_y;
194 m_driv_cov.m_mat[2][2] = var_gyr_z;
195 m_driv_cov.m_mat[3][3] = (TYPE) QWB_CONST;
196 m_driv_cov.m_mat[4][4] = (TYPE) QWB_CONST;
197 m_driv_cov.m_mat[5][5] = (TYPE) QWB_CONST;
199 m_aid_cov.m_mat[0][0] = var_roll;
200 m_aid_cov.m_mat[1][1] = var_pitch;
201 m_aid_cov.m_mat[2][2] = var_azimuth;
204 template <typename TYPE>
205 inline void orientation_filter<TYPE>::time_update()
207 quaternion<TYPE> quat_diff, quat_error, quat_output;
208 euler_angles<TYPE> euler_error;
209 euler_angles<TYPE> orientation;
211 m_tran_mat.m_mat[0][1] = m_gyro.m_data.m_vec[2];
212 m_tran_mat.m_mat[0][2] = -m_gyro.m_data.m_vec[1];
213 m_tran_mat.m_mat[1][0] = -m_gyro.m_data.m_vec[2];
214 m_tran_mat.m_mat[1][2] = m_gyro.m_data.m_vec[0];
215 m_tran_mat.m_mat[2][0] = m_gyro.m_data.m_vec[1];
216 m_tran_mat.m_mat[2][1] = -m_gyro.m_data.m_vec[0];
217 m_tran_mat.m_mat[3][3] = (TYPE) F_CONST;
218 m_tran_mat.m_mat[4][4] = (TYPE) F_CONST;
219 m_tran_mat.m_mat[5][5] = (TYPE) F_CONST;
221 m_measure_mat.m_mat[0][0] = 1;
222 m_measure_mat.m_mat[1][1] = 1;
223 m_measure_mat.m_mat[2][2] = 1;
225 if (is_initialized(m_state_old))
226 m_state_new = transpose(m_tran_mat * transpose(m_state_old));
228 m_pred_cov = (m_tran_mat * m_pred_cov * tran(m_tran_mat)) + m_driv_cov;
230 for (int j=0; j<M6X6C; ++j) {
231 for (int i=0; i<M6X6R; ++i) {
232 if (ABS(m_pred_cov.m_mat[i][j]) < NEGLIGIBLE_VAL)
233 m_pred_cov.m_mat[i][j] = NEGLIGIBLE_VAL;
235 if (ABS(m_state_new.m_vec[j]) < NEGLIGIBLE_VAL)
236 m_state_new.m_vec[j] = NEGLIGIBLE_VAL;
239 if(!is_initialized(m_quat_driv.m_quat))
240 m_quat_driv = m_quat_aid;
242 quaternion<TYPE> quat_rot_inc(0, m_gyro.m_data.m_vec[0], m_gyro.m_data.m_vec[1],
243 m_gyro.m_data.m_vec[2]);
245 quat_diff = (m_quat_driv * quat_rot_inc) * (TYPE) 0.5;
247 m_quat_driv = m_quat_driv + (quat_diff * (TYPE) m_gyro_dt * (TYPE) PI);
248 m_quat_driv.quat_normalize();
250 quat_output = phase_correction(m_quat_driv, m_quat_aid);
252 m_quat_9axis = quat_output;
254 orientation = quat2euler(quat_output);
256 m_orientation.m_ang.m_vec[0] = orientation.m_ang.m_vec[0] * m_pitch_phase_compensation;
257 m_orientation.m_ang.m_vec[1] = orientation.m_ang.m_vec[1] * m_roll_phase_compensation;
258 m_orientation.m_ang.m_vec[2] = orientation.m_ang.m_vec[2] * m_azimuth_phase_compensation;
260 m_rot_matrix = quat2rot_mat(m_quat_driv);
262 quat_error = m_quat_aid * m_quat_driv;
264 euler_error = (quat2euler(quat_error)).m_ang / (TYPE) PI;
266 quaternion<TYPE> quat_eu_er(1, euler_error.m_ang.m_vec[0], euler_error.m_ang.m_vec[1],
267 euler_error.m_ang.m_vec[2]);
269 m_quat_driv = (m_quat_driv * quat_eu_er) * (TYPE) PI;
270 m_quat_driv.quat_normalize();
272 if (is_initialized(m_state_new))
274 m_state_error.m_vec[0] = euler_error.m_ang.m_vec[0];
275 m_state_error.m_vec[1] = euler_error.m_ang.m_vec[1];
276 m_state_error.m_vec[2] = euler_error.m_ang.m_vec[2];
277 m_state_error.m_vec[3] = m_state_new.m_vec[3];
278 m_state_error.m_vec[4] = m_state_new.m_vec[4];
279 m_state_error.m_vec[5] = m_state_new.m_vec[5];
283 template <typename TYPE>
284 inline void orientation_filter<TYPE>::time_update_gaming_rv()
286 quaternion<TYPE> quat_diff, quat_error, quat_output;
287 euler_angles<TYPE> euler_error;
288 euler_angles<TYPE> orientation;
289 euler_angles<TYPE> euler_aid;
290 euler_angles<TYPE> euler_driv;
292 m_tran_mat.m_mat[0][1] = m_gyro.m_data.m_vec[2];
293 m_tran_mat.m_mat[0][2] = -m_gyro.m_data.m_vec[1];
294 m_tran_mat.m_mat[1][0] = -m_gyro.m_data.m_vec[2];
295 m_tran_mat.m_mat[1][2] = m_gyro.m_data.m_vec[0];
296 m_tran_mat.m_mat[2][0] = m_gyro.m_data.m_vec[1];
297 m_tran_mat.m_mat[2][1] = -m_gyro.m_data.m_vec[0];
298 m_tran_mat.m_mat[3][3] = (TYPE) F_CONST;
299 m_tran_mat.m_mat[4][4] = (TYPE) F_CONST;
300 m_tran_mat.m_mat[5][5] = (TYPE) F_CONST;
302 m_measure_mat.m_mat[0][0] = 1;
303 m_measure_mat.m_mat[1][1] = 1;
304 m_measure_mat.m_mat[2][2] = 1;
306 if (is_initialized(m_state_old))
307 m_state_new = transpose(mul(m_tran_mat, transpose(m_state_old)));
309 m_pred_cov = (m_tran_mat * m_pred_cov * tran(m_tran_mat)) + m_driv_cov;
311 if(!is_initialized(m_quat_driv.m_quat))
312 m_quat_driv = m_quat_aid;
314 quaternion<TYPE> quat_rot_inc(0, m_gyro.m_data.m_vec[0], m_gyro.m_data.m_vec[1],
315 m_gyro.m_data.m_vec[2]);
317 quat_diff = (m_quat_driv * quat_rot_inc) * (TYPE) 0.5;
319 m_quat_driv = m_quat_driv + (quat_diff * (TYPE) m_gyro_dt * (TYPE) PI);
320 m_quat_driv.quat_normalize();
322 quat_output = phase_correction(m_quat_driv, m_quat_aid);
324 quat_error = m_quat_aid * m_quat_driv;
326 euler_error = (quat2euler(quat_error)).m_ang / (TYPE) PI;
328 euler_aid = quat2euler(m_quat_aid);
329 euler_driv = quat2euler(quat_output);
331 euler_angles<TYPE> euler_gaming_rv(euler_aid.m_ang.m_vec[0], euler_aid.m_ang.m_vec[1],
332 euler_driv.m_ang.m_vec[2]);
334 m_quat_gaming_rv = euler2quat(euler_gaming_rv);
336 if (is_initialized(m_state_new))
338 m_state_error.m_vec[0] = euler_error.m_ang.m_vec[0];
339 m_state_error.m_vec[1] = euler_error.m_ang.m_vec[1];
340 m_state_error.m_vec[2] = euler_error.m_ang.m_vec[2];
341 m_state_error.m_vec[3] = m_state_new.m_vec[3];
342 m_state_error.m_vec[4] = m_state_new.m_vec[4];
343 m_state_error.m_vec[5] = m_state_new.m_vec[5];
347 template <typename TYPE>
348 inline void orientation_filter<TYPE>::measurement_update()
350 matrix<TYPE, M6X6R, M6X6C> gain;
351 matrix<TYPE, M6X6R, M6X6C> iden;
352 iden.m_mat[0][0] = iden.m_mat[1][1] = iden.m_mat[2][2] = 1;
353 iden.m_mat[3][3] = iden.m_mat[4][4] = iden.m_mat[5][5] = 1;
355 for (int j=0; j<M6X6C; ++j) {
356 for (int i=0; i<M6X6R; ++i) {
357 gain.m_mat[i][j] = m_pred_cov.m_mat[j][i] / (m_pred_cov.m_mat[j][j] + m_aid_cov.m_mat[j][j]);
358 m_state_new.m_vec[i] = m_state_new.m_vec[i] + gain.m_mat[i][j] * m_state_error.m_vec[j];
361 matrix<TYPE, M6X6R, M6X6C> temp = iden;
363 for (int i=0; i<M6X6R; ++i)
364 temp.m_mat[i][j] = iden.m_mat[i][j] - (gain.m_mat[i][j] * m_measure_mat.m_mat[j][i]);
366 m_pred_cov = temp * m_pred_cov;
369 for (int j=0; j<M6X6C; ++j) {
370 for (int i=0; i<M6X6R; ++i) {
371 if (ABS(m_pred_cov.m_mat[i][j]) < NEGLIGIBLE_VAL)
372 m_pred_cov.m_mat[i][j] = NEGLIGIBLE_VAL;
376 m_state_old = m_state_new;
378 TYPE arr_bias[V1x3S] = {m_state_new.m_vec[3], m_state_new.m_vec[4], m_state_new.m_vec[5]};
379 vect<TYPE, V1x3S> vec(arr_bias);
381 m_bias_correction = vec;
384 template <typename TYPE>
385 euler_angles<TYPE> orientation_filter<TYPE>::get_orientation(const sensor_data<TYPE> accel,
386 const sensor_data<TYPE> gyro, const sensor_data<TYPE> magnetic)
388 euler_angles<TYPE> cor_euler_ang;
390 init_accel_gyro_mag_data(accel, gyro, magnetic);
393 m_gyro.m_data = m_gyro.m_data * (TYPE) PI;
394 normalize(m_magnetic);
396 orientation_triad_algorithm();
398 compute_covariance();
402 measurement_update();
404 return m_orientation;
407 template <typename TYPE>
408 rotation_matrix<TYPE> orientation_filter<TYPE>::get_rotation_matrix(const sensor_data<TYPE> accel,
409 const sensor_data<TYPE> gyro, const sensor_data<TYPE> magnetic)
411 get_orientation(accel, gyro, magnetic);
416 template <typename TYPE>
417 quaternion<TYPE> orientation_filter<TYPE>::get_9axis_quaternion(const sensor_data<TYPE> accel,
418 const sensor_data<TYPE> gyro, const sensor_data<TYPE> magnetic)
421 get_orientation(accel, gyro, magnetic);
426 template <typename TYPE>
427 quaternion<TYPE> orientation_filter<TYPE>::get_geomagnetic_quaternion(const sensor_data<TYPE> accel,
428 const sensor_data<TYPE> magnetic)
430 init_accel_mag_data(accel, magnetic);
433 normalize(m_magnetic);
435 orientation_triad_algorithm();
440 template <typename TYPE>
441 quaternion<TYPE> orientation_filter<TYPE>::get_gaming_quaternion(const sensor_data<TYPE> accel,
442 const sensor_data<TYPE> gyro)
444 euler_angles<TYPE> cor_euler_ang;
446 init_accel_gyro_data(accel, gyro);
449 m_gyro.m_data = m_gyro.m_data * (TYPE) PI;
451 compute_accel_orientation();
453 compute_covariance();
455 time_update_gaming_rv();
457 measurement_update();
459 return m_quat_gaming_rv;
461 #endif //_ORIENTATION_FILTER_H_