高周波ノイズを含む信号にローパスフィルタをかけたら出力が全く想定と異なる挙動をしたので、これを解決する話。

まとめると

  • ローパスフィルタをかける信号の値域は確認しよう
  • ローパスフィルタと言って何も考えずに適当に移動平均を取るのはやめよう
  • エッジケースは確認しよう

経緯

スマートフォンで方位角を取得する処理を実装していて、センサの出力値をそのまま利用するのでは信号が安定しない (高周波のノイズ成分) ため、ローパスフィルタを適用しようとした。
まずは適当に10段の移動平均を取ることでローパスをかけようと思った。

起こったこと 1

  • 信号が安定した
  • 出力の時間遅れが発生した
  • 0°付近で方位角が一瞬荒ぶるようになった

なぜこうなったのか 1

時間遅れについては、まあ当たり前という感じで、例えば東を向いている状態から北を向いたとして、すぐに出力が北に変わるわけではなく、東から少し遅れて北に出力が移り変わっていくはずである。これはローパスフィルタを通している以上必ず起こりうることであって、どうしようもない。時間遅れを減らすには、移動平均に使う過去データ量を減らせば良い。8段くらいにした。

次に、0°付近で方位角が一瞬荒ぶるようになったというもの。これはおそらく想像できるであろうところだが、360と0の平均を取ると180になる。西から北に向いて、そこからさらに北北東を向く、もしくはその逆をすると再現できる。これも移動平均を使っている以上避けられない事態であって、そもそもローパスフィルタとして移動平均を使わないという解決策がある。

やったこと 1

移動平均が使えないので、8段のローパスフィルタを8段のメジアンフィルタに変えた。

起こったこと 2

  • 0°付近で方位角が荒ぶるのが修正された
  • 180°付近で方位角が荒ぶるようになった
    • 180°と0°を行ったり来たりするようになった

なぜこうなったのか 2

気づくまで30分くらいかかったが、センサの出力値は-π [rad]からπ [rad]までであった。Degreeに直すと-180°から180°までである。(ちゃんと調べていないけど片方はexclusiveだと思う)
ここで、メジアンフィルタの処理を思い出してみる。今回は8段であるので、

  • 一番古いデータを捨て、最新のデータを入れる
  • データを降順または昇順に並び替える
  • 4つめのデータと5つめのデータの平均値を取り、メジアンとする

このような処理になる。

例えば180° (真南) を向いた際、高周波ノイズによってセンサの出力値が178°から182°程度で得られたとする。出力値が-π [rad]からπ [rad]までであったことを思い出すと、リストの1~4つ目の要素は-3.14程度、5~8つ目のデータは3.14程度の値が得られることになる。このときのメジアンは(3.14 - 3.14)/2 = 0である…

やったこと 2

  • 8段のメジアンフィルタを7段のメジアンフィルタにした

起こったこと 3

  • 想定した出力を得た🎉