無人機飛行軌跡追蹤仰賴 GPS 定位技術,但由於取樣頻率限制,原始 GPS 資料呈現的軌跡並非平滑曲線。本文將探討如何利用 Raspberry Pi 與 GPS 模組,結合 Python 多執行緒程式設計,實作無人機飛行軌跡的自動化追蹤與記錄。文章涵蓋硬體設定、軟體開發、自動啟動組態、資料轉換及問題排除等導向,提供一套完整的技術解決方案。此外,文章也詳細說明如何將 GPS 資訊轉換為 KML 格式,方便在 Google Earth 上視覺化呈現飛行路徑,並提供程式碼解析與實務操作建議,協助讀者快速上手無人機軌跡追蹤應用。

無人機飛行軌跡追蹤與自動化技術

在無人機飛行過程中,GPS 追蹤是確保飛行路徑準確的重要技術。由於我們僅每 30 秒才取樣一次 GPS 位置,因此無法獲得平滑的飛行軌跡。相反地,飛行路徑會連線每次取樣時的位置,形成直線段落。如圖示所示,這些線段可能看起來不太自然,但這是因為我們只在每個間隔時取得位置資料。

雪地測試與環境選擇

在阿拉斯加進行測試時,所有地面都被雪覆寫,因此選擇停車場作為測試地點並不影響結果。對於初學者來說,建議選擇草地進行測試,因為草地上的墜機事故對無人機造成的損傷較小。無論如何,透過這些測試,我們能夠確保 GPS 資料的準確性和穩定性。

多執行緒與物件導向程式設計

在這個專案中,我們將使用多執行緒技術來同時處理多個任務。執行緒可以讓程式同時執行多個任務,而不會佔用過多的記憶體和處理器資源。透過執行緒,我們可以在背景中持續採集 GPS 資料,而不會影響主程式的執行。

執行緒的基本概念

執行緒允許電腦同時執行多個任務。雖然處理器仍然只能一次執行一個任務,但執行緒可以快速在任務之間切換,讓人覺得它們是同時執行的。例如,當你在電腦上使用文書處理軟體和網頁瀏覽器時,文書處理軟體在一個執行緒中執行,而網頁瀏覽器則在另一個執行緒中更新頁面內容。

執行緒的應用

在這個專案中,我們將使用執行緒來定期採集 GPS 接收器的位置資料。這樣可以避免主緩衝區被大量資料填滿,同時還能將資料記錄到日誌檔案中以供日後使用。為了達到最佳效果,我們將建立一個名為 Poller 的物件來定期從 GPS 接收器請求位置資料。

import threading
import time
import gps
from picamera import PiCamera

class Poller(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.camera = PiCamera()
        self.gps_session = gps.gps("localhost", "2947")
        self.gps_session.stream(gps.WATCH_ENABLE | gps.WATCH_NEWSTYLE)

    def run(self):
        while True:
            report = self.gps_session.next()
            if report['class'] == 'TPV':
                if hasattr(report, 'time'):
                    print("Time: %s" % report.time)
                if hasattr(report, 'lat'):
                    print("Latitude: %s" % report.lat)
                if hasattr(report, 'lon'):
                    print("Longitude: %s" % report.lon)
                self.camera.capture('/home/pi/Documents/plane/image.jpg')
            time.sleep(3)

內容解密:

  • 模組匯入:首先匯入必要的模組:threading 用於處理多執行緒、time 用於延遲、gps 用於與 GPS 接收器通訊、以及 PiCamera 用於拍照。
  • Poller 類別:這是一個繼承自 threading.Thread 的類別。
    • __init__:初始化方法,設定相機和 GPS 連線。
    • run:當執行緒啟動時會執行的方法。它不斷地從 GPS 接收器取得位置資料並拍照。
  • GPS 資料處理:當取得到有效的 GPS 資料時(即 report['class'] == 'TPV'),會檢查並列印時間、緯度和經度。
  • 拍照功能:每當取得到有效的 GPS 資料後,都會使用相機拍攝一張圖片並儲存到指定路徑。
  • 延遲:每次迴圈結束後會延遲三秒鐘再繼續下一次迴圈。

自動啟動設定

由於我們可能無法在無人機上連線顯示器或鍵盤來手動啟動程式,因此需要設定自動啟動功能。最簡單的方法是修改 /etc/rc.local 檔案,將我們的 GPS 日誌指令碼新增到這個檔案中。假設我們的指令碼名稱為 getGPS.py 並儲存在 /home/pi/Documents/plane 目錄下。

sudo nano /etc/rc.local

然後在檔案中新增以下一行:

python /home/pi/Documents/plane/getGPS.py &

確保這行程式碼新增在檔案最後一行之前。

透過這些設定,我們可以確保在每次啟動 Raspberry Pi 時自動啟動 GPS 日誌指令碼,從而實作無人機飛行軌跡的自動追蹤與記錄。

每段程式碼詳細解說

  1. 模組匯入:首先匯入必要的模組:threading 用於處理多執行緒、time 用於延遲、gps 用於與 GPS 接收器通訊、以及 PiCamera 用於拍照。
  2. Poller 類別:這是一個繼承自 threading.Thread 的類別。
    • __init__:初始化方法,設定相機和 GPS 連線。
    • run:當執行緒啟動時會執行的方法。它不斷地從 GPS 接收器取得位置資料並拍照。
  3. GPS 資料處理:當取得到有效的 GPS 資料時(即 report['class'] == 'TPV'),會檢查並列印時間、緯度和經度。
  4. 拍照功能:每當取得到有效的 GPS 資料後,都會使用相機拍攝一張圖片並儲存到指定路徑。
  5. 延遲:每次迴圈結束後會延遲三秒鐘再繼續下一次迴圈。

透過這些技術和設定,我們可以實作無人機飛行軌跡的自動追蹤與記錄,並確保程式能夠穩定執行。

流程圖:
```mermaid
graph TD;
    A[開始] --> B[初始化 Poller 類別];
    B --> C[啟動 Poller 執行緒];
    C --> D[取得 GPS 資料];
    D --> E{有效 GPS 資料?};
    E -- 是 --> F[拍照];
    F --> G[記錄日誌];
    G --> H[延遲三秒鐘];
    H --> D;
    E -- 否 --> D;



此圖示展示了 Poller 執行緒的工作流程。首先初始化 Poller 類別並啟動執行緒。接著不斷從 GPS 接收器取得位置資料。如果獲得有效的 GPS 資料(即 `report['class'] == 'TPV'`),則進行拍照並記錄日誌檔案;否則繼續取得下一次資料。每次迴圈結束後都會延遲三秒鐘再繼續下一次迴圈。

#### 建議和注意事項

1. **停車場測試**:初學者可以選擇草地進行無人機測試以減少墜機損失。
2. **自動啟動**:透過修改 `/etc/rc.local` 檔案來設定自動啟動功能。
3. **多執行緒最佳化**:確保多執行緒技術能夠有效運作以提升系統效能。
4. **GPS 資料精確度**:考慮增加 GPS 資料採集頻率以獲得更平滑的飛行軌跡。

透過這些技術和設定,玄貓相信大家可以順利實作無人機飛行軌跡追蹤與自動化技術應用。

## 利用GPS追蹤無線遙控飛機的飛行路徑

在這個專案中,我們將利用Raspberry Pi(以下簡稱Pi)和GPS模組來追蹤無線遙控飛機的飛行路徑。這不僅展示了Pi在物聯網IoT)應用中的潛力,也讓我們能夠記錄飛行過程中的每一個細節。以下是玄貓為你整理的詳細步驟和技術內容。

### 什麼是 rc.local 檔案?

`rc.local` 檔案是 Linux 系統中一個重要的啟動指令碼,位於 `/etc/` 目錄下。在系統啟動時,核心會依序執行這些指令碼檔案中的指令。`rc.local` 是最後一個執行的指令碼,通常用來放置那些不適合放在其他啟動指令碼中的指令。

由於 `rc.local` 不會以特定使用者身份執行,因此在指令碼中必須使用完整路徑來指定要執行的程式,而不能使用相對路徑(例如 `~/Documents/myscript.py`)。

### 設定 GPS 服務自動啟動

在開始記錄 GPS 資料之前,我們需要確保 GPS 模組在系統啟動時能夠正常運作。為此,我們需要將 GPS 服務的啟動指令加入 `/etc/rc.local` 檔案中。以下是具體步驟:

1. 開啟 `rc.local` 檔案:
    ```bash
    sudo nano /etc/rc.local
    ```

2. 在檔案的末尾加入以下指令:
    ```bash
    sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock
    sleep 45
    python /home/pi/Documents/plane/gpstest.py
    ```

這段指令的作用是先啟動 GPS 服務,然後等待 45 秒以確保 GPS 模組能夠取得到足夠的衛星訊號,最後執行記錄 GPS 資料的 Python 指令碼。

### 電源供應與硬體設定

為了讓無線遙控飛機能夠順利飛行並記錄資料,我們需要提供穩定的電源供應。這裡推薦使用 RC 愛好者常用的鋰聚合物(Li-Po)電池,因為它們輕便且能提供高效能的電力供應。

#### 電壓調節器

我們可以利用一個 USB 車載充電器來做為電壓調節器。將中間端子連線到電池的正極(+),其中一個外部端子連線到地線(GND),然後用 USB 接頭連線到 Pi 的電源輸入端。

### 無線遙控飛機硬體組裝

將所有元件安裝到無線遙控飛機上時,要特別注意保持飛機的平衡並避免幹擾翼面上的氣流。以下是具體安裝步驟:

1. **GPS 模組**:安裝在飛機鼻端。
2. **Raspberry Pi**:固定在機翼上。
3. **攝影機**:固定在機翼側面並朝向地面拍攝。

### 飛行前準備與測試

1. **電源供應**:確認 Pi 和所有外部裝置都已正確連線並供電。
2. **GPS 初始化**:啟動系統後等待 45 秒以確保 GPS 模組已獲得衛星訊號。
3. **測試飛行**:進行短暫測試飛行以確認所有裝置正常執行。

### 資料記錄與轉換

飛行結束後,我們可以將記錄下來的 GPS 資料轉換為 KML 檔案並匯入 Google Earth 中檢視飛行路徑。具體步驟如下:

1. **轉換指令碼**:執行 `kml.py` 指令碼將 `locations.log` 檔案轉換為 `plane.kml` 檔案。
2. **匯入 Google Earth**:將生成的 KML 檔案匯入 Google Earth 中檢視飛行路徑。

#### 轉換指令碼解析

```python
import string

# 開啟檔案進行讀寫操作
gps = open('locations.log', 'r')
kml = open('plane.kml', 'w')

# 寫入 KML 檔案頭部資訊
kml.write('<?xml version="1.0" encoding="UTF-8" ?>\n')
kml.write('<kml xmlns="http://www.opengis.net/kml/2.2">\n')
kml.write('<Document>\n')
kml.write('<name>Plane Path</name>\n')
kml.write('<description>Path taken by plane</description>\n')

# 定義樣式
kml.write('<Style id="yellowLineGreenPoly">\n')
kml.write('<LineStyle><color>7f00ffff</color><width>4</width></LineStyle>\n')
kml.write('<PolyStyle><color>7f00ff00</color></PolyStyle>\n')
kml.write('</Style>\n')

# 建立 Placemark 元素
kml.write('<Placemark><name>Plane Path</name>\n')
kml.write('<styleUrl>#yellowLineGreenPoly</styleUrl>\n')
kml.write('<LineString>\n')
kml.write('<extrude>1</extrude><tesselate>1</tesselate>\n')
kml.write('<altitudeMode>relative</altitudeMode>\n')
kml.write('<coordinates>\n')

# 陳述式解析與寫入座標資訊
for line in gps:
    coordinate = string.split(line)
    longitude = coordinate[0]
    latitude = coordinate[1]
    altitude = coordinate[2]
    kml.write(longitude + "," + latitude + "," + altitude + "\n")

# 寫入 KML 檔尾資訊
kml.write('</coordinates>\n')
kml.write('</LineString>\n')
kml.write('</Placemark>\n')
kml.write('</Document>\n')
kml.write('</kml>\n')

內容解密:

此段程式碼主要完成的是將GPS收集到的座標資訊轉換成KML格式,讓它可以在Google Earth中展示飛行路徑。首先它會開啟兩個檔案:一個讀取GPS資料的檔案、一個寫入KML格式資料的檔案。然後它會從GPS日誌中逐行讀取經緯度和海拔資訊並寫入KML格式中。

停止 GPS 指令碼

如果需要停止正在執行的 GPS 指令碼,可以使用以下命令:

top

找到名為 python 的程式並記下其 PID(程式識別碼),然後使用以下命令停止該程式:

sudo kill <PID>

內容解密:

此段落主要介紹如何停止正在執行中的 Python 指令碼。透過使用 top 命令來檢視所有正在執行中的程式並找到目標 Python 指令碼的 PID,再透過 sudo kill <PID> 命令來停止該程式。

問題排除與改進建議

  1. GPS 初始化時間:根據實際情況調整初始化時間以確保 GPS 模組能夠準確取得訊號。
  2. 電池續航:選擇更高容量或更高效率的電池以延長飛機飛行時間。
  3. 硬體穩定性:確保所有元件固定牢固,避免飛機飛行過程中硬體掉落或損壞。

飛機程式碼

這部分程式碼負責在飛機飛行過程中不斷記錄經緯度和拍攝照片。

import os
from gps import *
from time import *
import time
import threading
import logging
from subprocess import call

# 設定日誌檔案
logging.basicConfig(filename='locations.log', level=logging.DEBUG, format='%(message)s')

picnum = 0
gpsd = None

class GpsPoller(threading.Thread):
    def __init__(self): # 初始化執行緒
        threading.Thread.__init__(self)
        global gpsd
        gpsd = gps(mode=WATCH_ENABLE)
        self.current_value = None
        self.running = True

    def run(self): # 執行緒運作內容
        global gpsd
        while self.running:
            gpsd.next()

if __name__ == '__main__': # 主程式部分,
    gpsp = GpsPoller() # 啟動執行緒並開始記錄經緯度及拍照操作。
    try:
        gpsp.start()
        while True:
            # 記錄從GPS取得到的經緯度及海拔資訊.
            logging.info(str(gpsd.fix.longitude) + " " + str(gpsd.fix.latitude) + " " +
                         str(gpsd.fix.altitude))
            # 在正確目錄下儲存帶編號圖片.
            call(["raspistill -o /home/pi/Documents/plane/image" + str(picnum) +
                  ".jpg"], shell=True)
            picnum = picnum + 1 # 增加圖片編號.
            time.sleep(3)
    except (KeyboardInterrupt, SystemExit):
        gpsp.running = False
        gpsp.join()

內容解密:

此段落首先透過GPS模組取得經緯度及海拔資訊。由於要在主程式同時進行多項操作(包括記錄經緯度及每隔幾秒拍攝一次照片),因此需要使用多執行緒處理。GpsPoller類別實作了多執行緒功能,gps.next()方法會不斷更新最新之GPS資訊,**time.sleep(3)**則代表每3秒重新整理一次以及拍照一次。try-except區塊則是捕捉終止程式時之錯誤以安全地關閉執行緒.