道路施設の維持管理のために適切な予防保全が必要と言われていますが、人口が減少がしていく中で山間の生活道路の質を維持するために、コストをかけずに客観的な情報を蓄積していけるシステムが望まれているのではないでしょうか?
Arduino GPSロガーは路面のデコボコの振動を加速度センサーにより定量化しGPSによる位置情報とともにSDカードに記録して路面診断用のデータを蓄積するツールです。
使用するパーツ
Arduino Pro Mini 328 3.3V 8MHz(スイッチサイエンス 1,243 円)
あるいは互換機 EasyWordMall Pro Mini モジュール Atmega328 3.3V 8M Arduino用 (EasyWordMall 375円)
GPS受信機キット (秋月電子通商 2200円)1PPS出力付き
マイクロSDカードスロットDIP化キット(秋月電子通商 300円)
MPU-6050 使用 3軸ジャイロスコープ・3軸加速度センサー モジュール(GY-521)(Umemoto LLC 198円)
動作の概要
電源はIoT機器用モバイルバッテリー cheero Canvas 3200mAhがお勧めです。一般的なモバイルバッテリーでは消費電力が小さすぎて自動的に給電が停止してしまいます。USBケーブルの電源線から5VをPro MiniとGPSモジュールに給電し、Pro Miniから供給される3.3VでSDとGY-521に給電します。
GPSから日付、時刻、緯度、経度、捕捉衛星数を読み取ります。加速度・ジャイロセンサーから約50msec毎に200回データを読み取り、進行方向の加速度の平均値と標準偏差、上下方向の加速度の平均値と標準偏差、左右方向の回転角速度の平均値と最大値と最小値を求めます。
測定の周期はおよそ14秒でしたが、日付から自動生成されるファイル名のファイルmm_dd.txtにカンマ区切りで追記されます。
16.12.31追記
GPSでの時刻はUTC(世界協定時)で出力されるので日本では9時間加算する必要があります。日付の繰り上がりなどを考えると面倒なのでUTCで我慢していましたが、落ち着いて考えると日本時間の9時にファイルの日付が変わるのでそれはそれで面倒です。時差を反映させるTimeというライブラリを見つけましたので、実装しました。
データ表示
オープンソースソフトウェアのQGISを使用して表示します。国土地理院のタイル地図に測定点を表示します。例として振動が大きかったポイントは赤い大きな星印、振動が少なかった地点は小さな丸印で表示しています。
結線
(Arduino Pro Mini 3.3V)
[GPS–Pro Mini]
5V–5V
GND–GND
RXD–NC
TXD–D2
[SD–Pro Mini]
DAT2–NC
DAT3–D10
CMD–D11
VDD–3.3V
CLK–D13
VSS–GND
DAT0–D12
DAT1–NC
SWB–NC
SWA–NC
[GY-521–Pro Mini]
VCC–3.3V
GND–GND
SCL–A5(SCL)
SDA–A4(SDA)
XDA–NC
XCL–NC
ADD–NC
INT–NC
[LED–Pro Mini]
RED LED–D9
GREEN LED–D8
時差対応版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
#include <SPI.h> #include <SD.h> #include<Wire.h> #include <SoftwareSerial.h> #include <TimeLib.h> #include <TinyGPS.h> SoftwareSerial mySerial(2, 3); // RX, TX TinyGPS gps; const int sdsc = 10; File myFile; String fileName; void measure(); const int MPU_addr=0x68; // I2C address of the MPU-6050 int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; float dAcX,dAcY,dAcZ,dTmp,dGyZ; float avedAcX,sigmadAcX,avedAcZ,sigmadAcZ,adGyZ,tdGyZ,bdGyZ; char saveX[8],ssigX[8]; char saveZ[8],ssigZ[8]; // Offset hours from gps time (UTC) const int offset = 9; // Japan Time //const int offset = 1; // Central European Time //const int offset = -5; // Eastern Standard Time (USA) //const int offset = -4; // Eastern Daylight Time (USA) //const int offset = -8; // Pacific Standard Time (USA) //const int offset = -7; // Pacific Daylight Time (USA) int Year; //int year; byte Month, Day, Hour, Minute, Second, hundredths; //byte month, day, hour, minute, second, hundredths; unsigned long age; float flat, flon, falt; int isat; char slat[12],slon[12],salt[12]; int redLED=9; int greenLED=8; void setup(){ Wire.begin(); Wire.beginTransmission(MPU_addr); Wire.write(0x6B); // PWR_MGMT_1 register Wire.write(0); // set to zero (wakes up the MPU-6050) Wire.endTransmission(true); Serial.begin(9600); mySerial.begin(9600); if (!SD.begin(sdsc)) { Serial.println("SD failed"); } pinMode(redLED, OUTPUT); pinMode(greenLED, OUTPUT); } void loop(){ bool newData = false; unsigned long chars; unsigned short sentences, failed; // For one second we parse GPS data and report some key values for (unsigned long start = millis(); millis() - start < 1000;) { while (mySerial.available()) { char c = mySerial.read(); if (gps.encode(c)) newData = true; } } if (newData) { gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, &hundredths, &age); //gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age); setTime(Hour, Minute, Second, Day, Month, Year); adjustTime(offset * SECS_PER_HOUR); gps.f_get_position(&flat, &flon, &age); falt=gps.f_altitude(); isat=gps.satellites(); Serial.print(year()); Serial.print("/"); Serial.print(month()); Serial.print("/"); Serial.print(day()); Serial.print(","); Serial.print(hour()); Serial.print(":"); Serial.print(minute()); Serial.print(":"); Serial.print(second()); Serial.print(","); dtostrf(flat,9, 6, slat); dtostrf(flon,9, 6, slon); dtostrf(falt,5, 1, salt); Serial.print(slat); Serial.print(","); Serial.print(slon); Serial.print(","); Serial.print(salt); Serial.print(","); Serial.print(isat); Serial.print(","); } measure(); dtostrf(avedAcX,6, 4, saveX); dtostrf(sigmadAcX,6, 4, ssigX); dtostrf(avedAcZ,6, 4, saveZ); dtostrf(sigmadAcZ,6, 4, ssigZ); Serial.print(dTmp); Serial.print(","); Serial.print(saveX); Serial.print(","); Serial.print(ssigX); Serial.print(","); Serial.print(saveZ); Serial.print(","); Serial.print(ssigZ); Serial.print(","); Serial.print(adGyZ); Serial.print(","); Serial.print(tdGyZ); Serial.print(","); Serial.print(bdGyZ); Serial.print(","); Serial.println(""); digitalWrite(greenLED, LOW); digitalWrite(redLED, LOW); fileName=String("log/") + month() + "_" + day() + String(".txt"); Serial.println(fileName); //myFile = SD.open("log/roadpm.txt", FILE_WRITE); myFile = SD.open(fileName, FILE_WRITE); digitalWrite(greenLED, LOW); digitalWrite(redLED, LOW); unsigned long write_size = myFile.size(); if (myFile) { myFile.print(year()); myFile.print("/"); myFile.print(month()); myFile.print("/"); myFile.print(day()); myFile.print(","); myFile.print(hour()); myFile.print(":"); myFile.print(minute()); myFile.print(":"); myFile.print(second()); myFile.print(","); myFile.print(slat); myFile.print(","); myFile.print(slon); myFile.print(","); myFile.print(salt); myFile.print(","); myFile.print(isat); myFile.print(","); myFile.print(dTmp); myFile.print(","); myFile.print(saveX); myFile.print(","); myFile.print(ssigX); myFile.print(","); myFile.print(saveZ); myFile.print(","); myFile.print(ssigZ); myFile.print(","); myFile.print(adGyZ); myFile.print(","); myFile.print(tdGyZ); myFile.print(","); myFile.print(bdGyZ); myFile.print(","); myFile.println(""); write_size = myFile.size() - write_size; myFile.close(); } if(write_size>10){ digitalWrite(greenLED, HIGH); } else{ digitalWrite(redLED, HIGH); if(write_size==0){ SD.begin(sdsc); } } } void measure(){ int Count=200; float SsqdAcX=0.0,SdAcX=0.0; float sqdAcX,dAcX; float SsqdAcZ=0.0,SdAcZ=0.0; float sqdAcZ,dAcZ; adGyZ=0,tdGyZ=-999.0,bdGyZ=999.0; for(int i=0;i<Count;i++){ Wire.beginTransmission(MPU_addr); Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) Wire.endTransmission(false); Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L) AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L) AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L) Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L) GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L) GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L) GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L) dAcX=AcX/32768.0*2; dAcY=AcY/32768.0*2; dAcZ=AcZ/32768.0*2; sqdAcX=dAcX*dAcX; SsqdAcX = SsqdAcX + sqdAcX; SdAcX = SdAcX + dAcX; sqdAcZ=dAcZ*dAcZ; SsqdAcZ = SsqdAcZ + sqdAcZ; SdAcZ = SdAcZ + dAcZ; dTmp=Tmp/340.00+36.53; dGyZ=GyZ/32768.0*250; if(dGyZ>tdGyZ) tdGyZ=dGyZ; if(dGyZ<bdGyZ) bdGyZ=dGyZ; adGyZ = adGyZ + dGyZ; delay(50); } avedAcX = SdAcX/Count; sigmadAcX = sqrt((SsqdAcX-SdAcX*SdAcX/Count)/(Count-1)); avedAcZ = SdAcZ/Count; sigmadAcZ = sqrt((SsqdAcZ-SdAcZ*SdAcZ/Count)/(Count-1)); adGyZ=adGyZ/Count; } |
UTC版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
#include <SPI.h> #include <SD.h> #include<Wire.h> #include <SoftwareSerial.h> #include <TinyGPS.h> SoftwareSerial mySerial(2, 3); // RX, TX TinyGPS gps; const int sdsc = 10; File myFile; String fileName; void measure(); const int MPU_addr=0x68; // I2C address of the MPU-6050 int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; float dAcX,dAcY,dAcZ,dTmp,dGyZ; float avedAcX,sigmadAcX,avedAcZ,sigmadAcZ,adGyZ,tdGyZ,bdGyZ; char saveX[8],ssigX[8]; char saveZ[8],ssigZ[8]; int year; byte month, day, hour, minute, second, hundredths; unsigned long age; float flat, flon, falt; int isat; char slat[12],slon[12],salt[12]; int redLED=9; int greenLED=8; void setup(){ Wire.begin(); Wire.beginTransmission(MPU_addr); Wire.write(0x6B); // PWR_MGMT_1 register Wire.write(0); // set to zero (wakes up the MPU-6050) Wire.endTransmission(true); Serial.begin(9600); mySerial.begin(9600); if (!SD.begin(sdsc)) { Serial.println("SD failed"); } pinMode(redLED, OUTPUT); pinMode(greenLED, OUTPUT); } void loop(){ bool newData = false; unsigned long chars; unsigned short sentences, failed; // For one second we parse GPS data and report some key values for (unsigned long start = millis(); millis() - start < 1000;) { while (mySerial.available()) { char c = mySerial.read(); if (gps.encode(c)) newData = true; } } if (newData) { gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age); gps.f_get_position(&flat, &flon, &age); falt=gps.f_altitude(); isat=gps.satellites(); Serial.print(year); Serial.print("/"); Serial.print(month); Serial.print("/"); Serial.print(day); Serial.print(","); Serial.print(hour); Serial.print(":"); Serial.print(minute); Serial.print(":"); Serial.print(second); Serial.print(","); dtostrf(flat,9, 6, slat); dtostrf(flon,9, 6, slon); dtostrf(falt,5, 1, salt); Serial.print(slat); Serial.print(","); Serial.print(slon); Serial.print(","); Serial.print(salt); Serial.print(","); Serial.print(isat); Serial.print(","); } measure(); dtostrf(avedAcX,6, 4, saveX); dtostrf(sigmadAcX,6, 4, ssigX); dtostrf(avedAcZ,6, 4, saveZ); dtostrf(sigmadAcZ,6, 4, ssigZ); Serial.print(dTmp); Serial.print(","); Serial.print(saveX); Serial.print(","); Serial.print(ssigX); Serial.print(","); Serial.print(saveZ); Serial.print(","); Serial.print(ssigZ); Serial.print(","); Serial.print(adGyZ); Serial.print(","); Serial.print(tdGyZ); Serial.print(","); Serial.print(bdGyZ); Serial.print(","); Serial.println(""); digitalWrite(greenLED, LOW); digitalWrite(redLED, LOW); fileName=String("log/") + month + "_" + day + String(".txt"); Serial.println(fileName); //myFile = SD.open("log/roadpm.txt", FILE_WRITE); myFile = SD.open(fileName, FILE_WRITE); digitalWrite(greenLED, LOW); digitalWrite(redLED, LOW); unsigned long write_size = myFile.size(); if (myFile) { myFile.print(year); myFile.print("/"); myFile.print(month); myFile.print("/"); myFile.print(day); myFile.print(","); myFile.print(hour); myFile.print(":"); myFile.print(minute); myFile.print(":"); myFile.print(second); myFile.print(","); myFile.print(slat); myFile.print(","); myFile.print(slon); myFile.print(","); myFile.print(salt); myFile.print(","); myFile.print(isat); myFile.print(","); myFile.print(dTmp); myFile.print(","); myFile.print(saveX); myFile.print(","); myFile.print(ssigX); myFile.print(","); myFile.print(saveZ); myFile.print(","); myFile.print(ssigZ); myFile.print(","); myFile.print(adGyZ); myFile.print(","); myFile.print(tdGyZ); myFile.print(","); myFile.print(bdGyZ); myFile.print(","); myFile.println(""); write_size = myFile.size() - write_size; myFile.close(); } if(write_size>10){ digitalWrite(greenLED, HIGH); } else{ digitalWrite(redLED, HIGH); if(write_size==0){ SD.begin(sdsc); } } } void measure(){ int Count=200; float SsqdAcX=0.0,SdAcX=0.0; float sqdAcX,dAcX; float SsqdAcZ=0.0,SdAcZ=0.0; float sqdAcZ,dAcZ; adGyZ=0,tdGyZ=-999.0,bdGyZ=999.0; for(int i=0;i<Count;i++){ Wire.beginTransmission(MPU_addr); Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) Wire.endTransmission(false); Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L) AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L) AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L) Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L) GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L) GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L) GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L) dAcX=AcX/32768.0*2; dAcY=AcY/32768.0*2; dAcZ=AcZ/32768.0*2; sqdAcX=dAcX*dAcX; SsqdAcX = SsqdAcX + sqdAcX; SdAcX = SdAcX + dAcX; sqdAcZ=dAcZ*dAcZ; SsqdAcZ = SsqdAcZ + sqdAcZ; SdAcZ = SdAcZ + dAcZ; dTmp=Tmp/340.00+36.53; dGyZ=GyZ/32768.0*250; if(dGyZ>tdGyZ) tdGyZ=dGyZ; if(dGyZ<bdGyZ) bdGyZ=dGyZ; adGyZ = adGyZ + dGyZ; delay(50); } avedAcX = SdAcX/Count; sigmadAcX = sqrt((SsqdAcX-SdAcX*SdAcX/Count)/(Count-1)); avedAcZ = SdAcZ/Count; sigmadAcZ = sqrt((SsqdAcZ-SdAcZ*SdAcZ/Count)/(Count-1)); adGyZ=adGyZ/Count; } |
QGIS上でGoogle Satelliteに表示した例です。三陸海岸を探索していた軌跡です。
測定周期を変えるには、どうすればいいのですか?
こんにちは、
時差対応版プログラムの行番号で説明します。
振動などを常時モニターするという狙いでプログラムを作りましたので、加速度などの測定周期は行番号198のdelay(50)でおよそ50msec間隔にしています。行番号161のcount=200で、200回測定したら統計データを記録しています。
計算上は記録周期は50msec*200=10secですが、処理時間等がかかるのでSDには14秒間隔で記録されていました。
記録する間隔を変更するには目的に応じて行番号161と198の数値を変更するのが良いと思います。
ありがとうございます
もう一つ
arduino unoで動作確認したんですが、取得したデータはシリアルモニタにはでるのですが、sdカードに保存できてないのですが、原因はわかりますか? 電圧は3.3vまで降下しています。
こんにちは、
SDは3.3Vで読み書きしないといけないので、UNOを使い場合は注意が必要です。(私はPro Mini 3.3V版を使っているのでそのまま接続できました。)
Arduino UNO SDをキーワードに検索するとUNOでSDカードに記録する方法について書いてあるサイトが見つかると思います。
私がよく参考にさせてもらっている「なんでも作っちゃう、かも。」さんが参考になると思います。
http://arms22.blog91.fc2.com/blog-entry-502.html
SDに記録できると便利なので頑張ってください。
こんにちは
google earthでデータ表示することは、可能ですか?
こんにちは、
QGISのOpenLayers pluginを使ってGoogle MapsのSatelliteを表示してGPSログをプロットすることができます。
ほんとにありがとうございます
こんばんは
配線を変えたいのですが、SDのCMD–D14
CLK–D16 , DAT0–D15にするとき、プログラムを変更する必要はありますか?
私はSPIの標準的に指定されているピンでしか使ったことがないのでわかりませんが、
https://www.arduino.cc/en/Reference/SD
に書いてあることによればピンを変更するのは難しいのではないかと思います。ISCPというピンが付いているボードならばそれを使う選択肢があるようです。
https://garretlab.web.fc2.com/arduino_reference/libraries/standard_libraries/SPI/index.html
私も最近Nanoで3,5,9,10,11-pinをカウンター動作で使う必要があり、ISCPでSDを使う検討をしたのですが、10-pinがネックで断念しています。10-pinの代わりに4-pinを使っている例もあるのでできるのかもしれません。
チェレンジうまくいったら教えてください。
Koji様
初めまして。
私はバイクでローカルレースを楽しんでいる者です。
バイクの走行データを記録したくて色々検索しているうちにこのページにたどり着付きました。
早速、スケッチをコピーさせて頂きますコンパルを行ったのですがエラーが出てしまいました。
Tynigpsに関係する様なのですが、よく解らないのが正直なところです。このスケッチを使うに辺り何か中点は有りますでしょうか。素人質問で申し訳ありません。
こんにちは オノデンさん
書かれているエラーの状況から考えて可能性が高いものを書いてみますが、外れているかもしれません。以下の対応でダメな場合は「エラーをコピーしてコメント欄にペースト」してみてください。
“TinyGPS”はGPSからシリアルで受け取った大量の文字列を必要な部分だけを取り出しやすくするライブラリです。この機能を使うためにはインストールが必要になります。インストールされていなければコンパイルの時にエラーが出ます。
インストールする方法は以下のURLが参考になると思います。
https://garretlab.web.fc2.com/arduino/libraries/installation/index.html
ライブラリマネージャーの「検索をフィルタ…」にTinyGPSと入力するとTinyGPSのインストール欄が表示されると思います。
使っているライブラリは六個ですが、四個(SPI,SD,Wire,SoftwareSerial)は標準でインストールされていたと思います。TinyGPSとTimeLibはライブラリマネージャーで各自インストールする必要があります。
返信ありがとうございますオノデンです。
お恥ずかしい話ですがTinyGPSとTinyGPS++の違いでした。
TinyGPSをインストールする事で無事コンパイルする事が出来ました。
次の段階として私の使用しているESP-WROOM-02に合わせて
ピン配置の変更に挑戦中です。