ラベル FFT の投稿を表示しています。 すべての投稿を表示
ラベル FFT の投稿を表示しています。 すべての投稿を表示

2016年5月15日日曜日

Arduino+ADXL345で地震酔い対策


■地震酔いとは■

東日本大震災では東京はいくつかの施設を除いては被害を免れました。でも、頻繁に震度3-4の揺れを経験していると、そのうちふと揺れを感じているかのような錯覚を覚えることがあります。本当に揺れているのか錯覚なのかわからないんですよね。

これが地震酔いです。熊本では高頻度で強い揺れが続いているので、地震酔いの症状でお困りの方もおられるのではないかと思います。

ということで、Arduinoで「揺れたら点灯」なデバイスを作ってみました。机の上に置いて、「あれ?地震?」と感じた時に点灯していなければそれは気のせい、という感じに動いてくれればいいのですが(作ってから東京で大きな地震がないので未検証です)。

周波数成分ごとに色を変えたのですが、手に持っているとどんなに心を鎮めても赤(ゆっくりした揺れ)が消えてくれません。

アル中検出器を造ってしまったのかもしれません。

なお、「酔い」と言っても色々あります。上記のように「揺れているように感じる」程度なら、物理的に揺れていないことを確認することが一種の認知療法的な効果につながると思いますが、「常時揺れているように感じて気持ちが悪い」という状態であれば、内耳に作用するタイプの乗り物酔い止めが有効です。こういう症状では「どこの医者にかかって良いかわからない」と思うんですが、メマイや酔いなどについては耳鼻咽喉科に相談してみるのがよろしいです。3.11の時、かなり深刻な症状を訴える同僚もいましたが、でも今はみんなすっきり治っています。不快な症状を我慢しないで、医師に相談しましょう。

■ハード■

Arduino nano+加速度センサーADXL345+マイコン内蔵LED WS2811です。以前、「キーボードを強く叩き過ぎたら、警告する」ってのを作りましたが、その流用です。

Arduino接続先
3.3v出力ブレッドボードの+ライン
GNDブレッドボードの-ライン
5v出力マイコン内蔵LEDのVdd
D2マイコン内蔵LEDのDin
A4ADXL345のSCL
A5ADXL345のSDA


ADXL345接続先
SCLArduino A4
SDAArduino A5
SDOGND
CS3.3v
Vs3.3v
GNDGND
Vdd3.3v

■ソフト■

加速度センサーから読み取った値を3軸分合成してからFFTにかけて、ノイズ分を除外した値が一定値を超えたら帯域ごとのパワーにわけてRGB LEDを点灯します。

当初割り込みを使って連続的に計測するプログラムを書いたのですが、インタラプト処理ルーチンでI2Cを読んだ瞬間にフリーズする、という現象を解決できなかったので、諦めて単純な逐次処理にしました。ただ整数処理のFFTを使っているので、計測漏れはそれほど大きくないです。

ADXL345に限らず3軸加速度センサーには重力加速度が常時計測されます。ので、その影響を取り除く必要があります。また、体感する揺れは三軸の合成ベクトルなので、三軸分のスカラー値を求めて、そこから重力加速度分を引き、感度を高めるためにちょっと掛け算してます。read345というルーチンでは、その後、ムダなdual buffer処理をしていますが、これは最初に書いたように当初割り込みで処理しようとしていた名残です。書き換えるの面倒なのでそのままにしています。

ノイズ処理は単に「静かにしていてもFFTからの出力が0-2程度出ているので、2以下はノイズとしてカット。それを超えた分だけ集計して、集計値が一定値を超えたら、有効と判定」というごく単純な処理です。

FFTには整数型FFTを使いました(Topic: Modified 8bit FFT in c)。ありがとうございます。ただ、開発されてから時間が経っているので、いくつか修正が必要です。まず、include文で「WProgram.h」となっている箇所は「Arduino.h」に置換します。また、データタイプの定義が変わっているので、fix_fft.cppの頭の方に
#ifndef prog_int8_t
#define prog_int8_t int8_t
#endif
を貼っておいてください。

ADXL345のライブラリはAdafruitの新しいunifiedライブラリ版を使いましたが、unifiedなインタフェース(センサーが違っても、重力加速度としての値域に変換して返すなど、センサーごとの差異をわりといい感じに吸収してくれるライブラリ群)は使わずgetX, Y, Zで取ってます。FFTが整数ですし。

WS2811は特に問題ないと思います。


■感謝■

FIX_FFTの作者の方、およびADXL345/WS2811ライブラリを公開してくださったAdafruitに感謝申し上げます。ありがとうございました。

2015年9月22日火曜日

スペクトルアナライザー by MSP432P401R+Energia MT

ピンぼけじゃないもん影だもん(趣味:写真)

■Energia MT=マルチタスク■

EnergiaはArduino互換の開発環境ですが、MSP432P401R版はマルチタスクに対応しています。

マルチタスクというにはあまりにも単純でセマフォとかメールとかまったくありません。でも、今までのArduinoと比べれば格段に作りやすくなりました。

使い方は単純で

  • Energia IDEでタブを開き適当な名前を付ける
  • メイン同様setupとloop関数を用意するが、setupDisplay(), loopDisplay()のようにタスク名が後に続くようにするとsetup+loopのペアを別タスクとして認識する
  • メインに置いたヘッダやメイン上のGlobal変数は別タスクでも認識される

排他などについては厳密な排他制御は無理、暫定的にGlobal上にフラグを置いて自分で制御するしかありません。また、プリエンプションやタスク優先度がどうなっているのかも不明。その辺まったく情報がありません。まぁそのうち何とかなるでしょう。

ということで、とりあえず毎度お馴染みのスペクトルアナライザーをマルチタスクで作ってみました。

■材料

MSP432P401R、アンプ付きマイクAE-MICAMP、超小型グラフィック液晶AQM1284A、ブレッドボード、ジャンパ線…すべて秋月で揃います。合計4000円ぐらい? USBシリアルなども不要なのでArduino uno正規品を買うより安いです。

マイクはアナログ出力なのでドライバ等不要です。液晶についてはAQM1284AのコントローラIC ST7565RのライブラリをAdafruitさんが公開しているので、それを少し改造して使います。

FFTは大浦先生のライブラリそのままですが、M4F対応としてdoubleをfloatに置換して使用します。

以上です。

■配線■

マイクはGND, Vcc(5vが推奨されていますが出力電圧が大きくなりすぎるので3.3vにします)を接続し、OUTをEnergiaのP5.0に接続します。

AQM1284Aは以下の通りです。


AQM1284AEnergia MSP432P401R
VDD3V3
CSP3.0 / 18
RESETP2.5 / 19
RSP5.7 / 17
SCLKP1.5 / 7
SDIP1.6 / 15
GNDGND

■使い方■

各ソースは末尾に掲載します。長くてすみません。

Energiaは最新の16を使用してください。起動する前に、ST7565という名前のフォルダを作り、その中にST7565.cppとST7565.hを入れ、Energiaのlibraryフォルダに入れます。

Enegiaを起動したら、新しいプロジェクトを作り、FFTGraph_MTをコピペします。次に、ウィンドウ右上にある下向き三角をクリックして「新規タブ」を選び、TaskLCDという名前を付けてTaskLCDをコピペします(名前は何でも良いです)。

配線の点検をすませたらMSP432をマイクロUSBで接続し、Energiaでcmd+M、ビルドとプログラムの転送が終わるとグラフィック液晶に表示が出る…はずです。

なお、この作り方は厳密にいえば正しいマルチタスクではありません。TaskLCDが表示している最中にメインスレッドが表示バッファを書き換えてしまうこともありえます。でも、表示中にスペクトルが変わったとしても人間の目で追える変化ではないので、大目に見てやってください。

■毎度お馴染みのハマり■

Adafruitのライブラリの移植は、コンパイルエラーが出る→つぶす→ビルド→エラー→潰す…を淡々と繰り返すだけですが、それで終わらないのがコントラスト設定とpagemapでした。

コントラスト設定は秋月版との液晶の違いかAdafruit版だと真っ黒になってしまうのですが、Voltage Resistor Setを指定して回避できました。

pagemapはコントローラ上のページと液晶の表示位置を対応させるための表ですが、配布されているものと秋月のでは仕様が異なるようで、こんなのが出ました。左上隅から右下隅への直線がちょっと下にずれてます。64ドットで8ページ、たぶん2ページずれているのでしょう。


何度かの途中試行錯誤を経て

逆だ

なんだこれ

最終的にちゃんと表示が出ました。ここまで約1時間。



その次はマイクからの入力が常にゼロという現象が。そんなお行儀のいいアナログ入力はありえないのでしばらく頭を抱えていたのですが、これはanalogRead対応のピンにつないだつもりがanalogWriteのピンだった、というオチ。そりゃ値ゼロだわorz

FFTライブラリはつい最近使ったものなので問題なし。マルチタスク対応に関しては、奇跡的にも一発で動きました。何も難しいことしていないので当たり前ですけども、簡単なところで引っかかるのがこの私orz

でも、「自作補聴器」に今までで一番近づいたなぁ。すごいぞM4F、偉いぞM4F。





2015年9月20日日曜日

ゴメン、MSP432P401Rやっぱり速かった

基板裏

えー、前回、MSP432P401Rがイマイチ速くない、という記事を書きました。

32bit 48MhzのMSP432が16bit 16MhzのArduino nanoと比べて約1.7倍速いだけでクロック比やCPU差を考えるとぼろ負け状態でした。

あれ、FPUは仕事してないの?と思ったのですが…よくよく仕様を読んでみれば、M4FのFPUは単精度でした。今回使ったFFTライブラリは倍精度なので、FPU様の恩恵をまったく受けられません。

というわけで、dobuleをfloatにさくっと置換して、実行してみました。

その結果(前回同様64点のFFTを1000回実行)

デバイス結果
MSP432P401R82mSec
Arduino nano1943mSec

前回結果が1142mSecでしたから約14倍スピードアップ! 圧倒的です。一方のArduino nanoはほとんど変わっていません。改めて調べてみるとArduinoはdoubleもfloatも単精度なんですね…知らなかった。Energiaも同じ仕様なのですが、MSP432は特別なのでしょうか。とりあえずfloatが単精度でFPUを使っていることは確認できましたが、もしMSP432だけはdoubleがちゃんと倍精度で実装されているとしたら、この結果は納得がいきます。

前回の結果でも「なんとか補聴器作れるかなぁ…」という程度だったのですが、これなら十分です。あとは私の能力か…そこが一番ネックだな…。

関係ないけど、配列に一定の値を入れて逆FFTかけたらピンクノイズになるんですかね。一度実験したいと思ってたのですが、こないだ「よし、もう買い忘れはないな」とさんざん確認してから発注したにもかかわらず、見事にDACを注文し忘れました。

やはり私の能力が一番のネックだorz

2015年9月19日土曜日

MSP432P401Rがイマイチ速くない?


■追試結果■

イマイチ速くない原因がわかりました。MSP432搭載のFPUは単精度で今回使ったプログラムは倍精度、そのためFPUが寝てる状態でした。ただ、どちらもソフトウェアで演算しているなら、やはりクロック差ほどの性能差が出ていないので疑問は残ります。

追試結果はこちらです(ゴメン、MSP432P401Rやっぱり速かった)。

以下は、新しいCPU到着に浮かれるバカっぷりだけをお楽しみくださいorz

■本文■

ついに届いたぜ、M4Fだぜヒャッハー!

…などと浮かれていたのですが、energia上で浮動小数点64ポイントのFFTを1,000回走らせてみたところ、結果は1143ミリ秒でした。

一方、Arduino nanoに同じソースをコピペして実行したら、1992ミリ秒。

クロック比よりも遅い? 浮動小数点プロセッサはどーなった??

…という残念な結果になりました。

ちゃんとオプチマイズしないとダメですね。ARMに最適化されているであろうCMSIS-DSPはどうかなぁ…。

2015年5月2日土曜日

磁束密度をFFTで処理しグラフ表示


わー、なんだか久しぶり。

以前、磁石の動きを検出するのにアナログ出力のホール素子を使ってみたのですが、かなり強力な磁石を使っても10cm程度がやっとで、あとはノイズに埋もれてしまいました。まぁアナログ技術のある人ならもうちょっとマシな結果になるかもしれませんが。

ふと思い立って、FRDM-KL46Zについている電子コンパスMAG3110を使ってみました。測定してみると何もしなくても地球磁場が600-1200ぐらいの値を返してくるのですが、すぐ近くにネオジム磁石をおいてみても200程度しか値が変わりません。ただ、地球磁場は当然ながら安定しているので、ネオジム磁石の動きは十分信号として検出できそうな気配。

ということで、ずっと部品沼で眠っていたAQM1248(秋月の超小型グラフィック液晶)、最速の誉れも高き大浦先生のFFTライブラリなどを寄せ集めて、実験してみました。

■まず予備実験■

FRDM-KL46Z内蔵のMAG3110(ライブラリ:MAG3110…ワーニング出まくるので、あとで直してpush requestしたい)を内蔵の液晶(ライブラリ:SLCD)で表示してみました。MAG3110でボード内部のI2C端子名を指定するんですが、参照した資料が間違っていて苦労しました。
MAG3110 mag(PTE25, PTE24);
これで使えます、ご参考まで。

さて、表示してみると前述の通り、値は向きによって600-1200程度の幅で変化します。そこにネオジム磁石を近づけると200ぐらい増減します。当然離すと急激に弱くなりますが、値としてはわりとはっきりしているので、たとえば自転車のホイール回転のように周波数レンジの決まっているものであれば何とか処理できそう。

ということで信号処理はFFTを使い、表示に超小型グラフィック表示液晶を使うことにしました。

■まずAQM1248■

電子工作を再開したばかりのころ、秋月の「超小型液晶」を基板にナナメにハンダ付けしてしまったのは良い思い出…今も元気に活躍しておられます、ナナメのままで。

ということで、まずフラックスを塗って置いてしばらく放置し一カ所だけハンダ付けをして向き角度などを確認してから装着。FRDM-KL46Zとの配線は以下の通り:

AQM1248FRDM-KL46Z
Vcc3.3v
CSD10
RESETD9
RSD8
SCLKD13
SDID11

mbed用のほどよいライブラリaqm1248a_lcdをリンクしサンプルをそのままコピペして動作を確認。なお、同ライブラリはプラットフォーム(mbedの機種)を識別してGPIOなどを切り替える作りになっているのですが、概ね「LPC1768とKL05、それ以外」という感じなのでLPC1768とKL05, KL46以外の方はソースの中身と配線をご確認の上、お使いになった方が良いです。

一昨日aitendoのカラー液晶に悪戦苦闘したのですが、こっちは一発動作。やっぱ私にはaitendoはハードルが高すぎます。相場?の半値ぐらいで買えたんですが、家でビニール袋から出してみたら基板に液晶が接着済(笑)。工程ミスか指示ミスか知らないけど、液晶が端子を半分覆い隠す状態になっていて普通なら納品できない品だから安かったんでしょうね。まさにジャンク。

まぁそれはともかく、これで表示はばっちり。接続ピン番号埋め込まれていてちょっと不安だったものの動作は安定しているのでグラフ描画もかなり簡素化できそうな気配。ライブラリ開発者の方に改めてお礼申し上げます。

■高速FFTライブラリ■

今回はこれを使います。
コンパクトで高速で美しい…。mbedで使うには、

  1. パッケージをダウンロード、解凍
  2. fft4g.cをブラウザ上のmbedプロジェクトにドラッグ&ドロップ
  3. ファイル名をfft4g.cppに変更
  4. あとはサンプルを参考に書くだけ
余談ですが、サンプルではfft->ifftで誤差を確認する、という処理が書かれていて、「FFT+IFFTでフィルタ作りたい」と思っている私には拍子抜けするほど簡単w

数学どころか算数レベルの私には基数って何よ?の世界なのですが、ともかくこれも一発動作。今まで使ったFFTライブラリの中で一番簡単だったかもしれません。

■ということでプログラムの構造■

  • IntervalでMAG3110を読みバッファに値を書き込んでいく
  • メインループでバッファをチェックし、一杯になったらFFTかけて液晶にグラフ表示
これだけです。

で処理の結果ですが…まず、電子機器で一杯の室内で放置した状態でのスペクトル


ごらんのように、ほぼ全域に不規則な成分が出てます。実は圧倒的に直流成分が多いのですがスケールアウトしてしまうのでカットしてます(そりゃそうだ)。そして次が、FRDM上約10cmのところでネオジム磁石をおおよそ2秒で3往復ほどの周期で振ってみたところのスペクトル


明確にでかい山が出来てます。必死に高速フリフリをすると右へ移動するし、ゆっくりにすると左へ移動するので、なんとなくできてるかなーと。さて、次は、「こういうぐあいにでっかい山ができていることを検出する処理」というのを書かないといけないのですが、パターン認識とか苦手なんす。どなたかアイディアください。

以下ソースです。

ほとんどサンプルからのコピペですが、mbedリポジトリに公開しましたのでご笑覧くださいませ。下にも貼っておきますが、何かの参考にされるのであればリポジトリの方をご覧下さい(基本的に以下のソースは更新し忘れることが多いと思うので)。

2014年1月10日金曜日

ArduinoでFFT : 音声を入力してみた

ほぼ昨日のソースと同様、違うのは主に関数でデータを取り込む代わりに

data[i] = (analogRead(mic) >> 2) - 128;
としているところ。

マイクは秋月の「高感度マイクアンプキット 500円」。まずこれを組み立てたのですが…ハンダがランドに乗ってくれなくてまたしても苦戦。「こんだけ熱が回っちゃうとマイク壊れているかもしれないなぁ…」と思いつつ、オシロにつないでみると一応声を拾っている。良かった良かった。


FFTは昨日も使った8bit fft。で、結果なのですが…ピークtoピークのアナログ波形でも出力は0-50程度、どうも今ひとつダイナミックレンジが狭い感じ。また、人間の声についてはそれなりに拾っていますが、ビープ音についてはレンジを越えてしまう様子。128点のサンプリングに約15mSかかっているので約8500回/秒、その半分として4.2Khzは拾っているはずなのですが。うーん。

さて、今回は芸無くSerialへの出力でしたが、次回は何かもっと見やすく表示してみます。
#include <fix_fft.h>
#include <Time.h>
#define N_SAMPLES 128
char im[N_SAMPLES];      // 虚数部
char data[N_SAMPLES];    // 入力/実数部
char buf[20];            // 文字出力バッファ
int mic = 0;      // マイク接続ポート
int cnt = 0;      // ループカウンタ
int prevSec = 0;  // 秒カウント

void setup() {
  Serial.begin(115200);
  analogReference(DEFAULT);
 
  prevSec = -1;
}

void loop(){
  int  i;
  unsigned long mil;
 
  cnt ++;
 
  mil = millis();
  for (i = 0; i < N_SAMPLES; i++) {
    // 音声を読み込み
    data[i] = (analogRead(mic) >> 2) - 128;
   
    // 虚数部をクリア
    im[i]   = 0;
  }
  // 128個の読み込み時間を計測
  mil = millis() - mil;
  // FFT
  fix_fft(data, im, 7, 0);  // full scale 2^7=128, FFT mode
  for (i = 0; i < N_SAMPLES / 2; i++) {
    data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
  }
 
  // 1秒ごとのループ回数と128個の読み込み時間(mSec)を表示
  int s = second();
  if (prevSec != s) {
    prevSec = s;
    data[0] = cnt;  // 0をcnt出力に流用
    data[1] = mil;  // 1をmil出力に流用
    dispData("Results : ", data, N_SAMPLES / 2);  //  結果出力
    Serial.println("");
   
    cnt = 0;  //  カウンタをクリア
  }
}

void dispData(char *inMsg, char *inData, int inN) {
  Serial.print(inMsg);
  for (int i = 0; i < inN; i++) {
    sprintf(buf, "%5d", inData[i]);
    Serial.print(buf);
  }
}

2014年1月9日木曜日

ArduinoでFFT(8bit int)

ハンダ付け作業台、4980円

もちろん(と威張って言うことではないのですが)自分では書けないので、あちこちで公開されているライブラリを漁ってみたのですが…私の目的に一番フィットしていたのがこれでした。
  Modified 8bit FFT in c
Webに貼ってあるfix_fft.hとfix_fft.cppをテキストにしてArduinoライブラリフォルダに入れておく。それからArduino IDEを起動しSketchで以下のようなコードを実行(「マイコンボードへの書き込みが完了しました」という表示が出たところで、すぐにツール>シリアルモニタを開き、ボーレートが違っていたら57600に設定する)すると、意図したようなスペクトル数列が得られました。
#include <fix_fft.h>
char im[128];
char data[128];
char buf[20];

void setup() {
  Serial.begin(57600);
}

void loop(){
  int  i;
  char buf[6];
  for (i = 0; i < 128; i++) {
    //  data[i] = (i % 16) * 16 - 120;  // 三角波  
    //  data[i] = (((i >> 4) & 1) * 255) - 128;  // 方形波
    // 2つの正弦波を合成した波形
    float d = (float)i * (PI * 2.00) / 128.00;   // ラジアンへ変換:dは0〜2pi
    float f = (sin(d * 2.00) * 0.66 + sin(d * 30.00) * 0.33) * 127.00;  // 2fと30fの正弦波
    data[i] = f;
 
    // 虚数部をクリア
    im[i]   = 0;
  }
  Serial.println("");
  dispData("Source  : ", data, 128);
  Serial.println("");

  fix_fft(data, im, 7, 0);  // full scale 2^7=128, FFT mode

  for (i = 0; i < 64; i++) {
    data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
  }

  dispData("Results : ", data, 64);
  Serial.println("");
  while(1) ;
}

void dispData(char *inMsg, char *inData, int inN) {
  Serial.print(inMsg);
  for (int i = 0; i < inN; i++) {
    sprintf(buf, "%5d", inData[i]);
    Serial.print(buf);
  }
}

よかった良かった。ここ数日これに睡眠時間を削られていたので、安眠できます。

…でも、中学生ぐらいからずっと欲しかったハンダ付け作業台が昨日Amazonから届いたので、やっぱり睡眠不足は続く予感:-)