1. 개요
피터 스월링이 개발하고 루돌프 칼만이 제안한 필터로 오차(외란)를 가지는 관측치로부터 시스템의 상태를 추정하거나 제어하기 위한 알고리즘.가령 로켓의 연소기와 같이 정밀한 제어가 필요하지만 극한의 상황으로 인하여 센싱이 불가능한 시스템이 있다고 가정하자. 이 경우 우리는 로켓의 동특성을 나타낸 수학적 모델과 계측이 가능한 다른 계측치들로부터 센싱이 불가능했던 연소기의 상태에 대한 상태 추정이 가능하다. 하지만 이렇게 추정된 추정치는 모델 부정확성으로 인한 오차와 계측 한계로 인한 오차 두 가지 오차를 포함하고 있을 것이다. 이러한 오차를 최소화하기 위해선 피드백 제어를 통해 추정기를 보정해주는 과정이 필요하다. 실제 시스템에 대한 상태공간방정식 및 출력방정식이 아래와 같을 때
[math(\dot{x}=Ax + \omega)]
[math(y=Hx + v)]
피드백 제어를 포함한 상태 추정기의 상태공간방정식 및 출력방정식은 아래와 같이 유도할 수 있으며
[math(\dot{\hat{x}}=A\hat{x} + K(y - \hat{y}))]
[math(\hat{y}=H\hat{x})]
상기 두식의 차를 구함으로써 실제 시스템과 상태추정기의 오차 [math(\varepsilon)]는 아래와 같이 정리할 수 있다.
[math(\dot{\varepsilon}=(A-KH)\varepsilon)]
[math(\varepsilon=e^{(A-KH)})]
즉 [math((A-KH))]가 0보다 작다면 오차가 점진적으로 0에 도달할 수 있음을 알 수 있으며, 피드백 제어 게인 [math(K)]를 이용하여 그 속도를 조절할 수 있음을 알 수 있다. 칼만필터는 모델 오차와 계측 오차 사이 가중치를 통해 최적의 피드백 제어 게인 [math(K)]를 결정한다.
[math(K=PH^{T}(HPH^{T}+R)^{-1})]
모델 오차의 공분산 [math(P)]가 0에 매우 가깝다면 [math(K)]도 0이 되기 때문에 상태 추정기의 식은 수학적 모델과 같아지며 반대로 계측 오차의 공분산 [math(R)]이 0에 매우 가깝다면 [math(K)]는 [math(\frac{1}{H})]가 되어 상태 추정기의 식은 계측치와 같아진다. 이렇듯 칼만필터의 핵심은 칼만이득 [math(K)]를 계산을 위한 공분산 계산이라고 할 수 있으며, 공분산 계산 시 근사화 대상이 확률분포가 통과하는 비선형 함수이면 EKF, 확률분포 자체이면 UKF 등으로 부른다. 당연한 이야기지만 필터(재귀)이기 때문에 메모리가 크게 필요치는 않다. 하지만 수식 및 확률분포의 복잡성에 따라 연산량은 크게 달라질 수 있다. 실제 EKF에 비해 PKF는 어떠한 경우에도 공분산을 비교적 정확히 표현해낼 수 있기 때문에 정확성은 높지만 매우 많은 양의 샘플링과 비선형 연산을 필요로 하기 때문에 연산량이 결코 작다고 할 수 없다.
2. 칼만 필터 공식 유도
칼만필터의 유도 과정을 이해하기 위해선 베이지안 확률에 대한 컨셉을 아주 조금은 이해할 필요가 있다. 베이지안 확률은 확률을 실제 일어날 빈도로 보는 것이 아닌 신뢰성으로 보자는 개념이다. 가령 당첨 확률이 50%인 복권이 있다고 할 때 이를 10개 샀을 때 5개가 당첨되는 복권으로 해석하는 것이 아니라 1개를 샀을 때 당첨될 확률이 50%로 해석하는 것이다. 무엇이 달라지냐고 생각할 수도 있겠지만 베이지안 확률에서 확률은 실제 현상이 아닌 어디까지나 신뢰성을 의미한다. 그 뜻은 실제 현상과는 달리 주어진 정보에 따라 확률분포가 달라질 수 있다는 것으로 해석할 수 있다. 이공계 학부 1학년때 배우는 몬티홀 문제가 가장 대표적인 예시이다. 상품과 꽝 사이 어떠한 변화가 일어나지도 않았음에도 선택에 따른 확률이 각각 [math(\frac{1}{3})], [math(\frac{2}{3})]로 변화한 것은 몬티홀 문제에서 다루고 있는 확률이 어디까지나 선택에 대한 신뢰성을 이야기하기 때문이다. 추가적인 정보를 통해 선택에 대한 신뢰성이 변화한 것이지 실제 현상이 변화한 것은 아니기 때문에 빈도주의 관점에서 보자면 사실 확률은 변화하지 않은 것이 맞다. 칼만필터는 베이지안 확률의 이러한 컨셉으로부터 유도된다. 즉 칼만필터는 칼만이득 K에 따라 공분산을 조절할 수 있다는 아이디어로부터 공분산을 최소로 하는 칼만이득을 찾는 것으로 [math(\frac{\partial tr(P)}{\partial K}=0)]일때의 K를 구하는 것이다. 아래는 상기 설명한 컨셉을 기반으로 칼만필터 유도하는 과정을 보여준다.먼저 예측 단계는 상태공간방정식을 사용하여 미래의 상태값을 예측하는 단계로 이때 예측된 상태의 공분산[math(P_{k}^-)]는 아래와 같이 유도된다.
[math(P_{k}^-=E[(x_{k}-\hat{x}_{k}^-)(x_{k}-\hat{x}_{k}^-)^T])]
[math(P_{k}^-=E[(A(x_{k-1}-\hat{x}_{k-1})+\omega_{k})(A(x_{k-1}-\hat{x}_{k-1})+\omega_{k})^T])]
[math(P_{k}^-=AE[(x_{k-1}-\hat{x}_{k-1})(x_{k-1}-\hat{x}_{k-1})^T]A^T + E[\omega_{k}\omega_{k}^T])]
[math(P_{k}^-=AP_{k-1}A^T + Q_{k})]
보정단계는 오차를 이용하여 상태값을 보정해주는 단계로 보정항이 고려되었을 때 추정값의 오차와 공분산은 아래와 같이 다시 유도할 수 있다.
[math(x_{k}-\hat{x}_{k}=x_{k}-(\hat{x}_{k}^-+K_{k}(z_k-H\hat{x}_{k}^-)))]
[math(x_{k}-\hat{x}_{k}=(I-K_{k}H)(x_{k}-\hat{x}_{k}^-)-K_{k}v_{k})]
[math(P_{k}=E[(x_{k}-\hat{x}_{k})(x_{k}-\hat{x}_{k})^T])]
[math(P_{k}=(I-K_{k}H)E[(x_{k}-\hat{x}_{k}^-)(x_{k}-\hat{x}_{k}^-)^T](I-K_{k}H)^T+K_{k}E[v_{k}v_{k}^T]K_{k}^T)]
[math(P_{k}=(I-K_{k}H)P_k^-(I-K_{k}H)^T+K_{k}R_{k}K_{k}^T)]
칼만이득은 공분산을 최소로 하는 지점이라고 하였으므로 아래와 같이 유도가 가능하다.
[math(\frac{\partial tr(P_{k})}{\partial K_{k}}=-2P_{k}^{-}H^{T}+2K_{k}HP_{k}H^{T}+2K_{k}R_{k}=0)]
[math(\therefore K_{k}=P_{k}^{-}H^{T}(HP_{k}^{-}H^{T}+R_{k})^{-1})]
3. 칼만 필터 구현 과정
칼만필터를 구현하기 위한 알고리즘의 기본 순서는 다음과 같다.1. 초기값 선정: 추정하고자 하는 상태의 평균[math(x_0)]과 공분산[math(P_0)]의 초기값을 설정, 이는 어디까지나 초기값이기 때문에 공분산[math(P_0)]가 영행렬만 아니면 되긴 된다. 가령 상태 갯수가 n개라고 할 때 아래와 같이 설정할 수 있다.
[math(x_0=zeros(n))]
[math(P_0=0.01 * eye(n))]
2. 상태값 예측: 수식을 통해 다음 상태값을 예측하고 공분산을 계산한다. 이를 사전확률(prior)이라고도 부르는데 그냥 단순히 [math(x_k)]값으로 [math(x_{k+1})]을 계산하는 것이라고 생각하면 된다.
[math(\hat{x}_k^- = f(x_{k-1}))]
[math(\hat{P}_k^- = AP_{k-1}A^T + Q)]
여기서 Q는 Process 노이즈의 공분산으로 예측에 사용한 수식 [math(f(x))]의 정확도를 의미한다. 대부분은 구할 수 없는 값이기 때문에 이것도 그냥 [math(Q=(0.01)^2 * eye(n))] 같은 식으로 설정하면 된다.
3. 칼만이득 계산: 앞에서 설명한 대로 모델링의 부정확성 혹은 계측 오차로 인한 오차 등으로 인한 예측값과 실제값의 차이를 보정해주기 위하여 보정항에 들어가는 칼만이득 K를 구하는 과정이다. 여기서 칼만이득 K는 [math(\frac{\partial tr(P)}{\partial K}=0)]일 때의 값으로 아래와 같이 계산된다.
[math(K_k = \hat{P}_k^- H^T(H\hat{P}_k^- H^T + R)^{-1})]
여기서 R는 Measurement 노이즈의 공분산으로 계측기의 성능을 의미한다. 대개 제조사에서 제공하는 성능에 따라 결정하며 모르는 경우 계측기 갯수가 m개라고 할 때 [math(R=(0.01)^2 * eye(m))]와 같이 설정하면 된다.
4. 예측값 보정: 위에서 계산된 칼만이득 [math(K)]를 가지고 최종적인 추정값을 도출해내는 과정이다. 사후확률(posterior)이라고도 부르는데 그냥 기존 식에 보정항[math(K(y - \hat{y}))]을 추가하는 것뿐이다.
[math(x_k = \hat{x}_k + K_k(z_k - h(\hat{x}_k)))]
[math(P_k = (I - K_k H) \hat{P}_k^-)]
사실 칼만필터를 구현하는 데 필요한 수식은 매우 간단하기 때문에 구현 자체가 어렵지는 않다. 물론 경우에 따라 Jacobian을 유도하는 과정이 복잡할 순 있지만 이 역시도 심볼릭 연산으로 직접 Jacobian을 직접 유도할 때 이야기이고 유한차분이나 자동미분을 사용하여 간단하게 구현할 수 있으며, 무향칼만필터(UFK) 등에서는 단순히 시그마포인트를 구하고 시그마포인트를 [math(f(x))], [math(h(x))]를 통과시키면 되기 때문에 이러한 과정 조차 필요 없다.
학술 연구를 목적으로 칼만필터를 구현하고자 하는 경우 추천이 되는 방법은 라이브러리나 Toolbox를 활용하는 것이다. 가령 MATLAB에서는 알토대에서 개발한 EKF/UKF Toolbox가 가장 대표적이다. 이에 관한 80페이지 가량의 매뉴얼도 있으며 사용법도 매우 간단하다. Python에는 FilterPy 라이브러리가 있다. 애초에 학술목적으로 개발이 된 만큼 성능은 다소 아쉽지만 사용법 자체는 매우 간단하게 되어 있다. 아래는 FilterPy 라이브러리에서 무향칼만필터(UKF)의 구현 예시이다.
import numpy as np
from filterpy.kalman import MerweScaledSigmaPoints
from filterpy.kalman import UnscentedKalmanFilter
class UKF:
def __init__(self, n, m, dt):
self.n = n # 추정하고자 하는 상태 갯수
self.m = m # 계측기 갯수
self.dt = dt # Time Step
self.x_prev = np.zeros(n) # 초기 상태값
self.P_prev = 0.001 * np.eye(n) # 초기 공분산
def Statespace(self, x_prev, dt):
x_pred = np.exp(x_prev)
return x_pred
def Measurement(self, x_pred):
z = np.cos(x_pred)
return z
def Estimation(self, z):
fx = self.Statespace
hx = self.Measurement
points = MerweScaledSigmaPoints(self.n, alpha=.1, beta=2., kappa=-1)
ukf = UnscentedKalmanFilter(dim_x=self.n, dim_z=self.m, dt=self.dt, fx=fx, hx=hx, points=points)
ukf.x = self.x_prev
ukf.P = self.P_prev
ukf.R = (0.01**2)* np.eye(m) # Measurement Noise 공분산
ukf.Q = (0.01**2)* np.eye(m) # Process Noise 공분산
ukf.predict()
ukf.update(z)
self.x_prev = ukf.x
self.P_prev = ukf.P
return ukf.x
칼만필터가 제어공학의 끝판왕 중 하나로 취급을 받는 것은 단순히 제어공학만 이해하면 되는 것이 아닌 확률에 대한 개념도 이해를 하여야 한다는 점이다. 가령 입력 오차를 단순히 정규분포로 가정한다 하더라도 수식이 비선형이면 출력 오차의 분포는 정규분포가 아닌 다른 형태를 갖게 된다. 그리고 이 분포를 구하는 정형된 방법은 21세기 현재까지도 존재하지 않는다. 따라서 분포를 구하기 위해선 수식을 선형화하거나 몬테카를로 시뮬레이션과 유사한 방법을 취해야 하는데 그것이 EKF와 UKF이다. 당연한 이야기지만 몬테카를로 시뮬레이션은 컴퓨팅 파워가 충분히 좋아진 현재에 들어와서 가능해진 이야기이고 칼만필터가 처음 제안된 이후 최초로 구현된 칼만필터는 EKF이다. EKF는 단순히 수식을 선형화하는 과정을 포함하고 있을 뿐이라서 연산속도가 매우 빠르다는 장점을 가지고 있다. 하지만 수식의 비선형성이 매우 큰 경우 정확성이 떨어질 수 있다는 제약사항을 갖고 있다. 따라서 이후에 제안된 것이 UKF와 PKF이다. 대체로 수식의 비선형성보다는 확률분포의 비선형성이 낮다는 점에 착안하여 확률분포를 근사화한 방법으로 이때 확률분포가 정규분포이면 UKF, 아니면 PKF로 분류된다. 하지만 이 방법들도 앞서 설명하였다시피 매우 높은 컴퓨팅 파워를 요구하기 때문에 EKF에 비해 적용 가능한 필드가 제한된다는 단점이 존재한다.