高速でメモリ効率の良い移動平均計算


33

Cで移動平均を計算するための時間とメモリの効率的なソリューションを探しています。専用の除算ユニットを持たないPIC 16にいるため、除算を避ける必要があります。

現時点では、すべての値をリングバッファに保存し、新しい値が到着するたびに合計を保存および更新します。これは本当に効率的ですが、残念ながら利用可能なメモリのほとんどを使用します...


3
これを行うためのスペース効率の良い方法はこれ以上ないと思います。
Rocketmagnet

4
@JobyTaffeyは、制御システムで非常に広く使用されているアルゴリズムであり、限られたハードウェアリソースに対処する必要があります。だから私は彼がSOよりももっと助けを見つけると思う。
クラバッキオ

3
@Joby:この問題については、リソースが限られた小さなシステムに関連するいくつかのしわがあります。私の答えをご覧ください。SOの人々が慣れているような大規模なシステムでは、これを非常に異なる方法で行うことになります。これは、電子機器の設計の私の経験でたくさん出てきました。
オリンラスロップ

1
同意する。これは組み込みシステムに関連しているため、このフォーラムには非常に適しています。
Rocketmagnet

異議を撤回します
トビージャフィー

回答:


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ビットの右シフトになります。それ以外の場合、このフィルターは1つの減算と1つの加算のみを必要としますが、通常、数値は入力値よりも広くする必要があります(以下の別のセクションで数値精度について詳しく説明します)。

私は通常、A / Dの読み取りを必要な速度よりも大幅に高速化し、これらのフィルターのうちの2つをカスケード接続します。これは、直列の2つのRCフィルターに相当するデジタルであり、ロールオフ周波数より12 dB /オクターブ上で減衰します。ただし、A / D測定では、通常、ステップ応答を考慮して、時間領域でフィルターを確認する方が適切です。これは、測定対象が変化したときに、システムがどれだけ速く変化を見るかを示します。

これらのフィルターの設計を容易にするため(FFを選択し、カスケードする数を決定することを意味するだけです)、プログラムFILTBITSを使用します。カスケードされた一連のフィルターの各FFのシフトビット数を指定すると、ステップ応答とその他の値が計算されます。実際、私は通常、ラッパースクリプトPLOTFILTを介してこれを実行します。これにより、FILTBITSが実行され、CSVファイルが作成され、CSVファイルがプロットされます。たとえば、「PLOTFILT 4 4」の結果は次のとおりです。

PLOTFILTの2つのパラメーターは、上記のタイプの2つのフィルターがカスケード接続されることを意味します。値4は、FFによる乗算を実現するためのシフトビットの数を示します。したがって、この場合、2つのFF値は1/16です。

赤いトレースはユニットのステップ応答であり、注目すべき主なものです。たとえば、これは、入力が瞬間的に変化した場合、結合されたフィルターの出力が60回の反復で新しい値の90%に落ち着くことを示しています。約95%の整定時間を気にする場合は、約73回の反復を待つ必要があり、50%の整定時間では26回の反復のみです。

緑色のトレースは、単一の最大振幅スパイクからの出力を示しています。これにより、ランダムノイズ抑制のアイデアが得られます。単一のサンプルが出力に2.5%以上の変化を引き起こすことはないようです。

青いトレースは、このフィルターがホワイトノイズで行うことの主観的な感覚を与えるためのものです。PLOTFILTの今回の実行でホワイトノイズ入力として選択された乱数の内容が正確に何であるかは保証されないため、これは厳密なテストではありません。どれだけつぶされるか、どれだけ滑らかであるかを大まかに感じさせるだけです。

PLOTFILT、多分FILTBITS、およびその他の多くの有用なもの、特にPICファームウェア開発用のソフトウェアは、ソフトウェアダウンロードページのPIC開発ツールソフトウェアリリースで入手できます

数値精度について追加

私はコメントから、このフィルターを実装するのに必要なビット数を議論することに関心があるという新しい答えを見つけました。FFで乗算すると、バイナリポイントの下にLog 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番目の極だけがより広い値を必要としますが、ファームウェアの単純化のために、通常はフィルターのすべての極に対して同じ表現を使用し、それにより同じコードを使用します。

通常、1つのフィルターポール操作を実行するサブルーチンまたはマクロを記述し、それを各ポールに適用します。サブルーチンまたはマクロは、サイクルまたはプログラムメモリがその特定のプロジェクトでより重要であるかどうかに依存します。いずれにせよ、サブルーチン/マクロにNEWを渡すためにいくつかのスクラッチ状態を使用して、FILTを更新しますが、NEWが入っていた同じスクラッチ状態にロードします。次の新機能。サブルーチンの場合、途中でFILTを指すポインターを持つと便利です。これは、途中でFILTの直後に更新されます。このようにして、複数回呼び出された場合、サブルーチンはメモリ内の連続したフィルターで自動的に動作します。マクロを使用すると、各反復で動作するアドレスを渡すため、ポインターは必要ありません。

コード例

PIC 18について上記で説明したマクロの例を次に示します。

///////////////////////////////////////////////// ///////////////////////////////
//
//マクロフィルターフィルト
//
// NEWVALの新しい値で1つのフィルターポールを更新します。NEWVALが更新されました
//新しいフィルタリングされた値が含まれます。
//
// FILTはフィルター状態変数の名前です。24ビットと想定されます
//広く、地元の銀行。
//
//フィルタを更新するための式は次のとおりです。
//
// FILT <-FILT + FF(NEWVAL-FILT)
//
// FFによる乗算は、FILTBITSビットの右シフトによって実行されます。
//
/マクロフィルター
  /書きます
         dbankif lbankadr
         movf [arg 1] +0、w; NEWVAL <-NEWVAL-FILT
         subwf newval + 0
         movf [引数1] + 1、w
         subwfb newval + 1
         movf [引数1] + 2、w
         subwfb newval + 2

  /書きます
  / loop n filtbits;各ビットにつき1回、NEWVALを右にシフトします
         rlcf newval + 2、w; NEWVALを右に1ビットシフト
         rrcf newval + 2
         rrcf newval + 1
         rrcf newval + 0
    / endloop

  /書きます
         movf newval + 0、w;シフトした値をフィルターに追加し、NEWVALに保存する
         addwf [引数1] + 0、w
         movwf [引数1] +0
         movwf newval + 0

         movf newval + 1、w
         addwfc [arg 1] + 1、w
         movwf [arg 1] +1
         movwf newval + 1

         movf newval + 2、w
         addwfc [arg 1] + 2、w
         movwf [arg 1] +2
         movwf newval + 2
  / endmac

そして、これはPIC 24またはdsPIC 30または33の同様のマクロです:

///////////////////////////////////////////////// ///////////////////////////////
//
//マクロフィルターffbits
//
// 1つのローパスフィルターの状態を更新します。新しい入力値はW1:W0にあります
//そして、更新されるフィルタ状態はW2によってポイントされます。
//
//更新されたフィルター値もW1:W0で返され、W2は
//フィルタ状態を過ぎた最初のメモリへ。したがって、このマクロは
//一連のカスケードローパスフィルターを更新するために連続して呼び出されます。
//
//フィルタ式は次のとおりです。
//
// FILT <-FILT + FF(NEW-FILT)
//
// FFによる乗算は、算術右シフトによって実行されます
// FFBITS。
//
//警告:W3はゴミ箱に捨てられます。
//
/マクロフィルター
  / var new ffbits integer = [arg 1];シフトするビット数を取得

  /書きます
  / write ";単極ローパスフィルタリングを実行、シフトビット=" ffbits
  /書きます " ;"

         sub w0、[w2 ++]、w0; NEW-FILT-> W1:W0
         subb w1、[w2--]、w1

         lsr w0、#[v ffbits]、w0;結果をW1:W0で右にシフト
         sl w1、#[-16 ffbits]、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の高調波をフィルターで除外することです平均時間。
ジェイソンS

2
いい答えですが、2つだけです。まず、誤ったフィルターを選択することにつながるのは必ずしも注意の欠如ではありません。私の場合、その違いについて教えられたことは一度もないし、同じことが卒業していない人にも当てはまる。ですから、時々それは単に無知です。しかし、2番目:高次フィルタを使用する代わりに、2つの1次デジタルフィルタをカスケード接続するのはなぜですか?(理解するために、私は批判していません)
クラバキオ

3
カスケード接続された2つの単極IIRフィルターは、単一の2次IIRフィルターよりも数値の問題に対してより堅牢であり、設計が容易です。トレードオフは、2つのカスケードステージを使用すると、低いQ(= 1/2?)フィルターが得られることですが、ほとんどの場合、それは大した問題ではありません。
ジェイソンS

1
@clabacchio:言及すべき別の問題は、ファームウェアの実装です。単極ローパスフィルターサブルーチンを1回作成し、それを複数回適用できます。実際、私は通常、メモリ内のフィルター状態へのポインターを取得するサブルーチンを作成し、ポインターを進めて、多極フィルターを実現するために連続して簡単に呼び出せるようにします。
オリンラスロップ

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は、主な問題は過去のサンプルをメモリに保持することだと言います。
ジェイソンS

これは、OPの質問にはまったく対応していません。
Rocketmagnet

12
OPは、PIC16での分割とリングバッファーのメモリという2つの問題があると考えました。この答えは、分割が難しくないことを示しています。確かにメモリの問題には対処していませんが、SEシステムでは部分的な回答が許可されており、ユーザーは各回答から何かを自分で取得したり、他の回答を編集して組み合わせたりすることもできます。他の回答のいくつかは除算演算を必要とするため、PIC16でこれを効率的に達成する方法を示していないため、同様に不完全です。
マーティン

8

そこ少ないメモリ要件を持つ真の移動平均フィルタ(別名「ボックスカーフィルタ」)のための答えが、あれば、あなたがダウンサンプリング気にしません。カスケード積分櫛型フィルター(CIC)と呼ばれます。考え方としては、ある期間にわたって差分を取る積分器があり、重要なメモリ節約デバイスは、ダウンサンプリングによって、積分器のすべての値を保存する必要がないということです。次の擬似コードを使用して実装できます。

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サンプルを保持するだけで十分です。statesizeand decimationFactorが2の累乗である場合、除算演算子と剰余演算子がシフトとマスクアンドに置き換えられるため、明らかにパフォーマンスが向上します。


追記:移動平均フィルターの前に、単純なIIRフィルターを常に考慮する必要があることは、Olinに同意します。ボックスカーフィルターの周波数ヌルが不要な場合は、1極または2極のローパスフィルターがおそらく正常に機能します。

一方、デシメーションの目的でフィルタリングする場合(高サンプルレートの入力を取得し、低レートプロセスで使用するために平均化する場合)、CICフィルターはまさにあなたが探しているものです。(特に、statesize = 1を使用して、リングバッファを以前の単一のインテグレーター値のみで回避できる場合)


8

Olin Lathropがデジタル信号処理スタック交換で既に説明した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.
}

このフィルターは、アルファの値を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を調整する必要があります(カットオフ周波数に関連して)。

そして、明らかに必要なのは、どこにでも配置された2行だけで、独自の機能は必要ありません。このフィルターには、移動平均が入力信号の移動平均を表すまでのランプアップ時間があります。そのランプアップ時間をバイパスする必要がある場合は、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個のアイテムを追加して除算します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;
}

私があなたを正しく読んでいるなら、あなたは一次IIRフィルターを説明しているのです。減算する値は、最も古い値ではなく、前の値の平均です。1次IIRフィルターは確かに有用ですが、出力がすべての周期信号で同じであることを示唆するときに何を意味するのかわかりません。10KHzのサンプルレートで、100Hzの方形波を20段のボックスフィルターに供給すると、20サンプルで均一に上昇し、30でハイになり、20サンプルで均一に低下し、30でローになります。 IIRフィルター
...-supercat

...急激に上昇を開始し、入力の最大値の近くで(ただしではない)徐々に水平になり、その後、入力の最小値の近くで(ではなく)急激に低下し、徐々に水平になる波を生成します。非常に異なる動作。
supercat

そのとおり、2種類のフィルターを混同していました。これは確かに一次IIRです。一致するように答えを変更しています。ありがとう。
スティーブンコリングズ

1つの問題は、単純な移動平均が有用な場合とそうでない場合があることです。IIRフィルターを使用すると、比較的少ない計算で素晴らしいフィルターを取得できます。説明するFIRは、時間内に長方形(周波数のsinc)しか提供できず、サイドローブを管理することはできません。クロックの刻みを節約できる場合は、整数の乗算を数回実行して、対称で調整可能なFIRにするのがよいでしょう。
スコットサイドマン

@ScottSeidman:FIRの各ステージに単純にそのステージへの入力の平均とその前に保存された値を出力し、入力を保存する場合(乗算が必要な場合は、合計を使用できます)平均ではなく)。それがボックスフィルターより優れているかどうかは、アプリケーションに依存します(たとえば、合計遅延が1ミリ秒のボックスフィルターのステップ応答は、入力が変更されたときに厄介なd2 / dtスパイクを持ち、再び1ミリ秒後になりますが、合計1ミリ秒の遅延を持つフィルターの可能な最小d / dt)。
supercat

2

以下のようmikeselectricstuffは言った、あなたが本当にあなたの記憶のニーズを削減する必要がある、とあなたがあなたのインパルス応答が(代わりに、矩形パルスの)指数関数的であることを気にしないならば、私はのために行くだろう指数移動平均フィルタ。私はそれらを広範囲に使用しています。このタイプのフィルターを使用すると、バッファーは必要ありません。N個の過去のサンプルを保存する必要はありません。一つだけ。したがって、メモリ要件はN倍に削減されます。

また、そのための分割は必要ありません。乗算のみ。浮動小数点演算にアクセスできる場合は、浮動小数点乗算を使用してください。それ以外の場合は、整数の乗算を行い、右にシフトします。ただし、2012年です。浮動小数点数を操作できるコンパイラ(およびMCU)を使用することをお勧めします。

より効率的なメモリと高速であることに加えて(循環バッファ内のアイテムを更新する必要はありません)、より自然であると言えます。なぜなら、ほとんどの場合、指数インパルス応答は自然の振る舞いによりよく一致するからです。


5
浮動小数点数を使用することをお勧めすることに同意しません。OPはおそらく、理由により8ビットマイクロコントローラーを使用しています。ハードウェア浮動小数点サポートを備えた8ビットマイクロコントローラーを見つけるのは難しい作業です(ご存知ですか?)。また、ハードウェアサポートなしで浮動小数点数を使用すると、非常にリソースを消費するタスクになります。
PetPaulsen

5
常に浮動小数点機能を備えたプロセスを使用する必要があると言うのはばかげています。その上、どのプロセッサでも浮動小数点を実行できますが、それは単に速度の問題です。組み込みの世界では、数セントのビルドコストが意味を持つ場合があります。
オリンラスロップ

@Olin Lathrop and PetPaulsen:私は彼がハードウェアFPUを備えたMCUを使うべきだと言ったことは一度もありません。私の答えを読み直してください。「(およびMCU)」とは、すべてのMCUに当てはまるわけではない、ソフトウェアの浮動小数点演算を流動的な方法で動作させるのに十分なほど強力なMCUを意味します。
Telaclavo

4
1ポールローパスフィルターだけに浮動小数点(ハードウェアまたはソフトウェア)を使用する必要はありません。
ジェイソンS

1
彼が浮動小数点演算を行っていれば、そもそも除算に反対しませんでした。
フェデリコルッソ

0

@olinと@supercatにほとんど触れられているが、明らかに他の人に無視されているIIRフィルターの問題の1つは、切り捨てによって不正確さ(および潜在的にバイアス/切り捨て)が導入されることです:Nが2のべき乗であり、整数演算のみが使用すると、右シフトは体系的に新しいサンプルのLSBを削除します。これは、シリーズがどれくらい長くなる可能性があるかを意味しますが、平均ではそれらが考慮されません。

たとえば、徐々に減少する系列(8,8,8、...、8,7,7,7、... 7,6,6、)を想定し、平均が最初は実際に8であると仮定します。最初の「7」サンプルは、フィルター強度に関係なく、平均を7にします。1つのサンプルのみ。6などと同じ話です。今度は反対のことを考えてください。セリエが上がります。サンプルが変化するのに十分な大きさになるまで、平均は永遠に7のままになります。

もちろん、1/2 ^ N / 2を追加することで「バイアス」を修正できますが、それでも精度の問題は実際には解決しません。その場合、減少するシリーズは、サンプルが8-1になるまで8のままになります/ 2 ^(N / 2)。たとえば、N = 4の場合、ゼロを超えるサンプルは平均値を変更しません。

そのための解決策は、失われたLSBのアキュムレーターを保持することを意味すると思います。しかし、私はコードの準備が十分にできていなかったので、シリーズの他のいくつかのケースでIIRの力を害しないかどうかはわかりません(たとえば、7、9、7、9が平均して8になるかどうか) 。

@ Olin、2段カスケードも説明が必要です。反復で最初の結果を2番目の結果に2つの平均値を保持することを意味しますか?これの利点は何ですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.