3 % Copyright (c) 2014 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 Estimation using Gyroscope as the driving system and Accelerometer+Geo-Magnetic Sensors as Aiding System.
20 % - Quaternion based approach
21 % - Estimation and correction of Euler errors and bias errors for gyroscope using Kalman filter
23 function [OR_driv, OR_aid, OR_err] = estimate_orientation(Accel_data, Gyro_data, Mag_data)
25 LOW_PASS_FILTERING_ON = 0;
26 PLOT_SCALED_SENSOR_COMPARISON_DATA = 0;
27 PLOT_INDIVIDUAL_SENSOR_INPUT_DATA = 0;
28 MAGNETIC_ALIGNMENT_FACTOR = -1;
29 PITCH_PHASE_CORRECTION = -1;
30 ROLL_PHASE_CORRECTION = -1;
31 YAW_PHASE_CORRECTION = -1;
37 MOVING_AVERAGE_WINDOW_LENGTH = 20;
39 DEG2RAD = 0.0174532925;
40 US2S = 1.0 / 1000000.0;
43 % Systron-donner "Horizon"
44 ZigmaW = 0.05 * DEG2RAD; %deg/s
47 %ZigmaW = 0.05 * DEG2RAD; %deg/s
49 %FOGs (KVH Autogyro and Crossbow DMU-FOG)
52 BUFFER_SIZE = size(Accel_data,2);
56 ATime = Accel_data(4,:);
61 GTime = Gyro_data(4,:);
66 MTime = Mag_data(4,:);
68 % Gyroscope Bias Variables
69 Bx = 0; By = 0; Bz = 0;
71 % 1st order smoothening filter
73 a = [1.0000000 0.02 ];
75 % initialize filter output variables to zero
76 filt_Ax = zeros(1,BUFFER_SIZE);
77 filt_Ay = zeros(1,BUFFER_SIZE);
78 filt_Az = zeros(1,BUFFER_SIZE);
79 filt_Gx = zeros(1,BUFFER_SIZE);
80 filt_Gy = zeros(1,BUFFER_SIZE);
81 filt_Gz = zeros(1,BUFFER_SIZE);
82 filt_Mx = zeros(1,BUFFER_SIZE);
83 filt_My = zeros(1,BUFFER_SIZE);
84 filt_Mz = zeros(1,BUFFER_SIZE);
86 acc_x = zeros(1,BUFFER_SIZE);
87 acc_y = zeros(1,BUFFER_SIZE);
88 acc_z = zeros(1,BUFFER_SIZE);
89 gyr_x = zeros(1,BUFFER_SIZE);
90 gyr_y = zeros(1,BUFFER_SIZE);
91 gyr_z = zeros(1,BUFFER_SIZE);
92 mag_x = zeros(1,BUFFER_SIZE);
93 mag_y = zeros(1,BUFFER_SIZE);
94 mag_z = zeros(1,BUFFER_SIZE);
96 % User Acceleration mean and Variance
97 A_T = zeros(1,BUFFER_SIZE);
98 G_T = zeros(1,BUFFER_SIZE);
99 M_T = zeros(1,BUFFER_SIZE);
100 var_roll = zeros(1,BUFFER_SIZE);
101 var_pitch = zeros(1,BUFFER_SIZE);
102 var_yaw = zeros(1,BUFFER_SIZE);
103 var_Gx = zeros(1,BUFFER_SIZE);
104 var_Gy = zeros(1,BUFFER_SIZE);
105 var_Gz = zeros(1,BUFFER_SIZE);
107 roll = zeros(1,BUFFER_SIZE);
108 pitch = zeros(1,BUFFER_SIZE);
109 yaw = zeros(1,BUFFER_SIZE);
110 quat_driv = zeros(BUFFER_SIZE,4);
111 quat_aid = zeros(BUFFER_SIZE,4);
112 quat_error = zeros(BUFFER_SIZE,4);
113 euler = zeros(BUFFER_SIZE,3);
114 Ro = zeros(3, 3, BUFFER_SIZE);
116 OR_driv = zeros(3,BUFFER_SIZE);
117 OR_aid = zeros(3,BUFFER_SIZE);
118 OR_err = zeros(3,BUFFER_SIZE);
120 % system covariance matrix
123 % measurement covariance matrix
130 acc_e = [0.0;0.0;1.0]; % gravity vector in earth frame
131 mag_e = [0.0;MAGNETIC_ALIGNMENT_FACTOR;0.0]; % magnetic field vector in earth frame
133 H = [eye(3) zeros(3,3); zeros(3,6)];
134 x = zeros(6,BUFFER_SIZE);
136 P = 1 * eye(6);% state covariance matrix
138 quat_driv(1,:) = [1 0 0 0];
140 % first order filtering
141 for i = 1:BUFFER_SIZE
142 if LOW_PASS_FILTERING_ON == 1
154 filt_Ax(i) = -a(2)*filt_Ax(i-1)+b(1)*Ax(i);
155 filt_Ay(i) = -a(2)*filt_Ay(i-1)+b(1)*Ay(i);
156 filt_Az(i) = -a(2)*filt_Az(i-1)+b(1)*Az(i);
157 filt_Gx(i) = -a(2)*filt_Gx(i-1)+b(1)*Gx(i);
158 filt_Gy(i) = -a(2)*filt_Gy(i-1)+b(1)*Gy(i);
159 filt_Gz(i) = -a(2)*filt_Gz(i-1)+b(1)*Gz(i);
160 filt_Mx(i) = -a(2)*filt_Mx(i-1)+b(1)*Mx(i);
161 filt_My(i) = -a(2)*filt_My(i-1)+b(1)*My(i);
162 filt_Mz(i) = -a(2)*filt_Mz(i-1)+b(1)*Mz(i);
176 % normalize accelerometer measurements
177 norm_acc = 1/sqrt(filt_Ax(i)^2 + filt_Ay(i)^2 + filt_Az(i)^2);
178 acc_x(i) = norm_acc * filt_Ax(i);
179 acc_y(i) = norm_acc * filt_Ay(i);
180 acc_z(i) = norm_acc * filt_Az(i);
182 % normalize magnetometer measurements
183 norm_mag = 1/sqrt(filt_Mx(i)^2 + filt_My(i)^2 + filt_Mz(i)^2);
184 mag_x(i) = norm_mag * filt_Mx(i);
185 mag_y(i) = norm_mag * filt_My(i);
186 mag_z(i) = norm_mag * filt_Mz(i);
188 gyr_x(i) = filt_Gx(i) * PI;
189 gyr_y(i) = filt_Gy(i) * PI;
190 gyr_z(i) = filt_Gz(i) * PI;
192 UA(i) = sqrt(acc_x(i)^2 + acc_y(i)^2 + acc_z(i)^2) - GRAVITY;
194 gyr_x(i) = gyr_x(i) - Bx;
195 gyr_y(i) = gyr_y(i) - By;
196 gyr_z(i) = gyr_z(i) - Bz;
198 % Aiding System (Accelerometer + Geomagnetic) quaternion generation
199 % gravity vector in body frame
200 acc_b =[acc_x(i);acc_y(i);acc_z(i)];
201 % magnetic field vector in body frame
202 mag_b =[mag_x(i);mag_y(i);mag_z(i)];
204 % compute measurement quaternion with TRIAD algorithm
205 acc_b_x_mag_b = cross(acc_b,mag_b);
206 acc_e_x_mag_e = cross(acc_e,mag_e);
207 M_b = [acc_b acc_b_x_mag_b cross(acc_b_x_mag_b,acc_b)];
208 M_e = [acc_e acc_e_x_mag_e cross(acc_e_x_mag_e,acc_e)];
210 quat_aid(i,:) = rot_mat2quat(Rot_m);
212 euler = quat2euler(quat_aid(i,:));
217 if i <= MOVING_AVERAGE_WINDOW_LENGTH
218 var_Gx(i) = NON_ZERO_VAL;
219 var_Gy(i) = NON_ZERO_VAL;
220 var_Gz(i) = NON_ZERO_VAL;
221 var_roll(i) = NON_ZERO_VAL;
222 var_pitch(i) = NON_ZERO_VAL;
223 var_yaw(i) = NON_ZERO_VAL;
225 var_Gx(i) = var(gyr_x((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
226 var_Gy(i) = var(gyr_y((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
227 var_Gz(i) = var(gyr_z((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
228 var_roll(i) = var(roll((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
229 var_pitch(i) = var(pitch((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
230 var_yaw(i) = var(yaw((i-MOVING_AVERAGE_WINDOW_LENGTH):i));
233 A_T(i) = ATime(i) - ATime(i-1);
234 G_T(i) = GTime(i) - GTime(i-1);
235 M_T(i) = MTime(i) - MTime(i-1);
240 Qwn = [var_Gx(i) 0 0;0 var_Gy(i) 0;0 0 var_Gz(i);];
241 Qwb = (2 * (ZigmaW^2) / TauW) * eye(3);
243 % Process Noise Covariance
244 Q = [Qwn zeros(3,3);zeros(3,3) Qwb];
246 % Measurement Noise Covariance
247 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)];
249 % initialization for q
254 q_t = [q(2) q(3) q(4)]';
255 Rtan = (q(1)^2 - q_t'*q_t)*eye(3) + 2*q_t*q_t' - 2*q(1)*[0 -q(4) q(3);q(4) 0 -q(2);-q(3) q(2) 0];
256 F = [[0 gyr_z(i) -gyr_y(i);-gyr_z(i) 0 gyr_x(i);gyr_y(i) -gyr_x(i) 0] Rtan; zeros(3,3) (-(1/TauW) * eye(3))];
260 x(:,i) = F * x(:,i-1);
263 % compute covariance of prediction
264 P = (F * P * F') + Q;
266 % Driving System (Gyroscope) quaternion generation
267 % convert scaled gyro data to rad/s
268 qDot = 0.5 * quat_prod(q, [0 gyr_x(i) gyr_y(i) gyr_z(i)]);
270 % Integrate to yield quaternion
271 q = q + qDot * dt * PI;
273 % normalise quaternion
274 quat_driv(i,:) = q / norm(q);
277 quat_error(i,:) = quat_prod(quat_aid(i,:), quat_driv(i,:));
279 euler_e = quat2euler(quat_error(i,:));
284 q = quat_prod(quat_driv(i,:), [1 x1 x2 x3]) * PI;
288 e = [x1 x2 x3 x(4,i) x(5,i) x(6,i)];
292 % compute Kalman gain
293 K(:,j) = P(j ,:)./(P(j,j)+R(j,j));
294 % update state vector
295 x(:,i) = x(:,i) + K(:,j) * e(j);
296 % update covariance matrix
297 P = (eye(6) - (K(:,j) * H(j,:))) * P;
305 OR_aid(1,:) = roll * RAD2DEG;
306 OR_aid(2,:) = pitch * RAD2DEG;
307 OR_aid(3,:) = yaw * RAD2DEG;
309 euler = quat2euler(quat_driv);
310 OR_driv(1,:) = ROLL_PHASE_CORRECTION * euler(:,2)' * RAD2DEG;
311 OR_driv(2,:) = PITCH_PHASE_CORRECTION * euler(:,1)' * RAD2DEG;
312 OR_driv(3,:) = YAW_PHASE_CORRECTION * euler(:,3)' * RAD2DEG;
314 euler = quat2euler(quat_error);
315 OR_err(1,:) = euler(:,2)' * RAD2DEG;
316 OR_err(2,:) = euler(:,1)' * RAD2DEG;
317 OR_err(3,:) = euler(:,3)' * RAD2DEG;
319 if PLOT_SCALED_SENSOR_COMPARISON_DATA == 1
320 % Accelerometer/Gyroscope/Magnetometer scaled Plot results
322 scrsz = get(0,'ScreenSize');
323 set(hfig,'position',scrsz);
325 p1 = plot(1:BUFFER_SIZE,acc_x(1,1:BUFFER_SIZE),'r');
328 p2 = plot(1:BUFFER_SIZE,filt_Gx(1,1:BUFFER_SIZE),'b');
331 p3 = plot(1:BUFFER_SIZE,mag_x(1,1:BUFFER_SIZE),'k');
332 title(['Accelerometer/Gyroscope/Magnetometer X-Axis Plot']);
333 legend([p1 p2 p3],'Acc_X', 'Gyr_X', 'Mag_X');
335 p1 = plot(1:BUFFER_SIZE,acc_y(1,1:BUFFER_SIZE),'r');
338 p2 = plot(1:BUFFER_SIZE,filt_Gy(1,1:BUFFER_SIZE),'b');
341 p3 = plot(1:BUFFER_SIZE,mag_y(1,1:BUFFER_SIZE),'k');
342 title(['Accelerometer/Gyroscope/Magnetometer Y-Axis Plot']);
343 legend([p1 p2 p3],'Acc_Y', 'Gyr_Y', 'Mag_Y');
345 p1 = plot(1:BUFFER_SIZE,acc_z(1,1:BUFFER_SIZE),'r');
348 p2 = plot(1:BUFFER_SIZE,filt_Gz(1,1:BUFFER_SIZE),'b');
351 p3 = plot(1:BUFFER_SIZE,mag_z(1,1:BUFFER_SIZE),'k');
352 title(['Accelerometer/Gyroscope/Magnetometer Z-Axis Plot']);
353 legend([p1 p2 p3],'Acc_Z', 'Gyr_Z', 'Mag_Z');
356 if PLOT_INDIVIDUAL_SENSOR_INPUT_DATA == 1
357 % Accelerometer Raw (vs) filtered output
359 scrsz = get(0,'ScreenSize');
360 set(hfig,'position',scrsz);
362 p1 = plot(1:BUFFER_SIZE,Ax(1,1:BUFFER_SIZE),'r');
365 p2 = plot(1:BUFFER_SIZE,filt_Ax(1,1:BUFFER_SIZE),'b');
366 title(['Accelerometer X-Axis Plot']);
367 legend([p1 p2],'input signal','low-pass filtered signal');
369 p1 = plot(1:BUFFER_SIZE,Ay(1,1:BUFFER_SIZE),'r');
372 p2 = plot(1:BUFFER_SIZE,filt_Ay(1,1:BUFFER_SIZE),'b');
373 title(['Accelerometer Y-Axis Plot']);
374 legend([p1 p2],'input signal','low-pass filtered signal');
376 p1 = plot(1:BUFFER_SIZE,Az(1,1:BUFFER_SIZE),'r');
379 p2 = plot(1:BUFFER_SIZE,filt_Az(1,1:BUFFER_SIZE),'b');
380 title(['Accelerometer Z-Axis Plot']);
381 legend([p1 p2],'input signal','low-pass filtered signal');
383 % Gyroscope Raw (vs) filtered output
385 scrsz = get(0,'ScreenSize');
386 set(hfig,'position',scrsz);
388 p1 = plot(1:BUFFER_SIZE,Gx(1,1:BUFFER_SIZE),'r');
391 p2 = plot(1:BUFFER_SIZE,filt_Gx(1,1:BUFFER_SIZE),'b');
392 title(['Gyroscope X-Axis Plot']);
393 legend([p1 p2],'input signal','low-pass filtered signal');
395 p1 = plot(1:BUFFER_SIZE,Gy(1,1:BUFFER_SIZE),'r');
398 p2 = plot(1:BUFFER_SIZE,filt_Gy(1,1:BUFFER_SIZE),'b');
399 title(['Gyroscope Y-Axis Plot']);
400 legend([p1 p2],'input signal','low-pass filtered signal');
402 p1 = plot(1:BUFFER_SIZE,Gz(1,1:BUFFER_SIZE),'r');
405 p2 = plot(1:BUFFER_SIZE,filt_Gz(1,1:BUFFER_SIZE),'b');
406 title(['Gyroscope Z-Axis Plot']);
407 legend([p1 p2],'input signal','low-pass filtered signal');
409 % Magnetometer Raw (vs) filtered output
411 scrsz = get(0,'ScreenSize');
412 set(hfig,'position',scrsz);
414 p1 = plot(1:BUFFER_SIZE,Mx(1,1:BUFFER_SIZE),'r');
417 p2 = plot(1:BUFFER_SIZE,filt_Mx(1,1:BUFFER_SIZE),'b');
418 title(['Magnetometer X-Axis Plot']);
419 legend([p1 p2],'input signal','low-pass filtered signal');
421 p1 = plot(1:BUFFER_SIZE,My(1,1:BUFFER_SIZE),'r');
424 p2 = plot(1:BUFFER_SIZE,filt_My(1,1:BUFFER_SIZE),'b');
425 title(['Magnetometer Y-Axis Plot']);
426 legend([p1 p2],'input signal','low-pass filtered signal');
428 p1 = plot(1:BUFFER_SIZE,Mz(1,1:BUFFER_SIZE),'r');
431 p2 = plot(1:BUFFER_SIZE,filt_Mz(1,1:BUFFER_SIZE),'b');
432 title(['Magnetometer Z-Axis Plot']);
433 legend([p1 p2],'input signal','low-pass filtered signal');