3 % Copyright (c) 2015 Samsung Electronics Co., Ltd.
5 % Licensed under the Apache License, Version 2.0 (the "License");
6 % you may not use this file except in compliance with the License.
7 % You may obtain a copy of the License at
9 % http://www.apache.org/licenses/LICENSE-2.0
11 % Unless required by applicable law or agreed to in writing, software
12 % distributed under the License is distributed on an "AS IS" BASIS,
13 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 % See the License for the specific language governing permissions and
15 % limitations under the License.
17 % Orientation Estimation function
19 % - Orientation and rotation vector Estimation using Gyroscope as the driving system and
20 % Accelerometer+Geo-Magnetic Sensors as Aiding System.
21 % - Gaming rotation vector Estimation using Accelerometer and Gyroscope sensors.
22 % - Geomagnetic rotation vector Estimation using Accelerometer and Geomagnetic sensors.
23 % - Quaternion based approach
24 % - Estimation and correction of orientation errors and bias errors for gyroscope using Kalman filter
26 function [quat_driv, quat_aid, quat_error, gyro_bias] = estimate_orientation(Accel_data, Gyro_data, Mag_data)
27 PLOT_SCALED_SENSOR_COMPARISON_DATA = 1;
28 PLOT_INDIVIDUAL_SENSOR_INPUT_DATA = 1;
30 MAGNETIC_ALIGNMENT_FACTOR = -1;
31 GYRO_DATA_DISABLED = 0;
32 MAG_DATA_DISABLED = 0;
34 if Gyro_data(4,1) == 0
35 GYRO_DATA_DISABLED = 1;
39 MAG_DATA_DISABLED = 1;
44 ACCEL_THRESHOLD = 0.2;
45 GYRO_THRESHOLD = 0.01;
49 MOVING_AVERAGE_WINDOW_LENGTH = 20;
51 DEG2RAD = 0.0174532925;
52 US2S = 1.0 / 1000000.0;
54 ZigmaW = 0.05 * DEG2RAD;
57 BUFFER_SIZE = size(Accel_data,2);
61 ATime = Accel_data(4,:);
63 mag_x = zeros(1,BUFFER_SIZE);
64 mag_y = zeros(1,BUFFER_SIZE);
65 mag_z = zeros(1,BUFFER_SIZE);
66 MTime = zeros(1,BUFFER_SIZE);
68 gyro_bias = zeros(3,BUFFER_SIZE);
70 if MAG_DATA_DISABLED != 1
74 MTime = Mag_data(4,:);
77 acc_x = zeros(1,BUFFER_SIZE);
78 acc_y = zeros(1,BUFFER_SIZE);
79 acc_z = zeros(1,BUFFER_SIZE);
81 quat_aid = zeros(BUFFER_SIZE,4);
82 quat_driv = zeros(BUFFER_SIZE,4);
83 quat_gaming_rv = zeros(BUFFER_SIZE,4);
84 quat_error = zeros(BUFFER_SIZE,4);
86 acc_e = [0.0;0.0;1.0]; % gravity vector in earth frame
87 mag_e = [0.0;MAGNETIC_ALIGNMENT_FACTOR;0.0]; % magnetic field vector in earth frame
89 if GYRO_DATA_DISABLED != 1
90 % Gyroscope Bias Variables
91 Bx = 0; By = 0; Bz = 0;
96 GTime = Gyro_data(4,:);
98 gyr_x = zeros(1,BUFFER_SIZE);
99 gyr_y = zeros(1,BUFFER_SIZE);
100 gyr_z = zeros(1,BUFFER_SIZE);
102 % User Acceleration mean and Variance
103 A_T = zeros(1,BUFFER_SIZE);
104 G_T = zeros(1,BUFFER_SIZE);
105 M_T = zeros(1,BUFFER_SIZE);
106 var_roll = zeros(1,BUFFER_SIZE);
107 var_pitch = zeros(1,BUFFER_SIZE);
108 var_yaw = zeros(1,BUFFER_SIZE);
109 var_Gx = zeros(1,BUFFER_SIZE);
110 var_Gy = zeros(1,BUFFER_SIZE);
111 var_Gz = zeros(1,BUFFER_SIZE);
113 roll = zeros(1,BUFFER_SIZE);
114 pitch = zeros(1,BUFFER_SIZE);
115 yaw = zeros(1,BUFFER_SIZE);
117 % system covariance matrix
120 % measurement covariance matrix
127 H = [eye(3) zeros(3,3); zeros(3,6)];
128 x = zeros(6,BUFFER_SIZE);
130 P = 1 * eye(6);% state covariance matrix
132 quat_driv(1,:) = [1 0 0 0];
137 % first order filtering
138 for i = 1:BUFFER_SIZE
139 % normalize accelerometer measurements
140 norm_acc = 1/sqrt(Ax(i)^2 + Ay(i)^2 + Az(i)^2);
141 acc_x(i) = norm_acc * Ax(i);
142 acc_y(i) = norm_acc * Ay(i);
143 acc_z(i) = norm_acc * Az(i);
145 % gravity vector in body frame
146 acc_b =[acc_x(i);acc_y(i);acc_z(i)];
148 if MAG_DATA_DISABLED != 1
149 % normalize magnetometer measurements
150 norm_mag = 1/sqrt(Mx(i)^2 + My(i)^2 + Mz(i)^2);
151 mag_x(i) = norm_mag * Mx(i);
152 mag_y(i) = norm_mag * My(i);
153 mag_z(i) = norm_mag * Mz(i);
155 % Aiding System (Accelerometer + Geomagnetic) quaternion generation
156 % magnetic field vector in body frame
157 mag_b =[mag_x(i);mag_y(i);mag_z(i)];
159 % compute measurement quaternion with TRIAD algorithm
160 acc_b_x_mag_b = cross(acc_b,mag_b);
161 acc_e_x_mag_e = cross(acc_e,mag_e);
162 M_b = [acc_b acc_b_x_mag_b cross(acc_b_x_mag_b,acc_b)];
163 M_e = [acc_e acc_e_x_mag_e cross(acc_e_x_mag_e,acc_e)];
165 quat_aid(i,:) = rot_mat2quat(Rot_m);
167 axis = cross(acc_b, acc_e);
168 angle = acos(dot(acc_b, acc_e));
169 quat_aid(i,:) = axis_rot2quat(axis, angle);
172 if GYRO_DATA_DISABLED != 1
173 gyr_x(i) = Gx(i) * PI;
174 gyr_y(i) = Gy(i) * PI;
175 gyr_z(i) = Gz(i) * PI;
177 euler = quat2euler(quat_aid(i,:));
183 A_T(i) = ATime(i) - ATime(i-1);
184 G_T(i) = GTime(i) - GTime(i-1);
185 M_T(i) = MTime(i) - MTime(i-1);
190 % Driving System (Gyroscope) quaternion generation
191 % convert scaled gyro data to rad/s
192 qDot = 0.5 * quat_prod(q, [0 gyr_x(i) gyr_y(i) gyr_z(i)]);
194 % Integrate to yield quaternion
195 q = q + qDot * dt * PI;
197 % normalise quaternion
198 quat_driv(i,:) = q / norm(q);
201 quat_error(i,:) = quat_prod(quat_aid(i,:), quat_driv(i,:));
203 euler_e = quat2euler(quat_error(i,:));
205 gyr_x(i) = gyr_x(i) - (Bx + euler_e(1));
206 gyr_y(i) = gyr_y(i) - (By + euler_e(2));
207 gyr_z(i) = gyr_z(i) - (Bz + euler_e(3));
209 if i <= MOVING_AVERAGE_WINDOW_LENGTH
210 var_Gx(i) = NON_ZERO_VAL;
211 var_Gy(i) = NON_ZERO_VAL;
212 var_Gz(i) = NON_ZERO_VAL;
213 var_roll(i) = NON_ZERO_VAL;
214 var_pitch(i) = NON_ZERO_VAL;
215 var_yaw(i) = NON_ZERO_VAL;
217 var_Gx(i) = var(gyr_x((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
218 var_Gy(i) = var(gyr_y((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
219 var_Gz(i) = var(gyr_z((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
220 var_roll(i) = var(roll((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
221 var_pitch(i) = var(pitch((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
222 var_yaw(i) = var(yaw((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
225 Qwn = [var_Gx(i) 0 0;0 var_Gy(i) 0;0 0 var_Gz(i);];
226 Qwb = (2 * (ZigmaW^2) / TauW) * eye(3);
228 % Process Noise Covariance
229 Q = [Qwn zeros(3,3);zeros(3,3) Qwb];
231 % Measurement Noise Covariance
232 R = [[var_roll(i) 0 0;0 var_pitch(i) 0;0 0 var_yaw(i)] zeros(3,3); zeros(3,3) zeros(3,3)];
234 % initialization for q
239 F = [[0 gyr_z(i) -gyr_y(i);-gyr_z(i) 0 gyr_x(i);gyr_y(i) -gyr_x(i) 0] zeros(3,3); zeros(3,3) (-(1/TauW) * eye(3))];
243 x(:,i) = F * x(:,i-1);
246 % compute covariance of prediction
247 P = (F * P * F') + Q;
253 if MAG_DATA_DISABLED != 1
254 q = quat_prod(quat_driv(i,:), [1 x1 x2 x3]) * PI;
257 euler_aid = quat2euler(quat_aid(i,:));
258 euler_driv = quat2euler(quat_driv(i,:));
259 if (acc_y(i)^2 < (ACCEL_THRESHOLD) && Gx(i)^2 < (GYRO_THRESHOLD))
260 if (acc_x(i)^2 < (ACCEL_THRESHOLD) && Gy(i)^2 < (GYRO_THRESHOLD))
261 if (Gz(i)^2 < (GYRO_THRESHOLD))
262 q = euler2quat([euler_aid(2) euler_aid(1) euler_driv(3)]);
269 e = [x1 x2 x3 x(4,i) x(5,i) x(6,i)];
273 % compute Kalman gain
274 K(:,j) = P(j ,:)./(P(j,j)+R(j,j));
275 % update state vector
276 x(:,i) = x(:,i) + K(:,j) * e(j);
277 % update covariance matrix
278 P = (eye(6) - (K(:,j) * H(j,:))) * P;
284 gyro_bias(:,i) = [x1; x2; x3] * PI_DEG + [Bx; By; Bz];
288 if GYRO_DATA_DISABLED == 1
289 quat_driv = quat_aid;
292 if PLOT_SCALED_SENSOR_COMPARISON_DATA == 1
293 % Accelerometer/Gyroscope scaled Plot results
295 scrsz = get(0,'ScreenSize');
296 set(hfig,'position',scrsz);
298 p1 = plot(1:BUFFER_SIZE,acc_x(1,1:BUFFER_SIZE),'r');
301 if GYRO_DATA_DISABLED != 1
302 p2 = plot(1:BUFFER_SIZE,Gx(1,1:BUFFER_SIZE),'b');
303 if MAG_DATA_DISABLED != 1
306 p3 = plot(1:BUFFER_SIZE,mag_x(1,1:BUFFER_SIZE),'k');
307 title(['Accelerometer/Gyroscope/Magnetometer X-Axis Plot']);
308 legend([p1 p2 p3],'Acc_X', 'Gyr_X', 'Mag_X');
310 title(['Accelerometer/Gyroscope X-Axis Plot']);
311 legend([p1 p2],'Acc_X', 'Gyr_X');
314 p2 = plot(1:BUFFER_SIZE,mag_x(1,1:BUFFER_SIZE),'k');
315 title(['Accelerometer/Magnetometer X-Axis Plot']);
316 legend([p1 p2],'Acc_X', 'Mag_X');
319 p1 = plot(1:BUFFER_SIZE,acc_y(1,1:BUFFER_SIZE),'r');
322 if GYRO_DATA_DISABLED != 1
323 p2 = plot(1:BUFFER_SIZE,Gy(1,1:BUFFER_SIZE),'b');
324 if MAG_DATA_DISABLED != 1
327 p3 = plot(1:BUFFER_SIZE,mag_y(1,1:BUFFER_SIZE),'k');
328 title(['Accelerometer/Gyroscope/Magnetometer Y-Axis Plot']);
329 legend([p1 p2 p3],'Acc_Y', 'Gyr_Y', 'Mag_Y');
331 title(['Accelerometer/Gyroscope Y-Axis Plot']);
332 legend([p1 p2],'Acc_Y', 'Gyr_Y');
335 p2 = plot(1:BUFFER_SIZE,mag_y(1,1:BUFFER_SIZE),'k');
336 title(['Accelerometer/Magnetometer Y-Axis Plot']);
337 legend([p1 p2],'Acc_X', 'Mag_Y');
340 p1 = plot(1:BUFFER_SIZE,acc_z(1,1:BUFFER_SIZE),'r');
343 if GYRO_DATA_DISABLED != 1
344 p2 = plot(1:BUFFER_SIZE,Gz(1,1:BUFFER_SIZE),'b');
345 if MAG_DATA_DISABLED != 1
348 p3 = plot(1:BUFFER_SIZE,mag_z(1,1:BUFFER_SIZE),'k');
349 title(['Accelerometer/Gyroscope/Magnetometer Z-Axis Plot']);
350 legend([p1 p2 p3],'Acc_Z', 'Gyr_Z', 'Mag_Z');
352 title(['Accelerometer/Gyroscope Z-Axis Plot']);
353 legend([p1 p2],'Acc_Z', 'Gyr_Z');
356 p2 = plot(1:BUFFER_SIZE,mag_z(1,1:BUFFER_SIZE),'k');
357 title(['Accelerometer/Magnetometer Z-Axis Plot']);
358 legend([p1 p2],'Acc_Z', 'Mag_Z');
362 if PLOT_INDIVIDUAL_SENSOR_INPUT_DATA == 1
363 % Accelerometer Raw (vs) filtered output
365 scrsz = get(0,'ScreenSize');
366 set(hfig,'position',scrsz);
368 p1 = plot(1:BUFFER_SIZE,Ax(1,1:BUFFER_SIZE),'r');
371 title(['Accelerometer X-Axis Plot']);
373 p1 = plot(1:BUFFER_SIZE,Ay(1,1:BUFFER_SIZE),'r');
376 title(['Accelerometer Y-Axis Plot']);
378 p1 = plot(1:BUFFER_SIZE,Az(1,1:BUFFER_SIZE),'r');
381 title(['Accelerometer Z-Axis Plot']);
383 if GYRO_DATA_DISABLED != 1
384 % Gyroscope Raw (vs) filtered output
386 scrsz = get(0,'ScreenSize');
387 set(hfig,'position',scrsz);
389 p1 = plot(1:BUFFER_SIZE,Gx(1,1:BUFFER_SIZE),'r');
392 title(['Gyroscope X-Axis Plot']);
394 p1 = plot(1:BUFFER_SIZE,Gy(1,1:BUFFER_SIZE),'r');
397 title(['Gyroscope Y-Axis Plot']);
399 p1 = plot(1:BUFFER_SIZE,Gz(1,1:BUFFER_SIZE),'r');
402 title(['Gyroscope Z-Axis Plot']);
405 if MAG_DATA_DISABLED != 1
406 % Magnetometer Raw (vs) filtered output
408 scrsz = get(0,'ScreenSize');
409 set(hfig,'position',scrsz);
411 p1 = plot(1:BUFFER_SIZE,Mx(1,1:BUFFER_SIZE),'r');
414 title(['Magnetometer X-Axis Plot']);
416 p1 = plot(1:BUFFER_SIZE,My(1,1:BUFFER_SIZE),'r');
419 title(['Magnetometer Y-Axis Plot']);
421 p1 = plot(1:BUFFER_SIZE,Mz(1,1:BUFFER_SIZE),'r');
424 title(['Magnetometer Z-Axis Plot']);