fc2ブログ

帰省時のドラレコデータを解析する(5)

 今回で5回目ですが、話題を引っ張りすぎたので今回で最終回にします・・。
帰省の行きと帰りで「神戸JCT-高槻JCT間」は別ルートを通りました。行きは名神高速→中国道→山陽道の昔からあるルートで、帰りは山陽道→新名神高速→名神高速と2018年3月に開通したルートを初めて通りました。走行距離はほぼ同じです。

 行きは、吹田や宝塚を通るルートで渋滞が多く、いつも難儀します。ただ、万博公園の「太陽の塔」を横目にドライブできるのは気に入っています。HighwayMap1_190112.png 帰りの新名神は山間部を走るためか、ほとんどトンネルだった印象でした。HighwayMap2_190112.png
 今回、この両ルートについて、ドライブレコーダで記録した標高を比較してみました。作成したRのコード(行きの場合:帰りの場合は高槻と神戸が逆になる)は以下の通りです。
df_data <- read.csv('resultA.csv',header = TRUE) # Data read

N_lat = df_data[,3]
E_lng = df_data[,4]

# Takatsuki JCT
N_lat_target = 34.8671
E_lng_target = 135.6264
Distance1 = sqrt((N_lat-N_lat_target)^2 + (E_lng-E_lng_target)^2)
TargetIndex1 = which.min(Distance1)

# Kobe JCT
N_lat_target = 34.8497
E_lng_target = 135.2223
Distance2 = sqrt((N_lat-N_lat_target)^2 + (E_lng-E_lng_target)^2)
TargetIndex2 = which.min(Distance2)

Altitude = df_data[TargetIndex1:TargetIndex2,5]

# Output
plot(Altitude, type="l", col="red",ylab="Altitude [m]", xlab = "Data Index", main="Altitude of the highway")
print(df_data[TargetIndex1,])
print(df_data[TargetIndex2,])
summary(Altitude)
print(max(Altitude)-min(Altitude))
print(sd(Altitude))
両ルートの分岐点である高槻JCT、神戸JCTの位置情報(緯度・経度)を設定し、走行データの中からそれぞれに一番近い位置を検出し、解析範囲に設定しました。

行きの従来ルートの標高結果は以下の通りです。Result_OldRoute_190112.png

> print(df_data[TargetIndex1,])
Date Time North.latitude East.longitude Altitude
10080 2018/12/29 00:44:23 34.86683 135.6262 47.2
> print(df_data[TargetIndex2,])
Date Time North.latitude East.longitude Altitude
13304 2018/12/29 01:11:44 34.84879 135.2218 191.6
> summary(Altitude)
Min. 1st Qu. Median Mean 3rd Qu. Max.
26.10 40.10 50.60 87.06 98.40 278.80
> print(max(Altitude)-min(Altitude))
[1] 252.7
> print(sd(Altitude))
[1] 73.35336
平均値87.06m、中央値50.60m、標高差252.7mでした。吹田から神戸JCTに向かう中国道が山道で標高差が大きな結果になっていますが、ほとんど中央値の50m前後で平坦なルートであることが分かりました。

 帰りの新名神ルートの標高結果は以下の通りです。トンネルが多く、GPS(標高)データが取れていない所(グラフ上で不自然な直線になっている所)がありますが、標高が全体的に高く、道路の高低差も大きな結果でした。Result_NewRoute_190112.png

> print(df_data[TargetIndex1,])
Date Time North.latitude East.longitude Altitude
14060 2019/01/02 17:30:02 34.8498 135.2222 180.7
> print(df_data[TargetIndex2,])
Date Time North.latitude East.longitude Altitude
16185 2019/01/02 17:58:41 34.86737 135.6272 51.2
> summary(Altitude)
Min. 1st Qu. Median Mean 3rd Qu. Max.
47.4 122.1 193.6 177.1 231.8 278.4
> print(max(Altitude)-min(Altitude))
[1] 231
> print(sd(Altitude))
[1] 63.65288
平均値177.1m、中央値193.6m、標高差231mで、平均値、中央値ともに従来ルートより高い結果で、体感と同じものでした。

 帰省時のドライブレコーダのネタで5回もお話しましたが、GPSの基礎知識を含め、いろいろ勉強になりました。
スポンサーサイト



帰省時のドラレコデータを解析する(4)

 1回目の話で、高速道路のIC、SA等の位置情報をRでプロットした結果に触れました。今回はその補足です。
 高速道路の緯度、経度の位置情報はネット上にjsonファイルがありましたので、利用させていただきました。ありがとうございました。

 jsonファイルの中身はこんな感じです。例は山陽道のデータです。
{
"linecolor":"#008000",
"marker":[
{"type":"JCT","pname":"神戸","lat":34.849364,"lng":135.222967},
{"type":"IC","pname":"神戸北","lat":34.840141,"lng":135.198462},
{"type":"JCT","pname":"三木","lat":34.811528,"lng":135.075265},
・・・
{"type":"JCT","pname":"広島","lat":34.449155,"lng":132.412274},
{"type":"IC","pname":"五日市","lat":34.429538,"lng":132.391758},
{"type":"JCT","pname":"廿日市","lat":34.33718,"lng":132.294295}
]
}
 このjsonファイルを解析しやすいように、以下のPythonコードでCSVファイルに変換しました。
import json
import pandas as pd
import numpy as np

with open('highway-sanyou1.json', 'r') as f:
json_data = json.load(f)

print(json.dumps(json_data, sort_keys = True, ensure_ascii=False, indent = 4))
target_dicts = json_data['marker']

namelist = []
latlist = []
lnglist = []
for i in range(0,len(target_dicts)):
a0 = target_dicts[i]
namelist.append(a0['pname'])
latlist.append(a0['lat'])
lnglist.append(a0['lng'])

resdata = np.vstack([namelist, latlist, lnglist])
df = pd.DataFrame(data=resdata).T
df.to_csv('highway-sanyou1.csv', header=False, index=False)
 作成されたCSVファイルは以下の通りです。
神戸,34.849364,135.222967
神戸北,34.840141,135.198462
三木,34.811528,135.075265
・・・
広島,34.449155,132.412274
五日市,34.429538,132.391758
廿日市,34.33718,132.294295

 このCSVファイルを使って、3回目にお話した方法でRで日本地図にプロットしました。帰省ルートは、名神、中国、山陽、再び中国、九州の順に高速道路を利用しましたので、同じ作業を行い、最終結果がこのマップでした。HighWayData190103.png 作業が終わった後にいまさら気付いたのですが、Rでそのままjsonファイルを読み込めるはずと思い、調べてみると、以下の通り、Pythonより簡単に3行で「json→CSV変換」ができることが分かりました。灯台下暗し・・。
library(jsonlite)
data.json <- fromJSON("highway-sanyou1.json")
#print(class(data.json))
#print(data.json)
write.csv(data.json$marker, file = "highway-sanyou1.csv", quote = FALSE)
 ちなみprint文の出力結果は以下の通り。
> print(class(data.json))
[1] "list
> print(data.json)
$linecolor
[1] "#008000"

$marker
type pname lat lng
1 JCT 神戸 34.84936 135.2230
2 IC 神戸北 34.84014 135.1985
3 JCT 三木 34.81153 135.0753
・・・
 また、出力CSVファイルは以下の通りです。
,type,pname,lat,lng
1,JCT,神戸,34.849364,135.222967
2,IC,神戸北,34.840141,135.198462
3,JCT,三木,34.811528,135.075265
・・・
 ただ、もしかすると、Pythonでも今回のようなややこしい方法を用いなくても簡単にjson→CSV変換できる方法があるのかもしれません(勉強不足・・)。

帰省時のドラレコデータを解析する(3)

 前回は、ドライブレコーダのNMEAファイルから必要なデータを抽出し、CSVファイル(result.csv)にする所までのお話でした。今回はその続きです。

 NMEAファイルは3分おきに作成される設定にしており、1時間で20ファイル、帰省時の運転時間は約8時間だったので、160ファイルほどあります。データは0.5秒間隔で作成されていたので、3分(1ファイル)で360データ、8時間で57,600データと膨大になりました。このNMEAファイルからデータ抽出して保存したresult.csvは以下の通りです。
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141400.00,3510.07681,N,13614.27683,E,2,12,0.78,117.9,M,34.8,M,,0000*54"
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141401.00,3510.07318,N,13614.27954,E,2,12,0.78,118.2,M,34.8,M,,0000*51"
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141401.00,3510.07318,N,13614.27954,E,2,12,0.78,118.2,M,34.8,M,,0000*51"
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141402.00,3510.06950,N,13614.28226,E,2,12,0.78,118.5,M,34.8,M,,0000*53"
・・・
 まず、これらのデータから、日付、時刻、緯度、経度、標高のデータを以下のPythonのコード(注意:きれいなコードでありません・・)で抽出しました。
import pandas as pd
import numpy as np
import pytz
from datetime import datetime

# file load
df = pd.read_csv('result.csv', sep=',')

# variables
datex = []
timex = []
positionN = []
positionE = []
altitude = []

# Title label
datex.append("Date")
timex.append("Time")
positionN.append("North latitude")
positionE.append("East longitude")
altitude.append("Altitude")

# timezone setting
jst = pytz.timezone('Asia/Tokyo') # JST
utc = pytz.timezone('UTC')

for i in range(0, len(df.index)):
# date (JST temporary setting)
str_temp = df.iloc[i,0]
str_temp1 = str_temp[len(str_temp)-17:len(str_temp)-5]
jstdata_temp = jst.localize(datetime(int('20'+str_temp1[0:2]), int(str_temp1[2:4]), int(str_temp1[4:6]), int(str_temp1[6:8]), int(str_temp1[8:10]), int(str_temp1[10:12])))
# time (UTC to JST transformation)
str_temp0 = df.iloc[i,1]
a0 = str_temp0.split(",")
a00 = a0[1] # UTC time
utcdata_temp1 = utc.localize(datetime(int('20'+str_temp1[0:2]), int(str_temp1[2:4]), int(str_temp1[4:6]), int(a00[0:2]), int(a00[2:4]), int(a00[4:6])))
jstdata_temp1 = utcdata_temp1.astimezone(jst)
# date (JST Re-setting)
if jstdata_temp.hour - utcdata_temp1.hour < 0:
utcdata = utc.localize(datetime(int('20'+str_temp1[0:2]), int(str_temp1[2:4]), int(str_temp1[4:6])-1, int(a00[0:2]), int(a00[2:4]), int(a00[4:6])))
jstdata = utcdata.astimezone(jst)
else:
jstdata = jstdata_temp1
str_temp2 = '{0:02d}/{1:02d}/{2:02d}'.format(jstdata.year, jstdata.month, jstdata.day)
str_temp3 = '{0:02d}:{1:02d}:{2:02d}'.format(jstdata.hour, jstdata.minute, jstdata.second)
datex.append(str_temp2)
timex.append(str_temp3)
#position North
if a0[2] != "":
a1 = int(float(a0[2])/100)
a11 = (float(a0[2]) - a1*100 ) / 60
a1 = a1 + a11
else:
a1 = 0
positionN.append(str(a1))
#position East
if a0[4] != "":
a2 = int(float(a0[4])/100)
a21 = (float(a0[4]) - a2*100) / 60
a2 = a2 + a21
else:
a2 = 0
positionE.append(str(a2))
#altitude
if a0[9] != "":
a3 = float(a0[9])
else:
a3 = 0
altitude.append(str(a3))

resdata = np.vstack([datex, timex, positionN, positionE, altitude])
df = pd.DataFrame(data=resdata).T
df.to_csv('resultA.csv', header=False, index=False)
コード上の28〜43行目は日時のUTCからJSTへの変換箇所です。すごく面倒なことをしています・・(自己嫌悪・・)。日付データは「$GP始まりのデータ」に情報がないので、保存されたファイル名の時刻(JST)から採取しました(ここが問題)。時刻はUTCなのでJSTに変換しますが、変換時に日付の不整合が生じますので、39〜43行目で修正しています(深く考えずに、軽く流してください・・)。

 最終的に出力したresultA.csvは以下の通りです。
Date,Time,North latitude,East longitude,Altitude
2018/12/28,23:14:01,35.16788633333333,136.23799233333332,118.2
2018/12/28,23:14:01,35.16788633333333,136.23799233333332,118.2
2018/12/28,23:14:02,35.167825,136.23803766666666,118.5
2018/12/28,23:14:02,35.167825,136.23803766666666,118.5
・・・
以前のブログで台風進路をRでマッピングした時と同じ方法で、以下のコードでドライブルートをプロットしました。
setwd('/media/drbobt/UBUNTU_DATA/R/ne_10m_land') # working folder
library(maptools) # maptools

world <- readShapePoly("ne_10m_land.shp") # shape File read
lonlim = c(130,136) # Longitude setting(E130〜136 degrees)
latlim = c(33,36) # Latitude setting(N33〜36 degrees)
plot(world, xlim=lonlim, ylim=latlim, axes=TRUE, xlab="Longitude", ylab="Latitude")

df_data <- read.csv('resultA.csv',header = TRUE) # Data read
for(i in 1:nrow(df_data)){
points(as.numeric(df_data[i,4]), as.numeric(df_data[i,3]), pch=1,col = 'red') # Plot
}
結果は以下の通りで、前々回お話した通りです。MapResult_190108.png
 また、標高データは以下のRのコードで処理しました。
df_data <- read.csv('resultA.csv',header = TRUE) # Data read

altitude = df_data[,5]
plot(altitude, type="l", col="red",ylab="Altitude [m]", xlab = "Data Index", main="Altitude of the highway")

x_sum <- summary(altitude)
print(x_sum)
x_max <- x_sum[6] # max
df_data[altitude == x_max,]
結果は以下の通りで、北緯34.46921°、東経133.031°で最高標高が382.6mであることが分かりました。HighwayAltitudeResult_190108.pngGoogleでこの緯度・経度の値を入力して検索すると、GoogleSearch_190108.png検索した緯度・経度の地図が表示されます。SearchResult1_190108.png地図上のターゲットをクリックすると、ストリートビューが起動し、山陽自動車道の「最高標高地点」と一致することが分かりました。SearchResult2_190108.pngドライブレコーダのGPSでここまでできるとは感動的です。

 NMEAファイルには車の速度の情報も採取できており、処理すると以下の結果でした。DrivingSpeed_190108.png渋滞もなく安定して100km/hでドライブできていることが分かりました。速度が0に落ちている所は、SAで休憩したり、長いトンネルに入ってGPSが機能していない所です。
 長くなりましたので、今回はここまでにします。

帰省時のドラレコデータを解析する(2)

 前回の続きです。ドライブレコーダ(KENWOOD DRV-410:GPS付き)に装着しているSDカード内には動画データ(MOVファイル)の他、GPSデータ(NMEAファイル)が保存されており、今回はそのNMEAファイルの中身のお話です。

このドライブレコーダのNMEAファイルの中身は以下の通りです。$で始まる英数字ですが、2、3行目にN(北緯)やE(東経)の値が読み取れました。
$GTRIP,52b79544
$GPRMC,202645.00,A,3419.89655,N,13217.65892,E,47.063,189.07,281218,,,D*53
$GPGGA,202645.00,3419.89655,N,13217.65892,E,2,12,0.85,92.3,M,30.0,M,,0000*63
$GPGSV,4,1,15,02,26,296,36,03,14,047,22,05,08,233,18,06,57,331,42*76
$GPGSV,4,2,15,09,46,113,37,12,18,308,41,17,66,143,43,19,86,325,42*70
$GPGSV,4,3,15,23,36,072,29,28,08,186,30,42,48,158,31,50,48,158,31*7D
$GPGSV,4,4,15,193,76,164,37,194,03,178,,195,71,179,35*73
$GSENS, 0.127, 0.072,-0.885
$GSENS, 0.064,-0.058,-0.887
$GSENS, 0.082,-0.101,-0.898
$GSENS, 0.117, 0.066,-0.932
$GSENS, 0.105, 0.107,-0.949
以下、2〜12行のパターンで記録される
最初は安易に、データ3419.89655、13217.65892が、北緯34.1989655度、東経132.17.65892度を表しているんだろうと勝手に思い込んで、その値をそのまま解析に用いました。これが間違いでした。Rで地図にプロットすると、ドライブルートが、急にワープしたり海上を走っていたりする結果になりました。ErrorMapPlot_190105.png きちんとGPSが機能しているのだろうかと一瞬疑ってしまいましたが、前回お話した無償ソフトウェアKENWOOD Drive Reviewerではドライブルートが正しくプロットされていたので、データ自身の解釈がおかしいと考え直しました。結局、ネット上のサイトに「NMEAファイル」の解説や情報がありましたので、それらを参考に誤りを修正しました。何事も基本(フォーマット)を理解していないとダメですね。

 ドライブレコーダのNMEAファイル内にはメーカの独自情報($GP始まりでないもの)も含まれているようですが、メイン($GP始まり)の3センテンス($GPRMC、$GPGGA、$GPGSV)の意味は以下の通りです。GPSdata1_190105.png緯度・経度の計算方法が間違っていることが理解できました。度単位のみの値でなく、度と分が融合した値でした。修正したデータでプロットしたのが、前回お話したこのデータです。GPS_Data190103.png $GPGGAには海抜高さ(標高)データも採取できています。GPSdata2_190105.png$GPGSVはGPS衛星に関する情報です。GPSdata3_190105.pngビュー内の総衛星数が15個という多さに驚きを感じます。たくさんのGPS衛星が飛んでいるのですね。ちなみに検出された15個の衛星をこのサイトで調べると、以下の通りでした。GPSdata4_190105.png一部不明なものもありますが、ほとんどはアメリカの衛星です。日本の「みちびき」もありますね。『下町ロケット』のGPS衛星を利用した農業自動化を彷彿させます。

1行目の$GTRIP、8〜12行目の$GSENSは、ドライブレコーダ特有の情報のようです。$GTRIPはエンジンをかけてドライブレコーダのスイッチがONになる度にデータ更新され、OFFになるまで同じデータが記録されていたので、ドライブデータ(TRIPデータ)のラベルのようなものと思われます(詳細は分かりません)。また、$GSENSは加速度センサの値で、順番に「前後、左右、上下」の値で、5回計測されているようです。無償ソフトウェアKENWOOD Drive Reviewerでも加速度Gx、Gy、Gzの値がプロットされていました。

$GPRMCの値は、車がトンネルに入ってGPSが無効になってもデータがロックされて、(意味のないデータが)記録され続けているのに対し、$GPGGAの値は「データなし」になりました。そこで、データ解析を行うにあたり、後者の$GPGGAのデータを用いることにしました。NMEAファイルは3分毎に作成される設定にしているので、膨大な数になりました。データフォルダのファイルを一つずつオープンし、$GPGGAのデータを抽出し、CSVファイルに書き出す、以下の簡単なPythonのコードで処理しました。
from pathlib import Path
import pandas as pd
import numpy as np

p = Path("/home/drbobt/デスクトップ/NORMAL1") #datafile folder path
file_list = sorted(p.glob("*"))

fname = []
position = []
for filename in file_list:
with open(str(filename)) as f:
x0 = f.readlines()
for i in range(0,len(x0)):
x1 = x0[i].split(',')
if x1[0] == '$GPGGA':
fname.append(str(filename))
position.append(x0[i])

resdata = np.vstack([fname, position])
df = pd.DataFrame(data=resdata).T
df.to_csv('result.csv', header=False, index=False)
$GPGGAデータのみを抽出した出力CSVファイルは以下の通りです。
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141400.00,3510.07681,N,13614.27683,E,2,12,0.78,117.9,M,34.8,M,,0000*54"
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141401.00,3510.07318,N,13614.27954,E,2,12,0.78,118.2,M,34.8,M,,0000*51"
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141401.00,3510.07318,N,13614.27954,E,2,12,0.78,118.2,M,34.8,M,,0000*51"
/home/drbobt/デスクトップ/NORMAL1/181228231402.NMEA,"$GPGGA,141402.00,3510.06950,N,13614.28226,E,2,12,0.78,118.5,M,34.8,M,,0000*53"
・・・
長くなりましたので、ここまでにします。次回はこの最終のCSV出力ファイルを使って解析を進めて行きます。

帰省時のドラレコデータを解析する(1)

 前回のブログでお話した通り、年末年始(12/29〜1/2)に九州の実家に車で帰省していました。先月、その車にドライブレコーダ(KENWOOD DRV-410:GPS付き)を取り付けており、帰省(高速道路走行)時の位置データを始め、様々なデータを採取できましたので、早速処理してみました。膨大なデータ処理はPythonとRを使って行いましたが、初めてのGPSデータの取り扱いに引っかかった所もありますのでその備忘録です。

 GPSデータファイルは「NMEA(National Marine Electronics Association:米国海洋電子機器協会)フォーマット」が用いられているようです。移動速度データがノット(knot)単位で表されていたのですが、船(海洋)ならではですね。購入したKENWOODのドラレコにもSDカードにNMEAファイルが保存されていました。

 SDカードに保存されたデータをKENWOOD Drive Reviewerという無償ソフトウェアで処理した結果は以下の通りです。マップ上にドライブルートが簡単に表示できました。DriveRecorderSoftData190103.png次に複数のNMEAファイルから、位置情報(緯度、経度)を抽出し、Rで日本地図にプロットした結果は以下の通りです。当然のことながら、KENWOOD Drive Reviewerと同じドライブルート結果です。GPS_Data190103.pngちなみに、高速道路のIC、SA等の位置情報をRでプロットした結果も同じで、高速道路をドライブしたことが分かります。HighWayData190103.png
 結果を簡単にお話しましたが、具体的にどのような解析を実施したかについては、次回お話します。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: