将麦克风连接到 Raspberry Pi 上实现语音识别,对某些应用很有效,而且价格也不贵。如果你只需要语音识别,这与口语对话不同,你并不需要太多的机器规格。
有很多语音识别软件、服务和库,既有付费的,也有免费的。我们经常可以看到 API 服务,尤其是来自谷歌、亚马逊和 IBM 的 API 服务。
由于我们希望在本地环境(离线)下开展这个项目,我们考虑了 “Julius “和 “VOSK”,前者效率高、功能多,后者用 Python 程序就能轻松处理。这两种软件都是免费的,而且很容易上手。由于是本地环境,因此无需担心安全或隐私问题。
*本文将以日语语音识别为对象进行介绍。
Raspberry Pi 的规格不允许使用任何语音识别软件。如果日语语音识别模型过大,quicksilver 处理将无法跟上。
我试用了使用基本听写工具包的 Julius 和使用小型识别模式(小型)的 VOSK。
*文章中的 Python 示例程序是用 ChatGPT 创建的。它是表达操作测试的一种方式。错误处理和终止处理不完整,敬请谅解。
当时的环境
我们再次运行的测试机是 PL-R5M,这是一款使用 Raspberry Pi 计算模块 5 的机型。
使用的设备
- PL-R5M(Raspberry Pi CM5)
- Jabra SPEAK 510(USB 麦克风)
使用环境
- 树莓派操作系统(书虫)64 位
- Python 3.11.2
- pip 23.0.1
确认 USB 麦克风

本项目中使用的 USB 连接麦克风是 Jabra SPEAK 510,它是一款麦克风和扬声器,适合多人使用,如在会议中使用。
还有一些 USB 设备只是麦克风,其中大多数只需连接一根 USB 电缆即可使用。
您可以看到,只需连接 Raspberry Pi OS 桌面,就可以选择 Raspberry Pi OS。

不过,在 Python 程序中使用麦克风设备时,指定麦克风设备更可靠,所以要先查找索引号来指定它。
有时不指定索引号也没有问题,只要它是默认的就行。在这种情况下,我研究并设置了一个兼作麦克风和扬声器的设备。
USB 麦克风的卡号:
使用 Raspberry Pi 操作系统,可以用 arecord 命令查找。
我这次连接的 Jabra SPEAK 510 的卡号是 2,设备号是 0。在 ALSA 设备名称中是hw:2,0或plughw:2,0。
arecord -l
**** List of CAPTURE Hardware Devices ****
card 2: USB [Jabra SPEAK 510 USB], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
如果不指定卡号,就无法捕捉麦克风,因此我们将在连接后查找卡号。
安装朱利叶斯
我先说朱利叶斯。
朱利叶斯广为人知,但其信息已经过时,官方网站上的链接也存在问题,因此我很难安装它,尤其是在 Raspberry Pi 上。文档是有的,但我认为你可能会在安装阶段遇到困难。
阅读官方网站上的安装说明后,我发现在构建时需要为 Raspberry Pi 操作系统明确指定 ALSA。
而且,”配置构建 “已经过时(2008 年),无法识别 Raspberry Pi 5 系列(arch64、ARM64),因此我们不得不下载一个新文件。
由于没有 make uninstall,因此安装目标会更改为不需要 sudo 权限的本地环境 ($HOME/.local),以便日后轻松删除。
按以下顺序操作:
我是从 git clone 获取的,但如果从源代码中解压缩也是一样的。请注意,目录名是不同的。(wget https://github.com/julius-speech/julius/archive/v4.6.tar.gz)
sudo apt update
sudo apt install build-essential zlib1g-dev libsdl2-dev libasound2-dev
git clone https://github.com/julius-speech/julius.git
cd julius
wget -O config.guess 'https://git.savannah.gnu.org/cgit/config.git/plain/config.guess'
wget -O config.sub 'https://git.savannah.gnu.org/cgit/config.git/plain/config.sub'
chmod +x config.guess config.sub
cp config.guess support/config.guess
cp config.sub support/config.sub
./configure --build=aarch64-unknown-linux-gnu --with-mictype=alsa --prefix=$HOME/.local
make
make install
export LD_LIBRARY_PATH=$HOME/.local/lib:$LD_LIBRARY_PATH
export PATH=$HOME/.local/bin:$PATH
在赋予执行权限后,config.guess 和 config.sub 被复制到了 support 目录。
配置中指定的选项有以下含义
# Specify Raspberry Pi aarch64
--build=aarch64-unknown-linux-gnu
# Specify ALSA
--with-mictype=alsa
# By specifying a local directory, sudo is not required
--prefix=$HOME/.local
导出命令的最后两行是通过 locale 目录传递指定的 Julius 路径所必需的。
通过上述命令成功配置后,结果如下。

最后,检查 Julius 的版本,确保已安装并通过。
julius -version

没关系。
朱利叶斯日语听写工具包
获取听写工具包 v4.5。还有其他套件可供选择,如口语模型套件和演讲语音模型套件,但我们在此只测试了基本套件。
wget https://osdn.net/dl/julius/dictation-kit-4.5.zip
但这次,无论是 v4.5 还是 v4.4,都无法通过 wget 从官方链接下载。(从 OSDN 下载)
我别无选择,只能从镜像网站下载。如果无法下载,也请尝试镜像。
cd julius
wget https://ftp.iij.ad.jp/pub/osdn.jp/julius/71011/dictation-kit-4.5.zip
unzip dictation-kit-4.5.zip
查找 USB 麦克风的设备编号
检查所连接的第一个 USB 麦克风的卡号。如果指定不正确,则无法执行。
arecord -l
**** List of CAPTURE Hardware Devices ****
card 2: USB [Jabra SPEAK 510 USB], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
所连接的 Jabra SPEAK 510 的卡号是 2。 这次我们将运行一条命令来暂时识别它。
export ALSADEV=hw:2
重启后上述功能会恢复,如果你想一直使用,请在~/.profile 中添加备注。
运行朱利叶斯
现在准备就绪,让我们运行命令。我在-input mic中添加了一个显式选项。-nostrip没有 “*”也能运行,但它会被识别为没有语句的零,并会占满终端屏幕。最好也加上这个选项。
julius -C main.jconf -C am-gmm.jconf -nostrip -input mic
执行后显示一条长信息后,请在终端显示屏停在<<< please speak >>> 时与之对话。

以下是几次口语测试的结果。
第一句话是 “テスト”(测试)。
第一次我得到了 “ベスト”(最佳),第二次我多拿了一点,但 “テスト”(测试)还是能辨认出来的。

下次我会说 “停止”[teishi]。

它被误认为是 “天使 “[tenshi]或 “変身”[henshin]。第三次识别正确。
具有讽刺意味的是,麦克风接收到了我在最后无意识地嘟囔的 “うまく認識しない”(它不太會認識),并且在第一次尝试时就正确地识别了出来。

准确度不是很准确。可能是因为麦克风具有高性能的指向性。
语音识别似乎很好。
在我的印象中,即使是很小的声音也会被接收并识别为文字。
在某种程度上,我的印象是,如果有一个更 Chees 的麦克风或嘴边的麦克风,肯定能认出我。
,如果应该在嘈杂的大地方使用,除了消除噪音外,选择不会出错的词语可能也很重要。
我注意到的是,作为一个句子,它的标点符号是正确的。
下面的标点符号偶尔会看到,但在语句简短的情况下是无法识别的,如下图所示。
<input rejected by short input>
STAT: skip CMN parameter update since last input was invalid
三个字母如 “テスト”(测试)或 “停止”(停止)作为一个单词太短,会出现多次。相反,我觉得一个稍长的单词,比如 10 个字符,识别率会更高。
朱利叶斯服务器模式和 Python 程序
可能有脚踏开关等,但如果只需对着麦克风说 “停止”(stop)就能暂停,则会更方便、更省时。
示例程序是一个 Python 程序,用于处理从麦克风接收到的 “停止”(stop)和 “开始”(start)语句。
停止 “显示为”→停止识别”,”开始 “显示为”→开始识别”。其他单词则按所识别单词的原样显示。
我们假设在这种情况下,流程会根据一个特定的单词分支。
在本例中,为了在 Python 程序中处理朱利叶斯识别的单词,朱利叶斯是以服务器模式运行的,并带有-module选项。
在服务器模式下,可以通过 TCP 端口(10500)以套接字通信方式接收 XML 格式的语音数据。

在实际终端上,先以服务器模式启动 Julius。
然后,创建 julius_client.py 示例,并在 SSH 连接环境下或实际设备上的单独终端屏幕上运行 Python,以识别并显示备注。
以服务器模式启动朱利叶斯:
julius -C main.jconf -C am-gmm.jconf -module -input mic -nostrip

运行 julius_client.py,如果使用的是实际设备,可打开另一个终端,或通过 SSH 连接。
python3 julius_client.py
您可以使用 Crtl + C 强制退出。
julius_client.py:
import socket
import xml.etree.ElementTree as ET
HOST = "127.0.0.1"
PORT = 10500
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST, PORT))
print("Connected to Julius module server")
recv_buf = ""
def parse_words(xml_text):
try:
root = ET.fromstring(xml_text)
return [whypo.attrib["WORD"] for whypo in root.findall(".//SHYPO/WHYPO")
if "WORD" in whypo.attrib and whypo.attrib["WORD"] != "[s]"]
except Exception:
return []
print("Julius module client running... 'Stop'/'Resume' detection sample")
try:
while True:
data = client.recv(4096).decode("utf-8", errors="ignore")
if not data:
continue
recv_buf += data
while "<RECOGOUT>" in recv_buf and "</RECOGOUT>" in recv_buf:
start = recv_buf.index("<RECOGOUT>")
end = recv_buf.index("</RECOGOUT>") + len("</RECOGOUT>")
xml = recv_buf[start:end]
recv_buf = recv_buf[end:]
words = parse_words(xml)
if not words:
continue
sentence = " ".join(words)
print(f"Recognition result: {sentence}")
if "停止" in words:
print("→ 'Stop' recognized")
elif "再開" in words:
print("→ 'Resume' recognized")
except KeyboardInterrupt:
print("Exiting...")
finally:
client.close()
执行结果:
Connected to Julius module server
Julius module client running... '停止'/'再開' detection sample
Recognition result: 停止 。
→ 停止 recognized
Recognition result: 再会 。
Recognition result: 停止 。
→ 停止 recognized
Recognition result: 作業 を 再開 。
→ 再開 recognized
Recognition result: さようなら 。
Recognition result: こんにちは 。
由于“再開”[saikai](重新开始)未能正确识别为同音词“再会”[saikai](重逢),因此建议使用“停止作业”(停止工作)或“恢复作业”(恢复工作)更为妥当。
朱利叶斯使用的只是一个基本的听写工具包。因此,我们不能对识别准确率抱太大期望。不过,它的识别速度很快,单词和单句的识别效果都相当不错。
安装 VOSK
这款软件适合用 Python 处理,安装后可立即与 sounddevice 一起使用。
首先,下载日语音频模型并保存到适当位置。
VOSK 版本为 0.22。我选择了小机型,因为它尽可能轻便;大机型也可以使用,但 8GB 机型更适合内存扩展。
cd
wget https://alphacephei.com/vosk/models/vosk-model-small-ja-0.22.zip
unzip vosk-model-small-ja-0.22.zip
mv vosk-model-small-ja-0.22 model
第 4 行的 mv 命令将名称改为模型。

pip 应该安装在虚拟环境中,因此vosk_env 。(PEP 668 问题)
sounddevice 也一起安装,这是一个与 PortAudio 一起工作的模块。
python3 -m venv vosk_env
source vosk_env/bin/activate
pip3 install vosk sounddevice
要最终退出虚拟环境,可以在终端中使用deactive返回到原来的位置。
与 PortAudio 一起使用的麦克风的索引编号
Jabra SPEAK 510 被识别为 2 号卡上的设备 0。 (hw:2,0或plughw:2,0)
我从这些信息中指定了设备号,但可能不起作用。
显然,索引号与 Python 中处理的 PortAudio 不同。
有一种方法可以获取索引号并在程序中指定它,但我先将它添加到示例程序中并使其输出。(错误还是一样)
我在代码中添加了print(sd.query_devices()),以便检查:
0 vc4-hdmi-0: MAI PCM i2s-hifi-0 (hw:0,0), ALSA (0 in, 2 out)
1 Jabra SPEAK 510 USB: Audio (hw:2,0), ALSA (1 in, 2 out)
2 sysdefault, ALSA (0 in, 128 out)
3 hdmi, ALSA (0 in, 2 out)
4 pulse, ALSA (32 in, 32 out)
* 5 default, ALSA (32 in, 32 out)
这有点复杂。
这是通过声音设备(PortAudio) 使用VOSK 的情况,因此数字并不相同。
如果您使用的是 USB 麦克风,最好先检查麦克风的索引号。
如果您遇到这样的情况,即没有看到错误,但当您对着麦克风说话时却没有任何反应等,请尝试运行以下代码以找出 PortAudio 使用的索引号。
查找语音输入设备索引号的代码
运行以下程序时,您将看到由 ALSA 和 Python 处理的两个索引号。
如果您使用的 USB 麦克风无法输入音频,请以此为参考了解索引号。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import subprocess
import sounddevice as sd
import re
# 1. Get ALSA devices from `arecord -l`
def get_alsa_devices():
result = subprocess.run(['arecord', '-l'], stdout=subprocess.PIPE, text=True)
devices = []
lines = result.stdout.splitlines()
card = None
for line in lines:
# Detect card line
m_card = re.match(r'^card (\d+): (.+?)\s+\[.*\], device (\d+): (.+?)\s+\[.*\]', line)
if m_card:
card_num = int(m_card.group(1))
card_name = m_card.group(2).strip()
device_num = int(m_card.group(3))
device_name = m_card.group(4).strip()
devices.append({
'card': card_num,
'card_name': card_name,
'device': device_num,
'device_name': device_name
})
return devices
# 2. Get Python / PortAudio devices
def get_sounddevice_devices():
devices = []
for i, dev in enumerate(sd.query_devices()):
if dev['max_input_channels'] > 0:
devices.append({
'index': i,
'name': dev['name'],
'max_in': dev['max_input_channels'],
'max_out': dev['max_output_channels']
})
return devices
# 3. Display both
执行结果:
ALSA (arecord -l)
card 2, device 0: USB - USB Audio
Python / sounddevice
index 1: Jabra SPEAK 510 USB: Audio (hw:2,0) (1 in, 2 out)
index 4: pulse (32 in, 32 out)
index 5: default (32 in, 32 out)
在执行结果中查找Python / sounddevice下的 USB 麦克风名称。
在下面的示例程序中,指定了该索引号(#1)。
分支处理的 Python 程序
“停止 “的意思是 “停止”,读作 teishi。
再开的意思是 “恢复”,读作 saikai。
在 VOSK 中,使用 Python 相对容易。
在日语中,”停止”[teishi] 可以写成停止(汉字)、ていし(平假名)或テイシ(片假名)。
注意,设备索引指定为 1,单声道 1 声道。
您可以使用 Crtl + C 强制退出。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sounddevice as sd
import queue
import json
from vosk import Model, KaldiRecognizer
MODEL_PATH = "model"
SAMPLE_RATE = 16000
q = queue.Queue()
def callback(indata, frames, time, status):
if status:
print(status, flush=True)
q.put(bytes(indata))
def main():
print("Loading VOSK model...")
model = Model(MODEL_PATH)
rec = KaldiRecognizer(model, SAMPLE_RATE)
machine_running = True
# USB microphone: card 1, device 0, mono
with sd.RawInputStream(
device=1,
samplerate=SAMPLE_RATE,
blocksize=8000,
dtype="int16",
channels=1,
callback=callback):
print("Speech recognition started. Please say '停止' (stop) or '再開' (resume).")
while True:
data = q.get()
if rec.AcceptWaveform(data):
result = json.loads(rec.Result())
text = result.get("text", "")
if text:
print(f"Recognition result: {text}")
if any(word in text for word in ["停止", "ていし", "テイシ"]) and machine_running:
print(">>> 停止コマンド検出!")
machine_running = False
elif any(word in text for word in ["再開", "さいかい", "サイカイ"]) and not machine_running:
print(">>> Resume command detected!")
machine_running = True
if __name__ == "__main__":
main()
查看结果,没有错误,但识别结果是 “停止”[teishi] 不可避免地变成了 “天使”[tenshi]。
执行结果:
Recognition result: 天使
Recognition result: 天使
Recognition result: 開始
Recognition result: いや 天使
Recognition result: 天使
Recognition result: いい
Recognition result: て いい し
Recognition result: 天使
Recognition result: いい し
这种情况可能发生,因为它是一个小模型,取决于麦克风的类型和环境。
在嘈杂的环境中,识别的准确性也会降低。
提高 VOSK 准确性的一种方法是进行词汇校正。
VOSK 允许您指定要等待的字词。→ rec = KaldiRecognizer(model, SAMPLE_RATE, '["停止", "再開"]')
这样,”停止”[teishi] 和 “再開”[saikai] 以外的词都会被忽略,从而大大提高了准确性。
修改后的 Python 程序
最后,以下代码达到了预期的效果。
尝试在 “停止”[teishi]语句后说几次 “再開”[saikai],看看是否一次就能识别,以及停止过程的快慢。
停止[teishi]或再開[saikai]会在小模型约 1 秒(大模型约 2 秒)后显示。
如果对精确度的要求不高,系统可以 “停止”(stop)注释。
您可以使用 Crtl + C 强制退出。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sounddevice as sd
import queue
import json
from vosk import Model, KaldiRecognizer
# Settings
MODEL_PATH = "model" # Place vosk-model-small-ja-0.22 in model/
SAMPLE_RATE = 16000 # Small model recommends 16kHz
q = queue.Queue()
# Command handling functions
def stop_process():
print(">>> Executing machine STOP process")
def resume_process():
print(">>> Executing machine RESUME process")
# Audio data callback
def callback(indata, frames, time, status):
if status:
print(status, flush=True)
# With RawInputStream, indata is a _cffi_backend.buffer
# Convert to bytes before passing to VOSK
q.put(bytes(indata))
def main():
print("Loading VOSK model...")
model = Model(MODEL_PATH)
# Limit recognition vocabulary to '停止' and '再開'
rec = KaldiRecognizer(model, SAMPLE_RATE, '["停止","再開"]')
machine_running = True
# USB microphone: card 1, mono 1ch
with sd.RawInputStream(
device=1, # USB mic card number
samplerate=SAMPLE_RATE,
blocksize=8000,
dtype="int16",
channels=1, # Mono, 1 channel
callback=callback):
print("Speech recognition started. Please say '停止' (stop) or '再開' (resume).")
while True:
data = q.get()
if rec.AcceptWaveform(data):
result = json.loads(rec.Result())
text = result.get("text", "")
if text:
print(f"認識結果: {text}")
# 判定
if "停止" in text and machine_running:
stop_process()
machine_running = False
elif "再開" in text and not machine_running:
resume_process()
machine_running = True
if __name__ == "__main__":
main()
执行结果:
Speech recognition started. Please say '停止' (stop) or '再開' (resume).
Recognition result: 停止
>>> Executing machine STOP process
Recognition result: 停止
Recognition result: 再開
>>> Executing machine RESUME process
从识别结果中可以看出,”停止[teishi]”和 “再開[saikai]”都被识别出来了。
第二个 “停止[teishi]”也没有错,结果是在停止时即使被识别为 “停止[teishi]”也没有被处理。
通过与再開[saikai]对话,执行进程的分支也取得了成功。虽然响应时间较慢,但很流畅,没有错误。
这次是 “停止 “和 “再開 “等短词。停止”[teishi]有时会被误认为 “天使”[tenshi],原因很简单,因为我们使用的日语识别模型很小。
如果识别不可靠,也不妨选择一个不容易被误认为是其他词的词,如 “作业停止”(work stoppage),使这个词独一无二。
VOSK 还可以处理自定义字典。(朱利叶斯也可以拥有字典,不过方式不同)。
虽然可以从外部文件加载单词表,但在示例代码中指定单词表更为方便,具体如下。
# Restrict recognition vocabulary with a grammar list (phrase specification)
grammar = '["停止", "再開", "機械を停止して", "再開してください"]'
rec = KaldiRecognizer(model, SAMPLE_RATE, grammar)
这一次,停止和恢复只是通过打印语句来表示,但你可以添加一些处理,看看它是如何运行的。
实用语音识别
如果可以通过语音识别来扩展程序处理,那么它不仅可以用于工业应用,还可以作为开关按钮的替代品,不需要复杂的操作。
,只需要一个额外的 USB 连接的麦克风,就可以用一个词或一句话等短语来控制系统。
朱利叶斯有一个有用的服务器模式。编程比较复杂,但这似乎是将某些东西连接在一起的最佳方式。它可以非常灵活地整合在一起。
另一方面,VOSK 可以在 Python 环境中轻松运行。它最适合 Raspberry Pi,因为 Raspberry Pi 从一开始就有 Python 环境。
两人都认为 Raspberry Pi 足以满足处理规格的要求。
如果 Raspberry Pi 可以承担部分负荷,其优势将是省电和机身更小。
朱利叶斯https://github.com/julius-speech/julius
VOSK:https://alphacephei.com/vosk/models
文章由拉斯必达提供
非工程师也能愉快使用的 Raspberry Pi 信息网站 raspida.com一个非工程师也能享受和使用的 Raspberry Pi 信息网站。他还为 PiLink 网站提供有关工业用 Raspberry Pi 的技术博客文章。

