極小ESP-WROOM-02 に部品をとりつけてみた [Arduino]
苦労しましたが、無事に動きました!これで基本的な部分は出来ました。これに心拍センサーとOLEDディスプレイをつければ電気系は完成です。
ということで、着けてみました!ケーブルにもポリウレタン銅線を使ってかなり細くなりました。
一号機よりも小さくスタイリッシュになりました。いやー苦労した甲斐がありました。残すはソフトと息子担当の外装デザインのみとなりました。いよいよ最後の追い込みです!
(^_^)/~
ディアイワイモール (DIYmall) iic i2c OLEDモジュール ディスプレイ 0.96インチ 51マイクロコントローラ12864 for Arduino (ホワイト)
- 出版社/メーカー: Flyfun
- メディア: おもちゃ&ホビー
ESP-WROOM-02ピッチ変換済みモジュール《シンプル版》
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
極小 ESP-WROOM-02 計画 [Arduino]
^;
下にあるのは、バッテリー接続用のコネクターと3.7V から 3.3V を生成するための LDO 、チップコンデンサー、スイッチが入っている電源ブロックです。この大きさでPCからスケッチを書き込むこともできます。我ながら小さくできました!
σ(^_^)
HRTAPE-W10L30 : ポリイミド絶縁耐熱テープ (幅10mm、長さ30m) 電子工作の必需品 電子基板の金メッキが施されたエッジコネクタ・ゴールドフィンガーの保護に。
- 出版社/メーカー: ビープラス・テクノロジー
- メディア: エレクトロニクス
CLIP DISPLAY(仮) の再設計にチャレンジ [Arduino]
今までは Arduino Pro Mini を使っていましたが、CLIP DISPLAY(仮) の全ての機能を盛り込むとメモリ不足になってしまいます。ということで、代わりのマイコンとして ESP-WROOM-02 を選択しました。
ESP-WROOM-02 のメモリと処理能力は Arduino Pro Mini とは比較にならないくらい高いです。(参考記事:http://makers-with-myson.blog.so-net.ne.jp/2016-04-13) また大きさも Arduino Pro Mini よりも小さく、文句ない選択です。
あと試作をしていて困ったのがケーブルです。意外と機内配線のケーブルが体積を占め、全てのパーツを納めるのに苦労しました。その体積をできるだけ小さくするために、ポリウレタン銅線を使うことにしました。いわゆるエナメル線です。
2UEW エナメル線 2種 ポリウレタン銅線 0.2mm 20m
- 出版社/メーカー: サンコー電商
- メディア:
これは"はんだ付け"をすると表面のポリウレタンが溶けて結線でき、"はんだ"されていない部分は絶縁されたままという優れものです。何より、とにかく細いので省スペースで実装できそうです。
一番の悩みは電源。前回の試作はバッテリーを内蔵しましたが、これではバッテリ交換ができません。バッテリーは別モジュールにしようかと考えています。
( ̄_ ̄ )。o0
Arduino Pro Mini 328 3.3V 8MHz
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
2UEW エナメル線 2種 ポリウレタン銅線 0.2mm 20m
- 出版社/メーカー: サンコー電商
- メディア:
MPU6050 の Digital Motion Processor の性能を試してみた(5) [Arduino]
フィルターなしの場合は、動きを与えたあとにオフセットが発生していました。
今回、64サンプルのフィルターを使ってデータを取得してみました。センサーを左右に大きく振ってみました。
フィルターのおかげでオフセットはキャンセルされています。うまくいきました。このデータから速度のデータを算出してみました。
だいたい1.2m/sec。左右の振り幅は1mくらいなので、感覚的になんとなく合っています。次に距離を計算してみました。左右に大きく30回振りましたので、だいたい30m位のはずです。
悪くないデータになりましたが、静止状態でもずるずると距離が伸びてしまっています。加速度データの正負の値が一致していないので、差し引きで速度が残ってしまっています。これ位はしょうがないレベル?悩む。
(。-`ω´-)ンー
ESPr Developer(ESP-WROOM-02開発ボード)
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
実践ディジタル・フィルタ設計入門―JavaアプレットとC++プログラムを使った (ディジタル信号処理シリーズ)
- 作者: 岩田 利王
- 出版社/メーカー: CQ出版
- 発売日: 2004/10
- メディア: 単行本
MPU6050 の Digital Motion Processor の性能を試してみた(4) [Arduino]
一般的なやり方は、センサーの出力値からローパスフィルターを使ってDC成分を得て除去するというもの。Android のページでも紹介されています。
SensorEvent
https://developer.android.com/reference/android/hardware/SensorEvent.html
具体的には下記のようなアルゴリズムです。
// alpha は 0.5 ~ 0.8 で調整 lpf_signal = alpha * x[i-1] + (1 - alpha) * x[i]; // low pass filter hpf_signal = x[i] - lpf_signal; // distill high pass value using low pass value
しかし、これを実際のセンサーの出力(10msec間隔)に適用すると、ローパスフィルタの値がセンサーの出力値とほぼ重なってしまい、正しい加速度が算出できません。(赤がローパスフィルターの波形で、その下に青のセンサーからの出力波形が隠れています)
理想的には数十サンプルの平均値をとるようなローパスフィルターを実現したいところです。でも配列はメモリを食うので使いたくないし・・・。
ということで、下記のようなアルゴリズムを考えてみました。
#include#include #include "I2Cdev.h" #include "MPU6050DMP.h" extern "C" { #include "user_interface.h" } #define INT_PIN 4 MPU6050DMP mpu6050; bool dmpReady = false; uint16_t packetSize = 0; volatile bool mpuInterrupt = false; void dmpDataReady() { mpuInterrupt = true; } unsigned long getCurrentTimeMS() { return ESP.getCycleCount() / 80000L; } void setup() { Wire.begin(12, 14); Wire.setClock(400000); Serial.begin(115200); pinMode(INT_PIN, INPUT); WiFi.disconnect(); WiFi.mode(WIFI_OFF); WiFi.forceSleepBegin(); if (mpu6050.testConnection()) { Serial.println("MPU6050 OK"); attachInterrupt(INT_PIN, dmpDataReady, RISING); } else { Serial.println("MPU6050 NG"); return; } mpu6050.initialize(); // offset values for my sensor mpu6050.setXAccelOffset(565); mpu6050.setYAccelOffset(-5); mpu6050.setZAccelOffset(1432); mpu6050.setXGyroOffset(17); mpu6050.setYGyroOffset(550); mpu6050.setZGyroOffset(464); Serial.println(""); Serial.println(""); if (mpu6050.dmpInitialize() == 0) { Serial.println("MPU6050DMP OK"); mpu6050.setDMPEnabled(true); packetSize = mpu6050.dmpGetFIFOPacketSize(); dmpReady = true; } else { Serial.println("MPU6050DMP NG"); } } uint16_t fifoCount = 0; uint8_t fifoBuffer[64]; unsigned long preTime = 0; unsigned int loop_counter = 0; #define WSIZE 16 // change this value for your purpose long sum_x = 0; long sum_y = 0; long sum_z = 0; int adj_x = 0; int adj_y = 0; int adj_z = 0; void loop() { if (!dmpReady) return; while (!mpuInterrupt); mpuInterrupt = false; while (fifoCount < packetSize) { fifoCount = mpu6050.getFIFOCount(); } uint8_t mpuIntStatus = mpu6050.getIntStatus(); if ((mpuIntStatus & 0x10) || fifoCount == 1024) { mpu6050.resetFIFO(); return; } else if ((mpuIntStatus & 0x02) == false) { return; } mpu6050.getFIFOBytes(fifoBuffer, packetSize); fifoCount -= packetSize; unsigned long curTime = getCurrentTimeMS(); unsigned long duration = curTime - preTime; Quaternion q; VectorInt16 aa; VectorInt16 aaReal; VectorFloat gravity; mpu6050.dmpGetQuaternion(&q, fifoBuffer); mpu6050.dmpGetAccel(&aa, fifoBuffer); mpu6050.dmpGetGravity(&gravity, &q); mpu6050.dmpGetLinearAccel(&aaReal, &aa, &gravity); if (loop_counter < 2000) { if (loop_counter % 80 == 0) Serial.println(""); ++loop_counter; Serial.print("."); return; } else if (loop_counter < 2000 + WSIZE) { sum_x += aaReal.x; sum_y += aaReal.y; sum_z += aaReal.z; if (loop_counter % 80 == 0) Serial.println(""); ++loop_counter; Serial.print("*"); return; } else { adj_x = sum_x / WSIZE; adj_y = sum_y / WSIZE; adj_z = sum_z / WSIZE; sum_x = sum_x - adj_x + aaReal.x; sum_y = sum_y - adj_y + aaReal.y; sum_z = sum_z - adj_z + aaReal.z; /* // High-pass values aaReal.x -= adj_x; aaReal.y -= adj_y; aaReal.z -= adj_z; */ } Serial.print("areal\t"); Serial.print(aaReal.x); Serial.print("\t"); Serial.print(aaReal.y); Serial.print("\t"); Serial.print(aaReal.z); Serial.print("\t"); Serial.print(adj_x); Serial.print("\t"); Serial.print(adj_y); Serial.print("\t"); Serial.print(adj_z); Serial.print("\t"); Serial.print(duration); Serial.println(""); preTime = curTime; }
アイディアは単純です。指定サンプル分の積算値を保持しておき、そこから平均値を出します。計算が終わったら、積算値から平均値を一つ分を減算して、直近の得られた値を積算値に加えるというものです。
このアルゴリズム適用した結果を以下に示します。赤がサンプル数分の平均値で、青がセンサーからの出力です。なかなか良い感じのデータが得られました。
16サンプル平均
32サンプル平均
64サンプル平均
128サンプル平均
このフィルターの欠点はオリジナルの波形に対して少し位相が遅れてしまうところです。位相の遅延を最低限にしつつ加速度の値をとれそうなのは、64か32あたりかな・・・。
適正なサンプル数は速度や距離を実測して決めたいと思います。
(^^)
ESPr Developer(ESP-WROOM-02開発ボード)
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
実践ディジタル・フィルタ設計入門―JavaアプレットとC++プログラムを使った (ディジタル信号処理シリーズ)
- 作者: 岩田 利王
- 出版社/メーカー: CQ出版
- 発売日: 2004/10
- メディア: 単行本
MPU6050 の Digital Motion Processor の性能を試してみた(3) [Arduino]
問題はXYZ軸の加速度が静止状態でゼロでない値に収束してしまうことです。このままでは静止していても、計算上は、どんどん速度が上がってしまいます。
Google師匠に聞いてみると、キャリブレーションが効果がありそうです。
Arduino Sketch to automatically calculate MPU6050 offsets
http://www.i2cdevlib.com/forums/topic/96-arduino-sketch-to-automatically-calculate-mpu6050-offsets/
ということで、上記サイトにあるキャリブレーション用のスケッチを試してみることにしてみました。
MPU6050_calibration_v1.1.zip
フォーラム中でも話題になっていましたが、このキャリブレーションスケッチ、一向に値が収束しません。仕方ないので収束条件を緩めるように変更しました。
int acel_deadzone=30; //Acelerometer error allowed, make it lower to get more precision, but sketch may not converge (default:8) int giro_deadzone=5; //Giro error allowed, make it lower to get more precision, but sketch may not converge (default:1)
これでも値がなかなか収束しません。どうも、かなり厳密にセンサーを平行に保たないと収束しないようです。何度も試行錯誤して、ようやく結果が得られました。
XAccelOffset: 82
YAccelOffset: 333
ZAccelOffset: 1443
XGyroOffset: -3
YGyroOffset: 716
ZGyroOffset: 475
※センサーによって値はまったく異なるので注意してください。
実際に試してみると、確かに静止状態では前よりも収束していますが(これでも若干のオフセットがあります)、センサーに動きを与えると、また大きなオフセットが発生してしまいました。
これでは速度、距離を算出することはできません。どうやら、オフセットを除去するためのフィルターを考える必要がありそうです。
σ(。-`ω´-)
ESPr Developer(ESP-WROOM-02開発ボード)
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
MPU6050 の Digital Motion Processor の性能を試してみた(2) [Arduino]
まず、左に傾けてみました。
平面の静止状態とあまり変わりません。すごいですね。
次に右に傾けてみました。
傾向は変わらないようです。
今度は手前に傾けてみました。
ここまで来ると想像通りの振る舞いですね。
せっかくなので、奥にも傾けてみましょう。
予想通りの結果です。
これで平行に置かなくても値は収束することが分かりました。しかし、若干オフセットがかかっているのが気になります。加速度では変化なくても、速度にするとどんどん加速もしくは減速してしまいます。
やっぱり、キャリブレーションは必要そうですね。チャレンジしてみますか・・・。
σ( ̄・ω・ ̄)
サインスマート(SainSmart) MPU-6050 3軸ジャイロスコープ モジュール for Arduino
- 出版社/メーカー: サインスマート(SainSmart)
- メディア: エレクトロニクス
図解入門よくわかる最新センサーの基本と仕組み (How‐nual Visual Guide Book)
- 作者: 高橋 隆雄
- 出版社/メーカー: 秀和システム
- 発売日: 2011/03/23
- メディア: 単行本
MPU6050 の Digital Motion Processor の性能を試してみた(1) [Arduino]
DMPで取れる座標には REAL ACCEL とWORLD ACCEL の2種類あります。REAL ACCEL と WORLD ACCES の違いはこちらを参照してください。どちらがいいのか調べてみたら、下記のスレッドのやりとりで、WORLD ACCEL は精度が低いからやめろとありました。
MPU-6050 Accelerometer Calibration and DMP
http://www.i2cdevlib.com/forums/topic/75-mpu-6050-accelerometer-calibration-and-dmp/
ということで、静止状態でリセットした時の REAL ACCEL の出力データの様子を、下記のスケッチで試すことにしてみました。
#include <Wire.h> #include "I2Cdev.h" #include "MPU6050DMP.h" extern "C" { #include "user_interface.h" } #define INT_PIN 4 MPU6050DMP mpu6050; bool dmpReady = false; uint16_t packetSize = 0; volatile bool mpuInterrupt = false; void dmpDataReady() { mpuInterrupt = true; } unsigned long getCurrentTimeMS() { return ESP.getCycleCount() / 80000L; } void setup() { Wire.begin(12, 14); Wire.setClock(400000); Serial.begin(115200); pinMode(INT_PIN, INPUT); if (mpu6050.testConnection()) { Serial.println("MPU6050 OK"); attachInterrupt(INT_PIN, dmpDataReady, RISING); } else { Serial.println("MPU6050 NG"); return; } mpu6050.initialize(); mpu6050.setXGyroOffset(220); mpu6050.setYGyroOffset(76); mpu6050.setZGyroOffset(-85); mpu6050.setZAccelOffset(1688); if (mpu6050.dmpInitialize() == 0) { Serial.println("MPU6050DMP OK"); mpu6050.setDMPEnabled(true); packetSize = mpu6050.dmpGetFIFOPacketSize(); dmpReady = true; } else { Serial.println("MPU6050DMP NG"); } } uint16_t fifoCount = 0; uint8_t fifoBuffer[64]; unsigned long preTime = 0; void loop() { if (!dmpReady) return; while (!mpuInterrupt); mpuInterrupt = false; while (fifoCount < packetSize) { fifoCount = mpu6050.getFIFOCount(); } uint8_t mpuIntStatus = mpu6050.getIntStatus(); if ((mpuIntStatus & 0x10) || fifoCount == 1024) { mpu6050.resetFIFO(); return; } else if ((mpuIntStatus & 0x02) == false) { return; } mpu6050.getFIFOBytes(fifoBuffer, packetSize); fifoCount -= packetSize; unsigned long curTime = getCurrentTimeMS(); unsigned long duration = curTime - preTime; Quaternion q; VectorInt16 aa; VectorInt16 aaReal; VectorFloat gravity; mpu6050.dmpGetQuaternion(&q, fifoBuffer); mpu6050.dmpGetAccel(&aa, fifoBuffer); mpu6050.dmpGetGravity(&gravity, &q); mpu6050.dmpGetLinearAccel(&aaReal, &aa, &gravity); Serial.print("areal\t"); Serial.print(aaReal.x); Serial.print("\t"); Serial.print(aaReal.y); Serial.print("\t"); Serial.print(aaReal.z); Serial.print("\t"); Serial.print(duration); Serial.println(""); preTime = curTime; }
4回データを測定をしてみました。縦軸の単位は G(9.8m/s^2) になります。0.5 は0.5G の意味です。
だいたいの傾向が見えてきました。データの間隔は10msecなので、17秒くらいで収束をしているようです。20秒静止していれば問題ないでしょう。
XYZ軸いずれもノイズ成分は Peak to Peak で 0.1G 程度あるようです。結構大きいですね。またZ軸(グレー)はオフセットがかかっているようなので調整が必要そうです。
次は斜め静止状態でどのようなデータが取れるか試してみたいと思います。
(^_^)/~
SODIAL(R)MPU-6050モジュール3軸アナログジャイロセンサ+加速度センサーモジュールMPU 6050用
- 出版社/メーカー: SODIAL(R)
- メディア: エレクトロニクス
ESPr Developer(ESP-WROOM-02開発ボード)
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
Make: Sensors: Projects and Experiments to Measure the World with Arduino and Raspberry Pi
- 作者: Tero Karvinen
- 出版社/メーカー: Maker Media, Inc
- 発売日: 2014/06/02
- メディア: ペーパーバック
ESP-WROOM-02 と心拍センサーをバッテリーで動かしてみた。 [Arduino]
さて、ずっと気になっていた ESP-WROOM-02 の 110mAh バッテリーによる駆動。ようやく試してみました。
接続を図示します。バッテリからの出力が3.7VでESP-WROOM-02への入力が3.3Vです。
3.7Vから3.3Vを生成する低消費電力の小型LDOがなかなか無くて探し回りましたが、なんとか秋葉原で調達することができました。
さて実際に動くか試してみましょう。
おぉ、きちんと動きました。しかもLEDの抵抗なしで動くとは・・・。余力がまだありそうです。正直、ESP-WROOM-02の起動時の突入電流でまともに駆動できないのではと思ったのですが杞憂だったようです。
残す課題は MPU6050 の追加のみとなりました。バッテリーとFLASHの容量が足りるかなー?
σ(ー. ー )
ESP-WROOM-02で心拍センサーを動かしてみた [Arduino]
ESP-WROOM-02のADCは0Vから1Vの範囲しか測定できないので、3.3Vの心拍センサーの出力をだいたい1/3に分圧して入力しています。
スケッチを設計するポイントは、
・ シグナル値の読み込みに analobRead() 関数ではなく sysytem_adc_read() 関数を使用
・ 割り込みの設定に、timer0_isr_init()、timer0_attachInterrupt()、timer0_write() 関数を使用
・ 時間取得に、ESP.getCycleCount() 関数を使用
の3点です。
extern "C" { #include "user_interface.h" } #define N 10 volatile int BPM; volatile int Signal; volatile int IBI = 600; volatile boolean Pulse = false; volatile boolean QS = false; volatile int Rate[N]; volatile unsigned long CurrBeatTime = 0; volatile unsigned long LastBeatTime = 0; volatile int P = 500; volatile int T = 500; volatile int Threshold = 512; volatile int Amplifier = 100; int PulseSensorPin = 17; int FadePin = 4; int FadeRate = 0; void timer0_ISR (void) { noInterrupts(); Signal = system_adc_read(); CurrBeatTime = getCurrentTime(); // msec unsigned long interval = CurrBeatTime - LastBeatTime; // hold bottom if ((Signal < Threshold) && (interval > (IBI*3) / 5)) { if (Signal < T) { T = Signal; } } // hold peak if (Signal > Threshold && Signal > P) { P = Signal; } if (interval > 250 /* ms */) { // check if Signal is over Threshold if ((Signal > Threshold) && !Pulse && (interval > (IBI*3) / 5)) { Pulse = true; IBI = interval; if (Rate[0] < 0) { // first time Rate[0] = 0; LastBeatTime = getCurrentTime(); setupTimer(10); noInterrupts(); return; } else if (Rate[0] == 0) { // second time for (int i = 0; i < N; ++i) { Rate[i] = IBI; } } word running_total = 0; for (int i = 0; i < N-1; ++i) { Rate[i] = Rate[i+1]; running_total += Rate[i]; } Rate[N-1] = IBI; running_total += IBI; running_total /= N; BPM = 60000 / running_total; QS = true; LastBeatTime = getCurrentTime(); } } // check if Signal is under Threshold if ((Signal < Threshold) && Pulse) { Pulse = false; Amplifier = P - T; Threshold = Amplifier / 2 + T; // revise Threshold P = Threshold; T = Threshold; } // check if no Signal is over 2.5 sec if (interval > 2500 /* ms */) { Threshold = 512; P = 500; T = 500; LastBeatTime = getCurrentTime(); for (int i = 0; i < N; ++i) { Rate[i] = -1; } } setupTimer(10); interrupts(); } void setupTimer(int m /* msec */) { timer0_isr_init(); timer0_attachInterrupt(timer0_ISR); timer0_write(ESP.getCycleCount() + 80000L * m); // 80MHz/1000 == 1msec } unsigned long getCurrentTime() { return ESP.getCycleCount() / 80000L; } void setup() { pinMode(FadePin, OUTPUT); analogWriteRange(255); Serial.begin(115200); noInterrupts(); setupTimer(10); interrupts(); LastBeatTime = getCurrentTime(); // msec } void loop() { if (QS) { FadeRate = 255; Serial.print("BPM: "); Serial.println(BPM); QS = false; } FadeRate -= 15; FadeRate = constrain(FadeRate, 0, 255); analogWrite(FadePin, FadeRate); delay(20); }
さて、きちんと動くかなー?
システムから時間をとっているせいか、Arduno Pro Mini で使ったアルゴリズムよりも精度よく測定しているような気がします。次はいよいよバッテリー駆動にチャレンジかなぁ。
σ(^_^)