Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
ukf-linear-example.py
35
36""" @example ukf-linear-example.py
37 Example of a simple linear use-case of the Unscented Kalman Filter (UKF). Using a UKF
38 in this case is not necessary, it is done for learning purpose only.
39
40 The system we are interested in is a system moving on a 2D-plane.
41
42 The state vector of the UKF is:
43 \f[
44 \begin{array}{lcl}
45 \textbf{x}[0] &=& x \\
46 \textbf{x}[1] &=& \dot{x} \\
47 \textbf{x}[1] &=& y \\
48 \textbf{x}[2] &=& \dot{y}
49 \end{array}
50 \f]
51
52 The measurement \f$ \textbf{z} \f$ corresponds to the position along the x-axis
53 and y-axis. The measurement vector can be written as:
54 \f[
55 \begin{array}{lcl}
56 \textbf{z}[0] &=& x \\
57 \textbf{z}[1] &=& y
58 \end{array}
59 \f]
60
61 Some noise is added to the measurement vector to simulate a sensor which is
62 not perfect.
63"""
64
65from visp.core import ColVector, Matrix, UnscentedKalman, UKSigmaDrawerMerwe
66
67# For the Graphical User Interface
68try:
69 from visp.gui import Plot
70 has_gui = True
71except:
72 has_gui = False
73import numpy as np
74
75def fx(x: ColVector, dt: float) -> ColVector:
76 """
77 Process function that projects in time the internal state of the UKF.
78
79 :param x: The internal state of the UKF.
80 :param dt: The sampling time: how far in the future are we projecting x.
81
82 :return ColVector: The updated internal state, projected in time, also known as the prior.
83 """
84 return ColVector([
85 x[0] + dt * x[1],
86 x[1],
87 x[2] + dt * x[3],
88 x[3]
89 ])
90
91
92def hx(x: ColVector) -> ColVector:
93 """
94 Measurement function that expresses the internal state of the UKF in the measurement space.
95
96 :param x: The internal state of the UKF.
97
98 :return ColVector: The internal state, expressed in the measurement space.
99 """
100 return ColVector([
101 x[0],
102 x[2]
103 ])
104
105def add_state_vectors(a, b) -> ColVector:
106 """
107 Method that permits to add two state vectors.
108
109 :param a: The first state vector to which another state vector must be added.
110 :param b: The other state vector that must be added to a.
111
112 :return ColVector: The sum a + b.
113 """
114 return a + b
115
116def residual_state_vectors(a, b) -> ColVector:
117 """
118 Method that permits to subtract a state vector to another.
119
120 :param a: The first state vector to which another state vector must be subtracted.
121 :param b: The other state vector that must be subtracted to a.
122
123 :return ColVector: The subtraction a - b.
124 """
125 return a - b
126
127def generate_Q_matrix(dt: float) -> Matrix:
128 """
129 Method that generates the process covariance matrix for a process for which the
130 state vector can be written as (x, dx/dt)^T
131
132 :param dt: The sampling period.
133
134 :return Matrix: The corresponding process covariance matrix.
135 """
136 return Matrix(
137 [[dt**3/3, dt**2/2, 0, 0],
138 [dt**2/2, dt, 0, 0],
139 [0, 0, dt**3/3, dt**2/2],
140 [0, 0, dt**2/2, dt]])
141
142if __name__ == '__main__':
143 dt = 0.01 # The sampling period
144 gt_dx = 0.01 # The displacement along the x-axis between two timesteps
145 gt_dy = 0.005 # The displacement along the y-axis between two timesteps
146 gt_dX = ColVector([gt_dx, gt_dy]) # The displacement vector between two timesteps
147 gt_vx = gt_dx / dt # The velocity along the x-axis
148 gt_vy = gt_dy / dt # The velocity along the y-axis
149 proc_var = 0.000004 # The variance of the process function
150 sigma_x_meas = 0.05 # The standard deviation of the measurement noise for the x-axis measurement
151 sigma_y_meas = 0.05 # The standard deviation of the measurement noise for the y-axis measurement
152
153 # The object that draws the sigma points used by the UKF
154 drawer = UKSigmaDrawerMerwe(n=4, alpha=0.3, beta=2, kappa=-1, resFunc=residual_state_vectors, addFunc=add_state_vectors)
155
156 P0 = Matrix(np.eye(4) * 1.) # The initial estimate of the state covariance matrix
157 R = Matrix(np.eye(2) * 0.01) # The measurement covariance matrix
158 Q = generate_Q_matrix(dt) * proc_var # The process covariance matrix
159 ukf = UnscentedKalman(Q, R, drawer, fx, hx) # The Unscented Kalman Filter instance
160
161 # Initializing the state vector and state covariance matrix estimates
162 ukf.init(ColVector([0., 0.75 * gt_vx, 0., 0.75 * gt_vy]), P0)
163
164 # Initializing the Graphical User Interface if the needed libraries are available
165 if has_gui:
166 num_plots = 4
167 plot = Plot(num_plots)
168 plot_titles = [
169 'Position along X-axis', 'Velocity along X-axis',
170 'Position along Y-axis', 'Velocity along Y-axis'
171 ]
172 plot_y_units = [
173 'Position (m)', 'Velocity (m/s)',
174 'Position (m)', 'Velocity (m/s)'
175 ]
176 plot_legends = ['GT', 'Measure', 'Filtered']
177
178 # Initializing the subplots
179 for plot_index in range(num_plots):
180 plot.initGraph(plot_index, len(plot_legends))
181 plot.setTitle(plot_index, plot_titles[plot_index])
182 plot.setUnitY(plot_index, plot_y_units[plot_index])
183 plot.setUnitX(plot_index, 'Time (s)')
184 for legend_index in range(len(plot_legends)):
185 plot.setLegend(plot_index, legend_index, plot_legends[legend_index])
186
187 gt_X = ColVector(2, 0.) # Ground truth position
188 z_prec = ColVector(2, 0.) # Previous measurement vector
189 np.random.seed(4224)
190
191 for i in range(100):
192 # Creating noisy measurements
193 x_meas = gt_X[0] + np.random.normal(0.0, sigma_x_meas)
194 y_meas = gt_X[1] + np.random.normal(0.0, sigma_y_meas)
195 z = ColVector([x_meas, y_meas])
196
197 # Filtering using the UKF
198 ukf.filter(z, dt)
199
200 # Getting the filtered state vector
201 Xest = ukf.getXest()
202
203 # Computing the noisy velocity from the measueremnts
204 vX_meas = (x_meas - z_prec[0]) / dt
205 vY_meas = (y_meas - z_prec[1]) / dt
206
207 # Update the GUI if available
208 if has_gui:
209 plot.plot(0, 0, i, gt_X[0])
210 plot.plot(0, 1, i, x_meas)
211 plot.plot(0, 2, i, Xest[0])
212
213 plot.plot(1, 0, i, gt_vx)
214 plot.plot(1, 1, i, vX_meas)
215 plot.plot(1, 2, i, Xest[1])
216
217 plot.plot(2, 0, i, gt_X[1])
218 plot.plot(2, 1, i, y_meas)
219 plot.plot(2, 2, i, Xest[2])
220
221 plot.plot(3, 0, i, gt_vy)
222 plot.plot(3, 1, i, vY_meas)
223 plot.plot(3, 2, i, Xest[3])
224
225 # Updating ground-truth position
226 gt_X += gt_dX
227
228 # Updating last measurement for future computation of the noisy velocity
229 z_prec = z
230
231 print('Finished')
232 input('Press enter to quit')
ColVector add_state_vectors(a, b)
Matrix generate_Q_matrix(float dt)
ColVector fx(ColVector x, float dt)
ColVector hx(ColVector x)
ColVector residual_state_vectors(a, b)