プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

ハンドスピナーの回転数を測定する

ハンドスピナーの回転数をM5Stackで測ってみた

f:id:s51517765:20210828223704j:plain
f:id:s51517765:20210828223711j:plain
ハンドスピナーとは、上図のように複数の羽をもったもので、回転させて楽しむおもちゃです。
手遊びするフィジェット(fidget)の一つですが、性能のいいものや上手い人ならより高速により長く回転させることが出来ます。

これを測定する仕組みを作ってみました。
とても簡単で、光センサ(cds)でハンドスピナーの羽を検出し、FFT(Arduinoのライブラリを使用)で周波数を算出し、回転数を算出します。
コードの全容はGitHubを見ていただくとして、CDSを使った明るさ検知は↓のような回路で実現できます。
f:id:s51517765:20210828224045p:plain
CDSは明るさによって抵抗が変わるので、羽がCDSの上に来たときとそうでないときで抵抗が変わります。
これと基準抵抗(ここでは5kΩ)との分圧によって抵抗を検出すると、この切り替わりを検出することが出来ます。
上の写真ではかまぼこ板にCDSが張り付けてあります。

for (int i = 0; i < FFTsamples; i++)
{
    inputRe[i] = analogRead(CdS_Port); //0 - 4095
    delay(1000 / SAMPLING_FREQUENCY); //ms
    ave += inputRe[i];
}
ave /= FFTsamples;

アナログ入力で抵抗を検出し、これを一定間隔で取得します。
FFTを行いたいので、配列に順番に格納します。
FFTにおいては平均値が非ゼロであると、周波数0Hzの定数項が出ます。
ここでは定数項は除外したいので、平均値がゼロになるようにaveを算出しオフセットします。
これで平均が0になりFFTの定数項がでないようになります。

for (int i = 0; i < FFTsamples; i++)
{
  inputRe[i] -= ave; //平均値を0にする
}

このように取得したデータをArduinoFFTライブラリ#include "arduinoFFT.h"に入力すると、簡単に周波数を取得できます。
FFT.MajorPeakが最も強度の強い周波数を取得する関数です。

arduinoFFT FFT = arduinoFFT(inputRe, inputIm, FFTsamples, SAMPLING_FREQUENCY); // FFTオブジェクトを作る
FFT.Compute(inputRe, inputIm, FFTsamples, FFT_FORWARD);
FFT.ComplexToMagnitude(inputRe, inputIm, FFTsamples);
double Freq = FFT.MajorPeak(inputRe, FFTsamples, SAMPLING_FREQUENCY);

これをハンドスピナーの回転数に変換します。
ここでは羽が3枚なので取得される周波数に対して3で割り、周波数は1秒基準なのでrpm(回転数)にするため1分基準に変換します。

double Rpm = Freq / 3 * 60; 

はまったところ

実はこれを作るのに半分以上ライブラリの使い方で迷ってました。
迷った点というのがM5Stackにビルド・書き込みはできるのにRebootを繰り返すというものでした。
原因は入力データ数を9Bit以上にするとダメ、というものでした。

FFTは入力データ数を2のN乗にすることで効率的に数値計算ができるというものですが、サンプリング時間(サンプリング数)を多くしたほうが周波数分解能は高くなります。

まとめ

M5Stackでハンドスピナーの回転数を測定しました。
(ArduinoFFTライブラリは)FFTはデータ数8bit(256データ)までしか使えないようです。
実はFFTをハードコーディングもしてみたのですが、同様にRebootしていてライブラリの問題ではないようですが、真の原因はわかりません。
youtu.be
github.com
ArduinoIDEを使う人はsrc/main.cppの中身をArduinoIDEににコピペするかxxx.inoにファイル名を変更してください。