GPS位置情報を取得し、jsonログに出力するところまでをやってみたいと思います。
GPSレシーバーはAmazonで買えるものを選定しました。試しやすいと思います。
環境はWinsows10 64bitです。
前置き
・GPSのデータ方式 NMEA
・Amazonで買えるGPSレシーバー
やること
・UARTをUSBで受信
・pynmeaモジュールで位置情報を取得
・jsonログにする
それでは順に見ていきましょう!
前置き
そもそもGPSモジュールからどんな風にデータが送られてくるのでしょうか?
GPSレシーバーはどんなものがあるのでしょうか?
このあたりを説明していきたいと思います。
GPSのデータ方式 NMEA
GPSモジュールからNMEA(National Marine Electronics Association)という形式でデータが送られてきます。
GPSレシーバーなどの位置情報デバイスと他の機器との間でデータを共有するためのデータ方式です。
NMEAフォーマットは、ASCIIテキスト形式で、特定のセンテンス(文)によって情報を示します。
次は実際の受信例です。
例えば、GGAセンテンスは位置情報(緯度、経度、高度など)を提供し、RMCセンテンスは速度や進行方向を提供します。GPSレシーバーはこれらのセンテンスを定期的に出力し、他の機器やアプリケーションはこれを解釈して位置情報を利用します。
NMEAフォーマットは標準化されているため、さまざまなメーカーやデバイス間での互換性が高く、広く採用されています。これにより、GPSデータを利用する多様なシステムやアプリケーションが実現されています。
多くの製品はUSBやUARTなどの汎用的なインターフェースで利用できるようになっています。
例えばPCならUSB接続してデータを受信します。
ラズパイであれば、GPIOのUARTに接続するか、USBを利用します。
Amazonで買えるGPSレシーバー
では、GPSレシーバーはどんなものが手に入れられるのでしょうか。
今回は手に入りやすいものとして、Amazon、楽天で購入できるものを挙げました。
使用しているGPSモジュールは、u-blox製のNEO-6Mです。
GPSレシーバーからはUART出力されます。
今回はWindows PCのUSBで受信したいので、UARTをUSBに変換するコンバーターも合わせて使用します。
先述のとおり環境によってはコンバーターは不要です。
ちなみにOSOYOOはこういう教育キットを手掛けているメーカーです。
DSD TECHは安価なIoT関連デバイスを手掛けているメーカーで、安心できるところです。
聞いたこともないメーカーの物なら、もう少しお安いものもあるのですが。
私は以前、ラベルだけ貼り換えた模造品を掴まされたことがあります。
実際にこれらのデバイスで、GPSデータを受信して測位ログ作成までやってみたいと思います。
やること
UARTをUSBで受信
まずコンバーターとUSBレシーバーを接続し、PCのUSBコネクタに繋ぎます。
接続先にご注意。
・GPSレシーバーのRX → コンバーターのTX
・GPSレシーバーのTX → コンバーターのRX
私の環境では、コンバーターの変換チップ(CP2102N, SiliconLab製)のドライバが入っていませんでした。
下記のサイトからダウンロードし、インストールします。
デバイスマネージャーを確認して、次のように認識できればいればOKです。
COM番号は次のステップで使用します。
シリアルデータを受信
ここからがPythonプログラムになります。
pynmeaモジュールと、pyserialモジュールを使用します。
まずはインストールします。
pip install pynmea2
pip install pyserial
USBポートからシリアルデータを受信します。
import serial
# シリアルポートの設定
port = 'COM5' # GPSデバイスが接続されているシリアルポートを指定
baudrate = 9600 # GPSデバイスのボーレートに合わせて設定
# シリアルポートの初期化
ser = serial.Serial(port, baudrate)
# GPSデータの受信
while True:
data = ser.readline().decode('utf-8') # NMEAデータの読み込み
# シリアルポートのクローズ
ser.close()
UARTのボーレートは9600bpsです。
port変数には、先ほどデバイスマネージャーで確認したCOM番号を記載します。
デバッグに、「print(data)」を記述しておくと、冒頭にあったNMEAフォーマットのレスポンスを受信できます。
最後にシリアルポートをクローズします。
上記は説明用に切り出したものです。
このままだとクローズしにいけないです。
デバッグに利用される場合はtry-finallyなどでうまくクローズできるようにご注意ください。
pynmeaモジュールで位置情報を取得
次は意味を解釈して位置情報を取得したいと思います。
pynmea2モジュールを利用して、GPRMCなどのセンテンスから情報を抽出します。
import pynmea2
# シリアルデータ受信ごとに処理
if data.startswith('$GPRMC'): # GNRMCセンテンスの処理
msg = pynmea2.parse(data)
status = msg.status # ステータス (A: 有効、V: 無効)
gnrmc_cnt += 1
elif data.startswith('$GPGGA'): # 例: GPGGAセンテンスの処理
msg = pynmea2.parse(data)
lat_dir = msg.lat_dir
latitude = msg.latitude # 緯度
longitude = msg.longitude # 経度
lon_dir = msg.lon_dir
altitude = msg.altitude # 高度
hdop = msg.horizontal_dil # 水平精度 (HDOP)
time = msg.timestamp # 時刻
time = datetime.combine(datetime.today().date(), time)
先ほどはシリアルデータの受信までをやっていました。
ここでやっているのは、受信したシリアルデータが「$GPRMC」であればステータス情報を、
「$GPGGA」であれば緯度経度などの測位情報を抽出しています。
pynmea2モジュールがパースしてくれるので、かなり楽に取得できています。
jsonログにする
測位データを何かの後処理に掛けると想定して、「data.json」ファイルにログ出力したいと思います。
import json
from datetime import datetime
# センサーデータを辞書として作成
sensor_datum = {
"timestamp": datetime.now().isoformat(),
"latitude": latitude,
"lat_dir":lat_dir,
"longitude": longitude,
"lon_dir":lon_dir,
"altitude": altitude,
"hdop": hdop,
"Status": status
}
# センサーデータをリストに追加
sensor_data.append(sensor_datum)
# data.jsonにセンサーデータを書き込み
with open('data.json', 'w') as file:
json.dump(sensor_data, file, indent=2)
先ほど「$GPRMC」センテンスと「$GPGGA」からパースしていたデータに、
タイムスタンプを加えて辞書データにしています。
作成した辞書データを「data.json」ファイルに書き込みます。
これでjsonログ出力までできるようになりました。
サンプルコード
それでは、ここまでの物を合体してサンプルコードにしたいと思います。
import serial
import pynmea2
import json
from datetime import datetime
# シリアルポートの設定
port = 'COM5' # GPSデバイスが接続されているシリアルポートを指定
baudrate = 9600 # GPSデバイスのボーレートに合わせて設定
# シリアルポートの初期化
ser = serial.Serial(port, baudrate)
# 測定結果を格納するリスト
sensor_data = []
# カウンタ
gnrmc_cnt = 0
cnt_num = 10
# GPSデータの受信と解析
while True:
try:
data = ser.readline().decode('utf-8') # NMEAデータの読み込み
# シリアルデータ受信ごとに処理
if data.startswith('$GPRMC'): # GNRMCセンテンスの処理
msg = pynmea2.parse(data)
status = msg.status # ステータス (A: 有効、V: 無効)
gnrmc_cnt += 1
elif data.startswith('$GPGGA'): # 例: GPGGAセンテンスの処理
msg = pynmea2.parse(data)
lat_dir = msg.lat_dir
latitude = msg.latitude # 緯度
longitude = msg.longitude # 経度
lon_dir = msg.lon_dir
altitude = msg.altitude # 高度
hdop = msg.horizontal_dil # 水平精度 (HDOP)
time = msg.timestamp # 時刻
time = datetime.combine(datetime.today().date(), time)
# カウンタ値が所定値になったら
if gnrmc_cnt >= cnt_num:
gnrmc_cnt =0
# センサーデータを辞書として作成
sensor_datum = {
"timestamp": datetime.now().isoformat(),
"latitude": latitude,
"lat_dir":lat_dir,
"longitude": longitude,
"lon_dir":lon_dir,
"altitude": altitude,
"hdop": hdop,
"Status": status
}
# センサーデータをリストに追加
sensor_data.append(sensor_datum)
# 他の属性も利用可能
# ...
print(f"Time:{time}")
print(f"Latitude:{latitude}")
print(f"lat_dir:{lat_dir}")
print(f"Longitude:{longitude}")
print(f"lon_dir:{lon_dir}")
print(f"Altitude:{altitude}")
print(f"hdop:{hdop}")
print(f"Status:{status}")
# data.jsonにセンサーデータを書き込み
with open('data.json', 'w') as file:
json.dump(sensor_data, file, indent=2)
except KeyboardInterrupt:
break
# シリアルポートのクローズ
ser.close()
出力結果は次のようになります。
タイムスタンプと測位結果を出力します。
[
{
"timestamp": "2023-07-19T20:52:54.031310",
"latitude": 35.65859297916666,
"lat_dir": "N",
"longitude": 139.74541359375,
"lon_dir": "E",
"altitude": 84.7,
"hdop": "1.22",
"Status": "A"
},
{
"timestamp": "2023-07-19T20:53:04.027535",
"latitude": 35.658584812499996,
"lat_dir": "N",
"longitude": 139.74543476041666,
"lon_dir": "E",
"altitude": 81.1,
"hdop": "1.50",
"Status": "A"
}
]
これで、Amazonで買えるGPSレシーバーで位置情報を取得してjsonログ出力することができました。
今回は以上となります。
この記事が少しでもお役に立てれば幸いです。
それでは、読んで頂きありがとうございました