ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

IoT-For-Beginners 交通运输模块指南:用 GPS、云存储与地理围栏实现食品运输全程追踪

2026/9/16 14:11:22 拓冰建站 浏览量
IoT-For-Beginners 交通运输模块指南:用 GPS、云存储与地理围栏实现食品运输全程追踪 IoT-For-Beginners 交通运输模块指南用 GPS、云存储与地理围栏实现食品运输全程追踪【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本篇文章围绕开源课程 IoT-For-Beginners 的第三个项目模块《从农场到工厂的运输》Transport from farm to factory展开系统讲解如何借助物联网技术追踪食品在运输途中的位置从 GPS 传感器采集经纬度到通过 Azure IoT Hub 上报、用 Serverless 函数落盘云存储再到在地图上可视化轨迹最终利用地理围栏Geofence实现车辆即将到达的自动告警。读完本文你将掌握一套完整可运行的车辆位置追踪端到端实现方案并理解供应链、物流与 IoT 结合的工程思路。模块背景为什么要在食品运输中引入 IoT很多农场主种植农作物用于出售无论是商业化农场主还是出售剩余产出以换取生活必需品的自给型农户食物都必须从农场运到消费者手中。这一过程通常依赖大宗运输从农场运往集散中心或加工厂再运往商店。以番茄为例农户收获番茄 → 装箱 → 装车 → 运抵加工厂 → 分拣 → 以加工食品、零售或餐厅原料的形式送达消费者。供应链Supply Chain是为生产并交付某样东西所需的一系列活动。以番茄种植为例它涵盖种子、土壤、肥料与水的供应番茄种植运往中央集散中心运往超市区域中心再运往具体超市上架陈列最终售卖给消费者带回家食用。每一步都像链条中的一环。 供应链中负责运输的部分称为物流Logistics例如农民把成箱番茄装上卡车、运到中央仓库、再装上可能混装多种农产品的第二辆卡车最终送往超市。IoT 能够帮助改善这条供应链追踪运输途中的食品确保司机行驶在正确路线、监控车辆位置、并在车辆到达时发出告警使食品能尽快卸货并进入下一步加工。本模块用 4 节课带你完成一个完整的虚拟货车追踪项目把 GPS 数据装入虚拟货车→ 途中持续追踪 → 存储与可视化 → 到站告警。模块课程总览本模块3-transport/目录包含 4 节课对应英文原版课程结构捷克语翻译见translations/cs/3-transport/课程主题核心内容第 1 课位置追踪Location trackingGPS 原理、地理坐标、NMEA 数据与解码第 2 课存储位置数据Store location data结构化/非结构化数据、IoT Hub 上报、Serverless 写入 Blob 存储第 3 课可视化位置数据Visualize location dataAzure Maps、GeoJSON 格式、网页地图打点第 4 课地理围栏Geofences地理围栏定义、点与围栏的包含关系判定、Serverless 集成 本模块会使用一些云资源。如果你不打算完成本项目的全部课程记得按 clean-up.md 清理云资源避免持续产生费用。第 1 课GPS 位置追踪的原理与实践连接车辆与车队管理IoT 正在通过连接车辆Connected Vehicles改变货物运输方式。这些车辆接入中央 IT 系统上报位置与其他传感器数据从而带来一系列收益位置追踪随时精确定位车辆——车辆即将到达时提前安排卸货班组、定位被盗车辆、结合路况中途重新规划路线、依据公/私道路里程合规计算税费、故障时派遣维修队。司机遥测确保司机遵守限速、合理过弯、提前高效制动配合车载摄像头记录事故可关联保险为优良司机提供折扣。工时合规根据发动机启停时间确保司机仅在法定可驾驶时长内行驶。这些收益可以组合例如把工时合规与位置追踪结合当司机在允许驾驶时长内无法到达目的地时重新派线也可以与冷藏车温度遥测结合避免因绕路导致货物温度失控。地理空间坐标经纬度基础GPS 传感器的核心输入是地球上某一点的定义即地理空间坐标Geospatial Coordinates。纬度Latitude用平行于赤道的纬线度量南北方向。赤道为 0°北极 90°N南极 -90°90°S南北半球各分 90°。经度Longitude度量东西方向。0° 起点称为本初子午线Prime Meridian1884 年定义为穿过英国格林尼治皇家天文台、连接南北两极的线经度从 -180°180°W经 0° 到 180°180°E。180° 与 -180° 是同一条线——对跖子午线Antimeridian。坐标始终写作纬度, 经度的顺序。例如美国华盛顿州雷德蒙德微软园区位于47.6423109, -122.1390293纬度 47.6423109赤道以北 47.6423109°经度 -122.1390293本初子午线以西 122.1390293°。六十进制 vs 十进制小数度传统上经纬度使用巴比伦人发明的六十进制度量1°60′1′60″。在赤道上1° 纬度 ≈111.3 公里1′ 纬度 ≈ 111.3/60 1.855 公里1″ 纬度 ≈ 1.855/60 0.031 公里例如2°1743写作 2 度 17 分 43 秒计算机不使用六十进制因此 GPS 数据中通常给出十进制小数度decimal degrees2°1743 2.295277。NMEA 原始数据则使用(dd)dmm.mmmm度分的小数形式表示。GPS 系统工作原理GPS 依赖环绕地球的多颗卫星。每颗卫星持续通过无线电波发送自身位置与精确时间戳GPS 传感器上的天线接收信号后利用信号到达耗时 × 无线电波恒定速度推算与卫星的距离结合至少 3 颗卫星的位置数据即可解算出自身在地球上的位置。由于卫星在运动而非悬停在头顶定位结果还会包含海拔高度。一些需要了解的工程细节天线的天空视野至关重要挡风玻璃或车顶是车载 GPS 天线常见安装位。GPS 精度曾受美国军方限制在约 5 米2000 年后限制解除理论上可达 30 厘米但实际会受信号干扰影响。卫星上的原子钟每天相对地面漂移约 38 微秒——这正是爱因斯坦狭义/广义相对论时间随速度变慢的预测证明GPS 系统设计时必须校正这一效应。除美国 GPS 外俄罗斯GLONASS、欧盟、日本、印度、中国等也部署了卫星系统现代 GPS 传感器可连接多个星座以获得更快更准的定位。读取 GPS 传感器数据大多数 GPS 传感器通过UART发送数据UART 详见 2-farm 项目第 2 课。课程为三类硬件分别提供了接线与读取指南Arduino Wio Terminalwio-terminal-gps-sensor.md树莓派单板机pi-gps-sensor.md虚拟设备CounterFit 模拟virtual-device-gps-sensor.mdNMEA 数据格式传感器输出的乱码其实是标准 NMEA 0183 格式 GPS 数据NMEA 为美国国家海洋电子协会制定、用于航海电子设备间通信的标准。每条消息是文本语句以$开头后接 2 个字符表示系统来源GP美国 GPSGNGLONASS 等再 3 个字符表示消息类型其余为逗号分隔的字段以换行结束。常见消息类型类型描述GGAGPS 定位数据纬度、经度、海拔、参与解算的卫星数ZDA当前日期与时间含本地时区GSV可见卫星详情 GPS 数据自带时间戳IoT 设备可以直接从 GPS 取时而不必依赖 NTP 服务器或内部 RTC。GGA 消息用(dd)dmm.mmmm格式携带位置外加一个方向字符纬度用N/S经度用E/W。以示例语句$GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67为例纬度字段4738.538654,N4738.538654换算为十进制小数度为 47.6423109方向N北为正即47.6423109经度字段12208.341758,W12208.341758即 122.1390293°方向W西为负即-122.1390293。解码 NMEA 数据直接消费原始 NMEA 字符串不如先解码成结构化数据。课程提供了两种解码方式Wio TerminalArduinowio-terminal-gps-decode.md树莓派/虚拟设备single-board-computer-gps-decode.md虚拟设备示例代码位于 code-gps-decode/virtual-device/gps-sensor/app.py使用 Python 的pynmea2库解析 NMEA 语句并通过pynmea2.dm_to_sd()把度分格式转换为小数度再依据lat_dir/lon_dir处理南纬/西经的符号msg pynmea2.parse(line) if msg.sentence_type GGA: lat pynmea2.dm_to_sd(msg.lat) lon pynmea2.dm_to_sd(msg.lon) if msg.lat_dir S: lat lat * -1 if msg.lon_dir W: lon lon * -1 print(f{lat},{lon} - from {msg.num_sats} satellites) 课后挑战不依赖第三方库自己编写一个 NMEA 解码器从语句中提取经纬度。第 2 课把位置数据存储到云端上一课采集到 GPS 数据后要支持地图可视化货车位置与轨迹必须把数据发送到云端 IoT 服务并落盘存储。结构化数据与非结构化数据结构化数据结构固定、一般不改变通常映射为带关系的数据表例如姓名 出生日期 地址。非结构化数据没有固定刚性结构结构可能频繁变化例如文档、电子表格。半结构化数据有结构但不适配固定表。IoT 数据通常被视为非结构化数据。设想大型商业农场车队拖拉机需要 GPS 确认在正确田地作业送货卡车除 GPS 外还需要速度、加速度、司机身份与启停数据以合规冷藏车还要温度数据防止货物变质。不同车辆数据类型不同且会变化如挂车更换后数据字段随之变化但都发往同一个 IoT 服务因此存储方案必须支持异构结构。SQL 数据库 vs NoSQL 数据库SQL 数据库关系型RDBMS有严格模式schema一组定义好的表每张表含多个命名列插入数据即添加行。表之间可建立关系如用户表 ID 关联订单表 user_id。代表产品Microsoft SQL Server、MySQL、PostgreSQL。适合存储结构固定、需要保证数据匹配模式的数据。NoSQL 数据库文档数据库没有预定义模式可直接插入任意非结构化数据通常以 JSON 文档形式组织在文件夹中不同文档可以有不同的字段。新增字段如带磅秤卡车记录货物重量无需修改数据库。代表产品Azure CosmosDB、MongoDB、CouchDB。本课使用 NoSQL 风格存储 IoT 数据。热路径、温路径与冷路径数据从设备到云并不总是实时处理热路径Hot path实时/准实时处理用于告警如车辆接近仓库、冷藏车温度过高。温路径Warm path接收后稍后处理用于日报或短期分析如前一日车辆里程报告数据先存入可快速访问的存储。冷路径Cold path历史数据长期存储用于年度里程报告、路线优化等通常由定时任务定期把温路径数据搬入数据仓库。本课中IoT Hub → 事件触发器 → Blob 存储的链路属于温路径数据存储下来供第 3 课做旅程报告。发送 GPS 数据到 IoT Hub复用上一项目2-farm 第 4 课的 Azure IoT Hub 使用方法用免费层新建 IoT Hub资源组命名为gps-sensorHub 名取基于gps-sensor的唯一名如gps-sensor-你的名字已有 Hub 也可复用。在 Hub 中新增设备gps-sensor获取设备连接字符串。修改设备代码把 GPS 数据以 JSON 上报到该 Hub{ gps : { lat : latitude, lon : longitude } }每分钟发送一次避免用尽每日消息配额。Wio Terminal 使用 ArduinoJson 构造报文需配置 NTP 校时、确保串口读净后再发送DynamicJsonDocument doc(1024); doc[gps][lat] gps.location.lat(); doc[gps][lon] gps.location.lng();树莓派/虚拟设备使用 Python Azure IoT SDKmessage_json { gps : { lat:lat, lon:lon } } print(Sending telemetry, message_json) message Message(json.dumps(message_json))用az iot hub monitor-eventsCLI 命令确认消息持续进入 IoT Hub。用 Serverless 代码处理 GPS 事件数据进入 IoT Hub 后编写 Azure Functions 监听其 Event Hub 兼容端点用 Azure Functions CLI 创建 Python 运行时项目目录名gps-trigger并建立虚拟环境。添加使用 IoT Hub Event Hub 兼容端点的 IoT Hub 事件触发器。在local.settings.json配置 Event Hub 兼容端点连接字符串在function.json中引用对应键。使用 Azurite 作为本地存储模拟器。运行函数应用确认收到设备事件Python EventHub trigger processed an event: {gps: {lat: 47.73481, lon: -122.25701}}Azure 存储账户与四种存储类型Azure Storage Accounts 是通用存储服务可同时以多种方式存数据Blob 存储Blob 指二进制大对象binary large objects已泛指任何非结构化数据可存 JSON、图片、视频以容器container为命名桶容器内可用文件夹分层。本课用它存 IoT 数据。表存储Table storage实为 NoSQL 数据库用唯一键定义每行适合半结构化数据。队列存储Queue storage存储最大 64KB 的消息先进先出可长期积压如每月任务可先每日入队、月底批量处理。文件存储File storage云中文件系统支持业界标准协议挂载如 PC/Mac 映射为驱动器。把 Serverless 代码连接到 Blob 存储本课采用Python SDK而非输出绑定写入 Blob。完整实现见 iot-hub-trigger/init.py要点如下创建存储账户如gps你的名字用以下命令获取连接字符串并写入local.settings.json的STORAGE_CONNECTION_STRINGaz storage account show-connection-string --output table --name storage_name在requirements.txt添加azure-storage-blob并安装若报错先pip install --upgrade pip。导入依赖并实现容器不存在则创建的辅助函数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_accessPublicAccess.Container)说明Python Blob SDK 没有创建容器若不存在的辅助方法这里手动遍历判断新容器授予公开读权限供第 3 课网页直接读取。每个事件都存储不同于土壤湿度只存阈值附近数据在main的事件循环内生成按设备分文件夹的 Blob 名device_id event.iothub_metadata[connection-device-id] blob_name f{device_id}/{str(uuid.uuid1())}.json例如设备gps-sensor的 Blob 名形如gps-sensor/a9487ac2-b9cf-11eb-b5cd-1e00621e3648.json。组装 Blob 内容并上传——注意时间戳取消息的enqueuedtime入队时间而非当前时间因为函数未运行时消息可能在 Hub 上滞留container_client get_or_create_container(gps-data) blob container_client.get_blob_client(blob_name) 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] } logging.info(fWriting blob to {blob_name} - {blob_body}) blob.upload_blob(json.dumps(blob_body).encode(utf-8))落盘 JSON 格式{ device_id: device_id, timestamp : time, gps : { lat : latitude, lon : longitude } }运行函数后输出中会看到类似Writing blob to gps-sensor/4b6089fe-...-3648.json - {device_id: gps-sensor, timestamp: 2021-05-21T00:57:53.878Z, gps: {lat: 47.73092, lon: -122.26206}}的日志注意不要同时运行 IoT Hub 事件监视器二者共享$Default消费组。验证已上传的 Blob用az storage account keys list --output table --account-name storage_name取key1然后az storage blob list --container-name gps-data \ --output table \ --account-name storage_name \ --account-key key1下载单个 Blobaz storage blob download --container-name gps-data \ --account-name storage_name \ --account-key key1 \ --name blob_name \ --file file_name打开下载的 JSON 即可看到完整 GPS 记录如{device_id: gps-sensor, timestamp: 2021-05-21T00:57:53.878Z, gps: {lat: 47.73092, lon: -122.26206}}。部署到云端创建 Functions App复用此前存储账户命名如gps-sensor-唯一标识把IOT_HUB_CONNECTION_STRING与STORAGE_CONNECTION_STRING上传到 Application Settings再部署本地函数。 课后挑战GPS 精度有限隧道、高楼区可能偏差数米思考卫星导航如何结合已有数据做出更好的位置预测。第 3 课在地图上可视化位置数据数据可视化与地图服务数据可视化的目标是让人更快理解数据、更快决策。以农场项目的土壤湿度为例一张按小时记录的数据表是无意义的数字墙画成折线图并叠加湿度达 450 时自动浇灌开启的参考线后规律一目了然。对 GPS 数据而言最清晰的可视化就是把点画在地图上——加工厂的工人可以看到货车何时到达若地图还标注车辆装载内容如冷藏车还能提前准备冷藏库位。地图服务有很多选择Bing Maps、Leaflet、OpenStreetMap、Google Maps本课选用Azure Maps。它提供多种地图样式空白画布、瓦片、卫星图、叠加道路的卫星图、灰度、带阴影浮雕、夜间、高对比度支持事件栅格实时更新、气泡/线/多边形/热力图层等渲染以及 REST API、Web SDK 和 Android SDK。本课用Web SDK在网页上绘制地图并展示 GPS 轨迹。创建 Azure Maps 资源在gps-sensor资源组创建 Maps 账户S1 为付费层含较充裕的免费调用额度az maps account create --name gps-sensor \ --resource-group gps-sensor \ --accept-tos \ --sku S1获取 API KeyPrimaryKeyaz maps account keys list --name gps-sensor \ --resource-group gps-sensor \ --output table在网页上显示地图创建一个单文件index.html完整代码见 code/index.html页面结构body onloadinit()内放一个idmyMap的div并让地图占满页面。引入 Web SDK 的样式与脚本link relstylesheet hrefhttps://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css typetext/css / script srchttps://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js/script用atlas.Map初始化地图并居中到西雅图附近把subscription_key替换为你的 API Keyfunction init() { var map new atlas.Map(myMap, { center: [-122.26473, 47.73444], zoom: 12, authOptions: { authType: subscriptionKey, subscriptionKey: subscription_key, } }); } 本地开发建议用http-server需先装 node.js 与 npm在index.html所在目录运行http-server访问http://127.0.0.1:8080/。GeoJSON 格式Azure Maps 要求数据采用GeoJSON——一个开放的 JSON 规范专为地理数据设计。其核心是嵌套结构FeatureCollection包含若干FeatureFeature内有geometrygeometry的coordinates给出坐标{ type: FeatureCollection, features: [ { type: Feature, geometry: { type: Point, coordinates: [ -2.10237979888916, 57.164918677004714 ] } } ] }⚠️注意经纬度顺序GeoJSON 中 Point 的坐标顺序是lon, lat经度在前、纬度在后与课程中纬度, 经度的通用写法相反写错会导致点位出现在错误位置。geometry类型可以多样化点、多边形等Azure Maps 还支持 GeoJSON 的扩展特性如绘制圆形。用 GeoJSON 在地图上绘制 GPS 数据第一步为存储开启 CORS。浏览器直接 fetch 存储会报错需显式允许跨域读取az storage cors add --methods GET \ --origins * \ --services b \ --account-name storage_name \ --account-key key1其中--origins *表示允许任何网站--services b表示仅作用于 Blob。第二步从存储加载 GPS 数据并渲染。替换init函数先用fetch请求容器列表 URLrestypecontainercomplist拿到每个 Blob 的 URL再逐个调用loadJSON解析地图ready事件中创建DataSource并添加BubbleLayer气泡图层渲染所有点fetch(https://storage_name.blob.core.windows.net/gps-data/?restypecontainercomplist) .then(response response.text()) .then(str new window.DOMParser().parseFromString(str, text/xml)) .then(xml { let blobList Array.from(xml.querySelectorAll(Url)); blobList.forEach(async blobUrl { loadJSON(blobUrl.innerHTML) }); }) .then( response { map new atlas.Map(myMap, { center: [-122.26473, 47.73444], zoom: 14, authOptions: { authType: subscriptionKey, subscriptionKey: subscription_key, } }); map.events.add(ready, function () { var source new atlas.source.DataSource(); map.sources.add(source); map.layers.add(new atlas.layer.BubbleLayer(source)); source.add(features); }) })loadJSON用XMLHttpRequest抓取单个 Blob解析 JSON 后按lon, lat顺序构造atlas.data.Featurevar map, features; function loadJSON(file) { var xhr new XMLHttpRequest(); features []; xhr.onreadystatechange function () { if (xhr.readyState XMLHttpRequest.DONE) { if (xhr.status 200) { gps JSON.parse(xhr.responseText) features.push( new atlas.data.Feature(new atlas.data.Point([parseFloat(gps.gps.lon), parseFloat(gps.gps.lat)])) ) } } }; xhr.open(GET, file, true); xhr.send(); }刷新页面后地图会沿数据轨迹显示一串气泡标记示例输出为西雅图附近圣爱德华州立公园的环形路径。 课后挑战把静态标记升级为动画利用带时间戳的 JSON 文件按时间顺序展示路径。第 4 课用地理围栏实现到达即告警前三步完成后下一步是效率提升的关键当货车即将到达加工枢纽时发出告警让卸货班组带着叉车等设备提前就位避免货车和司机空等。什么是地理围栏地理围栏Geofence是现实地理区域的虚拟周界可以是点 半径的圆形如某建筑周围 100 米也可以是覆盖校区、市区范围的多边形。你可能早已用过iOS 提醒事项或 Google Keep 的位置提醒本质就是地理围栏。判定车辆在围栏内外的典型用途卸货准备车辆到场即通知减少等待司机一天可跑更多趟税费合规如新西兰按公共道路行驶里程对柴油车征税围栏可区分公共道路与农场/林场内部道路里程防盗监控车辆离开限定区域如农场可能被盗区域合规防止载有化肥、农药的车辆进入有机作物种植区。Azure Maps 支持定义围栏并对点做包含判定。用 GeoJSON 定义地理围栏围栏与第 3 课的点一样用 GeoJSON 描述区别在于geometry类型是Polygon。多边形coordinates是一个由[lon, lat]点对组成的数组的数组且首尾点相同以闭合多边形矩形因此有 5 个点{ type: FeatureCollection, features: [ { type: Feature, geometry: { type: Polygon, coordinates: [ [ [-122.13393688201903, 47.63829579223815], [-122.13389128446579, 47.63782047131512], [-122.13240802288054, 47.63783312249837], [-122.13238388299942, 47.63829037035086], [-122.13393688201903, 47.63829579223815] ] ] }, properties: { geometryId: 1 } } ] }必须在与geometry平级的位置添加含geometryId的properties同一文件可上传多个围栏每个geometryId必须唯一。上传围栏并获取 UDID围栏必须先上传到 Azure Maps 账户获得唯一标识UDID后才能用于判定。上传使用 Maps Web API通过curl调用curl --request POST https://atlas.microsoft.com/mapData/upload?api-version1.0dataFormatgeojsonsubscription-keysubscription_key \ --header Content-Type: application/json \ --include \ --data geofence.json上传是异步的响应头里的location给出了查询进度的 URL。用curl --request GET locationsubscription-keysubscription_key轮询直到status为Succeeded再从resourceLocation中提取 UDID——即metadata/之后、api-version之前的那段 UUID例如7c3776eb-da87-4c52-ae83-caadf980323a。用 searchBuffer 与 distance 判定点与围栏的关系判定接口为https://atlas.microsoft.com/spatial/geofence/json?api-version1.0deviceIdgps-sensorsubscription-keysubscription-keyudidUDIDlatlatlonlon可额外传searchBufferdistance默认 50 米可设 0–500 米来告诉 API 期望的精度——因为 GPS 本身有数米误差。响应中的geometries数组每个元素含三个关键字段nearestLat/nearestLon围栏边界上离被测点最近点的经纬度distance被测点到围栏最近边界的距离负值在围栏内正值在围栏外若距离超过 searchBuffer则返回999外部或-999内部。典型响应{ geometries: [ { deviceId: gps-sensor, udId: 7c3776eb-da87-4c52-ae83-caadf980323a, geometryId: 1, distance: 999.0, nearestLat: 47.645875, nearestLon: -122.142713 } ], expiredGeofenceGeometryId: [], invalidPeriodGeofenceGeometryId: [] }⚠️ 若响应为BadRequest且提示Invalid GeoJSON: All feature properties should contain a geometryId...说明 GeoJSON 缺少properties.geometryId需修正后重新上传并获取新 UDID。决策时需谨慎单个 GPS 读数可能因误差把车放进围栏例如沿 520 公路行驶的货车有一个点落在微软园区围栏内但该处并无道路可达。工程上应结合历史读数、速度与道路数据综合判断而非单独采信单点结果。消费组Consumer Groups多触发器并行消费IoT Hub 允许重放已接收未处理的事件但当多个触发器同时连接时Hub 无法区分谁处理过哪条消息——解决办法是消费组Consumer Groups每个应用组件连接不同的消费组各自独立管理未读消息重放。理论上一组最多 5 个应用连接但最佳实践是每个消费组只由一个应用消费避免重复处理例如本地与云端同时运行会写入重复 Blob。默认 IoT Hub 自带$Default消费组。为新增的围栏触发器创建独立消费组az iot hub consumer-group create --name geofence \ --hub-name hub_name查看消费组列表az iot hub consumer-group list --output table --hub-name hub_name用 Serverless 触发器判定围栏在gps-trigger函数应用中新增geofence-trigger触发器并在 function.json 中把绑定改为新消费组{ type: eventHubTrigger, name: events, direction: in, eventHubName: samples-workitems, connection: IOT_HUB_CONNECTION_STRING, cardinality: many, consumerGroup: geofence, dataType: binary }同时在local.settings.json增加MAPS_KEYMaps 订阅密钥与GEOFENCE_UDID围栏 UDID。核心逻辑见 geofence-trigger/init.py——Azure Maps 没有官方 Python SDK因此用requests直接调用 Web APImaps_key os.environ[MAPS_KEY] geofence_udid os.environ[GEOFENCE_UDID] for event in events: event_body json.loads(event.get_body().decode(utf-8)) lat event_body[gps][lat] lon event_body[gps][lon] url https://atlas.microsoft.com/spatial/geofence/json params { api-version: 1.0, deviceId: gps-sensor, subscription-key: maps_key, udid : geofence_udid, lat : lat, lon : lon } response requests.get(url, paramsparams) response_body json.loads(response.text) distance response_body[geometries][0][distance] if distance 999: logging.info(Point is outside geofence) elif distance 0: logging.info(fPoint is just outside geofence by a distance of {distance}m) elif distance -999: logging.info(Point is inside geofence) else: logging.info(fPoint is just inside geofence by a distance of {distance}m)运行函数应用可先用az functionapp stop/az functionapp start暂停云端函数避免重复写入日志会显示 GPS 点位于围栏内/外及距边界的米数移动传感器或改动虚拟设备坐标即可看到判定结果变化。就绪后把新 Application Settings 一起部署到云端。 课后挑战一次上传含多个多边形的 GeoJSON 文件扩展代码找出 GPS 坐标距离最近/所在的围栏。 这是本项目最后一课完成课后作业assignment.md后记得按 clean-up.md 清理云服务。配套练习与学习建议每节课都配有课前/课后测验、动手任务、挑战题与作业第 1 课作业 assignment.md调研其他 GPS 数据第 2 课作业 assignment.md调研函数绑定第 3 课作业 assignment.md部署你的 Web 应用第 4 课作业 assignment.md用 Twilio 发送短信通知。若需复习前置知识可回看 2-farm 项目中的 IoT Hub 创建、Serverless 应用创建与事件触发器、UART 通信 等章节。所有课程代码均随仓库提供包括 GPS 读取/解码、存储函数与地图页面可直接对照学习与运行验证。本模块所有课程由 Jen Looper 与 Jim Bennett 编写并提供了捷克语等多语言翻译版本见translations/cs/3-transport/等目录。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考