-
Notifications
You must be signed in to change notification settings - Fork 0
/
KF_with_localLevelModel.R
172 lines (119 loc) · 3.76 KB
/
KF_with_localLevelModel.R
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# カルマンフィルタの考え方
# Logics of Blue
# --------------------------------------------------------------------------------------
# ライブラリを使わないカルマンフィルタの実装
# --------------------------------------------------------------------------------------
localLevelModel <- function(y, xPre, pPre, sigmaW, sigmaV) {
# お手製のカルマンフィルタ関数
# 状態の予測(ローカルレベルモデルなので、予測値は、前期の値と同じ)
xForecast <- xPre
# 状態の予測誤差の分散
pForecast <- pPre + sigmaW
# カルマンゲイン
kGain <- pForecast / (pForecast + sigmaV)
# カルマンゲインを使って補正された状態
xFiltered <- xForecast + kGain * (y - xForecast)
# 補正された状態の予測誤差の分散
pFiltered <- (1 - kGain) * pForecast
# 結果の格納
result <- data.frame(
xFiltered = xFiltered,
pFiltered = pFiltered
)
return(result)
}
# ナイル川流量データで計算
Nile
# サンプルサイズ
N <- length(Nile)
# 状態の推定値
x <- numeric(N)
# 状態の予測誤差の分散
P <- numeric(N)
# 「状態」の初期値は0とします
x <- c(0, x)
x
# 「状態の予測誤差の分散」の初期値は1000にします
P <- c(1000, P)
P
# カルマンフィルタの逐次計算を行う
for(i in 1:N) {
kekka <- localLevelModel(Nile[i], x[i], P[i], sigmaW = 1000, sigmaV = 10000)
x[i + 1] <- kekka$xFiltered
P[i + 1] <- kekka$pFiltered
}
# 結果の図示
year <- 1871:1970
plot(
as.numeric(Nile) ~ year,
main="お手製カルマンフィルタ関数の結果",
ylab="ナイル川の流量",
type="l", lty=2
)
lines(x[-1] ~ year, type="l", col=2)
legend(
"topright",
legend = c("観測値", "フィルタリングされた結果"),
lty = c(2,1), col = c(1,2)
)
# --------------------------------------------------------------------------------------
# パラメタを変えたときの挙動
# --------------------------------------------------------------------------------------
calcState <- function(data, x0, P0, sigmaW, sigmaV){
# カルマンフィルタを使って、状態を一気に推定する
# サンプルサイズ
N <- length(data)
# 状態の予測誤差の分散
P <- numeric(N)
# 「状態の予測誤差の分散」の初期値の設定
P <- c(P0, P)
# 状態の推定値
x <- numeric(N)
# 「状態」の初期値の設定
x <- c(x0, x)
# カルマンフィルタの逐次計算を行う
for(i in 1:N) {
kekka <- localLevelModel(Nile[i],x[i],P[i], sigmaW = sigmaW, sigmaV = sigmaV)
x[i + 1] <- kekka$xFiltered
P[i + 1] <- kekka$pFiltered
}
# 推定された状態を返す
return(x[-1])
}
# 最初に計算したパラメタと同じ
x1 <- calcState(data=Nile, x0=0, P0=1000, sigmaW=1000, sigmaV=10000)
# 「状態の予測誤差の分散」の初期値を増やした
x2 <- calcState(data=Nile, x0=0, P0=100000, sigmaW=1000, sigmaV=10000)
# 状態方程式のノイズの分散をとても小さくした
x3 <- calcState(data=Nile, x0=1000, P0=0.1, sigmaW=0.001,sigmaV=1000000)
# 観測方程式におけるノイズの分散をとても小さくした
x4 <- calcState(data=Nile, x0=1000, P0=100000, sigmaW=10000, sigmaV=100)
# 結果
year <- 1871:1970
plot(
as.numeric(Nile) ~ year,
main="パラメタを変えた結果",
ylab="ナイル川の流量",
type="l", lty=2, lwd=2
)
lines(x1 ~ year, type="l", col=2)
lines(x2 ~ year, type="l", col=6)
lines(x3 ~ year, type="l", col=4)
lines(x4 ~ year, type="l", col=5)
legend(
"topright",
legend = c("観測値", "x1:P0小", "x2:P0大", "x3:観測誤差大", "x4:観測誤差小"),
lty = c(2,1,1,1,1), col = c(1,2,6,4,5)
)
# --------------------------------------------------------------------------------------
# dlmパッケージを使ったカルマンフィルタ
# --------------------------------------------------------------------------------------
library(dlm)
# 参考
x5 <- calcState(data=Nile, x0=0, P0=10000000, sigmaW=1000, sigmaV=10000)
# dlmのパラメタの設定
modelDlm <- dlmModPoly(order=1, m0=0, C0=10000000, dW = 1000, dV = 10000)
# カルマンフィルタの実行
Filter <- dlmFilter(Nile, modelDlm)
# 結果の比較
sum((Filter$m[-1] - x5)^2)