IoT-For-Beginners 运输模块实战:用 IoT 与 GPS 追踪农产品运输全链路

发布时间:2026/9/15 12:51:47
IoT-For-Beginners 运输模块实战:用 IoT 与 GPS 追踪农产品运输全链路 IoT-For-Beginners 运输模块实战用 IoT 与 GPS 追踪农产品运输全链路【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners在农产品从农场到工厂加工再到消费者手中的过程中运输环节物流是供应链中最依赖实时信息的一环。本指南聚焦 GitHub 推荐项目精选 / io / IoT-For-Beginners 中的 Transport运输模块系统讲解如何用物联网设备为运货卡车装上 GPS「眼睛」从读取 GPS 传感器数据、通过 NMEA 协议解析经纬度到把数据送入 Azure IoT Hub、用无服务器代码存入 Blob 存储再到用 Azure Maps 在地图上可视化行驶轨迹最后利用地理围栏Geofence在卡车抵达目的地时自动触发通知。读完本指南你将掌握一套从传感器采集、云端存储、地图可视化到地理围栏告警的完整端到端 IoT 追踪方案。本模块对应的英文原文档位于 3-transport/README.md阿拉伯语译本即本次关联文档 translations/ar/3-transport/README.md。本文以模块为主线深度结合四课内容与仓库源码展开。为什么需要物联网追踪食物运输许多农民种植食物用于出售——无论是出售全部收成的商业农场主还是出售多余农产品以换取必需品的自给型农民。食物必须从农场送达消费者手中这通常依赖大宗运输从农场到枢纽或加工厂再到商店。例如一个番茄农场主会收获番茄、装箱、把箱子装上卡车然后运往加工厂番茄在那里被分拣再以加工食品、零售或餐厅消耗的形式送达消费者。物联网可以在这条供应链中发挥关键作用具体方式包括确保司机前往正确的地点实时定位每辆车及时发现偏离路线的情况监控车辆位置让调度中心随时掌握车队分布车辆到达即告警一旦卡车接近目的地立即通知卸货团队做好准备尽可能缩短等待时间让食物尽快进入加工环节。供应链Supply Chain是制造并交付某样东西所需的系列活动序列。以番茄种植为例它涵盖种子、土壤、肥料与水的供应、种植番茄、把番茄送往中心枢纽、运输到超市区域枢纽、再运到单个超市、上架陈列、最终卖给消费者并带回家食用。每一步都像链条上的一个环节。 供应链中的运输部分被称为物流Logistics。模块结构四课循序渐进的完整技术路线本模块包含四节课构成一条完整的技术链路课程主题核心技术第 1 课位置追踪GPS 原理、地理坐标、NMEA 协议、传感器数据读取与解码第 2 课存储位置数据结构化/非结构化数据、IoT Hub 接入、热/温/冷数据路径、Azure Functions、Blob 存储第 3 课可视化位置数据数据可视化、Azure Maps、GeoJSON 格式、网页地图标记第 4 课地理围栏地理围栏定义与测试、消费者组Consumer Groups、无服务器地理围栏检测整个模块围绕一台虚拟卡车从装货到抵达目的地的旅程展开你学到的每项技能都直接服务于监控食物运输这一目标。第 1 课位置追踪——GPS 传感器的核心原理联网车队带来的业务价值给车队装上 GPS 传感器后车辆就变成了联网车辆Connected Vehicles持续向中央 IT 系统上报位置与其他传感器数据。这带来广泛收益位置追踪随时定位车辆用于到达告警、定位被盗车辆、结合路况中途重新规划路线、满足按公路里程计税的国家如新西兰的 RUC 道路使用费的合规要求、在车辆抛锚时调度维修团队驾驶员遥测确保司机遵守限速、合理过弯、提前高效刹车摄像头记录事故还可与保险联动获得优惠费率驾驶时长合规根据发动机启停时间确保司机不超过法定驾驶时长。这些收益还可以组合使用例如把驾驶时长合规与位置追踪结合在司机无法于法定驾驶时间内到达目的地时重新规划路线或与冷藏车的温度遥测结合在路线可能导致货物无法保温时改道。地理空间坐标经纬度与度分秒GPS 定位的基础是地理坐标。地球被划分为 360 度纬度Latitude度量南北方向的角度经度Longitude度量东西方向的角度。纬度用平行于赤道的线度量赤道为 0°北极 90°90° North南极 -90°90° South经度以本初子午线Prime Meridian为 0° 起点1884 年定义为穿过英国格林尼治皇家天文台的南北线向西到 -180°180° West向东到 180°180° East180° 与 -180° 是同一个点即对跖子午线antimeridian。在赤道附近1° 纬度约等于111.3 公里1 分约 1.855 公里1 秒约 0.031 公里。传统上坐标使用六十进制度分秒如 2°1743但计算机系统普遍使用十进制度数2°1743 ≈ 2.295277。坐标始终以latitude, longitude顺序给出例如 Microsoft 园区位于 47.6423109, -122.1390293。GPS 卫星定位原理GPS 通过环绕地球的多颗卫星定位卫星持续广播自身位置和精确时间戳GPS 传感器接收后根据无线电波恒定速度推算与卫星的距离综合至少 3 颗卫星的数据即可在地球上精确定位。卫星并非固定在传感器上方因此定位数据还包含海拔高度。关于精度有一个值得了解的背景GPS 过去受美国军方限制精度约为 5 米2000 年取消限制后理论上可达 30 厘米但信号干扰会使实际精度下降。卫星搭载的原子钟每天相对地面漂移 38 微秒相对论效应GPS 系统设计时必须校正这一偏差。目前美国、俄罗斯、日本、印度、欧盟、中国等都已部署各自的卫星系统称为星座现代 GPS 传感器可连接多个系统以获得更快更准的定位。读取 GPS 数据UART 与 NMEA 协议大多数 GPS 传感器通过UART串口发送数据UART 相关内容在 2-farm 项目土壤湿度课程 中有详细介绍。针对不同硬件平台仓库提供了三种读取指南Arduino - Wio Terminal 读取指南树莓派读取指南虚拟设备读取指南虚拟设备的示例代码位于 code-gps/virtual-device/gps-sensor/app.py其核心逻辑是循环读取串口数据并逐行打印serial counterfit_shims_serial.Serial(/dev/ttyAMA0) while True: line serial.readline().decode(utf-8) while len(line) 0: print_gps_data(line) line serial.readline().decode(utf-8) time.sleep(1)运行后终端输出的乱码其实是标准的NMEA 消息。NMEA 0183 是美国国家海洋电子协会National Marine Electronics Association制定的海事电子通信标准。每条消息以$开头后跟 2 个字符表示消息来源GP代表美国 GPS 系统GN代表俄罗斯 GLONASS再跟 3 个字符表示消息类型其余为逗号分隔的字段以换行符结束。常见的 NMEA 消息类型类型说明GGAGPS 定位数据包括纬度、经度、海拔以及计算该定位所用的可见卫星数量ZDA当前日期与时间含本地时区GSV可见卫星的详细信息GPS 传感器能检测到信号的卫星 GPS 数据自带时间戳因此 IoT 设备可以从 GPS 传感器直接获取时间而不必依赖 NTP 服务器或内部实时时钟。GGA 消息中的位置字段采用(dd)dmm.mmmm格式后面跟一个方向字符纬度用N/S经度用E/W。以 NMEA 语句$GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67为例纬度部分4738.538654,N换算为十进制度数 47.6423109方向为 N北半球纬度为正值经度部分12208.341758,W换算为 -122.1390293方向为 W西经经度为负值。为了把原始 NMEA 数据转换成可用格式课程推荐使用开源库进行解码并在 挑战 中鼓励读者手写一个自己的 NMEA 解码器来提取经纬度。解码指南见 Wio Terminal 解码指南 与 树莓派/虚拟设备解码指南。第 2 课存储位置数据——从传感器到云端存储有了 GPS 数据之后要在地图上可视化卡车位置与轨迹就必须把数据发送到云端 IoT 服务并存储起来。本课解决存到哪里、怎么存的问题。结构化数据与无监督数据IoT 数据为何属于后者计算机系统处理的数据可分为两类结构化数据Structured data结构定义明确、固定不变通常映射为带关系的表。例如包含姓名、出生日期、地址的用户信息非结构化数据Unstructured data没有固定刚性结构结构可频繁变化例如文档、电子表格还存在介于两者之间的半结构化数据。IoT 数据通常被视为非结构化数据。设想一个大型商业农场的车队拖拉机需要 GPS 确保作业在正确的地块运输卡车的 GPS 之外还要速度、加速度、驾驶员身份、启停数据以保障安全驾驶与工时合规冷藏车还需要温度数据防止食物变质。这些数据随车辆类型不断变化但都会发送到同一个 IoT 服务处理——因此存储方案必须既能支持检索与分析又能容纳千变万化的数据结构。SQL 与 NoSQL两种存储范式SQL 数据库关系型数据库管理系统如 Microsoft SQL Server、MySQL、PostgreSQL由 schema 定义一组固定表插入数据即向各列填入行。它适合存储结构化数据、确保数据符合 schema但新增列需要在数据库层面操作并为既有行填充值。NoSQL 数据库如 Azure CosmosDB、MongoDB、CouchDB没有预定义的 schema可存储任意非结构化数据通常为 JSON 文档文档可组织成文件夹。每个文档字段可以不同——例如农场车辆数据有的有加速度和速度字段有的有车厢温度字段。若新增一种带车载秤的卡车IoT 设备只需添加新字段即可直接存储无需修改数据库。本课采用 NoSQL 存储 IoT 数据。发送 GPS 数据到 Azure IoT Hub本模块沿用前一个项目2-farm使用的 Azure IoT Hub 作为云端 IoT 服务。操作要点使用免费层新建 IoT Hub资源组命名为gps-sensorIoT Hub 命名为基于gps-sensor的唯一名称如gps-sensor-你的名字也可复用之前项目的 IoT Hub添加名为gps-sensor的新设备并获取设备连接字符串修改设备代码以 JSON 格式发送 GPS 数据{ gps : { lat : latitude, lon : longitude } }每分钟发送一次以免耗尽每日免费消息配额。虚拟设备端的完整发送逻辑位于 code/virtual-device/gps-sensor/app.py它先用pynmea2解析 NMEA 语句、处理南纬/西经的符号再构造 JSON 消息发送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 message_json { gps : { lat:lat, lon:lon } } print(Sending telemetry, message_json) message Message(json.dumps(message_json)) device_client.send_message(message)运行后用az iot hub monitor-eventsCLI 命令即可确认消息是否流入 IoT Hub。热路径、温路径与冷路径从 IoT 设备流向云端的数据并非都要实时处理。按处理时效可将数据流划分为三种路径热路径Hot path需实时或近实时处理的数据用于告警——例如车辆接近仓库、冷藏车温度过高的即时提醒温路径Warm path可稍后处理的数据用于报表或短期分析——例如用前一天数据生成车辆里程日报数据收到后存入可快速访问的存储冷路径Cold path历史数据长期存储、按需处理——例如年度里程报表、分析最优路线以降低油耗。冷路径数据存放在数据仓库中通常由云端定时任务每日/每周/每月把温路径数据搬运进去。在本模块中GPS 数据被送入 IoT Hub 后由无服务器代码监听 Event Hub 兼容端点这正是温路径——数据被存储供第 3 课报表与可视化使用。用无服务器代码处理 GPS 事件使用 Azure Functions CLI 创建 Functions 应用Python 运行时项目文件夹gps-trigger并创建虚拟环境添加使用 IoT Hub Event Hub 兼容端点的 IoT Hub 事件触发器在local.settings.json中配置 Event Hub 兼容端点连接字符串并在function.json中引用对应键使用 Azurite 作为本地存储模拟器运行 Functions 应用确认收到设备事件Python EventHub trigger processed an event: {gps: {lat: 47.73481, lon: -122.25701}}Azure 存储账户与 Blob 存储Azure Storage Accounts 是通用存储服务可同时以四种方式存储数据Blob 存储BlobBinary Large Objects指任意非结构化数据可存 JSON 文档、图片、视频等以容器container为命名桶容器内可有多级文件夹。本课用 Blob 存储保存 IoT 数据表存储半结构化数据本质是 NoSQL 数据库按唯一键定义行队列存储存储不超过 64KB 的消息队尾入队、队首出队只要存储空间充足消息就长期保留——例如每天把 GPS 数据入队一个月月底一次性处理文件存储云端文件存储应用或设备可用行业标准协议连接甚至可挂载为本地驱动器。连接无服务器代码到存储两种方式函数应用连接 Blob 存储有两种途径在函数代码中用 Blob 存储 Python SDK 直接连接并写入 Blob使用输出函数绑定把函数返回值自动绑定保存为 Blob。本课采用第一种方式以展示 SDK 用法。保存的 JSON Blob 格式为{ device_id: device_id, timestamp : time, gps : { lat : latitude, lon : longitude } }实际操作步骤对应 iot-hub-trigger/init.py 的实现创建存储账户命名如gps你的名字用az storage account show-connection-string --output table --name storage_name获取连接字符串写入local.settings.json的STORAGE_CONNECTION_STRING在requirements.txt添加azure-storage-blob并安装添加导入import json、import os、import uuid、from azure.storage.blob import BlobServiceClient, PublicAccess定义get_or_create_container(name)辅助函数从环境变量读取连接字符串创建BlobServiceClient遍历容器查找同名容器存在则返回ContainerClient否则创建并返回新容器。新容器授予公共访问权限供第 3 课在网页地图上直接查询 Blobdef 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)与土壤湿度示例不同这里要保存每一个事件。在main的事件循环内从事件元数据获取设备 ID并用uuid生成唯一文档名——设备 ID 作为文件夹名例如gps-sensor/a9487ac2-b9cf-11eb-b5cd-1e00621e3648.jsondevice_id event.iothub_metadata[connection-device-id] blob_name f{device_id}/{str(uuid.uuid1())}.json container_client get_or_create_container(gps-data) blob container_client.get_blob_client(blob_name)构造 Blob 内容并上传。注意时间戳应使用消息的入队时间enqueued time而非当前时间——如果 Functions 应用未运行消息可能在 Hub 上滞留一段时间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))运行 Functions 应用后输出类似[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 storage account keys list获取账户密钥key1再用az storage blob list --container-name gps-data --output table --account-name storage_name --account-key key1列出容器内所有 Blob最后用az storage blob download下载某个 Blob在 VS Code 中查看 JSON 内容部署到云端新建 Functions 应用复用前面创建的存储账户把IOT_HUB_CONNECTION_STRING与STORAGE_CONNECTION_STRING上传到 Application Settings然后部署本地 Functions 应用。第 3 课可视化位置数据——用 Azure Maps 绘制行车轨迹数据可视化的价值数据可视化的本质是让人类更容易理解数据、更快做决策。课程用一个贴切的例子说明一张每小时记录的土壤湿度数字表让人难以理解画成折线图后一目了然再加上一条 450 阈值的参考线就能立刻看出自动浇水系统在哪些时刻启动对比见 chart-soil-moisture.png 与 chart-soil-moisture-relay.png。最好的可视化能让人类快速决策——有时一个闪烁的红灯就是最好的可视化。对于 GPS 数据最清晰的可视化就是把数据点标在地图上显示送货卡车位置的实时地图能让加工厂的工人预判卡车到达时间如果地图还能显示卡车装载内容如冷藏车工人就能提前准备冷藏空间。创建 Azure Maps 资源Azure Maps 是一组使用最新地图数据的空间服务与 SDK为 Web 和移动应用提供地理上下文支持多种地图样式空白画布、瓦片、卫星图、卫星道路叠加、灰度、地形阴影、夜间视图、高对比度地图可与 Azure Event Grid 集成获得实时更新并支持气泡、线、多边形、热力图等图层。可通过 REST API、Web SDK 或 Android SDK 访问。创建地图资源与获取 API 密钥az maps account create --name gps-sensor \ --resource-group gps-sensor \ --accept-tos \ --sku S1 az maps account keys list --name gps-sensor \ --resource-group gps-sensor \ --output tableS1是付费层级包含一系列功能且提供较充裕的免费调用额度。记下PrimaryKey作为订阅密钥。在网页上显示地图创建一个index.html先放置地图容器与样式再引入 Web SDK 的样式表与脚本相当于 C 的头文件或 Python 的模块导入html head 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 style #myMap { width:100%; height:100%; } /style /head body onloadinit() div idmyMap/div /body /html再用init()函数实例化地图并居中到 Seattle 地区subscription_key替换为刚才的 API 密钥function init() { var map new atlas.Map(myMap, { center: [-122.26473, 47.73444], zoom: 12, authOptions: { authType: subscriptionKey, subscriptionKey: subscription_key, } }); } 本地开发 Web 应用更规范的方式是安装http-server需要 node.js 与 npm在index.html所在目录执行http-server然后访问本地服务器地址查看。GeoJSON 格式地图数据的事实标准GeoJSON 是面向地理数据专门设计的开放标准 JSON 规范。示例{ type: FeatureCollection, features: [ { type: Feature, geometry: { type: Point, coordinates: [ -2.10237979888916, 57.164918677004714 ] } } ] }Feature嵌套在FeatureCollection中geometry.coordinates记录经纬度。关键陷阱GeoJSON 中坐标的顺序是lon, lat经度在前、纬度在后而不是习惯的lat, lon写反会导致标记点位置错误。geometry可以是点、多边形等不同类型。Azure Maps 还支持标准 GeoJSON 之外的增强特性如绘制圆形。从存储加载 GPS 数据并绘制路径要让网页读取存储账户中的 Blob首先必须为存储启用CORS跨域资源共享否则浏览器控制台会报错az storage cors add --methods GET \ --origins * \ --services b \ --account-name storage_name \ --account-key key1该命令允许任意网站通配符*对存储账户发起 GET 请求--services b表示仅对 Blob 生效。接着在init函数中用fetch调用 Blob 容器列表端点解析返回的 XML 拿到每个 Blob 的 URL再逐一下载并转换为 GeoJSON 要素。完整实现见 3-visualize-location-data/code/index.htmlfetch(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); }) })URL 中的restypecontainercomplist表示列出gps-data容器内所有 Blob 的信息不含内容只含每个 Blob 的可加载 URL。ready事件在地图显示后触发创建数据源并用气泡图层BubbleLayer在每个坐标点上画圆。loadJSON函数用 XMLHttpRequest 下载每个 Blob解析 JSON 后构造 GeoJSONFeature注意此处把gps.gps.lon放在前、gps.gps.lat放在后与 GeoJSON 的lon, lat顺序保持一致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(); }加载页面后地图会先渲染随后从存储加载全部 GPS 数据并沿轨迹绘制出气泡路径效果可参考 map-path.png。课程挑战环节建议进一步为这些带时间戳的 JSON 文件加入动画让标记随时间推移展示行进轨迹。第 4 课地理围栏——车辆到达即触发告警前三课解决了车在哪、轨迹如何的问题。本课更进一步在卡车即将抵达加工枢纽时主动告警让负责卸货的团队带着叉车等设备提前待命避免卡车和司机空等从而降低等待成本。什么是地理围栏地理围栏Geofence是现实地理区域的虚拟边界可以是圆心半径的圆如建筑周围 100 米也可以是覆盖某区域的多边形如校区、城市边界。你可能早已用过它——在 iOS 提醒事项或 Google Keep 中设置到达某地提醒本质就是手机进入地理围栏时触发通知。判断车辆是否进出地理围栏有大量实际用途卸货准备车辆到场即通知团队缩短等待时间让司机一天内完成更多趟配送税收合规一些国家如新西兰仅对在公共道路上行驶的柴油车辆按重量收取道路税地理围栏可区分公共道路与农场/林场内部道路的里程防盗监控车辆若离开仅限停留的区域如农场可能被盗位置合规工作区、农场或工厂的某些区域可能禁止某些车辆进入如装载化肥、农药的车辆不得靠近有机种植田车辆进入围栏即违规并通知司机。Azure Maps上一课的可视化服务允许定义地理围栏并测试某个点是否位于围栏内。用 GeoJSON 定义地理围栏地理围栏同样用 GeoJSON 定义只是geometry类型从Point变为Polygon{ 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 } } ] }要点多边形的每个点都是[经度, 纬度]对再次强调 GeoJSON 是lon, lat顺序coordinates数组的条目数总是比多边形顶点数多 1最后一项与第一项相同以闭合多边形——例如矩形需要 5 个点可参考 polygon-points.png 中 47,-122 → 47,-121 → 46,-121 → 46,-122 → 回到起点 47,-122 的示例必须在与geometry同级的properties中加入geometryId并在文件内唯一可以在同一文件里以多个Feature上传多个围栏只要geometryId不同。上传围栏并测试坐标点Azure Maps 要求先把 GeoJSON 上传到账户上传成功后返回唯一 UDID后续用该 UDID 测试坐标点。保存geofence.json后用 curl 调用上传 APIcurl --request POST https://atlas.microsoft.com/mapData/upload?api-version1.0dataFormatgeojsonsubscription-keysubscription_key \ --header Content-Type: application/json \ --include \ --data geofence.json响应头中的location字段指向一个处理状态查询地址Azure Maps 异步处理上传需用 GET 请求轮询状态在location后追加subscription-keysubscription_key直到返回status为Succeededcurl --request GET locationsubscription-keysubscription_key成功后从响应的resourceLocation中提取 UDID——即metadata/之后的 UUID不含api-version。例如{ resourceLocation: https://us.atlas.microsoft.com/mapData/metadata/7c3776eb-da87-4c52-ae83-caadf980323a?api-version1.0 }则 UDID 为7c3776eb-da87-4c52-ae83-caadf980323a。测试坐标与 searchBuffer 的距离语义测试某个点是否在围栏内的 API 是https://atlas.microsoft.com/spatial/geofence/jsonURL 格式https://atlas.microsoft.com/spatial/geofence/json?api-version1.0deviceIdgps-sensorsubscription-keysubscription-keyudidUDIDlatlatlonlon其中deviceId是必填参数应填经纬度来源设备的名称。响应中每个多边形geometries包含三个关键字段distance、nearestLat、nearestLon围栏边界上距测试点最近点的经纬度。searchBuffer搜索缓冲区用于抵消 GPS 的固有误差隧道、高楼区定位可能偏差数米甚至更多。默认 50 米可设置为 0500 米。distance是测试点到围栏边界最近点的距离正值在围栏外负值在围栏内若该距离小于搜索缓冲区则返回实际米数否则返回999点位于围栏外且超出缓冲区或-999点位于围栏内且深入超过缓冲区。例如一个 50 米缓冲区的围栏中心点距离为 -999缓冲区外的点距离为 999围栏内 6 米处距离为 6围栏外 39 米处距离为 39示意图见 search-buffer-and-distance.png。距离信息必须与其他信息综合判断不能单独依赖一次测试结果。课程给出典型场景GPS 读数显示车辆沿公路行驶但某一次读数因精度误差落进路旁围栏内。由于该处并无车辆可通行的道路这次进入围栏的读数显然不成立代码需要结合前后多次读数以及速度、道路数据再决定是否触发动作可参考 geofence-crossing-inaccurate-gps.png 中的 520 公路示例。如果收到BadRequest且错误为 Invalid GeoJSON: All feature properties should contain a geometryId...说明 GeoJSON 缺少带geometryId的properties修正后需重新上传并获取新 UDID。消费者组多个触发器如何并行消费事件问题来了IoT Hub 允许重放已接收未处理的事件但多个触发器连接时Hub 无法知道谁处理了哪些事件。解决方案是消费者组Consumer Groups——定义多个独立连接各自管理未读消息的重放每个应用组件连接不同的消费者组示意图见 consumer-groups.png。理论上每个消费者组最多可连接 5 个应用但最佳实践是每个消费者组只让一个应用访问以避免重复消息处理例如本地和云端同时运行 Functions 应用会向存储写入重复 Blob。创建 IoT Hub 时默认生成$Default消费者组此前课程用az iot hub monitor-events监控事件时连接的就是$Default这也是为什么不能同时运行事件监控和事件触发器——如需并行可让函数应用都使用其他消费者组把$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输出示例Name ResourceGroup -------- --------------- $Default gps-sensor geofence gps-sensor无服务器地理围栏检测在gps-trigger函数应用中添加新的 IoT Hub 触发器geofence-trigger将其function.json中的consumerGroup从默认值改为geofence完整配置见 geofence-trigger/function.jsonconsumerGroup: geofence在local.settings.json中加入MAPS_KEYAzure Maps 订阅密钥和GEOFENCE_UDID围栏 UDID。由于 Azure Maps 没有 Python SDK触发器用requests库直接调用 Web API。完整实现见 geofence-trigger/init.pymaps_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(fPoint is inside geofence) else: logging.info(fPoint is just inside geofence by a distance of {distance}m)运行后即可在日志中看到每个 GPS 坐标点是位于围栏内、围栏外还是距边界 50 米以内此时会输出精确距离。为避免与云端函数重复写 Blob可用az functionapp stop暂停云端函数应用需要时用az functionapp start重启。部署时记得上传新的 Application Settings。挑战环节鼓励读者一次上传包含多个多边形的 GeoJSON并修改代码判断坐标点最接近或位于哪个多边形内。硬件支持与虚拟设备整个模块支持三种运行环境每课都提供对应指南与代码Arduino - Wio TerminalC 代码src/main.cppplatformio.ini发送前需通过 NTP 服务器设置时间并确保串口数据已全部读取构造 JSON 用DynamicJsonDocument doc(1024); doc[gps][lat] gps.location.lat(); doc[gps][lon] gps.location.lng();单板计算机 - 树莓派Python 代码code/pi目录单板计算机 - 虚拟设备Python 代码code/virtual-device目录通过 CounterFit 模拟串口与 GPS 传感器适合无硬件的学习者。仓库中两处可直接对照的源码GPS 传感器读取 1-location-tracking/code-gps/virtual-device/gps-sensor/app.py 与云端存储 2-store-location-data/code/functions/gps-trigger/iot-hub-trigger/init.py它们共同展示了从串口读 NMEA、解析经纬度、上报 IoT Hub、经 Event Hub 触发器写入 Blob 的完整调用链。完成后的清理本模块使用了多项云端资源IoT Hub、Functions 应用、存储账户、Azure Maps。由于这是本项目的最后一组课程完成所有课程和作业后请务必按照 clean-up.md 的指引清理云服务避免产生不必要的费用。扩展阅读每课末尾都附有知识巩固环节第 1 课作业——研究其他 GPS 数据、第 2 课作业——研究函数绑定、第 3 课作业——部署你的应用、第 4 课作业——用 Twilio 发送通知前序项目 2-farm农场模块 中第 4、5 课提供了 IoT Hub 创建、设备连接、Functions 项目创建与部署等被本模块反复引用的前置操作模块级草图笔记sketchnotes可参考 sketchnotes/lesson-11.jpg 至 sketchnotes/lesson-14.jpg。结语至此你已完整掌握 IoT-For-Beginners 运输模块的四步方法论采集GPS 传感器与 NMEA 解析→ 传输与存储IoT Hub 无服务器函数 Blob→ 可视化Azure Maps GeoJSON→ 智能化地理围栏与到达告警。这套模式不仅适用于农产品运输稍加改造即可复用到冷链监控、车队管理、资产追踪等各类万物互联场景——而这正是该项目12 周、24 课、IoT for All理念中运输板块要传达的核心能力。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询