ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

IoT-For-Beginners 运输篇:使用 Azure Maps 地理围栏(Geofence)实现车辆到站提醒

IoT-For-Beginners 运输篇:使用 Azure Maps 地理围栏(Geofence)实现车辆到站提醒 IoT-For-Beginners 运输篇使用 Azure Maps 地理围栏Geofence实现车辆到站提醒【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners导读本文聚焦 IoT-For-Beginners 项目运输Transport专题的第 4 课讲解如何用 Azure Maps 地理围栏Geofence解决供应链中的一个关键问题当装载农产品的卡车即将抵达加工中心时提前通知卸货团队做好准备。你将掌握 GeoJSON 多边形围栏的定义与上传、用 UDID 对 GPS 坐标做内/外判定、理解searchBuffer与distance的语义并最终在无服务器函数Azure Functions中通过 IoT Hub 消费者组Consumer Group实现实时到站检测——这是本项目运输专题的收官课程。背景为什么需要地理围栏在前三课中你已经完成了完整的农产品运输追踪链路用 GPS 传感器采集位置第 1 课、把遥测数据存入云端第 2 课、在地图上可视化第 3 课。但知道车在哪还不够——如果能在卡车即将到达处理中心时触发警报装卸团队就能提前备好叉车等设备车辆一到即可快速卸载从而避免卡车和司机空等造成的成本浪费。地理围栏Geofence正是为此而生它是一块真实世界地理区域的虚拟边界。围栏可以是点 半径构成的圆形例如围绕某栋建筑半径 100 米的范围也可以是覆盖一片区域的多边形如学区、城市边界、大学或办公园区。 你可能早已在不知不觉中使用过地理围栏在 iOS 提醒事项或 Google Keep 中设置基于位置的提醒时应用就会围绕指定位置建立围栏并在你的手机进入围栏时发出提醒。判断一辆车是否位于围栏内/外有丰富的应用场景卸载准备车辆到场即通知团队提前就位缩短等待时间让司机一天内完成更多趟配送税务合规部分国家如新西兰仅对行驶在公共道路上的柴油车辆按重量征收道路使用费用围栏可以区分公共道路与农场、林场等私路里程防盗监控车辆应只停留在特定区域如农场一旦离开围栏可能意味着被盗区域合规作业现场、农场或工厂的某些区域禁止特定车辆进入如载有人工化肥、农药的车辆不得靠近有机农田车辆进入围栏即表示违规可及时通知司机。用 GeoJSON 定义地理围栏与上一课在地图上添加点位一样Azure Maps 的地理围栏同样使用GeoJSON定义。区别在于围栏不再是Point构成的FeatureCollection而是包含一个Polygon的FeatureCollection{ 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 } } ] }关键语法点多边形上的每个点都是[经度, 纬度]二元数组上一课中的Point其coordinates是一个含 2 个值的数组而Polygon的coordinates则是由这些二元数组组成的嵌套数组⚠️ 再次提醒GeoJSON 使用longitude, latitude经度在前不是latitude, longitude闭合规则多边形坐标数组的元素个数总是比实际顶点多 1——最后一个点必须与第一个点相同以闭合多边形。例如一个矩形需要 5 个点。✅ 练习尝试用 GeoJSON.io 之类的在线编辑器围绕你家或学校画一个 GeoJSON 多边形。任务一把围栏上传到 Azure Maps在 Azure Maps 中使用围栏必须先将其上传到地图账号。上传成功后会返回一个唯一 IDUDID之后用它来测试任意点是否在围栏内。上传需要调用 Azure Maps 的 Web API可以通过curl完成。检查 curl 是否已安装Linux、macOS 及较新版本的 Windows 10 通常自带curl --version创建包含多边形的 GeoJSON 文件可用上面的示例手动修改或用 GeoJSON.io 生成。文件必须是包含FeatureCollection的结构其中Feature的geometry类型为Polygon并且必须在与geometry同级的properties中放入geometryIdproperties: { geometryId: 1 }需要注意geometryId在同一文件中必须唯一可以在同一个 GeoJSON 文件的FeatureCollection中放入多个Feature即多个围栏只要各自的geometryId不同即可不同时间、从不同文件上传的多边形可以复用相同的geometryId。将文件保存为geofence.json并在终端中定位到该文件所在目录运行以下命令上传curl --request POST https://atlas.microsoft.com/mapData/upload?api-version1.0dataFormatgeojsonsubscription-keysubscription_key \ --header Content-Type: application/json \ --include \ --data geofence.json参数说明subscription_key替换为 Azure Maps 账号的 API 密钥api-version1.0指定使用的 API 版本保证 API 演进时向后兼容dataFormatgeojson声明上传的数据格式--include让响应头一并输出。 调用 Web 端点时可以在 URL 后加?并以keyvalue形式传参多个参数用分隔。上传请求会返回一组响应头其中包含名为location的头指向一个可轮询的操作状态 URLcontent-type: application/json location: https://us.atlas.microsoft.com/mapData/operations/1560ced6-3a80-46f2-84b2-5b1531820eab?api-version1.0 x-ms-azuremaps-region: West US 2 x-content-type-options: nosniff strict-transport-security: max-age31536000; includeSubDomains x-cache: CONFIG_NOCACHE date: Sat, 22 May 2021 21:34:57 GMT content-length: 0Azure Maps 不会立即处理上传需要用location头的 URL 轮询处理状态。在locationURL 末尾追加subscription-keysubscription_key替换为你的 API 密钥后发起 GET 请求curl --request GET locationsubscription-keysubscription_key检查响应中的status字段若不为Succeeded则等一分钟再试。状态为Succeeded后从响应中读取resourceLocation其中的唯一 IDUDID就是metadata/之后、不含api-version的那段值。例如{ resourceLocation: https://us.atlas.microsoft.com/mapData/metadata/7c3776eb-da87-4c52-ae83-caadf980323a?api-version1.0 }则 UDID 为7c3776eb-da87-4c52-ae83-caadf980323a。务必保存好 UDID测试围栏时需要用到。在仓库的 地理围栏课程代码目录 中上传后的 UDID 被写入 local.settings.json 的GEOFENCE_UDID配置项供触发器读取——这是课程推荐的配置管理方式避免把 UDID 硬编码进源码。理解 searchBuffer 与 distance 语义围栏上传后可以通过 Web API 请求传入 UDID 和待测点的经纬度判断点在围栏内还是围栏外。请求时还可以传一个searchBuffer参数它告诉 Maps API 结果的精确程度。原因是 GPS 本身并不完全精确位置有时会偏差数米甚至更多。searchBuffer默认50 米取值范围0 到 500 米。API 返回结果中distance表示被测点到围栏边缘最近点的距离点在外为正、在内为负。若该距离小于 search buffer则返回实际米数否则返回999或-999——999表示点位于围栏外且超出缓冲范围-999表示点位于围栏内且超出缓冲范围。上图是带 50 米缓冲的围栏四种典型情况情形distance 值围栏正中心、远在缓冲区内-999远在缓冲区之外999围栏内且位于缓冲区内、距边缘 6 米6m负数在 JSON 中体现围栏外且位于缓冲区内、距边缘 39 米39m为什么 distance 比内/外更有价值知道到围栏边缘的距离很重要做决策时应将其与其他信息如前后 GPS 读数、车速、道路数据结合。例如GPS 读数显示车辆沿一条紧邻围栏的道路行驶若某次读数不准确、把车放进了围栏——而实际上该位置根本没有车辆入口那么这个异常读数可以被忽略。上图中红色线路代表卡车沿 520 公路行驶圆圈是 GPS 读数。大多数读数准确地位于公路上唯有一个失准读数落入围栏内——该读数不可能正确因为不存在让卡车从 520 突然转入校园再转回公路的路径。检查围栏的代码必须结合历史读数才能决定是否采信某次围栏测试结果。✅ 思考要判断某个 GPS 读数是否可信还需要核验哪些附加数据任务二用 curl 测试点与围栏的位置关系构造查询 URL格式如下https://atlas.microsoft.com/spatial/geofence/json?api-version1.0deviceIdgps-sensorsubscription-keysubscription-keyudidUDIDlatlatlonlonsubscription_keyAzure Maps API 密钥UDID上一步得到的围栏 UDIDlat/lon待测点的纬度 / 经度deviceId为必填参数应填写该经纬度来源设备的名称默认 search buffer 为 50 米可通过追加searchBufferdistance修改distance取值范围 0500。用 curl 发起 GET 请求curl --request GET URL 如果返回BadRequest且错误信息为Invalid GeoJSON: All feature properties should contain a geometryId, which is used for identifying the geofence.说明你的 GeoJSON 缺少含geometryId的properties部分。需要修复 GeoJSON 后重新上传获取新的 UDID。响应中包含geometries列表每个多边形对应一个 geometry其中三个字段值得关注distance、nearestLat、nearestLon{ geometries: [ { deviceId: gps-sensor, udId: 7c3776eb-da87-4c52-ae83-caadf980323a, geometryId: 1, distance: 999.0, nearestLat: 47.645875, nearestLon: -122.142713 } ], expiredGeofenceGeometryId: [], invalidPeriodGeofenceGeometryId: [] }nearestLat/nearestLon围栏边缘上离被测点最近的那个点的经纬度distance被测点到围栏边缘最近点的距离负数为围栏内、正数为围栏外数值小于默认 search buffer50 米或为 999。用围栏内外的多个位置重复测试。把围栏检测接入无服务器代码现在可以为 Functions 应用添加一个新的触发器让 IoT Hub 的 GPS 事件数据实时过一遍围栏检测。为什么要用消费者组Consumer Group回忆前几课的内容IoT Hub 允许回放已收到但尚未处理的事件。但如果多个触发器同时连接IoT Hub 如何知道哪个组件处理了哪些事件答案是——它做不到。解决方案是定义多个独立的连接去读取事件每个连接各自管理未读消息的回放这些连接就是消费者组Consumer Group。连接端点时可以指定使用哪个消费者组应用中的每个组件应连接到不同的消费者组。理论上每个消费者组最多可有 5 个应用连接它们会在消息到达时都收到消息。但最佳实践是每个消费者组只让一个应用访问以避免重复处理消息并确保重启时所有排队消息都被正确处理。例如若同时在本机启动 Functions 应用又在云端运行二者都会处理消息导致存储账号中出现重复的 blob。回顾此前课程中 IoT Hub 触发器的function.json可以看到事件中心触发器绑定中的消费者组配置consumerGroup: $Default创建 IoT Hub 时默认会生成$Default消费者组。若要新增触发器可创建一个新消费者组供其使用。 本课用一个独立的函数而非存储 GPS 数据的那个来测试围栏目的是演示消费者组用法并让代码更易读、更易理解。生产应用中架构方式多种多样可以合并到同一个函数、用存储账号触发器触发围栏检查函数也可以用多个函数——没有唯一正确的方案取决于应用其余部分与实际需求。创建新消费者组用 Azure CLI 为 IoT Hub 创建名为geofence的消费者组az iot hub consumer-group create --name geofence \ --hub-name hub_name查看某个 IoT Hub 的全部消费者组az iot hub consumer-group list --output table \ --hub-name hub_name输出示例Name ResourceGroup -------- --------------- $Default gps-sensor geofence gps-sensor 此前课程运行 IoT Hub 事件监视器时它连接的是$Default消费者组这就是为什么事件监视器和事件触发器不能同时运行的原因。若想两者共存可以让所有函数应用使用其他消费者组把$Default留给事件监视器。创建新的 IoT Hub 触发器在之前创建的gps-trigger函数应用中新增一个 IoT Hub 事件触发器命名为geofence-trigger。仓库中已给出该触发器的完整实现位于 geofence-trigger 目录。参照其中两个文件完成配置若需要创建触发器的完整步骤可回看 2-farm 项目第 5 课 中创建 IoT Hub 事件触发器的说明。在function.json中配置 IoT Hub 连接字符串。local.settings.json在 Function App 的所有触发器间共享仓库示例 local.settings.json 中通过IOT_HUB_CONNECTION_STRING引用连接字符串。把function.json中consumerGroup的值改为新的geofence消费者组——仓库中 geofence-trigger/function.json 即如此配置consumerGroup: geofence触发器需要使用 Azure Maps 的订阅密钥在local.settings.json中新增MAPS_KEY配置项。运行 Functions 应用确认其能正常连接并处理消息。此前课程的iot-hub-trigger也会同时运行并上传 blob 到存储。为避免 blob 存储中出现重复 GPS 读数可以先停止云端正在运行的 Functions 应用az functionapp stop --resource-group gps-sensor \ --name functions_app_name之后可随时重启az functionapp start --resource-group gps-sensor \ --name functions_app_name在触发器中测试围栏之前我们用 curl 查询围栏其实可以在触发器代码里发起同样的 Web 请求。仓库中的init.py 是完整可运行的实现下面逐步解读在local.settings.json中新增GEOFENCE_UDID配置项值为围栏的 UDID。打开geofence-trigger的__init__.py在文件顶部添加导入import json import os import requestsrequests包用于发起 Web API 调用。Azure Maps 没有 Python SDK在 Python 代码中必须通过 Web API 调用其服务。注仓库的 requirements.txt 中列出了azure-functions与azure-storage-blob由于本触发器依赖requests部署前需确保该依赖已加入依赖清单可通过pip install requests本地安装并在部署时包含。在main方法开头读取 Maps 订阅密钥与围栏 UDIDmaps_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]这段代码把事件体中的 JSON 解析为字典再从gps字段中取出lat和lon——这正是前几课 GPS 传感器遥测数据中的字段结构。使用requests时不必像 curl 那样拼长 URL只需给出 URL 主体、把参数放进字典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 }params字典中的键值对与 curl 调用时 URL 上的参数一一对应。发起请求并解析响应response requests.get(url, paramsparams) response_body json.loads(response.text)根据distance记录不同的日志消息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)这段代码假设响应中只有一个 geometry并提取该 geometry 的distance再根据距离值输出不同的日志。999表示明确在围栏外、-999表示明确在围栏内、其余正值/负值分别表示在缓冲区内刚刚越过边界的两侧。运行代码即可在日志输出中看到 GPS 坐标位于围栏内还是围栏外若点位在 50 米缓冲区内还会带出具体距离。可以用 GPS 传感器所在位置的不同围栏测试或移动传感器例如连接手机热点或在虚拟 IoT 设备上改用不同坐标观察结果变化。验证通过后把代码部署到云端 Functions 应用别忘了同步部署新的 Application Settings。上传与部署步骤可参照 2-farm 项目第 5 课 中的说明。课后挑战多围栏支持本课只上传了一个单多边形围栏。实际上可以在一个 GeoJSON 文件中同时上传多个多边形只要它们的properties中geometryId各不相同即可。挑战如下尝试上传包含多个多边形的 GeoJSON 文件并调整代码找出 GPS 坐标最接近或位于其中的哪一个多边形。注意多 geometry 情况下geometries数组会有多项需要遍历并根据distance筛选而不能像示例代码那样只取geometries[0]。复习与延伸阅读本课配套的课程笔记图可查看 sketchnotes/lesson-14.jpg方便快速回顾围栏与 Azure Maps 的关键概念进一步学习围栏的典型用例以及 Azure Maps 地理围栏 API 与事件中心消费者组的官方文档见原课文的 Review Self Study 章节本课结束后请记得清理云服务资源可参考 clean-up.md 的清理指南如需先完成课后作业请保留相关服务。课后作业用 Twilio 发送到站通知目前代码只是把到围栏的距离写进日志。本课作业要求在此基础上增加通知能力当 GPS 坐标进入围栏时通过短信或邮件通知相关人员。要点Azure Functions 提供大量绑定Binding包括对 Twilio 这类第三方通信平台的绑定可选用 Twilio SMS 短信绑定或 SendGrid 绑定发送邮件绑定触发条件应为坐标位于围栏内或围栏外——二选一不能同时发送。评分标准节选自 assignment.md标准优秀达标待改进配置函数绑定并收到邮件/短信能配置绑定且仅在围栏内或围栏外而非两种情况收到通知能配置绑定但无法实际发出邮件/短信或内外两种情况都会触发无法配置绑定也无法发送邮件/短信【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表