08.RK3588本地ASR语音识别
·
1.本地模型
选用shepa-onnx,它是Apache 2.0 协议的离线语音推理部署框架,仅做模型推理、不负责模型训练,底层基于 ONNX Runtime 运行 ONNX 格式语音模型,核心解决各类语音模型跨端离线部署难题。
2.核心特点
- 纯本地离线
音频全设备本地运算,不上传云端,隐私性强、无网络延迟。
- 跨全平台
Windows/macOS/Linux、Android/iOS、鸿蒙、树莓派、RK3588 等嵌入式、浏览器 Wasm、服务器;支持 RKNN、QNN、昇腾等 NPU 硬件加速。
- 多语言开发接口
C++、Python、Java、Go、Rust、C#、Kotlin、Swift、Dart 等十几种绑定,适配移动端、后端、嵌入式开发。
- 轻量化无重型依赖
不需要 PyTorch/TensorFlow,仅依赖 ONNX Runtime,包体小、启动速度快。
3. 覆盖完整语音能力
- ASR 语音识别:流式 / 非流式,支持 SenseVoice、Whisper、Paraformer、Zipformer 等主流模型
- TTS 语音合成:Matcha-TTS、Piper、VITS 等
- 配套工具:Silero VAD 语音端点检测、说话人分离 / 声纹识别、关键词唤醒、语音降噪、语种识别、自动加标点
4. 典型使用场景
本地实时字幕、手机离线语音输入法、智能音箱 / 车载离线语音、录音笔转写、浏览器语音工具、嵌入式工控语音交互、隐私优先的本地语音助手。
5.RK3588上部署
5.1 在rk3588上安装 sherpa-onnx
pip install sherpa-onnx
5.2 下载预转换的onnx模型
从 GitHub Releases 下载预转换的 ONNX 模型,这一步可以在虚拟机上下载,然后再把需要的文件转到板子上
# SenseVoice 模型(中英日韩粤,229MB int8版本),模型来源和SenseVoice相同
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
解压后内容如下:
我们在rk3588上只需要用到 model.int8.onnx、tokens.txt、test_wavs
把这几个文件依次转到板子上
adb push model.int8.onnx /userdata/aidemo/sensevoice
adb push tokens.txt /userdata/aidemo/sensevoice
adb push test_wavs/ /userdata/aidemo/sensevoice
5.3 离线语音识别测试
新建test.py,内容如下
import sherpa_onnx
import wave
import numpy as np
#创建识别器(指定本地路径,零联网)
recognizer = sherpa_onnx.OfflineRecognizer.from_sense_voice(
model="./model.int8.onnx",
tokens="./tokens.txt",
num_threads=4,
use_itn=True, #启用逆文本正则化(数字、日期等转换)
language="zh", #语言:zh/en/ja/ko/yue/auto
)
#读取音频
def read_wave(wav_path):
with wave.open(wav_path, 'rb') as wf:
sample_rate = wf.getframerate()
data = wf.readframes(wf.getnframes())
samples = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0
return samples, sample_rate
samples, sample_rate = read_wave("./test_wavs/zh.wav")
stream = recognizer.create_stream()
stream.accept_waveform(sample_rate, samples)
recognizer.decode_stream(stream)
print(stream.result.text)
文件结构如下:

测试结果如下:

5.4 麦克风实时本地识别
5.4.1简单识别
新建real_time.py,内容如下:
import sherpa_onnx
import subprocess
import struct
--------------------------
#1. 加载SenseVoice离线识别器(旧版唯一可用 from_sense_voice)
--------------------------
recognizer = sherpa_onnx.OfflineRecognizer.from_sense_voice(
model="./model.int8.onnx",
tokens="./tokens.txt",
num_threads=4,
language="zh",
use_itn=True
)
#音频参数
CHUNK = 960
SAMPLE_RATE = 16000
#缓存完整PCM音频,攒够一段送给离线SenseVoice
audio_buffer = bytes()
#每1.5秒做一次识别,可按需调整
SEGMENT_SAMPLES = int(SAMPLE_RATE * 1.5)
SEGMENT_BYTES = SEGMENT_SAMPLES * 2 # S16_LE 每个采样2字节
--------------------------
#2. arecord 原生ALSA录音
--------------------------
arecord_cmd = [
"arecord",
"-r", str(SAMPLE_RATE),
"-c", "1",
"-f", "S16_LE",
"-D", "default",
"-t", "raw"
]
proc = subprocess.Popen(
arecord_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL
)
print("实时语音识别启动,说话等待1.5秒出结果,Ctrl+C退出")
try:
while True:
data = proc.stdout.read(CHUNK)
if not data:
break
audio_buffer += data
# 缓存达到设定长度,执行一次识别
if len(audio_buffer) >= SEGMENT_BYTES:
# 把bytes转float32数组(sherpa标准输入格式)
samples = struct.unpack("<" + "h"*(len(audio_buffer)//2), audio_buffer)
samples = [s / 32768.0 for s in samples]
stream = recognizer.create_stream()
stream.accept_waveform(SAMPLE_RATE, samples)
recognizer.decode_stream(stream)
text = stream.result.text.strip()
if text:
print("实时识别:", text)
# 清空缓存,接收下一段语音
audio_buffer = b""
except KeyboardInterrupt:
print("\n程序退出")
finally:
proc.terminate()
proc.wait()

此时不能区分静音和人声,麦克风数据都会送入模型进行识别,所以不说话时也会输出“我。”等
5.4.2 支持vad 的麦克风实时本地语音识别
VAD = Voice Activity Detection(语音活动检测)
用来判断“现在有没有人在说话”的技术。
此处给出 python环境对应的requirement.txt方便后续复现环境
aiofiles==23.2.1
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.14.0
blinker==1.9.0
brotli==1.2.0
certifi==2026.5.20
click==8.4.1
coloredlogs==15.0.1
contourpy==1.3.2
cycler==0.12.1
exceptiongroup==1.3.1
fastapi==0.137.1
ffmpy==1.0.0
filelock==3.29.4
Flask==3.1.3
flatbuffers==25.12.19
fonttools==4.63.0
fsspec==2026.6.0
gradio==6.18.0
gradio_client==2.5.0
groovy==0.1.2
h11==0.16.0
hf-gradio==0.4.1
hf-xet==1.5.1
httpcore==1.0.9
httpx==0.28.1
huggingface_hub==1.19.0
humanfriendly==10.0
idna==3.18
importlib_resources==6.5.2
itsdangerous==2.2.0
Jinja2==3.1.6
kiwisolver==1.5.0
markdown-it-py==4.2.0
MarkupSafe==2.1.5
matplotlib==3.10.9
mdurl==0.1.2
mpmath==1.3.0
numpy==2.2.6
onnxruntime==1.23.2
orjson==3.11.9
packaging==26.2
pandas==2.3.3
pillow==10.4.0
protobuf==3.18.0
psutil==5.8.0
pydantic==2.13.4
pydantic_core==2.46.4
pydub==0.25.1
Pygments==2.20.0
pyparsing==3.3.2
python-dateutil==2.9.0.post0
python-multipart==0.0.32
pytz==2026.2
PyYAML==6.0.3
rich==15.0.0
ruamel.yaml==0.17.31
ruamel.yaml.clib==0.2.7
ruff==0.15.17
safehttpx==0.1.7
semantic-version==2.10.0
shellingham==1.5.4
sherpa-onnx==1.13.3
sherpa-onnx-core==1.13.3
six==1.16.0
smbus==1.1
starlette==1.3.1
sympy==1.14.0
tomlkit==0.12.0
tqdm==4.68.2
typer==0.25.1
typing-inspection==0.4.2
typing_extensions==4.15.0
tzdata==2026.2
urllib3==2.7.0
uvicorn==0.49.0
websockets==12.0
Werkzeug==3.1.8
下载vad模型文件,后续实时识别需要用到它进行语音活动检测
wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
文件结构如下:

venv_sherpa_backup 是我用requirement.txt新建的虚拟环境,大家可以不管
测试代码
#!/usr/bin/env python3
import subprocess
import numpy as np
import sherpa_onnx
# ===================== 1、路径参数,按需修改 =====================
SILERO_VAD_MODEL = "./silero_vad.onnx"
SENSEVOICE_MODEL = "./model.int8.onnx"
SENSEVOICE_TOKENS = "./tokens.txt"
SAMPLE_RATE = 16000
CHUNK_SAMPLES = 960 # 每次读取采样点数
CHUNK_BYTES = CHUNK_SAMPLES * 2 # S16_LE 2字节每采样
def create_vad():
config = sherpa_onnx.VadModelConfig()
config.silero_vad.model = SILERO_VAD_MODEL
# 人声静音核心参数
config.silero_vad.threshold = 0.13
config.silero_vad.min_speech_duration = 0.2
config.silero_vad.min_silence_duration = 0.32
config.silero_vad.max_speech_duration = 5.0
config.sample_rate = SAMPLE_RATE
# 缓存30秒音频,官方必填第二个参数
vad = sherpa_onnx.VoiceActivityDetector(config, buffer_size_in_seconds=30)
print("[LOG] VAD 初始化完成")
return vad
def create_recognizer():
recognizer = sherpa_onnx.OfflineRecognizer.from_sense_voice(
model=SENSEVOICE_MODEL,
tokens=SENSEVOICE_TOKENS,
num_threads=4,
language="zh",
use_itn=True
)
print("[LOG] SenseVoice 识别器初始化完成")
return recognizer
def main():
vad = create_vad()
recognizer = create_recognizer()
# arecord 录音进程
arecord = subprocess.Popen(
[
"arecord",
"-r", str(SAMPLE_RATE),
"-c", "1",
"-f", "S16_LE",
"-D", "default",
"-t", "raw"
],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL
)
print("SenseVoice+SileroVAD 实时识别启动,Ctrl+C 退出\n")
printed = False
frame_cnt = 0
try:
while True:
raw = arecord.stdout.read(CHUNK_BYTES)
frame_cnt += 1
if not raw:
print(f"[WARN-{frame_cnt}] 录音读取为空,麦克风异常!")
break
# S16_LE bytes -> float32 [-1,1]
pcm_int16 = np.frombuffer(raw, dtype=np.int16)
pcm_float = pcm_int16.astype(np.float32) / 32768.0
#frame_noise_level = np.mean(np.abs(pcm_float))
#print(f"当前帧噪音幅度:{frame_noise_level:.4f}")
vad.accept_waveform(pcm_float)
# 打印人声开始提示(和官方示例逻辑一致)
if vad.is_speech_detected() and not printed:
print("[INFO] 检测到人声开始")
printed = True
if not vad.is_speech_detected():
printed = False
# =====官方标准队列读取,无None问题=====
while not vad.empty():
seg = vad.front
speech_audio = seg.samples
dur = len(speech_audio) / SAMPLE_RATE
print(f"\n[SPEECH] 语音片段时长:{dur:.3f}s")
# 送入SenseVoice识别
stream = recognizer.create_stream()
stream.accept_waveform(SAMPLE_RATE, speech_audio)
recognizer.decode_stream(stream)
raw_text = stream.result.text.strip()
print(f"[ASR 原始] |{raw_text}|")
# 过滤无意义语气词
filter_set = {"嗯", "啊", "哦", "呃", "。", ",", "、", "!", "?", " "}
if raw_text and not all(c in filter_set for c in raw_text):
print(f"=====识别结果:{raw_text}=====\n")
vad.pop() # 弹出已处理片段
except KeyboardInterrupt:
print("\n程序正常退出")
finally:
arecord.terminate()
arecord.wait()
if __name__ == "__main__":
main()
测试结果

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐

所有评论(0)