Merge lp:~oontvoo/kalmanfilterimpl/runnable_class into lp:kalmanfilterimpl
- runnable_class
- Merge into trunk
Proposed by
Vy Nguyen
Status: | Needs review |
---|---|
Proposed branch: | lp:~oontvoo/kalmanfilterimpl/runnable_class |
Merge into: | lp:kalmanfilterimpl |
Diff against target: |
723 lines (+588/-86) 6 files modified
.bzrignore (+2/-0) src/kalmanfilter/RunFilter.java (+13/-1) src/kalmanfilter/RunFilter2.java (+355/-0) src/kalmanfilter/impl/KalmanFilter.java (+33/-85) src/kalmanfilter/impl/KalmanFilter1.java (+84/-0) src/kalmanfilter/impl/KalmanFilter2.java (+101/-0) |
To merge this branch: | bzr merge lp:~oontvoo/kalmanfilterimpl/runnable_class |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vy Thuy Nguyen | Pending | ||
Review via email:
|
Commit message
Description of the change
- Refactor KalmanFilter into an interface.
- Add a runnable class that could generate/read data from text file which can then be used (by Excel) to graph the data
Probably needs A LOT of fixings. The graph doesn't look that good.
To post a comment you must log in.
Unmerged revisions
- 2. By vnguyen (<email address hidden>)
-
create a runnable class
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file '.bzrignore' | |||
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 | |||
3 | +++ .bzrignore 2012-07-07 07:30:27 +0000 | |||
4 | @@ -0,0 +1,2 @@ | |||
5 | 1 | nbproject/** | ||
6 | 2 | build/** | ||
7 | 0 | 3 | ||
8 | === modified file 'src/kalmanfilter/RunFilter.java' | |||
9 | --- src/kalmanfilter/RunFilter.java 2012-07-03 18:46:12 +0000 | |||
10 | +++ src/kalmanfilter/RunFilter.java 2012-07-07 07:30:27 +0000 | |||
11 | @@ -26,6 +26,8 @@ | |||
12 | 26 | import java.util.Arrays; | 26 | import java.util.Arrays; |
13 | 27 | import java.util.Collections; | 27 | import java.util.Collections; |
14 | 28 | import java.util.List; | 28 | import java.util.List; |
15 | 29 | import kalmanfilter.impl.KalmanFilter1; | ||
16 | 30 | import kalmanfilter.impl.KalmanFilter2; | ||
17 | 29 | 31 | ||
18 | 30 | /** | 32 | /** |
19 | 31 | * | 33 | * |
20 | @@ -67,6 +69,16 @@ | |||
21 | 67 | this.ay = ay; | 69 | this.ay = ay; |
22 | 68 | } | 70 | } |
23 | 69 | 71 | ||
24 | 72 | public String toString() | ||
25 | 73 | { | ||
26 | 74 | return "(" + x + ", " | ||
27 | 75 | + y + ", " | ||
28 | 76 | + vx + ", " | ||
29 | 77 | + vy + ", " | ||
30 | 78 | + ax + ", " | ||
31 | 79 | + ay + ", " | ||
32 | 80 | + t + ")"; | ||
33 | 81 | } | ||
34 | 70 | @Override | 82 | @Override |
35 | 71 | public int compareTo(Point o) | 83 | public int compareTo(Point o) |
36 | 72 | { | 84 | { |
37 | @@ -88,7 +100,7 @@ | |||
38 | 88 | List<Point> output = new ArrayList<Point>(inputs.size()); | 100 | List<Point> output = new ArrayList<Point>(inputs.size()); |
39 | 89 | 101 | ||
40 | 90 | //(Matrix X, Matrix P, Matrix Q, Matrix H, Matrix A, Matrix R) | 102 | //(Matrix X, Matrix P, Matrix Q, Matrix H, Matrix A, Matrix R) |
42 | 91 | KalmanFilter filter = new KalmanFilter(createX(inputs, 0), | 103 | KalmanFilter2 filter = new KalmanFilter2(createX(inputs, 0), |
43 | 92 | getP0(), | 104 | getP0(), |
44 | 93 | getQ(), | 105 | getQ(), |
45 | 94 | getH(), | 106 | getH(), |
46 | 95 | 107 | ||
47 | === added file 'src/kalmanfilter/RunFilter2.java' | |||
48 | --- src/kalmanfilter/RunFilter2.java 1970-01-01 00:00:00 +0000 | |||
49 | +++ src/kalmanfilter/RunFilter2.java 2012-07-07 07:30:27 +0000 | |||
50 | @@ -0,0 +1,355 @@ | |||
51 | 1 | /** | ||
52 | 2 | * Implementation of the Kalman Filter | ||
53 | 3 | * Copyright (C) 2012 Vy Nguyen | ||
54 | 4 | * | ||
55 | 5 | * This library is free software; you can redistribute it and/or | ||
56 | 6 | * modify it under the terms of the GNU Library General Public | ||
57 | 7 | * License as published by the Free Software Foundation; either | ||
58 | 8 | * version 2 of the License, or (at your option) any later version. | ||
59 | 9 | * | ||
60 | 10 | * This library is distributed in the hope that it will be useful, | ||
61 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
62 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
63 | 13 | * Library General Public License for more details. | ||
64 | 14 | * | ||
65 | 15 | * You should have received a copy of the GNU Library General Public | ||
66 | 16 | * License along with this library; if not, write to the | ||
67 | 17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, | ||
68 | 18 | * Boston, MA 02110-1301, USA. | ||
69 | 19 | */ | ||
70 | 20 | |||
71 | 21 | package kalmanfilter; | ||
72 | 22 | |||
73 | 23 | import Jama.Matrix; | ||
74 | 24 | import java.io.*; | ||
75 | 25 | import java.util.*; | ||
76 | 26 | import kalmanfilter.RunFilter.Point; | ||
77 | 27 | import kalmanfilter.impl.KalmanFilter; | ||
78 | 28 | import kalmanfilter.impl.KalmanFilter1; | ||
79 | 29 | |||
80 | 30 | /** | ||
81 | 31 | * | ||
82 | 32 | * @author Vy Thao Nguyen | ||
83 | 33 | */ | ||
84 | 34 | public class RunFilter2 | ||
85 | 35 | { | ||
86 | 36 | private static List<Point> createIdealValues(int count, double dt, | ||
87 | 37 | double ax0, double ay0, | ||
88 | 38 | double vx0, double vy0, | ||
89 | 39 | double x0, double y0) | ||
90 | 40 | { | ||
91 | 41 | // x = x0 + v * dt | ||
92 | 42 | // v = v0 + a * dt | ||
93 | 43 | |||
94 | 44 | List<Point> ret = new ArrayList<Point>(); | ||
95 | 45 | ret.add(new Point(x0, y0, 0, vx0, vy0, ax0, ay0)); // first point | ||
96 | 46 | --count; | ||
97 | 47 | |||
98 | 48 | double t = dt; | ||
99 | 49 | for (int n = 0; n < count; ++n) | ||
100 | 50 | { | ||
101 | 51 | double vx = vx0 + ax0 * t; | ||
102 | 52 | double x = x0 + vx0 * t + 0.5 * ay0 * t * t; | ||
103 | 53 | |||
104 | 54 | double vy = vy0 + ay0 * t; | ||
105 | 55 | double y = y0 + vy0 * t + 0.5 * ay0 * t * t; | ||
106 | 56 | |||
107 | 57 | ret.add(new Point(x, y, t, vx, vy, ax0, ay0)); | ||
108 | 58 | t += dt; | ||
109 | 59 | } | ||
110 | 60 | return ret; | ||
111 | 61 | } | ||
112 | 62 | |||
113 | 63 | private static void writeToFile(String fileName, List<Point> toWrite) throws IOException | ||
114 | 64 | { | ||
115 | 65 | StringBuilder bd = new StringBuilder(); | ||
116 | 66 | |||
117 | 67 | for (Point p : toWrite) | ||
118 | 68 | bd.append(p.x).append(" ").append(p.y).append(System.getProperty("line.separator")); | ||
119 | 69 | |||
120 | 70 | FileWriter fstream = new FileWriter(fileName); | ||
121 | 71 | BufferedWriter out = new BufferedWriter(fstream); | ||
122 | 72 | out.write(bd.toString()); | ||
123 | 73 | out.close(); | ||
124 | 74 | } | ||
125 | 75 | |||
126 | 76 | private static double parseArg(String args[]) | ||
127 | 77 | { | ||
128 | 78 | if (args.length != 1) | ||
129 | 79 | { | ||
130 | 80 | System.out.println("default noise: " + DEFAULT_NOISE); | ||
131 | 81 | return DEFAULT_NOISE; | ||
132 | 82 | } | ||
133 | 83 | return Double.parseDouble(args[0]); | ||
134 | 84 | } | ||
135 | 85 | |||
136 | 86 | private static List<Point> readSensorData(double dt, double ax) | ||
137 | 87 | { | ||
138 | 88 | try | ||
139 | 89 | { | ||
140 | 90 | Scanner input = new Scanner(new File(SENSOR)); | ||
141 | 91 | double t = 0; | ||
142 | 92 | List<Point> ret = new ArrayList<Point>(); | ||
143 | 93 | |||
144 | 94 | while(input.hasNext()) | ||
145 | 95 | { | ||
146 | 96 | double x = Double.parseDouble(input.next()); | ||
147 | 97 | double y = Double.parseDouble(input.next()); | ||
148 | 98 | ret.add(new Point(x, y, t, 0, 0, ax, ax)); | ||
149 | 99 | t += dt; | ||
150 | 100 | } | ||
151 | 101 | return ret; | ||
152 | 102 | } | ||
153 | 103 | catch (FileNotFoundException ex) | ||
154 | 104 | { | ||
155 | 105 | System.out.println("File not found: " + SENSOR); | ||
156 | 106 | System.exit(2); | ||
157 | 107 | } | ||
158 | 108 | return null; | ||
159 | 109 | } | ||
160 | 110 | |||
161 | 111 | public static void main (String args[]) throws IOException | ||
162 | 112 | { | ||
163 | 113 | double ax = 2.5; | ||
164 | 114 | double ay = 1.256; | ||
165 | 115 | double dt = 0.8; | ||
166 | 116 | |||
167 | 117 | System.out.println("computing the ideal values"); | ||
168 | 118 | List<Point> idealData = createIdealValues(9, dt, | ||
169 | 119 | ax, ay , | ||
170 | 120 | 0, 0, | ||
171 | 121 | 0, 0); | ||
172 | 122 | writeToFile(IDEAL, idealData); | ||
173 | 123 | |||
174 | 124 | System.out.println("reading sensor data"); | ||
175 | 125 | List<Point> sensor = readSensorData(dt, ax); | ||
176 | 126 | double noise = parseArg(args); | ||
177 | 127 | |||
178 | 128 | System.out.println("filtering sensor data"); | ||
179 | 129 | writeToFile(CORRECTED, filter(sensor, noise)); | ||
180 | 130 | } | ||
181 | 131 | /** | ||
182 | 132 | * | ||
183 | 133 | * @param inputs | ||
184 | 134 | * @param sensorNoise | ||
185 | 135 | * @return | ||
186 | 136 | */ | ||
187 | 137 | public static List<Point> filter(List<Point> inputs, | ||
188 | 138 | double sensorNoise) | ||
189 | 139 | { | ||
190 | 140 | // sort the points in time order | ||
191 | 141 | Collections.sort(inputs); | ||
192 | 142 | |||
193 | 143 | // compute the velocity | ||
194 | 144 | List<Point> updated = computeVAndA(inputs); | ||
195 | 145 | |||
196 | 146 | // get the standard deviation of acceleration | ||
197 | 147 | double sd = getSd(updated); | ||
198 | 148 | |||
199 | 149 | List<Point> output = new ArrayList<Point>(inputs.size()); | ||
200 | 150 | output.add(inputs.get(0)); // first point is the same | ||
201 | 151 | |||
202 | 152 | Matrix P0 = getP0(); | ||
203 | 153 | Matrix H = getH(); | ||
204 | 154 | Matrix R = createR(sensorNoise); | ||
205 | 155 | |||
206 | 156 | KalmanFilter filterX = new KalmanFilter1(createXx(updated.get(0)), | ||
207 | 157 | P0, | ||
208 | 158 | H, | ||
209 | 159 | R | ||
210 | 160 | ); | ||
211 | 161 | |||
212 | 162 | KalmanFilter filterY = new KalmanFilter1(createXy(updated.get(0)), | ||
213 | 163 | P0, | ||
214 | 164 | H, | ||
215 | 165 | R | ||
216 | 166 | ); | ||
217 | 167 | for (int n = 1; n < updated.size(); ++n) | ||
218 | 168 | { | ||
219 | 169 | Point cur = updated.get(n); | ||
220 | 170 | Matrix A = createA(cur); | ||
221 | 171 | Matrix G = createG(cur); | ||
222 | 172 | Matrix Gx = G.times(cur.ax); | ||
223 | 173 | Matrix Gy = G.times(cur.ay); | ||
224 | 174 | Matrix Q = getQ(G, sd); | ||
225 | 175 | |||
226 | 176 | Matrix correctedX = filterX.timeUpdate(A, Q, Gx).messurementUpdate(createXx(cur)); | ||
227 | 177 | Matrix correctedY = filterY.timeUpdate(A, Q, Gy).messurementUpdate(createXy(cur)); | ||
228 | 178 | |||
229 | 179 | output.add(decodeXY(correctedX, correctedY, inputs.get(n))); | ||
230 | 180 | } | ||
231 | 181 | |||
232 | 182 | return output; | ||
233 | 183 | } | ||
234 | 184 | private static double getSd(List<Point> p) | ||
235 | 185 | { | ||
236 | 186 | // for now, the accelerations are const | ||
237 | 187 | return 0; | ||
238 | 188 | } | ||
239 | 189 | |||
240 | 190 | /** | ||
241 | 191 | * TODO: correctly decode the state matrix into a point. | ||
242 | 192 | * (that is, retrieve a and v) | ||
243 | 193 | * | ||
244 | 194 | * @param X the corrected state matrix | ||
245 | 195 | * @return the corrected value of (x,y,t) | ||
246 | 196 | */ | ||
247 | 197 | private static Point decodeXY(Matrix X, Matrix Y, Point input) | ||
248 | 198 | { | ||
249 | 199 | return new Point(X.get(0, 0) / 10, // x | ||
250 | 200 | Y.get(0, 0) / 10, // y | ||
251 | 201 | X.get(1, 0) / 10, // vx | ||
252 | 202 | Y.get(1, 0) / 10, // vy | ||
253 | 203 | input.ax, // unchanged | ||
254 | 204 | input.ay, // unchanged | ||
255 | 205 | input.t // unchanged | ||
256 | 206 | ); | ||
257 | 207 | } | ||
258 | 208 | |||
259 | 209 | /** | ||
260 | 210 | * TODO: correctly encode the point (velocity, acceleration, etc.) into a matrix. | ||
261 | 211 | * | ||
262 | 212 | * Vector x = {x, vx , ax, ay} | ||
263 | 213 | * | ||
264 | 214 | * @param points | ||
265 | 215 | * @param currentIndex index of the point the filter is currently looking at | ||
266 | 216 | * @return the input vector X | ||
267 | 217 | */ | ||
268 | 218 | private static Matrix createXx(Point current) | ||
269 | 219 | { | ||
270 | 220 | Matrix x = new Matrix(2, 1); | ||
271 | 221 | |||
272 | 222 | x.set(0, 0, current.x); | ||
273 | 223 | x.set(1, 0, current.vx); | ||
274 | 224 | |||
275 | 225 | return x; | ||
276 | 226 | } | ||
277 | 227 | |||
278 | 228 | private static Matrix createXy(Point cur) | ||
279 | 229 | { | ||
280 | 230 | Matrix y = new Matrix(2, 1); | ||
281 | 231 | |||
282 | 232 | y.set(0, 0, cur.y); | ||
283 | 233 | y.set(1, 0, cur.vy); | ||
284 | 234 | |||
285 | 235 | return y; | ||
286 | 236 | } | ||
287 | 237 | /** | ||
288 | 238 | * Compute the instant velocity and acceleration of each point | ||
289 | 239 | * <b>This method makes the following assumptions:</b> | ||
290 | 240 | * 1. Acceleration of a Point object is constant and was properly set in the constructor. | ||
291 | 241 | * 2. The velocity of the first point is either 0 or has already been correctly calculated. | ||
292 | 242 | * | ||
293 | 243 | * TODO: take variable acceleration into account. | ||
294 | 244 | * | ||
295 | 245 | * @param points | ||
296 | 246 | * @return list of point with correct v and a | ||
297 | 247 | */ | ||
298 | 248 | private static List<Point> computeVAndA(List<Point> points) | ||
299 | 249 | { | ||
300 | 250 | int size = points.size(); | ||
301 | 251 | double vx_0 = points.get(0).vx; | ||
302 | 252 | double vy_0 = points.get(0).vy; | ||
303 | 253 | double ax_0 = points.get(0).ax; | ||
304 | 254 | double ay_0 = points.get(0).ay; | ||
305 | 255 | |||
306 | 256 | List<Point> result = new ArrayList<Point> (size); | ||
307 | 257 | result.add(points.get(0)); | ||
308 | 258 | |||
309 | 259 | for (int i = 1; i < size; ++i) | ||
310 | 260 | { | ||
311 | 261 | double dt = points.get(i).t - points.get(0).t; | ||
312 | 262 | double vx = vx_0 + ax_0 * dt; | ||
313 | 263 | double vy = vy_0 + ay_0 * dt; | ||
314 | 264 | |||
315 | 265 | result.add(new Point(points.get(i).x, | ||
316 | 266 | points.get(i).y, | ||
317 | 267 | dt, | ||
318 | 268 | vx, | ||
319 | 269 | vy, | ||
320 | 270 | points.get(i).ax, | ||
321 | 271 | points.get(i).ay | ||
322 | 272 | )); | ||
323 | 273 | } | ||
324 | 274 | |||
325 | 275 | return result; | ||
326 | 276 | } | ||
327 | 277 | |||
328 | 278 | /** | ||
329 | 279 | * TODO: | ||
330 | 280 | * Same as createX(...)'s | ||
331 | 281 | * | ||
332 | 282 | * @param inputs | ||
333 | 283 | * @param currentIndex | ||
334 | 284 | * @return the state matrix A based on the the current and past position | ||
335 | 285 | */ | ||
336 | 286 | private static Matrix createA(Point current) | ||
337 | 287 | { | ||
338 | 288 | Matrix A = Matrix.identity(2, 2); | ||
339 | 289 | A.set(0, 1, current.t); | ||
340 | 290 | return A; | ||
341 | 291 | } | ||
342 | 292 | |||
343 | 293 | /** | ||
344 | 294 | * | ||
345 | 295 | * @param error the sensor's error | ||
346 | 296 | * @return the measurement noise covariance matrix | ||
347 | 297 | */ | ||
348 | 298 | private static Matrix createR (double error) | ||
349 | 299 | { | ||
350 | 300 | Matrix R = new Matrix(1,1); | ||
351 | 301 | R.set(0, 0, error); | ||
352 | 302 | return R; | ||
353 | 303 | } | ||
354 | 304 | /** | ||
355 | 305 | * | ||
356 | 306 | * @return the initial P matrix, which contains all zeroes | ||
357 | 307 | */ | ||
358 | 308 | private static Matrix getP0() | ||
359 | 309 | { | ||
360 | 310 | double m2x2[][] = new double[VARS_COUNT][VARS_COUNT]; | ||
361 | 311 | |||
362 | 312 | for (int n = 0; n < VARS_COUNT; ++n) | ||
363 | 313 | Arrays.fill(m2x2[n], 0); | ||
364 | 314 | |||
365 | 315 | return Matrix.constructWithCopy(m2x2); | ||
366 | 316 | } | ||
367 | 317 | |||
368 | 318 | /** | ||
369 | 319 | * TODO: | ||
370 | 320 | * correctly determine this | ||
371 | 321 | * | ||
372 | 322 | * @return the process noise covariance matrix | ||
373 | 323 | */ | ||
374 | 324 | private static Matrix getQ(Matrix G, double sd) | ||
375 | 325 | { | ||
376 | 326 | return G.times(G.transpose()).times(sd * sd); | ||
377 | 327 | } | ||
378 | 328 | |||
379 | 329 | /** | ||
380 | 330 | * | ||
381 | 331 | * @return the [1 by n] observation matrix: [1 0 0 0 .... 0 ] | ||
382 | 332 | */ | ||
383 | 333 | private static Matrix getH() | ||
384 | 334 | { | ||
385 | 335 | Matrix H = new Matrix(1,2); | ||
386 | 336 | H.set(0, 0, 1); | ||
387 | 337 | return H; | ||
388 | 338 | } | ||
389 | 339 | |||
390 | 340 | private static Matrix createG(Point p) | ||
391 | 341 | { | ||
392 | 342 | Matrix ret = new Matrix(2, 1); | ||
393 | 343 | ret.set(0, 0, p.t * p.t / 2); | ||
394 | 344 | ret.set(1, 0, p.t); | ||
395 | 345 | |||
396 | 346 | return ret; | ||
397 | 347 | } | ||
398 | 348 | |||
399 | 349 | private static final String IDEAL = "C:\\Users\\NhatPhan\\Desktop\\Choe\\Sensor Research\\data\\ideal.txt"; | ||
400 | 350 | private static final String SENSOR = "C:\\Users\\NhatPhan\\Desktop\\Choe\\Sensor Research\\data\\sensor.txt"; | ||
401 | 351 | private static final String CORRECTED = "C:\\Users\\NhatPhan\\Desktop\\Choe\\Sensor Research\\data\\corrected.txt"; | ||
402 | 352 | |||
403 | 353 | private static final double DEFAULT_NOISE = 0.5; | ||
404 | 354 | private static final int VARS_COUNT = 2; | ||
405 | 355 | } | ||
406 | 0 | 356 | ||
407 | === modified file 'src/kalmanfilter/impl/KalmanFilter.java' | |||
408 | --- src/kalmanfilter/impl/KalmanFilter.java 2012-07-03 18:46:12 +0000 | |||
409 | +++ src/kalmanfilter/impl/KalmanFilter.java 2012-07-07 07:30:27 +0000 | |||
410 | @@ -1,85 +1,33 @@ | |||
496 | 1 | /** | 1 | /** |
497 | 2 | * Implementation of the Kalman Filter | 2 | * Implementation of the Kalman Filter |
498 | 3 | * Copyright (C) 2012 Vy Nguyen | 3 | * Copyright (C) 2012 Vy Nguyen |
499 | 4 | * | 4 | * |
500 | 5 | * This library is free software; you can redistribute it and/or | 5 | * This library is free software; you can redistribute it and/or |
501 | 6 | * modify it under the terms of the GNU Library General Public | 6 | * modify it under the terms of the GNU Library General Public |
502 | 7 | * License as published by the Free Software Foundation; either | 7 | * License as published by the Free Software Foundation; either |
503 | 8 | * version 2 of the License, or (at your option) any later version. | 8 | * version 2 of the License, or (at your option) any later version. |
504 | 9 | * | 9 | * |
505 | 10 | * This library is distributed in the hope that it will be useful, | 10 | * This library is distributed in the hope that it will be useful, |
506 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
507 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
508 | 13 | * Library General Public License for more details. | 13 | * Library General Public License for more details. |
509 | 14 | * | 14 | * |
510 | 15 | * You should have received a copy of the GNU Library General Public | 15 | * You should have received a copy of the GNU Library General Public |
511 | 16 | * License along with this library; if not, write to the | 16 | * License along with this library; if not, write to the |
512 | 17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, | 17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
513 | 18 | * Boston, MA 02110-1301, USA. | 18 | * Boston, MA 02110-1301, USA. |
514 | 19 | */ | 19 | */ |
515 | 20 | package kalmanfilter.impl; | 20 | |
516 | 21 | 21 | package kalmanfilter.impl; | |
517 | 22 | import Jama.Matrix; | 22 | |
518 | 23 | 23 | import Jama.Matrix; | |
519 | 24 | /** | 24 | |
520 | 25 | * Implements the Kalman Filter using Jama's Matrix API | 25 | /** |
521 | 26 | * | 26 | * |
522 | 27 | * @author Vy Thao Nguyen | 27 | * @author Vy Thao Nguyen |
523 | 28 | */ | 28 | */ |
524 | 29 | public class KalmanFilter | 29 | public interface KalmanFilter |
525 | 30 | { | 30 | { |
526 | 31 | // variable states | 31 | public KalmanFilter timeUpdate(Matrix A, Matrix Q, Matrix Ga); |
527 | 32 | private Matrix X; // input vector | 32 | public Matrix messurementUpdate(Matrix Xk); |
528 | 33 | private Matrix P; // state covariance matrix | 33 | } |
444 | 34 | private Matrix Q; // process noise covariance matrix | ||
445 | 35 | |||
446 | 36 | // const states | ||
447 | 37 | private final Matrix H; // observation matrix | ||
448 | 38 | private final Matrix I; // the identity matrix | ||
449 | 39 | private final Matrix A; // state matrix | ||
450 | 40 | private final Matrix R; // measurement noise covariance matrix | ||
451 | 41 | private final Matrix B; // control input matrix | ||
452 | 42 | private final Matrix u; | ||
453 | 43 | |||
454 | 44 | public KalmanFilter(Matrix X, Matrix P, Matrix Q, Matrix H, Matrix A, Matrix R, Matrix B, Matrix u) | ||
455 | 45 | { | ||
456 | 46 | this.X = X; | ||
457 | 47 | this.P = P; | ||
458 | 48 | this.Q = Q; | ||
459 | 49 | this.H = H; | ||
460 | 50 | this.A = A; | ||
461 | 51 | this.R = R; | ||
462 | 52 | this.B = B; | ||
463 | 53 | this.u = u; | ||
464 | 54 | |||
465 | 55 | I = Matrix.identity(P.getRowDimension(), P.getColumnDimension()); | ||
466 | 56 | } | ||
467 | 57 | |||
468 | 58 | public KalmanFilter timeUpdate() | ||
469 | 59 | { | ||
470 | 60 | X = A.times(X).plus(B.times(u)); | ||
471 | 61 | P = A.times(P).times(A.transpose()).plus(Q); | ||
472 | 62 | return this; // for easy chaining | ||
473 | 63 | } | ||
474 | 64 | |||
475 | 65 | /** | ||
476 | 66 | * | ||
477 | 67 | * @param Xk: the new measurement (sensor reading) | ||
478 | 68 | * @return | ||
479 | 69 | */ | ||
480 | 70 | public Matrix messurementUpdate(Matrix Xk) | ||
481 | 71 | { | ||
482 | 72 | // compute kalman gain | ||
483 | 73 | //Kk+1 = PkHT(H PkHT + R)-1 | ||
484 | 74 | Matrix K = P.times(H.transpose()).times((H.times(P).times(H.trace()).plus(R)).inverse()); | ||
485 | 75 | |||
486 | 76 | // update x | ||
487 | 77 | // xk+1 = xk + K(zk+1 − Hxk ) | ||
488 | 78 | X = X.plus(K.times((Xk.times(H)).minus(H.times(X)))); | ||
489 | 79 | |||
490 | 80 | // update P | ||
491 | 81 | P = (I.minus(K.times(H))).times(P); | ||
492 | 82 | |||
493 | 83 | return X; | ||
494 | 84 | } | ||
495 | 85 | } | ||
529 | 86 | 34 | ||
530 | === added file 'src/kalmanfilter/impl/KalmanFilter1.java' | |||
531 | --- src/kalmanfilter/impl/KalmanFilter1.java 1970-01-01 00:00:00 +0000 | |||
532 | +++ src/kalmanfilter/impl/KalmanFilter1.java 2012-07-07 07:30:27 +0000 | |||
533 | @@ -0,0 +1,84 @@ | |||
534 | 1 | /** | ||
535 | 2 | * Implementation of the Kalman Filter | ||
536 | 3 | * Copyright (C) 2012 Vy Nguyen | ||
537 | 4 | * | ||
538 | 5 | * This library is free software; you can redistribute it and/or | ||
539 | 6 | * modify it under the terms of the GNU Library General Public | ||
540 | 7 | * License as published by the Free Software Foundation; either | ||
541 | 8 | * version 2 of the License, or (at your option) any later version. | ||
542 | 9 | * | ||
543 | 10 | * This library is distributed in the hope that it will be useful, | ||
544 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
545 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
546 | 13 | * Library General Public License for more details. | ||
547 | 14 | * | ||
548 | 15 | * You should have received a copy of the GNU Library General Public | ||
549 | 16 | * License along with this library; if not, write to the | ||
550 | 17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, | ||
551 | 18 | * Boston, MA 02110-1301, USA. | ||
552 | 19 | */ | ||
553 | 20 | package kalmanfilter.impl; | ||
554 | 21 | |||
555 | 22 | import Jama.Matrix; | ||
556 | 23 | |||
557 | 24 | /** | ||
558 | 25 | * Implements the original Kalman Filter using Jama's Matrix API | ||
559 | 26 | * | ||
560 | 27 | * *original* being all states are encoded into one state matrix. | ||
561 | 28 | * If you have difficulty encoding all states into one, you might want to consider | ||
562 | 29 | * using KalmanFilter2.java | ||
563 | 30 | * | ||
564 | 31 | * @author Vy Thao Nguyen | ||
565 | 32 | */ | ||
566 | 33 | public class KalmanFilter1 implements KalmanFilter | ||
567 | 34 | { | ||
568 | 35 | // variable states | ||
569 | 36 | private Matrix X; // input vector | ||
570 | 37 | private Matrix P; // state covariance matrix | ||
571 | 38 | |||
572 | 39 | // const states | ||
573 | 40 | private final Matrix H; // observation matrix | ||
574 | 41 | private final Matrix I; // the identity matrix | ||
575 | 42 | private final Matrix R; // measurement noise covariance matrix | ||
576 | 43 | |||
577 | 44 | public KalmanFilter1(Matrix X, Matrix P, Matrix H, Matrix R) | ||
578 | 45 | { | ||
579 | 46 | this.X = X; | ||
580 | 47 | this.P = P; | ||
581 | 48 | this.H = H; | ||
582 | 49 | this.R = R; | ||
583 | 50 | |||
584 | 51 | I = Matrix.identity(P.getRowDimension(), P.getColumnDimension()); | ||
585 | 52 | } | ||
586 | 53 | |||
587 | 54 | @Override | ||
588 | 55 | public KalmanFilter1 timeUpdate(Matrix A, Matrix Q, Matrix Ga) | ||
589 | 56 | { | ||
590 | 57 | // Matrix Ax = A.times(X); | ||
591 | 58 | X = (A.times(X)).plus(Ga); | ||
592 | 59 | P = A.times(P).times(A.transpose()).plus(Q); | ||
593 | 60 | return this; // for easy chaining | ||
594 | 61 | } | ||
595 | 62 | |||
596 | 63 | /** | ||
597 | 64 | * | ||
598 | 65 | * @param Xk: the new measurement (sensor reading) | ||
599 | 66 | * @return | ||
600 | 67 | */ | ||
601 | 68 | @Override | ||
602 | 69 | public Matrix messurementUpdate(Matrix Xk) | ||
603 | 70 | { | ||
604 | 71 | // compute kalman gain | ||
605 | 72 | //Kk+1 = PkHT(H PkHT + R)-1 | ||
606 | 73 | Matrix K = (P.times(H.transpose())).times((H.times(P).times(H.transpose()).plus(R)).inverse()); | ||
607 | 74 | |||
608 | 75 | // update x | ||
609 | 76 | // xk+1 = xk + K(zk+1 − Hxk ) | ||
610 | 77 | X = X.plus(K.times((H.times(Xk)).minus(H.times(X)))); | ||
611 | 78 | |||
612 | 79 | // update P | ||
613 | 80 | P = (I.minus(K.times(H))).times(P); | ||
614 | 81 | |||
615 | 82 | return X; | ||
616 | 83 | } | ||
617 | 84 | } | ||
618 | 0 | 85 | ||
619 | === added file 'src/kalmanfilter/impl/KalmanFilter2.java' | |||
620 | --- src/kalmanfilter/impl/KalmanFilter2.java 1970-01-01 00:00:00 +0000 | |||
621 | +++ src/kalmanfilter/impl/KalmanFilter2.java 2012-07-07 07:30:27 +0000 | |||
622 | @@ -0,0 +1,101 @@ | |||
623 | 1 | /** | ||
624 | 2 | * Implementation of the Kalman Filter | ||
625 | 3 | * Copyright (C) 2012 Vy Nguyen | ||
626 | 4 | * | ||
627 | 5 | * This library is free software; you can redistribute it and/or | ||
628 | 6 | * modify it under the terms of the GNU Library General Public | ||
629 | 7 | * License as published by the Free Software Foundation; either | ||
630 | 8 | * version 2 of the License, or (at your option) any later version. | ||
631 | 9 | * | ||
632 | 10 | * This library is distributed in the hope that it will be useful, | ||
633 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
634 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
635 | 13 | * Library General Public License for more details. | ||
636 | 14 | * | ||
637 | 15 | * You should have received a copy of the GNU Library General Public | ||
638 | 16 | * License along with this library; if not, write to the | ||
639 | 17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, | ||
640 | 18 | * Boston, MA 02110-1301, USA. | ||
641 | 19 | */ | ||
642 | 20 | |||
643 | 21 | package kalmanfilter.impl; | ||
644 | 22 | |||
645 | 23 | import Jama.Matrix; | ||
646 | 24 | |||
647 | 25 | /** | ||
648 | 26 | * Implements the filter with different 'groups' of states encoded in different | ||
649 | 27 | * matrices. | ||
650 | 28 | * | ||
651 | 29 | * For eg., | ||
652 | 30 | * For a system with the following states: | ||
653 | 31 | * x: horizontal position | ||
654 | 32 | * y: vertical position | ||
655 | 33 | * u: horizontal velocity | ||
656 | 34 | * v: vertical velocity | ||
657 | 35 | * | ||
658 | 36 | * divided into two groups: X1 = {x, u}, X2 = {y, v} | ||
659 | 37 | * | ||
660 | 38 | * TODO: make the number of 'groups' variable. Currently, this is only | ||
661 | 39 | * capable of handling a fixed 2 groups | ||
662 | 40 | * | ||
663 | 41 | * @author Vy Thao Nguyen | ||
664 | 42 | */ | ||
665 | 43 | public class KalmanFilter2 //implements KalmanFilter | ||
666 | 44 | { | ||
667 | 45 | // variable states | ||
668 | 46 | private Matrix X; // input vector | ||
669 | 47 | private Matrix P; // state covariance matrix | ||
670 | 48 | private Matrix Q; // process noise covariance matrix | ||
671 | 49 | |||
672 | 50 | // const states | ||
673 | 51 | private final Matrix H; // observation matrix | ||
674 | 52 | private final Matrix I; // the identity matrix | ||
675 | 53 | private final Matrix A; // state matrix | ||
676 | 54 | private final Matrix R; // measurement noise covariance matrix | ||
677 | 55 | private final Matrix B; // control input matrix | ||
678 | 56 | private final Matrix u; | ||
679 | 57 | |||
680 | 58 | public KalmanFilter2(Matrix X, Matrix P, Matrix Q, Matrix H, Matrix A, Matrix R, Matrix B, Matrix u) | ||
681 | 59 | { | ||
682 | 60 | this.X = X; | ||
683 | 61 | this.P = P; | ||
684 | 62 | this.Q = Q; | ||
685 | 63 | this.H = H; | ||
686 | 64 | this.A = A; | ||
687 | 65 | this.R = R; | ||
688 | 66 | this.B = B; | ||
689 | 67 | this.u = u; | ||
690 | 68 | |||
691 | 69 | I = Matrix.identity(P.getRowDimension(), P.getColumnDimension()); | ||
692 | 70 | } | ||
693 | 71 | |||
694 | 72 | // @Override | ||
695 | 73 | public KalmanFilter2 timeUpdate() | ||
696 | 74 | { | ||
697 | 75 | X = A.times(X).plus(B.times(u)); | ||
698 | 76 | P = A.times(P).times(A.transpose()).plus(Q); | ||
699 | 77 | return this; // for easy chaining | ||
700 | 78 | } | ||
701 | 79 | |||
702 | 80 | /** | ||
703 | 81 | * | ||
704 | 82 | * @param Xk: the new measurement (sensor reading) | ||
705 | 83 | * @return | ||
706 | 84 | */ | ||
707 | 85 | // @Override | ||
708 | 86 | public Matrix messurementUpdate(Matrix Xk) | ||
709 | 87 | { | ||
710 | 88 | // compute kalman gain | ||
711 | 89 | //Kk+1 = PkHT(H PkHT + R)-1 | ||
712 | 90 | Matrix K = P.times(H.transpose()).times((H.times(P).times(H.trace()).plus(R)).inverse()); | ||
713 | 91 | |||
714 | 92 | // update x | ||
715 | 93 | // xk+1 = xk + K(zk+1 − Hxk ) | ||
716 | 94 | X = X.plus(K.times((Xk.times(H)).minus(H.times(X)))); | ||
717 | 95 | |||
718 | 96 | // update P | ||
719 | 97 | P = (I.minus(K.times(H))).times(P); | ||
720 | 98 | |||
721 | 99 | return X; | ||
722 | 100 | } | ||
723 | 101 | } |