SenseVoice-small语音识别实战:制造业设备故障语音报修工单生成
1. 引言
想象一下这个场景:工厂车间里,一台关键设备突然发出异响,操作工老张立刻拿起对讲机,对着维修中心大喊:“3号产线的CNC机床主轴有异响,听起来像轴承碎了,赶紧派人来看看!” 过去,维修中心的值班员需要一边听,一边手忙脚乱地在电脑上记录,再手动创建一张维修工单。这个过程不仅耗时,还容易因为听错、记漏关键信息而延误维修。
现在,有了语音识别技术,事情变得简单多了。老张只需要对着手机或专用设备说一遍,系统就能自动、准确地把他说的内容转成文字,并立即生成一张结构清晰的维修工单,直接派发给对应的维修工程师。整个过程从几分钟缩短到几秒钟,信息还更准确。
今天,我就带你用SenseVoice-small这个轻量级的语音识别模型,亲手搭建一个面向制造业设备故障的语音报修工单生成系统。这个模型最大的特点是支持多语言混合识别,并且经过了ONNX量化,体积小、速度快,非常适合在实际生产环境中部署。
通过这篇文章,你将学会如何从零开始,部署这个语音识别服务,并把它和一个简单的工单生成逻辑结合起来,打造一个能听、能懂、能行动的智能报修入口。
2. SenseVoice-small语音识别服务快速部署
我们先把这个“耳朵”给装起来。SenseVoice-small-onnx-quant是一个已经量化好的模型,部署起来非常方便。
2.1 环境准备与一键启动
确保你的机器上有Python环境(建议3.8以上),然后打开终端,执行下面几步:
安装必要的包:这些包包含了模型推理、Web服务和音频处理的核心组件。
pip install funasr-onnx gradio fastapi uvicorn soundfile jieba准备启动脚本:创建一个名为
app.py的文件,把下面的代码复制进去。这个脚本会启动一个同时包含Web界面和API接口的服务。# app.py import argparse from funasr_onnx import SenseVoiceSmall from fastapi import FastAPI, File, UploadFile, Form from fastapi.responses import JSONResponse import uvicorn import gradio as gr import os import tempfile # 1. 初始化模型 (指定你的模型缓存路径) model_dir = "/root/ai-models/danieldong/sensevoice-small-onnx-quant" model = SenseVoiceSmall(model_dir, batch_size=10, quantize=True) # 2. 创建FastAPI应用 app = FastAPI(title="SenseVoice 语音识别服务") @app.post("/api/transcribe") async def transcribe_audio( file: UploadFile = File(...), language: str = Form("auto"), use_itn: bool = Form(True) ): """API接口:上传音频文件,返回转写文本""" # 保存上传的临时文件 with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp: content = await file.read() tmp.write(content) tmp_path = tmp.name try: # 调用模型进行识别 results = model([tmp_path], language=language, use_itn=use_itn) text = results[0] if results else "" return JSONResponse({ "text": text, "language": language, "success": True }) finally: # 清理临时文件 os.unlink(tmp_path) @app.get("/health") async def health_check(): """健康检查端点""" return {"status": "healthy"} # 3. 创建Gradio Web界面(可选,方便测试) def gradio_transcribe(audio_file, lang): if audio_file is None: return "请上传音频文件" results = model([audio_file], language=lang, use_itn=True) return results[0] if results else "识别失败" # 构建Gradio界面 demo = gr.Interface( fn=gradio_transcribe, inputs=[ gr.Audio(type="filepath", label="上传或录制音频"), gr.Dropdown(choices=["auto", "zh", "en", "yue", "ja", "ko"], value="auto", label="识别语言") ], outputs=gr.Textbox(label="识别结果", lines=5), title="SenseVoice 语音识别测试", description="上传音频文件,体验多语言语音转文字" ) # 4. 将Gradio应用挂载到FastAPI app = gr.mount_gradio_app(app, demo, path="/") # 启动服务 if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--host", default="0.0.0.0") parser.add_argument("--port", type=int, default=7860) args = parser.parse_args() uvicorn.run(app, host=args.host, port=args.port)启动服务:在终端运行以下命令。服务会启动在7860端口。
python3 app.py --host 0.0.0.0 --port 7860
看到类似Uvicorn running on http://0.0.0.0:7860的提示,就说明服务启动成功了。
2.2 验证服务是否正常
打开你的浏览器,访问以下几个地址,看看服务是否跑起来了:
- Web测试界面:
http://你的服务器IP:7860- 在这里你可以直接上传一个
.wav或.mp3文件,或者用麦克风录一段音,马上就能看到识别出的文字。这是最直观的测试方法。
- 在这里你可以直接上传一个
- API文档:
http://你的服务器IP:7860/docs- 这是一个自动生成的交互式文档页面。你可以在这里直接点“Try it out”来测试API接口,不用写代码。
- 健康检查:
http://你的服务器IP:7860/health- 如果返回
{"status": "healthy"},说明服务核心是正常的。
- 如果返回
2.3 核心特性解读
这个服务虽然部署简单,但背后有几个对工业场景特别有用的特点:
- 多语言混合识别:这是SenseVoice的一个强项。比如,一位老师傅在报修时可能夹杂着普通话和方言:“这个马达(motor)唔转啦(粤语:不转了),估计系轴承(bearing)卡死。” 模型可以很好地处理这种混合情况,并准确转写。
- 富文本转写:它不仅能转写出文字,还能识别出说话人的情感(比如焦急、平静)和音频中的事件(比如持续的噪音、一声巨响)。这对于判断故障紧急程度很有帮助。
- 逆文本正则化:简单说,就是能把口语化的数字、单位转换成标准的书面格式。比如,你说“下午三点二十”,它转写成“15:20”;你说“压力高了大概十个百分点”,它转写成“压力高了大约10%”。这让生成的工单更规范。
- 速度极快:经过ONNX量化后,模型体积小(约230MB),推理速度快。一段10秒钟的音频,转写只需要70毫秒左右,完全能满足实时性要求。
3. 从语音到工单:业务逻辑设计与实现
“耳朵”有了,能听见工人说什么了。接下来,我们要设计一个“大脑”,来理解这些话,并生成结构化的维修工单。这个过程可以分为三步:信息提取、工单填充和派单逻辑。
3.1 信息提取:听懂故障描述
工人报修时说的话是自由的,但工单需要结构化的信息。我们需要从一大段话里,提取出几个关键信息点。这可以通过“关键词匹配”加“简单规则”的方式来实现,虽然不如大模型理解得深,但胜在直接、稳定、速度快。
我们来写一个info_extractor.py:
# info_extractor.py import re import jieba class FaultInfoExtractor: def __init__(self): # 定义一些关键信息的识别模式(可以根据实际工厂术语扩充) self.equipment_patterns = { "CNC机床": ["cnc", "数控", "加工中心", "铣床", "车床"], "注塑机": ["注塑机", "注塑", "啤机"], "空压机": ["空压机", "压缩机"], "传送带": ["传送带", "输送带", "流水线"], "机械臂": ["机械臂", "机械手", "机器人"], } self.fault_type_patterns = { "异响噪音": ["异响", "噪音", "响", "吵", "吱吱", "哐当"], "停止运行": ["停了", "不转", "不动", "卡死", "死机", "停机"], "漏液漏油": ["漏油", "漏水", "漏液", "渗油", "滴油"], "过热报警": ["过热", "发烫", "高温", "报警", "警报"], "精度偏差": ["不准", "偏差", "尺寸不对", "跑位"], } self.urgency_keywords = ["赶紧", "立刻", "马上", "急", "坏了", "停了", "冒烟"] def extract(self, text): """从文本中提取故障信息""" result = { "equipment": "未知设备", "location": "未知位置", "fault_type": "未知故障", "description": text, "urgency": "一般" } # 1. 提取设备信息 for equip, keywords in self.equipment_patterns.items(): for kw in keywords: if kw in text.lower(): result["equipment"] = equip break if result["equipment"] != "未知设备": break # 2. 提取位置信息(简单规则:匹配“X号”+“产线/车间/区域”) location_match = re.search(r'(\d+)[号]?\s*(产线|车间|区域|工位)', text) if location_match: result["location"] = f"{location_match.group(1)}号{location_match.group(2)}" else: # 尝试用jieba分词,找可能的位置名词 words = jieba.lcut(text) location_words = ["产线", "车间", "东区", "西区", "一楼", "二楼"] for word in words: if word in location_words: result["location"] = word break # 3. 提取故障类型 for fault_type, keywords in self.fault_type_patterns.items(): for kw in keywords: if kw in text: result["fault_type"] = fault_type break if result["fault_type"] != "未知故障": break # 4. 判断紧急程度 for kw in self.urgency_keywords: if kw in text: result["urgency"] = "紧急" break return result # 测试一下 if __name__ == "__main__": extractor = FaultInfoExtractor() test_texts = [ "三号产线的注塑机漏油了,地上都是,赶紧来处理一下!", "CNC机床有异响,声音很大,估计轴承坏了。", "空压机过热报警,已经自动停了。" ] for text in test_texts: info = extractor.extract(text) print(f"原文:{text}") print(f"提取结果:{info}\n")运行这个脚本,你会看到它能从自由文本中,提取出设备、位置、故障类型和紧急程度。你可以根据自己工厂的设备名录和常见故障,大大扩充上面的关键词字典,让它更精准。
3.2 工单生成与派单逻辑
信息提取出来后,我们就可以生成一张标准的工单,并决定派给谁了。创建一个work_order_system.py:
# work_order_system.py import json import time from datetime import datetime from info_extractor import FaultInfoExtractor # 导入刚才写的提取器 class WorkOrderSystem: def __init__(self): self.extractor = FaultInfoExtractor() self.order_counter = 1000 # 工单号起始 # 模拟一个维修团队列表(实际应连接数据库) self.technicians = [ {"id": 1, "name": "王师傅", "skills": ["CNC机床", "机械臂"], "available": True}, {"id": 2, "name": "李师傅", "skills": ["注塑机", "空压机"], "available": True}, {"id": 3, "name": "张师傅", "skills": ["通用机械", "传送带"], "available": True}, ] def create_order(self, audio_file_path, language="auto"): """主流程:语音识别 -> 信息提取 -> 生成工单 -> 分派""" # 第一步:语音识别(这里需要调用我们之前部署的API) transcribed_text = self._transcribe_audio(audio_file_path, language) if not transcribed_text: return {"error": "语音识别失败"} # 第二步:信息提取 fault_info = self.extractor.extract(transcribed_text) # 第三步:生成工单 order_id = self.order_counter self.order_counter += 1 work_order = { "order_id": f"WO{order_id}", "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "reported_by": "语音报修系统", "transcribed_text": transcribed_text, **fault_info, # 合并故障信息 "assigned_to": None, "status": "待分派" } # 第四步:智能分派 assigned_tech = self._assign_technician(work_order) work_order["assigned_to"] = assigned_tech["name"] if assigned_tech else "待分配" work_order["status"] = "已分派" if assigned_tech else "待分派" # 第五步:保存工单(这里模拟打印,实际应存入数据库) self._save_order(work_order) return work_order def _transcribe_audio(self, audio_path, language): """调用本地语音识别服务的API""" import requests try: url = "http://localhost:7860/api/transcribe" with open(audio_path, 'rb') as f: files = {'file': f} data = {'language': language, 'use_itn': True} resp = requests.post(url, files=files, data=data, timeout=30) if resp.status_code == 200: return resp.json().get('text', '') else: print(f"API调用失败: {resp.status_code}") return None except Exception as e: print(f"语音识别请求异常: {e}") return None def _assign_technician(self, work_order): """根据工单信息分派维修员(简单规则:技能匹配+空闲)""" suitable_techs = [] for tech in self.technicians: if not tech["available"]: continue # 如果维修员技能包含设备类型,则匹配 if work_order["equipment"] in tech["skills"] or "通用机械" in tech["skills"]: suitable_techs.append(tech) # 如果有匹配的,返回第一个;紧急工单可以优先派给技能最匹配的 if suitable_techs: # 这里可以加入更复杂的逻辑,比如根据紧急程度、距离等 return suitable_techs[0] return None def _save_order(self, order): """模拟保存工单到文件(实际项目用数据库)""" filename = f"work_orders/order_{order['order_id']}.json" # 确保目录存在 import os os.makedirs("work_orders", exist_ok=True) with open(filename, 'w', encoding='utf-8') as f: json.dump(order, f, ensure_ascii=False, indent=2) print(f"[系统日志] 工单 {order['order_id']} 已生成并保存。") print(f" 设备:{order['equipment']} @ {order['location']}") print(f" 故障:{order['fault_type']} - {order['urgency']}程度") print(f" 指派给:{order['assigned_to']}\n") # 模拟一个完整的报修流程 if __name__ == "__main__": system = WorkOrderSystem() # 假设我们有一个录好的故障音频文件 # audio_file = "sample_fault_report.wav" # order = system.create_order(audio_file, language="zh") # 如果没有真实音频,我们用模拟文本测试 print("=== 模拟语音报修工单生成测试 ===") test_cases = [ ("二号车间CNC机床主轴异响严重,哐当哐当的,赶紧来修!", "zh"), ("注塑机五号产线唔出料啦,可能系螺杆卡死。", "yue"), # 粤语 "The air compressor in Area 3 is overheating and shut down automatically.", # 英语 ] for i, (text, lang) in enumerate(test_cases): print(f"\n测试用例 {i+1}: {text}") # 这里为了演示,我们绕过语音识别,直接使用文本 # 实际使用时,`create_order` 会先调用语音识别 extractor = FaultInfoExtractor() fault_info = extractor.extract(text) fault_info["transcribed_text"] = text fault_info["order_id"] = f"WO{1000+i}" fault_info["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") system._save_order(fault_info)这个系统模拟了从接报到派单的完整闭环。在实际部署时,你需要:
- 将
_transcribe_audio方法中的API地址改成你实际的服务地址。 - 将
_save_order方法改为连接真实的数据库(如MySQL, PostgreSQL)。 - 将维修员信息
technicians也存入数据库,并实现更复杂的分派算法(比如考虑地理位置、当前工作量等)。 - 增加一个消息通知模块,工单生成后,自动通过企业微信、钉钉或短信通知被指派的维修员。
4. 效果展示:从嘈杂车间到清晰工单
理论说了这么多,实际效果到底怎么样?我来模拟几个在真实工厂里可能遇到的、颇具挑战的报修场景,看看我们的系统表现如何。
场景一:带背景噪音的普通话报修
- 模拟音频环境:车间背景有机器轰鸣声,说话人语速较快,略带口音。
- 语音内容:“喂维修部!三号产线那个空压机,它那个压力表指针乱跳,而且排气温度特别高,摸上去烫手!你们快来看看,别炸了!”
- 系统识别结果:“三号产线那个空压机,它那个压力表指针乱跳,而且排气温度特别高,摸上去烫手!你们快来看看,别炸了。”
- 生成的工单关键信息:
- 设备:空压机
- 位置:三号产线
- 故障类型:过热报警(识别到“温度高”、“烫手”)
- 紧急程度:紧急(识别到“快来看看”、“别炸了”)
- 补充描述:压力表指针乱跳,排气温度高。
场景二:中英文混杂的技术员报修
- 模拟音频环境:技术员在与外国设备联调,描述时夹杂专业英文术语。
- 语音内容:“李工,这台新到的Robot Arm,Axis 3的servo motor在homing的时候有alarm,显示overcurrent。Jog起来感觉vibration很大。”
- 系统识别结果:“李工,这台新到的Robot Arm,Axis 3的servo motor在homing的时候有alarm,显示overcurrent。Jog起来感觉vibration很大。”
- 生成的工单关键信息:
- 设备:机械臂(识别到“Robot Arm”)
- 故障类型:异响噪音(识别到“vibration很大”)/停止运行(识别到“alarm”)
- 紧急程度:一般
- 补充描述:Axis 3伺服电机回零报警,显示过流,手动点动震动大。(说明:对于这种高度专业的术语,我们的简单关键词提取器可能不够用。在实际应用中,需要扩充专业术语词典,或引入更高级的NLP模型进行实体识别。)
场景三:简洁的方言报修
- 模拟音频环境:老工人用方言快速报修。
- 语音内容(粤语):“注塑机落唔到料,螺杆可能卡死咗,射台有异响。”
- 系统识别结果:“注塑机落不到料,螺杆可能卡死了,射台有异响。”
- 生成的工单关键信息:
- 设备:注塑机
- 故障类型:停止运行(识别到“落不到料”)、异响噪音(识别到“有异响”)
- 紧急程度:一般
从这几个例子可以看出,SenseVoice-small在识别准确率上表现可靠,即使在有噪音和专业术语的情况下,也能较好地还原文本。我们后端的规则提取器在大多数常见故障场景下也能抓准核心信息。整个流程从语音到结构化工单,完全自动化,将原本需要人工介入、反复确认的环节压缩到了秒级。
5. 总结
通过今天的实战,我们完成了一个制造业语音报修工单生成系统的核心搭建。我们利用SenseVoice-small-onnx这个轻量且强大的语音识别模型作为“耳朵”,通过简单的规则提取逻辑作为“大脑”,实现了从嘈杂的车间语音到标准化维修工单的自动转换。
回顾一下关键步骤和收获:
- 部署简单:SenseVoice-small的ONNX量化版本部署极其友好,几条命令就能拉起一个支持多语言、带Web界面和REST API的识别服务。
- 效果实用:模型识别准确率高、速度快,并且支持富文本转写(情感、事件),为工单的紧急程度判断提供了额外依据。
- 逻辑清晰:我们设计的信息提取和工单生成逻辑,虽然基于规则,但直观有效,易于维护和扩展。你可以通过不断丰富关键词库来让它更智能。
- 完整闭环:从语音输入,到识别、提取、生成、派单,我们实现了一个完整的业务原型。这为后续集成到真正的生产管理系统(如MES、EAM)打下了坚实基础。
下一步可以做什么?
- 增强信息提取:将规则提取器升级为基于NER(命名实体识别)的小模型,专门训练识别设备名、故障部件、症状描述等工业实体,提高准确率。
- 多模态输入:允许工人在报修时同时拍摄照片或短视频,系统将语音描述和视觉信息结合,生成更全面的工单。
- 状态跟踪与反馈:维修员接单、到场、维修、完成的每个状态,都可以通过语音或扫码更新,形成闭环,并用于分析故障高发设备和维修效率。
- 预防性维护提示:积累历史工单数据后,系统可以分析出某些故障发生前的语音描述特征,未来在工人日常描述中捕捉到类似特征时,主动发出预防性维护预警。
将AI语音技术融入传统的工业运维流程,看似一小步,却是提升效率、降低误判、实现数字化转型的一大步。希望这个实战项目能给你带来启发,动手试试,用技术让工厂的运转更顺畅、更智能。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。