道路施設の維持管理のために適切な予防保全が必要と言われていますが、人口が減少がしていく中で山間の生活道路の質を維持するために、コストをかけずに客観的な情報を蓄積していけるシステムが望まれているのではないでしょうか?
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に表示した例です。三陸海岸を探索していた軌跡です。