在物聯網系統開發中,感測器和致動器的整合至關重要,但硬體的取得和佈署常造成初期開發的瓶頸。透過模擬感測器和致動器產生的資料,開發者得以在早期階段驗證系統邏輯和演算法,並及早發現潛在問題。本文將介紹如何使用 Python 和相關函式庫生成模擬資料,並探討如何將這些資料整合到物聯網系統的應用程式設計中,包含資料容器的設計、感測器和致動器資料的抽象化,以及如何處理磁滯效應等實際應用中的挑戰。

資料模擬:感知與致動的關鍵技術

在物聯網(IoT)系統中,感知與致動是兩個基本且重要的功能。感測器與致動器是連線物理世界與邏輯世界的關鍵介面,它們的多樣性與介面需求為開發帶來了挑戰。為了簡化開發流程並聚焦於初始使用案例,本章將著重於利用資料模擬與硬體模擬來提供所需的「物理世界」介面。

模擬資料的生成與處理

本章的核心內容是測量與建模在簡單感測器與致動器模擬環境中生成的資料。這些資料將代表少數環境感測器的讀數,並學習如何處理簡單的門檻值穿越(threshold crossings)以觸發模擬的致動事件。

感測器與致動器的模擬

在 Constrained Device Application(CDA)的 PiotConfig.props 組態檔案中,我們可以找到一些可組態的引數,例如濕度、壓力和溫度的感測器警示限制:

# 可組態的感測器警示限制
humiditySensorFloor = 35.0
humiditySensorCeiling = 45.0
pressureSensorFloor = 990.0
pressureSensorCeiling = 1010.0
tempSensorFloor = 15.0
tempSensorCeiling = 25.0
# 可組態的致動器觸發引數
handleTempChangeOnDevice = True
triggerHvacTempFloor = 18.0
triggerHvacTempCeiling = 22.0

組態引數的作用

這些引數定義了感測器的基礎設定值(最低和最高值),以及觸發 HVAC 警示的門檻值。此外,還有布林值標誌用於指示應用程式是否應該採取行動。

磁滯效應(Hysteresis)的處理

在處理感測器資料時,磁滯效應是一個重要的考慮因素。磁滯效應是一種滯後效應,受系統輸入的影響。例如,在溫度控制中,我們不希望溫控器在環境溫度稍微高於或低於給定值時頻繁地開啟和關閉 HVAC 系統。相反,我們應該追蹤環境溫度的逐漸變化,並在一段時間內執行 HVAC 系統,以將溫度還原到正常範圍。

簡單的組態屬性實作磁滯效應

本文將使用簡單的組態屬性來實作磁滯效應,讀者也可以根據需要引入更進階的功能。

Gateway Device Application(GDA)的組態

在 GDA 的 PiotConfig.props 組態檔案中,我們可以找到一些可組態的觸發值,例如:

# 可組態的致動器觸發引數
enableHandleHumidityChangeOnDevice = True
triggerHumidityFloor = 30.0
triggerHumidityCeiling = 40.0

GDA 組態引數的作用

這些引數將在後續章節中被參照,用於實作系統的 Model 和 Manage 設計特性。

本章重點

本章將學習如何設計和建構邏輯元件,以與模擬的感測器和致動器互動。透過資料模擬和硬體模擬,我們將簡化 IoT 系統的開發流程,並聚焦於初始使用案例。讀者將學習如何生成和處理模擬資料,以及如何實作簡單的門檻值穿越觸發機制。

模擬感測器與執行器

在模擬感測器時,需要了解幾個關鍵要素,包括:

  • 該感測器將生成的資料型別
  • 感測器的型別及其用途(可透過名稱和ID值表示)
  • 感測器可支援的資料範圍

對於執行器的設定也類別似,但不同的是,不會從執行器收集資料,而是會向其傳送一或多個命令(如開啟或關閉訊號),甚至可能包含數值和其他資訊(如狀態資料)。

最後,需要存取模擬感測器邏輯可使用的資料來源。在CDA中整合資料來源的方法有很多,因此假設資料必須是本地的。這意味著本質上有兩種選擇:為每個感測器使用固定的資料集(可簡單地儲存在檔案系統中),或為每個感測器生成動態資料集。在我的Connected Devices課程中,我採用後者,並依賴一個簡單的模組——SensorDataGenerator,該模組位於./src/main/python/programmingtheiot/cda/sim/SensorDataGenerator.py檔案中。

使用感測器資料生成器類別生成模擬資料

SensorDataGenerator是一個相對簡單的類別,可用於生成代表CDA將收集的感測器測量值的資料集,例如溫度、壓力和濕度。它依賴NumPy套件在給定的時間範圍內生成一系列浮點數值。它還允許引入不同程度的雜訊或波動,並將其應用於每個資料值。

雖然這是一種非常基本的模擬資料生成方法,旨在用於測試和原型設計目的,但它將為每個感測器的資料提供足夠的變化,以觸發根據在PiotConfig.props中為CDA和GDA設定的組態屬性的執行事件。將此類別與組態邊界值結合,提供了一個初始模型,能夠封裝我們在本章中將要使用的資料的限制、範圍和時間條目。

SensorDataGenerator方法介紹

SensorDataGenerator實作了六個方法,可用於生成溫度、壓力、濕度或其他任何根據浮點數的範圍的值,包括一個將在螢幕上繪製資料圖形(用於視覺驗證目的)的方法。後者名為generateOnScreenGraph(),使用Matplotlib生成靜態圖形視覺化。

資料生成方法,如generateDailyEnvironmentHumidityDataSet()generateDailyEnvironmentPressureDataSet()generateDailyIndoorTemperatureDataSet()generateDailyMonitorTemperatureDataSet()generateDailySensorDataSet(),都傳回一個SensorDataSet例項,其中包含由上述方法呼叫生成的所有時間戳和浮點數值條目。

每個方法都是引數化的,因此可以透過設定範圍(下限和上限值)、正弦型別(近似)、雜訊水平(我的自定義水平範圍從0%到100%)、集合中的專案數量以及是否使用秒級粒度(預設為每分鐘一個樣本)來輕鬆自定義輸出。

模擬資料視覺化範例

圖3-1顯示了一個使用generateDailyIndoorTemperatureDataSet()生成的室內溫度範圍的範例。從圖中可以看出,存在一定的雜訊,以模擬溫度感測器可能在每個讀取值上振盪1%至2%的真實溫度值。隨著時間的推移,可以看到整體下限約為18°C,上限約為22°C,溫度從其起始點約20°C逐漸升高至第7小時的最大值,然後在第19小時降至最低點。

import numpy as np
import matplotlib.pyplot as plt

# 範例程式碼:使用SensorDataGenerator生成模擬溫度資料
class SensorDataGenerator:
    def generateDailyIndoorTemperatureDataSet(self, floor, ceiling, noise_level, num_items):
        # 省略實作細節
        pass

    def generateOnScreenGraph(self, sensor_data_set, y_label, x_label):
        # 省略實作細節
        pass

# #### 內容解密:
# * `generateDailyIndoorTemperatureDataSet`方法根據給定的引數生成模擬的室內溫度資料集。
# * `generateOnScreenGraph`方法將生成的資料集繪製成圖形,以視覺化的方式呈現資料趨勢。

# 使用範例
generator = SensorDataGenerator()
data_set = generator.generateDailyIndoorTemperatureDataSet(18, 22, 0.01, 1000)
generator.generateOnScreenGraph(data_set, '溫度 (°C)', '時間 (小時)')

圖3-2顯示了另一個圖形範例,這次代表濕度隨時間變化的趨勢。濕度水平從約35%的相對濕度上升到約45%的相對濕度,然後再次下降。在這種情況下,我向方法傳遞了一個較低的雜訊值,以模擬來自濕度感測器的較乾淨的值讀取。無論哪種方式,趨勢都很明顯,並提供了足夠的機會來設定一個上限值,從而透過執行事件開啟除濕機或空調系統。

程式設計練習

回想一下,Edge Tier應用程式的整體設計重點是感測和執行(在CDA內)以及與雲端的整合和一些分析功能(在GDA內)。這意味著CDA即將變得更加複雜,因此檢視應用程式設計以及您將要更新的模組將非常有幫助。

在應用程式設計中整合感測與執行模擬

圖3-3展示了具備模擬感測和執行功能的受限裝置應用程式(Constrained Device App)。

圖3-3. 受限裝置應用程式模擬器設計

在圖3-3中,可以看到我們正在進行更複雜的軟體工程設計,這需要探討資料管理、功能抽象和回呼函式。不過,不用擔心,我將逐步分解這些部分,使其易於理解。

實作這些功能的一種方法是從頂層開始,逐步向下推進到資料結構。然而,從實作角度來看,從底層開始比較容易,因為頂層的元件(如DeviceDataManager等)都依賴於這些底層結構和類別。

圖3-4. 受限裝置應用程式模擬器UML表示

圖3-4顯示了一些需要實作的元件及其關係的UML表示。此模式僅代表DeviceDataManager、SystemPerformanceManager和SensorAdapterManager(僅溫度模擬任務)之間的兩個例項化使用案例。其他感測器模擬任務和ActuatorAdapterManager例項化序列將遵循相同的模式。

在開始之前,請務必閱讀PIOT-CDA-03-000,並為本章建立一個新的分支。

在應用程式中表示感測器和執行器資料

在進一步探討之前,讓我們退一步思考所有這些資料將如何在內部儲存,並隨後傳遞給GDA(和雲端)。由於模擬感測器的屬性與執行器的屬性(以及您已經收集的系統效能資料)之間存在相似性,因此可以將許多功能抽象到基底類別中。這些屬性包括:

  • 唯一的裝置名稱,用於識別物理裝置例項,表示為字串值。
  • 用於表示感測器或執行器型別的ID,表示為整數(可用於對映到每個應用程式中的字串名稱)。
  • 時間戳記,以ISO 8601時間/日曆表示法儲存,並在資料容器初始化(或更新)時生成。
  • 相關的位置資訊,為了簡單起見,使用浮點數值儲存十進位制緯度、十進位制經度和海拔高度(以公尺為單位)。
  • 錯誤指示旗標,布林值,預設為False。
  • 狀態碼,用於表示感測器或執行器的目前狀態,預設為0。

抽象化可以有所幫助,但您還需要一些邊界值、預設值和預設名稱,用於感測器、執行器和系統效能資料。在第三部分(特別是在第10章)中,您將看到如何將這些預設值集中在GDA中,但現在可以簡單地將它們作為ConfigConst類別中的「consts」進行存取。

建立資料容器以支援資料收集和執行

要確定任何系統中每個資料收集活動的適當詳細程度可能很具挑戰性,更不用說對於可能需要處理多種不同型別資料的IoT邊緣裝置。選項似乎無窮無盡——從具有不同精確度等級的值(整數或浮點數——如果是後者,小數點後有多少位?)到具有不同編碼方案的根據文字的表示法。

為了簡單起見,假設所有感測器和執行器資料值都將表示為32位浮點數值。我們將信任Python 3 float和Java 11 float所提供的精確度足以滿足我們的需要。

有了這種簡化的儲存值方法,您可以看到PIOT-CDA-03-001中列出的需求可能看起來有些繁瑣,但實作應該相對簡單。讓我們從基底類別——BaseIotData開始。

在基底類別中抽象分享屬性

BaseIotData是一個容器,用於儲存所有感測器、系統效能和執行器資料的共同屬性。這些屬性包括:

class BaseIotData:
    def __init__(self, name: str, typeID: int):
        self.name = name
        self.typeID = typeID
        self.timeStamp = self.generate_time_stamp()
        self.locationID = "constraineddevice001"
        self.latitude = 0.0
        self.longitude = 0.0
        self.elevation = 0.0
        self.hasError = False
        self.statusCode = 0

    def generate_time_stamp(self) -> str:
        # 生成ISO 8601格式的時間戳記
        from datetime import datetime
        return datetime.utcnow().strftime('%Y%m%dT%H:%M:%SZ')

    def updateTimeStamp(self):
        self.timeStamp = self.generate_time_stamp()

內容解密:

  • BaseIotData類別的建構函式接受nametypeID作為引數,並初始化其他屬性。
  • generate_time_stamp方法生成符合ISO 8601標準的時間戳記。
  • updateTimeStamp方法更新時間戳記。
  • 屬性的初始值根據需求設定,例如hasError預設為FalsestatusCode預設為0。

這種設計使得所有感測器、系統效能和執行器資料都可以分享相同的基礎結構,簡化了資料的管理和處理。