【termux】スマホでGPSスピードメーターをつくる
前回Androidスマホとtermux、Pythonを使って、GPS現在地座標取得とGoogleマップでの確認をしました。
https://chayarokurokuro.hatenablog.com/entry/2019/10/21/224330
環境
目次
概要
自動車やバイクの機械式アナログスピードメーターはタイヤなどの回転をワイヤーでメーターに伝えてスピード表示しています。
デジタルスピードメーターの場合はエンジン回転数や車速センサーのパルス信号を利用する方式です。
Googleプレイ等でスマホアプリのスピードメーターを探すとたくさん出てきますが、一体どのような仕組みになっているのでしょうか。
Androidアプリ開発においてAndroidJavaの場合はgetSpeed()関数
というものが用意されておりその値を取得するだけで済むらしいのですが、Pythonでつかえません。はてスピードを得るには…
一般的にスピードを求めるには、移動距離と掛かった時間が分かれば計算できます。距離を掛かった時間で割るだけ。
スピード = 距離 / 時間
一時間で100キロ移動したら平均時速100km/h。または、1秒間に何メートル移動したから時速に換算すると毎時何キロに相当、といった具合です。
距離と時間さえ得られればスピードが求まります。
距離はスマホのGPSを使って二ヶ所の座標を取得し計算で出します。
2点の座標が分かればピタゴラスの三平方の定理で出せますが、地球は丸いので、地球の断面図で見ると弧の距離ということになります。
大円距離
、Haversineの公式
というものに代入して出します。これはのちほど。
掛かった時間はGPSで二ヶ所の座標を取るときの時間間隔です。
現在地GPS座標の取得時刻の差で掛かった時間を出します。
スピードの精度は、GPS座標の精度とそこから算出した距離、座標取得の時間間隔の精度に依存しているということになります。
もし現在地のGPS座標が10kmズレるとすると瞬間的に10キロ移動したことになるので、ロケットのようなスピードを叩き出すことになります。役に立ちそうにありません。
実装
ここからは実装。
現在地のGPS座標取得は前回やりました。
それを時刻を取得しながら行い、距離を求める方法を探ります。
Haversine式から2点間の距離を求める
数式を使います。
2つの場所の緯度と経度から距離を算出します。
Haversineの公式
なるものに2ヵ所の座標を代入すると距離が求まるようです。その公式は次。
詳しくはこちらWikipedia 大円距離
数式のφ(ファイ)とλ(ラムダ)に、2点のGPS座標の緯度と経度をそれぞれ入れていくと距離が出せます。
上の式のsin2xの部分を変形して、sin^2x = 1/2 - 1/2 cos2x
に置き換え、Pythonのコードで数式を表すと次のようなものになる。
2 * r * asin(sqrt(0.5 - cos((lat2 - lat1) * p)/2 + cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2))
lat1と2は緯度の新しいのと1個古いの。
lon1と2は経度の新しいのと1個古いのを表す。
コード
スピードを算出し表示する為のコードは次のような考え方で動かす。
・while文で絶えず時刻とGPS座標を取得しながらその都度座標リストの先頭にデータを新規追加していきます。
・一番新しいデータと、一つ古いデータから経過時間と距離を算出します。
・経過時間と距離から最新の平均スピードを算出し、ターミナルに随時表示し続けます。
プログラムを終了する時はCtrl+c
を入力して下さい。
ということでコード。(実は写経)
import subprocess import ast import time from math import asin, cos, pi, sqrt # 地球の半径 単位km r = 6371 # 全ての位置のGPSデータをリストで保存する locationList = [] # 座標取得関数 def getLocation(): location=subprocess.run("termux-location", stdout=subprocess.PIPE) current_time = time.time() #時刻取得 location=location.stdout.decode("utf-8") location=ast.literal_eval(location) #jsonで取得 locationList.insert(0, { "latitude": location["latitude"], "longitude": location["longitude"], "time" : current_time}) #新規データを先頭に挿入 # 距離算出関数 def calDistance(a, b): p = pi/180 #ラジアンに変換 lat1 = locationList[b]["latitude"] #前のデータの緯度 lon1 = locationList[b]["longitude"] #前のデータの経度 lat2 = locationList[a]["latitude"] #新規データの緯度 lon2 = locationList[a]["longitude"] #新規データの経度 return 2 * r * asin(sqrt(0.5 - cos((lat2 - lat1) * p)/2 + cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2)) # スピード算出関数 def calSpeed(a, b): time = locationList[a]["time"] - locationList[b]["time"] distance = calDistance(a, b) return (distance*3600//time) #時速に換算 ##### 実行 ##### print("スピード計測を開始します。") try: while True: # if len(locationList) < 1: getLocation() else: getLocation() current_speed = calSpeed(0, 1) #最新の瞬間スピード print("只今のスピード : " + str(current_speed) + " km/h") except KeyboardInterrupt: print("\nプログラムを終了します。")
参考
ほぼこちらの写経 https://medium.com/@ishivam93/tracking-vehicle-speed-using-android-phone-c9a9bd5d982
astモジュールについて https://note.nkmk.me/python-ast-literal-eval/
Wikipedia 大円距離 https://ja.m.wikipedia.org/wiki/%E5%A4%A7%E5%86%86%E8%B7%9D%E9%9B%A2
スピードメーターのテスト
スマホの現在地とネット接続をオンにしてプログラムを走らせながらバイクに乗ってきた。
その時の画面のスクリーンショット。
時速189キロを叩き出しています!(笑)
スピード違反ではなくメーターがアレだから!
以上です。
追記
Googleマップにはスピードメーターがついてるんですね。知らんかった。運転モードで設定からオン/オフの切替ができる。
GPSと加速度センサーを利用したものらしい?自動車のスピードメーターと比較実験してるサイトでは誤差は1km/hとかかなり少ない模様。
加速度を積分すると速度が出せますが、加速度センサーのみから速度を得ようとすると誤差が大きくて使い物にならないとのことで。