news 2026/9/3 20:45:25

FunASR Docker部署实战:搭建2Pass实时语音识别服务并完成WebSocket测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FunASR Docker部署实战:搭建2Pass实时语音识别服务并完成WebSocket测试

最近需要在本地部署一个语音识别服务,用于后续的音频转文字功能。经过对比后选择了阿里开源的FunASR

本文记录一次完整的 FunASR 部署过程,包括:

  • Docker 部署 FunASR Runtime
  • 配置 Paraformer 离线模型
  • 配置 Online 实时模型
  • 配置 VAD、标点和 ITN
  • 启动 2Pass WebSocket 服务
  • Python 客户端测试
  • 解决wss:///ws://导致的ConnectionResetError
  • 使用 FunASR 官方 WebSocket 客户端进行测试
  • 总结自己编写 WebSocket 客户端时需要注意的问题

一、FunASR 简介

FunASR 是阿里巴巴开源的一套语音识别工具包,提供了语音识别、语音活动检测(VAD)、标点恢复、时间戳等能力。

对于实际项目来说,FunASR Runtime 提供了 WebSocket 服务,可以让客户端通过 WebSocket 持续发送音频数据,然后实时获取识别结果。

其中比较值得关注的是2Pass模式。

简单来说:

音频流 ↓ Online 实时识别 ↓ 快速返回实时结果 同时 ↓ Offline 离线识别 ↓ 对实时结果进行修正 ↓ 得到更准确的最终结果

因此 2Pass 比单纯的 Online 实时识别更适合对实时性和准确率都有要求的场景。


二、准备环境

本次部署环境使用:

Ubuntu Docker FunASR Runtime CPU版本 Python

如果只是测试 FunASR,CPU 版本已经可以使用。

本次使用的 Runtime 镜像为:

registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12

这里需要注意,FunASR Runtime 的版本比较多,网上很多旧文章使用的是:

funasr-runtime-sdk-online-cpu-0.1.4

如果按照旧教程部署,部分客户端代码和参数可能与新版本存在差异。

本文最终使用的是:

0.1.12

三、创建模型目录

首先创建一个目录保存 FunASR 的模型:

mkdir -p /mnt/data/funasr/models cd /mnt/data/funasr

最终目录结构类似:

/mnt/data/funasr ├── models └── funasr_samples

Docker 启动的时候把宿主机的models挂载到容器:

宿主机: /mnt/data/funasr/models ↓ Docker Volume 容器: /workspace/models

这样模型文件就不会随着 Docker 容器删除而丢失。


四、拉取 FunASR Docker 镜像

执行:

docker pull registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12

查看镜像:

docker images | grep funasr

应该可以看到类似:

registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr funasr-runtime-sdk-online-cpu-0.1.12

五、启动 FunASR Docker 容器并进入交互模式

执行:

docker run -it --rm \ --name funasr \ -p 10096:10095 \ -v $PWD/models:/workspace/models \ --privileged=true \ registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12 \ /bin/bash

这里几个参数需要特别说明。

1. 端口映射

-p 10096:10095

表示:

宿主机 10096 ↓ 容器 10095

因此宿主机上的客户端应该连接:

127.0.0.1:10096

而不是:

127.0.0.1:10095

2. 模型目录

-v $PWD/models:/workspace/models

表示把当前目录下的models挂载到:

/workspace/models

后续 FunASR 下载的模型就可以保存在这里。


六、进入 FunASR 容器

上面的命令执行后已经进入容器。

然后进入 Runtime 目录:

cd /workspace/FunASR/runtime

查看文件:

ls

这里可以看到 FunASR Runtime 相关脚本。


七、启动2Pass WebSocket服务

本次使用:

run_server_2pass.sh

启动命令:

nohup bash run_server_2pass.sh \ --download-model-dir /workspace/models \ --model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-onnx \ --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx \ --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx \ --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx \ --itn-dir thuduj12/fst_itn_zh \ --certfile 0 \ > log.out 2>&1 &

这里使用了几个模型。

离线模型

speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-onnx

用于 Offline ASR。

Online模型

speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx

用于实时 Online ASR。

VAD模型

speech_fsmn_vad_zh-cn-16k-common-onnx

用于检测语音开始和结束。

标点模型

punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx

用于恢复标点。

ITN模型

fst_itn_zh

用于逆文本规范化。


八、为什么使用--certfile 0

这里是整个部署过程中非常容易踩坑的地方。

启动服务时:

--certfile 0

意味着当前 WebSocket 服务没有启用 SSL。

因此客户端应该使用:

ws://

而不是:

wss://

也就是说:

ws://127.0.0.1:10096

是正确的。

而:

wss://127.0.0.1:10096

是不正确的。


九、检查服务是否正常启动

查看日志:

tail -f log.out

或者:

docker exec -it funasr bash cd /workspace/FunASR/runtime tail -f log.out

如果服务正常启动,可以看到 Runtime 服务相关日志。

也可以在宿主机检查端口:

ss -lntp | grep 10096

应该能看到:

10096

十、下载FunASR官方Python客户端

FunASR 官方提供了 Python WebSocket 客户端,可以直接用于测试 Runtime 服务。

官方客户端源码:

funasr_wss_client.py(GitHub官方源码)

FunASR 官方 Runtime 快速开始文档:

FunASR Runtime 快速开始文档

如果需要一次性下载官方 samples 测试工具,也可以使用官方提供的压缩包:

wget https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/sample/funasr_samples.tar.gz

然后解压:

tar -zxvf funasr_samples.tar.gz

进入 Python 客户端目录:

cd funasr_samples/samples/python

其中可以找到:

funasr_wss_client.py funasr_client_api.py

官方文档目前也是通过funasr_wss_client.py来测试 Runtime WebSocket 服务,并支持offlineonline2pass模式。


1. 安装客户端依赖

funasr_wss_client.py使用 Python WebSocket 客户端,因此首先安装:

pip install websockets

如果使用的是官方较老版本的funasr_client_api.py,则需要:

pip install websocket-client

2. 使用官方客户端测试2Pass

我们的 FunASR 服务运行在:

127.0.0.1:10096

由于服务端启动时使用了:

--certfile 0

没有开启 SSL,所以客户端必须使用普通 WebSocket。

执行:

python3 funasr_wss_client.py \ --host "127.0.0.1" \ --port 10096 \ --mode 2pass \ --audio_in 001_fixed.wav \ --ssl 0

这里最重要的是:

--ssl 0

官方客户端的--ssl参数默认值为1,设置为0后使用普通ws://连接。

正常情况下,客户端会显示:

connect to ws://127.0.0.1:10096

然后开始发送音频并接收识别结果。


3. 为什么不能直接使用默认参数?

如果直接执行:

python3 funasr_wss_client.py \ --host "127.0.0.1" \ --port 10096 \ --mode 2pass \ --audio_in 001_fixed.wav

客户端默认:

--ssl 1

因此会连接:

wss://127.0.0.1:10096

而我们的 FunASR 服务端使用:

--certfile 0

关闭了 SSL,因此服务端实际监听的是:

ws://127.0.0.1:10096

两者协议不一致:

客户端 服务端 wss:// ───── TLS ──────×──── ws://

最终会出现:

ConnectionResetError

因此,在关闭 SSL 的 FunASR Runtime 服务上测试时,需要显式添加:

--ssl 0

4. 官方文档和客户端源码

如果后续需要查看客户端支持的全部参数,建议直接查看官方源码:

查看 funasr_wss_client.py 源码

官方 Runtime 文档:

查看 FunASR Runtime 快速开始文档

官方文档中的实时 2Pass 客户端示例也是:

python funasr_wss_client.py \ --host "127.0.0.1" \ --port 10095 \ --mode 2pass \ --chunk_size "5,10,5"

如果 Docker 将容器的10095映射到了宿主机的10096,则把端口改成:

--port 10096

即可。


十一、第一次测试遇到的 ConnectionResetError

最开始直接运行:

python3 funasr_wss_client.py \ --host "127.0.0.1" \ --port 10096 \ --mode 2pass \ --audio_in 001_fixed.wav

客户端输出:

ssl=1

并且:

connect to wss://127.0.0.1:10096

随后出现:

ConnectionResetError

原因其实很明确:

客户端 ↓ wss:// ↓ TLS/SSL连接 × FunASR服务 ↓ ws:// ↓ 普通WebSocket

两边的协议不一致。

客户端尝试进行 TLS 握手,而 FunASR 服务端并没有开启 TLS,因此连接被服务端直接断开。


十二、解决 ConnectionResetError

只需要增加:

--ssl 0

完整命令:

python3 funasr_wss_client.py \ --host "127.0.0.1" \ --port 10096 \ --mode 2pass \ --audio_in 001_fixed.wav \ --ssl 0

此时客户端应该连接:

connect to ws://127.0.0.1:10096

而不是:

connect to wss://127.0.0.1:10096

这时候就可以正常进行 WebSocket 通信。


十三、WAV文件需要注意什么

FunASR Runtime 的 WebSocket 接口并不是简单地:

open("test.wav", "rb").read()

然后把整个 WAV 文件发送出去。

WAV 文件结构大致是:

┌────────────────────┐ │ WAV Header │ ├────────────────────┤ │ PCM Audio Data │ ├────────────────────┤ │ PCM Audio Data │ ├────────────────────┤ │ ... │ └────────────────────┘

WebSocket 客户端发送音频时,应该发送其中的:

PCM Audio Data

而不是完整的 WAV 文件。

FunASR samples 中的客户端就是先读取 WAV:

with wave.open(wav_path, "rb") as wav_file: params = wav_file.getparams() frames = wav_file.readframes(wav_file.getnframes()) audio_bytes = bytes(frames)

然后再把audio_bytes分块发送给 WebSocket 服务。

因此如果自己编写客户端,不要简单写成:

with open("001_fixed.wav", "rb") as f: audio_data = f.read() ws.send(audio_data)

否则可能导致服务端无法按照预期处理音频。


十四、自己编写Python客户端

如果不想使用官方的funasr_wss_client.py,也可以自己实现。

核心流程是:

读取WAV ↓ 提取PCM ↓ 建立WebSocket ↓ 发送JSON握手 ↓ 分块发送PCM ↓ 发送 is_speaking=false ↓ 接收最终结果

例如:

import websocket import json import wave def test_funasr(): ws = websocket.create_connection( "ws://127.0.0.1:10096", timeout=30 ) print("WebSocket连接成功") with wave.open("001_fixed.wav", "rb") as wf: channels = wf.getnchannels() sample_width = wf.getsampwidth() sample_rate = wf.getframerate() pcm_data = wf.readframes( wf.getnframes() ) print( "channels:", channels, "sample_width:", sample_width, "sample_rate:", sample_rate ) # 建议音频格式: # 16kHz / 单声道 / 16bit PCM handshake = { "mode": "2pass", "wav_name": "test", "audio_fs": 16000, "is_speaking": True, "itn": False } ws.send( json.dumps(handshake) ) # 根据实际项目需求分块发送 chunk_size = 1920 for i in range( 0, len(pcm_data), chunk_size ): chunk = pcm_data[ i:i + chunk_size ] ws.send( chunk, opcode=websocket.ABNF.OPCODE_BINARY ) # 告诉服务端音频发送结束 ws.send( json.dumps({ "is_speaking": False }) ) try: while True: result = ws.recv() if not result: break print( "识别结果:", result ) except websocket.WebSocketTimeoutException: print("等待识别结果超时") finally: ws.close() if __name__ == "__main__": test_funasr()

实际生产环境中,还需要按照 FunASR 2Pass 协议处理 Online、Offline 和最终结果,而不是简单地把所有消息直接打印出来。


十五、funasr_client_api.py是什么?

FunASR samples 中还有:

funasr_client_api.py

这个文件名字很容易让人误以为它是 HTTP API 客户端。

实际上,从代码可以看到,它仍然使用:

from websocket import create_connection

建立 WebSocket 连接。

它根据is_ssl决定使用:

wss://

还是:

ws://

代码逻辑是:

if is_ssl == True: uri = "wss://{}:{}".format(host, port) else: uri = "ws://{}:{}".format(host, port)

因此当前我们部署的:

--certfile 0

对应:

is_ssl=False

这一点在使用这个客户端时同样需要注意。


十六、funasr_client_api.py的另一个优点

这个客户端并不是把整个 WAV 文件直接发送给服务器。

它首先使用:

wave.open()

读取音频,然后:

frames = wav_file.readframes( wav_file.getnframes() )

获取 PCM 音频数据。

之后再计算:

stride

将音频分成多个 chunk:

WAV ↓ PCM ↓ chunk 1 ↓ chunk 2 ↓ chunk 3 ↓ ... ↓ FunASR WebSocket

这也是自己实现 FunASR WebSocket 客户端时非常值得参考的地方。


十七、整个部署架构

完成部署之后,整体结构如下:

宿主机 ┌─────────────────────────────────────┐ │ │ │ Python Client │ │ │ │ │ │ WebSocket │ │ │ ws://127.0.0.1:10096 │ │ ▼ │ │ Docker Port │ │ │ │ │ │ 10096 → 10095 │ │ ▼ │ │ ┌───────────────────────────────┐ │ │ │ FunASR Container │ │ │ │ │ │ │ │ websocket-server-2pass │ │ │ │ │ │ │ │ │ ┌─────┴─────┐ │ │ │ │ │ │ │ │ │ │ Online Offline │ │ │ │ │ │ │ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │ │ │ 2Pass │ │ │ │ │ │ │ │ │ 最终结果 │ │ │ └───────────────────────────────┘ │ │ │ │ /workspace/models │ │ ▲ │ └─────────────────┼───────────────────┘ │ /mnt/data/funasr/models

十八、常见问题总结

1.docker exec提示容器没有运行

如果:

docker exec -it funasr bash

提示:

container ... is not running

说明容器启动后马上退出。

首先不要反复执行docker run,先查看:

docker ps -a

然后:

docker logs funasr

这通常可以直接找到容器退出原因。


2.ConnectionResetError

如果看到:

connect to wss://127.0.0.1:10096

然后:

ConnectionResetError

检查服务端是不是使用:

--certfile 0

如果是,那么客户端必须:

--ssl 0

即:

ws://

而不是:

wss://

3. 10096和10095不要弄混

Docker:

-p 10096:10095

意味着:

宿主机:10096 容器:10095

宿主机上的客户端:

127.0.0.1:10096

容器内部的服务:

10095

4. 不要直接发送完整WAV

不要简单:

open("test.wav", "rb").read()

然后:

ws.send(data)

应该先提取:

PCM

再按照 FunASR Runtime 的协议分块发送。


十九、最终测试命令

如果前面的服务已经启动,最简单的测试方式就是:

cd funasr_samples/samples/python python3 funasr_wss_client.py \ --host "127.0.0.1" \ --port 10096 \ --mode 2pass \ --audio_in 001_fixed.wav \ --ssl 0

如果能够正常输出识别结果,就说明:

Docker ↓ FunASR Runtime ↓ Paraformer ↓ VAD ↓ 2Pass ↓ WebSocket ↓ Python Client

整个链路已经打通。


二十、后续项目集成

完成 FunASR 部署后,可以进一步把它集成到自己的业务系统中。

例如:

浏览器 ↓ 上传音频 ↓ Next.js API ↓ Python ASR Service ↓ FunASR Runtime ↓ WebSocket ↓ Paraformer ↓ 返回文字

如果只是普通的音频文件转文字,可以考虑将 FunASR 封装成一个独立的 ASR 服务。

如果需要实时语音转写,则可以让前端通过 WebSocket 持续发送音频数据,并使用 FunASR 的 Online + Offline 2Pass 能力实现实时识别和最终结果修正。


总结

这次部署中最容易踩坑的其实不是 Docker,而是FunASR WebSocket 协议和 SSL 配置

最关键的几个点可以总结成:

① Docker端口 10096:10095 ② 服务端没有启用SSL --certfile 0 ③ 客户端必须使用 ws:// 而不是 wss:// ④ WAV不要直接发送 提取PCM ⑤ 2Pass Online + Offline = 最终更准确的识别结果

对于第一次部署 FunASR 的用户来说,建议优先使用官方提供的funasr_wss_client.py进行验证,确认服务端本身工作正常之后,再根据自己的业务需求编写客户端。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 20:44:46

洞穴建图实战:ROS1与ROS2下的传感器配置与Cartographer建图

如果你在露天园区用 2D 激光雷达建一张地图,一般十几分钟就能出一张能用的栅格图;可一旦把同样一套 ROS 建图流程搬到洞穴、地下矿道或隧道里,情况会很快失控——地图漂移、回环闭合失败、点云撕裂,甚至跑着跑着系统直接报“找不到…

作者头像 李华
网站建设 2026/9/3 20:42:16

数据链路层(以太网+以太网帧格式+访问控制方式+ARP协议)

首先要清楚的是,真正在网络中跑的报文,其实都是数据帧(IP报文是要给到数据链路层的)认识以太网以太网不是一种具体的网络,而是一种技术标准;它完整规定了物理层介质和信号、数据链路层的帧格式,…

作者头像 李华
网站建设 2026/9/3 20:39:46

双相机视觉项目实战:C#海康工业相机连接与并发取流全解析

简介:一套基于C#的海康工业相机双机连接示例工程,采用WinForms界面,面向工业视觉开发者、自动化设备调试人员和C#硬件交互学习者,目标是解决两台相机同步连接、独立参数配置与图像采集的常见问题。资源共48个文件,压缩…

作者头像 李华
网站建设 2026/9/3 20:39:21

电气符号库整理避坑指南:标准、软件与项目实战全解析

简介:这份CAD电气符号总集面向电气工程师、电子设计人员及自动化领域学习者,集中了电阻、电容、二极管、晶体管、继电器、开关、电机等常见电气元件符号,可覆盖工业控制、建筑布线、自动化系统、电力工程等多种设计场景,帮助用户直…

作者头像 李华
网站建设 2026/9/3 20:33:04

mod_RSsim:SAR原始回波仿真与成像算法验证的MATLAB实践

简介:面向通信工程教学、科研与产品开发场景的mod_RSsim仿真模拟软件,专注卷积码、里德-所罗门码等纠错编码机制的分析与验证。软件源代码开放,用户可查看和修改核心实现,便于按需定制;图形界面直观,支持常…

作者头像 李华
网站建设 2026/9/3 20:30:26

注意避坑!不是所有 AI 都能写论文,2026 导师推荐工具盘点

每年毕业季,无数同学深陷论文难题:开题毫无思路、搭建框架耗费数日、初稿逻辑松散、查重标红泛滥、AI检测超标、格式反复被导师驳回。面对这些痛点,不少学生转向通用型AI工具寻求帮助,但市面上的AI平台大多存在明显短板。它们往往…

作者头像 李华