ラズパイに接続したGPSからデータを取得するコードを作成する

## まずROSとは？
**ROS（Robot Operating System）** は、オープンソースのロボットソフトウェア開発フレームワークです。ROSは、ロボットの開発、制御、シミュレーション、センシングなど、さまざまなロボット関連のタスクをサポートするために設計されたものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958142/c52875c9-1772-7981-5ebe-1e57fa409eb0.png)
&emsp;

以下に、ROSの主な特徴をいくつか挙げていきます。
&emsp;

1. **分散型フレームワーク**: ROSはノードと呼ばれるプロセスがメッセージを介して通信する分散型のフレームワークです。各ノードは異なるタスクを担当し、必要に応じて情報を交換します。

2. **メッセージ通信**: ROSでは、ノード間でメッセージを交換してデータをやり取りします。これにより、異なるプログラミング言語で書かれたノード同士でも簡単に連携できます。

3. **パッケージ管理**: ROSはパッケージ単位でソフトウェアを組織し、再利用性を高めています。各パッケージにはライブラリ、ツール、ドライバ、デモなどが含まれ、開発者はこれを利用して効率的にロボットアプリケーションを構築することができます。

4. **メタオペレーティングシステム**: ROSは、メタオペレーティングシステムとも呼ばれ、ハードウェアや通信プロトコルに依存せず、様々なロボットプラットフォームで利用できます。

5. **シミュレーション環境**: ROSはGazeboやRVizなどのシミュレーションツールと統合されており、ロボットの挙動や性能をシミュレートすることができます。

6. **豊富なライブラリとドライバ**: ROSは、ロボット開発に必要な様々なライブラリやドライバが提供されています。これにより、センシング、制御、運動計画、画像処理などの機能を容易に利用できます。
&emsp;

ROSは主にLinux環境で動作し、PythonやC++を用いてプログラミングされることが一般的です。研究機関や産業界で幅広く利用され、様々なロボットプロジェクトで採用されています。私も卒業研究で利用していました。
&emsp;

## GPSデータを取得する
```Python:GPS.py
#!/usr/bin/env python

import rospy
from robot.msg import GPS
from robot.msg import GP

import serial
import time
import datetime
import csv

# ROSノードを初期化
rospy.init_node('GPS_DATE_OUT')

# ROSパブリッシャーを作成
pub_gps = rospy.Publisher('gps_DATE', GPS, queue_size=10)
pub_gp = rospy.Publisher('GP_DATE', GP, queue_size=10)

# シリアルポートおよびボーレートの設定
dev = '/dev/serial0'
rate = 9600
s = serial.Serial(dev, rate)
print(s.name)

# CSVファイルの名前をタイムスタンプを使って生成
timenow = datetime.datetime.today()
csv_name = str(timenow) + '_gps.csv'

# CSVファイルを書き込みモードで開く
f = open(csv_name, 'a')
writer = csv.writer(f, lineterminator='\n')
# ヘッダー行をCSVファイルに書き込む
writer.writerow(['raspi_time', 'gps_year', 'gps_month', 'gps_day', 'gps_time', 'global_longitude', 'global_latitude', '$GPGGA', 'GPGLL', 'GPGSA', 'GPGSV', 'GPRMC', 'GPVTG', 'GPZDA'])

# GPおよびGPSメッセージのインスタンスを作成
msg_GP = GP()
msg_gps = GPS()

# GPSデータを取得する関数
def gps_get():
    while not rospy.is_shutdown():
        # シリアルポートから1行読み取る
        c = s.readline()
        raspi_time = datetime.datetime.today()

        # GPSデータをデコード
        gps = c.decode('utf-8')
        gps = gps.split(',')

        # $GPGGAセンテンスの処理
        if gps[0] == '$GPGGA':
            msg_gps.gps_get_time = str(raspi_time)

            # GPGGAデータをメッセージにセット
            GPGGA = gps
            msg_GP.GPGGA = str(GPGGA)
            msg_gps.longitude = GPGGA[4]
            msg_gps.latitude = GPGGA[2]

            print('GPGGA')

        # 各センテンスに対する条件分岐
        elif gps[0] == '$GPGLL':
            GPGLL = gps
            msg_GP.GPGLL = str(GPGLL)

            print('GPGLL')

        elif gps[0] == '$GPGSA':
            GPGSA = gps
            msg_GP.GPGSA = str(GPGSA)

            print('GPGSA')

        elif gps[0] == '$GPGSV':
            GPGSV = gps
            msg_GP.GPGSV1 = str(GPGSV)

            print('GPGSV')

        elif gps[0] == '$GPRMC':
            GPRMC = gps
            msg_GP.GPRMC = str(GPRMC)

            print('GPRMC')

        elif gps[0] == '$GPVTG':
            GPVTG = gps
            msg_GP.GPVTG = str(GPVTG)

            print('GPVTG')

        elif gps[0] == '$GPZDA':
            GPZDA = gps
            msg_GP.GPZDA = str(GPZDA)

            msg_gps.time_hour = GPZDA[3]
            msg_gps.time_minute = GPZDA[2]
            msg_gps.time_second = GPZDA[1]

            print('GPZDA')

            # データをCSVファイルに書き込み
            writer.writerow([str(raspi_time), GPZDA[4], GPZDA[3], GPZDA[2], GPZDA[1], GPGGA[4], GPGGA[2], str(GPGGA), str(GPGLL), str(GPGSA), str(GPGSV), str(GPRMC), str(GPVTG), str(GPZDA)])

            # メッセージを公開
            pub_gp.publish(msg_GP)
            pub_gps.publish(msg_gps)

if __name__ == '__main__':
    try:
        # GPSデータを取得する関数を呼び出す
        gps_get()

    except rospy.ROSInterruptException:
        # ROSの例外が発生した場合、CSVファイルを閉じてプログラムを終了
        f.close()
        print('ROSが終了しました')

    except KeyboardInterrupt:
        # キーボード割り込みが発生した場合も、CSVファイルを閉じてプログラムを終了
        f.close()
        print('キーボード割り込みが発生しました')
```

## プログラムの結果
プログラムの結果を張り付けていたのですが、自宅の位置情報だらけになっていたので説明だけにしています。
&emsp;

1. `raspi_time`: Raspberry Pi上での実行時刻
2. `gps_year`: GPSデータの年
3. `gps_month`: GPSデータの月
4. `gps_day`: GPSデータの日
5. `gps_time`: GPSデータの時刻
6. `global_longitude`: GPGGAセンテンスから取得した経度
7. `global_latitude`: GPGGAセンテンスから取得した緯度
8. `$GPGGA`: GPGGAセンテンスの生データ
9. `$GPGLL`: GPGLLセンテンスの生データ
10. `$GPGSA`: GPGSAセンテンスの生データ
11. `$GPGSV`: GPGSVセンテンスの生データ
12. `$GPRMC`: GPRMCセンテンスの生データ
13. `$GPVTG`: GPVTGセンテンスの生データ
14. `$GPZDA`: GPZDAセンテンスの生データ
&emsp;

これらのデータは、各GPSセンテンス（GPGGA、GPGLL、GPGSAなど）から抽出された情報や、Raspberry Pi上での時間に関する情報を含んでいます。データはCSV形式で書き込まれており、各セルはカンマで区切られています。

CSVファイルの一部は以下のようになります（実際のデータは実行時のものになります）：

```
raspi_time, gps_year, gps_month, gps_day, gps_time, global_longitude, global_latitude, $GPGGA, $GPGLL, $GPGSA, $GPGSV, $GPRMC, $GPVTG, $GPZDA
2023-11-15 12:34:56, 2023, 11, 15, 12:34:56, 35.1234, -120.5678, GPGGA_data, GPGLL_data, GPGSA_data, GPGSV_data, GPRMC_data, GPVTG_data, GPZDA_data
...
```

各行が1回のGPSデータの記録を示し、各列がそのデータの要素を表しています。データは新しい行に追加され、ファイルが更新されるたびに新しいデータが追加されます。