빠르고 메모리 효율적인 이동 평균 계산


33

C 로 이동 평균 을 계산하는 시간 및 메모리 효율적인 솔루션을 찾고 있습니다. 전용 분할 장치가없는 PIC 16을 사용하기 때문에 나누는 것을 피해야합니다.

현재는 모든 값을 링 버퍼에 저장하고 새 값이 도착할 때마다 합계를 저장하고 업데이트하기 만합니다. 이것은 실제로 효율적이지만 불행히도 사용 가능한 대부분의 메모리를 사용합니다 ...


3
이 작업을 수행하는 데 더 공간 효율적인 방법이 없다고 생각합니다.
Rocketmagnet

4
@JobyTaffey는 제어 시스템에서 널리 사용되는 알고리즘이며 제한된 하드웨어 리소스를 처리해야합니다. 그래서 나는 그가 여기보다 더 많은 도움을 얻을 것이라고 생각합니다.
clabacchio

3
@Joby :이 질문에는 작은 자원 제한 시스템과 관련된 몇 가지 주름이 있습니다. 내 대답을 참조하십시오. 당신은 SO 사람들과 같은 큰 시스템에서 이것을 매우 다르게 할 것입니다. 이것은 전자 제품 설계 경험에서 많이 나타났습니다.
Olin Lathrop

1
동의한다. 이것은 임베디드 시스템과 관련되어 있기 때문에이 포럼에 매우 적합합니다.
Rocketmagnet

나는 이의를 철회합니다
Toby Jaffey

답변:


55

다른 사람들이 언급했듯이 현재 사용중인 FIR (유한 임펄스 응답) 필터 대신 IIR (무한 임펄스 응답) 필터를 고려해야합니다. 더 많은 것이 있지만 언뜻보기에 FIR 필터는 명시 적 컨볼 루션 및 방정식이있는 IIR 필터로 구현됩니다.

마이크로 컨트롤러에서 많이 사용하는 특정 IIR 필터는 단일 극 저역 통과 필터입니다. 이것은 간단한 RC 아날로그 필터와 동등한 디지털입니다. 대부분의 응용 프로그램에서 사용중인 상자 필터보다 특성이 더 좋습니다. 필자가 경험 한 박스 필터의 대부분은 특정 특성이 필요하지 않은 디지털 신호 처리 클래스에서주의를 기울이지 않은 결과입니다. 노이즈 인 고주파 만 감쇠 시키려면 단일 극 저역 통과 필터가 더 좋습니다. 마이크로 컨트롤러에서 디지털 방식으로 구현하는 가장 좋은 방법은 일반적으로 다음과 같습니다.

FILT <-FILT + FF (NEW-FILT)

FILT는 지속성 상태입니다. 이 필터를 계산하는 데 필요한 유일한 지속 변수입니다. NEW는 필터가이 반복으로 업데이트되는 새로운 값입니다. FF는 필터 비율로 필터 의 "무거움"을 조정합니다. 이 알고리즘을보고 FF = 0의 경우 출력이 절대 변하지 않기 때문에 필터가 무한히 무겁다는 것을 알 수 있습니다. FF = 1의 경우 출력이 입력을 따르기 때문에 실제로 필터가 없습니다. 유용한 값은 사이에 있습니다. 작은 시스템에서는 FF를 1/2 N으로 선택하십시오.따라서 FF를 곱하면 N 비트만큼 오른쪽으로 시프트 할 수 있습니다. 예를 들어, FF는 1/16 일 수 있으며 FF에 곱하면 4 비트의 오른쪽 시프트입니다. 그렇지 않으면이 필터는 하나의 빼기와 하나의 덧셈 만 필요하지만 숫자는 일반적으로 입력 값보다 넓어야합니다 (아래의 별도 섹션에서 숫자 정밀도에 대한 자세한 내용 참조).

필자는 일반적으로 A / D 판독 값을 필요한 것보다 훨씬 빠르게 가져 와서이 두 필터를 계단식으로 적용합니다. 이것은 2 개의 RC 필터를 직렬로 디지털 연결 한 것으로 롤오프 주파수보다 12dB / 옥타브 이상 감쇠합니다. 그러나 A / D 판독의 경우 일반적으로 단계 응답을 고려하여 시간 영역에서 필터를 보는 것이 더 관련이 있습니다. 이것은 측정하는 것이 변경 될 때 시스템이 얼마나 빨리 변화를 볼 수 있는지 알려줍니다.

이 필터를 쉽게 설계 할 수 있도록 (FF 선택 및 캐스케이드 수를 결정하는 것만 의미 함) 프로그램 FILTBITS를 사용합니다. 계단식 일련의 필터에서 각 FF에 대한 시프트 비트 수를 지정하고 단계 응답 및 기타 값을 계산합니다. 실제로 나는 보통 래퍼 스크립트 PLOTFILT를 통해 이것을 실행합니다. FILTBITS를 실행하여 CSV 파일을 만든 다음 CSV 파일을 플로팅합니다. 예를 들어, 다음은 "PLOTFILT 4 4"의 결과입니다.

PLOTFILT에 대한 두 개의 매개 변수는 위에서 설명한 유형의 캐스케이드 된 두 개의 필터가 있음을 의미합니다. 4의 값은 FF를 곱한 것을 실현하기위한 시프트 비트의 수를 나타낸다. 따라서이 경우 두 개의 FF 값은 1/16입니다.

빨간색 추적은 단위 단계 응답이며, 가장 중요합니다. 예를 들어, 입력이 즉시 변경되면 결합 된 필터의 출력이 60 회 반복에서 새 값의 90 %로 설정됩니다. 약 95 %의 정착 시간을 신경 쓰면 약 73 회 반복해야하고 50 %의 정착 시간 동안 26 회만 기다려야합니다.

녹색 트레이스는 단일 최대 진폭 스파이크의 출력을 보여줍니다. 이것은 임의 노이즈 억제에 대한 아이디어를 제공합니다. 단일 샘플이 출력을 2.5 % 이상 변화시키지 않는 것처럼 보입니다.

파란 흔적은이 필터가 백색 노이즈로하는 일에 대한 주관적인 느낌을주는 것입니다. 이 PLOTFILT 실행에 대해 화이트 노이즈 입력으로 선택된 난수의 내용이 정확히 무엇인지 보장 할 수 없으므로 엄격한 테스트가 아닙니다. 그것은 얼마나 스쿼시되고 얼마나 매끄러 울지에 대한 거친 느낌을주기 위해서입니다.

PLOTFILT, FILTBITS 및 기타 PIC 펌웨어 개발에 유용한 기타 유용한 정보는 내 소프트웨어 다운로드 페이지 의 PIC Development Tools 소프트웨어 릴리스에서 사용할 수 있습니다 .

수치 정밀도에 대해 추가됨

의견 에서이 필터를 구현하는 데 필요한 비트 수에 대해 관심이 있다는 새로운 답변을 볼 수 있습니다. FF를 곱 하면 이진 포인트 아래에 새로운 비트 2 (FF) 가 생성 됩니다. 소형 시스템에서 FF는 일반적으로 1/2 N 으로 선택 되므로이 곱셈은 실제로 N 비트의 오른쪽 시프트로 실현됩니다.

따라서 FILT는 일반적으로 고정 소수점 정수입니다. 이것은 프로세서의 관점에서 수학을 변경하지 않습니다. 예를 들어 10 비트 A / D 판독 값을 필터링하고 N = 4 (FF = 1/16) 인 경우 10 비트 정수 A / D 판독 값 아래에 4 개의 분수 비트가 필요합니다. 대부분의 프로세서는 10 비트 A / D 판독으로 인해 16 비트 정수 연산을 수행합니다. 이 경우에도 정확히 동일한 16 비트 정수 연산을 수행 할 수 있지만 A / D 판독 값은 4 비트 씩 왼쪽으로 시작합니다. 프로세서는 차이점을 알지 못하고 필요하지 않습니다. 16 비트 정수 전체에 대해 계산을 수행하면 12.4 고정 소수점 또는 실제 16 비트 정수 (16.0 고정 소수점)로 간주됩니다.

일반적으로 수치 표현으로 인해 노이즈를 추가하지 않으려면 각 필터 폴에 N 비트를 추가해야합니다. 위의 예에서 2의 두 번째 필터는 정보를 잃지 않기 위해 10 + 4 + 4 = 18 비트를 가져야합니다. 실제로 8 비트 시스템에서는 24 비트 값을 사용합니다. 기술적으로 2의 두 번째 극만 더 넓은 값이 필요하지만 펌웨어 단순성을 위해 필터의 모든 극에 대해 동일한 표현을 사용하므로 동일한 코드를 사용합니다.

일반적으로 하나의 필터 폴 작업을 수행하기 위해 서브 루틴 또는 매크로를 작성한 다음 각 폴에 적용합니다. 서브 루틴 또는 매크로가 특정 프로젝트에서 사이클 또는 프로그램 메모리가 더 중요한지 여부에 따라 달라집니다. 어느 쪽이든, 나는 스크래치 상태를 사용하여 NEW를 서브 루틴 / 매크로로 전달하여 FILT를 업데이트하지만 NEW가 있던 동일한 스크래치 상태로로드합니다. 이는 한 극의 업데이트 된 FILT가 다음 것의 새로운 것. 서브 루틴 인 경우, 도중에 FILT를 가리키는 포인터를 갖는 것이 유용합니다. 이렇게하면 서브 루틴이 여러 번 호출되는 경우 메모리의 연속 필터에서 자동으로 작동합니다. 매크로를 사용하면 각 반복에서 작동하기 위해 주소를 전달하므로 포인터가 필요하지 않습니다.

코드 예

PIC 18에 대해 위에서 설명한 매크로의 예는 다음과 같습니다.

///////////////////////////////////////////////////////// /////////////////////////////////
//
// 매크로 필터 필트
//
// NEWVAL에서 새 값으로 필터 폴 하나를 업데이트합니다. NEWVAL이 (가)로 업데이트되었습니다
// 새로운 필터링 된 값을 포함합니다.
//
// FILT는 필터 상태 변수의 이름입니다. 24 비트로 가정
// 넓고 지역 은행.
//
// 필터 업데이트 공식은 다음과 같습니다.
//
// FILT <-FILT + FF (NEWVAL-FILT)
//
// FFF를 곱하면 FILTBITS 비트가 오른쪽으로 이동합니다.
//
/ 매크로 필터
  /쓰다
         dbankif lbankadr
         movf [arg 1] +0, w; NEWVAL <-NEWVAL-FILT
         subwf newval + 0
         movf [arg 1] +1, 승
         subwfb newval + 1
         movf [arg 1] +2, 승
         subwfb newval + 2

  /쓰다
  / loop n filtbits; 각 비트마다 한 번씩 NEWVAL을 오른쪽으로 이동
         rlcf newval + 2, w; NEWVAL 오른쪽으로 1 비트 이동
         rrcf newval + 2
         rrcf newval + 1
         rrcf newval + 0
    / endloop

  /쓰다
         movf newval + 0, w; 필터에 시프트 된 값을 추가하고 NEWVAL에 저장
         addwf [arg 1] +0, 승
         movwf [arg 1] +0
         movwf newval + 0

         movf newval + 1, 승
         addwfc [arg 1] +1, 승
         movwf [arg 1] +1
         movwf newval + 1

         movf newval + 2, 승
         addwfc [arg 1] +2, 승
         movwf [arg 1] +2
         movwf newval + 2
  / endmac

다음은 PIC 24 또는 dsPIC 30 또는 33과 유사한 매크로입니다.

///////////////////////////////////////////////////////// /////////////////////////////////
//
// 매크로 필터 ffbits
//
// 하나의 저역 통과 필터 상태를 업데이트합니다. 새로운 입력 값은 W1 : W0에 있습니다.
// 업데이트 할 필터 상태는 W2가 가리 킵니다.
//
// 업데이트 된 필터 값도 W1 : W0에 반환되고 W2는
// 필터 상태를 지난 첫 번째 메모리로. 따라서이 매크로는
// 일련의 계단식 저역 통과 필터를 업데이트하기 위해 연속적으로 호출되었습니다.
//
// 필터 공식은 다음과 같습니다.
//
// FILT <-FILT + FF (신규-FILT)
//
// FF를 곱한 값은 산술 오른쪽 시프트
// FFBITS.
//
// 경고 : W3이 휴지통에 있습니다.
//
/ 매크로 필터
  / var new ffbits integer = [arg 1]; 전환 할 비트 수를 얻습니다.

  /쓰다
  / write "; 하나의 극 저역 통과 필터링, 시프트 비트 ="ffbits
  / write ";"

         하위 w0, [w2 ++], w0; NEW-FILT-> ​​W1 : W0
         Subb w1, [w2--], w1

         lsr w0, # [v ffbits], w0; W1 : W0에서 결과를 오른쪽으로 이동
         sl w1, # [-16ffbits], w3
         ior w0, w3, w0
         asr w1, # [v ffbits], w1

         w0, [w2 ++], w0 추가; FILT를 추가하여 W1 : W0의 최종 결과 생성
         addc w1, [w2--], w1

         mov w0, [w2 ++]; 결과를 필터 상태에 기록, 사전 포인터
         mov w1, [w2 ++]

  /쓰다
  / endmac

이 두 예제는 PIC 어셈블러 프리 프로세서를 사용하여 매크로로 구현되며 내장 매크로 기능보다 기능이 뛰어납니다.


1
+1-바로 돈. 내가 추가 할 유일한 것은 이동 평균 필터가 일부 작업에 동기식으로 수행 할 때 (초음파 발생기를 구동하는 구동 파형 생성과 같은) 수행 할 수 있으므로 T는 이동하는 1 / T의 고조파를 필터링합니다. 평균 시간.
Jason S

2
좋은 대답이지만 두 가지만 있습니다. 첫째, 잘못된 필터를 선택하도록주의를 기울일 필요는 없습니다. 제 경우에는 그 차이에 대해 배워 본 적이 없으며, 비 졸업생에게도 마찬가지입니다. 때때로 그것은 단지 무지입니다. 그러나 두 번째 : 왜 고차 필터 대신 두 개의 1 차 디지털 필터를 캐스케이드합니까? (단지 이해하고, 나는 비판하지 않습니다)
clabacchio

3
2 개의 캐스케이드 단극 IIR 필터는 단일 2 차 IIR 필터보다 수치 문제에 더 견고하고 설계가 더 쉽습니다. 2 개의 계단식 스테이지에서는 낮은 Q (= 1/2?) 필터를 얻을 수 있지만 대부분의 경우 큰 문제는 아닙니다.
Jason S

1
@clabacchio : 언급해야 할 또 다른 문제는 펌웨어 구현입니다. 단일 극 저역 통과 필터 서브 루틴을 한 번 작성한 다음 여러 번 적용 할 수 있습니다. 실제로 필자는 일반적으로 메모리에서 포인터를 필터 상태로 가져 오기 위해 이러한 서브 루틴을 작성한 다음 포인터를 계속 진행하여 다중 극 필터를 쉽게 실현할 수 있도록 포인터를 진행시킵니다.
Olin Lathrop

1
1. 답변 해 주셔서 대단히 감사합니다. 이 IIR 필터를 사용하기로 결정했지만이 필터는 카운터 값을 평균화하고 비교하여 특정 범위의 변화를 감지해야하기 때문에 표준 저역 통과 필터로 사용되지 않습니다. 이 값은 하드웨어에 따라 크기가 매우 다르기 때문에 이러한 하드웨어 관련 변경 사항에 자동으로 반응하기 위해 평균을 취하고 싶었습니다.
sensslen

18

2 개 항목의 제곱의 평균을 평균으로 제한 할 수있는 경우 (예 : 2,4,8,16,32 등) 전용 분할없이 저 성능 마이크로에서 쉽고 효율적으로 나눌 수 있습니다. 비트 시프트로 수행 할 수 있습니다. 각 교대 권한은 2의 1 제곱입니다. 예 :

avg = sum >> 2; //divide by 2^2 (4)

또는

avg = sum >> 3; //divide by 2^3 (8)

기타


어떻게 도움이 되나요? OP는 주요 문제는 과거 샘플을 메모리에 유지하는 것이라고 말합니다.
Jason S

이것은 OP의 질문을 전혀 다루지 않습니다.
Rocketmagnet

12
OP는 PIC16과 링 버퍼의 메모리로 나누어 두 가지 문제가 있다고 생각했다. 이 답변은 나누기가 어렵지 않다는 것을 보여줍니다. 분명히 메모리 문제를 해결하지는 않지만 SE 시스템은 부분 답변을 허용하며 사용자는 각 답변에서 무언가를 가져 가거나 다른 답변을 편집하고 결합 할 수 있습니다. 다른 답변 중 일부는 나누기 연산이 필요하기 때문에 PIC16에서이를 효율적으로 달성하는 방법을 보여주지 않기 때문에 유사하게 불완전합니다.
Martin

8

거기 입니다 적은 메모리 요구 사항이 진정한 이동 평균 필터 (일명 '박스 카 필터 ")에 대한 대답은, 경우에 당신은 다운 샘플링 상관하지 않습니다. 이를 CIC ( Cascaded Integrator-Comb Filter )라고합니다. 아이디어는 일정 기간에 걸쳐 차이를 취하는 적분기가 있고, 주요 메모리 절약 장치는 다운 샘플링을 통해 적분기의 모든 값을 저장할 필요가 없다는 것입니다. 다음 의사 코드를 사용하여 구현할 수 있습니다.

function out = filterInput(in)
{
   const int decimationFactor = /* 2 or 4 or 8 or whatever */;
   const int statesize = /* whatever */
   static int integrator = 0;
   static int downsample_count = 0;
   static int ringbuffer[statesize];
   // don't forget to initialize the ringbuffer somehow
   static int ringbuffer_ptr = 0;
   static int outstate = 0;

   integrator += in;
   if (++downsample_count >= decimationFactor)
   {
     int oldintegrator = ringbuffer[ringbuffer_ptr];
     ringbuffer[ringbuffer_ptr] = integrator;
     ringbuffer_ptr = (ringbuffer_ptr + 1) % statesize;
     outstate = (integrator - oldintegrator) / (statesize * decimationFactor);
   }
   return outstate;
}

당신의 효과적인 이동 평균 길이는 decimationFactor*statesize있지만 statesize샘플 주위에 유지해야합니다 . 귀하의 경우 분명히 당신은 더 나은 성능을 얻을 수 statesize와는 decimationFactor2의 거듭 제곱이 때문에 분열과 나머지 운영자가 교대로 대체하고 마스크-의 ands을 얻을이다.


Postscript : 이동 평균 필터를 사용하기 전에 항상 간단한 IIR 필터를 고려해야한다는 Olin에 동의합니다. 박스 카 필터의 주파수 널이 필요하지 않은 경우 1 극 또는 2 극 저역 통과 필터가 정상적으로 작동합니다.

반면, 소멸을 목적으로 필터링하는 경우 (높은 샘플 레이트 입력을 취하고 낮은 레이트 프로세스에서 사용하기 위해 평균화하는 경우) CIC 필터가 원하는 것일 수 있습니다. (특히 statesize = 1을 사용할 수 있고 이전의 단일 적분기 값으로 링 버퍼를 사용하지 않는 경우)


8

Olin Lathrop이 이미 Digital Signal Processing 스택 교환 에서 설명한 1 차 IIR 필터를 사용하여 수학에 대한 심층 분석이 있습니다 (많은 예쁜 그림 포함).이 IIR 필터의 방정식은 다음과 같습니다.

y [n] = αx [n] + (1-α) y [n-1]

이것은 정수 만 사용하고 다음 코드를 사용하여 나누기를 사용하여 구현할 수 있습니다 (메모리에서 입력 할 때 디버깅이 필요할 수 있습니다).

/**
*  @details    Implement a first order IIR filter to approximate a K sample 
*              moving average.  This function implements the equation:
*
*                  y[n] = alpha * x[n] + (1 - alpha) * y[n-1]
*
*  @param      *filter - a Signed 15.16 fixed-point value.
*  @param      sample - the 16-bit value of the current sample.
*/

#define BITS 2      ///< This is roughly = log2( 1 / alpha )

short IIR_Filter(long *filter, short sample)
{
    long local_sample = sample << 16;

    *filter += (local_sample - *filter) >> BITS;

    return (short)((*filter+0x8000) >> 16);     ///< Round by adding .5 and truncating.
}

이 필터는 alpha 값을 1 / K로 설정하여 마지막 K 샘플의 이동 평균에 가깝습니다. 이전 코드 에서 LOG2 (K) 에 #defineing BITS하여 (예 : K = 16 BITS을 4로 설정 하고, K = 4 BITS를 2로 설정 하는 등) 수행하십시오.

(변경 사항이있는 즉시 여기에 나열된 코드를 확인하고 필요한 경우이 답변을 수정하겠습니다.)


6

단극 저역 통과 필터는 다음과 같습니다 (이동 평균, 차단 주파수 = CutoffFrequency). 매우 간단하고 빠르며 훌륭하게 작동하며 메모리 오버 헤드가 거의 없습니다.

참고 : newInput에 전달 된 것을 제외하고 모든 변수는 필터 함수를 넘어서는 범위

// One-time calculations (can be pre-calculated at compile-time and loaded with constants)
DecayFactor = exp(-2.0 * PI * CutoffFrequency / SampleRate);
AmplitudeFactor = (1.0 - DecayFactor);

// Filter Loop Function ----- THIS IS IT -----
double Filter(double newInput)
{
   MovingAverage *= DecayFactor;
   MovingAverage += AmplitudeFactor * newInput;

   return (MovingAverage);
}

참고 : 이것은 단일 스테이지 필터입니다. 여러 단계를 함께 연결하여 필터의 선명도를 높일 수 있습니다. 하나 이상의 스테이지를 사용하는 경우, DecayFactor (컷오프 주파수와 관련하여)를 조정하여 조정해야합니다.

그리고 분명히 당신이 필요로하는 것은 두 줄이 어디에나 배치되어 있으며, 그들 자신의 기능이 필요하지 않습니다. 이 필터는 이동 평균이 입력 신호의 이동 평균을 나타 내기 전에 상승 시간이 있습니다. 그 램프 업 시간을 우회 해야하는 경우 MovingAverage를 0 대신에 newInput의 첫 번째 값으로 초기화하고 첫 번째 newInput이 이상 치가 아니기를 바랍니다.

(CutoffFrequency / SampleRate)의 범위는 0에서 0.5 사이입니다. DecayFactor는 0과 1 사이의 값이며 일반적으로 1에 가깝습니다.

단 정밀도 플로트는 대부분의 경우 충분합니다. 정수를 고수 해야하는 경우 DecayFactor와 Amplitude Factor를 분수로 변환하여 분자가 정수로 저장되고 분모가 정수 2의 정수로 변환됩니다 (따라서 오른쪽으로 비트 시프트 할 수 있음) 필터 루프 중에 분할하지 않고 분모). 예를 들어 DecayFactor = 0.99이고 정수를 사용하려는 경우 DecayFactor = 0.99 * 65536 = 64881을 설정할 수 있습니다. 그런 다음 필터 루프에서 DecayFactor를 곱하면 결과를 >> 16으로 변경하면됩니다.

이에 대한 자세한 정보는 온라인에있는 훌륭한 책인 재귀 필터에 대한 19 장을 참조하십시오 . http://www.dspguide.com/ch19.htm

PS 이동 평균 패러다임의 경우 DecayFactor 및 AmplitudeFactor 설정에 대한 다른 접근 방식은 사용자의 요구와 더 관련이있을 수 있습니다. 이전의 약 6 개 항목의 평균을 합산하여 신중하게 수행한다고 가정 해 보겠습니다. 6이므로 AmplitudeFactor를 1/6로, DecayFactor를 (1.0-AmplitudeFactor)로 설정할 수 있습니다.


4

간단한 IIR 필터를 사용하여 일부 응용 프로그램의 이동 평균을 추정 할 수 있습니다.

가중치는 0..255 값, 높은 값 = 평균화 시간 단축

값 = (newvalue * weight + value * (256-weight)) / 256

반올림 오류를 피하기 위해 값은 일반적으로 길어질 수 있으며, 그 중 '실제'값으로 상위 바이트 만 사용합니다.


3

다른 모든 사람들은 IIR과 FIR의 유용성 및 2의 제곱에 대해 철저히 언급했습니다. 구현 세부 정보를 제공하고 싶습니다. 아래는 FPU가없는 소형 마이크로 컨트롤러에서 잘 작동합니다. 곱셈이 없으며 N을 2의 거듭 제곱으로 유지하면 모든 구간이 단일 사이클 비트 시프 팅입니다.

기본 FIR 링 버퍼 : 마지막 N 값의 실행 버퍼와 버퍼에있는 모든 값의 실행 SUM을 유지합니다. 새 샘플이 들어올 때마다 SUM에서 버퍼에서 가장 오래된 값을 빼고 새 샘플로 바꾸고 새 샘플을 SUM에 추가하고 SUM / N을 출력합니다.

unsigned int Filter(unsigned int sample){
    static unsigned int buffer[N];
    static unsigned char oldest = 0;
    static unsigned long sum;

    sum -= buffer[oldest];
    sum += sample;
    buffer[oldest] = sample;
    oldest += 1;
    if (oldest >= N) oldest = 0;

    return sum/N;
}

수정 된 IIR 링 버퍼 : 마지막 N 값의 실행중인 SUM을 유지합니다. 새 샘플이 들어올 때마다 SUM-= SUM / N, 새 샘플을 추가하고 SUM / N을 출력합니다.

unsigned int Filter(unsigned int sample){
    static unsigned long sum;

    sum -= sum/N;
    sum += sample;

    return sum/N;
}

내가 잘 읽고 있다면 1 차 IIR 필터를 설명하는 것입니다. 빼는 값은 가장 오래된 값이 아니라 이전 값의 평균입니다. 1 차 IIR 필터는 확실히 유용 할 수 있지만 출력이 모든주기적인 신호에 대해 동일하다고 제안 할 때 무슨 의미인지 잘 모르겠습니다. 10KHz 샘플 속도에서, 20Hz 박스 필터에 100Hz 구형파를 공급하면 20 개의 샘플에 대해 균일하게 상승하고, 30에 대해 높게 앉고, 20 개의 샘플에 대해 균일하게 떨어지고, 30에 대해 낮은 신호를 생성합니다. IIR 필터 ...
supercat

... 파형이 급격히 상승하기 시작하여 입력 최대 값 근처에서 점차적으로 떨어지지 만, 급격히 떨어지기 시작하고 입력 최소값 근처에서 점차적으로 떨어집니다. 매우 다른 행동.
supercat

네 말이 맞아, 나는 두 종류의 필터를 혼동하고 있었다. 이것은 실제로 1 차 IIR입니다. 일치하도록 답변을 변경하고 있습니다. 감사.
Stephen Collings

한 가지 문제는 단순한 이동 평균이 유용하거나 유용하지 않을 수 있다는 것입니다. IIR 필터를 사용하면 상대적으로 적은 양의 calc가있는 멋진 필터를 얻을 수 있습니다. 설명하는 FIR은 시간의 사각형 (주파수의 sinc) 만 제공 할 수 있으며 사이드 로브를 관리 할 수 ​​없습니다. 클럭 틱을 절약 할 수 있다면 몇 개의 정수 곱셈을 던져서 대칭으로 조정 가능한 FIR을 만드는 것이 좋습니다.
Scott Seidman

@ScottSeidman : FIR의 각 단계마다 단순히 단계의 입력과 해당 단계의 이전 저장된 값을 출력 한 다음 입력을 저장합니다 (숫자 범위가있는 경우 합계를 사용할 수 있음). 평균보다는). 박스 필터보다 나은지 여부는 애플리케이션에 따라 다릅니다 (예를 들어, 총 지연 시간이 1ms 인 박스 필터의 스텝 응답은 입력 변경시 불쾌한 d2 / dt 스파이크가 발생하지만 1ms 후에는 다시 발생하지만 총 1ms 지연을 갖는 필터에 가능한 최소 d / dt).
supercat

2

으로 mikeselectricstuff는 당신이 정말로 당신의 메모리 요구 사항을 줄일 필요가, 당신은 (대신 직사각형 펄스의) 지수 인 당신의 임펄스 응답 괜찮다면 말했다, 나는 갈 것이다 이동 평균 지수 필터를. 나는 그들을 광범위하게 사용합니다. 해당 유형의 필터를 사용하면 버퍼가 필요하지 않습니다. N 개의 과거 샘플을 저장할 필요가 없습니다. 딱 하나만. 따라서 메모리 요구 사항은 N의 계수로 줄어 듭니다.

또한 그에 대한 구분이 필요하지 않습니다. 곱셈 만. 부동 소수점 산술에 액세스 할 수있는 경우 부동 소수점 곱셈을 사용하십시오. 그렇지 않으면 정수 곱셈을 수행하고 오른쪽으로 이동합니다. 그러나 우리는 2012 년에 있으며 부동 소수점 숫자로 작업 할 수있는 컴파일러 (및 MCU)를 사용하는 것이 좋습니다.

더 많은 메모리 효율성과 빠른 속도 (순환 버퍼에서 항목을 업데이트 할 필요가 없음) 외에도 지수 임펄스 응답이 자연이 행동하는 방식과 더 잘 일치하기 때문에 더 자연 스럽다고 말합니다 .


5
부동 소수점 숫자 사용 권장 사항에 동의하지 않습니다. OP는 아마도 이유 때문에 8 비트 마이크로 컨트롤러를 사용합니다. 하드웨어 부동 소수점을 지원하는 8 비트 마이크로 컨트롤러를 찾는 것은 어려운 작업입니다 (아는가?). 하드웨어를 지원하지 않고 부동 소수점 숫자를 사용하는 것은 리소스를 많이 사용하는 작업입니다.
PetPaulsen

5
항상 부동 소수점 기능이있는 프로세스를 사용해야한다고 말하는 것은 어리석은 일입니다. 또한 모든 프로세서는 부동 소수점을 수행 할 수 있으며 속도 문제 일뿐입니다. 임베디드 세계에서는 몇 센트의 빌드 비용이 의미가 있습니다.
Olin Lathrop

@Olin Lathrop 및 PetPaulsen : 하드웨어 FPU가있는 MCU를 사용해야한다고 말한 적이 없습니다. 내 대답을 다시 읽으십시오. "(및 MCU)"란 유동적 인 방식으로 소프트웨어 부동 소수점 산술 작업을 수행 할 수있을만큼 강력한 MCU를 의미하며 모든 MCU에 해당되는 것은 아닙니다.
Telaclavo

4
단극 저역 통과 필터에만 부동 소수점 (하드웨어 또는 소프트웨어)을 사용할 필요가 없습니다.
Jason S

1
만약 그가 부동 소수점 연산을했다면 그는 처음부터 나누기를 반대하지 않을 것이다.
Federico Russo

0

@olin 및 @supercat에 거의 영향을 받았지만 다른 사람들에게는 무시되는 IIR 필터의 한 가지 문제는 반올림으로 인해 약간의 부정확성 (및 잠재적으로 바이어스 / 잘림)이 발생한다는 것입니다. 사용되면, 시프트 오른쪽은 새로운 샘플의 LSB를 체계적으로 제거합니다. 즉, 시리즈가 얼마나 오래 지속될 수 있는지 평균은 결코 시리즈를 고려하지 않습니다.

예를 들어, 천천히 감소하는 계열 (8,8,8, ..., 8,7,7,7, ... 7,6,6,)이 시작되고 평균이 실제로 8이라고 가정합니다. 주먹 "7"샘플은 필터 강도에 관계없이 평균을 7로 가져옵니다. 하나의 샘플 만. 6에 대한 같은 이야기. 이제 반대의 생각 : 세리가 올라갑니다. 샘플이 변경 될 수있을만큼 커질 때까지 평균은 영원히 7에 머무 릅니다.

물론 1 / 2 ^ N / 2를 추가하여 "바이어스"를 교정 할 수 있지만 정밀도 문제는 실제로 해결되지 않습니다.이 경우 감소하는 시리즈는 샘플이 8-1이 될 때까지 8에서 영원히 유지됩니다. / 2 ^ (N / 2). 예를 들어 N = 4의 경우 0보다 큰 샘플은 평균을 변경하지 않습니다.

나는 그 해결책이 잃어버린 LSB의 누산기를 보유 할 것이라고 생각합니다. 그러나 코드를 준비 할만 큼 충분히 만들지 않았으며 다른 시리즈의 경우 (예 : 7,9,7,9가 평균 8이 될지 여부) IIR 전력에 해를 끼치 지 않을 것이라고 확신하지 않습니다. .

@Olin, 2 단계 캐스케이드도 설명이 필요합니다. 반복 에서 첫 번째 결과를 두 번째 평균으로 공급하여 두 개의 평균 값을 유지하는 것을 의미 합니까? 이것의 장점은 무엇입니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.