Master Thesis Code
by Simon Moser
Loading...
Searching...
No Matches
derivation3RtsSmoother.m
Go to the documentation of this file.
1% =========================================================================== %
2%> @example derivation3RtsSmoother.m
3%>
4%> This example demonstrates the application of the RTS Smoother for the
5%> estimation of the position, velocity and acceleration of a moving object.
6%> The example is based on the explanations in @ref derivation3RtsSmoother
7%> and uses the same trajectory and sensor data generation as in the previous
8%> examples. The example also includes the zero velocity detection and the
9%> pseudo measurement of the position. The example also includes the Kalman.
10%>
11%> @note a more detailed explanation can be found here: @ref derivation3RtsSmoother
12%>
13%> <details>
14% =========================================================================== %
15
16clear; close all; clc;
17
18% flags if the pesudo measurement of distance should be used
19PM_P = true;
20
21rng(1); % seed the random number generator for reproducibility
22
23%% Sensor Data Generation
24% Define the trajectory object
25traj = Trajectory("fs", 200); % 200 Hz sampling frequency
26
27% add waypoints
28traj.addWaypoints(seconds(0), seconds(1), [0,0,0], quaternion.ones(1)); %initial pos for one second
29traj.addWaypoints(seconds(10), seconds(1), [10,0,0], quaternion.ones(1)); %move 10m in x direction and stay there for one second
30traj.addWaypoints(seconds(20), seconds(1), [0,0,0], quaternion.ones(1)); %move back to origin
31
32% generate the trajectory
33traj.generateTrajectory("pos","minJerk"); % generate the trajectory with minimum jerk interpolation
34
35% Define the sensor object (realistic sensor)
36js = JumpSensor;
37
38% generate the sensor data
39data = js.generateFromTrajectory(traj);
40accelReadings = data.tt.acc(:,1); % only consider the x axis
41
42% define time step
43dt = 1/data.mpu_rate;
44
45% get actual bias and sensitivity
46bias = js.getParams.mpu.acc.ConstantBias(1);
47sensitivity = js.getParams.mpu.acc.AxesMisalignment(1,1) / 100; % convert from percentage to fraction
48
49%% Zero Velocity Detection
50% a good zero velocity estimation on a one axis accelerometer is not easy to
51% achieve. Therefore the zero velocity detection is hard coded here.
52zv = false(size(accelReadings));
53
54% in the actual data the zero velocity is between index 1:200, 2000:2200 and
55% 4000:4200. But we would't know that in a real scenario. Therefore only a few
56% zero velocity points are set to true.
57zv(1) = true;
58zv(end) = true;
59
60%% Position Pseudo Measurement
61% We define a pseudo measurement of the position at the static position at 10m
62% and 20s.
63PM_P_idx = [2100,4100];
64PM_P_val = [10,0];
65
66%% Kalman smoothing
67% Define the number of iterations
68N = length(accelReadings);
69
70% Initialize the state vectors (position; velocity; acceleration)
71x_fm = nan(4,N); % fm for forward, minus
72x_fp = nan(size(x_fm)); % fp for forward, plus
73x_bp = nan(size(x_fm)); % bp for backward, plus
74x_0 = [0; 0; 0; 0]; % initial state vector
75
76% Initialize the covariance matrices
77P_fm = nan(4,4,N); % fm for forward, minus
78P_fp = nan(size(P_fm)); % fp for forward, plus
79P_bp = nan(size(P_fm)); % bp for backward, plus
80P_0 = zeros(4,4); P_0(4,4) = 0.59; % initial covariance matrix
81
82% Initialize the state transition matrix
83F = [...
84 1, dt, 0.5*dt^2, 0; ...
85 0, 1, dt, 0; ...
86 0, 0, 1, 0; ...
87 0, 0, 0, 1; ...
88 ];
89
90% Initialize the process noise matrix
91stdDevAccel = 0.0025; % standard deviation of the acceleration process noise
92Q = [...
93 dt^4/4, dt^3/2, dt^2/2, 0, ;...
94 dt^3/2, dt^2, dt, 0, ;...
95 dt^2/2, dt, 1, 0, ;...
96 0, 0, 0, 1e-9/stdDevAccel...
97 ] * stdDevAccel;
98
99% Initialize the observation matrix (updated at each iteration)
100if PM_P
101 H = [...
102 0, 0, 1, 1; ...
103 0, 1, 0, 0; ...
104 1, 0, 0, 0 ...
105 ];
106else
107 H = [0, 0, 1, 1; 0, 1, 0, 0];
108end
109
110% Initialize the measurement noise matrix
111if PM_P
112 R = [0.0449, 0, 0; 0, 0.001, 0; 0, 0, 0.5];
113else
114 R = [0.0449, 0; 0, 0.001];
115end
116
117% iterate forward (kalman filter)
118for k = 1:N
119
120 % predict
121 if k == 1
122 % initial prediction
123 x_fm(:,k) = F * x_0;
124 P_fm(:,:,k) = F * P_0 * F' + Q;
125 else
126 x_fm(:,k) = F * x_fp(:,k-1);
127 P_fm(:,:,k) = F * P_fp(:,:,k-1) * F' + Q;
128 end
129
130 % update
131 if PM_P
132 z = [accelReadings(k); 0; 0];
133 else
134 z = [accelReadings(k); 0];
135 end
136
137 % handle zero velocity detection
138 if zv(k)
139 H(2,2) = 1;
140 else
141 H(2,2) = 0;
142 end
143
144 % handle pseudo measurement of position
145 if PM_P
146 if any(k == PM_P_idx)
147 % pseudo measurement of position
148 H(3,1) = 1;
149 z(3) = PM_P_val(PM_P_idx == k);
150 else
151 H(3,1) = 0;
152 end
153 end
154
155 % calculate the kalman gain
156 K = P_fm(:,:,k) * H' * inv(H * P_fm(:,:,k) * H' + R);
157
158 % calculate the state vector and the covariance matrix
159 x_fp(:,k) = x_fm(:,k) + K * (z - H * x_fm(:,k));
160 P_fp(:,:,k) = (eye(4) - K * H) * P_fm(:,:,k);
161
162end
163
164% iterate backward (smoother)
165for k = N-1:-1:1 % not including zero since zero is perfectly known
166 if k == N-1
167 % Initialization
168 x_bp(:,N) = x_fp(:,N);
169 P_bp(:,:,N) = P_fp(:,:,N);
170 end
171
172 I = P_fm(:,:,k+1)^-1;
173 K = P_fp(:,:,k) * F' * I;
174 x_bp(:,k) = x_fp(:,k) + K * (x_bp(:,k+1) - x_fm(:,k+1));
175 P_bp(:,:,k) = P_fp(:,:,k) - K * (P_fm(:,:,k+1) - P_bp(:,:,k+1)) * K';
176end
177
178%% calculate 95% confidence intervals for acc, vel and pos (forward and smoothed)
179% calculate the 95% confidence intervals for the acceleration
180conf_acc_filt = [x_fp(3,:)' - 1.96 * sqrt(squeeze(P_fp(3,3,:))), x_fp(3,:)' + 1.96 * sqrt(squeeze(P_fp(3,3,:)))];
181conf_acc_smooth = [x_bp(3,:)' - 1.96 * sqrt(squeeze(P_bp(3,3,:))), x_bp(3,:)' + 1.96 * sqrt(squeeze(P_bp(3,3,:)))];
182
183% calculate the 95% confidence intervals for the velocity
184conf_vel_filt = [x_fp(2,:)' - 1.96 * sqrt(squeeze(P_fp(2,2,:))), x_fp(2,:)' + 1.96 * sqrt(squeeze(P_fp(2,2,:)))];
185conf_vel_smooth = [x_bp(2,:)' - 1.96 * sqrt(squeeze(P_bp(2,2,:))), x_bp(2,:)' + 1.96 * sqrt(squeeze(P_bp(2,2,:)))];
186
187% calculate the 95% confidence intervals for the position
188conf_pos_filt = [x_fp(1,:)' - 1.96 * sqrt(squeeze(P_fp(1,1,:))), x_fp(1,:)' + 1.96 * sqrt(squeeze(P_fp(1,1,:)))];
189conf_pos_smooth = [x_bp(1,:)' - 1.96 * sqrt(squeeze(P_bp(1,1,:))), x_bp(1,:)' + 1.96 * sqrt(squeeze(P_bp(1,1,:)))];
190
191%% Plot the results
192% visualize the results
193figure("Position", [100, 400, 1200, 600]);
194subplot(3,1,1); % position
195yyaxis left;
196plot(traj.getTrajectory.t, traj.getTrajectory.pos(:,1), 'DisplayName', 'True position');
197hold on;
198plot(traj.getTrajectory.t, x_fp(1,:), 'DisplayName', 'Estimated position (filtered)');
199plot(traj.getTrajectory.t, x_bp(1,:), 'DisplayName', 'Estimated position (smoothed)');
200fill([traj.getTrajectory.t; flipud(traj.getTrajectory.t)], [conf_pos_filt(:,1); flipud(conf_pos_filt(:,2))], 'r', 'EdgeColor', 'none', 'FaceAlpha', 0.3, 'DisplayName', '95% confidence interval (filtered)');
201fill([traj.getTrajectory.t; flipud(traj.getTrajectory.t)], [conf_pos_smooth(:,1); flipud(conf_pos_smooth(:,2))], 'b', 'EdgeColor', 'none', 'FaceAlpha', 0.3, 'DisplayName', '95% confidence interval (smoothed)');
202hold off;
203xlabel('t [s]');
204ylabel('p [m]');
205legend("Location","bestoutside");
206grid on;
207yyaxis right;
208plot(traj.getTrajectory.t, traj.getTrajectory.pos(:,1) - x_fp(1,:)', 'DisplayName', 'Error after filtering');
209hold on;
210plot(traj.getTrajectory.t, traj.getTrajectory.pos(:,1) - x_bp(1,:)', 'DisplayName', 'Error after smoothing');
211
212ylabel('\Delta p [m]');
213
214subplot(3,1,2); % velocity
215yyaxis left;
216plot(traj.getTrajectory.t, traj.getTrajectory.vel(:,1), 'DisplayName', 'True velocity');
217hold on;
218plot(traj.getTrajectory.t, x_fp(2,:), 'DisplayName', 'Estimated velocity (filtered)');
219plot(traj.getTrajectory.t, x_bp(2,:), 'DisplayName', 'Estimated velocity (smoothed)');
220fill([traj.getTrajectory.t; flipud(traj.getTrajectory.t)], [conf_vel_filt(:,1); flipud(conf_vel_filt(:,2))], 'r', 'EdgeColor', 'none', 'FaceAlpha', 0.3, 'DisplayName', '95% confidence interval (filtered)');
221fill([traj.getTrajectory.t; flipud(traj.getTrajectory.t)], [conf_vel_smooth(:,1); flipud(conf_vel_smooth(:,2))], 'b', 'EdgeColor', 'none', 'FaceAlpha', 0.3, 'DisplayName', '95% confidence interval (smoothed)');
222hold off;
223xlabel('t [s]');
224ylabel('v [m/s]');
225legend("Location","bestoutside");
226grid on;
227yyaxis right;
228plot(traj.getTrajectory.t, traj.getTrajectory.vel(:,1) - x_fp(2,:)', 'DisplayName', 'Error after filtering');
229hold on;
230plot(traj.getTrajectory.t, traj.getTrajectory.vel(:,1) - x_bp(2,:)', 'DisplayName', 'Error after smoothing');
231ylabel('\Delta v [m/s]');
232
233subplot(3,1,3); % acceleration
234yyaxis left;
235plot(traj.getTrajectory.t, traj.getTrajectory.acc(:,1), 'DisplayName', 'True acceleration');
236hold on;
237plot(traj.getTrajectory.t, x_fp(3,:), 'DisplayName', 'Estimated acceleration (filtered)');
238plot(traj.getTrajectory.t, x_bp(3,:), 'DisplayName', 'Estimated acceleration (smoothed)');
239plot(traj.getTrajectory.t, accelReadings, 'DisplayName', 'Measured acceleration');
240fill([traj.getTrajectory.t; flipud(traj.getTrajectory.t)], [conf_acc_filt(:,1); flipud(conf_acc_filt(:,2))], 'r', 'EdgeColor', 'none', 'FaceAlpha', 0.3, 'DisplayName', '95% confidence interval (filtered)');
241fill([traj.getTrajectory.t; flipud(traj.getTrajectory.t)], [conf_acc_smooth(:,1); flipud(conf_acc_smooth(:,2))], 'b', 'EdgeColor', 'none', 'FaceAlpha', 0.3, 'DisplayName', '95% confidence interval (smoothed)');
242hold off;
243xlabel('t [s]');
244ylabel('a [m/s^2]');
245legend("Location","bestoutside");
246grid on;
247yyaxis right;
248plot(traj.getTrajectory.t, traj.getTrajectory.acc(:,1) - x_fp(3,:)', 'DisplayName', 'Error after filtering');
249hold on;
250plot(traj.getTrajectory.t, traj.getTrajectory.acc(:,1) - x_bp(3,:)', 'DisplayName', 'Error after smoothing');
251ylabel('\Delta a [m/s^2]');
252
253% common x axis
254linkaxes(findall(gcf,'type','axes'),'x');
JumpSensor is a simulation of the JUMP sensor of the ZurichMove Project.
Definition JumpSensor.m:41
function generateFromTrajectory(in obj, in traj)
generates data from a Trajectory object.
Trajectory Class for generating and manipulating trajectories from waypoints.
Definition Trajectory.m:37