1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ArduinoとMEMSマイクでFFT解析を試す

Last updated at Posted at 2024-09-27

概要

今回はArduinoでFFT解析を試してみました。具体的にはRP2040を使用し、内蔵ADCでアナログ方式のMEMSマイクが出力する信号を処理します。精度は微妙ですが、とても簡単に試せました。
picture.jpg

開発環境

RP2040はArduino開発環境で扱います。予めボードは追加しておく必要があります。

  • Windows 11 Home 23H2
  • Arduino IDE 2.3.0
  • Arduino-Pico 3.8.1
  • Arduino FFT 2.0.2

部品

まず、要となるRP2040の内蔵ADCは分解能が12ビット、SAR型です。少し精度は不安ですが、詳細については後で補足します。マイクは回路をシンプルに組むため、アンプが内蔵された製品を選びました。外部にコンデンサ、抵抗器を接続するだけで増幅率は変更できます。今回は最大の増幅率20dBに設定しました。また、マイクの信号に含まれる直流分はコンデンサに通すことで除去します。

販売コード 名称 型番 単価 数量 小計
117044 Seeed Studio XIAO RP2040 102010428 850円 1個 850円
108940 MEMSマイク AE-SPU0414 220円 1個 220円
105156 ブレッドボード BB-601 170円 1個 170円
117891 電解コンデンサ 2.2uF 50PX2R2MEFC5X11 10円 1個 10円
104620 電解コンデンサ 1uF UFG1H010MDM 10円 1個 10円

部品代は合計1260円でした。秋月電子通商から全て入手できます。

ADCの速度

サンプリング周波数を48kHzに設定するため、ADCの速度は1回当たり20us以内に収める必要があります。そこで以下のプログラムを書き込み、実際に掛かる時間を測定してみました。分解能は12ビットを指定します。また、クロック周波数はデフォルトの125MHzに設定しました。

adc.ino
#define MICROPHONE_PIN A0

const long repeat = 100000;


void setup() {
  delay(5000);

  Serial.begin(115200);
  Serial.println("Started measuring...");

  analogReadResolution(12); //12 bits

  unsigned long startTime = micros();
  for (long i = 0; i < repeat; i++) {
    int result = analogRead(MICROPHONE_PIN);
  }
  unsigned long stopTime = micros();

  Serial.println("Stopped measuring");
  Serial.println(stopTime - startTime);
}

void loop() {
  //do something
}

結果は10万回当たり約470ms、平均すると1回当たり約5usでした。問題なさそうです。

プログラム

今回のFFT解析について、サンプル数$N$が1024点、サンプリング周波数$f_s$が48kHzなので周波数分解能$\Delta f$は次式より約47Hzと求まります。サンプル数は必ず2の冪数で指定します。ここから時間窓長$T$も必然的に決まります。また、サンプリング定理よりナイキスト周波数はサンプリング周波数の半分なので24kHzと求まります。つまり、理論上は最大24kHzまで測定できることになります。

\Delta f=\frac{f_s}{N}=\frac{1}{T}

リーケージ誤差を減らすため、窓関数についても考えます。無難にハン窓を使いたいところですが、ライブラリにバグ1があるみたいなので今回はハミング窓を使っています。振幅の値が重要なら正規化や窓関数の補正を行う必要もありますが、今回は省略しました。

fft.ino
#include <ArduinoFFT.h>

#define MICROPHONE_PIN A2

const double samplingFrequency = 48000.0f; //48kHz
const int samples = 1024; //this value must always be a power of 2

double real[samples] = {0};
double imaginary[samples] = {0};

ArduinoFFT<double> fft = ArduinoFFT<double>(real, imaginary, samples, samplingFrequency);


void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(17, OUTPUT); //red
  pinMode(16, OUTPUT); //green
  pinMode(25, OUTPUT); //blue
  pinMode(MICROPHONE_PIN, INPUT);

  //turn off the onboard LED
  digitalWrite(17, HIGH); //red
  digitalWrite(16, HIGH); //green
  digitalWrite(25, HIGH); //blue

  analogReadResolution(12); //12 bits
  delay(1000);
}

void loop() {
  static const int samplingPeriod = floor(1000000.0f / samplingFrequency); //in microseconds

  for (int i = 0; i < samples; i++) {
    unsigned long time = micros();
    real[i] = analogRead(MICROPHONE_PIN);
    imaginary[i] = 0;

    while (micros() < (time + samplingPeriod)) {
      //do nothing
    }
  }

  fft.dcRemoval();
  fft.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  fft.compute(FFT_FORWARD);
  fft.complexToMagnitude(); //overwrite the first half of the integer data array

  for (int i = 0; i < samples / 2; i++) {
    if (i > 0) {
      Serial.print(",");
    }
    Serial.print(real[i]);
  }
  Serial.println();

  delay(1000);
}

実行すると随時シリアルモニタにFFT解析の結果が出力されるはずです。これをコピーしてCSVファイルへ保存し、表計算ソフトに取り込めばグラフ化できます。

検証

こちらのアプリを使用して1kHzの純音を再生し、FFT解析の結果を確認します。
chart-1.png
概ね1kHz付近にピークが現れることを確認できました。

RP2040の内蔵ADCは精度が低い可能性

ここでRP2040の内蔵ADCについて、少し補足します。こちらのデータシート、565ページ目を見ると微分非直線性誤差、及び積分非直線性誤差の値を確認できます。どちらも1LSB以下が望ましいものの、そこまで高い精度は期待できなそうです。さらに特定の値で不連続な特性を示すことも読み取れます。なお、用語に関してはこちらのページが参考になりました。

総括

ライブラリのおかげで簡単にFFT解析できました。ただし、今回はRP2040の内蔵ADCを利用したので精度は微妙です。高性能な外付けADCに変更するなど、改善の余地はありそうです。あるいはPDM方式、つまり、デジタル方式のMEMSマイクへ置き換えてみることも検討してみたいです。

  1. http://vabenecosi.blog.fc2.com/blog-entry-106.html

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?