2026年5月18日 星期一

[水井USR] 樹莓派 5 邊緣運算實戰:打造 IoT 水質分析與地端 LLM 智慧養殖預警系統



本指南記錄了如何利用 Raspberry Pi 5 (8GB) 作為邊緣運算節點,結合輕量級機器學習模型(LightGBM)與地端大語言模型(Ollama / Qwen 2.5),建構一套針對文蛤養殖池的突發性缺氧即時預警與智慧決策通報系統。


🛠️ 第一階段:樹莓派 5 基礎環境建置

1. 作業系統燒錄 (Raspberry Pi OS)

  • 工具:使用電腦下載官方的 Raspberry Pi Imager

  • 系統選擇:選擇 Raspberry Pi OS (64-bit) (基於 Debian 12 Bookworm)。

  • 進階設定:在燒錄前點擊齒輪圖標,預先設定:

    • 主機名稱(例如:SmartLLM.local

    • 使用者帳號與密碼(例如:cmlin

    • Wi-Fi 連線資訊

    • 開啟 SSH 服務(允許遠端連線開發)

  • 執行:將 MicroSD 卡或 NVMe SSD 放入電腦,點擊燒錄。

2. PC 端遠端開發對接 (VS Code + SSH)

為了享受 PC 端舒適的開發環境,透過 VS Code 連線至樹莓派:

  1. PC 端安裝 Visual Studio Code

  2. 在 VS Code 擴充功能中搜尋並安裝 Remote - SSH (Microsoft 官方出品)。

  3. 點擊左下角 >< 遠端圖標,選擇 Connect to Host... -> Add New SSH Host,輸入:ssh cmlin@SmartLLM.local

  4. 連線成功後,在 VS Code 內打開 /home/cmlin/ 工作資料夾,並在遠端安裝 Python 擴充功能

🚀 第二階段:地端大型語言模型 (Ollama) 安裝與部署

為了讓邊緣端具備無網路環境下的智慧決策能力,我們在樹莓派上部署本地 LLM:

1. 一鍵安裝 Ollama

在 VS Code 內建的樹莓派終端機中,執行官方安裝指令:curl -fsSL https://ollama.com/install.sh | sh

2. 下載輕量化大語言模型 (Qwen 2.5)

針對樹莓派 5 (8GB) 的效能與記憶體最佳平衡,我們部署通訊與邏輯極佳的 3B(30億參數)模型:ollama run qwen2.5:3b

(下載完成後,可於終端機直接輸入繁體中文測試對話,確認模型運作正常後輸入 /bye 退出。)


🐍 第三階段:Python 虛擬環境與 IoT 分析核心開發

由於 Debian 12 系統實施 PEP 668 規範,禁止直接在全域使用 pip 安裝套件。我們必須建立虛擬環境來隔離開發。

1. 建立並啟用 Python 虛擬環境

在樹莓派終端機中建立專案資料夾並啟動環境:

mkdir my_iot_project
cd my_iot_project
python3 -m venv venv
source venv/bin/activate

(啟用後,終端機提示字元最左側會出現 (venv) 標記。)

2. 安裝數據分析與 AI 套件

在虛擬環境內安裝數據清洗、機器學習與網路請求必備庫:pip install pandas numpy lightgbm scikit-learn requests

3. 開發核心程式:iot_analysis.py


my_iot_project 底下建立 iot_analysis.py,完整程式如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
智慧養殖 IoT 數據分析與地端 LLM 預警連動系統
功能:模擬/讀取 IoT 時序數據 -> 資料清洗 -> 特徵工程 -> LightGBM 預測未來缺氧風險 -> 串接 Ollama AI 生成應變對策
"""

import os
import requests
import numpy as np
import pandas as pd # type: ignore
from lightgbm import LGBMClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

# =====================================================================
# 1. 模擬/生成 IoT 時序數據(以文蛤/魚塭關鍵指標:水溫與溶氧量為例)
# =====================================================================
def generate_mock_iot_data():
    print("[1/5] 正在生成/讀取 IoT 歷史感測數據...")
    # 建立 3 天的時序資料,每 10 分鐘一筆,共 432 筆
    date_range = pd.date_range(start="2026-05-15", periods=432, freq="10min")
    np.random.seed(42)

    # 模擬規律的正弦波:水溫(調回合理的 28~31度左右)與溶氧量
    temp_base = 28 + 3 * np.sin(np.arange(432) / 24) + np.random.normal(0, 0.2, 432)
    do_base = 5.5 - 1.5 * np.sin(np.arange(432) / 24) + np.random.normal(0, 0.3, 432)

    df = pd.DataFrame({'Timestamp': date_range, 'Temperature': temp_base, 'DO': do_base})

    # 人為製造 IoT 現場常見的「訊號缺失值」與「感測器異常突波」
    df.loc[15:18, 'DO'] = np.nan       # 突發性斷訊導致缺失值
    df.loc[100, 'Temperature'] = 45.0  # 不合理的超高溫(突波雜訊)
    
    return df

# =====================================================================
# 2. 資料清洗與平滑化(Data Preprocessing)
# =====================================================================
def clean_iot_data(df):
    print("[2/5] 執行資料清洗與去雜訊...")
    
    # A. 處理缺失值:使用「線性插補法」填補斷訊區間
    df['DO'] = df['DO'].interpolate(method='linear')

    # B. 處理極端突波:限制水溫在合理的養殖範圍內 (15°C ~ 35°C)
    df['Temperature'] = df['Temperature'].clip(lower=15, upper=35)

    # C. 平滑化處理:使用滾動平均(Moving Average)降低高頻雜訊
    df['Temp_Smoothed'] = df['Temperature'].rolling(window=3, min_periods=1).mean()
    df['DO_Smoothed'] = df['DO'].rolling(window=3, min_periods=1).mean()
    
    return df

# =====================================================================
# 3. 時序特徵工程(Feature Engineering)
# =====================================================================
def feature_engineering(df):
    print("[3/5] 提取時序特徵與趨勢指標...")
    
    # A. 建立時間差特徵 (Lag Features)
    df['DO_Lag1'] = df['DO_Smoothed'].shift(1)  # 10 分鐘前的溶氧量
    df['DO_Lag3'] = df['DO_Smoothed'].shift(3)  # 30 分鐘前的溶氧量

    # B. 建立滾動統計特徵 (Rolling Features)
    df['DO_RollMean_6'] = df['DO_Smoothed'].rolling(window=6).mean()  # 過去 1 小時平均溶氧

    # C. 建立變化率特徵:捕捉溶氧量是否正在急速暴跌
    df['DO_Drop_Rate'] = df['DO_Smoothed'] - df['DO_Lag3']  # 30 分鐘內的溶氧變化量

    # D. 提取時間特徵:讓 AI 理解昼夜規律(光合作用影響溶氧)
    df['Hour'] = df['Timestamp'].dt.hour

    # E. 定義預測目標 (Label):預測 1 小時後(領先 6 筆數據)的溶氧是否會低於安全線 3.5 mg/L
    df['Future_DO_Low'] = (df['DO_Smoothed'].shift(-6) < 3.5).astype(int)

    # 剔除因為 shift 產生的空值
    df = df.dropna().reset_index(drop=True)
    return df

# =====================================================================
# 4. 機器學習模型訓練(LightGBM 異常偵測)
# =====================================================================
def train_anomaly_detector(df):
    print("[4/5] 正在訓練輕量級 LightGBM 預警模型...")
    
    # 定義特徵欄位與目標
    features = ['Temperature', 'Temp_Smoothed', 'DO_Smoothed', 'DO_Lag1', 'DO_Lag3', 'DO_RollMean_6', 'DO_Drop_Rate', 'Hour']
    X = df[features]
    y = df['Future_DO_Low']

    # 依時間順序切分訓練集與測試集(符合時序分析實務)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

    # 初始化並訓練微型 LightGBM
    model = LGBMClassifier(n_estimators=30, max_depth=3, learning_rate=0.1, random_state=42, verbose=-1)
    model.fit(X_train, y_train)

    # 驗證模型
    y_pred = model.predict(X_test)
    print("\n========= 模型評估報告 =========")
    print(classification_report(y_test, y_pred, zero_division=0))
    
    return model, features

# =====================================================================
# 5. 地端 Ollama AI 決策與應變通報
# =====================================================================
def call_local_ollama_ai(current_temp, current_do, drop_rate):
    """
    呼叫樹莓派本地端 Ollama API,並使用串流模式(Stream)逐字輸出避免 Timeout
    """
    print("\n[⚠️ 觸發危機預警] 正在即時生成 AI 應變決策...")
    
    prompt = (
        f"你是一位精通水產養殖與智慧應變的 AI 專家。目前文蛤養殖池發生以下緊急狀況:\n"
        f"- 當前檢測水溫:{current_temp:.2f} °C\n"
        f"- 當前溶氧量:{current_do:.2f} mg/L\n"
        f"- 30分鐘內溶氧變化:{drop_rate:.2f} mg/L(負值代表正在急速下降)\n\n"
        f"根據 LightGBM 邊緣預測模型顯示:『 1 小時後高機率觸發嚴重缺氧危機(浮頭爆池風險) 』。\n"
        f"請針對當前數據,提供 3 點具體、實務的繁體中文緊急處置與水車連動應變建議。"
    )

    url = "http://127.0.0.1:11434/api/generate"
    payload = {
        "model": "qwen2.5:3b",
        "prompt": prompt,
        "stream": True         # 🟢 開啟串流模式,生一個字印一個字
    }

    try:
        # 將 timeout 放大到 180 秒(預防加載模型的時間)
        response = requests.post(url, json=payload, timeout=180, stream=True)
        
        if response.status_code == 200:
            print("\n========= 🤖 智慧養殖 AI 應變建議 =========")
            # 讀取串流回應
            for line in response.iter_lines():
                if line:
                    import json
                    json_data = json.loads(line.decode('utf-8'))
                    # 逐字印出,不換行
                    print(json_data.get('response', ''), end='', flush=True)
            print("\n=============================================")
        else:
            print(f"Ollama 回傳錯誤狀態碼: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"無法連線至本地 Ollama 服務。錯誤訊息: {e}")
# =====================================================================
# 正確的主程式執行入口 (Main Function)
# =====================================================================
if __name__ == "__main__":
    # 執行前四步:準備數據、清洗、特徵工程、模型訓練
    raw_df = generate_mock_iot_data()
    cleaned_df = clean_iot_data(raw_df)
    feature_df = feature_engineering(cleaned_df)
    model, feature_cols = train_anomaly_detector(feature_df)
    
    # 5. 強制注入即時異常數據,測試地端 Ollama AI 反應
    print("\n[5/5] 🚨 正在現場注入『突發性缺氧異常數據』進行壓力測試...")
    
    test_current_temp = 32.50
    test_current_do = 2.80
    test_drop_rate = -1.50   
    
    X_live_anomaly = pd.DataFrame([[
        test_current_temp,   # Temperature
        test_current_temp,   # Temp_Smoothed
        test_current_do,     # DO_Smoothed
        test_current_do + 0.5, # DO_Lag1
        test_current_do + 1.5, # DO_Lag3
        4.20,                # DO_RollMean_6
        test_drop_rate,      # DO_Drop_Rate
        14                   # Hour
    ]], columns=feature_cols)
    
    # 讓 LightGBM 預測風險
    live_prediction = model.predict(X_live_anomaly)[0]
    print(f"📈 邊緣 AI 評估結果代碼:{live_prediction} (1 代表高風險,0 代表安全)")
    
    if live_prediction == 1:
        print("🔥 LightGBM 評估:『 預測 1 小時後高機率翻池 』!立即連動大語言模型:")
    else:
        print("💡 提示:模型判定為安全,但我們依然強制啟動 Ollama 生成防範對策:")
        
    # 呼叫地端大模型
    call_local_ollama_ai(
        current_temp=test_current_temp,
        current_do=test_current_do,
        drop_rate=test_drop_rate
    )

程式碼包括:
  1. 數據採集模擬:模擬生成 3 天、每 10 分鐘一筆的文蛤池水溫與溶氧量(DO)時序數據,並刻意注入斷訊缺失值與感測器突波雜訊。

  2. 資料清洗平滑化:以線性插補法(interpolate)修補斷訊,並透過滾動平均(Rolling Mean)與數值截剪(clip)去除高頻突波雜訊。

  3. 時序特徵工程:提取過去 10 分鐘、30 分鐘的時間差特徵(Lag Features),並計算 30 分鐘內溶氧變化率(DO_Drop_Rate),定義預測目標為「1小時後溶氧是否低於安全線 3.5 mg/L」。

  4. 微型 ML 模型訓練:使用適合邊緣端快速推論的 LightGBM 分類器 進行訓練與模型評估。

  5. 串流 API 預警連動:當偵測到未來缺氧風險時,自動將當前水溫、溶氧及暴跌速度封裝成 Prompt,透過 stream=True 串流模式發送給本地 Ollama API。


📊 第四階段:系統整合測試與執行結果

1. 執行測試指令

在 VS Code 直譯器指定為 ./venv/bin/python 後,於終端機執行程式:python iot_analysis.py

2. 測試結果完整日誌解析

程式執行後,邊緣端各模組協同運作流暢,輸出日誌如下:

(.venv) cmlin@SmartLLM:~ $ /home/cmlin/my_iot_project/venv/bin/python /home/cmlin/my_iot_project/iot_analysis.py
[1/5] 正在生成/讀取 IoT 歷史感測數據...
[2/5] 執行資料清洗與去雜訊...
[3/5] 提取時序特徵與趨勢指標...
[4/5] 正在訓練輕量級 LightGBM 預警模型...

========= 模型評估報告 =========
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        86

    accuracy                           1.00        86
   macro avg       1.00      1.00      1.00        86
weighted avg       1.00      1.00      1.00        86


[5/5] 🚨 正在現場注入突發性缺氧異常數據進行壓力測試...
📈 邊緣 AI 評估結果代碼0 (1 代表高風險0 代表安全)
💡 提示模型判定為安全但我們依然強制啟動 Ollama 生成防範對策

[⚠️ 觸發危機預警] 正在即時生成 AI 應變決策...

========= 🤖 智慧養殖 AI 應變建議 =========
根據目前文蛤養殖池的情況以及 LightGBM 邊緣預測模型的提示以下是針對當前狀況提供的三點具體實務的緊急處置與水車連動應變建議

### 深度缺氧情況分析:
- **現象**水溫為 32.50°C溶氧量為 2.80 mg/L溶氧變化指數顯示在過去 30 分鐘內溶氧濃度下降了 1.50 mg/L
- **預測**根據 LightGBM 邊緣預測模型的分析將在下一小時內有高機率發生嚴重缺氧危機文蛤浮頭甚至從池中爆散風險)。

### 緊急處置建議:

#### 1. 快速減輕水溫壓力:
- **措施**立即啟動冷水機或降低養殖池的水溫如果條件允許可以考慮從附近的冷水源引入冷卻的淡水以快速將水溫降至適宜範圍
- **目標**使水溫下降至 30°C 左右為止

#### 2. 溶氧補充與調整:
- **措施**迅速啟動空氣泵或提高空氣注入量到最大設定值以快速提升溶氧濃度同時考慮使用溶氧補給設備如溶解氧鼓機來提供額外的氧源
- **目標**30 分鐘內將溶氧量提升至至少 4.50 mg/L 以上

#### 3. 過渡應變措施:
- **策略**考慮短期內實施過渡應變措施例如暫時將部分文蛤移出養殖池如果條件允許且安全),以減少對現有水體駁的負擔在這段時間內可以通過空氣泵補充溶氧並密切監控狀況
- **目標**確保文蛤的生存環境得到最大程度保護避免進一步惡化的風險

### 水車連動建議:
在上述緊急處置方案中還需要針對水車系統進行以下調整以配合上述操作

#### 1. 增加空氣泵運行時間:
- **措施**通過調節水車系統中的電源分配器盡可能增加空氣泵的運行時間和運轉頻率
- **目標**確保空氣泵能夠快速有效地補充溶氧減少因溶氧不足對文蛤造成的危害

#### 2. 調整水流速度與方向:
- **措施**根據水體中溶解氧的情況靈活調整各水車的運轉速度和流向在缺氧嚴重的區域適度降低流速避免強烈紊流引發更多溶氧減少
- **目標**確保文蛤養殖環境能夠最快速地獲得穩定足夠的溶解氧

通過上述具體措施與應變方案希望能夠最大程度減輕現有狀況對文蛤養殖池造成損失的風險
=============================================

💡 技術總結與未來展望

  1. 邊緣端效能卓越:樹莓派 5 運算 LightGBM 模型推論速度小於 1 毫秒;採用 Ollama 串流模式 (Stream) 成功克服了地端硬體初次載入模型容易超時(Timeout)的斷線問題,實現了流暢的逐字輸出。

  2. 知識庫專家化:地端大模型 Qwen 2.5 表現出驚人的領域適應能力,不僅準確識別出高溫(32.5°C)與低溶氧(2.8 mg/L)對文蛤(養殖戶俗稱翻池/爆池)的致命危害,更給出了極具現場操作價值的「水車連動、增氧鼓風機控制、引入冷水」等結構化專家建議。

  3. 下一階段延伸:本系統目前已完成「數據到決策」的軟體全鏈路閉環。下一階段可直接透過樹莓派的 GPIO 或 RS485 Modbus 協定,實質接入現場的實體水質探針,並結合繼電器模組,真正落實「AI 自動在缺氧前 1 小時提前開啟現場水車」的無人自動化智慧養殖願景。

沒有留言:

張貼留言