KalmanFilter
particle_filter.m
Go to the documentation of this file.
1 function [estimates, covariances] = particle_filter(f_func,jacobian_func,dt_between_measurements,start_time,state_count,sensor_count,measurement_count,particle_count,particle,C,Q_root,R_root,P_0_root,x_0, measurements)
2 %Particle filter using Extended Kalman filter for local linearization
3 %and Sequential Importance Resampling (SIR) for the resampling.
4 %estimate and covariances are at the time step before all the
5 %measurements - be wary of the off-by-one error. If f_func is a
6 %linear function the code is equivalent to discrete-discrete Kalman
7 %filter.
8 %[estimates, covariances] = particle_filter(f_func,jacobian_func,state_count,sensor_count,measurement_count,particle_count,particle,C,Q_root,R_root,P_0_root,x_0, measurements)
9 %INPUT:
10 % f_func: x_{k+1} = f_func(x_k,t) where x_k is the state. The
11 % function's second argument is time t for cases when the function
12 % changes with time. The argument can be also used an internal
13 % counter variable for f_func when start_time is set to zero and
14 % dt_between_measurements is set to 1.
15 %
16 % jacobian_func(x,t): jacobian of f_func with state x at time t
17 %
18 % dt_between_measurements: time distance between incoming
19 % measurements. Used for incrementing time counter for each
20 % successive measurement with the time counter initialized with
21 % start_time. The time counter is fed into f_func(x,t) as t.
22 % (usually set to 1)
23 %
24 % start_time: the time of first measurement
25 %
26 % state_count: dimension of the state
27 %
28 % sensor_count: dimension of observation vector
29 %
30 % measurement_count: The total amount of measurements.
31 %
32 % particle_count: The amount of particles in the particle_filter.
33 %
34 % particle: `state_count X particle_count` matrix where the i^th
35 % column is the i^th particle. Can be initialized as
36 % `particle = mvnrnd(x_0,P_0,particle_count)';` on user end. The
37 % particle weights will be initialized as uniform.
38 %
39 % C: observation matrix of size 'sensor_count by state_count'
40 %
41 % R_root: The root of sensor error covariance matrix R where
42 % R = R_root*(R_root'). R_root is of size 'sensor_count by
43 % sensor_count'. R_root = chol(R)' is one way to derive it.
44 %
45 % Q_root: The root of process error covariance matrix Q where
46 % Q = Q_root*(Q_root'). Q_root is of size 'state_count by
47 % state_count'. Q_root = chol(Q)' is one way to derive it.
48 %
49 % P_0_root: The root of initial covariance matrix P_0 where
50 % P_0 = P_0_root*(P_root'); P_0_root is of size 'state_count by
51 % state_count'. % P_0_root = chol(P_0)' is one way to derive it.
52 % Particles will be initialized with P_0 as the initial covariance.
53 %
54 % x_0:Initial state estimate of size 'state_count by 1'
55 %
56 % measurements: ith column is ith measurement. Matrix of size
57 % 'sensor_count by measurement_count'
58 %
59 %OUTPUT:
60 % estimates: 'state_count by measurement_count+1'
61 % ith column is ith estimate. first column is x_0
62 %
63 % covariances: cell of size 'measurement_count+1' by 1
64 % where each entry is the P covariance matrix at that time
65 % Time is computed based on dt_between_measurements
66 
67 %basic input checking
68 assert(size(P_0_root,1)==state_count &&...
69  size(P_0_root,2)==state_count);
70 assert(size(C,1)==sensor_count && size(C,2)==state_count);
71 assert(size(Q_root,1)==state_count &&...
72  size(Q_root,2)==state_count);
73 assert(size(x_0,1)==state_count && size(x_0,2)==1);
74 assert(size(R_root,1)==sensor_count &&...
75  size(R_root,2)==sensor_count);
76 test = f_func(x_0,start_time);
77 assert(size(test,1)==state_count && size(test,2)==1);
78 test = jacobian_func(x_0,start_time);
79 assert(size(test,1)==state_count && size(test,2)==state_count);
80 assert(size(measurements,1)==sensor_count &&...
81  size(measurements,2)==measurement_count);
82 assert(size(particle,1)==state_count&&...
83  size(particle,2)==particle_count);
84 
85 
86 estimates = zeros(state_count,measurement_count+1);
87 covariances = cell(measurement_count+1,1);
88 %%%particles = cell(measurement_count+1,1);
89 %covariance is set to be the same for all initial particles
90 for particle_index=1:particle_count
91  particle_covariance{particle_index} = P_0_root*P_0_root';
92 end
93 %initialized as uniform
94 particle_weight = (1/particle_count).*ones(particle_count,1);
95 
96 estimates(:,1) = x_0;
97 covariances{1} = P_0_root*P_0_root';
98 
99 for k=1:measurement_count
100  %%%particles{k} = particle;
101 
102  %project EKF in each particle i
103  %this (of course) can be parallelized as other for loops here can too
104  for particle_index=1:particle_count
105  %needed just for the covariance - estimate still computed within ddekf_predict_phase - can be removed to further optimize
106  [covariance_sqrt] = predict_phase(f_func,jacobian_func,k,particle_covariance{particle_index},particle(:,particle_index),Q_root);
107 
108  particle(:,particle_index) = f_func(particle(:,particle_index),k) + mvnrnd(zeros(state_count,1),Q_root*Q_root')';
109  particle_covariance{particle_index} = covariance_sqrt;
110  end
111 
112  %update EKF in each particle i
113  for particle_index=1:particle_count
114  [estimate, covariance_sqrt] = update_phase(R_root,particle_covariance{particle_index},C,particle(:,particle_index),measurements(k));
115 
116  particle_covariance{particle_index} = covariance_sqrt;
117  end
118 
119  %update particle weights
120  for weight_index=1:particle_count
121  particle_weight(weight_index) = particle_weight(weight_index)*normpdf(measurements(k),C*particle(:,weight_index),R_root*R_root');
122  end
123  %https://stats.stackexchange.com/questions/201545/likelihood-calculation-in-particle-filtering
124  %normalize particle weights
125  particle_weight = (1/sum(particle_weight)).*particle_weight;
126 
127  %compute and store mean estimate and mean covariance
128  mean_estimate = zeros(state_count,1);
129  covariance = zeros(state_count,state_count);
130  for particle_index=1:particle_count
131  mean_estimate = mean_estimate + particle_weight(particle_index)*particle(:,particle_index);
132  end
133  for particle_index=1:particle_count
134  covariance = covariance + particle_weight(particle_index).*( (particle(:,particle_index) - mean_estimate)*(particle(:,particle_index) - mean_estimate)');
135  end
136  estimates(:,k+1) = mean_estimate;
137  covariances{k+1} = covariance;
138 
139  %resample
140  %compute cumulitive profile using a rolling sum
141  %(cumulitive distribution function [cdf] - increasing
142  %function with 0 at -infinity and 1 at +infinity).
143  cumulitive_profile = zeros(particle_count,1);
144  cumulitive_profile(1) = particle_weight(1);
145  for weight_index=2:particle_count
146  cumulitive_profile(weight_index) = cumulitive_profile(weight_index-1) + particle_weight(weight_index);
147  end
148  cumulitive_profile = (1/cumulitive_profile(end)).*cumulitive_profile;%normalize
149 
150  %use uniform distribution(rand) to select from [cdf]
151  %cumulitive_profile to resample/update the particles and their weights.
152  %The particle with the biggest weight will have the biggest y-axis
153  %'gain' in the cdf plot and therefore chosen_index will more frequently
154  %select this particle and produce more particles in particle_new
155  %with uniform particle weight (1/particle_count).
156  %Can modify to do resampling less frequently.
157 
158  particle_new = zeros(state_count,particle_count);
159  particle_covariance_new = cell(state_count,1);
160  for weight_index=1:particle_count
161  chosen_index = (find( cumulitive_profile >= rand,1));
162  particle_new(:,weight_index) = particle(:,chosen_index);
163  particle_covariance_new{weight_index} = particle_covariance{chosen_index};
164  end
165  particle_weight = (1/particle_count).*ones(particle_count,1);
166  particle = particle_new;
167  particle_covariance = particle_covariance_new;
168 end
169 
170 end
function particle_filter(in f_func, in jacobian_func, in dt_between_measurements, in start_time, in state_count, in sensor_count, in measurement_count, in particle_count, in particle, in C, in Q_root, in R_root, in P_0_root, in x_0, in measurements)
function ddekf_predict_phase(in f_func, in jacobian_func, in t, in P_0_sqrt, in x_0, in Q_root)
function update_phase(in R_root, in P_root, in C, in estimate, in measurement)
function predict_phase(in f_func, in jacobian_func, in t, in P_0_sqrt, in x_0, in Q_root)