构建一个可以语音转文字手套
我们最后的作业之一要求我们设计一个嵌入式系统,来解决现实中的一个问题。其中一个我感兴趣的课题是减少使用手语者与不理解手语者之间的沟通障碍。我选择这个课题的主要动机在于该领域本身非常有趣。目前最流行的做法似乎是使用“手语转语音手套”,它能将检测到的手势转换为可听的语音。
然而,市场上现有的解决方案要么供应量小,要么价格昂贵。对于我的项目,我想制作一款注重低成本和DIY友好的语音手套。
硬件
由于时间和预算的限制,我只制作了一副手套,但它仍然具备我所期望的大部分功能。
我最终设计的解决方案涉及大量的线路连接。要开始,你需要以下材料:
•树莓派4(或更高级的型号)
•很多跳线
•9个2.2英寸柔性传感器
•9x 47k #sym... 欧姆电阻
•HC4067 16通道多路复用器
•MCP3008 8通道10位ADC DIP16
•USB音箱
•一副手套
•外接电池 + USB-C 数据线
每个弹性传感器应分别放置在手套的每个指节上,以及手指的近节指间关节(PIP关节)处。
拇指没有额外的弯曲传感器,是因为这些弯曲传感器太长,无法仅覆盖其PIP关节。对于每个弯曲传感器,将一端连接到3.3V电源,另一端接入上拉电阻电路(使用47k欧姆电阻)。连接哪一端到哪一端并不重要。
每个柔性传感器应有9根导线,其电压会根据传感器的弯曲程度而变化。但由于这些是模拟信号,而树莓派仅支持数字信号,目前我们还无法直接连接到树莓派。为了解决这个问题,我们将每根导线连接到HC4065多路复用器的某个通道上,再通过模数转换器连接到树莓派。我在多路复用器中使用了7至15号通道,并将它们连接到以下柔性传感器:
•CH7 -> -> -> 粉色 MCP
•CH8 -> -> -> 粉色PIP
•CH9 -> -> -> 环形MCP
•CH10 -> 信令环 PIP
•CH11 -> 中间MCP
•CH12 -> 中等PIP
•CH13 -> 主页 MCP
•CH14 -> -> -> 索引 PIP
•CH15 -> -> -> 食指
然后将输出连接到模数转换器的CH7,将ADS0、ADS1、ADS2和ADS3分别连接到树莓派的第7、11、13和15号引脚。
在MCP3008 ADC上,将CLK、D_OUT、D_IN和CS连接到相应的RPi引脚23、21、19和24。现在我们可以使用SPI协议和地址引脚来读取每个柔性传感器输出所对应的二进制数字。
最后,最简单的连接方式是将USB扬声器插入RPi 4的其中一个USB接口,同时通过一根长的USB-C数据线使用外接电池为RPi供电。所有设备连接完成后,我强烈建议使用布艺胶水将所有部件固定在手套上(电池除外),而电池则应放在用户使用手套时的口袋中。
软件
我为此创建的软件是一个 Rust Rust Rust 程序,可在此处获取。
它包含一个简单的 nix flakes,其中包含了你所需的所有依赖项。如果你尚未安装 nix,可以先进行安装。
使用由Determinate Systems开发的安装程序。
传感器
我已将所有与柔性传感器交互的代码分离开来,放入了独立的 sensors 命名空间。最重要的是,这暴露了一个 Sensor 结构体,有助于抽象出 SPI 协议等实现细节。
当传感器结构被创建时,它会设置SPI协议并访问GPIO引脚。
现在我们可以创建一个方法,返回每个柔性传感器的输出结果
手势信号
我接下来制作的箱子中包含了一个手部当前位置的表示。
“HandSign”” 表示手指的 MCP 或 PIP 关节弯曲或屈曲所形成的姿势。由于屈肌传感器读数存在大量噪声且容易出现“漂移”,因此难以获得准确的读数,这种表示方式较为二元化。
手
该机箱是应用中最大的,可将原始的柔性传感器读数转换为检测到的手势信号。
当创建一个 Hand 结构体时,我们需要先进行一些必要的初始化工作。由于柔性传感器的读数存在较大噪声,因此需要记录每个原始柔性传感器值的历史数据,以便取平均值,获得更平滑的读数。同时,我们还需要跟踪时间相关的数据,以计算每个指节的加速度。Hand::new()() 函数会负责初始化这些参数。
以下两种方法 get_finger_positions() 和 get_finger_accelerations() 是计算当前手势的主要逻辑所依据的。这两个方法不会修改 Hand 结构,仅从其中获取数据。它们输出的值在通过 update() 方法更新状态后可能会发生变化,这一点将在后文讨论。
`update()` 方法较为复杂,首先更新当前的柔性传感器历史记录以及自上次调用 `update()` 方法以来所记录的时间。然后,我们计算每个手指的加速度。如果某根指节在夹紧方向上以每秒10个单位的速度移动,则该指节被视为“弯曲”;如果其在中立位置以每秒10个单位的速度移动,则该指节被视为“中立”;若两者都不是,则使用之前计算出的指节状态。
此外,如果发现任何手指弯曲,应记录每个手指弯曲的时间长度。
现在我们已经掌握了预测用户是否正在做出手势所需的所有信息。如果用户刚刚将手重新放回中立状态,我们会查看每个手指部分弯曲的时间,并将其与 `Hand` 结构体构建时使用的 `word_to_sign` 字典进行比较。如果有任何手势的相似度超过90%,则返回相似度最高的那个手势。如果没有符合该条件的,则不返回任何结果。
应用
`app`` 模块是程序的入口点,负责将 `Hand` 结构体中检测到的单词通过文本转语音功能进行朗读。在文本转语音方面,我们使用了 `piper_rs`` 模块作为文本转语音模型,并结合 `rodio`` 模块来管理音频播放。
首先,我们初始化 TTS Piper 模型,该模型位于项目中的 `model/` 目录下。
在成功加载TTS模型后,我们创建一个发送者和接收者的通道,并将接收通道分配给一个新的线程。该通道将用于向该线程发送文字,而该线程则负责将文本转换为语音音频输出。我们希望将此过程运行在独立的线程中,以避免阻塞柔性传感器的数据处理。如果一位听障用户使用这款手套,当他们开始新的手语输入时,由于手套正在说话,导致无法及时处理,这会令人感到沮丧,而用户也无法察觉到这一点。
主函数的其余部分非常简单。我们构建了一个包含3个短语的非常基础的单词到符号字典,然后通过主线程持续处理柔性传感器。每当检测到一个单词并从`Hand`结构体返回时,就会将其发送到TTS线程,以便进行语音输出。
反思
这个项目最初的想法是制作两副高质量的手套,让它们能够无缝协作,识别你所做出的手势,并将其转化为语音。然而回顾起来,考虑到我可用的时间和预算,这目标实在过于宏大。尽管如此,我对最终成果仍然非常满意。幸运的是,事实证明,如果要实现准确的听觉转语音功能,我的柔性传感器方案并不是完全可行的选择。
我也有更多时间去思考这些手套原本旨在解决的问题。我最初认为这个项目最好的结果,是能够制造出高质量、价格低廉且广泛可及的手势转语音手套。然而,这实际上会导致各种手语的学习减少,因为这些手套可以当作拐杖使用。我认为,这些手套更合适的用途,是让不熟悉手语的人将其用作自我学习的工具,帮助他们在尝试与他人交流时,及时发现自己的手语表达失误。
本文编译自hackster.io





