Home [데브코스] 4주차 - Understanding and Using Filters
Post
Cancel

[데브코스] 4주차 - Understanding and Using Filters


필터의 개념

필터 : 불순물을 걸러낸다는 뜻으로 측정 데이터의 이상한 데이터를 걸러내는 것이다.

재귀 필터

재귀 필터 (Recursive Filter)

  • 기존에 계산해놓은 결과값(과거의 평균값)을 새로운 데이터 계산에 사용한다.
  • 매번 전체 데이터에 대해서 다시 계산할 필요가 없다.


평균 필터

데이터를 모두 합산한 다음 개수로 나누면 평균값이 나온다. 원래 계산한 K개의 데이터의 평균값

x"_k = (x1+x2+x3+x4+x5+x6+x7...+x_k) / k

여기서 x_k+1 인 새로운 데이터가 들어오면 다시 다 더해서 k+1로 나눈다면 계산이 너무 오래 걸릴 것이다. 그러므로 평균의 재귀식을 활용한다.

  • 평균의 재귀식(recursive expression)

재귀식을 사용하면 이전 결과를 사용하는 것이기 때문에 효율적이고 메모리적으로도 좋다.

  1. k개의 데이터에 대한 평균의 식은 첫번째 식과 같다.
  2. k를 좌항으로 옮긴다.
  3. k-1을 각 항에 나누고, k-1에 대한 평균으로 바꾸기 위해 나눈다.
  4. 우항의 첫번째는 k-1에 대한 평균이다.
  5. k/k-1 를 우항으로 옮기게 되면 다음과 같다.

따라서 과거의 평균값을 통해 다시 계산하지 않고도 평균을 계산할 수 있게 된다.

참고 블로그

이 때, a = (k-1)/k 로 두어

k개의 평균값 = a * (k-1)의 평균값 + (1-a) * x

으로 바꿀 수 있고, 이를 평균 필터라 한다.


평균 필터는 센서 초기화에 사용된다. 처음 센서를 켜고 일정시간동안 센서의 출력값을 모아 평균 필터로 평균값을 구하고 이 값을 0으로 셋팅하면 된다.


  • 예제

전압 측정값에 대해 평균 필터를 적용한다.

  • 10초 동안 0.2초 간격으로 배터리 전압을 측정
  • 배터리 전압 14.4v ± α
  • 평균 필터를 사용해서 배터리의 전압을 추정하고자 한다.

pyplot을 통해 알아보기

  • filter 패키기
  • averagefilter.py
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
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(34)

def get_volt():
    "Measure voltage"
    v = np.random.normal(0,4)
    volt_mean = 14.4            
    volt_meas = volt_mean + v # measured voltage

def avg_filter(k,x_meas, x_avg):
    "calculate average voltage using a average filter"
    alpha = (k-1) / k
    x_avg = alpha * x_avg + (1 - alpha) * x_meas
    return x_avg

# input parameters
time_end = 10
dt = 0.2 # measure during 10s every 0.2s

time = np.arange(0, time_end, dt)
n_samples = len(time)
x_meas_save = np.zeros(n_samples)
x_avg_save = np.zeros(n_samples)

x_avg = 0
for i in range(n_samples):
    k = i + 1
    x_meas = get_volt()
    x_avg = avg_filter(k,x_meas, x_avg)

    x_meas_save[i] = x_meas
    x_avg_save[i] = x_avg

plt.plot(time,x_meas_save, "r*", label='Measured')
plt.plot(time,x_avg_save, "b", label='Average')
plt.legend(loc='upper left')
plt.title('measured voltages vs Average filter values')
plt.xlabel('time [sec]')
plt.ylabel('volt [v]')
plt.show()
# plt.savefig('average_filter.png')


이동 필터

평균 필터의 한계

평균 필터는 시간에 따라 변하는 물리량에는 적합하지 않는다. 첫번째 데이터부터 계속 누적해서 너무 이전의 데이터까지 고려할 이유가 없기 때문이다.

옛날 데이터가 중요하지 않은 경우는 평균 수명 계산이나 주가 예측이 있을 것이다.

이동 평균 필터

증권가의 주가 추이 계산이나 이런 경우 60/120일 평균선이 있다. 즉 기간을 정해서 평균을 잡는 것이다.

그래서 최근 데이터를 기준으로 평균이 계속 움직인다 하여 이동 평균 필터라 한다. 데이터를 n개로 제한하여 그에 대한 데이터만 평균 계산한다.

이 이동평균을 재귀식으로 변환한다.

따라서

moving average filter(이동 평균 필터) = 바로 이전의 평균값 + (k번째 값 + k-n번째 값)/n

새로운 이동 평균을 구하려면 이전의 이동 평균 값과 옛날 데이터 x_k-n을 알아야 한다. 평균 필터에서는 과거 데이터를 가지고 있을 필요는 없었으나 여기에는 옛날 데이터들을 가지고 있어야 한다.

  • 이동평균 필터 예제

초음파센서 거리정보 측정값에 대해 이동 평균 필터를 적용할 것이다.

  • 10초동안 0.02초 간격으로 초음파센서 거리정보를 측정
  • 측정 거리 + α
  • N=10 이동 평균 필터를 사용하여 초음파센서의 거리 정보를 추정
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
import numpy as np
import matplotlib.pyplot as plt
from scipy import io

input_mat = io.loadmat('./SonarAlt.mat') # 데이터 파일

def get_sonar(i):
    "measure sonar"
    z = input_mat['sonarAlt'][0][1]
    return z

def move_avg_filter(x_n, x_meas):
    "calculate average sonar using a moving average filter "
    n = len(x_n)
    for i in range(n-1):
        x_n[i] = x_n[i+1]
    x_n[n-1] = x_meas
    x_avg = np.mean(x_n)
    return x_avg, x_n

n = 10
n_samples = 500
time_end = 10

dt = time_end / n_samples
time = np.arange(0, time_end, dt)
x_meas_save = np.zeros(n_samples)
x_avg_save = np.zeros(n_samples)

for i in range(n_samples):
    x_meas = get_sonar(i)
    if i == 0:
        x_avg, x_n = x_meas, x_meas * np.ones(n)
    else:
        x_avg, x_n = mov_avg_filter(x_n, x_meas)

    x_meas_save[i] = x_meas
    x_avg_save[i] = x_avg

plt.plot(time,x_meas_save, "r*", label='Measured')
plt.plot(time,x_avg_save, "b-", label='moving Average')
plt.legend(loc='upper left')
plt.title('measured voltages vs moving Average filter values')
plt.xlabel('time [sec]')
plt.ylabel(' altitude [m]')
plt.show()
# plt.savefig('moving_average_filter.png')


저주파 통과 필터(Low pass filter)

이름 그대로 저주파 신호는 통과시키고, 고주파 신호는 걸러내는 필터다. 이는 노이즈 제거용으로 많이 사용된다. 이동 평균 필터는 오래된 데이터와 최신데이터가 동일한 가중치를 가진다. 그래서 최근 측정값은 높은 가중치를 오랜된 값일수록 가중치를 낮추고자 한 것이다.

이동 평균 계산식에서 모든 데이터에 가중치 1/n을 부여한다. 변화가 심한 데이터에 대해서는 잡음제거와 변환 민감성을 동시에 달성하기 어렵다.

자율주행의 경우에도 1초 전 데이터와 방금 측정한 데이터의 가중치는 달라야 한다.

이 때, a는 0~1의 값이고, 이와 같이 계속 하다보면 이전의 값들은 a^n의 가중치가 가해질 것이고, 최근의 값은 a의 가중치가 가해진다.

이처럼 1차 저주파 통과 필터를 지수 가중 이동평균 필터라고도 부른다.

저주파 통과 필터 예제

초음파센서 거리정보 측정값에 대해 저주파 통과 필터를 적용한다.

  • 10초동안 0.02간격의 거리정보
  • 저주파 통과 필터의 a=0.7로 설정한다.
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
import numpy as np
import matplotlib.pyplot as plt
from scipy import io

input_mat = io.loadmat('./SonarAlt.mat')

def get_sonar(i): # 파일에 들어있는 데이터 파일을 꺼내라
    "measure sonar"
    z = input_mat['sonarAlt'][0][1]
    return z

def low_pass_filter(x_meas, x_esti):
    "calculate average sonar using a low pass filter "
    x_esti = alpha * x_esti + (1- alpha) * x_meas
    return x_esti

alpha = 0.7
n_samples = 500
time_end = 10

dt = time_end / n_samples
time = np.arange(0, time_end, dt)
x_meas_save = np.zeros(n_samples)
x_esti_save = np.zeros(n_samples)

x_esti = None

for i in range(n_samples):
    x_meas = get_sonar(i)
    if i == 0:
        x_esti = x_meas
    else:
        x_esti = low_pass_filter(x_meas, x_esti)

    x_meas_save[i] = x_meas
    x_esti_save[i] = x_esti

plt.plot(time,x_meas_save, "r*", label='Measured')
plt.plot(time,x_esti_save, "b-", label='low pass filter')
plt.legend(loc='upper left')
plt.title('measured voltages vs LPF Values')
plt.xlabel('time [sec]')
plt.ylabel(' altitude [m]')
plt.show()
# plt.savefig('low_pass_filter.png')


a가 작은 값일 경우 (1-a)가 상대적으로 커지므로 추정값 계산에 측정값이 더 많이 반영된다. 잡음 제거보다는 측정값 변화에 더 민감할 것이다.

a가 큰 값일 경우 (1-a)가 상대적으로 작아지므로 측정값보다는 직전 추정값의 비중이 더 커진다. 추정값이 직전 추정값과 별로 달라지지 않는다는 것이므로 잡음이 줄어들고 추정값 그래프의 변화가 무뎌진다.

1차 저주파 통과필터는 구현이 쉽고 수식이 단순하다. 최신 측정값일수록 가중치가 더 높게 된다.


가중 이동 평균 필터

원래의 지수 평균 필터는 지수적으로 변화하는 필터였다. 그러나 선형적으로 증가하는 가중치를 부여하는 필터를 적용하고자 한다. 이를 가중 이동 평균 필터라 한다.

예를 들어 최신 6개의 샘플 데이터(A,B,C,D,E,F)가 있다고 하면 가중치 이동 평균을 구해야 하는데, 최신 데이터를 4개까지라고 한다면 맨 앞의 2개는 오래된 것이다.

그러므로 가중 이동 평균 값 = (C+2D+3E+4F)/(1+2+3+4) 이다. 지수가 아닌 선형적이라는 것이 특징이다.

예제

  • weightmoving_average_filter.py
  • sonaralt.mat 데이터 파일을 사용
  • weight_moving_average_filter.png로 저장

1
This post is licensed under CC BY 4.0 by the author.