Kinect

采用 Kinect 的上下文感知对话

Leland Holmquest

下载代码示例

与我的办公室助理 Lily 会面。 我们经常交谈,在我的指导下,Lily 负责执行常见业务任务,例如查找信息和处理 Microsoft Office 文档。 但更为重要的是,Lily 是虚拟办公室助理,是一个支持 Microsoft Kinect 的 Windows Presentation Foundation (WPF) 应用程序,属于可推动上下文感知对话方式和多模式沟通方式发展的项目的一部分。

在论述我的应用程序(我在乔治梅森大学完成毕业论文期间开发了该应用程序)中的代码细节之前,我将首先介绍上下文感知对话和多模式沟通的含义。

上下文感知对话和多模式沟通

人与人之间的沟通有着丰富而复杂的含义。 请考虑以下场景: 一个幼儿开始哭闹。 当幼儿注意到他的妈妈正在看他时,他指向地上的饼干。 妈妈同情地露出微笑,弯下腰,捡起地上的饼干并将其还给幼儿。 幼儿因重获宝物而兴奋不已,他尖叫着并快速拍手,然后贪婪地抓住饼干。

此场景描述了一个简单的事件序列。 但请进一步了解一下。 查看所进行的沟通的模式。 考虑实现一个软件系统,其中没有幼儿或妈妈,沟通由系统帮助进行。 您很快便会发现,这两个参与者所采用的沟通方式实际上是多么复杂、难懂。 在理解幼儿的哭喊、高兴的尖叫以及拍手声时,需要进行音频处理。 同时还需要进行视觉分析,以理解幼儿指向饼干时的手势,并推断妈妈通过露出同情的微笑所做出的温和的责备。 通常,上述举止行为普遍存在,我们对所采用的复杂级别已经习以为常,直至通过计算机实现相同级别的体验。

让我们略微增加沟通方法的复杂性。 请考虑以下场景。 您走进一个房间,房间里有几个人正在交谈。 您只听到一个词: “酷”。房间里的其他人希望您加入他们的谈话。 您能做些什么? “酷”可能表示很多含义。 例如,此人可能正在谈论房间里的温度。 说话者可能正在对某件事情表示赞同(“那辆车真酷”)。 此人可能正在谈论国家/地区之间的关系(“谈判开始进入僵持状态”)。 如果不考虑某个词的具体语境,那么此时几乎无法了解这个词的含义。 必须有某种级别的语义理解,才能理解预期的含义。 此概念是本文的核心。

项目 Lily

在乔治梅森大学就学期间,我在 João Pedro Sousa 博士 的指导下创建了项目 Lily 作为最终项目 CS895:用于上下文感知多用户系统的软件 。 如上所述,Lily 是一个用于典型商务办公环境的虚拟助手。 我使用的是 Kinect 设备和 Kinect for Windows SDK Beta 2。 Kinect 提供一个彩色相机、一个可感知深度的相机、一个包含四个麦克风的阵列以及一个简易的可用于创建自然 UI 的 API。 此外,Microsoft Kinect for Windows 站点 (microsoft.com/en-us/kinectforwindows) 和第 9 频道 (bit.ly/zD15UR) 还提供了很多有用的相关示例。 Kinect 在(相对)低成本的程序包中为开发者提供了难以置信的功能。 打破吉尼斯世界纪录“卖的最快的消费类设备”记录的 Kinect 证实了这一点 (on.mash.to/hVbZOA)。 Kinect 技术规范 (bit.ly/zZ1PN7) 包括:

  • 彩色 VGA 运动型相机: 速度为 30 帧/秒 (fps) 时,像素分辨率为 640x480
  • 深度相机: 速度为 30 fps 时,像素分辨率为 640x480
  • 包含 4 个麦克风的阵列
  • 视野
    • 水平视野: 57 度
    • 垂直视野: 43 度
    • 身体倾斜角度: ± 27 度
    • 深度传感器范围: 1.2m - 3.5m
  • 骨骼跟踪系统
    • 最多能够跟踪六个人,包括两个活动玩家
    • 能够跟踪每个活动玩家的 20 个关节的运动
  • 可增强语音输入效果的回声消除系统
  • 语音识别

Kinect 体系结构

Microsoft 还提供了作为 Kinect 的构建基础的体系结构的说明,如图 1 所示。

Kinect for Windows Architecture
图 1 Kinect for Windows 体系结构

图 1 中圆圈中的数字对应于以下内容:

  1. Kinect 硬件: 包括 Kinect 传感器和 USB 集线器在内的硬件组件,传感器通过这些组件连接至计算机。
  2. Microsoft Kinect 驱动程序: 在本文所述的测试版 SDK 安装过程中,已安装用于 Kinect 传感器的 Windows 7 驱动程序。 Microsoft Kinect 驱动程序支持:
    • 将 Kinect 传感器的麦克风阵列作为可通过 Windows 中的标准音频 API 访问的内核模式音频设备。
    • 流式处理图像和景深数据。
    • 允许应用程序使用多个连接至计算机的 Kinect 传感器设备枚举功能。
  3. NUI API: 一组从图像传感器中检索数据并控制 Kinect 设备的 API。
  4. KinectAudio DMO: Kinect DMO 扩展了 Windows 7 中的麦克风阵列支持,以提供波束成型和源代码本地化功能。
  5. Windows 7 标准 API: Windows 7 中的音频、语音和介质 API,如 Windows 7 SDK 和 Microsoft Speech SDK(Kinect for Windows SDK Beta 编程指南)中所述。

在本文中,我将演示如何使用麦克风阵列和语音识别引擎 (SRE) 创建特定于上下文的词汇。 换句话说,Kinect 听到的词汇将依赖于用户正在创建的上下文。 我将演示一个框架,应用程序将在该框架中监视用户正在执行的操作、在 SRE 内外部交换语法(取决于上下文),以便用户可以自然且直观的方式与 Lily 交互,而不必记住特定命令和使用模式。

SpeechTracker 类

对于 Lily 项目,我使用了一个单独的名为 SpeechTracker 的类来处理所有语音。 SpeechTracker 设计用于在独立于 UI 的线程中运行,以使其响应性更好,这是此应用程序的一个关键方面。 从不倾听的助手有何优点?

在讨论 SpeechTracker 的核心内容之前,需要先考虑几个问题。 首先应确定应用程序需要倾听的上下文。 例如,Lily 将有一个“Research”上下文用于处理与查找数据和信息有关的操作;“Office”上下文处理打开 Word 文档和 PowerPoint 演示文稿等操作以及与 Office 设置有关的其他任务;“Operations”上下文允许用户对 Kinect 设备进行调整;“General”上下文用于处理独立于任何真实上下文的所有琐碎事务(例如,“现在几点?”、“关闭”系统或为系统提供反馈)。 我还在示例中创建了其他几个上下文,但另一个非常重要的上下文是“Context”。 此上下文用于和 Lily 应该位于的引用帧进行通信。 换句话说,“Context”枚举用于确定系统应倾听的上下文。 稍后将对此进行详细介绍。

然后,通过使用枚举对每个上下文建模,如图 2 中所示。

Example of Context
图 2:上下文示例

对上下文建模后,接下来需要一种方法来传达用户在指定上下文中的意图。 为了满足此要求,我使用了一个结构,其中只包含一个用于每个上下文的储存器和一个布尔值:

struct Intention
{
  public Context context;
  public General general;
  public Research research;
  public Office office;
  public Operations kinnyops;
  public Meeting meeting;
  public Translate translate;
  public Entertain entertain;
  public bool contextOnly;
}

该布尔值非常重要。 如果 Intention 只是执行上下文更改,则 contextOnly 为 true,否则为 false。 我将在稍后详细介绍此布尔值的使用;现在,只需知道它是一个必需标记。

用户的意图是什么?

现在,项目 Lily 能够和 Intention 通信,它需要知道在运行时使用 Intention 的时间以及使用哪些 Intention。 为执行此操作,我创建了一个 System.Collections.Generic.Dictionary<TKey, TValue> 字典,其中,键是说出的词或短语,值是相关联的 Intention。 从 Microsoft .NET Framework 3.0 起,可采用一种简洁的方法来创建对象和初始化属性,如图 3 中所示。

The ContextPhrases Dictionary
图 3 ContextPhrases 字典

该特定 Dictionary 定义 Context 短语。 键是最终用户说出的、然后与 Intention 关联的词和短语。 将在单行中声明每个 Intention 并设置其属性。 请注意,单个 Intention(例如,Research)可以映射到多个单词和短语。 这样便可创建丰富的词汇,以便对特定于域的语言和随手可及的主题词典建模。 (请注意有关 ContextPhrases 的一个重要事项: 属性 contextOnly 设置为 true。 此设置告知系统,此操作仅用于更改处于活动状态的上下文。 这使得系统可以在处理发音事件后对逻辑进行短期循环。)

要通过示例更好地了解这一点例,请查看图 4 中所示的 GeneralPhrases 字典中的代码片段。

The GeneralPhrases Dictionary
图 4 GeneralPhrases 字典

请注意,在某些情况下,可使用不同短语对同一 Intention 建模,以使系统能够以丰富、人性化的方式处理与用户的对话。 您需要了解的一个限制是,SRE 将要使用的任何字典所包含的条目不能超过 300 个。 因此,比较明智的做法是谨慎地对词汇和语法建模。 即使这不是强制限制,也应将其视为尽可能减少字典中条目数以获得卓越性能的最佳做法。

现在,词汇已映射到 Intention,我可以开始介绍最有趣的部分了。 首先,系统需要获得 SpeechRecognitionEngine 的句柄:

ri = SpeechRecognitionEngine.InstalledRecognizers()
    .Where(r => r.Id ==  RecognizerId).FirstOrDefault();
if (ri == null)
{
  // No RecognizerInfo => bail
  return;
}
sre = new SpeechRecognitionEngine(ri.Id);

现在,我将获取之前设置的短语并将其转换为 Choices:

// Build our categories of Choices
var contextPhrases = new Choices();
foreach (var phrase in ContextPhrases)
  contextPhrases.Add(phrase.Key);

我将对前面创建的所有短语执行相同操作。 然后,将 Choices 传入 GrammarBuilder 上的 Append 方法。 最后需要创建 Grammar 对象并将其加载到 SRE 中。 为完成此操作,我只需创建一个新的 Grammar 并传入表示所需语法的 GrammarBuilder,如图 5 中所示。

图 5 创建 Grammar 并加载语音识别引擎

// And finally create our Grammars
gContext = new Grammar(gbContext);
gGeneral = new Grammar(gbGeneral);
gResearch = new Grammar(gbResearch);
gOffice = new Grammar(gbOffice);
gOperations = new Grammar(gbOperations);
gMeeting = new Grammar(gbMeeting);
gTranslation = new Grammar(gbTranslation);
gEntertain = new Grammar(gbEntertain);
// We're only going to load the Context and General grammars at this point
sre.LoadGrammar(gContext);
sre.LoadGrammar(gGeneral);
allDicts = new List<Dictionary<string, 
    Intention>>() { ContextPhrases,
                    GeneralPhrases,
                    ResearchPhrases,
                    OfficePhrases,
                    OperationsPhrases,
                    MeetingPhrases,
                    TranslationPhrases,
                    EntertainPhrases };

请注意,只有两个 Grammar gContext 和 gGeneral 加载到了 SRE 中,并且所有 Grammar 都加载到了短语列表中。 我通过此方式来影响倾听内容的上下文感知部分。 General 和 Context 短语需要始终位于 SRE 中,因为用户随时可能会说这些短语,没有任何预先确定的模式。 但是,在识别上下文短语时,将加载另一个 Grammar。 为了完成应用程序的这一部分,我只处理 SRE 中的 SpeechRecognized 事件。 SpeechRecognizedEventArgs 参数用于评估说话内容。 参考前面的 4,如果参数有一个标记为 contextOnly 的 Intention,则系统需要从 SRE 中卸载第三个 Grammar(如果存在一个)并加载新标识的 Grammar。 这使得应用程序可以根据当前范围内的上下文倾听不同的词汇和词典。

字典(参见图 4)包含一个表示所说短语的字符串类型的键,还有一个 Intention 类型的值,用于指示系统为满足用户需要而执行的操作。 字典中添加的每个条目都是 Intention 的构造函数,该构造函数通常由三个部分组成: 上下文关联;是否为上下文更改事件;以及要执行的操作(如果是非上下文更改)。

定义了受支持短语的所有字典后,我将此信息添加到 Kinect 设备提供的 SRE 中,如图 6 中所示。

Speech Recognition Engine
图 6 语音识别引擎

这将有效告知 SRE 要倾听哪些内容以及在听到信息后如何标记该信息。 但是,为了使系统更加智能、易用,我已将系统限制为仅在任意指定时间将三个上下文及其各自的短语字典加载到 SRE 中。 由于其基础特性,将始终加载 Context 和 General 短语。 加载的第三个上下文和短语由与最终用户进行的交互决定。 当 Lily 侦听环境时,“她”会对关键的词和短语做出反应并删除一组短语,以将其替换为 SRE 中的另一组短语。 列举示例可以帮助说明这一点。

工作原理

当 Lily 初次启动时,会将 ContextPhrases 和 GeneralPhrases 加载到 SRE 中。 这使得系统能够听到将使系统上下文发生更改或推动执行常规操作的命令。 例如,在完成初始化之后,如果用户问“现在几点了?”,Lily 将会“理解”此问题(它是 GeneralPhrases 的一部分)并用当前时间做出答复。 类似地,如果用户说“我需要一些信息”,Lily 会理解为这是一个用于将 ResearchPhrases 加载到 SRE 中的标记,并开始倾听映射到 Research 上下文的 Intention。 这使得 Lily 可以实现三个重要目标:

  1. 通过仅倾听可能相关的最少的一组短语,来最大程度地提高 Lily 的性能。
  2. 通过只允许使用包含指定上下文的词典,支持使用由于在不同上下文中具有不同含义(回想一下“酷”示例)而变得模糊的语言。
  3. 使系统可以倾听多种不同短语,但将多个短语映射到相同操作(例如,“Lily,现在几点了?”“现在几点了?”和“您知道现在的时间吗?”都可以映射到同一操作,即告诉用户时间)。 这可能会为与系统进行的上下文特定对话提供丰富的词典。 不强制用户以一对一的方式记住关键字或短语,而是将预期操作映射到一个命令,以便设计人员可对多种不同的常见方式建模,以指示同样的事情。 这使得用户能够以正常且轻松的方式灵活地表述。 普适计算的目标之一是让设备淡入背景中。 创建上下文感知的对话系统(如 Lily)可帮助用户不再有意识地去关注计算机 - 它变成了一个助手,而不是应用程序。

装备所有必备知识后,现在 Lily 可以倾听并用符合上下文的操作来做出答复。 还需要做的一件事情是实例化 KinectAudioSource 并指定其参数。 对于 Lily 项目,我将所有音频处理都放在 SpeechTracker 类中。 然后,我在新线程中调用 BeginListening 方法,将其与 UI 线程分离。 图 7 显示了该方法。

图 7 KinectAudioSource

private void BeginListening()
{
  kinectSource = new KinectAudioSource());
  kinectSource.SystemMode = SystemMode.OptibeamArrayOnly;
  kinectSource.FeatureMode = true;
  kinectSource.AutomaticGainControl = true;
  kinectSource.MicArrayMode = MicArrayMode.MicArrayAdaptiveBeam;
  var kinectStream = kinectSource.Start();
  sre.SetInputToAudioStream(kinectStream,
       new SpeechAudioFormatInfo(EncodingFormat.Pcm,
       16000,
       16,
       1,
       32000,
       2,
       null));
sre.RecognizeAsync(RecognizeMode.Multiple);
}

根据生成的应用程序,可以设置多个参数。 有关这些选项的详细信息,请参阅《Kinect for Windows SDK 编程指南》 (bit.ly/Avrfkd) 中的文档。 然后,我只让 WPF 应用程序向 SpeechTracker SpeechDetected 事件进行注册,该事件基本上是 SRE SpeechRecognized 事件的传递,但使用 Intention 作为事件参数的一部分。 如果 SRE 找到它已加载的任何上下文短语的匹配项,它会引发 SpeechRecognized 事件。 SpeechTracker 处理该事件并评估 Intention 是否指示对上下文进行更改。 如果指示更改上下文,SpeechTracker 将相应处理 Grammar 的卸载和加载并引发 SpeechContextChanged 事件。 否则,它将引发 Speech­Detected 事件并允许监视该事件的任何对象对其进行处理。

Confidence 属性

需要注意一点: 我在线找到的一个示例表明,SpeechRecognizedEventArgs 中的 Confidence 属性不可靠,将不使用它(与 SDK 文档不符)。 我发现,当不使用该属性时,即使用户什么也没说,也会不断引发 SpeechRecognized 事件。 因此,在我的 SpeechRecognized 事件处理程序中,我做的第一件事是检查 Confidence。 如果它的置信度没有超过 95%,则忽略结果。 (数字 95 只是来自试错法 - 我不能因分析有效值而居功。 95% 看起来可提供我要寻找的结果水平。 实际上,SDK 建议基于案例测试和评估此值。) 当我执行此操作时,误报率接近于 0。 因此,建议您认真对在线找到的任何语句和解决方案进行测试。 我的经验是,SDK 文档以及 Microsoft 在 Kinect for Windows 站点中提供的示例极具价值并极其精确。

经常有人问我: Kinect 需要进行多少语音识别培训? 以我的经验来看,无需进行任何培训。 在设置 Confidence 值后,Kinect 出色地完成了工作,无需任何培训、优化等。 我把自己作为主要测试对象,还包括我 7 岁和 8 岁的女儿(感谢 Kenzy 和 Shahrazad!),她们兴奋地发现他们能够告诉爸爸的计算机要做什么,并且计算机可以理解并执行相应操作。 这使得她们具有极大的控制权;而对爸爸而言则收获颇丰!

实现使用人类助理的梦想

能够根据系统可以在环境中观测到的内容切换上下文,可以在用户(人类)与 Lily(计算机)之间形成丰富的交互。 为了进一步真正实现使用助理的梦想,我添加了许多小功能。 例如,当我们与他人聊天时,我们不必一遍一遍地重复相同的话。 因此,在处理“pulse”Intention 时,系统会选出随机短语来答复用户。 换句话说,当用户问“Lily?”时,Lily 会随机答复“哎,”“我在这儿”“我能帮你做些什么?”或一些其他短语。

我将进一步阐明这一点。 有些短语包括一个存储器,用于存储特定于用户姓名或性别的代词(先生或女士)。 如果其中一个短语是随机选择的,则会随机确定是使用姓名还是代词。 这样便会创建从不完全相同的对话。 诸如此类的小细节看起来很琐碎,不值得做工作,但当您查看用户在与系统交互时获得的响应时,您会认识到我们的沟通与交流部分来自这些细微之处。 我发现我自认为 Lily 与其他应用程序有所不同。 当我进行测试和调试时,我需要查找一些规范或某些代码。 因此,当我开始搜索时,我将继续与 Lily 谈话并指示“她”关闭等。 过了一会儿,我发现在 Lily 关闭时,我错过了该级别的“人际交互”。 我认为这是我可以提供的用于证明 Kinect 能够开启自然 UI 的全新纪元的最佳证据。

为便于回顾,图 8 显示了创建用于倾听用户口令并执行相应操作的系统所需对象的流程图。

Creating a System that Listens and Acts on Verbal Commands
图 8 创建用于倾听号令和执行相应操作的系统

在下一篇文章中,我将介绍利用 Kinect 的深度和骨骼跟踪功能,并将其与语音识别功能相结合,以实现多模式沟通。 在多模式沟通组件中,将更加突出上下文的重要性。 您将了解如何将身体的运动与音频命令相链接,并让系统评估人类沟通频谱的完整性。

Leland Holmquest就职于 Microsoft。 以前,他曾为 Naval Surface Warfare Center Dahlgren 工作过。 他获得了乔治梅森大学信息技术专业的博士学位。

衷心感谢以下技术专家对本文的审阅: Russ Williams