無人機飛行軌跡追蹤仰賴 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 日誌指令碼,從而實作無人機飛行軌跡的自動追蹤與記錄。
每段程式碼詳細解說
- 模組匯入:首先匯入必要的模組:
threading用於處理多執行緒、time用於延遲、gps用於與 GPS 接收器通訊、以及PiCamera用於拍照。 - Poller 類別:這是一個繼承自
threading.Thread的類別。__init__:初始化方法,設定相機和 GPS 連線。run:當執行緒啟動時會執行的方法。它不斷地從 GPS 接收器取得位置資料並拍照。
- GPS 資料處理:當取得到有效的 GPS 資料時(即
report['class'] == 'TPV'),會檢查並列印時間、緯度和經度。 - 拍照功能:每當取得到有效的 GPS 資料後,都會使用相機拍攝一張圖片並儲存到指定路徑。
- 延遲:每次迴圈結束後會延遲三秒鐘再繼續下一次迴圈。
透過這些技術和設定,我們可以實作無人機飛行軌跡的自動追蹤與記錄,並確保程式能夠穩定執行。
流程圖:
```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> 命令來停止該程式。
問題排除與改進建議
- GPS 初始化時間:根據實際情況調整初始化時間以確保 GPS 模組能夠準確取得訊號。
- 電池續航:選擇更高容量或更高效率的電池以延長飛機飛行時間。
- 硬體穩定性:確保所有元件固定牢固,避免飛機飛行過程中硬體掉落或損壞。
飛機程式碼
這部分程式碼負責在飛機飛行過程中不斷記錄經緯度和拍攝照片。
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區塊則是捕捉終止程式時之錯誤以安全地關閉執行緒.
