HoloLens(第一代)和 Azure 313:IoT 中心服务

注意

混合现实学院教程在制作时考虑到了 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 函数应用将消息存储在 Azure 表中
  • 设置 Power BI 以收集存储的消息并创建报告
  • 在 Power BI 中可视化 IoT 消息数据

将使用的服务包括:

  • Azure IoT 中心是一项 Microsoft Azure 服务,它允许开发人员连接、监视和管理 IoT 资产。 有关详细信息,请访问“Azure IoT 中心服务”页

  • Azure 容器注册表是一项 Microsoft Azure 服务,它允许开发人员为各种类型的容器存储容器映像。 有关详细信息,请访问“Azure 容器注册表服务”页

  • Azure 函数应用是一项 Microsoft Azure 服务,它允许开发人员在 Azure 中运行小段代码“函数”。 这提供了一种将工作委托给云(而不是本地应用程序)的方法,这种方法会有很多优势。 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 家庭版上的 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 的详细信息。

开始之前

  1. 设置并测试 HoloLens。 如需有关设置 HoloLens 的支持,请确保参阅“HoloLens 设置”一文
  2. 在开始开发新的 HoloLens 应用时,最好执行校准和传感器优化(有时 HoloLens 应用可以帮助为每个用户执行这些任务)

有关校准的帮助信息,请单击此链接访问“HoloLens 校准”一文

有关传感器优化的帮助信息,请单击此链接访问“HoloLens 传感器优化”一文

  1. 使用 Hyper-V 设置 Ubuntu 虚拟机。 以下资源将帮助完成此过程。

    1. 首先,请单击此链接下载 Ubuntu 16.04.4 LTS (Xenial Xerus) ISO。 选择 64 位 (AMD64) 桌面映像
    2. 请确保 Windows 10 计算机上启用了 Hyper-V。 可以单击此链接,获取有关在 Windows 10 上安装和启用 Hyper-V 的指南。
    3. 启动 Hyper-V 并创建新的 Ubuntu VM。 可以单击此链接,了解有关如何使用 Hyper-V 创建 VM 的分步指导。 当请求“从可启动的映像文件安装操作系统”时,选择之前下载的 Ubuntu ISO

    注意

    不建议使用“Hyper-V 快速创建”

第 1 章 - 检索自定义视觉模型

在本课程中,你将有权访问预生成的自定义视觉模型,该模型用于从映像中检测键盘和鼠标。 如果使用此模型,请转到第 2 章

但是,如果想要使用自己的自定义视觉模型,则可以执行以下步骤:

  1. 在“自定义视觉项目”中转到“性能”选项卡

    警告

    模型必须使用精简域来导出模型。 可以在设置中更改项目的模型域。

    性能选项卡

  2. 选择要导出的“迭代”,然后单击“导出”。 随即出现一个边栏选项卡。

    导出边栏选项卡

  3. 在边栏选项卡中,单击“Docker 文件”

    选择 Docker

  4. 单击下拉菜单中的“Linux”,然后单击“下载”

    单击下载

  5. 解压缩内容。 稍后将在本课程中使用它。

第 2 章 - 容器注册表服务

容器注册表服务是用于托管容器的存储库

将在本课程中生成和使用的 IoT 中心服务是指容器注册表服务,用于获取要部署在 Edge 设备中的容器

  1. 首先,单击此链接转到 Azure 门户,然后使用凭据登录。

  2. 转到“创建资源”并查找“容器注册表”

    容器注册表

  3. 单击“创建”。

    显示选择“创建”的位置的屏幕截图。

  4. 设置服务设置参数:

    1. 插入项目的名称,在本示例中,它称为 IoTCRegistry

    2. 选择一个资源组或创建一个新资源组。 资源组提供一种监视、控制访问、预配和管理 Azure 资产集合计费的方法。 建议保留与常用资源组下的单个项目(例如这些课程)关联的所有 Azure 服务。

    3. 设置服务位置。

    4. 将“管理员用户”设置为“启用”

    5. 将“SKU”设置为“基本”

    显示 SKU 设置为“基本”的位置的屏幕截图。

  5. 单击“创建”并等待服务创建完成

  6. 在弹出通知并显示已成功创建容器注册表后,单击“转到资源”以重定向到“服务”页

    显示“转到资源”位置的屏幕截图。

  7. 在“容器注册表”的“服务”页中,单击“访问密钥”

  8. 记下(可以使用记事本)以下参数:

    1. 登录服务器
    2. 用户名
    3. 密码

    显示在何处查看登录服务器、用户名和密码参数的屏幕截图。

第 3 章 - IoT 中心服务

现在开始创建和设置 IoT 中心服务

  1. 如果尚未登录,请登录到 Azure 门户

  2. 登录后,单击左上角的“创建资源”,搜索“IoT 中心”,并单击“Enter”

显示“所有内容”窗口打开的屏幕截图。搜索结果中的 I O T 中心,左上角的“创建资源”以红色圆圈。

  1. 新页面将提供“存储帐户”服务的说明。 在该提示的左下角,单击“创建”按钮,创建此服务的实例

    显示“I O T 中心概述”页的屏幕截图。

  2. 单击“创建”后,随即显示一个面板

    1. 选择一个资源组或创建一个新资源组。 通过资源组,可监视和预配 Azure 资产集合、控制其访问权限并管理其计费。 建议保留与常用资源组下的单个项目(例如这些课程)关联的所有 Azure 服务。

      若要详细了解 Azure 资源组,请单击此链接了解如何管理资源组

    2. 选择适当的位置(本课程中创建的所有服务使用同一位置)

    3. 插入此服务实例的所需名称

  3. 在页面底部单击“下一步: 大小和规模”。

    显示“I O T 中心基本信息”页的屏幕截图。下一个大小和刻度在页面底部以红色圆圈。

  4. 在此页中,选择“定价和缩放层”(如果这是首个 IoT 中心服务实例,则应可使用免费层)

  5. 单击“查看 + 创建”

    显示“I O T 中心大小和缩放”页的屏幕截图。在“定价和缩放层”字段中选择了 F 1 免费层。查看和创建在页面底部的红色圆圈。

  6. 查看设置,然后单击“创建”

    显示“I O T 中心审阅和创建”页面的屏幕截图。创建在页面底部以红色圆圈。

  7. 在弹出通知并显示已成功创建 IoT 中心服务后,单击“转到资源”以重定向到“服务”页。

    显示部署成功通知的屏幕截图。“转到资源”按钮以红色圆圈。

  8. 滚动左侧面板,直到看到“自动设备管理”,然后单击“IoT Edge”

    显示“自动设备管理”菜单中选中的“I O T Edge”的屏幕截图。

  9. 在右侧出现的窗口中,单击“添加 IoT Edge 设备”。 右侧随即出现一个边栏选项卡。

  10. 在边栏选项卡中,提供新设备的“设备 ID”(你选择的名称)。 然后,单击“保存”。 如果已勾选“自动生成”,则将自动生成“主密钥”和“辅助密钥”

    显示“添加设备”页的屏幕截图。Edge Device 0 1 在“设备 ID”字段中输入。选中“自动生成密钥”框。

  11. 导航回“IoT Edge 设备”部分,其中将列出新设备。 单击新设备(下图红色边框所示)。

    显示“I O T Edge 设备”页的屏幕截图。该框在页面底部的 Edge 设备旁边选中。

  12. 在出现的“设备详细信息”页中,复制连接字符串(主密钥)

    显示“设备详细信息”页的屏幕截图。连接字符串主键以红色圆圈。

  13. 返回左侧面板,单击“共享访问策略”将其打开。

  14. 在显示的页面中,单击“iothubowner”,屏幕右侧随即出现一个边栏选项卡

  15. 记下(在记事本上)连接字符串(主密钥),以便之后在设置“连接字符串”以连接到设备时使用。

    显示位于“设置”下的“共享访问策略”页的屏幕截图。“策略”下选择了“I O T 中心所有者”。在弹出窗口中,连接字符串主键字段以红色圆圈。

第 4 章 - 设置开发环境

若要为 IoT 中心 Edge 创建和部署模块,需要在运行 Windows 10 的开发计算机上安装以下组件

  1. 适用于 Windows 的 Docker,它将要求你创建一个可供下载的帐户。

    下载适用于 Windows 的 Docker

    重要

    Docker 需要 Windows 10 PRO、Enterprise 14393 或 Windows Server 2016 RTM 才能运行。 如果运行的是其他版本的 Windows 10,可以尝试使用 Docker 工具箱安装 Docker。

  2. Python 3.6

    下载 Python 3.6

  3. Visual Studio Code(也称为 VS Code)

    下载 VS Code

安装上述软件之后,需要重启计算机。

第 5 章 - 设置 Ubuntu 环境

现在,可以转到设置运行 Ubuntu OS 的设备。 按照以下步骤安装所需的软件,以便在开发板上部署容器:

重要

应始终将 sudo 置于终端命令之前,以便以管理员用户身份运行。 即:

sudo docker \<option> \<command> \<argument>
  1. 打开 Ubuntu 终端,并使用以下命令安装 pip

    [!提示] 可以使用键盘快捷方式非常轻松地打开终端:Ctrl + Alt + T

        sudo apt-get install python-pip
    
  2. 在本章中,终端可能会提示你授予使用设备存储的权限并要求你输入 y/n(是或否),请键入“y”,然后按“Enter”键接受

  3. 完成该命令后,使用以下命令安装 curl

        sudo apt install curl
    
  4. 安装 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
    
  5. 此时,系统会提示你打开运行时配置文件,并插入在创建 IoT 中心服务(第 3 章,步骤 14)时记下(在记事本中)的设备连接字符串。 在终端上运行以下行,以打开该文件:

        sudo nano /etc/iotedge/config.yaml
    
  6. 此时将显示 config.yaml 文件,以供编辑

    警告

    此文件打开时,可能会令你有些困惑。 你将在终端本身中编辑此文件的文本

    1. 使用键盘上的箭头键向下滚动(需稍稍向下滚动一点),到达包含以下内容的列:

      “<在此处添加设备连接字符串>”

    2. 用之前记下的设备连接字符串替换这一行(包括括号)

  7. 放置连接字符串后,请在键盘上按 Ctrl + X 键保存该文件。 系统将要求键入 Y 进行确认。然后,按 Enter 键以确认。 将返回到常规终端

  8. 成功运行这些命令后,将安装 IoT Edge 运行时。 初始化后,每次打开设备时,运行时都会自行启动,并在后台等待从 IoT 中心服务部署模块

  9. 运行以下命令行,初始化 IoT Edge 运行时

        sudo systemctl restart iotedge
    

    重要

    如果对 .yaml 文件或上述设置进行更改,则需要在终端中再次运行上述重启行

  10. 运行以下命令行,检查 IoT Edge 运行时状态。 此时将显示带绿色文本“活动(正在运行)”状态的运行时

        sudo systemctl status iotedge
    
  11. 按 Ctrl + C 键退出状态页。 可以通过键入以下命令来验证 IoT Edge 运行时是否正在正确拉取容器

        sudo docker ps
    
  12. 此时应显示包含两 (2) 个容器的列表。 这些是 IoT 中心服务自动创建的默认模块(edgeAgent 和 edgeHub)。 创建和部署自己的模块后,这些模块将显示在此列表的默认模块下方。

第 6 章 - 安装扩展

重要

接下来几章 (6-9) 的步骤将在 Windows 10 计算机上执行。

  1. 打开 VS Code

  2. 单击 VS Code 左侧栏上的“扩展”(方形)按钮,打开“扩展”面板

  3. 搜索并安装以下扩展(如下图所示):

    1. Azure IoT Edge
    2. Azure IoT 工具包
    3. Docker

    显示扩展窗口的屏幕截图。Azure I O T Edge、Azure I O T 工具包和 Docker 以红色圆圈。

  4. 安装扩展后,关闭并重新打开 VS Code。

  5. 再次打开 VS Code 后,导航到“查看”>“集成终端”

  6. 现在安装“Cookiecutter”。 在终端中运行以下 bash 命令:

        pip install --upgrade --user cookiecutter
    

    [!提示] 如果运行此命令出现问题:

    1. 重启 VS Code 和/或计算机。
    2. 可能需要将 VS Code 终端切换到用于安装 Python 的终端,即 Powershell(尤其是在计算机上已安装 Python 环境的情况下)。 打开终端后,将在终端右侧找到下拉菜单。 显示下拉菜单中选择了 1 个 powershell 的屏幕截图。
    3. 确保已在计算机上将 Python 安装路径添加为环境变量。 Cookiecutter 应为同一位置路径的一部分。 请单击此链接详细了解环境变量
  7. 完成 Cookiecutter 安装后,应重启计算机,以便在系统环境中将 Cookiecutter 识别为命令

第 7 章 - 创建容器解决方案

此时,需要创建包含模块的容器,以推送到容器注册表中。 推送容器后,将使用 IoT 中心 Edge 服务将其部署到运行 IoT Edge 运行时的设备

  1. 从 VS Code 单击“查看”>“命令面板”

  2. 在面板中,搜索并运行“Azure IoT Edge: 新 IoT Edge 解决方案”

  3. 浏览到要创建解决方案的位置。 按 Enter 键接受该位置

  4. 为解决方案提供名称。 按 Enter 键确认提供的名称

  5. 现在,系统会提示你为解决方案选择模板框架。 单击“Python 模块”。 按 Enter 键确认选择

  6. 为模板提供名称。 按 Enter 键确认模块的名称。 请务必记下(使用记事本)模块名称,因为稍后会使用它。

  7. 此时会发现面板上显示预生成的 Docker 映像存储库地址。 如下所示:

    localhost:5000/-模板名称-

  8. 删除 localhost:5000,并将容器注册表登录服务器地址插入到创建容器注册表服务时记下的容器注册表登录服务器地址(在第 2 章的步骤 8 中)。 按 Enter 键确认地址

  9. 此时,将创建包含 Python 模块模板的解决方案,其结构将显示在屏幕左侧的 VS Code 的“浏览”选项卡中。 如果“浏览”选项卡未打开,可以通过单击左侧栏中最顶部的按钮将其打开。

    显示“浏览”选项卡按钮的屏幕截图,该按钮类似于两张堆积纸。

  10. 本章的最后一步是从“浏览”选项卡中单击并打开 .env 文件,然后添加容器注册表用户名和密码 git 会忽略此文件,但在生成容器时,此文件将设置用于访问容器注册表服务的凭据

    显示命令窗口的屏幕截图,其中第 1 行输入了文本“容器注册表用户名”,第 2 行输入了容器注册表密码。

第 8 章 - 编辑容器解决方案

现在将通过更新以下文件来完成容器解决方案:

  • main.py Python 脚本
  • requirements.txt
  • deployment.template.json
  • Dockerfile.amd64

然后创建“images”文件夹,Python 脚本将使用该文件夹来检查映像是否与自定义视觉模型相匹配。 最后将添加 labels.txt 文件(以帮助读取模型)和 model.pb 文件(即模型)

  1. 打开 VS Code,导航到模块文件夹,并查找名为main.py 的脚本。 双击将其打开。

  2. 删除文件内容并插入以下代码:

    # 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)
    
  3. 打开名为 requirements.txt 的文件,将其内容替换为以下内容

    azure-iothub-device-client==1.4.0.0b3
    opencv-python==3.3.1.11
    tensorflow==1.8.0
    pillow==5.1.0
    
  4. 打开名为 deployment.template.json 的文件,并按照以下准则替换其内容

    1. 由于你将拥有自己的唯一 JSON 结构,因此需要手动编辑它(而不是复制示例)。 为了方便操作,请使用下图作为指南。

    2. 看起来不同于你的内容但不应更改的区域以黄色突出显示

    3. 需要删除的部分以红色突出显示

    4. 请小心删除正确的括号以及逗号。

      显示代码行 12 到 15、49 到 57 的代码行的屏幕截图,第 67 行以黄色圆圈。代码行 39 到 48 行,第 66 行以红色圆圈。

    5. 已完成的 JSON 应如下图所示(虽有唯一不同之处:用户名/密码/模块名称/模块引用):

      显示命令窗口的屏幕截图,其中删除了带圆圈的红线。

  5. 打开名为 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" ]
    
    
  6. 右键单击模块下的文件夹(它将具有之前提供的名称;在接下来的示例中,它称为 pythonmodule),然后单击“新建文件夹”。 将文件夹命名为“images”

  7. 在此文件夹中,添加一些包含鼠标或键盘的映像。 这些是 Tensorflow 模型将会分析的映像。

    警告

    如果使用自己的模型,则需要更改此模型以反映自己的模型数据。

  8. 现在需要从模型文件夹中检索之前在第 1 章中下载(或从自己的自定义视觉服务创建)的 labels.txt 和 model.pb 文件。 获得文件后,将它们与其他文件一起放在解决方案中。 最终结果的外观应类似于下图:

    显示“资源管理器”窗口的屏幕截图。Python 模块文件夹已打开。

第 9 章 - 将解决方案打包为容器

  1. 现在可以将文件“打包”为容器,并将容器推送到 Azure 容器注册表。 在 VS Code 中,打开集成终端(“查看”>“集成终端”或Ctrl + ),然后使用以下行登录到 Docker(将命令值替换为 Azure 容器注册表 (ACR) 的凭据)`

        docker login -u <ACR username> -p <ACR password> <ACR login server>
    
  2. 右键单击文件 deployment.template.json,然后单击“生成 IoT Edge 解决方案”。 此生成过程需要很长时间(具体取决于设备),因此请准备好等待。 生成过程完成后,将在名为 config 的新文件夹中创建 deployment.json 文件。

    显示配置文件夹和部署点 jason 文件以红色圆圈的屏幕截图。

  3. 再次打开“命令面板”,并搜索“Azure: 登录”。 按照提示使用 Azure 帐户凭据;VS Code 将提供“复制并打开”选项,该选项将复制即将需要的设备代码并打开默认 Web 浏览器。 在系统要求时,粘贴设备代码以对计算机进行身份验证。

    复制并打开

  4. 登录后,会发现“浏览”面板底部出现一个名为“Azure IoT 中心设备”的新的部分。 单击此部分将其展开。

    Edge 设备

  5. 如果设备不在此处,则需要右键单击“Azure IoT 中心设备”,然后单击“设置 IoT 中心连接字符串”。 然后,会看到命令面板(位于 VS Code 顶部)提示输入连接字符串。 这是在第 3 章末尾记下的连接字符串。 将字符串复制进去后,按 Enter 键

  6. 此时应加载并显示设备。 右键单击设备名称,然后单击“为单个设备创建部署”。

    显示右键单击菜单的屏幕截图。突出显示了“为单个设备创建部署”。

  7. 将获得一条文件资源管理器提示,可在其中导航到 config 文件夹,然后选择 deployment.json 文件。 选择该文件后,单击“选择 Edge 部署清单”按钮。

    显示文件资源管理器窗口的屏幕截图。选择部署点 jason 文件,选择 Edge 部署清单以红色圆圈。

  8. 此时已为 IoT 中心服务提供了清单,以便其将容器作为模块从 Azure 容器注册表有效部署到设备

  9. 若要查看从设备发送到 IoT 中心的消息,请在“资源管理器”面板的“Azure IoT 中心设备”部分中再次右键单击设备名称,然后单击“开始监视 D2C 消息”。 从设备发送的消息应显示在 VS 终端中。 请耐心等待,因为这可能需要一些时间。 请参阅下一章进行调试,并检查部署是否成功。

此模块现在将循环访问 images 文件夹中的图像,并分析每一次迭代的图像。 显然,这只演示了如何在 IoT Edge 设备环境中使用基本机器学习模型。

若要扩展此示例的功能,可以通过多种方法继续操作。 一种方法可以是在容器中包含一些代码,这些代码从连接到设备的网络摄像头捕获照片,并将图像保存在 images 文件夹中。

另一种方法可以是将图像从 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 表服务。

  1. 如果尚未登录,请登录到 Azure 门户

  2. 登录后,单击左上角的“创建资源”,搜索“存储帐户”,然后按 Enter 键开始搜索

  3. 显示搜索结果后,单击列表中的“存储帐户 - Blob、文件、表、队列”

    显示“新建”窗口的屏幕截图。存储在搜索栏中输入。在“特别推荐”下,存储帐户 blob、文件、表、队列以红色圆圈。

  4. 新页面将提供“存储帐户”服务的说明。 在该提示的左下角,单击“创建”按钮,创建此服务的实例

    显示“存储帐户”页的屏幕截图。页面底部的“创建”按钮以红色圆圈。

  5. 单击“创建”后,随即显示一个面板

    1. 插入此服务实例的所需名称(必须全部小写)

    2. 对于“部署模型”,请单击“资源管理器”

    3. 对于“帐户类型”,请使用下拉菜单并单击“存储(常规用途 v1)”

    4. 单击相应的位置

    5. 对于“复制”下拉菜单,请单击“读取访问异地冗余存储(RA-GRS)”

    6. 对于“性能”,请单击“标准”。

    7. 在“需要安全传输”部分中,单击“禁用”

    8. 在“订阅”下拉菜单中,单击相应的订阅

    9. 选择一个资源组或创建一个新资源组。 资源组提供一种监视、控制访问、预配和管理 Azure 资产集合计费的方法。 建议保留与常用资源组下的单个项目(例如这些课程)关联的所有 Azure 服务。

      若要详细了解 Azure 资源组,请单击此链接了解如何管理资源组

    10. 将“虚拟网络”保留为“禁用”状态(如果有此选项)

    11. 单击 “创建”

      填充存储详细信息

  6. 单击“创建”后,必须等待服务创建,这可能需要一分钟时间

  7. 创建服务实例后,门户中将显示一条通知。 单击通知以浏览新的服务实例。

    新的存储通知

  8. 单击通知中的“转到资源”按钮,将转到新的存储服务实例概述页。

    显示部署成功通知的屏幕截图。“转到资源”按钮以红色圆圈。

  9. 在概述页中,单击右侧的“表”

    表

  10. 右侧面板将更改为显示“表服务”信息,其中需要添加新表。 为此,单击左上角的“+ 表”按钮

    打开表

  11. 随即显示一个新页面,其中需要输入表名称。 这是稍后几章(创建函数应用和 Power BI)中用于引用应用程序数据的名称。 插入 IoTMessages 作为名称(可以选择自己的名称,只需记住它以便在本文档稍后使用),然后单击“确定”

  12. 创建新表后,可以在“表服务”页(底部)看到此表

    显示“表服务”页的屏幕截图,其中列出了一个表。

  13. 现在单击“访问密钥”,并复制存储帐户名称和密钥(使用记事本)。本课程稍后创建 Azure 函数应用时将使用这些值

    显示“设置”下的“访问密钥”页的屏幕截图。存储帐户名称字段和密钥字段以红色圆圈。

  14. 再次使用左侧面板,滚动到“表服务”部分,单击“表”(或更新门户中的“浏览表”),然后复制表 URL(使用记事本)。 本课程稍后将表链接到 Power BI 应用程序时,将使用此值。

    显示“表服务”下的“表”页的屏幕截图。I O T 消息表的 U R L 以红色圆圈。

第 12 章 - 完成 Azure 表

设置表服务存储帐户后,可以向其添加数据,之后将用于存储和检索信息。 可以通过 Visual Studio 来编辑表。

  1. 打开 Visual Studio(非 Visual Studio Code)

  2. 从菜单中,单击“查看”>“Cloud Explorer”。

    打开 Cloud Explorer

  3. “Cloud Explorer”将以停靠项方式打开(请耐心等待,加载可能需要一些时间)。

    警告

    如果用于创建存储帐户的订阅不可见,请确保

    • 已登录的帐户与用于 Azure 门户的帐户相同。

    • 从“帐户管理”页选择了订阅(可能需要在帐户设置中应用筛选器):

      查找订阅

  4. 随即将显示 Azure 云服务。 查找存储帐户,并单击其左侧的箭头以展开帐户

    打开存储帐户

  5. 展开后,新创建的存储帐户应可用。 单击存储左侧的箭头,然后在展开后,查找“表”并单击表旁边的箭头,以显示在上一章中创建的表。 双击表

  6. 表将在 Visual Studio 窗口的中央位置打开。 单击带有 +(加号)的表图标。

    添加新表

  7. 随即显示一个窗口,提示“添加实体”。 只会创建一个实体,但它具有三个属性。 会注意到已提供“PartitionKey”和“RowKey”,因为表使用这些属性来查找数据

    分区键和行键

  8. 请更新以下值:

    • 名称:PartitionKey,值:PK_IoTMessages

    • 名称:RowKey,值:RK_1_IoTMessages

  9. 然后,单击“添加属性”(“添加实体”窗口的左下角)并添加以下属性

    • 作为字符串的 MessageContent,值保留为空
  10. 表应与下图中的表匹配:

    添加正确的值

    注意

    在行键中,实体的编号为 1,是因为你可能需要添加更多消息,希望在本课程中进行进一步试验。

  11. 完成后,单击确定。 表现已准备就绪,可供使用。

第 13 章 - 创建 Azure 函数应用

现在创建 Azure 函数应用,IoT 中心服务将调用该应用将 IoT Edge 设备消息存储在上一章中创建的表服务中

首先,需要创建一个文件,该文件允许 Azure 函数加载所需的库。

  1. 打开“记事本”(按 Windows 键,然后键入“记事本”)。

    打开记事本

  2. 打开“记事本”后,将下面的 JSON 结构插入其中。 完成此操作后,将其以 project.json 形式保存在桌面上。 此文件定义函数将使用的库。 如果使用过 NuGet,你会非常熟悉此文件。

    警告

    命名务必正确,确保它没有 .txt 文件扩展名。 请参阅下图以供参考:

    保存为 JSON

    {
    "frameworks": {
        "net46":{
        "dependencies": {
            "WindowsAzure.Storage": "9.2.0"
        }
        }
    }
    }
    
  3. 登录到 Azure 门户

  4. 登录后,单击左上角的“创建资源”,搜索“函数应用”,然后按 Enter 键进行搜索。 在结果中单击“函数应用”,以打开新面板。

    搜索函数应用

  5. 新面板将提供“函数应用”服务的说明。 在此面板的左下角,单击“创建”按钮,以创建与此服务的关联

    函数应用实例

  6. 单击“创建”后,请填写以下内容

    1. 对于“应用名称”,请插入此服务实例的所需名称

    2. 选择一个“订阅” 。

    3. 选择适合的定价层,如果这是第一次创建函数应用服务,则应可使用免费层

    4. 选择一个资源组或创建一个新资源组。 资源组提供一种监视、控制访问、预配和管理 Azure 资产集合计费的方法。 建议保留与常用资源组下的单个项目(例如这些课程)关联的所有 Azure 服务。

      若要详细了解 Azure 资源组,请单击此链接了解如何管理资源组

    5. 对于“OS”,请单击“Windows”,因为这是目标平台

    6. 选择一种托管计划(本教程使用“消耗计划”)。

    7. 选择一个位置(选择与在上一步中生成的存储相同的位置

    8. 对于“存储”部分,必须选择在上一步中创建的存储服务。

    9. 此应用中无需 Application Insights,因此可根据需要将其保留为“关闭”状态

    10. 单击 “创建”

      新建实例

  7. 单击“创建”后,必须等待服务创建,这可能需要一分钟时间

  8. 创建服务实例后,门户中将显示一条通知。

    新通知

  9. 部署成功(已完成)后,单击此通知。

  10. 单击通知中的“转到资源”按钮,浏览新的服务实例

    显示“部署成功通知”窗口的屏幕截图。“转到资源”按钮以红色圆圈。

  11. 在新面板的左侧,单击“函数”旁边的 +(加号)图标以创建新函数

    显示“函数应用”页的屏幕截图。在 Functions 旁边的左侧菜单中,加号以红色圆圈。

  12. 在中央面板中,将显示“函数”创建窗口。 再向下滚动,然后单击“自定义函数”

    显示“函数创建”窗口底部的屏幕截图。自定义函数以红色圆圈。

  13. 在下一页中向下滚动,直到找到“IoT 中心(事件中心)”,然后单击它

    显示以红色圆圈圈的 I O T 中心事件中心框的屏幕截图。

  14. 在“IoT 中心(事件中心)”边栏选项卡中,将“语言”设置为“C#”,然后单击“新建”

    显示“新建函数”页的屏幕截图。C 锐利在“语言”字段中被选中。“事件中心连接”选项旁的红色圆圈新建。

  15. 在出现的窗口中,确保已选中“IoT 中心”,并且“IoT 中心”字段的名称对应于之前(第 3 章,步骤 8)创建的“IoT 中心服务”的名称。 然后单击“选择”按钮。

    显示“I O T 中心”选项卡打开的屏幕截图。I O T 中心和终结点字段以红色圆圈。

  16. 返回到“IoT 中心(事件中心)”边栏选项卡,单击“创建”

    显示“新建函数”页的屏幕截图。页面底部的“创建”按钮以红色圆圈。

  17. 将会重定向到函数编辑器。

    显示函数编辑器页的屏幕截图。该函数在左侧菜单中处于选中状态。

  18. 删除其中的所有代码,并将其替换为以下代码:

    #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; }   
    }
    
  19. 更改以下变量,以便它们对应于存储帐户中找到的相应值(分别是第 11 章步骤 11 和 13 中的“表”和“存储”值)

    • tableName,替换为位于存储帐户的表的名称
    • tableURL,替换为位于存储帐户的表的 URL
    • storageAccountName,替换为与存储帐户名称的名称对应的值的名称
    • storageAccountKey,替换为在之前创建的存储服务中获取的密钥

    显示命令提示符的屏幕截图。第 15 行、16 行、19 行和 21 行以红色圆圈。

  20. 代码准备就绪后,单击“保存”

  21. 接下来,单击页面右侧的 <(箭头)图标。

    显示“函数应用”页的屏幕截图。

  22. 随即一个面板从右侧滑入。 在该面板中,单击“上传”,随即显示“文件浏览器”

  23. 导航到之前在记事本中创建的 project.json 文件并单击该文件,然后单击“打开”按钮。 此文件定义函数将使用的库。

    显示“查看文件”下红色圆圈的上传按钮的屏幕截图。在文件浏览器中,已选择项目点 jason。

  24. 文件上传后,它将显示在右侧的面板中。 单击它会在“函数”编辑器中打开它。 它必须与下图完全相同

    显示“函数应用”页的屏幕截图。左侧菜单中的集成以红色圆圈。

  25. 此时,最好测试函数将消息存储在表中的功能。 在窗口的右上角,单击“测试”

    显示“函数应用”页的屏幕截图。右上角的测试,左下角的“运行”按钮以红色圆圈。

  26. 在请求正文中插入一条消息(如上图所示),然后单击“运行”

  27. 函数将运行并显示结果状态(会发现“输出”窗口上方显示绿色“状态 202 已接受”,意味着调用已成功):

    输出结果

第 14 章 - 查看活动消息

如果现在打开 Visual Studio(非 Visual Studio Code),可以可视化测试消息结果,因为它将存储在“MessageContent”字符串区域中

显示 Visual Studio 中打开的“I O T 消息表”选项卡 Microsoft的屏幕截图。

在表服务和函数应用准备就绪后,Ubuntu 设备消息将显示在“IoTMessages”表中。 如果尚未运行,则重新启动设备,这样便能通过使用 Visual Studio Cloud Explorer 在表中看到来自设备和模块的结果消息

可视化数据

第 15 章 - 设置 Power BI

若要可视化 IOT 设备的数据,需要设置 Power BI(桌面版本),以便从刚创建的表服务中收集数据。 然后,Power BI 的 HoloLens 版本将使用该数据来可视化结果

  1. 打开 Windows 10 上的 Microsoft Store 并搜索“Power BI Desktop”

    显示“Microsoft应用商店”窗口的屏幕截图。Power B I 在搜索栏中输入,Power B I Desktop 以红色圆圈。

  2. 下载此应用程序。 下载完成后,将其打开。

  3. 使用 Microsoft 365 帐户登录 Power BI。 可能会重定向到浏览器进行注册。 注册后,返回到 Power BI 应用,然后再次登录。

  4. 单击“获取数据”,然后单击“更多...”

    显示 Power BI Desktop 的屏幕截图。在“获取数据”下拉菜单中,更多内容以红色圆圈。

  5. 依次单击“Azure”、“Azure 表存储”,然后单击“连接”

    显示“获取数据”窗口的屏幕截图。Azure 表存储在 Azure 菜单选项中选择。右下角的“连接”按钮以红色圆圈。

  6. 系统将提示插入先前创建表服务时收集的“表 URL”(第 11 章,步骤 13。 插入 URL 后,删除路径中引用表“子文件夹”的部分(在本课程中为 IoTMessages)。 最终结果应如下图所示。 然后单击“确定”。

    显示“Azure 表存储”对话框的屏幕截图。在“帐户名称”或“U R L”字段中输入 U R L。

  7. 系统将提示插入先前创建表存储时记下的“存储密钥”(第 11 章,步骤 11。 然后单击“连接”

    显示 Azure 表存储帐户密钥页的屏幕截图。

  8. 随即将显示“导航器”面板,勾选表旁边的框,然后单击“加载”

    显示导航器面板的屏幕截图。选中 I O T 消息文件旁边的框。

  9. Power BI 中现已加载此表,但需要查询来显示表中的值。 为此,请右键单击屏幕右侧“字段”面板中的表名称。 然后单击“编辑查询”

    显示“字段”面板的屏幕截图。“I O T 消息”右键单击菜单中的“编辑查询”以红色圆圈。

  10. “Power Query 编辑器”将作为新窗口打开,并显示表。 单击表的“内容”列中的字词“记录”,以可视化存储的内容

    显示Power Query 编辑器窗口的屏幕截图。在“内容”下,“记录”以红色圆圈。

  11. 单击窗口左上角的“信息表”

    显示Power Query 编辑器中的“转换”选项卡的屏幕截图。在左上角,“进入表格”以红色圆圈。

  12. 单击“关闭并应用”

    显示Power Query 编辑器中的“开始”选项卡的屏幕截图。关闭并应用以红色圆圈。

  13. 查询加载完成后,在屏幕右侧的“字段”面板中,勾选参数名称和值对应的框,以可视化“MessageContent”列内容。

    显示“字段”面板的屏幕截图。在“I O T 消息”下,检查名称和值。带有“名称和值”列的弹出窗口以红色圆圈。

  14. 单击窗口左上角的蓝色磁盘图标,将工作保存到所选的文件夹中

    显示 Power BI Desktop 的屏幕截图。左上角的蓝色磁盘以红色圆圈。“另存为”对话框处于打开状态,“保存”按钮以红色圆圈。

  15. 现在可以单击“发布”按钮将表上传到工作区。 出现提示时,单击“我的工作区”,然后单击“选择”。 等待显示成功提交的结果。

    显示“发布”圆圈为红色的屏幕截图。在“发布到 Power B I”对话框中,“选择目标”下选择了“我的工作区”。

    显示一条通知的屏幕截图,指示发布到 Power B I 是否成功。

警告

下一章内容特定于 HoloLens。 Power BI 目前不能作为沉浸式应用程序使用,但可以通过桌面应用在 Windows Mixed Reality 门户中运行桌面版本(也称为“悬崖小屋”)。

第 16 章 - 在 HoloLens 上显示 Power BI 数据

  1. 在 HoloLens 上,通过在应用程序列表中点击图标登录到“Microsoft Store”

    Holo Lens 显示导航窗口中Microsoft应用商店图标周围的红色圆圈。

  2. 搜索并下载“Power BI”应用程序。

    显示Microsoft应用商店页面的 Holo Lens 显示。已选择滚动工具,并在搜索栏中输入 Power B。

  3. 从应用程序列表启动“Power BI”

  4. Power BI 可能会要求登录到 Microsoft 365 帐户

  5. 进入应用后,默认情况下工作区应会显示,如下图所示。 如果未显示,只需单击窗口左侧的工作区图标。

    Holo Lens 显示Microsoft Power B I 页面。拖动工具在顶部处于选中状态。工作区图标(类似于图形)以红色圆圈。报表下的 I O T Edge 表以红色圆圈。

你已完成 IoT 中心应用程序

恭喜,你已成功创建 IoT 中心服务以及模拟虚拟机 Edge 设备。 通过 Azure 函数应用,设备可以将机器学习模型的结果传达给 Azure 表服务,随后将结果读入 Power BI 并在 Microsoft HoloLens 内进行可视化。

Holo Lens 显示Microsoft Power B I 页面。

额外练习

练习 1

扩展表中存储的消息结构并将其显示为图形。 可能要收集更多数据,并将其存储在同一个表中,以便以后显示。

练习 2

额外创建一个“摄像头捕获”模块以部署在 IoT 开发板上,从而通过摄像头捕获要分析的图像。