■地震酔いとは■
東日本大震災では東京はいくつかの施設を除いては被害を免れました。でも、頻繁に震度3-4の揺れを経験していると、そのうちふと揺れを感じているかのような錯覚を覚えることがあります。本当に揺れているのか錯覚なのかわからないんですよね。これが地震酔いです。熊本では高頻度で強い揺れが続いているので、地震酔いの症状でお困りの方もおられるのではないかと思います。
ということで、Arduinoで「揺れたら点灯」なデバイスを作ってみました。机の上に置いて、「あれ?地震?」と感じた時に点灯していなければそれは気のせい、という感じに動いてくれればいいのですが(作ってから東京で大きな地震がないので未検証です)。
周波数成分ごとに色を変えたのですが、手に持っているとどんなに心を鎮めても赤(ゆっくりした揺れ)が消えてくれません。
アル中検出器を造ってしまったのかもしれません。
なお、「酔い」と言っても色々あります。上記のように「揺れているように感じる」程度なら、物理的に揺れていないことを確認することが一種の認知療法的な効果につながると思いますが、「常時揺れているように感じて気持ちが悪い」という状態であれば、内耳に作用するタイプの乗り物酔い止めが有効です。こういう症状では「どこの医者にかかって良いかわからない」と思うんですが、メマイや酔いなどについては耳鼻咽喉科に相談してみるのがよろしいです。3.11の時、かなり深刻な症状を訴える同僚もいましたが、でも今はみんなすっきり治っています。不快な症状を我慢しないで、医師に相談しましょう。
■ハード■
Arduino nano+加速度センサーADXL345+マイコン内蔵LED WS2811です。以前、「キーボードを強く叩き過ぎたら、警告する」ってのを作りましたが、その流用です。Arduino | 接続先 |
3.3v出力 | ブレッドボードの+ライン |
GND | ブレッドボードの-ライン |
5v出力 | マイコン内蔵LEDのVdd |
D2 | マイコン内蔵LEDのDin |
A4 | ADXL345のSCL |
A5 | ADXL345のSDA |
ADXL345 | 接続先 |
SCL | Arduino A4 |
SDA | Arduino A5 |
SDO | GND |
CS | 3.3v |
Vs | 3.3v |
GND | GND |
Vdd | 3.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は特に問題ないと思います。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Arduino_DetectQuake | |
// | |
// Thanks: | |
// Fix_fft | |
// by http://forum.arduino.cc/index.php?topic=38153.0 | |
// caution : include WProgram.h is deprecated. change to Arduino.h | |
// and have to append these codes to avoid error. | |
// #ifndef prog_int8_t | |
// #define prog_int8_t int8_t | |
// #endif | |
// | |
// ADXL Library | |
// https://github.com/adafruit/Adafruit_ADXL345 | |
// | |
// NeoPixel Library | |
// https://github.com/adafruit/Adafruit_NeoPixel | |
// | |
// 震度と加速度の関係 | |
// http://www.data.jma.go.jp/svd/eqev/data/kyoshin/kaisetsu/comp.htm | |
// | |
#include <Wire.h> | |
#include <Adafruit_Sensor.h> | |
#include <Adafruit_NeoPixel.h> | |
#include <Adafruit_ADXL345_U.h> | |
#include <Time.h> | |
#include <fix_fft.h> | |
const int kSamples = 128; | |
const int kSamplesHalf = kSamples / 2; | |
char im[kSamples]; | |
char Buffer1[kSamples], Buffer2[kSamples]; | |
const int kDataNotReady = -1; | |
const int kToBuffer1 = 1; | |
const int kToBuffer2 = 2; | |
int DataCount = 0; | |
char *FillingTo = Buffer1; | |
char *FilledIs = NULL; | |
bool OverRun = false; | |
// ADXL345 | |
Adafruit_ADXL345_Unified adxl = Adafruit_ADXL345_Unified(345); | |
void setupADXL345() { | |
if (!adxl.begin()) { | |
Serial.println("ADXL345 is not available."); | |
return; | |
} | |
Serial.println("ADXL is ready"); | |
adxl.setRange(ADXL345_RANGE_2_G); | |
adxl.setDataRate(ADXL345_DATARATE_200_HZ); | |
} | |
void read345() { | |
float x = adxl.getX(); | |
float y = adxl.getY(); | |
float z = adxl.getZ(); | |
int16_t value = sqrt(x * x + y * y + z * z); // 三軸を合成 | |
value = (value - 240) * 4; // 重力加速度分を引いてから、感度を4倍に | |
if (value >= 127) value = 127; // 8bitの範囲におさまらない値はカット | |
if (value <= -128) value = -128; // 同じく | |
if (DataCount < kSamples) { | |
FillingTo[DataCount++] = value; | |
} | |
if (DataCount == kSamples) { | |
if (FilledIs == NULL) { | |
FilledIs = FillingTo; | |
DataCount = 0; | |
FillingTo = (FillingTo == Buffer1) ? Buffer2 : Buffer1; | |
} else { | |
OverRun = true; | |
} | |
} | |
} | |
// setup WS2811 | |
#ifdef __AVR__ | |
#include <avr/power.h> | |
#endif | |
#define PIN 2 | |
#define NUMPIXELS 1 | |
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); | |
void setupWS2811() { | |
//#if defined (__AVR_ATtiny85__) | |
// if (F_CPU == 16000000) clock_prescale_set(clock_div_1); | |
//#endif | |
// | |
pixels.begin(); | |
int green, red, blue; | |
green = red = blue = 0; | |
pixels.setPixelColor(0, pixels.Color(green, red, blue)); | |
pixels.show(); | |
} | |
// | |
// Arduino Setup | |
// | |
void setup() { | |
Serial.begin(115200); | |
Serial.println("Start"); | |
setupADXL345(); | |
setupWS2811(); | |
} | |
// | |
// Arduino loop | |
// | |
void loop() { | |
char buf[30]; | |
if (adxl.readRegister(ADXL345_REG_INT_SOURCE) & 0x80) { | |
read345(); | |
} | |
if (FilledIs == NULL) { // データが揃ってないので何もしない | |
delay(1); | |
} else { // FFT処理開始 | |
char *pData = (char *)FilledIs; | |
// 虚数部をクリア | |
for (int i = 0; i < kSamples; i++) { | |
im[i] = 0; | |
} | |
// FFT実行 | |
fix_fft(pData, (char *)im, 7, 0); | |
// パワー | |
for (int i = 0; i < kSamplesHalf; i++) { | |
pData[i] = sqrt(pData[i] * pData[i] + im[i] * im[i]); | |
} | |
// 帯域ごとに集計 | |
int low, middle, high; | |
low = middle = high = 0; | |
for (int i = 1; i < kSamplesHalf; i++) { // [0]はDCなので除外 | |
char data = pData[i]; | |
sprintf(buf, "%2d", data); | |
Serial.print(buf); | |
if (data <= 2) data = 0; | |
if (i < 20) { | |
low += data; | |
} else if (i < 40) { | |
middle += data; | |
} else { | |
high += data; | |
} | |
} | |
int sum = low + middle + high; | |
// if (sum < 10) { | |
// low = middle = high = 0; | |
// } | |
pixels.setPixelColor(0, pixels.Color(middle, low, high)); // green, red, blue | |
pixels.show(); | |
sprintf(buf, "l=%4d, m=%4d, h=%4d, sum=%5d", low, middle, high, low+middle+high); | |
Serial.println(buf); | |
// 処理が終わったのでフラグをクリア | |
FilledIs = NULL; | |
} | |
} |
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。