16. 使用 Vosk(离线)的语音控制小车

Vosk 是一个轻量级的语音转文字(STT)引擎,支持多种语言,并且可以在树莓派上完全 离线 运行。 你只需联网一次下载语言模型,之后就可以在没有网络连接的情况下正常使用。

在本课程中,我们将:

  • 检查树莓派上的麦克风。

  • 安装并测试 Vosk 的语言模型。

  • 构建一台 语音控制的 PiCar-X,通过唤醒词启动,并识别诸如 forwardbackwardleftright 等语音指令。


开始之前

确保你已经准备好以下内容:


1. 检查麦克风

在使用语音识别前,先确保你的 USB 麦克风工作正常。

  1. 列出可用的录音设备:

    arecord -l
    

    找到类似 card 1: ... device 0 的行。

  2. 录制一个 3 秒的测试音频(将 1,0 替换为上面找到的数字):

    arecord -D plughw:1,0 -f S16_LE -r 16000 -d 3 test.wav
    
    • 示例:如果你的设备是 card 2, device 0,使用:

    arecord -D plughw:2,0 -f S16_LE -r 16000 -d 3 test.wav
    
  3. 回放录制的音频,确认录音是否成功:

    aplay test.wav
    
  4. 如果录音声音太小,可以调整麦克风音量:

    alsamixer
    
    • F6 选择你的 USB 麦克风。

    • 找到 MicCapture 通道。

    • 确保麦克风未静音([MM] 表示静音,按 M 解除静音 → 应显示 [OO])。

    • 使用 ↑ / ↓ 方向键调整音量。

2. 测试 Vosk

步骤如下

  1. 创建一个新文件:

    cd ~/picar-x/example
    sudo nano test_stt_vosk.py
    
  2. 将以下示例代码复制进去,然后按 Ctrl+X,再按 YEnter 保存退出。

    from picarx.stt import Vosk
    
    vosk = Vosk(language="en-us")
    
    print(vosk.available_languages)
    
    while True:
        print("Say something")
        result = vosk.listen(stream=False)
        print(result)
    
  3. 运行程序:

    sudo python3 test_stt_vosk.py
    
  4. 第一次使用新语言运行时,Vosk 会**自动下载语言模型** (默认下载 small 版本)。 同时会打印支持的语言列表,然后你会看到:

    vosk-model-small-en-us-0.15.zip: 100%|███████████████████| 39.3M/39.3M [00:05<00:00, 7.85MB/s]
    ['ar', 'ar-tn', 'ca', 'cn', 'cs', 'de', 'en-gb', 'en-in', 'en-us', 'eo', 'es', 'fa', 'fr', 'gu', 'hi', 'it', 'ja', 'ko', 'kz', 'nl', 'pl', 'pt', 'ru', 'sv', 'te', 'tg', 'tr', 'ua', 'uz', 'vn']
    Say something
    

    这意味着:

    • 模型文件(vosk-model-small-en-us-0.15)已成功下载。

    • 已打印支持的语言列表。

    • 系统正在监听麦克风输入 —— 对 PiCar-X 说话,识别出的文本会实时显示在终端中。

    提示

    • 保持麦克风距离 15–30 厘米。

    • 选择与你的语言和口音匹配的模型。

流式模式(可选)

你也可以使用流式识别模式,在说话时实时查看部分识别结果。

from picarx.stt import Vosk

vosk = Vosk(language="en-us")

while True:
    print("Say something")
    for result in vosk.listen(stream=True):
        if result["done"]:
            print(f"final:   {result['final']}")
        else:
            print(f"partial: {result['partial']}", end="\r", flush=True)

3. 语音控制小车

现在,让我们把语音识别和 PiCar-X 连接起来!

我们将使用一个 唤醒词 (”hey robot”),只有当小车被激活后才会监听指令。 这不仅节省 CPU,还可以避免误触发。

运行代码

cd ~/picar-x/example
sudo python3 16.voice_controlled_car.py

在这个程序中,小车将会:

  • 等待唤醒词 “hey robot”

  • 之后你就可以自然地说话了 —— 只要你的句子中包含以下任一关键词(forwardbackwardleftright),小车就会做出对应动作。

    例如:

    • “Can you move forward a little?” → 小车前进。

    • “Please turn left now.” → 小车左转。

  • 当你说 “sleep” 时,小车会停止控制循环,重新进入等待模式。

代码

from picarx import Picarx
from picarx.stt import Vosk
import time

px = Picarx()
stt = Vosk(language="en-us")

WAKE_WORDS = ["hey robot"]

print('Say "hey robot" to wake me up! Then say: forward / backward / left / right. Say "sleep" to stop listening.')

try:
    while True:
        # --- wait for wake word once ---
        stt.wait_until_heard(WAKE_WORDS)
        print("Wake word detected. Listening for commands... (say 'sleep' to pause)")

        # --- command loop: multiple commands after one wake ---
        while True:
            res = stt.listen(stream=False)
            text = res.get("text", "") if isinstance(res, dict) else str(res)
            text = text.lower().strip()
            if not text:
                continue

            print("Heard:", text)

            if "sleep" in text:
                # pause command mode; go back to wait for wake word
                px.stop(); px.set_dir_servo_angle(0)
                print("Sleeping. Say 'hey robot' to wake me again.")
                break

            elif "forward" in text:
                px.set_dir_servo_angle(0)
                px.forward(30); time.sleep(1); px.stop()

            elif "backward" in text:
                px.set_dir_servo_angle(0)
                px.backward(30); time.sleep(1); px.stop()

            elif "left" in text:
                px.set_dir_servo_angle(-25)
                px.forward(30); time.sleep(1)
                px.stop(); px.set_dir_servo_angle(0)

            elif "right" in text:
                px.set_dir_servo_angle(25)
                px.forward(30); time.sleep(1)
                px.stop(); px.set_dir_servo_angle(0)
            # (ignore other words)

except KeyboardInterrupt:
    pass
finally:
    px.stop(); px.set_dir_servo_angle(0)
    print("Stopped and centered. Bye.")

故障排查

  • No such file or directory(运行 `arecord` 时出现)

    你可能使用了错误的声卡 / 设备编号。 运行:

    arecord -l
    

    然后用 USB 麦克风对应的编号替换 1,0

  • 录音文件没有声音

    打开混音器检查麦克风音量:

    alsamixer
    
    • F6 选择你的 USB 麦克风。

    • 确保 Mic/Capture 没有被静音(应显示 [OO] 而不是 [MM])。

    • 使用 ↑ 键提高音量。

  • Vosk 无法识别语音

    • 确保 语言代码 与模型匹配(例如英语为 en-us,中文为 zh-cn)。

    • 保持麦克风距离 15–30 厘米,并避免背景噪音。

    • 清晰、适中速度地说话。

  • 唤醒词(“hey robot”)无法触发

    • 语气自然,不要太快。

    • 检查程序是否有识别输出,如果没有,说明麦克风可能未正常工作。

  • 识别延迟高 / 速度慢

    • 默认下载的是 small 模型 (速度较快但准确率较低)。

    • 如果仍然较慢,可关闭其他程序释放 CPU。