本章节介绍通过平台标准协议接入的设备。
| 设备 | 说明 | 文档 |
|---|---|---|
| 应变读数仪 | 高频应变监测设备 | 查看详情 → |
应变读数仪用于结构健康监测中的应变数据采集,通过 MQTT + Protobuf 协议上报数据。
| 文档 | 说明 |
|---|---|
| 协议规格 | MQTT 主题、Protobuf 定义、字段说明 |
| 型号 | 通道数 | 最高采样频率 | 说明 |
|---|---|---|---|
| SGR-10 | 10 | 100Hz | 振玄 10 通道应变读数仪 |
| 参数 | 值 |
|---|---|
| 协议 | MQTT v3.1.1 |
| Broker | 由平台提供 |
| 端口 | 1883 |
| Payload | Protobuf |
支持两种 MQTT 认证方式:
| 方式 | clientId | username | password |
|---|---|---|---|
| Access Token | 任意 | Access Token | 留空 |
| Basic | deviceId | username | password |
认证凭据在平台创建设备后获取。clientId 建议使用 deviceId。
| 主题 | 方向 | QoS | 说明 |
|---|---|---|---|
strain/{deviceId}/telemetry | 上行 | 0 | 遥测数据(高频应变值) |
strain/{deviceId}/attributes | 上行 | 1 | 设备属性上报 |
strain/{deviceId}/attributes/subscribe | 下行 | 1 | 平台下发配置指令 |
设备端编译以下 .proto 文件生成对应语言的代码:
syntax = "proto3";
package shm.strain.v1;
// 遥测上报消息
message StrainTelemetry {
StrainBatch strainBatch = 1;
message StrainBatch {
uint64 timestamp = 1; // 首样本设备时间戳(毫秒)
uint32 ch_no = 2; // 通道编号(从 1 开始)
uint32 sample_interval_ms = 3; // 采样间隔(ms),100Hz=10, 50Hz=20, 20Hz=50
repeated sint32 values = 4; // 该通道的应变值数组(με)
optional float temperature = 5; // 该通道温度值(℃)
optional uint32 battery = 6; // 电池电量 0-100%
optional uint32 mode = 7; // 设备工作模式
optional uint32 status = 8; // 状态码,0=正常
}
}
// 属性上报消息
message StrainDeviceAttributes {
string firmware_version = 1; // 固件版本
string serial_number = 2; // 设备序列号
string device_model = 3; // 设备型号
uint32 channel_count = 4; // 通道数
uint32 sample_interval_ms = 5; // 采样间隔(ms),所有通道统一
}
遥测数据封装在 StrainTelemetry.strainBatch 中,每个通道独立一包。
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
timestamp | uint64 | 是 | 本批次首样本的设备时间戳,毫秒 | 1776206400000 |
ch_no | uint32 | 是 | 通道编号,从 1 开始 | 1 |
sample_interval_ms | uint32 | 是 | 采样间隔,100Hz=10, 50Hz=20, 20Hz=50 | 10 |
values | repeated sint32 | 是 | 该通道应变值数组(με),长度 = 1000 / sample_interval_ms | [1500,1502,1498,...] |
temperature | float | 否 | 该通道温度值(℃) | 25.3 |
battery | uint32 | 否 | 电池电量百分比 | 60 |
mode | uint32 | 否 | 工作模式编码 | 0 |
status | uint32 | 否 | 0=正常,其他=异常码 | 0 |
设备启动、固件升级或配置变更时上报一次。
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
firmware_version | string | 是 | 固件版本 | "1.0.3" |
serial_number | string | 是 | 设备序列号 | "ZX-0001" |
device_model | string | 是 | 设备型号 | "SGR-10" |
channel_count | uint32 | 是 | 通道数 | 10 |
sample_interval_ms | uint32 | 是 | 采样间隔(ms),所有通道统一 | 10 |
平台通过修改共享属性下发配置指令,设备订阅 strain/{deviceId}/attributes/subscribe 接收变更。
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
sample_interval_ms | uint32 | 修改采样间隔 | 20(50Hz) |
mode | uint32 | 切换工作模式 | 1 |
reboot | bool | 重启设备 | true |
sync_time | uint64 | 时间同步,平台当前时间(ms) | 1776206400000 |
设备收到属性变更后直接执行,无需回复。设备重连后可主动请求最新共享属性获取最新配置。
每个通道每秒上报一包,包内样本时间按以下公式推导:
第 n 个样本时间 = timestamp + n × sample_interval_ms
示例(通道 1,100Hz,timestamp = 1776206400000):
| 样本 | 时间戳 |
|---|---|
| 第 1 个(n=0) | 1776206400000 |
| 第 2 个(n=1) | 1776206400010 |
| 第 100 个(n=99) | 1776206400990 |
以 10 通道 100Hz 设备为例,每秒上报 10 个包:
每秒重复:
1. 获取当前设备时间戳 timestamp
2. 对每个通道 ch_no = 1..10:
a. 采集 100 个应变值
b. 构造 StrainTelemetry 消息
c. 序列化为 Protobuf 二进制
d. 发布到 strain/{deviceId}/telemetry,QoS 0
| 包 | ch_no | sample_interval_ms | values 长度 |
|---|---|---|---|
| 包1 | 1 | 10 | 100 |
| 包2 | 2 | 10 | 100 |
| … | … | … | … |
| 包10 | 10 | 10 | 100 |
同一秒的 10 个包共享同一个 timestamp。
pip install paho-mqtt protobuf
将上方 Protobuf 定义保存为 strain.proto,然后编译:
protoc --python_out=. strain.proto
#!/usr/bin/env python3
"""应变读数仪模拟器 - 10通道 100Hz"""
import math, random, time
import paho.mqtt.client as mqtt
import strain_pb2
BROKER = "gw.mqtt.inteagle.com"
PORT = 1883
TOKEN = "your_access_token" # 替换为平台分配的 Access Token
DEVICE_ID = "ZX-0001" # 替换为平台分配的设备编号
CHANNELS = 10
SAMPLE_INTERVAL_MS = 10 # 100Hz
def on_connect(client, userdata, flags, rc, properties=None):
print(f"连接{'成功' if rc == 0 else '失败: ' + str(rc)}")
def main():
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"strain-{DEVICE_ID}")
client.username_pw_set(TOKEN)
client.on_connect = on_connect
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
time.sleep(1)
# 上报设备属性(仅启动时一次)
attrs = strain_pb2.StrainDeviceAttributes()
attrs.firmware_version = "1.0.3"
attrs.serial_number = DEVICE_ID
attrs.device_model = "SGR-10"
attrs.channel_count = CHANNELS
attrs.sample_interval_ms = SAMPLE_INTERVAL_MS
client.publish(f"strain/{DEVICE_ID}/attributes", attrs.SerializeToString(), qos=1)
print("属性上报完成")
# 持续上报遥测
topic = f"strain/{DEVICE_ID}/telemetry"
num_samples = 1000 // SAMPLE_INTERVAL_MS # 100
for sec in range(10):
ts_ms = int(time.time() * 1000)
for ch in range(1, CHANNELS + 1):
msg = strain_pb2.StrainTelemetry()
batch = msg.strainBatch
batch.timestamp = ts_ms
batch.ch_no = ch
batch.sample_interval_ms = SAMPLE_INTERVAL_MS
# 模拟应变数据
base = 1500 + ch * 100
for i in range(num_samples):
t = i * SAMPLE_INTERVAL_MS / 1000.0
val = int(base + 200 * math.sin(2 * math.pi * 2.0 * t) + random.randint(-10, 10))
batch.values.append(val)
batch.temperature = 25.3
batch.battery = 85
batch.status = 0
client.publish(topic, msg.SerializeToString(), qos=0)
print(f"第{sec+1}秒: 发送 {CHANNELS} 包")
time.sleep(1)
client.loop_stop()
client.disconnect()
print("完成")
if __name__ == "__main__":
main()
python sim_strain.py
连接成功
属性上报完成
第1秒: 发送 10 包
第2秒: 发送 10 包
第3秒: 发送 10 包
...
完成