1% =========================================================================== %
2%> @example derivation3RtsSmoother.m
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.
11%> @note a more detailed explanation can be found here: @ref derivation3RtsSmoother
14% =========================================================================== %
18% flags
if the pesudo measurement of distance should be used
21rng(1); % seed the random number generator
for reproducibility
23%% Sensor Data Generation
24% Define the trajectory
object
25traj =
Trajectory(
"fs", 200); % 200 Hz sampling frequency
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
32% generate the trajectory
33traj.generateTrajectory(
"pos",
"minJerk"); % generate the trajectory with minimum jerk interpolation
35% Define the sensor object (realistic sensor)
38% generate the sensor data
40accelReadings = data.tt.acc(:,1); % only consider the x axis
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
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));
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.
60%% Position Pseudo Measurement
61% We define a pseudo measurement of the position at the static position at 10m
63PM_P_idx = [2100,4100];
67% Define the number of iterations
68N = length(accelReadings);
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
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
82% Initialize the state transition matrix
84 1, dt, 0.5*dt^2, 0; ...
90% Initialize the process noise matrix
91stdDevAccel = 0.0025; % standard deviation of the acceleration process noise
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...
99% Initialize the observation matrix (updated at each iteration)
107 H = [0, 0, 1, 1; 0, 1, 0, 0];
110% Initialize the measurement noise matrix
112 R = [0.0449, 0, 0; 0, 0.001, 0; 0, 0, 0.5];
114 R = [0.0449, 0; 0, 0.001];
117% iterate forward (kalman filter)
124 P_fm(:,:,k) = F * P_0 * F' + Q;
126 x_fm(:,k) = F * x_fp(:,k-1);
127 P_fm(:,:,k) = F * P_fp(:,:,k-1) * F
' + Q;
132 z = [accelReadings(k); 0; 0];
134 z = [accelReadings(k); 0];
137 % handle zero velocity detection
144 % handle pseudo measurement of position
146 if any(k == PM_P_idx)
147 % pseudo measurement of position
149 z(3) = PM_P_val(PM_P_idx == k);
155 % calculate the kalman gain
156 K = P_fm(:,:,k) * H' * inv(H * P_fm(:,:,k) * H
' + R);
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);
164% iterate backward (smoother)
165for k = N-1:-1:1 % not including zero since zero is perfectly known
168 x_bp(:,N) = x_fp(:,N);
169 P_bp(:,:,N) = P_fp(:,:,N);
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
';
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,:)))];
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,:)))];
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,:)))];
192% visualize the results
193figure("Position", [100, 400, 1200, 600]);
194subplot(3,1,1); % position
196plot(traj.getTrajectory.t, traj.getTrajectory.pos(:,1), 'DisplayName
', 'True position
');
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)
');
205legend("Location","bestoutside");
208plot(traj.getTrajectory.t, traj.getTrajectory.pos(:,1) - x_fp(1,:)',
'DisplayName',
'Error after filtering');
210plot(traj.getTrajectory.t, traj.getTrajectory.pos(:,1) - x_bp(1,:)
', 'DisplayName
', 'Error after smoothing
');
212ylabel('\Delta p [m]
');
214subplot(3,1,2); % velocity
216plot(traj.getTrajectory.t, traj.getTrajectory.vel(:,1), 'DisplayName
', 'True velocity
');
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)
');
225legend("Location","bestoutside");
228plot(traj.getTrajectory.t, traj.getTrajectory.vel(:,1) - x_fp(2,:)',
'DisplayName',
'Error after filtering');
230plot(traj.getTrajectory.t, traj.getTrajectory.vel(:,1) - x_bp(2,:)
', 'DisplayName
', 'Error after smoothing
');
231ylabel('\Delta v [m/s]
');
233subplot(3,1,3); % acceleration
235plot(traj.getTrajectory.t, traj.getTrajectory.acc(:,1), 'DisplayName
', 'True acceleration
');
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)
');
245legend("Location","bestoutside");
248plot(traj.getTrajectory.t, traj.getTrajectory.acc(:,1) - x_fp(3,:)',
'DisplayName',
'Error after filtering');
250plot(traj.getTrajectory.t, traj.getTrajectory.acc(:,1) - x_bp(3,:)
', 'DisplayName
', 'Error after smoothing
');
251ylabel('\Delta a [m/s^2]
');
254linkaxes(findall(gcf,'type
','axes
'),'x
');
JumpSensor is a simulation of the JUMP sensor of the ZurichMove Project.
function generateFromTrajectory(in obj, in traj)
generates data from a Trajectory object.
Trajectory Class for generating and manipulating trajectories from waypoints.