Help us understand the problem. What is going on with this article?

Raspberry Pi3のPythonでGPSを扱う

More than 1 year has passed since last update.

Raspberry Pi3のPythonでGPSモジュールを扱ったメモです。Raspberry Pi3のOSバージョンは

Raspbian GNU/Linux 8.0 (jessie)
Raspbian GNU/Linux 9.4 (stretch) (2018年10月5日修正)

です。
Raspberry Pi3 + GPS

GPSモジュール

GPSモジュールは秋月電子の「GPS受信機キット 1PPS出力付き 『みちびき』対応」を使いました。

このGPSモジュールは、受信したGPSデーターをシリアルで送出します。Raspberry Pi3でシリアル通信するにはUSBポートを使う方法とGPIOピンを使う方法があります。USBポートを使う場合はUSBシリアル変換モジュールが必要になるので、今回はGPIOピンを使いました。
Raspberry Pi3とGPSモジュールとは次のように接続します。

Raspberry Pi3 GPSモジュール
04(5v) 5V
06(Ground) GND
08(TXD0) RXD
10(RXD0) TXD

GPSの1PPSピンは使わないので何もつなぎません。

Raspberry Pi3のPythonでシリアル通信

Raspberry Pi3でシリアル通信をするには、raspi-configでシリアルを有効化し、シリアルをコンソールとして使わない設定をします。

raspi-configでシリアルの有効化

次のようにraspi-configを起動し、シリアルを有効にします。

pi$ sudo raspi-config

「5 Interfacing Options」を選択し、さらに「 P6 Serial 」を選択します。「Would you like a login shell to be accessible over serial?」に対してYesを選択、Finishし、リブートします。

pi$ sudo reboot

/dev/serial0というデバイスが作られます。

pi$ ls /dev/se*
/dev/serial0  /dev/serial1

/boot/cmdline.txtの修正

/boot/cmdline.txtを修正し、serial0をコンソールとして使わないように設定します。

pi$ cat /boot/cmdline.txt 
dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles

エディターで「console=serial0,115200」を削除し、リブートします。

pi$ sudo reboot

Pythonのシリアルモジュールをインストールする

Pythonのシリアルモジュールをインストールします。(2018年10月5日: python-serialからpyserialに変更)

pi$ pip install pyserial

pyenvを使っていない場合はpip3でインストールしてください。

これでRaspberry Pi3のPythonでシリアル通信ができるようになります。Raspberry Pi3にGPSモジュールを接続し、次のようにPythonを立ち上げて、確認すると、GPSデーターが読めているのが確認できます。

pi$ python3
>>> import serial
>>> s = serial.Serial('/dev/serial0', 9600, timeout=10)
>>> print(s.readline())
b'\x00\x00\xc0\x00\xfe\x00pK&&KLN\xcb14,302,,06,08,254,27,30,07,230,29*70\r\n'
>>> print(s.readline())
b'$GPGSV,4,4,14,23,05,150,19,14,04,054,*74\r\n'
>>>

1回目のreadline()は中途半端なデーターになっていますが、2回目以降のreadline()はGPSデーターが読めているようです。

PythonのGPSライブラリー

GPSモジュールから送られてくるデーターはNMEA-0183というフォーマットの文字列です。NMEA-0183のデーター形式については「NMEA 0183 sentences データ解析」というサイトが分かりやすいです。

GPSモジュールはGPS衛星からの信号を受信し、時刻、緯度、経度、海抜高度、測位に利用した衛星の数やID、それぞれの衛星の位置(方位角と仰角)などの情報をNMEA-0183フォーマットの文字列として送出します。

この文字列データーをプログラムで扱いやすいデーターに変換するPythonライブラリーがあります。Webを検索すると、micropyGPSpynmea2が見つかりました。

micropyGPSはPython3.xとMicroPythonで動作するGPSデーターの解析ライブラリーで、GPSデーターを入力すると、それを解析してGPSオブジェクトにデーターを追加、更新していきます。GPSオブジェクトのデーターとして時刻、緯度、経度、測位に利用した衛星の数やIDなどの情報が得られます。ドキュメントもしっかりしています。

pynmea2はGPSデーターを1行ずつパースするだけのようで、ドキュメントも貧弱そうに見えます。

ということで、ライブラリーとしてはmicropyGPSを使うことにしました。使うためにはGithubからmicropyGPS.pyをダウンロードして、Pythonを起動するディレクトリーに置くだけです。

Pythonプログラム

GPSデーターを読むPythonプログラムは次のようになりました。GPSモジュールからデーターを読み、
GPSオブジェクトにデーターを追加、更新する処理をスレッドにして動かし、そのデーターを3秒毎に出力しています。

MicroGPSオブジェクトを生成する時の引数として、タイムゾーンの時差(日本は+9時間)と、緯度経度の出力フォーマットを指定しています。出力フォーマットは次の形式が指定できるようです。

'ddm'   10進の度、分 (40° 26.767′ N)
'dms'   10進の度、分、秒 (40° 26′ 46″ N)
'dd'      10進の度 (40.446° N)
import serial
import micropyGPS
import threading
import time

gps = micropyGPS.MicropyGPS(9, 'dd') # MicroGPSオブジェクトを生成する。
                                     # 引数はタイムゾーンの時差と出力フォーマット

def rungps(): # GPSモジュールを読み、GPSオブジェクトを更新する
    s = serial.Serial('/dev/serial0', 9600, timeout=10)
    s.readline() # 最初の1行は中途半端なデーターが読めることがあるので、捨てる
    while True:
        sentence = s.readline().decode('utf-8') # GPSデーターを読み、文字列に変換する
        if sentence[0] != '$': # 先頭が'$'でなければ捨てる
            continue
        for x in sentence: # 読んだ文字列を解析してGPSオブジェクトにデーターを追加、更新する
            gps.update(x)

gpsthread = threading.Thread(target=rungps, args=()) # 上の関数を実行するスレッドを生成
gpsthread.daemon = True
gpsthread.start() # スレッドを起動

while True:
    if gps.clean_sentences > 20: # ちゃんとしたデーターがある程度たまったら出力する
        h = gps.timestamp[0] if gps.timestamp[0] < 24 else gps.timestamp[0] - 24
        print('%2d:%02d:%04.1f' % (h, gps.timestamp[1], gps.timestamp[2]))
        print('緯度経度: %2.8f, %2.8f' % (gps.latitude[0], gps.longitude[0]))
        print('海抜: %f' % gps.altitude)
        print(gps.satellites_used)
        print('衛星番号: (仰角, 方位角, SN比)')
        for k, v in gps.satellite_data.items():
            print('%d: %s' % (k, v))
        print('')
    time.sleep(3.0)

プログラムを動かすと、次のような結果が出力されました。緯度経度の小数点以下は伏せてあります。

14:02:19.0
緯度経度: 35.********, 139.********
海抜: 51.900000
測位利用衛星: [17, 28, 6, 3, 193, 1, 22, 8]
衛星番号: (仰角, 方位角, SN比)
193: (59, 173, 40)
3: (71, 124, 24)
6: (13, 259, 27)
1: (49, 42, 16)
8: (15, 115, 21)
42: (48, 170, 34)
11: (40, 69, None)
14: (4, 47, None)
17: (42, 317, 15)
19: (20, 307, None)
22: (56, 66, 21)
23: (10, 145, None)
28: (63, 248, 38)
30: (3, 224, 16)

「衛星番号」の次からの行が捕捉した衛星の衛星番号と仰角、方位角、SN比です。衛星番号193は「準天頂衛星初号機みちびき」です。

AmbientData
IoTデーター可視化サービス「Ambient」を開発・運用しています。著書:「IoT開発スタートブック( https://ambidata.io/books/iotstartbook/ )」「みんなのM5Stack入門(https://amzn.to/2nLmvi6)」
https://ambidata.io
iotlt
IoT縛りの勉強会です。 毎月イベントを実施しているので是非遊びに来てください! 登壇者を中心にQiitaでも情報発信していきます。 https://iotlt.connpass.com
https://iotlt.connpass.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした