Master Thesis Code
by Simon Moser
Loading...
Searching...
No Matches
exp_SonEMS_1.m
Go to the documentation of this file.
1%
2% For LICENSE / TERMS OF USE, see the file LICENSE in the root directory of the repository.
3
4clear; close all; clc;
5
6
7%% Sensor Data
8datapath = getDataPath();
9datapath = fullfile(datapath, "ETH_Data/SonE_36/RawData/ZM");
10data = jumpReadData(fullfile(datapath, "13167000.BIN"));
11dt = 1/data.mpu_rate;
12
13% manually find start and stop movement phases
14idx_tug_post = [999486, 1001570];
15idx_walk = [605943, 731541];
16
17%% Zero velocity detection
18zv = jumpZeroVelocityDetection(data, "threshold_acc", 1, "threshold_gyr", 10, "windowSize", 20, "plot",false);
19zv_tug_post = zv(idx_tug_post(1):idx_tug_post(2));
20zv_walk = zv(idx_walk(1):idx_walk(2));
21
22%% Filter TUG post
23
24% instance of filter
25ekf = EKF1;
26ekf.sigma_w_p = ekf.sigma_w_p * 5; % as not many zv points are available, trust in accel measurements must be bigger.
27
28% measurements
29ekf.zv = zv_tug_post;
30ekf.z_acc = data.tt.acc(idx_tug_post(1):idx_tug_post(2),:)';
31ekf.z_vel = zeros(size(ekf.z_acc));
32ekf.z_gyr = data.tt.gyr(idx_tug_post(1):idx_tug_post(2),:)' * pi/180; % convert to rad/s
33ekf.z_pos = zeros(size(ekf.z_acc));
34ekf.z_pos(1:2,:) = nan;
35ekf.z_pos(1:2,end-100:end) = 0;
36ekf.z_pos(1:2,1:100) = 0;
37ekf.z_yaw = nan(1, size(ekf.z_acc,2));
38
39
40% initial orientation
41
42% init orientation
43a_x = mean(data.tt.acc(idx_tug_post(1):idx_tug_post(1)+200,1));
44a_y = mean(data.tt.acc(idx_tug_post(1):idx_tug_post(1)+200,2));
45a_z = mean(data.tt.acc(idx_tug_post(1):idx_tug_post(1)+200,3));
46
47% calculate initial orientation
48roll = atan2(a_y, a_z);
49pitch = atan2(-a_x, sqrt(a_y^2 + a_z^2));
50ekf.q_0 = quaternion([0, pitch, roll], "eulerd", "ZYX", "frame").normalize.compact;
51
52% Run filter
53smoothed = ekf.run(plot=true);
54
55%% compare TUG post
56
57% load ground truth
58load("C:\Users\moss\OneDrive - ZHAW\MT\Measurements\ETH_Data\SonE_36\Processed\Vicon\matfiles\TUG_Post.mat")
59GT = VD.Marker.LTO3 / 1000;
60%GT = GT(240:2941,:); % trim away the jump
61
62% align trajectories
63smoothed_cld = pointCloud(smoothed(8:10,:)');
64tform = rigidtform3d([180,0,0], [0,0,0]);
65smoothed_cld = pctransform(smoothed_cld, tform);
66
67GT_cld = pointCloud(GT);
68[~, GT_cld] = pcregistericp(GT_cld, smoothed_cld, "Metric","pointToPoint", "MaxIterations", 200, "Verbose",false);
69
70% format
71GT_cld.Color = [0,1,0];
72GT_cld.Intensity = 10 * ones(GT_cld.Count,1);
73smoothed_cld.Color = [1,0,0];
74
75% display
76figure
77pcshow(GT_cld,"BackgroundColor","white");
78hold on;
79pcshow(smoothed_cld, "MarkerSize", 5);
80hold off;
81
82
83%% Filter walking
84
85close all
86
87% instance of filter
88ekf = EKF1;
89
90% initial gyroscope bias
91ekf.b_w_0 = [mean(data.tt.gyr(zv_walk(1:4000),1));mean(data.tt.gyr(zv_walk(1:4000),2));mean(data.tt.gyr(zv_walk(1:4000),3))] * pi / 180;
92
93
94ekf.P_0_b_w = 1e-6; % original 5e-3, trust the initial bias much
95ekf.P_0_q = 1e-3;
96ekf.var_gyr = 1e-4; % opriginal 12e-4, less trust in zero rotation
97
98% tune measurement noise
99%ekf.var_vel_ZUPT = 1e-1;
100ekf.var_acc_xy = 0.5e-3; %original: 45e-3
101ekf.var_acc_z = 1e-3; %original: 70e-3
102ekf.var_pos_pseudo = 1e-3; % very low trust, only a slight nudging
103ekf.var_b_w = 1e-7;
104
105% tune process noise
106ekf.sigma_w_p = ekf.sigma_w_p * 2; % as not many zv points are available, trust in accel measurements must be bigger.
107
108% measurements
109ekf.zv = zv_walk;
110ekf.z_acc = data.tt.acc(idx_walk(1):idx_walk(2),:)';
111ekf.z_vel = zeros(size(ekf.z_acc));
112ekf.z_gyr = data.tt.gyr(idx_walk(1):idx_walk(2),:)' * pi/180; % convert to rad/s
113ekf.z_pos = zeros(size(ekf.z_acc));
114speed = 3.2/3.6; % m/s;
115
116% pseudo position update
117ekf.z_pos(1,92:end) = cumsum(speed * dt * ones(size(ekf.z_pos(1,92:end))));
118%ekf.z_pos(1:2,:) = nan;
119
120% pseudo yaw update
121ekf.z_yaw = deg2rad(10) * ones(1, size(ekf.z_acc,2));
122%ekf.z_yaw = nan(1, size(ekf.z_acc,2));
123
124
125% initial orientation
126
127% mean accelerations during stance
128a_x = mean(data.tt.acc(zv_walk,1));
129a_y = mean(data.tt.acc(zv_walk,2));
130a_z = mean(data.tt.acc(zv_walk,3));
131
132% calculate initial orientation
133roll = atan2(a_y, a_z);
134pitch = atan2(-a_x, sqrt(a_y^2 + a_z^2));
135yaw = deg2rad(130);
136ekf.q_0 = quaternion([yaw, pitch, roll], "euler", "ZYX", "frame").normalize.compact;
137
138% Run filter
139smoothed = ekf.run(plot=true);
140%% compare walking
141
142% load ground truth
143load("C:\Users\moss\OneDrive - ZHAW\MT\Measurements\ETH_Data\SonE_36\Processed\Vicon\matfiles\WN35.mat")
144GT = VD.Marker.LTO3 / 1000;
145
146v = diff(GT(:,2))/0.005;
147figure
148plot(v);
149
150GT(:,2) = GT(:,2) - cumsum(speed * dt * ones(1,height(GT)))';
151
152plot(-GT(:,2));
153hold on
154plot(smoothed(8,:));
155hold off
156%% show the Problem to Yong
157figure
158plot(data.tt.t, vecnorm(data.tt.acc,2,2))
159title("SonEMS 36 - TUG Pre (partially)")
160grid on
161ylabel("||a||_2 [m/s^2]", Interpreter="tex")
162ylim([8,12])
163xlim([datetime("01.11.2022 10:25:38"), datetime("01.11.2022 10:25:43")])
164pause;
165title("SonEMS 36 - Treadmill (partially)")
166xlim([datetime("01.11.2022 10:47:00"), datetime("01.11.2022 10:47:05")])
167
168test = vecnorm(data.tt.acc,2,2);
169TUG_test = test(254555:256260);
170WALK_test= test(649824:651544);
EKF1 is an implementation of a state estimator for JUMP sensors.
Definition EKF1.m:36
Property sigma_w_p
angular velocity standard deviation (default 876e-6 rad/s)
Definition EKF1.m:248
function getDataPath()
function jumpReadData(in filePath, in options)
function jumpZeroVelocityDetection(in data, in options)