Skip to content

Commit a4b89a9

Browse files
authored
Merge pull request #45 from Xylambda/doc/update_doc
DOC, ENH: improve docs. Fix minor typos
2 parents 155d907 + e51799e commit a4b89a9

2 files changed

Lines changed: 136 additions & 108 deletions

File tree

examples/extended.py

Lines changed: 0 additions & 108 deletions
This file was deleted.

examples/pendulum.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"""
2+
In this example we are going to see how to use the Extended Kalman Filter. The
3+
problem configuration is taken from the book "Bayesian Filtering and Smoothing"
4+
by Simo Särkkä: Example 5.1 of Bayesian Filtering and Smoothing.
5+
6+
The Unofficial associated code for the book was als used:
7+
https://github.com/EEA-sensors/Bayesian-Filtering-and-Smoothing
8+
9+
To generate the observations we have to use the equations that define the
10+
system:
11+
12+
xk = f(xk-1, uk-1) + qk
13+
zk = h(xk) + rk
14+
15+
Then, we set the parameters and the jacobian matrices.
16+
"""
17+
import numpy as np
18+
import matplotlib.pyplot as plt
19+
from scipy.special import erfcinv
20+
from kalmankit import ExtendedKalmanFilter
21+
22+
# constants
23+
DT = 0.01 # delta t
24+
G = 9.81
25+
np.random.seed(1)
26+
27+
28+
def f(xk, uk=None):
29+
arr = np.array([xk[0] + DT * xk[1], xk[1] - G * DT * np.sin(xk[0])])
30+
return arr
31+
32+
33+
def jacobian_A(xk, uk=None):
34+
"""
35+
Jacobian of f with respect to x.
36+
"""
37+
arr = np.array([[1, DT], [-G * np.cos(xk[0]) * DT, 1]])
38+
return arr
39+
40+
41+
def h(xk):
42+
return np.sin(xk[0])
43+
44+
45+
def jacobian_H(xk):
46+
"""
47+
Jacobian of h with respect to x.
48+
"""
49+
jac = np.array([[np.cos(xk[0]), 0.]])
50+
return jac
51+
52+
53+
def generate_observations(f, h, qk, rk, size=100):
54+
# -------------------------------------------------------------------------
55+
# initial mean estimate
56+
xk = np.array([1.5, 0.])
57+
58+
# define noises
59+
cholesky_qk = np.linalg.cholesky(qk)
60+
61+
# -------------------------------------------------------------------------
62+
# generate observations
63+
Z = np.empty((size, 2))
64+
X = np.empty((size, 2))
65+
for k in range(0, size):
66+
67+
noise = erfcinv(2 * np.random.rand(2))
68+
69+
xk_ = f(xk, None) + cholesky_qk @ noise
70+
zk = h(xk_) + np.sqrt(rk)
71+
Z[k] = np.sin(zk) + np.sqrt(rk) + noise
72+
X[k] = xk_
73+
74+
xk = xk_
75+
76+
time = np.arange(DT, (size + 1) * DT, DT)
77+
78+
return Z, X, time
79+
80+
81+
def main():
82+
# -------------------------------------------------------------------------
83+
xk = np.array([1.5, 0.])
84+
Pk = np.array(
85+
[
86+
[0.1, 0.],
87+
[0., 0.1]
88+
]
89+
)
90+
91+
qk = 0.01 * np.array(
92+
[
93+
[DT ** 3 / 3, DT ** 2 / 2],
94+
[DT ** 2 / 2, DT]
95+
]
96+
)
97+
rk = 0.1
98+
99+
Z, true_X, time = generate_observations(f, h, qk=qk, rk=0.01, size=500)
100+
101+
Q = np.stack([qk] * len(Z))
102+
R = np.ones(len(Z)) * rk
103+
104+
# -------------------------------------------------------------------------
105+
ekf = ExtendedKalmanFilter(
106+
xk=xk,
107+
Pk=Pk,
108+
Q=Q,
109+
R=R,
110+
f=f,
111+
h=h,
112+
jacobian_A=jacobian_A,
113+
jacobian_H=jacobian_H,
114+
)
115+
116+
states, errors = ekf.filter(Z[:, 0], None)
117+
118+
# -------------------------------------------------------------------------
119+
fig, ax = plt.subplots(figsize=(15, 7))
120+
ax.scatter(time, Z[:, 0], alpha=0.5, label="Observations")
121+
ax.plot(time, true_X[:, 0], color="red", label="True State")
122+
ax.plot(
123+
time,
124+
states[:, 0],
125+
color="orange",
126+
label="EKF Estimate",
127+
linestyle="--"
128+
)
129+
ax.grid(True, alpha=0.5)
130+
ax.set_title("EKF Pendulum", fontsize=25)
131+
ax.legend()
132+
plt.show()
133+
134+
135+
if __name__ == "__main__":
136+
main()

0 commit comments

Comments
 (0)