news 2026/9/14 4:36:26

在 IoT-For-Beginners 中存储 IoT 位置数据:从 GPS 遥测到 Azure Blob 存储的完整实现指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 IoT-For-Beginners 中存储 IoT 位置数据:从 GPS 遥测到 Azure Blob 存储的完整实现指南

在 IoT-For-Beginners 中存储 IoT 位置数据:从 GPS 遥测到 Azure Blob 存储的完整实现指南

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

导读

本篇文章是《IoT-For-Beginners》交通(Transport)项目第二课《Store location data》的技术实战指南,核心解决一个具体问题:上一课中 GPS 传感器捕获到的经纬度数据,如何发送到 Azure IoT Hub,再通过 Serverless 代码落入可查询的存储中,从而为下一课的路线可视化提供数据基础。读完本文你将掌握:结构化与非结构化数据的判别方法、SQL 与 NoSQL 存储的选型逻辑、热/温/冷数据路径的设计思想,以及基于 Azure Functions(Event Hub 触发器)+ Azure Blob Storage 构建 GPS 遥测落库链路的完整可运行方案。

本文配套的完整课程代码位于仓库3-transport/lessons/2-store-location-data/code/目录,包含wio-terminalpivirtual-device三套设备端实现与一套functions云端 Serverless 实现。


一、结构化数据与非结构化数据

计算机系统处理的数据形态千差万别:从单个数值、大段文本,到视频图片,再到 IoT 遥测数据。按结构特征,数据通常可划分为两类:

  • 结构化数据(Structured data):具有定义良好、固定不变、严格的表结构,通常映射为带关系的数据表。典型例子是个人信息(姓名、出生日期、地址)。
  • 非结构化数据(Unstructured data):没有严格固定结构,结构可以频繁变化。典型例子是文档(文本文档或电子表格)。
  • 半结构化数据(Semi-structured data):介于两者之间,有一定结构但无法固定进数据表,例如 JSON 文档。原文档对此也有提示(> 💁注记)。

IoT 数据通常被视为非结构化数据。这一点在运输场景中非常直观:假设你要为一家大型商业化农场车队部署 IoT 设备,不同车辆需要不同传感器组合:

  • 拖拉机需要 GPS 数据,确认其在正确农田作业;
  • 运输食品到仓库的货车需要 GPS、速度与加速度数据(评估驾驶安全),还需要驾驶员身份与启停数据(核查工时合规);
  • 冷藏车还需要温度数据,防止食品在运输途中过热或过冷而变质。

这些数据随挂车更换而动态变化——例如只有挂上冷藏挂车时才上报温度字段。它们结构各异,却都要进入同一个 IoT 服务处理,因此 IoT 服务必须能兼容这种多变的结构。这正是本课采用NoSQL 存储的原因。

SQL 与 NoSQL 存储

数据库是用于存储与查询数据的服务,分为 SQL 与 NoSQL 两大类型。

SQL 数据库

最早出现的数据库是关系型数据库管理系统(RDBMS,Relational Database Management System),因为使用结构化查询语言(SQL)进行增删改查,也称 SQL 数据库。其核心是Schema(模式):一组定义良好的数据表,类似电子表格,每张表有多个命名列;插入数据即向表添加行并为各列赋值。

SQL 数据库是关系型的:一张表可以通过键与另一张表建立关联。例如用户个人信息表为每位用户分配唯一 ID,购买记录表用该 ID 作为外键关联;查询用户时可用 ID 分别从两张表取出其个人信息与购买记录。

SQL 数据库的特点决定了它非常适合存储结构化数据,以及需要确保数据与 Schema 完全匹配的场景。缺点是结构刚性:虽然列可以留空,但若要新增一列,必须先在数据库层面执行变更并为已有行填充值。常见的 SQL 数据库有 Microsoft SQL Server、MySQL、PostgreSQL 等。

NoSQL 数据库

NoSQL 数据库名称源于其没有 SQL 数据库那样严格的表结构,也常被称为文档数据库(Document databases),因为它们可以直接存储文档这类非结构化数据。

💁 尽管名字如此,部分 NoSQL 数据库仍允许使用 SQL 语法查询数据。

NoSQL 数据库没有预定义的 Schema 限制存储方式,而是允许插入任意非结构化数据(通常为 JSON 文档)。文档可组织进类似计算机文件夹的集合中;每个文档可以与其他文档拥有完全不同的字段。仍以农场车队为例:部分车辆文档含加速度与速度字段,部分含挂车温度字段;若新增一种带车载称重装置的卡车,IoT 设备只需在文档里添加新字段即可,数据库无需任何改动。

常见 NoSQL 数据库包括 Azure CosmosDB、MongoDB、CouchDB 等。本课将使用 NoSQL 存储来存放 IoT 数据。


二、将 GPS 数据发送到 IoT Hub

上一课中设备从 GPS 传感器解析出经纬度。要把这些数据存入云端,第一步是发送到 IoT 服务。这里沿用前面项目用过的Azure IoT Hub

实操:创建 IoT Hub 并发送 GPS 数据

  1. 创建免费层 IoT Hub。可参照项目 2 第 4 课《在云端创建 IoT 服务》的说明。建议新建资源组并命名为gps-sensor,IoT Hub 名称基于gps-sensor取唯一名,例如gps-sensor-<your name>

    💁 若上一个项目的 IoT Hub 仍在,可复用;后续创建其他服务时记得沿用该 IoT Hub 名称及其资源组。

  2. 添加新设备:在 IoT Hub 中注册设备并命名为gps-sensor,复制该设备的连接字符串。
  3. 更新设备代码,使用上述设备连接字符串连接新 IoT Hub,参照项目 2 第 4 课《将设备连接到 IoT 服务》。
  4. 发送 JSON 格式的 GPS 数据,结构如下:
{ "gps" : { "lat" : <latitude>, "lon" : <longitude> } }
  1. 每分钟发送一次,避免耗尽每日消息配额。
Wio Terminal 端实现

仓库中的 Wio Terminal 设备代码 完整实现了上述流程:setup()中初始化串口Serial3与 GPS 模块,通过connectWiFi()连接 Wi-Fi(凭据在 config.h 中以SSID/PASSWORD占位符配置),并用initTime()通过 NTP 服务器校准时间(见 ntp.h)。构造 JSON 文档的核心代码为:

DynamicJsonDocument doc(1024); doc["gps"]["lat"] = gps.location.lat(); doc["gps"]["lon"] = gps.location.lng();

loop()中先用while (Serial3.available() > 0) { send_gps_data(); }确保读完串口缓冲区的全部 NMEA 数据后再发送当前位置,最后调用work_delay(60000)实现 60 秒间隔。发送动作经由 AzureIoTHub SDK 的sendTelemetry()完成(main.cpp)。依赖库清单在 platformio.ini 中声明,包括 ArduinoJson 6.17.3、AzureIoTHub 1.6.0、TinyGPSPlus 1.0.2 等。

Raspberry Pi 与虚拟 IoT 设备

Pi 设备代码 与虚拟设备代码 结构一致:用pynmea2.parse()解析 NMEA 句子,仅在GGA句式中提取经纬度,并用dm_to_sd()将度分格式转为十进制度,南纬/西经取负值。随后按 JSON 格式发送:

message_json = { "gps" : { "lat":lat, "lon":lon } } print("Sending telemetry", message_json) message = Message(json.dumps(message_json))

主循环同样是“读完串口全部行 +time.sleep(60)”(app.py)。注意:虚拟设备需在虚拟环境中安装counterfit_connectioncounterfit_shims_serialpynmea2azure-iot-device等依赖。

运行设备代码后,用az iot hub monitor-eventsCLI 命令确认消息已流入 IoT Hub。


三、热路径、温路径与冷路径

从 IoT 设备流向云端的数据并非全部需要实时处理:有的要实时响应,有的可以稍后处理,有的则可以长期沉淀。按处理时延的不同,数据流可分为热路径(Hot path)、温路径(Warm path)、冷路径(Cold path)

路径处理时延典型用途数据去向
热路径实时/近实时告警,如车辆接近仓库、冷藏车温度过高云服务收到事件后立即响应
温路径收到后不久日报、短期分析,如基于前一天数据的车辆里程日报存入可快速访问的存储
冷路径长期年度里程报告、路线分析以降低油耗数据仓库(Data Warehouse)

冷路径数据通常存放在数据仓库——为存放海量且永不变更、可被快速查询的数据而设计的数据库。一般会在云应用中设置定期作业(每天/每周/每月),把数据从温路径存储搬入数据仓库。

✅ 思考:你在前几课捕获的数据分别属于哪条路径?

本课数据属于温路径:数据落库后,将在下一课用于路线报告与可视化。


四、用 Serverless 代码处理 GPS 事件

数据流入 IoT Hub 后,可以编写 Serverless 代码监听Event Hub 兼容端点发布的事件——这就是温路径:数据将被存储,供下一课汇报行程使用。

实操:创建事件触发器函数

  1. 用 Azure Functions CLI 创建 Python 运行时函数应用,项目文件夹与项目名均为gps-trigger,并创建虚拟环境。可参照项目 2 第 5 课《创建 Serverless 应用》。
  2. 添加使用 IoT Hub Event Hub 兼容端点的IoT Hub 事件触发器,可参照项目 2 第 5 课《创建 IoT Hub 事件触发器》。仓库中 function.json 展示了绑定配置的关键:
{ "scriptFile": "__init__.py", "bindings": [ { "type": "eventHubTrigger", "name": "events", "direction": "in", "eventHubName": "samples-workitems", "connection": "IOT_HUB_CONNECTION_STRING", "cardinality": "many", "consumerGroup": "$Default", "dataType": "binary" } ] }

其中connection指向配置项键名,cardinality: many表示批处理接收多个事件,dataType: binary表示事件体按二进制处理。 3. 在 local.settings.json 中设置 Event Hub 兼容端点连接字符串,并在function.json中使用对应键名:

{ "IsEncrypted": false, "Values": { "FUNCTIONS_WORKER_RUNTIME": "python", "AzureWebJobsStorage": "UseDevelopmentStorage=true", "IOT_HUB_CONNECTION_STRING": "<connection string>", "STORAGE_CONNECTION_STRING": "<connection string>" } }
  1. 使用Azurite作为本地存储模拟器(对应AzureWebJobsStorage: UseDevelopmentStorage=true)。
  2. 保持设备持续发送 GPS 数据,运行函数应用验证事件接收:
Python EventHub trigger processed an event: {"gps": {"lat": 47.73481, "lon": -122.25701}}

五、Azure 存储账户:四种存储类型

Azure 存储账户是一个通用存储服务,可同时以Blob、队列、表、文件四种方式存储数据。

Blob 存储

Blob意为二进制大对象(binary large objects),如今泛指任意非结构化数据。Blob 存储可存放任何数据——从包含 IoT 数据的 JSON 文档到图片、电影文件。它有容器(container)的概念:命名桶,类似关系数据库的表;容器内可以有多个文件夹,文件夹可嵌套,如同计算机硬盘上的文件结构。本课将使用 Blob 存储存放 IoT 数据。

表存储

表存储用于存储半结构化数据。它本质上是 NoSQL 数据库,无需预先定义表集合,但数据仍组织在一个或多个表中,并以唯一键标识每行。

队列存储

队列存储允许在队列中存放最大 64KB的消息。消息从队尾追加、从队头读取。只要还有存储空间,队列可以无限期保存消息,因此支持长期暂存后按需处理。例如:想每月跑一次 GPS 数据处理任务,可以每天把数据加入队列攒一个月,月末一次性从队列消费全部消息。

文件存储

文件存储是在云端存放文件的服务,任何应用或设备都能用行业标准协议接入。可以把文件写入文件存储后,作为驱动器挂载到 PC 或 Mac。


六、将 Serverless 代码连接到存储

函数应用现在需要连接 Blob 存储来保存来自 IoT Hub 的消息。有两条路径:

  1. 在函数代码内使用 Blob 存储 Python SDK 连接并写入 Blob;
  2. 使用输出绑定(output binding),把函数返回值绑定到 Blob 存储,由平台自动保存。

本课采用方式 1(Python SDK),以便直观理解与 Blob 存储的交互;作业部分则要求调研方式 2。

落库数据将以如下 JSON Blob 格式保存:

{ "device_id": <device_id>, "timestamp" : <time>, "gps" : { "lat" : <latitude>, "lon" : <longitude> } }

实操:连接函数代码到 Blob 存储

  1. 创建 Azure 存储账户,命名为gps<your name>之类。可参照项目 2 第 5 课《创建云资源》;上一个项目的存储账户可复用(后续也会用它部署函数应用)。
  2. 获取连接字符串
az storage account show-connection-string --output table \ --name <storage_name>
  1. local.settings.json中新增键STORAGE_CONNECTION_STRING,填入上一步的值。
  2. 在 requirements.txt 中添加依赖并在虚拟环境安装:
azure-storage-blob

若安装报错,先在虚拟环境中升级 Pip:pip install --upgrade pip,再重试。

  1. 在触发器函数的__init__.py顶部添加导入(仓库中init.py 已给出完整实现):
import json import os import uuid from azure.storage.blob import BlobServiceClient, PublicAccess

各模块职责:json用于读写 JSON,os用于读取连接字符串,uuid用于生成 GPS 读数唯一 ID;azure.storage.blob提供 Blob 存储 Python SDK。

  1. main方法之前添加容器辅助函数:
def get_or_create_container(name): connection_str = os.environ['STORAGE_CONNECTION_STRING'] blob_service_client = BlobServiceClient.from_connection_string(connection_str) for container in blob_service_client.list_containers(): if container.name == name: return blob_service_client.get_container_client(container.name) return blob_service_client.create_container(name, public_access=PublicAccess.Container)

说明:Python Blob SDK 没有“不存在则创建容器”的便捷方法。该函数从local.settings.json(云端部署后为 Application Settings)读取连接字符串,创建BlobServiceClient与存储账户交互;遍历账户全部容器查找同名容器,找到则返回对应的ContainerClient(用于创建 Blob),未找到则创建新容器并返回其客户端。新建容器时授予公开访问权限,供下一课在地图上可视化 GPS 数据使用。

  1. 与土壤湿度场景不同,这里每个事件都要保存,因此在for event in events:循环内、logging语句下方添加:
device_id = event.iothub_metadata['connection-device-id'] blob_name = f'{device_id}/{str(uuid.uuid1())}.json'

这段代码从事件元数据取设备 ID,并据此构造 Blob 名。Blob 支持文件夹结构:设备 ID 作为文件夹名,使每台设备的所有 GPS 事件归入同一文件夹;Blob 名为“文件夹 + 文档名”,以正斜杠分隔(类似 Linux/macOS 路径),文档名由uuid模块生成的唯一 ID 加.json后缀构成。例如设备gps-sensor的 Blob 名可能为gps-sensor/a9487ac2-b9cf-11eb-b5cd-1e00621e3648.json

  1. 在其下方添加:
container_client = get_or_create_container('gps-data') blob = container_client.get_blob_client(blob_name)

通过get_or_create_container辅助函数获取gps-data容器的客户端,再按 Blob 名获取 Blob 客户端对象(可指向已有 Blob,也可如本例指向新 Blob)。

  1. 继续添加:
event_body = json.loads(event.get_body().decode('utf-8')) blob_body = { 'device_id' : device_id, 'timestamp' : event.iothub_metadata['enqueuedtime'], 'gps': event_body['gps'] }

构造写入 Blob 存储的正文:包含设备 ID、遥测进入 IoT Hub 的时间(enqueued time)以及遥测中的 GPS 坐标。

💁 必须使用消息的enqueued time(入队时间)而非当前时间,以还原消息真实发送时刻——若函数应用当时未运行,消息可能已在 Hub 上停留一段时间。

  1. 最后添加写入逻辑:
logging.info(f'Writing blob to {blob_name} - {blob_body}') blob.upload_blob(json.dumps(blob_body).encode('utf-8'))
  1. 运行函数应用,输出中应看到所有 GPS 事件对应的 Blob 写入记录:
[2021-05-21T01:31:14.325Z] Python EventHub trigger processed an event: {"gps": {"lat": 47.73092, "lon": -122.26206}} ... [2021-05-21T01:31:14.351Z] Writing blob to gps-sensor/4b6089fe-ba8d-11eb-bc7b-1e00621e3648.json - {'device_id': 'gps-sensor', 'timestamp': '2021-05-21T00:57:53.878Z', 'gps': {'lat': 47.73092, 'lon': -122.26206}}

💁 注意不要与az iot hub monitor-events监控同时运行。

完整实现见仓库 code/functions 目录。

实操:验证已上传的 Blob

查看已创建的 Blob 有两种方式:Azure Storage Explorer(免费工具,可查看与管理存储账户),或 CLI。

使用 CLI 的步骤:

  1. 获取账户密钥
az storage account keys list --output table \ --account-name <storage_name>

复制输出中的key1值。

  1. 列出容器中的 Blob
az storage blob list --container-name gps-data \ --output table \ --account-name <storage_name> \ --account-key <key1>

输出示例:

Name Blob Type Blob Tier Length Content Type Last Modified Snapshot ---------------------------------------------------- ----------- ----------- -------- ------------------------ ------------------------- ---------- gps-sensor/1810d55e-b9cf-11eb-9f5b-1e00621e3648.json BlockBlob Hot 45 application/octet-stream 2021-05-21T00:54:27+00:00 gps-sensor/18293e46-b9cf-11eb-9f5b-1e00621e3648.json BlockBlob Hot 45 application/octet-stream 2021-05-21T00:54:28+00:00 gps-sensor/1844549c-b9cf-11eb-9f5b-1e00621e3648.json BlockBlob Hot 45 application/octet-stream 2021-05-21T00:54:28+00:00 gps-sensor/1894d714-b9cf-11eb-9f5b-1e00621e3648.json BlockBlob Hot 45 application/octet-stream 2021-05-21T00:54:28+00:00
  1. 下载单个 Blob
az storage blob download --container-name gps-data \ --account-name <storage_name> \ --account-key <key1> \ --name <blob_name> \ --file <file_name>

<blob_name>需包含上一步Name列的完整名称(含文件夹前缀),<file_name>为本地保存路径。下载后可用 VS Code 打开 JSON 文件,内容应包含 GPS 位置详情:

{"device_id": "gps-sensor", "timestamp": "2021-05-21T00:57:53.878Z", "gps": {"lat": 47.73092, "lon": -122.26206}}

实操:将函数应用部署到云端

本地验证成功后即可部署:

  1. 创建新的 Azure Functions 应用,使用之前创建的存储账户,命名类似gps-sensor-<unique>(如随机单词或你的名字),参照项目 2 第 5 课《创建云资源》。
  2. 上传IOT_HUB_CONNECTION_STRINGSTORAGE_CONNECTION_STRING到 Application Settings,参照项目 2 第 5 课《上传 Application Settings》。
  3. 部署本地函数应用到云端,参照项目 2 第 5 课《部署函数应用》。部署后函数从 Application Settings 读取两个连接字符串,与本地行为一致。

七、挑战:应对 GPS 定位误差

GPS 数据并非绝对精确,定位可能偏差数米,在隧道与高楼密集区误差更大。思考:卫星导航系统是如何克服这一问题的?你的导航设备掌握了哪些数据,能让它对当前位置做出更准确的预测?


八、作业:调研函数绑定

函数绑定(Function bindings)允许代码通过从main函数返回值即可把 Blob 保存到 Blob 存储——存储账户、容器等细节配置在function.json中。作业要求阅读 Azure Functions 绑定文档,独立配置 Blob 输出绑定,让函数返回的 Blob 成功写入存储。

建议阅读的文档页:Azure Functions 触发器与绑定概念、Azure Blob 存储绑定概览、Azure Blob 存储输出绑定。评估标准(assignment.md):

标准优秀合格待改进
配置 Blob 存储输出绑定成功配置输出绑定,返回 Blob 并成功写入存储配置了输出绑定或返回了 Blob,但未能成功写入存储未能配置输出绑定

回顾与自学

  • 结构化数据、半结构化数据与非结构化数据的定义与差异;
  • Azure Storage 及其四种存储类型(Blob、表、队列、文件)的适用场景;
  • 本课核心链路:GPS 设备 → IoT Hub(Event Hub 兼容端点)→ Azure Functions(Event Hub 触发器)→ Azure Blob Storage(按设备分文件夹的 JSON Blob)。

本课全部代码与配置均可在仓库3-transport/lessons/2-store-location-data/code/下找到:设备端见wio-terminalpivirtual-device,云端见functions。下一课将基于这些落库的 GPS 数据,在地图上可视化卡车的完整行程。

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 4:32:34

ES集群脑裂与故障排查:Master选举机制与恢复实践

ES集群脑裂与故障排查&#xff1a;Master选举机制与恢复实践 1. ES集群脑裂问题概述&#xff1a;定义、成因与影响 Elasticsearch集群脑裂&#xff08;Split-Brain&#xff09;是指集群中的节点之间出现通信问题&#xff0c;导致集群分裂成多个独立的小集群&#xff0c;每个小集…

作者头像 李华
网站建设 2026/9/14 4:29:45

GoogleTest深入解析:断言宏、参数化测试与Bazel工程实践

简介&#xff1a;GoogleTest谷歌C测试框架是一套面向C开发者的开源单元测试解决方案&#xff0c;基于成熟的xUnit架构&#xff0c;能够自动发现并运行测试&#xff0c;省去手动注册的繁琐流程。除了一般的相等性、异常等断言外&#xff0c;还可以自定义断言&#xff0c;并借助致…

作者头像 李华
网站建设 2026/9/14 4:29:37

无人机通信安全:MAVLink AES-128-GCM加密实战指南

刚开始接触无人机组装和飞控开发的朋友&#xff0c;多半会碰上这么一件事&#xff1a;地面站和飞控之间用MAVLink协议通信&#xff0c;参数、航点、遥控指令全都明文在空中飞来飞去。懂点通信安全的人看一眼就会后背发凉——这意味着附近任何人拿一台接收机&#xff0c;就能把你…

作者头像 李华