Python으로 배우는 DSP/Part 3. 디지털 필터 설계와 구현

[20] 칼만 필터(Kalman Filter): 센서 데이터 예측 및 필터링 기초

multimedia 2026. 6. 20. 10:00
반응형

들어가며

지난 시간까지 우리는 신호에 섞인 노이즈를 주파수 대역으로 잘라내거나(FIR/IIR), 오차를 계산해 필터 스스로 계수를 적응시키는(LMS) 방법들을 배웠습니다. 이러한 기법들은 신호 자체의 파형이나 주파수 특성을 가공하는 데 탁월한 성능을 발휘합니다.

하지만 우리가 다루어야 할 데이터가 오디오나 통신 신호가 아니라, 현실 세계를 움직이는 물리적 객체의 상태(위치, 속도, 온도 등)라면 어떨까요? GPS 수신기, 드론의 자이로 센서, 자율주행차의 LIDAR — 이들 센서에는 공통적인 고민이 있습니다. 측정값 자체에 노이즈가 섞여 있다는 점입니다. 단순히 주파수 대역을 잘라내는 FIR/IIR 필터로는, 불규칙하게 튀는 측정 오차를 제대로 다룰 수 없습니다.

칼만 필터(Kalman Filter)는 1960년 루돌프 칼만(Rudolf E. Kálmán)이 발표한 알고리즘으로, "이전 상태로부터 예측"과 "현재의 불확실한 측정값을 융합"하는 두 단계를 반복함으로써 최적의 상태를 추정합니다. 아폴로 달 탐사선의 항법 시스템에 처음 실용화된 이래, 60여 년이 지난 지금도 로봇공학, 항공우주, 금융 시계열 분석에 이르기까지 폭넓게 활용되고 있습니다.

이번 글에서는 칼만 필터의 수학적 원리를 단계별로 살펴보고, Python으로 1D 위치 추정 시뮬레이션을 직접 구현해 보겠습니다.

 

1. 칼만 필터란 무엇인가?

우리가 지금까지 다룬 필터들은 주로 입력된 신호 $x[n]$의 최근 값들을 이리저리 조합하여 노이즈를 제거했습니다. 반면 칼만 필터는 "물리적 법칙에 따른 예측"과 "센서의 측정값"이라는 두 가지 서로 다른 정보를 융합하여 최적의 해를 찾습니다.

자동차를 예로 들어보겠습니다.

  • 예측(Prediction): 1초 전에 시속 60km로 달리고 있었다면, 1초 뒤에는 현재 위치에서 대략 16.6m 앞으로 이동해 있을 것이라는 물리적(수학적) 모델링이 가능합니다.
  • 측정(Measurement): 동시에 GPS 센서는 현재 위치 좌표를 수신하여 알려줍니다. 하지만 이 값은 5~10m의 오차(노이즈)를 포함하고 있습니다.

칼만 필터는 예측된 위치와 측정된 위치 중 '어느 쪽의 오차(불확실성)가 더 작은가?'를 계산하여 두 값을 절묘하게 섞어 줍니다. 바로 이 원리를, 불확실한 세계에서 최선의 추측을 반복적으로 갱신하는 재귀 알고리즘으로 구현하는 것입니다. 이 과정을 아래 표로 정리했습니다.

단계 동작 원리 및 의미
1. 예측
(Predict)
이전 상태(위치, 속도 등)와 시스템의 물리적 모델을 바탕으로 다음 상태가 어떨지 추측합니다. 이 과정에는 모델 자체가 가진 불확실성(Process Noise)이 추가됩니다.
2. 보정
(Update)
새롭게 들어온 불확실한 센서 측정값(Measurement Noise 포함)을 예측값과 비교합니다. 두 값의 확률적 분산(불확실성)을 계산하여 최적의 가중치로 융합합니다.

이 문제를 풀기 위한 핵심 전제는 두 가지입니다.

  • 시스템의 동적 모델(Process Model)을 알고 있다 — 예를 들어, "현재 속도가 유지된다면 다음 위치는 이 정도일 것이다"
  • 측정 노이즈와 프로세스 노이즈가 가우시안(Gaussian) 분포를 따른다

이 두 조건이 성립할 때, 칼만 필터는 선형 최소 분산 추정(Linear Minimum Variance Estimation)의 의미에서 이론적으로 최적(Optimal)임이 수학적으로 증명되어 있습니다.

 

2. 칼만 필터의 5단계 수식

칼만 필터는 예측(Predict) 단계 2개와 갱신(Update) 단계 3개, 총 5개의 수식으로 이루어져 있습니다.

상태 공간 모델 (State Space Model)

칼만 필터는 다음과 같은 선형 시스템을 가정합니다.

$$\mathbf{x}_k = \mathbf{F}\mathbf{x}_{k-1} + \mathbf{B}\mathbf{u}_k + \mathbf{w}_k$$
$$\mathbf{z}_k = \mathbf{H}\mathbf{x}_k + \mathbf{v}_k$$
기호 의미
$\mathbf{x}_k$ $k$ 시점의 상태 벡터 (예: 위치, 속도)
$\mathbf{F}$ 상태 전이 행렬 (State Transition Matrix)
$\mathbf{B}$ 제어 입력 행렬 (Control Input Matrix)
$\mathbf{u}_k$ 외부 제어 입력 벡터 (예: 모터 출력, 가속도 명령값)
$\mathbf{w}_k$ 프로세스 노이즈 벡터
$\mathbf{z}_k$ 측정 벡터
$\mathbf{H}$ 관측 행렬 (Observation Matrix)
$\mathbf{v}_k$ 측정 노이즈 벡터

두 노이즈는 서로 독립이며, 평균이 0인 가우시안 분포를 따른다고 가정합니다.

$$\mathbf{w}_k \sim \mathcal{N}(\mathbf{0}, \mathbf{Q}), \quad \mathbf{v}_k \sim \mathcal{N}(\mathbf{0}, \mathbf{R})$$

즉, $\mathbf{Q}$는 프로세스 노이즈 $\mathbf{w}_k$의 공분산 행렬이며, $\mathbf{R}$은 측정 노이즈 $\mathbf{v}_k$의 공분산 행렬입니다. 이 두 행렬은 칼만 필터 설계의 핵심 파라미터로, 이후 Section 5에서 자세히 다루겠습니다.

2.1 예측 단계

현재 상태를 바탕으로 다음 상태를 예측합니다.

Step 1 — 상태 예측 (Predicted State Estimate)

$$\hat{\mathbf{x}}_{k|k-1} = \mathbf{F}\hat{\mathbf{x}}_{k-1|k-1} + \mathbf{B}\mathbf{u}_k$$

이전 추정값을 시스템 모델로 한 스텝 앞으로 전파합니다. $\mathbf{B}\mathbf{u}_k$ 항은 모터 제어 명령이나 알려진 가속도처럼 시스템에 가해지는 외부 입력을 반영합니다.

Step 2 — 오차 공분산 예측 (Predicted Error Covariance)

$$\mathbf{P}_{k|k-1} = \mathbf{F}\mathbf{P}_{k-1|k-1}\mathbf{F}^\top + \mathbf{Q}$$

상태 예측의 불확실성을 함께 전파하되, 프로세스 노이즈 공분산 $\mathbf{Q}$만큼 불확실성이 증가합니다.

2.2 갱신 단계

예측된 상태를 센서의 측정값 $\mathbf{z}_k$를 이용해 교정합니다.

Step 3 — 칼만 이득 계산 (Kalman Gain)

$$\mathbf{K}_k = \mathbf{P}_{k|k-1}\mathbf{H}^\top \left( \mathbf{H}\mathbf{P}_{k|k-1}\mathbf{H}^\top + \mathbf{R} \right)^{-1}$$

이 수식이 칼만 필터의 핵심입니다. $\mathbf{K}_k$는 "예측을 얼마나 믿고, 측정을 얼마나 믿을 것인가"의 가중치를 결정합니다.

  • $\mathbf{R}$이 크면 (측정 노이즈가 크면) $\rightarrow$ $\mathbf{K}_k$가 작아져 예측을 더 신뢰
  • $\mathbf{P}_{k|k-1}$이 크면 (예측 불확실성이 크면) $\rightarrow$ $\mathbf{K}_k$가 커져 측정값을 더 신뢰

Step 4 — 상태 갱신 (Updated State Estimate)

$$\hat{\mathbf{x}}_{k|k} = \hat{\mathbf{x}}_{k|k-1} + \mathbf{K}_k \left( \mathbf{z}_k - \mathbf{H}\hat{\mathbf{x}}_{k|k-1} \right)$$

$\left( \mathbf{z}_k - \mathbf{H}\hat{\mathbf{x}}_{k|k-1} \right)$ 항을 이노베이션(Innovation) 또는 잔차(Residual)라 부릅니다. 예측과 실제 측정 사이의 차이를 칼만 이득으로 보정하는 것입니다.

Step 5 — 오차 공분산 갱신 (Updated Error Covariance)

$$\mathbf{P}_{k|k} = \left( \mathbf{I} - \mathbf{K}_k\mathbf{H} \right) \mathbf{P}_{k|k-1}$$

측정값을 반영한 후, 불확실성이 감소합니다. 이 다섯 단계가 매 시점마다 반복됩니다.

 

3. 직관적 이해: 예측과 측정의 가중 평균

1D 스칼라 문제로 단순화하면 칼만 필터의 직관을 명확히 볼 수 있습니다.

$$\hat{x}_{k|k} = \hat{x}_{k|k-1} + K_k (z_k - \hat{x}_{k|k-1})$$
$$K_k = \frac{P_{k|k-1}}{P_{k|k-1} + R}$$
  • $K_k = 0$이면 $\rightarrow$ 측정값을 완전히 무시, 예측값만 사용
  • $K_k = 1$이면 $\rightarrow$ 예측값을 완전히 무시, 측정값만 사용
  • $0 < K_k < 1$이면 $\rightarrow$ 예측과 측정의 최적 가중 평균

결국 칼만 필터는 두 불확실한 정보를 각자의 신뢰도(분산의 역수)에 비례하여 합산하는 베이즈 추론(Bayesian Inference)의 재귀적 구현이라 할 수 있습니다.

 

4. Python 구현: 1D 위치 추정 시뮬레이션

이론을 바탕으로 Python 코드를 작성해 봅니다. 일정한 속도로 달리는 자동차의 GPS 센서(위치 측정) 데이터에 심한 노이즈가 끼어 있을 때, 칼만 필터를 이용해 원래의 궤적을 부드럽고 정확하게 복원하는 시뮬레이션입니다.

상태 벡터: $\mathbf{x} = [\text{위치}, \text{속도}]^\top$

본 예제는 외부 제어 입력이 없는 등속 운동 모델이므로, $\mathbf{B}\mathbf{u}_k$ 항은 구현에서 생략합니다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.family'] = 'Malgun Gothic'
matplotlib.rcParams['axes.unicode_minus'] = False

# ── 시뮬레이션 파라미터 ───────────────────────────────────────────
np.random.seed(42)
dt       = 1.0          # 샘플링 간격 (초)
n_steps  = 60           # 총 스텝 수
true_vel = 1.0          # 실제 속도 (m/s)

# ── 노이즈 설정 ───────────────────────────────────────────────────
process_noise_std    = 0.1   # 프로세스 노이즈 표준편차
measurement_noise_std = 2.0  # 측정 노이즈 표준편차 (GPS 오차)

# ── 실제 궤적 및 측정값 생성 ──────────────────────────────────────
true_positions = np.array([true_vel * k * dt for k in range(n_steps)])
measurements   = true_positions + np.random.normal(0, measurement_noise_std, n_steps)

# ── 칼만 필터 행렬 정의 ───────────────────────────────────────────
F = np.array([[1, dt],
              [0,  1]])   # 등속 운동 모델: [pos, vel] -> [pos+vel*dt, vel]

H = np.array([[1, 0]])    # 위치만 관측

Q = process_noise_std**2 * np.array([[dt**4/4, dt**3/2],
                                     [dt**3/2,   dt**2]])  # 프로세스 노이즈 공분산

R = np.array([[measurement_noise_std**2]])  # 측정 노이즈 공분산

# ── 초기 상태 ─────────────────────────────────────────────────────
x_est = np.array([[0.0],   # 초기 위치 추정
                  [0.0]])  # 초기 속도 추정
P     = np.eye(2) * 100.0  # 초기 오차 공분산 (높은 불확실성)

# ── 칼만 필터 루프 (Bu 항 없는 등속 모델) ─────────────────────────
estimated_positions = []
kalman_gains        = []

for k in range(n_steps):
    # ── Predict ──────────────────────────────────────────────────
    x_pred = F @ x_est          # u_k = 0 이므로 Bu 항 생략
    P_pred = F @ P @ F.T + Q

    # ── Update ───────────────────────────────────────────────────
    z = np.array([[measurements[k]]])
    S = H @ P_pred @ H.T + R
    K = P_pred @ H.T @ np.linalg.inv(S)

    innovation = z - H @ x_pred
    x_est = x_pred + K @ innovation
    P     = (np.eye(2) - K @ H) @ P_pred

    estimated_positions.append(x_est[0, 0])
    kalman_gains.append(K[0, 0])

# ── 시각화 ────────────────────────────────────────────────────────
fig, axes = plt.subplots(2, 1, figsize=(12, 9))
time_axis = np.arange(n_steps) * dt

# (1) 위치 추정 결과
ax1 = axes[0]
ax1.plot(time_axis, true_positions, 'g-',  lw=2,   label='실제 위치 (Ground Truth)')
ax1.scatter(time_axis, measurements, s=15, c='tomato', alpha=0.5, label='GPS 측정값 (노이즈 포함)')
ax1.plot(time_axis, estimated_positions, 'b-', lw=2, label='칼만 필터 추정값')
ax1.set_title('칼만 필터 — 1D 위치 추정 결과', fontsize=14, fontweight='bold')
ax1.set_xlabel('시간 (초)')
ax1.set_ylabel('위치 (m)')
ax1.legend()
ax1.grid(True, alpha=0.3)

# (2) 칼만 이득 수렴
ax2 = axes[1]
ax2.plot(time_axis, kalman_gains, 'purple', lw=2)
ax2.set_title('칼만 이득(Kalman Gain) 수렴 과정', fontsize=14, fontweight='bold')
ax2.set_xlabel('시간 (초)')
ax2.set_ylabel('칼만 이득 K')
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('kalman_filter_result.png', dpi=150, bbox_inches='tight')
plt.show()

# ── 오차 분석 ─────────────────────────────────────────────────────
meas_rmse = np.sqrt(np.mean((measurements - true_positions)**2))
kf_rmse   = np.sqrt(np.mean((np.array(estimated_positions) - true_positions)**2))
print(f"측정값 RMSE    : {meas_rmse:.4f} m")
print(f"칼만 필터 RMSE : {kf_rmse:.4f} m")
print(f"노이즈 감소율  : {(1 - kf_rmse/meas_rmse)*100:.1f} %")

4.1 시뮬레이션 결과 해석

코드를 실행하면 다음과 같은 결과를 확인할 수 있습니다.

측정값 RMSE    : 1.8282 m
칼만 필터 RMSE : 1.0344 m
노이즈 감소율  : 43.4 %

위치 추정 그래프: 빨간 점으로 흩어진 GPS 측정값과 달리, 파란 칼만 필터 추정값은 실제 궤적(녹색)을 매우 가깝게 추종합니다.

칼만 이득 수렴 그래프: 초반에는 이득이 높게 시작하다가 — 초기 불확실성($\mathbf{P}$)이 크기 때문입니다 — 빠르게 일정 값으로 수렴합니다. 이것이 정상 상태 칼만 이득(Steady-State Kalman Gain)입니다. 필터가 시스템을 '학습'했다는 의미입니다.

오차 분석: 칼만 필터 RMSE는 1.0344 m로, 측정값 RMSE 1.8282 m 대비 43.4%의 노이즈 감소를 달성하였습니다. 측정 노이즈 표준편차 2.0 m 설정 대비 실질적인 추정 정밀도가 크게 향상되었음을 확인할 수 있습니다.

 

5. 주요 파라미터 조정 가이드

칼만 필터 실무에서 가장 중요한 설계 결정은 $\mathbf{Q}$와 $\mathbf{R}$의 설정입니다.

파라미터 의미 크게 설정하면 작게 설정하면
$\mathbf{Q}$ 프로세스 노이즈 $\mathbf{w}_k$의 공분산 모델을 불신 $\rightarrow$ 측정값에 빠르게 반응 모델을 신뢰 $\rightarrow$ 측정값 변화에 둔감
$\mathbf{R}$ 측정 노이즈 $\mathbf{v}_k$의 공분산 측정값을 불신 $\rightarrow$ 예측에 의존 측정값을 신뢰 $\rightarrow$ 측정값에 민감

실전 조언: $\mathbf{R}$은 센서 datasheet의 노이즈 스펙에서 직접 구할 수 있습니다. 반면 $\mathbf{Q}$는 시스템 모델의 불완전함을 보정하는 튜닝 파라미터로, 시뮬레이션이나 실험을 통해 경험적으로 결정하는 경우가 많습니다.

 

6. 칼만 필터의 한계와 확장

선형 시스템 가정: 기본 칼만 필터는 선형 시스템에서만 최적입니다. 비선형 시스템(예: 로봇 팔의 관절 각도, 항공기의 자세 추정)에는 다음과 같은 확장형이 사용됩니다.

  • EKF (Extended Kalman Filter): 비선형 함수를 야코비안(Jacobian)으로 선형 근사
  • UKF (Unscented Kalman Filter): 시그마 포인트(Sigma Points)를 이용하여 비선형 변환을 수치적으로 근사. EKF보다 정확도가 높음
  • Particle Filter: 가우시안 가정도 없애고, 임의의 확률 분포를 입자(particle)로 표현. 가장 일반적이나 계산 비용이 높음

이러한 확장들은 모두 "예측–갱신(Predict–Update)"의 두 단계 구조를 공유하며, 칼만 필터의 철학적 근간 위에서 발전한 것입니다.

 

[Insight]

기존의 신호 처리가 파형에 섞인 '고주파 노이즈 성분'을 필터의 차단 주파수(Cut-off)로 잘라내는 것에 집중했다면, 칼만 필터는 우리가 관찰하는 시스템의 '물리적 메커니즘'을 확률 모델에 직접 투영합니다. "자동차가 갑자기 1초 만에 100m를 순간이동 할 수 없다"는 물리학의 절대적인 진리를 행렬 $\mathbf{F}$에 담아 필터에게 알려줌으로써, 그 범위를 벗어나는 센서의 튀는 값을 노이즈로 간주하고 무시하는 방식입니다.

칼만 필터의 핵심은 단 하나입니다: 불확실성을 명시적으로 모델링하고, 두 불확실한 정보를 신뢰도에 따라 최적으로 결합한다. FIR/IIR 필터가 주파수 영역에서 신호를 분리하는 도구라면, 칼만 필터는 상태 공간(State Space)에서 확률적 추론을 수행하는 도구입니다. 이 관점의 전환이 중요합니다.

오늘 구현한 기본 칼만 필터는 행렬 연산 기반이므로 '선형(Linear)' 시스템에서만 동작합니다. 그러나 현실의 드론 자세 제어나 로봇 팔의 궤적 추적은 수많은 삼각함수로 얽힌 비선형 시스템입니다. 이를 해결하기 위해 수학적 테일러 전개로 선형화를 시도하거나, 통계적 샘플링을 활용하는 알고리즘이 발전해 왔습니다.

이러한 칼만 필터 패밀리는 현대의 자율주행 센서 퓨전(LiDAR + Radar + Camera 융합) 시스템을 떠받치는 가장 거대하고 강력한 근간이 되었습니다.

 

다음 글 예고

14편부터 시작된 [Part 3. 디지털 필터 설계와 구현] 챕터가 FIR, IIR, 적응 필터를 거쳐 칼만 필터까지 오며 성공적으로 마무리되었습니다. 주파수를 자르고, 스스로 환경에 적응하고, 확률적으로 미래를 예측하는 다양한 필터의 무기들을 장착하셨기를 바랍니다.

이제 Part 4로 넘어가, 필터링의 실전 무대인 오디오 신호 처리로 시야를 확장할 차례입니다. 다음 시간에는 [21. 오디오 특징 추출: Librosa를 활용한 MFCC, Chroma 추출 (AI 전처리)] 편을 통해, 오디오 신호에서 AI 모델이 실제로 사용하는 특징(Feature)을 어떻게 추출하는지 살펴보겠습니다. 음성 인식, 음악 장르 분류, 감정 분석 모델 — 이 모든 AI 시스템의 입력 단에서 MFCC(Mel-Frequency Cepstral Coefficient)와 Chroma Feature가 어떤 역할을 하는지, Librosa 라이브러리로 직접 실습해 보겠습니다.

 

📌 이전 연재 글 보기

 

반응형