[Python]Amazonで買えるGPSレシーバーで位置情報を取得してjsonログ出力する

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テキスト形式で、特定のセンテンス(文)によって情報を示します。
次は実際の受信例です。

$GPRMC,,V,,,,,,,,,,N53
$GPVTG,,,,,,,,,N30
$GPGGA,,,,,,0,00,99.99,,,,,,48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.9930
$GPGSV,1,1,01,09,,,2675
$GPGLL,,,,,,V,N64

例えば、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製)のドライバが入っていませんでした。
下記のサイトからダウンロードし、インストールします。

 USB – UART ブリッジ VCP ドライバ

デバイスマネージャーを確認して、次のように認識できればいれば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ログ出力することができました。

今回は以上となります。
この記事が少しでもお役に立てれば幸いです。
それでは、読んで頂きありがとうございました

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次