嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

使用语者验证的访问控制系统

通过 Riverdi IoT Display 模块和 EasyVR 3 Plus 语音识别系统实现双重认证。

如今,我们已经习惯了使用简单语音命令的设备所带来的便利。在 Google Assistant 或 Alexa 等项目的推动下,虚拟助手将不仅可以用于远程开灯,而且还能获取当前天气信息,或订购披萨。

这一趋势没能逃过将语音控制系统集成在模块中的消费电子产品制造商的注意。在产品中应用已完全开发的助手(例如 Google Assistant、Siri 或 Alexa)要求设备运行全功能 Android 或 iOS 系统,这迫使设计师为设备提供足够的计算能力,并在硬件和编程方面大幅延长产品开发时间

通过语者验证实现双重认证

我们想达到与数字化安全同等程度的便利。正因为如此,我们制作了演示视频,用于指导用户如何在 Riverdi 显示屏上进行双重认证,其中第二重认证即采用 Sensory 创新技术的 语者验证。这开创了全球同类项目的先河。

本文中,借助 Riverdi IoT 显示智能模块,专用 Zerynth 编程环境(可使用 Python 方便地创建应用)和适用性较广的 Fortebit EasyVR 3 Plus 语音识别模块,我们将创建房间访问的双重认证控制系统,认证方法是输入 PIN 码并报出唯一访问密码。与用户系统之间的互动将通过 Riverdi IoT 显示模块的 5 英寸图形显示屏中显示的图形用户界面以及由 EasyVR 3 Plus 模块生成的音频消息实现。

已实施项目的方块图

在开始写入第一行代码之前,让我们先分析应用方块图。

一旦涉及简单的图形界面,就值得花时间制作清晰的窗口功能描述(参考图 1)。

  1. Riverdi 徽标 – 第一个窗口显示 Riverdi 徽标并确认显示初始化已完成。
  2. Riverdi 屏幕保护 – 屏幕保护在设备待机时出现,显示图案为不断移动的简单图形。借助 Fortebit EasyVR 模块,可以说出触发词“Hello device”来唤醒显示屏。触发指令使用非特定人 (SI) 技术,与语音密码使用的语者验证 (SV) 技术相反。
  3. PIN 码键盘 – 接着,用户需要输入 PIN 码(对应消息也将以音频消息的形式播放)。如果 PIN 码输入不正确,系统将提示用户再次输入密码。如果未能激活,应用将切换至默认屏幕保护显示模式。输入正确的 PIN 码后,用户将来到下一个认证环节。
  4. 输入语音密码 – 用户需要报出唯一密码(特定人 (SD) 语音识别)。正确说出分配至指定 PIN 码的语音密码后,系统显示“允许访问”窗口予以确认,并打开控制电磁锁的继电器 5 秒。这段时间过后,门再次锁紧,用户界面回到屏幕保护显示阶段。

双重认证:

two-factor-authen.-image-steps__505363a1ecea33c1d192088aacd836c46193e24b.jpg

图 1. 已实施访问模块的方块图

精选硬件解决方案

以上部分中,我们详细介绍了已实施访问模块的操作模块。从实施设备选择的角度审视设计,我们能够准确地定义设备层的要求:

  • 用于运行语言识别算法并生成语音消息的 EasyVR 模块、
  • 由微控制器和屏幕直径为 5 英寸的高质量显示屏组成的集成模块、
  • 方便设备扩展,无需另外焊接接口(最佳选择是 EasyVR 模块配备的 microBUS 连接器)、
  • 框架中精选设备的选配支持,可用高级语言方便地创建代码(例如 Python)。

收集实施项目的所有必要要求,即高质量电容式触摸显示屏、microBUS 连接器,以及兼容 Zerynth 编程环境(可直接用 Python 准备应用)。于是我们很快做出选择:集成 LCD 显示屏系统 Riverdi IoT Display 模块(对角 5 英寸,800x480 屏幕分辨率)、Bridgetek BT81x 图形控制器和著名的 ESP32 微处理器。Riverdi 提供的所有智能 IoT 显示屏都预先安装 Zerynth Studio 许可,因此开箱后即可直接使用。下面我们将简单总结硬件选择的特征:

Riverdi IoT Display – 此模块将是本项目的核心。凭借分辨率为 800x480 的 5 英寸电容式显示屏和图形控制器 BT815,我们将能够轻松、快速地准备图形用户界面,无需另外创建所有组件。在最后一个解决方案中,此模块将作为美观的墙壁模块进行安装(见图 2)。

iot-riverdi-cap-ux-front_9d810d3c7cafb67528861aa1d9c5bc35591d7950.jpg

iot-riverdi-cap-ux-back_%281%29_38e6f643e7eb2b67a174f41da7403d64c490ce73.jpg

图 2. Riverdi IoT Display 模块

EasyVR 3 Plus – 此模块负责实施与语音识别和分析以及回放录音样本相关的任务,它的作用与显示屏中显示的图形用户界面相互补充。EasyVR 模块的一个重要功能是区分指令“非特定人”(SI)、“特定人”(SD) 和“语者验证”(SV)。默认情况下,该模块已预先载入自动化系统常用的基本 SI 指令,以及定义方向、数字或动作指令的通用指令。该模块还可通过专用 QT2SI Lite 软件上传自定义 SI 指令。

image104_891fc78021129ad0fd5814fe7dc84b94304921fd.jpg

图 3. EasyVR 3 (Plus) 语音识别模块

Relay Click – MikroElektronika 生产的 MikroBUS 板 (820-9858) 配备两个继电器输出,并通过模块上的 LED 指示状态。在已实施的项目中,继电器输出将负责控制电磁锁的运行。image191_0b46da128c84edd0dd60c80c1b8ab74acfa8a612.png4. Relay Click 继电器板

 

整个系统的安装难度如何?前所未有的简单!

按照印刷电路板上的标记,将 Relay click 和 EasyVR 模块插入 Riverdi IoT Display 模块上的 microBUS 插座中,使用 micro-USB 电缆将已准备就绪的装置连接至电脑,大功告成!我们现在就可以开始设置软件了。

开发环境

Zerynth – 可快速、简便地在多数常见的嵌入式系统中创建 Python 软件的编程环境(还可运行 C 语言函数)。大量受支持的硬件平台(包括 Riverdi IoT Display)和现成的软件库(包括可简化 EasyVR 模块操作的库)将大大加快和推动目标访问模块软件的准备阶段进展。

image133_7a58e44728320610c0d45813e514ceeb29988f6b.png

图 5. 集成代码编辑器 – Zerynth Studio

EasyVR Commander – 为了管理 EasyVR 模块(包括创建自己的语音指令,上传音频样本或配置模块),开发商准备了配备简单直观图形界面的专用 EasyVR Commander 应用程序(见图 6)。

image55_7197c599477bfd74193fdf3f29bf38bb47530dd0.png

图 6. EasyVR Commander 主窗口

EasyVR 模块配置

我们开始用 Fortebit EasyVR 3 模块进行设备配置。首先,用随附的 USB-UART 信号转换器将模块连接至电脑。启动 EasyVR Commander 应用程序后,选择串行端口并点击“Connect”(连接)按钮。应用程序将自动读取模块程序中的当前指令组,并以列表形式显示(见图 7)。

image86_9db6e2bf2d8ea7d0a78adae756b96e24f0d14c18.png

图 7. 从 EasyVR module 模块读取的指令组列表

根据已采用的概念,用户密码将位于专用“Password”(密码)组下方(用数字 16 标注),可保存多达 5 组密码,并基于语者验证 (SV) 技术保存和识别(与特定人技术相反,使用 SV 保存的密码对正确消除背景噪音和与麦克风之间的距离影响更为敏感)。如需将系统从屏幕保护显示阶段唤醒,我们将使用“Grammar”(语法)组中的指令,其中用户可通过 QuickT2SI 软件设置一系列自定义 SI 指令(此模块可通过 EasyVR 3 Commander 软件另外安装)。模块制造商制作了简短的指导视频,以演示上传指令集至“Grammar”(语法)部分的常规操作:

除创建自定义“Grammar”(语法)集以外,还可以使用 EasyVR 模块制造商网站提供下载的现成“Grammar”(语法)集。

image113_417ed28e98341478e2cc2eb4dcfccbde697e8c1e.png

图 8. Group 1(组 1)中定义的音频样本集

“Recognition Settings”(识别设置)选项卡中可精确配置语言识别过程参数和麦克风相对用户的位置等因素的相关参数(见图 9)。

image141_e16e4dcbc038157f0184d9a60da54a1e4d8b55f6.png

图 9. “Recognition Settings”(识别设置)窗口

Zerynth 环境安装和设备虚拟化

选好硬件解决方案后,我们很快就可以转至与软件直接相关的问题。我们通过下载集成和跨平台(兼容 Windows、Linux 和 macOS)的 Zerynth Studio 开发环境开始此过程。在兼容操作系统中,整个安装过程非常标准。安装程序首次启动后,将提示用户接受许可协议并选择安装方法(在线或离线安装,离线安装的前提是用户已下载库)。安装过程的最后一步是选择软件版本(截至本文撰写时,最新版本为 r2.3.0)。安装过程完成后,我们用 micro-USB 电缆将 Riverdi IoT Display 连接至电脑。接下来让我们新建虚拟机。为此,从“Device info”(设备信息)下拉菜单中选择设备,单击“Create”(创建)按钮新建虚拟机,然后单击“Virtualize”(虚拟化)按钮虚拟化您的设备,见下图 10。

image74_f0595f7d99be0ee2eb6a447c680d82242d0085f7.png

图 10. 在 Zerynth 环境中注册和虚拟化新的 Riverdi IoT Display 设备

设备虚拟化过程结束后,我们就可以开始编程了!

Riverdi IoT Display 模块软件

为了提高所创建代码的可读性,代码结构分为两个独立的文件:main.py 和 gui.py,前者包含实施程序主函数的代码,后者则包含决定用户界面布局的函数。我们开始编辑代码,首先向 main.py 文件中导入库,以支持 BT81x 图形控制器和 EasyVR 语言识别模块:

from riverdi.displays.bt81x import ctp50

from bridgetek.bt81x import bt81x

from fortebit.easyvr import easyvr

下一步,我们继续配置串行端口 – SERIAL1 负责 EasyVR 模块的通信:

streams.serial()

ser = streams.serial(SERIAL1, baud=9600, set_default=False)

evr = easyvr.EasyVR(ser)

为了满足启动画面和屏幕保护的需要,我们将之前准备好的 PNG 格式图像附加到项目的图像资源上:

new_resource('images/gui_riverdi_logo.png')

new_resource('images/screensaver.png')

为了满足项目需要(使用 EasyVR 3 Commander),SoundTable 部分包含一系列音频样本,其中包括用户语音消息,例如“access denied”(访问拒绝)、“please say your password”(请说出密码)等。为了不使用每个音频样本的序号,我们为每段录音分配了已明确定义的变量:

SND_Access_denied=1

SND_Access_granted=2

SND_Hello=3

SND_Please_repeat=4

SND_Please_say_your_password=5

SND_Hello_give_command=6

SND_Please_say_name=7

SND_Pwd_activated=8

接着,main.py 文件链接正在初始化连接至 SPI 总线的 BT81x 控制器:

bt81x.init(SPI0, D4, D33, D34)

初始化的最后一步是根据应用需求调节 EasyVR 模块配置。在初始化阶段,我们配置准确性参数,因此包括指令识别速度、语者语言或超时值:

evr.setLevel(2)

evr.setLanguage(evr.ENGLISH)

evr.setKnob(evr.STRICTER)

evr.setCommandLatency(evr.MODE_FAST)

evr.setTimeout(6)

就这样,我们完成了初始化过程。在下一行代码中创建简单的循环,以循环运行 EasyVR 模块的检测过程:

while not evr.detect():

print("EasyVR not detected!")

print("EasyVR detected")

id = evr.getID()

print("EasyVR version id: %s" % id)

当 EasyVR 模块检测成功后,让我们检查模块存储器中的内容,向终端中输入

mask = evr.getGroupMask()

if mask != None:

for group in range(evr.PASSWORD + 1): #all groups: 0 to 16

if mask & 0x01 != 0:

count = evr.getCommandCount(group)

if group == evr.TRIGGER:

print("Trigger: "+ str(count))

elif group == evr.PASSWORD:

print("Password: " + str(count))

else:

print("Group " + str(group) + ": " + str(count))

for idx in range(count):

(name, train) = evr.dumpCommand(group, idx)

if not evr.isConflict():

print("%d %s Trained %d times, OK" % (idx,name,train))

else:

confl = evr.getWord()

if confl >= 0:

print("%d %s Trained %d times, Similar to Word %d" % (idx,name,train,confl))

else:

confl = evr.getCommand()

print("%d %s Trained %d times, Similar to Command %d" % (idx,name,train,confl))

mask >>= 1

image201_00a8b720ae7e6858c871741a412b326f0196f367.png

图 11. 显示 EasyVR 模块存储器的内容

我们已经准备好绘制第一个用户界面窗口了!在开始之前,最好先熟悉针对 BT81x 控制器编写的 Zerynth API 文档

为了加快工作,我们创建一个函数,以将图片载入到 BT81x 控制器的 RAM 内存中(从 RAM_G 存储区的地址 0 开始):

def loadImage(image):

bt81x.load_image(0, 0, image)

其中,后续参数表示 RAM 中的地址,而附加选项和变量分别代表已载入的图片。

位于存储器中的图片将通过 gui.py 文件中的 showLogo () 函数显示。我们从清理屏幕开始运行函数:

def showLogo():

bt81x.dl_start()

bt81x.clear_color(rgb=(0xff, 0xff, 0xff))

bt81x.clear(1, 1, 1)

然后创建 Bitmap 类别对象,并使用 draw () 方法在显示屏上绘制图像,其中图像坐标以参数形式给出:

image = bt81x.Bitmap(1, 0, (bt81x.ARGB4, 642 * 2), (bt81x.BILINEAR, bt81x.BORDER, bt81x.BORDER, 642, 144))

image.prepare_draw()

image.draw(((bt81x.display_conf.width - 642)//2, (bt81x.display_conf.height - 144)//2), vertex_fmt=0)

最后,完成框架,更换当前显示列表:

bt81x.display()

bt81x.swap_and_empty()

Main.py 文件置入 gui.loadImage () 和 gui.showLogo () 函数:

gui.loadImage('gui_riverdi_logo.png')

gui.showLogo()

sleep(3000)

最终成果显示在图 12 中。

image161_74f778122338938e1fa34f0bab1674a66f338b36.jpg

图 12. 调用 loadImage () 和 showLogo () 方法的效果

变量 screenLayout 的作用是指示界面当前显示的元素,因此它负责后续用户界面屏幕之间的转换。屏幕选择将在主程序循环中实施:

if screenLayout == 1:

gui.loadImage("screensaver.png")

#screensaver logo parameters

screensaver_logo_width = 300

screensaver_logo_height = 75

evr.recognizeWord(4)

cnt = 0

现在该实现与屏幕保护显示相关的功能了,等待短语“Hello Device”(通过启用“Grammar 4”(语法 4)组指令识别)出现:

while screenLayout == 1:

if evr.hasFinished():

if evr.getWord() == 0:

screenLayout = 2

break

evr.recognizeWord(4)

else:

cnt += 1

sleep(100)

if (cnt == 20):

x = random(1,bt81x.display_conf.width - screensaver_logo_width)

y = random(1,bt81x.display_conf.height - screensaver_logo_height)

gui.showScreensaver(x,y)

cnt = 0

在 while 主循环语句中,当条件为 screenLayout = 1 时,我们循环检查短语“Hello Device”是否正确识别,然后刷新屏幕保护内容。当返回已识别时,screenLayout 变量的值发生改变,while () 循环被打破。showScreensaver (x, y) 主体是方法 showImage () 的修改版:

def showScreensaver(x,y):

bt81x.dl_start()

bt81x.clear_color(rgb=(0x00, 0x00, 0x00))

bt81x.clear(1, 1, 1)

image = bt81x.Bitmap(1, 0, (bt81x.ARGB4, (300) * 2), (bt81x.BILINEAR, bt81x.BORDER, bt81x.BORDER, 300, 75))

image.prepare_draw()

image.draw((x, y), vertex_fmt=0)

bt81x.display()

bt81x.swap_and_empty()

image61_b4071feb530e7f23e555c9710fccb58831394915.jpg

图 13. 屏幕保护窗口

现在到了第一重用户验证 – 用屏幕键盘输入 PIN 码。下面显示 main.py 文件中负责显示此部分用户界面的部分内容:

counter = 0

while (screenLayout == 2):

if (wait == True):

wait = False

sleep(2000)

gui.pinScreen(pin)

if counter == 500:

screenLayout = 1

sleep(10)

counter += 1

pin=""

gui.pinScreen () 的任务为显示数字屏幕键盘。此操作可通过 bt81x.add_keys () 方法轻松实现:

bt81x.track(430, 50, 350, 70, 0)

bt81x.add_keys(430, 50, 350, 70, 30, 0, "123")

bt81x.add_keys(430, 130, 350, 70, 30, 0, "456")

bt81x.add_keys(430, 210, 350, 70, 30, 0, "789")

bt81x.add_keys(430, 290, 350, 70, 30, 0, ".0C")

数字键盘的功能与“Connect”(连接)按钮相互补充,然后我们把它指定为 TAG 字段的值,这样就能在程序后期操作按钮:

btn = bt81x.Button(430, 370, 350, 70, 30, 0, "Connect")

bt81x.tag(1)

bt81x.add_button(btn)

image43_27c31fc3114a4ede52b0326ecec5e8130345f449.jpg

14. 带数字键盘的用户界面

按钮支持需要支持触摸屏操作的回调事件函数,事件在 main.py 文件中定义。本例中,pressed () 函数将负责所有事件:

bt81x.touch_loop(((-1, pressed), ))

Body function pressed ():

def pressed(tag, tracked, tp):

global screenLayout

global pin

global wait

global counter

global user2

#if we are in pinscreen

if (screenLayout == 2):

counter = 0

user2 = False

if ((tag != 67) and (tag !=1)):

if (len(pin) >= 4):

return

pin = pin + str(chr(tag))

elif ((tag == 67) and (len(pin) > 0)):

pin = pin[:-1]

elif ((tag == 1) and ((pin == valid_pin) or (pin == valid_pin2))):

if pin == valid_pin2:

user2 = True

screenLayout = 4

elif (len(pin) > 0):

wait = True

pin = ""

gui.showMessage("Access Denied")

#go to screensaver

evr.playSound(SND_Access_denied, evr.VOL_FULL)

每次轻触屏幕调用 pressed () 函数时,计数器变量将负责检测设备复位的闲置时间。选择“Connect”(连接)按钮确认输入的四位数 PIN 码。如果与预定义的 PIN 码之一不一致,设备屏幕上将显示短消息,通知访问被拒绝,如图 15 所示。

image116_e9d374ac036b3290fad3346a5064976564f21f3e.jpg

图 15. 屏幕通知 PIN 码输入不正确

我们将使用 gui.py 中定义的 showMessage () 函数显示允许或拒绝访问的消息。此函数中只包含屏幕清理和显示简单文本信息的功能,信息内容由函数参数指定:

def showMessage(text):

# start

bt81x.dl_start()

bt81x.clear(1, 1, 1)

#text

txt = bt81x.Text(400, 240, 31, bt81x.OPT_CENTERX | bt81x.OPT_CENTERY, text, )

bt81x.add_text(txt)

# display

bt81x.display()

bt81x.swap_and_empty()

正确输入 PIN 码后,我们顺利进入语音验证。我们希望系统一开始提供语音和文本消息以提示用户输入密码,其中文本消息在显示屏上显示(图 15)。

if screenLayout == 4:

gui.showMessage("Enter Voice Password")

evr.playSound(SND_Please_say_your_password, evr.VOL_FULL)

image122_92f3ba2a14356e1486b41349b73d0f95bb70523a.jpg

图 16. 提示输入访问密码的用户消息

在程序的主循环中,启用第 16 组(“Password”(密码)组)的识别指令,然后等待返回标记。如果结果值为 -1,则表示无法识别口头短语,或出现超时:

while screenLayout == 4:

evr.recognizeCommand(16)

while not evr.hasFinished():

sleep(100)

解除 while () 循环时,需要检查已返回的值是否代表密码错误或超时。这种情况下,我们将收到无法访问的通知,并提示重新输入密码

if ((evr.getCommand() == -1) and (not evr.isTimeout())):

gui.showMessage("Access Denied")

evr.playSound(SND_Access_denied, evr.VOL_FULL)

sleep(1000)

break

每位用户通过提供一组固定数据(即 PIN 码和为用户指定的语音密码)获得授权。因此必须检查 PIN 码对应的语音密码:

if ((user2 and (evr.getCommand() != 2) and (evr.getCommand() != -1)) or ((not user2) and ((evr.getCommand() != 5) and (evr.getCommand() != -1)))):

gui.showMessage("Access Denied")

evr.playSound(SND_Access_denied, evr.VOL_FULL)

sleep(1000)

break

当出现超时的时候,用户界面返回 PIN 输入屏幕:

if evr.isTimeout():

#if time out go to pin screen

screenLayout = 2

break

如果未出现以上情况,且密码与用户的 PIN 码一致,界面将进入下一阶段,通知已验证成功的用户获得进入受保护房间的权限(电磁锁的解锁时间为 5 秒):

if screenLayout == 5:

gui.showMessage("Access Granted")

evr.playSound(SND_Access_granted, evr.VOL_FULL)

sleep(1000)

relay_on()

for i in range(5):

time = str(5-i)

gui.showMessage("time left: " + time)

sleep(1000)

relay_off()

screenLayout = 1

image311_e2a60a0e200ffa635995ec7a7044cdf13c3eeabf.jpg

图17. 屏幕通知用户获得正确的访问权限

这里我们将讨论 Relay click 继电器模块。除非需要特殊硬件配置,否则我们必须在程序代码中选择相应的输出控制引脚,并创建简单函数来控制模块运行。图 18 中显示了 Relay click 模块的引脚输出图。

image152_cdd95c997375ffbce7013286631df7c89e821665.png

图 18.Relay click 的引脚输出

在已实现的项目中,电磁锁将通过 RL1 继电器控制。Relay click 模块已连接至位于 Riverdi IoT Display 模块上的 microBUS 1 插座。插座引脚输出图如图 19 所示。

image96_5ca9e8237a58fe3db90d6543fe013ffa909a12d2.png

图 19. microBUS1 插座的引脚输出图

因此,RL1 继电器控制将通过 D23 实现:

pinMode(D23, OUTPUT)

def relay_on():

digitalWrite(D23, HIGH)

def relay_off():

digitalWrite(D23, LOW)

以下视频将演示完整项目的全部功能:

结论

我们希望此演示视频简单、易参考。现在大家可以看到,用 Python 为 Riverdi IoT 显示屏和 Fortebit EasyVR 3 Plus 编程就是如此简单。如果想购买 Riverdi IoT 显示屏,请访问官方网页

----------------------------------------------------------

资源:

Riverdi 官方网站:www.riverdi.com

Zerynth 官方网站:www.zerynth.com

Fortebit 官方网站:www.fortebit.tech

Relay click: (820-9858)

Riverdi IoT 模块:https://riverdi.com/product-category/iot/

EasyVR 3:https://fortebit.tech/easyvr-3-plus/

Riverdi GitHub:https://github.com/riverdi/riverdi-speech-recognition-demo

I’ve learned early on in my career that I love to be at the forefront of innovation. To see new technologies emerge, and be a part of their creation. From the first days on the internet and the Silicon Valley revolution to The Internet of Things — I’ve seen it all, and I’ve done it all. Now, I’m ready to use this knowledge to help young companies thrive. I have over 35 years of experience in driving revenue, encouraging new ideas, and fostering client relationships.