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