注意
混合现实学院教程的设计考虑到了 HoloLens (第一代) 和混合现实沉浸式头戴显示设备。 因此,我们认为,对于仍在为这些设备进行开发指南的开发人员来说,保留这些教程非常重要。 这些教程未使用用于HoloLens 2的最新工具集或交互进行更新。 会维护它们以继续在受支持的设备上工作。 将来将发布一系列新的教程,演示如何针对HoloLens 2进行开发。 发布这些教程时,会更新此通知,其中包含指向这些教程的链接。
本课程介绍如何在运行 Ubuntu 16.4作系统的虚拟机上实现Azure IoT 中心服务。 然后使用Azure函数应用从 Ubuntu VM 接收消息,并将结果存储在Azure表服务中。 然后,可以在 Microsoft HoloLens 或沉浸式 (VR) 头戴显示设备上使用 Power BI 查看此数据。
本课程的内容适用于IoT Edge设备。 虽然在本课程中,重点在于虚拟机环境,因此无需访问物理 Edge 设备。
通过完成本课程,你将了解如何:
- 将IoT Edge模块部署到虚拟机 (Ubuntu 16 OS) ,该虚拟机表示 IoT 设备。
- 使用分析容器中存储的图像的代码,将Azure 自定义视觉 Tensorflow 模型添加到 Edge 模块。
- 设置模块以将分析结果消息发送回IoT 中心服务。
- 使用 Azure Function App 将消息存储在 Azure 表中。
- 设置 Power BI 以收集存储的消息并创建报表。
- 在 Power BI 中可视化 IoT 消息数据。
将使用的服务包括:
Azure IoT 中心是一项Microsoft Azure服务,它允许开发人员连接、监视和管理 IoT 资产。 有关详细信息,请访问“Azure IoT 中心服务”页。
Azure 容器注册表是一种Microsoft Azure服务,它允许开发人员存储各种类型的容器的容器映像。 有关详细信息,请访问“Azure 容器注册表服务”页。
Azure Function App 是一种Microsoft Azure服务,它允许开发人员在 Azure 中运行小段代码“functions”。 它提供了一种将工作委托给云而不是本地应用程序的方法,这可能有很多好处。 Azure Functions支持多种开发语言,包括 C#、F#、Node.js、Java 和 PHP。 有关详细信息,请访问Azure Functions页。
Azure存储:表是一种Microsoft Azure服务,它允许开发人员在云中存储结构化的非 SQL 数据,使其可在任何位置轻松访问。 该服务采用无架构设计,可根据需要演变表,因此非常灵活。 有关详细信息,请访问“Azure表”页
本课程介绍如何设置和使用 IoT 中心 服务,然后可视化设备提供的响应。 这些概念由你来决定将这些概念应用到自定义IoT 中心服务设置,而你可能正在构建该设置。
设备支持
| 课程 | HoloLens | 沉浸式头戴显示设备 |
|---|---|---|
| MR 和 Azure 313:IoT 中心 服务 | ✔️ | ✔️ |
先决条件
有关使用 混合现实(包括使用 Microsoft HoloLens 进行开发)的最新先决条件,请访问安装工具一文。
注意
本教程专为具有 Python 基本经验的开发人员设计。 此外,本文档中的先决条件和书面说明表示在撰写 2018 年 7 月 () 时测试和验证的内容。 你可以自由使用 安装工具 文章中列出的最新软件,但不应假定本课程中的信息与你在较新软件中发现的信息完全匹配,而不是下面列出的软件。
需要以下硬件和软件:
Windows 10 Fall Creators Update (或更高版本) ,已启用开发人员模式
警告
无法在 Windows 10 家庭版 Edition 上使用 Hyper-V 运行虚拟机。
Windows 10 SDK (最新版本)
已启用开发人员模式的 HoloLens
Visual Studio 2017.15.4 (仅用于访问 Azure Cloud Explorer)
适用于 Azure 和 IoT 中心 服务的 Internet 访问。 有关详细信息,请访问此链接以IoT 中心服务页
机器学习模型。 如果没有自己的现成模型, 可以使用本课程提供的模型。
在Windows 10开发计算机上启用了 Hyper-V 软件。
运行 Ubuntu (16.4 或 18.4) 、在开发计算机上运行的虚拟机,或者可以使用运行 Linux (Ubuntu 16.4 或 18.4) 的单独计算机。 可以在“开始之前”一章中找到有关如何使用 Hyper-V 在 Windows 上创建 VM 的详细信息。
准备工作
- 设置并测试 HoloLens。 如果需要支持设置 HoloLens, 请务必访问 HoloLens 设置一文。
- 在开始开发新的 HoloLens 应用时,最好执行 校准 和 传感器优化 (有时可以帮助每个用户) 执行这些任务。
有关校准的帮助,请参阅 HoloLens 校准一文的此链接。
有关传感器优化的帮助,请参阅 HoloLens 传感器优化一文的此链接。
使用 Hyper-V 设置 Ubuntu 虚拟机。 以下资源可帮助你完成此过程。
- 首先,按照此链接 下载 Ubuntu 16.04.4 LTS (Xenial Xerus) ISO。 选择 64 位电脑 (AMD64) 桌面映像。
- 确保在 Windows 10 计算机上启用了 Hyper-V。 可以按照此链接获取有关在 Windows 10 上安装和启用 Hyper-V 的指南。
- 启动 Hyper-V 并创建新的 Ubuntu VM。 可以按照此链接获取 有关如何使用 Hyper-V 创建 VM 的分步指南。 当请求 “从可启动映像文件安装作系统”时,请选择之前下载的 Ubuntu ISO 。
注意
不建议使用 Hyper-V 快速创建 。
第 1 章 - 检索自定义视觉模型
通过本课程,可以访问预建的自定义视觉模型,该模型检测图像中的键盘和鼠标。 如果使用,请转到 第 2 章。
但是,如果要使用自己的自定义视觉模型,可以按照以下步骤作:
在自定义视觉项目中,转到“性能”选项卡。
警告
模型必须使用 压缩 域来导出模型。 可以在项目的设置中更改模型域。
选择要导出的 迭代 ,然后单击“ 导出”。 此时会显示边栏选项卡。
在边栏选项卡中,单击“ Docker 文件”。
单击下拉菜单中的“ Linux ”,然后单击“ 下载”。
解压缩内容。 本课程稍后会用到它。
第 2 章 - 容器注册表服务
容器注册表服务是用于托管容器的存储库。
在本课程中生成和使用IoT 中心服务是指容器注册表服务,以获取要部署在 Edge 设备中的容器。
首先,通过此链接转到Azure门户,然后使用凭据登录。
转到 “创建资源 ”并查找 “容器注册表”。
单击“ 创建”。
设置服务设置参数:
插入项目的名称,在此示例中,该项目名为 IoTCRegistry。
选择资源组或创建新 资源组 。 资源组提供了一种监视、控制访问、预配和管理Azure资产集合计费的方法。 例如,建议将与单个项目关联的所有Azure服务保留 (,例如,这些课程) 在公共资源组) 下。
设置服务的位置。
将“管理员用户”设置为“启用”。
将 SKU 设置为 “基本”。
单击“ 创建 ”并等待创建服务。
弹出通知,通知成功创建 容器注册表后,单击“ 转到资源 ”以重定向到“服务”页。
在 “容器注册表 服务”页中,单击“ 访问密钥”。
记下 (可以使用记事本) 以下参数:
- 登录服务器
- Username
- Password
第 3 章 - IoT 中心服务
现在开始创建和设置IoT 中心服务。
如果尚未登录,请登录到 Azure 门户。
登录后,单击左上角的“创建资源”,搜索IoT 中心,然后单击 Enter。
新页提供 存储帐户 服务的说明。 在此提示符的左下角,单击“ 创建 ”按钮,创建此服务的实例。
单击“ 创建”后,将显示一个面板:
选择资源组或创建新 资源组 。 资源组提供了一种监视、控制访问、预配和管理Azure资产集合计费的方法。 例如,建议将与单个项目关联的所有Azure服务保留 (,例如,这些课程) 在通用资源组下。
如果想要详细了解Azure资源组,请按照此链接了解如何管理资源组。
选择适当的 位置 (在本课程中创建的所有服务) 使用相同的位置。
为此服务实例插入所需的 名称 。
在页面底部,单击“ 下一步:大小和缩放”。
在此页中,选择定价层和缩放层 (如果是第一个IoT 中心服务实例,则免费层应该可供) 使用。
单击“ 查看 + 创建”。
查看设置,然后单击“ 创建”。
弹出通知,通知成功创建IoT 中心服务后,单击“转到资源”以重定向到“服务”页。
滚动左侧的侧面板,直到看到自动设备管理,单击IoT Edge。
在右侧显示的窗口中,单击“添加IoT Edge设备”。 右侧会显示边栏选项卡。
在边栏选项卡中,为新设备提供 设备 ID (所选) 的名称。 然后,单击“保存”。 如果勾选了“自动生成”,则自动生成主密钥和辅助密钥。
导航回到“IoT Edge设备”部分,其中列出了新设备。 单击新设备 (下图) 中的红色轮廓。
在出现的“ 设备详细信息 ”页上,获取“ 连接字符串 ” (主键) 的副本。
若要打开它,请返回到左侧的面板,然后单击“ 共享访问策略”。
在出现的页面上,单击“ iothubowner”,屏幕右侧会显示一个边栏选项卡。
记下记下记事本 () 连接字符串 (主键) ,以供以后在将 连接字符串 设置为设备时使用。
第 4 章 - 设置开发环境
若要为 IoT 中心 Edge 创建和部署模块,需要在运行 Windows 10 的开发计算机上安装以下组件:
Docker for Windows,它要求你创建一个能够下载的帐户。
重要
Docker 需要 Windows 10 PRO、Enterprise 14393 或 Windows Server 2016 RTM 才能运行。 如果正在运行其他版本的 Windows 10,可以尝试使用 Docker 工具箱安装 Docker。
安装上述软件后,需要重新启动计算机。
第 5 章 - 设置 Ubuntu 环境
现在,你可以继续设置 运行 Ubuntu OS 的设备。 按照步骤安装所需的软件,在开发板上部署容器:
重要
应始终在终端命令之前使用 sudo 以管理员用户身份运行。 即:
sudo docker \<option> \<command> \<argument>
打开 Ubuntu 终端,并使用以下命令安装 pip:
[!提示] 可以使用键盘快捷方式(Ctrl + Alt + T)轻松打开终端。
sudo apt-get install python-pip在本章中, 终端可能会提示你允许使用设备存储,并输入 y/n (是或否) ,键入 “y”,然后按 Enter 键接受。
该命令完成后,使用以下命令安装 curl:
sudo apt install curl安装 pip 和 curl 后,使用以下命令安装IoT Edge运行时,这是在开发板上部署和控制模块所必需的:
curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > ./microsoft-prod.list sudo cp ./microsoft-prod.list /etc/apt/sources.list.d/ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg sudo cp ./microsoft.gpg /etc/apt/trusted.gpg.d/ sudo apt-get update sudo apt-get install moby-engine sudo apt-get install moby-cli sudo apt-get update sudo apt-get install iotedge此时,系统会提示打开运行时配置文件,以插入设备连接字符串,在记事本 () 创建IoT 中心服务 (时记下) 第 3 章的步骤 14。 在终端上运行以下行以打开该文件:
sudo nano /etc/iotedge/config.yamlconfig.yaml 文件随即显示,可供编辑:
警告
当此文件打开时,它可能会令人困惑。 你是在 终端 本身中编辑此文件的文本。
使用键盘上的箭头键向下滚动, (需要向下滚动一点) ,以到达包含“的行:
“<在此处>添加设备连接字符串”。
将行 (包括括号)替换为前面记录的设备 连接字符串 。
使用“连接字符串”,在键盘上按 Ctrl-X 键保存文件。 它要求你通过键入 Y 进行确认。然后,按 Enter 键进行确认。 返回到常规 终端。
成功运行这些命令后,将安装 IoT Edge Runtime。 初始化后,每次设备通电时,运行时都会自行启动,并将位于后台,等待从 IoT 中心 服务部署模块。
运行以下命令行以初始化IoT Edge运行时:
sudo systemctl restart iotedge重要
如果对 .yaml 文件或上述设置进行更改,则需要在 终端中再次运行重启行命令。
通过运行以下命令行来检查IoT Edge运行时状态。 运行时应显示为 活动状态 (运行) 以绿色文本显示。
sudo systemctl status iotedge按 Ctrl-C 键退出状态页。 通过键入以下命令,可以验证IoT Edge运行时是否正确拉取容器:
sudo docker ps应显示包含两个 (2 个) 容器的列表。 这些是 IoT 中心 Service (edgeAgent 和 edgeHub) 自动创建的默认模块。 创建并部署自己的模块后,它们将显示在此列表中的默认模块下方。
第 6 章 - 安装扩展
重要
接下来的几章 (6-9) 将在Windows 10计算机上执行。
打开 VS Code。
单击 VS Code 左侧栏上的“ 扩展 (方形) ”按钮,打开 “扩展”面板。
搜索并安装以下扩展 (,如下图所示) :
- Azure IoT Edge
- Azure IoT 工具包
- Docker
安装扩展后,关闭并重新打开 VS Code。
再次打开 VS Code 后,导航到 “查看>集成终端”。
现在安装 Cookiecutter。 在终端中,运行以下 bash 命令:
pip install --upgrade --user cookiecutter[!HINT] 如果在使用此命令时遇到问题:
- 重启 VS Code 和/或计算机。
- 可能需要将 VS Code 终端 切换到用于安装 Python 的终端,即 Powershell (尤其是在计算机上已安装 Python 环境的情况下) 。 打开终端后,可在终端右侧找到下拉菜单。
- 请确保将 Python 安装路径添加为计算机上的 环境变量 。 Cookiecutter 应属于同一位置路径。 有关 环境变量的详细信息,请访问此链接,
安装完 Cookiecutter 后,应重启计算机,以便在系统环境中将 Cookiecutter 识别为命令。
第 7 章 - 创建容器解决方案
此时,需要使用 模块创建容器,以推送到 容器注册表中。 推送容器后,可以使用 IoT 中心 Edge 服务将其部署到运行IoT Edge运行时的设备。
在 VS Code 中,单击“ 查看>命令”面板。
在调色板中,搜索并运行Azure IoT Edge:新的 IoT Edge 解决方案。
浏览到要在其中创建解决方案的位置。 按 Enter 键接受位置。
为解决方案命名。 按 Enter 键确认提供的名称。
现在,系统会提示你为解决方案选择模板框架。 单击“ Python 模块”。 按 Enter 键确认此选项。
为模块命名。 按 Enter 键确认模块的名称。 确保记下 (记事本) 模块名称,因为稍后会用到。
你会看到预生成的 Docker 映像存储库 地址显示在调色板上。 如下所示:
localhost:5000/-模块的名称。
删除 localhost:5000,并在其位置插入 容器注册表登录服务器 地址,在创建 容器注册表服务 (步骤 8,第 2 章) 。 按 Enter 键确认地址。
此时,将创建包含 Python 模块模板的解决方案,其结构将显示在屏幕左侧 VS Code 的 “浏览”选项卡中。 如果 “浏览”选项卡 未打开,可以通过单击左侧栏中的最顶部按钮将其打开。
本章的最后一步是从“浏览”选项卡中单击并打开 .env 文件,然后添加容器注册表用户名和密码。 git 忽略此文件,但在生成容器时,设置凭据以访问 容器注册表服务。
第 8 章 - 编辑容器解决方案
现在,通过更新以下文件完成容器解决方案:
- main.py python 脚本。
- requirements.txt。
- deployment.template.json。
- Dockerfile.amd64
然后创建图像文件夹,python 脚本使用该文件夹来检查图像,以便与自定义视觉模型匹配。 最后,添加 labels.txt 文件以帮助读取模型,以及 model.pb 文件(即模型)。
打开 VS Code 后,导航到模块文件夹,并查找名为 main.py 的脚本。 双击将其打开。
删除文件的内容并插入以下代码:
# Copyright (c) Microsoft. All rights reserved. # Licensed under the MIT license. See LICENSE file in the project root for # full license information. import random import sched, time import sys import iothub_client from iothub_client import IoTHubModuleClient, IoTHubClientError, IoTHubTransportProvider from iothub_client import IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError import json import os import tensorflow as tf import os from PIL import Image import numpy as np import cv2 # messageTimeout - the maximum time in milliseconds until a message times out. # The timeout period starts at IoTHubModuleClient.send_event_async. # By default, messages do not expire. MESSAGE_TIMEOUT = 10000 # global counters RECEIVE_CALLBACKS = 0 SEND_CALLBACKS = 0 TEMPERATURE_THRESHOLD = 25 TWIN_CALLBACKS = 0 # Choose HTTP, AMQP or MQTT as transport protocol. Currently only MQTT is supported. PROTOCOL = IoTHubTransportProvider.MQTT # Callback received when the message that we're forwarding is processed. def send_confirmation_callback(message, result, user_context): global SEND_CALLBACKS print ( "Confirmation[%d] received for message with result = %s" % (user_context, result) ) map_properties = message.properties() key_value_pair = map_properties.get_internals() print ( " Properties: %s" % key_value_pair ) SEND_CALLBACKS += 1 print ( " Total calls confirmed: %d" % SEND_CALLBACKS ) def convert_to_opencv(image): # RGB -> BGR conversion is performed as well. r,g,b = np.array(image).T opencv_image = np.array([b,g,r]).transpose() return opencv_image def crop_center(img,cropx,cropy): h, w = img.shape[:2] startx = w//2-(cropx//2) starty = h//2-(cropy//2) return img[starty:starty+cropy, startx:startx+cropx] def resize_down_to_1600_max_dim(image): h, w = image.shape[:2] if (h < 1600 and w < 1600): return image new_size = (1600 * w // h, 1600) if (h > w) else (1600, 1600 * h // w) return cv2.resize(image, new_size, interpolation = cv2.INTER_LINEAR) def resize_to_256_square(image): h, w = image.shape[:2] return cv2.resize(image, (256, 256), interpolation = cv2.INTER_LINEAR) def update_orientation(image): exif_orientation_tag = 0x0112 if hasattr(image, '_getexif'): exif = image._getexif() if (exif != None and exif_orientation_tag in exif): orientation = exif.get(exif_orientation_tag, 1) # orientation is 1 based, shift to zero based and flip/transpose based on 0-based values orientation -= 1 if orientation >= 4: image = image.transpose(Image.TRANSPOSE) if orientation == 2 or orientation == 3 or orientation == 6 or orientation == 7: image = image.transpose(Image.FLIP_TOP_BOTTOM) if orientation == 1 or orientation == 2 or orientation == 5 or orientation == 6: image = image.transpose(Image.FLIP_LEFT_RIGHT) return image def analyse(hubManager): messages_sent = 0; while True: #def send_message(): print ("Load the model into the project") # These names are part of the model and cannot be changed. output_layer = 'loss:0' input_node = 'Placeholder:0' graph_def = tf.GraphDef() labels = [] labels_filename = "labels.txt" filename = "model.pb" # Import the TF graph with tf.gfile.FastGFile(filename, 'rb') as f: graph_def.ParseFromString(f.read()) tf.import_graph_def(graph_def, name='') # Create a list of labels with open(labels_filename, 'rt') as lf: for l in lf: labels.append(l.strip()) print ("Model loaded into the project") results_dic = dict() # create the JSON to be sent as a message json_message = '' # Iterate through images print ("List of images to analyse:") for file in os.listdir('images'): print(file) image = Image.open("images/" + file) # Update orientation based on EXIF tags, if the file has orientation info. image = update_orientation(image) # Convert to OpenCV format image = convert_to_opencv(image) # If the image has either w or h greater than 1600 we resize it down respecting # aspect ratio such that the largest dimension is 1600 image = resize_down_to_1600_max_dim(image) # We next get the largest center square h, w = image.shape[:2] min_dim = min(w,h) max_square_image = crop_center(image, min_dim, min_dim) # Resize that square down to 256x256 augmented_image = resize_to_256_square(max_square_image) # The compact models have a network size of 227x227, the model requires this size. network_input_size = 227 # Crop the center for the specified network_input_Size augmented_image = crop_center(augmented_image, network_input_size, network_input_size) try: with tf.Session() as sess: prob_tensor = sess.graph.get_tensor_by_name(output_layer) predictions, = sess.run(prob_tensor, {input_node: [augmented_image] }) except Exception as identifier: print ("Identifier error: ", identifier) print ("Print the highest probability label") highest_probability_index = np.argmax(predictions) print('FINAL RESULT! Classified as: ' + labels[highest_probability_index]) l = labels[highest_probability_index] results_dic[file] = l # Or you can print out all of the results mapping labels to probabilities. label_index = 0 for p in predictions: truncated_probablity = np.float64(round(p,8)) print (labels[label_index], truncated_probablity) label_index += 1 print("Results dictionary") print(results_dic) json_message = json.dumps(results_dic) print("Json result") print(json_message) # Initialize a new message message = IoTHubMessage(bytearray(json_message, 'utf8')) hubManager.send_event_to_output("output1", message, 0) messages_sent += 1 print("Message sent! - Total: " + str(messages_sent)) print('----------------------------') # This is the wait time before repeating the analysis # Currently set to 10 seconds time.sleep(10) class HubManager(object): def __init__( self, protocol=IoTHubTransportProvider.MQTT): self.client_protocol = protocol self.client = IoTHubModuleClient() self.client.create_from_environment(protocol) # set the time until a message times out self.client.set_option("messageTimeout", MESSAGE_TIMEOUT) # Forwards the message received onto the next stage in the process. def forward_event_to_output(self, outputQueueName, event, send_context): self.client.send_event_async( outputQueueName, event, send_confirmation_callback, send_context) def send_event_to_output(self, outputQueueName, event, send_context): self.client.send_event_async(outputQueueName, event, send_confirmation_callback, send_context) def main(protocol): try: hub_manager = HubManager(protocol) analyse(hub_manager) while True: time.sleep(1) except IoTHubError as iothub_error: print ( "Unexpected error %s from IoTHub" % iothub_error ) return except KeyboardInterrupt: print ( "IoTHubModuleClient sample stopped" ) if __name__ == '__main__': main(PROTOCOL)打开名为 requirements.txt的文件,并将其内容替换为以下内容:
azure-iothub-device-client==1.4.0.0b3 opencv-python==3.3.1.11 tensorflow==1.8.0 pillow==5.1.0打开名为 deployment.template.json 的文件,并按照准则替换其内容:
由于你有自己唯一的 JSON 结构,因此需要手动 (编辑它,而不是) 复制示例。 若要简化作,请使用下图作为指南。
看起来与你的不同但 不应更改的区域将突出显示为黄色。
需要删除的部分突出显示为红色。
请小心删除正确的括号,同时删除逗号。
不过,完成的 JSON 应如下图所示 (,但存在独特的差异: 用户名/密码/模块名称/模块引用) :
打开名为 Dockerfile.amd64 的文件,并将其内容替换为以下内容:
FROM ubuntu:xenial WORKDIR /app RUN apt-get update && \ apt-get install -y --no-install-recommends libcurl4-openssl-dev python-pip libboost-python-dev && \ rm -rf /var/lib/apt/lists/* RUN pip install --upgrade pip RUN pip install setuptools COPY requirements.txt ./ RUN pip install -r requirements.txt RUN pip install pillow RUN pip install numpy RUN apt-get update && apt-get install -y \ pkg-config \ python-dev \ python-opencv \ libopencv-dev \ libav-tools \ libjpeg-dev \ libpng-dev \ libtiff-dev \ libjasper-dev \ python-numpy \ python-pycurl \ python-opencv RUN pip install opencv-python RUN pip install tensorflow RUN pip install --upgrade tensorflow COPY . . RUN useradd -ms /bin/bash moduleuser USER moduleuser CMD [ "python", "-u", "./main.py" ]右键单击 模块 下面的文件夹, (该文件夹具有之前提供的名称;在示例中,它称为 pythonmodule) ,然后单击“ 新建文件夹”。 命名文件夹 图像。
在 文件夹中,添加一些包含鼠标或键盘的图像。 这些是 Tensorflow 模型分析的图像。
警告
如果使用自己的模型,则需要对其进行更改以反映自己的模型数据。
现在,需要从 model 文件夹中检索 labels.txt 和 model.pb 文件,这些文件之前 (下载或从第 1 章中的自己的 自定义视觉 服务) 创建。 获得文件后,将它们与其他文件一起放在解决方案中。 最终结果应如下图所示:
第 9 章 - 将解决方案打包为容器
现在可以将文件“打包”为容器,并将其推送到Azure 容器注册表。 在 VS Code 中,打开集成终端 (视图>集成终端或 Ctrl+`) ,使用以下行登录到 Docker, (将命令的值替换为Azure 容器注册表 (ACR) ) 的凭据:
docker login -u <ACR username> -p <ACR password> <ACR login server>右键单击文件deployment.template.json,然后单击“生成IoT Edge解决方案”。 此生成过程需要花费相当长的时间 (具体取决于设备) ,因此请准备好等待。 生成过程完成后,将在名为 config 的新文件夹中创建一个deployment.json文件。
再次打开命令面板,搜索Azure:登录。 使用 Azure 帐户凭据按照提示进行作;VS Code 提供了一个“复制和打开”选项,这会复制你即将需要的设备代码,并打开默认 Web 浏览器。 当系统询问时,粘贴设备代码以对计算机进行身份验证。
登录后,你会注意到“浏览”面板底部有一个名为“Azure IoT 中心设备”的新部分。 单击此部分以将其展开。
如果设备不在此处,则需要右键单击“Azure IoT 中心设备”,然后单击“设置IoT 中心连接字符串”。 然后,你将看到 VS Code) 顶部的 命令面板 (,提示你输入 连接字符串。 这是你在第 3 章末尾记录的连接字符串。 复制字符串后,按 Enter 键。
设备应加载并显示。 右键单击设备名称,然后单击“ 为单个设备创建部署”。
出现文件资源管理器提示,可在其中导航到 config 文件夹,然后选择deployment.json文件。 选中该文件后,单击“ 选择边缘部署清单” 按钮。
此时,你向 IoT 中心 服务提供了清单,以便该服务将容器作为模块从Azure 容器注册表部署,从而有效地将其部署到设备。
若要查看从设备发送到IoT 中心的消息,请在“Azure IoT 中心设备”部分的“资源管理器”面板中再次右键单击设备名称,然后单击“开始监视 D2C 消息”。 从设备发送的消息应显示在 VS 终端中。 要有耐心,因为这可能需要一些时间。 有关调试和检查部署是否成功的,请参阅下一章。
此模块现在循环访问 图像文件夹中的图像 ,并分析它们,每次迭代。 这只是演示如何让基本机器学习模型在IoT Edge设备环境中工作的演示。
若要扩展此示例的功能,可以通过多种方式继续作。 一种方法是在容器中包含一些代码,该代码从连接到设备的网络摄像头捕获照片,并将图像保存在图像文件夹中。
另一种方法是将映像从 IoT 设备复制到容器中。 执行此作的一种实用方法是在 IoT 设备终端中运行以下命令, (如果希望自动执行此过程) ,则也许小型应用可以完成该工作。 可以通过从存储文件的文件夹位置手动运行此命令来测试此命令:
sudo docker cp <filename> <modulename>:/app/images/<a name of your choice>
第 10 章 - 调试IoT Edge运行时
下面是命令行列表和提示,可帮助你从 Ubuntu 设备监视和调试 IoT Edge 运行时的消息传送活动。
通过运行以下命令行检查IoT Edge运行时状态:
sudo systemctl status iotedge注意
请记得按 Ctrl + C 完成查看状态。
列出当前部署的容器。 如果IoT 中心服务成功部署了容器,则通过运行以下命令行来显示容器:
sudo iotedge list或
sudo docker ps注意
运行命令是检查模块是否已成功部署的好方法,因为它显示在列表中;否则只能看到 edgeHub 和 edgeAgent。
若要显示容器的代码日志,请运行以下命令行:
journalctl -u iotedge
用于管理IoT Edge运行时的有用命令:
删除主机中的所有容器:
sudo docker rm -f $(sudo docker ps -aq)停止IoT Edge运行时:
sudo systemctl stop iotedge
第 11 章 - 创建表服务
导航回到Azure门户,通过创建存储资源,可在其中创建Azure表服务。
如果尚未登录,请登录到 Azure 门户。
登录后,单击左上角的“ 创建资源”,搜索 “存储帐户”,然后按 Enter 键开始搜索。
出现后,从列表中单击“ 存储帐户 - blob、文件、表、队列 ”。
新页提供 存储帐户 服务的说明。 在此提示符的左下角,单击“ 创建 ”按钮,创建此服务的实例。
单击“ 创建”后,将显示一个面板:
插入此服务实例的所需 名称 (必须为全小写) 。
对于 “部署模型”,请单击“ 资源管理器”。
对于 “帐户类型”,使用下拉菜单单击“ 存储” (常规用途 v1) 。
单击相应的 位置。
对于“ 复制 ”下拉菜单,请单击“ 读取访问异地冗余存储 (RA-GRS) ”。
对于“性能”,请单击“Standard”。
在“ 需要安全传输 ”部分中,单击“ 已禁用”。
在“ 订阅 ”下拉菜单中,单击相应的订阅。
选择资源组或创建新 资源组 。 资源组提供了一种监视、控制访问、预配和管理Azure资产集合计费的方法。 例如,建议将与单个项目关联的所有Azure服务保留 (,例如,这些课程) 在通用资源组下。
如果想要详细了解Azure资源组,请按照此链接了解如何管理资源组。
如果 “虚拟网络 ”是一个选项,请将“虚拟网络”保留为 “已禁用”。
单击“创建”。
单击“ 创建”后,必须等待服务创建,可能需要一分钟时间。
创建服务实例后,门户中会显示一条通知。 单击通知以浏览新的服务实例。
单击通知中的“ 转到资源 ”按钮,将转到新的存储服务实例概述页。
在概述页的右侧,单击“ 表”。
右侧的面板将更改为显示 表服务 信息,其中需要添加新表。 单击左上角的“ + 表格 ”按钮。
将显示一个新页面,其中需要输入 表名称。 这是在后面的创建函数应用和 Power BI) (章节中引用应用程序中数据的名称。 插入 IoTMessages 作为名称, (可以选择自己的名称,只需记住它,稍后在本文档中) ,然后单击 “确定”。
创建新表后,可以在底部) 的“ 表服务 ”页 (中看到它。
现在,单击“访问密钥”,并使用记事本) 获取存储帐户名称和密钥 (的副本,稍后在本课程中创建Azure函数应用时,将使用这些值。
再次使用左侧的面板,滚动到“ 表服务 ”部分,在较新的门户中单击“ 表 (”或“ 浏览表”,) 并使用记事本) 获取 表 URL (的副本。 在本课程稍后将表链接到 Power BI 应用程序时,将使用此值。
第 12 章 - 完成Azure表
设置 表服务 存储帐户后,可以向其添加可用于存储和检索信息的数据。 表的编辑可以通过 Visual Studio 完成。
打开 Visual Studio (不Visual Studio Code) 。
在菜单中,单击“ 查看>云资源管理器”。
云资源管理器作为停靠项打开, (耐心等待,因为加载可能需要时间) 。
警告
如果用于创建 存储帐户 的订阅不可见,请确保:
登录到与用于 Azure 门户的帐户相同的帐户。
从“帐户管理”页面选择了订阅, (可能需要从帐户设置) 应用筛选器:
将显示Azure云服务。 找到 “存储帐户 ”,然后单击该帐户左侧的箭头以展开帐户。
展开后,新创建的 存储帐户 应可用。 单击存储左侧的箭头,展开后,找到 “表 ”,然后单击旁边的箭头,以显示在上一章中创建的 表 。 双击 表。
表在 Visual Studio 窗口的中心打开。 单击带有 + (加上) 的表图标。
此时会显示一个窗口,提示你 添加实体。 仅创建一个实体,但它有三个属性。 你注意到已经提供了 PartitionKey 和 RowKey ,因为表会使用这些键来查找数据。
更新以下值:
名称: PartitionKey,值: PK_IoTMessages
名称: RowKey,值: RK_1_IoTMessages
然后,单击“ 添加实体 ”窗口左下角的“ 添加 属性 () 并添加以下属性:
- MessageContent 作为 字符串,将“值”留空。
你的表应与下图中的表匹配:
注意
实体在行键中具有数字 1 的原因是,如果想要进一步尝试本课程,则可能需要添加更多消息。
完成后,单击“ 确定 ”。 表现已准备就绪,可供使用。
第 13 章 - 创建Azure函数应用
现在,可以创建Azure函数应用,IoT 中心服务将IoT Edge设备消息存储在上一章中创建的表服务中。
首先,需要创建一个文件,允许 Azure 函数加载所需的库。
打开 记事本 (按 Windows 键,然后键入 记事本) 。
打开记事本后,将以下 JSON 结构插入其中。 执行此作后,将其作为 project.json保存在桌面上。 此文件定义函数使用的库。 如果使用 NuGet,它看起来很熟悉。
警告
命名正确,这一点很重要。确保它 没有 .txt 文件扩展名。 有关参考,请参阅以下 JSON 结构:
{ "frameworks": { "net46":{ "dependencies": { "WindowsAzure.Storage": "9.2.0" } } } }登录到 Azure 门户。
登录后,单击左上角的“ 创建资源 ”,搜索 “Function App”,然后按 Enter 键进行搜索。 若要打开新面板,请在结果中单击“ 函数应用 ”。
新面板提供 函数应用 服务的说明。 在此面板的左下角,单击“ 创建 ”按钮,以创建此服务的关联。
单击“ 创建”后,填写以下内容:
对于 “应用名称”,请为此服务实例插入所需的名称。
选择 订阅。
选择适合你的定价层,如果这是首次创建函数App 服务,则应可以使用免费层。
选择资源组或创建新 资源组 。 资源组提供了一种监视、控制访问、预配和管理Azure资产集合计费的方法。 例如,建议将与单个项目关联的所有Azure服务保留 (,例如,这些课程) 在通用资源组下。
如果想要详细了解Azure资源组,请按照此链接了解如何管理资源组。
对于 OS,请单击“Windows”,因为这是预期的平台。
(本教程使用消耗计划) ,请选择托管计划。
选择 “位置 ” (选择与在上一步) 中生成的存储相同的位置。
对于 “存储 ”部分, 必须选择在上一步中创建的存储服务。
此应用中不需要 Application Insights ,因此请随时将其 关闭。
单击“创建”。
单击“ 创建”后,必须等待服务创建,可能需要一分钟时间。
创建服务实例后,门户中会显示一条通知。
在部署成功后,单击通知 (完成) 。
单击通知中的“ 转到资源 ”按钮,浏览新的服务实例。
在新面板的左侧,单击“+函数”旁边的 (加) 图标以创建新函数。
在中央面板中,将显示 “函数 创建”窗口。 进一步向下滚动,然后单击“ 自定义函数”。
向下滚动下一页,直到找到IoT 中心 (事件中心) ,然后单击它。
在“IoT 中心 (事件中心) ”边栏选项卡中,将“语言”设置为“C#”,然后单击“新建”。
在出现的窗口中,确保选中IoT 中心,并且IoT 中心字段的名称与前面 (第 3 章第3 章的步骤 8 中创建IoT 中心服务的名称相对应) 。 然后单击“ 选择” 按钮。
返回到“IoT 中心 (事件中心) ”边栏选项卡,单击“创建”。
将重定向到函数编辑器。
删除其中的所有代码,并将其替换为以下代码:
#r "Microsoft.WindowsAzure.Storage" #r "NewtonSoft.Json" using System; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Table; using Newtonsoft.Json; using System.Threading.Tasks; public static async Task Run(string myIoTHubMessage, TraceWriter log) { log.Info($"C# IoT Hub trigger function processed a message: {myIoTHubMessage}"); //RowKey of the table object to be changed string tableName = "IoTMessages"; string tableURL = "https://iothubmrstorage.table.core.windows.net/IoTMessages"; // If you did not name your Storage Service as suggested in the course, change the name here with the one you chose. string storageAccountName = "iotedgestor"; string storageAccountKey = "<Insert your Storage Key here>"; string partitionKey = "PK_IoTMessages"; string rowKey = "RK_1_IoTMessages"; Microsoft.WindowsAzure.Storage.Auth.StorageCredentials storageCredentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(storageAccountName, storageAccountKey); CloudStorageAccount storageAccount = new CloudStorageAccount(storageCredentials, true); // Create the table client. CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); // Get a reference to a table named "IoTMessages" CloudTable messageTable = tableClient.GetTableReference(tableName); //Retrieve the table object by its RowKey TableOperation operation = TableOperation.Retrieve<MessageEntity>(partitionKey, rowKey); TableResult result = await messageTable.ExecuteAsync(operation); //Create a MessageEntity so to set its parameters MessageEntity messageEntity = (MessageEntity)result.Result; messageEntity.MessageContent = myIoTHubMessage; messageEntity.PartitionKey = partitionKey; messageEntity.RowKey = rowKey; //Replace the table appropriate table Entity with the value of the MessageEntity Ccass structure. operation = TableOperation.Replace(messageEntity); // Execute the insert operation. await messageTable.ExecuteAsync(operation); } // This MessageEntity structure which will represent a Table Entity public class MessageEntity : TableEntity { public string Type { get; set; } public string MessageContent { get; set; } }更改以下变量,使其对应于存储帐户中分别 (第 11 章的步骤11 和 1) 3 中的表值和存储值:
- tableName, 表 的名称位于 存储帐户中。
- tableURL, 表 的 URL 位于 存储帐户中。
- storageAccountName,其值的名称与 存储帐户 名称对应。
- storageAccountKey,以及之前创建的存储服务中获取的密钥。
代码就位后,单击“ 保存”。
接下来,单击 < 页面右侧的 (箭头) 图标。
面板从右侧滑入。 在该面板中,单击“ 上传”,此时会显示 “文件浏览器 ”。
导航到之前在记事本中创建的project.json文件,然后单击“打开”按钮。 此文件定义函数使用的库。
上传文件时,它将显示在右侧的面板中。 单击它可在 函数 编辑器中打开它。 它必须与下一个图像 完全相同 。
此时,最好测试函数在 表上存储消息的功能。 在窗口的右上角,单击“ 测试”。
如上图所示,在 请求正文上插入一条消息,然后单击“ 运行”。
函数在“输出”窗口中运行,显示结果状态 (你注意到绿色的“状态 202 已接受”,这意味着调用成功) :
第 14 章 - 查看活动消息
如果现在打开 Visual Studio (不Visual Studio Code) ,则可以直观显示测试消息结果,因为它存储在 MessageContent 字符串区域中。
表服务和函数应用到位后,Ubuntu 设备消息将显示在 IoTMessages 表中。 如果尚未运行,请再次启动设备,并且通过使用 Visual Studio Cloud Explorer,可以在表中查看设备和模块的结果消息。
第 15 章 - Power BI 设置
若要可视化 IOT 设备中的数据,需要设置 Power BI (桌面版本) ,以从创建的 表 服务收集数据。 然后 ,Power BI 的 HoloLens 版本使用该数据可视化结果。
打开Windows 10 上的 Microsoft Store并搜索Power BI Desktop。
下载应用程序。 下载完成后,将其打开。
使用 Microsoft 365 帐户登录到 Power BI。 你可能会被重定向到浏览器进行注册。 注册后,返回到 Power BI 应用,然后再次登录。
单击“ 获取数据 ”,然后单击“ 更多...”。
单击“Azure”,Azure“表存储”,然后单击“连接”。
在创建表服务时,系统会提示插入前面收集 (第 11 章第 13 章第 13 章) 收集的表 URL。 插入 URL 后,在本课程) 中删除引用表“子文件夹” ((即 IoTMessages)的路径部分。 最终结果应如下图所示。 然后单击“ 确定”。
创建表存储时,系统会提示你插入 (前面第 11 章的步骤 11 中记录的存储密钥) 。 然后单击“ 连接”。
将显示 “导航器面板 ”,勾选“表”旁边的框,然后单击“ 加载”。
现在,你的表已加载到 Power BI 上,但它需要一个查询来显示其中的值。 右键单击位于屏幕右侧“ 字段”面板中 的表名称。 然后单击“ 编辑查询”。
Power Query 编辑器将作为新窗口打开,显示表。 单击表格“内容”列中的“记录”一词,以可视化存储的内容。
单击窗口左上角的“ 进入表格”。
单击“ 关闭 & 应用”。
加载完查询后,在 “字段”面板的屏幕右侧,勾选与参数 “名称” 和“ 值”对应的框,以可视化 MessageContent 列内容。
单击窗口左上角的 蓝色磁盘图标 ,将工作保存在所选的文件夹中。
现在可以单击“发布”按钮,将表上传到工作区。 出现提示时,单击“ 我的工作区 ”,然后单击“ 选择”。 等待它显示提交的成功结果。
警告
以下章节特定于 HoloLens。 Power BI 当前不作为沉浸式应用程序提供,但可以通过桌面应用在 Windows Mixed Reality 门户中 (Cliff House) 运行桌面版本。
第 16 章 - 在 HoloLens 上显示 Power BI 数据
在 HoloLens 上,通过点击应用程序列表中的图标登录到 Microsoft 应用商店。
搜索并下载 Power BI 应用程序。
从应用程序列表中启动 Power BI 。
Power BI 可能会要求你登录到 Microsoft 365 帐户。
进入应用后,工作区应默认显示,如下图所示。 如果未发生这种情况,只需单击窗口左侧的工作区图标即可。
已完成IoT 中心应用程序
恭喜,已成功使用模拟虚拟机 Edge 设备创建IoT 中心服务。 设备可以将机器学习模型的结果传达给Azure表服务,Azure函数应用可读取到 Power BI 中,并在Microsoft HoloLens中可视化。
奖励练习
练习 1
展开存储在表中的消息传送结构,并将其显示为图形。 你可能希望收集更多数据并将其存储在同一表中,以便稍后显示。
练习 2
创建另一个要部署在 IoT 板上的“相机捕获”模块,以便它可以通过相机捕获要分析的图像。