GPSログのビジュアライズ#01

作りかけているGPSロガーですが、ちょっと持ち歩いてみた感じ、どうやら動いていそうなので、ログを見てみたいと思います。

保存されている、GPS.LOGを開いて、一行抜き出してみると(詳細は隠しています)、こんな感じ。

2020/12/2,9:44:17,43.xxxxxx,141.xxxxxx,39.60

1行がカンマで区切られたCSVで、

日付 , 時間, 緯度, 経度, 高度

となっています。ひとまず、緯度と経度をプロットしてみたいと思います。python でサクッとできるとスマートなのかなとも思いますが、使い慣れてないのでやめたい。Processing なのか、openFrameworks なのか、実は使い慣れている Objective-C++ とOpenGLでやるのが最終的に速い気がするのですが、環境としてはマイナーなのでやめた方がよいかもしれないですね…

せっかくなので、Python でやってみることにします。

ちょっと調べてみると、csv周りは pandas 、グラフ周りは matplotlib がよさそうなので、これらをpipでインストールしてみました。pipが古いと言われましたが放置して、デスクトップにおいた先ほどの “GPS.LOG” を、”gps.csv”にリネーム。(あとで確認したら、そのままでも読めました…)

とりあえず、Terminal から Python3 を立ち上げて、読み込み&コラムに適当な名前を放り込んでダンプしてみると、

>>> import pandas as pd
>>> import matplotlib.pyplot as plt
>>> df = pd.read_csv('/Users/h/Desktop/gps.csv', names=['date', 'time', 'lat', 'lng', 'alt'])
>>> print(df)
            date      time        lat        lng   alt
0       2000/0/0  23:59:42   0.000000    0.00000   0.0
1       2000/0/0  23:59:43   0.000000    0.00000   0.0
2       2000/0/0  23:59:44   0.000000    0.00000   0.0
3       2000/0/0  23:59:45   0.000000    0.00000   0.0
4       2000/0/0  23:59:46   0.000000    0.00000   0.0
...          ...       ...        ...        ...   ...
19114  2020/12/2   9:44:37  43.xxxxxx  141.xxxxx  40.6
19115  2020/12/2   9:44:37  43.xxxxxx  141.xxxxx  40.6
19116  2020/12/2   9:44:38  43.xxxxxx  141.xxxxx  40.6
19117  2020/12/2   9:44:38  43.xxxxxx  141.xxxxx  40.6
19118  2020/12/2   9:44:39  43.xxxxxx  141.xxxxx  40.7

[19119 rows x 5 columns]

という感じで読み込めました。次は、lat, lng を xy でプロットしたいので、しばし検索したところ、scatter という関数をみつけました。

plt.scatter(df['lng'],df['lat'])

リターン。何も起きず。

plt.show()

リターンすると、ウィンドウが開きました。こんな感じ。

なんとなく緯度経度でプロットされてそうな気配。簡単すぎて拍子抜けしていると、丁寧にUIがついている!ことに気づきました。ルーペを使って拡大すると、

きれいに録れてます。よかった。

コードをまとめると、

import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('/Users/h/Desktop/1202_tesetGPS.LOG', names=['date', 'time', 'lat', 'lng', 'alt'])
plt.scatter(df['lng'],df['lat'])
plt.show()

たったこれだけでした。Pythonユーザが多いのも納得できます。

簡単なので、気になっていることを確認してみます。というのも、高度情報の精度がかなり怪しい気がしていて、何かのタイミングで確認しないといけないと思ってたところでした。

単なる折れ線グラフはこれで書けます。

plt.plot(df['alt'])

リターンして、show()すると、

!!!

これは怪しいというか、全然あてにならない?準天頂衛星のみちびき対応のモジュールなのでちょっと期待してたのですが。。。ライブラリのバグ?の可能性は検索する限り報告されてなさそうですし、、、ただ、前半の値が安定している部分以外は、部屋に置いていた時間である可能性もなくはないので、もうちょっと詳細に検証が必要です。今は時間がないのですが、いずれ検証したいと思います。

GPSロガー改良 #02

前回は、GPSからの受信データを簡単に扱うことのできるライブラリ、TinyGPS++を使ってみました。今回はSDカードに書き込む部分を追加する予定でしたが、「TinyGPS++ の、location.isUpdated() がtrueのときに、緯度/経度/高度をSDカードに書き込む、という部分は、それほど難しくなく実装できるうえ、これでは時間の情報が残らないため、GPSのログとしては物足りません。なので、緯度/経度/高度だけでなく、日付や時間を一緒に残すように書き換えてみたいと思います。

Arduino IDEでは、ライブラリの中身をエディタから参照できないので(僕が知る限り)、ひとまずライブラリの中身をの覗いて作戦を立てます。

/Arduino/libraries/TinyGPSPlus/src/TinyGPS++.cppとヘッダ をXcodeで開いてツラツラと眺めてみたところ、時間が更新されたかどうかは、TinyGPSTimeのupdated というフラグを見てやるのがよさそうです。

TinyGPSDateという構造体もあって、日付をまたぐ場合は、こいつのupdatedを見ないといけないかな?とも思いましたが、それって基本的に時間も更新されてるはずなので、TinyGPSTime だけ見ればよいのかなと思います。ただ、日付が入っているGPSのセンテンスと、時間が入っているGPSのセンテンスが違ったような気がするので、必ずしも日付と時間が同じタイミングで更新されるかどうかはちょっと怪しいですね。深掘りした方がよいのでしょうが、とりあえず今の段階では、TinyGPS++の時間が更新されたタイミングで、日付、時刻、位置情報を書き込むことにします。なので、受信状態がよければ、基本的に1秒間隔のデータが出来上がるはずです。

ちょっと整えてみたものがこちら。(改行が多いとよく言われますが、僕としてはこれが読みやすいと思っています)

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SD.h>

#define SERIAL_BAUD 9600

const int GPS_RX = 8;
const int GPS_TX = 9;

const int CHIP_SELECT = 4;  //for SD

TinyGPSPlus gps;
SoftwareSerial gpsSerial(GPS_RX, GPS_TX);


void setup() {

  Serial.begin(SERIAL_BAUD);
  while (!Serial) {} // wait
  Serial.println("serial initialized\n");

  gpsSerial.begin(9600);

  setupSD();
}

void loop() {

  while (gpsSerial.available() > 0) {
    
    char c = gpsSerial.read();
    //Serial.print(c);
    gps.encode(c);

    if (gps.time.isUpdated()) {

      writeData();

      Serial.print("year : "); Serial.println(gps.date.year());
      Serial.print("mon : "); Serial.println(gps.date.month());
      Serial.print("day : "); Serial.println(gps.date.day());

      Serial.print("hour : "); Serial.println(gps.time.hour());
      Serial.print("min : "); Serial.println(gps.time.minute());
      Serial.print("sec : "); Serial.println(gps.time.second());

      Serial.print("lat : "); Serial.println(gps.location.lat(), 6);
      Serial.print("lng : "); Serial.println(gps.location.lng(), 6);
      Serial.print("alt : "); Serial.println(gps.altitude.meters());

    }
  }
}


void setupSD() {
  
  Serial.print("Init SD.");
  
  if (!SD.begin(CHIP_SELECT)) {
    Serial.println("Card failed, or not present");
    while (1);
  }
  Serial.println("SD initialized.");
}


void writeData() {

  File dataFile = SD.open("gps.log", FILE_WRITE);

  if (dataFile) {

    String str;
    str = String(gps.date.year());
    str += "/";
    str += String(gps.date.month());
    str += "/";
    str += String(gps.date.day());
    str += ",";
    str += String(gps.time.hour());
    str += ":";
    str += String(gps.time.minute());
    str += ":";
    str += String(gps.time.second());
    str += ",";
    str += String(gps.location.lat(), 6);
    str += ",";
    str += String(gps.location.lng(), 6);
    str += ",";
    str += String(gps.altitude.meters());

    dataFile.println(str);
    dataFile.flush();

  } else {
    Serial.println("error opening gps.log");
  }

  dataFile.close();

}

しばらく走らせてみて、出来上がったファイルはこんな感じです。

住所がばれるのでモザイク入ってます

同じ時刻に2回保存されているのは、時間を更新するセンテンスが2回くるからですかね。。。最後に書き込んだ時刻を見て、重複する場合は書き込まない、とした方がきれいかもですね。

途中で信号が切れた場合どうなるのか、など、ちょっと挙動がわからない部分があるので検証が必要です。5日にSIAFラボのメンバーでちょっと長い距離を歩くので、それまでに動作確認したいと思います。

GPSロガー改良 #01

大雪山に持っていったGPSロガーで録ったデータですが、データの欠損がそこそこありました。受信状態は悪くなかったはずなので、何か他に原因があるのではないかと考えています。GPSモジュールからの信号はシリアルで受け取りますが、いつ何バイト送られてくるかが決まっているわけではないので、SDカードに書き込むタイミングとの問題があるかな?と想像しています。

よく使われているTinyGPS++というライブラリがありますが、容量がそこそこ大きいので、のちのち、あれこれセンサーを載せたいと考えていたため、前回はライブラリは使わず、自分でパースしていました。きちんと動いていましたが、処理が最適とは思えないので、今回は、ひとまずライブラリを使って実装してみたいと思います。

前回持っていったロガーはこんな感じです。冷凍庫の実験で-35℃まで下げた結果、動作がおかしくなりましたが、常温で放置した結果、今のところ動いているようです。(このあたり、きちんと検証していくためにも、まずきちんとモニタリングできる低温環境を作らなければなりません…)

デバッグ用にLEDがついてます。

ひとまず、ライブラリを使って受信するところまでのコード。

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#define SERIAL_BAUD 9600

const int GPS_RX = 8;
const int GPS_TX = 9;


TinyGPSPlus gps;
SoftwareSerial gpsSerial(GPS_RX, GPS_TX);


void setup() {

  Serial.begin(SERIAL_BAUD);
  while (!Serial) {} // wait
  Serial.println("serial initialized\n");

  gpsSerial.begin(9600);

}

void loop() {

  while (gpsSerial.available() > 0) {
    char c = gpsSerial.read();
    Serial.print(c);

    gps.encode(c);
    if (gps.location.isUpdated()) {
      Serial.print("lat : "); Serial.println(gps.location.lat(), 6);
      Serial.print("lng : "); Serial.println(gps.location.lng(), 6);
      Serial.print("alt : "); Serial.println(gps.altitude.meters());
    }
  }
}

シリアルモニターに返ってきているのを確認できました。

次回、SDカードに書き込む部分を追加していきますが、ここでタイミングを考えないと前回と同じ結果になる気がします。ひとまず、一定間隔で書き込むのではなく、受信できているときだけ、緯度/経度/高度をSDカードに書き込んでみたいと思います。

 

大雪山VR動画公開

https://youtu.be/AQ7V0IVCxlI

10月26日に大雪山に行ってきました。編集自体はとうの昔に終わっていたのですが、タイミングを計りかねて公開できていませんでした。位置付けとしてはプロジェクトのキックオフです。EDL&RV では、引き続き、ロケ撮影のVR動画をアップしていきたいと思いますので、SIAFラボのYoutubeチャンネルもチェックしていただければと思います。

この日に録ったデータについては、また改めてポストしたいと思います。