尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Django实战:构建语音识别智能垃圾分类系统

发布时间:2026/9/29 18:34:07

资讯中心
01
ARTICLE

Django实战:构建语音识别智能垃圾分类系统

Django实战:构建语音识别智能垃圾分类系统
简介这是一份基于Django与语音识别技术的智能垃圾分类系统项目源码适用于计算机毕业设计、课程设计及Python项目实战练习也可供对语音交互和Web开发感兴趣的开发者学习参考。压缩包仅9.4MB共305个文件主要由31个Python源文件、14个HTML模板、22个CSS样式、35个JavaScript脚本以及GIF演示图、PNG图标、字体资源、音频示例、说明文档和演示视频组成。目前已有388人浏览学习项目功能形成前后台闭环前台支持系统信息展示、语音上传垃圾分类、用户注册登录、资料修改与退出后台支持垃圾分类管理、用户信息维护和系统首页配置。源码亲测可用附带SQL数据库文件部署后可快速体验完整流程目录结构清晰便于继续扩展识别算法、优化界面或改造为其他分类应用是理解Django全栈开发与语音识别集成方式的优质参考。1. 从“鸡蛋壳是什么垃圾”到语音分类这个 Django 项目到底在做什么你在小区垃圾桶前站过十秒钟掏出手机搜“粽叶是什么垃圾”吗智能垃圾分类系统的核心诉求就是这样用最短的操作路径让用户说出垃圾名称系统立刻回一个可回收、厨余、有害还是其他。这个标题里的 Django 项目做的就是这件事——用户按住录音说“鸡蛋壳”后端把音频转成文字再通过词库匹配出“厨余垃圾”把结果返回页面。它的技术主干是 Django 语音识别ASR 分类匹配适合三种人想找一个不那么“学生管理系统”的 Django 实战选题的人课程设计或毕业设计需要完整源码加演示链路的人以及想搞明白“语音识别结果怎么接到 Web 后端里”的开发者。这套东西不需要你训练语音模型用现成识别引擎加一份词库就能跑起来难点不在算法而在把录音、转码、识别、匹配这条链路接顺。2. 先立骨架Django 项目结构、数据模型与语音识别方案选型2.1 三个语音识别方案怎么选在线 API、本地 Vosk、免训练规则表做语音识别接入之前先要决定识别引擎。常见做法是三条路各有各的坑。第一类是百度、讯飞这类短语音识别 API。准确率高中文效果好但需要注册应用拿 key有 QPS 限制免费额度用完就得付费。演示时一旦网络抖动接口超时页面就卡住。适合毕设答辩这种“必须一次成功”的场合前提是你接受把密钥放在配置里。第二类是 Vosk 这类本地离线识别引擎。下载一个中文小模型解码在本地完成不依赖网络响应稳定。代价是模型小识别准确率明显不如大厂的云端引擎同音字错误很常见“报纸”能给你识别成“抱子”“鸡蛋壳”识别成“鸡但可”。所以用 Vosk 的话后面匹配层必须做容错不能指望 ASR 给你绝对准确的文本。第三类是纯规则表也就是不接语音识别前端用下拉框选垃圾名称后端直接匹配类别。这条路学习成本最低但没碰到音频处理语音识别这个技术点就丢了答辩时容易被问穿。我给这个项目的建议是主通道用 Vosk 本地识别同时预留一个 API 识别的配置开关。Vosk 保证演示不依赖外网API 留作效果不佳时一键切换。代码上做一个函数封装两个实现互相替换不牵扯业务逻辑。2.2 django-admin 创建项目与 app目录划分先建虚拟环境装依赖创建项目和 app。以下是完整命令序列# 创建并激活虚拟环境Python 版本建议 3.9 以上 python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate # 安装核心依赖 pip install django djangorestframework vosk pypinyin django-cors-headers # 创建项目项目名直接叫 intelligent_garbage django-admin startproject intelligent_garbage . # 创建业务 app名字叫 voice_garbage便于一看就懂 python manage.py startapp voice_garbage # 验证项目能启动 python manage.py runserverstartproject后面有个点表示在当前目录生成 manage.py 和配置包不要漏。app 命名为voice_garbage而不是garbage是为了把“语音”这个入口语义留在名字里。pypinyin是后面做拼音模糊匹配用的django-cors-headers解决前端跨端口访问的问题这两个依赖后面都会用到。创建完 app 后在settings.py的INSTALLED_APPS里加上voice_garbage和corsheaders这是新手最容易漏的一步。漏了之后后面python manage.py makemigrations会提示没有检测到模型变更或者请求时报 500都是因为 app 没注册。2.3 数据模型垃圾词库表、识别记录表垃圾分类系统的核心数据模型就是词库表。每一条记录是一个标准垃圾名称加上类别、别名、积分字段# voice_garbage/models.py from django.db import models class GarbageItem(models.Model): 垃圾词库表标准名称 类别 别名 name models.CharField(max_length50, uniqueTrue, verbose_name标准名称) category models.CharField(max_length20, verbose_name垃圾类别) # 可回收/厨余/有害/其他 aliases models.CharField(max_length200, blankTrue, verbose_name别名) points models.IntegerField(default0, verbose_name积分) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Meta: db_table garbage_item indexes [models.Index(fields[name])] def __str__(self): return f{self.name}({self.category}) class RecognizeRecord(models.Model): 识别记录表记录每次语音识别的文本与分类结果 text models.CharField(max_length100, verbose_name识别文本) result models.CharField(max_length20, verbose_name分类结果) created_at models.DateTimeField(auto_now_addTrue, verbose_name识别时间) class Meta: db_table recognize_record ordering [-created_at]aliases字段是匹配策略的关键。Vosk 识别出的文本可能不是词库里的标准名称比如用户说“蛋壳”库里的标准名是“鸡蛋壳”靠别名表就能命中。recognize_record表用来做演示数据展示答辩时可以打开后台给评委看今天识别了多少条、分类准确率如何。写完模型后执行python manage.py makemigrations voice_garbage和python manage.py migrate。如果提示表已存在就手动清理数据库再迁移不要加--fake蒙混过去后面遇到数据对不上会很难排查。2.4 词库数据从哪来写一个 CSV 导入脚本词库表是空的不行得灌数据。手工一条一条在 admin 里加太慢常规做法是准备一份 CSV 然后写 management command 批量导入# voice_garbage/management/commands/import_garbage.py import csv from django.core.management.base import BaseCommand from voice_garbage.models import GarbageItem class Command(BaseCommand): help 从 CSV 导入垃圾词库CSV 列: name,category,aliases def add_arguments(self, parser): parser.add_argument(csv_path, typestr) def handle(self, *args, **options): path options[csv_path] count 0 with open(path, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: GarbageItem.objects.update_or_create( namerow[name].strip(), defaults{ category: row[category].strip(), aliases: row.get(aliases, ).strip(), }, ) count 1 self.stdout.write(self.style.SUCCESS(f词库导入完成共处理 {count} 条))调用方式python manage.py import_garbage garbage_data.csvCSV 编码用utf-8-sig而不是utf-8是因为 Excel 导出的 CSV 自带 BOM 头用utf-8读第一列会多一个不可见字符导致匹配失败。update_or_create保证重复执行脚本不会产生重复记录词库扩充时直接覆盖运行即可。这个脚本建议从一开始就写后面调试分类时你会不断往 CSV 里加词条重新导入比手动一条条敲快得多。3. 把语音变成文字MediaRecorder 录音、Vosk 识别与转码处理3.1 前端录音MediaRecorder 拿 WebM 格式音频用户的语音从哪里来最省事的前端方案是MediaRecorder接口。它在浏览器里直接调用麦克风录完拿到 Blob不需要额外插件!-- voice_garbage/templates/voice_garbage/index.html 片段 -- button idrecordBtn按住说话/button input typehidden idaudioBase64 / script const recordBtn document.getElementById(recordBtn); let mediaRecorder null; let chunks []; recordBtn.addEventListener(mousedown, async () { chunks []; const stream await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder new MediaRecorder(stream); mediaRecorder.ondataavailable (e) chunks.push(e.data); mediaRecorder.onstop () { const blob new Blob(chunks, { type: audio/webm }); const reader new FileReader(); reader.onload () { document.getElementById(audioBase64).value reader.result; }; reader.readAsDataURL(blob); }; mediaRecorder.start(); }); recordBtn.addEventListener(mouseup, () { mediaRecorder.stop(); }); /scriptFileReader.readAsDataURL会把音频转成 base64 字符串里面带了data:audio/webm;base64,前缀后端接收时要把前缀剥掉。这里有个体验细节按住说话用mousedown开始录音、mouseup停止比点击两次按钮更自然。移动端触摸事件要额外兼容touchstart/touchend否则手机浏览器录不了。MediaRecorder 录出来的格式是 webm这是坑的起点——后端如果用 pyaudio 直接读会报“Unknown format”。必须先用 ffmpeg 转成 WAV 再喂给识别引擎。3.2 Django 视图接收音频base64 解码与接口设计前端把音频转成 base64 后通过 POST 表单传给 Django。视图层这样接# voice_garbage/views.py import base64 from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .recognizer import recognize_audio from .classifier import classify_garbage csrf_exempt def process_voice(request): 接收音频完成识别分类一次返回结果 if request.method ! POST: return JsonResponse({error: 仅支持POST}, status405) audio_b64 request.POST.get(audio_base64, ) if not audio_b64: return JsonResponse({error: 缺少 audio_base64 参数}, status400) # 去掉 Data URL 前缀 if audio_b64.startswith(data:): audio_b64 audio_b64.split(,, 1)[1] audio_bytes base64.b64decode(audio_b64) text recognize_audio(audio_bytes) if not text: return JsonResponse({error: 未识别到语音内容}, status422) result classify_garbage(text) return JsonResponse({text: text, category: result[category], name: result[name]})这个接口把识别和分类合在了一起前端一次 POST 就拿到最终类别。csrf_exempt仅用于本地开发演示如果项目要部署到公网必须换掉——要么用 Django 的 CSRF 中间件配合前端带 token要么在登录态基础上加 JWT。我在项目里见过有人图省事全局关掉 CSRF结果被人拿来刷接口日志里全是垃圾请求这就是偷懒的代价。务实做法是给这个视图单独加csrf_exempt其他接口保持默认。3.3 识别函数ffmpeg 转码 Vosk 本地识别后端拿到的是 webm 二进制先写临时文件用 ffmpeg 转成 16kHz 单声道 WAV再交给 Vosk# voice_garbage/recognizer.py import json import subprocess import tempfile import wave from pathlib import Path from django.conf import settings from vosk import Model, KaldiRecognizer # 模型路径用 BASE_DIR 拼接避免工作目录不一致导致找不到模型 MODEL_PATH Path(settings.BASE_DIR) / model / vosk-model-small-cn-0.22 def recognize_audio(audio_bytes: bytes) - str: 接收 webm 音频字节返回识别文本 # 写临时文件 with tempfile.NamedTemporaryFile(suffix.webm, deleteFalse) as tmp: tmp.write(audio_bytes) webm_path tmp.name wav_path webm_path.replace(.webm, .wav) # ffmpeg 转码16kHz 采样率、单声道、16bit PCM subprocess.run( [ ffmpeg, -y, -i, webm_path, -ar, 16000, -ac, 1, -c:a, pcm_s16le, wav_path, ], checkTrue, capture_outputTrue, ) # Vosk 解码 model Model(str(MODEL_PATH)) recognizer KaldiRecognizer(model, 16000) with wave.open(wav_path, rb) as wf: while True: data wf.readframes(4000) if len(data) 0: break recognizer.AcceptWaveform(data) result json.loads(recognizer.FinalResult()) return result.get(text, ).strip()-ar 16000是采样率语音识别模型基本都按 16kHz 训练你不转它就只能识别出乱码。-ac 1是单声道双声道音频会让识别结果重复或丢字。-c:a pcm_s16le显式指定输出编码为 16 位 PCM避免 ffmpeg 按默认编码输出成其他格式。tempfile.NamedTemporaryFile用完别急着删Vosk 是流式读取文件句柄关闭后读不到内容调试时留着临时文件反而方便检查。识别启动后如果返回空字符串先看日志里有没有“Model not found”。这个问题我踩过两次都是因为Model(model/...)用了相对路径而 Django 启动目录和命令执行目录不一致。用settings.BASE_DIR拼绝对路径就不会有这种玄学问题。3.4 在线 API 识别Vosk 效果差时的备用通道Vosk 小模型遇到生僻词、方言、环境噪声大时识别结果基本没法看。这时候可以切在线 API。下面是通用的调用模板# voice_garbage/recognizer.py 追加 import requests def recognize_by_api(audio_bytes: bytes, api_url: str, api_token: str) - str: 调云端短语音识别接口返回文本 files {audio: audio_bytes} headers {Authorization: fBearer {api_token}} resp requests.post(api_url, headersheaders, filesfiles, timeout10) resp.raise_for_status() data resp.json() return data.get(result, {}).get(text, ).strip()具体请求参数要以你选的 API 文档为准但结构基本一致音频二进制、鉴权头、超时控制。调用前先确认音频采样率是否符合接口要求有的接口不接收 webm那就得复用前面的 ffmpeg 转码结果再上传。密钥不要硬编码进代码从settings.py里读环境变量上线时在服务器环境里配置代码仓库里不放任何真实 token。我在自己的项目里习惯把USE_API False写在 settings 里切换识别通道只需改这个布尔值不动业务代码。4. 把文字变成垃圾类别三层匹配策略、视图整合与后台词库管理4.1 三层匹配精确 → 别名 → 拼音模糊ASR 识别出来的文本不能直接拿去查数据库。用户可能说“鸡蛋壳”也可能说“蛋壳”Vosk 可能把“报纸”识别成“抱子”。所以分类层要做三级匹配# voice_garbage/classifier.py import re from difflib import SequenceMatcher from pypinyin import lazy_pinyin from voice_garbage.models import GarbageItem def classify_garbage(spoken_text: str) - dict: 把语音识别文本映射为垃圾类别返回类别和标准名称 text re.sub(r[\s。,.、], , spoken_text) if not text: return {category: 未识别, name: } # 第一层精确匹配标准名称 item GarbageItem.objects.filter(nametext).first() if item: return {category: item.category, name: item.name} # 第二层别名匹配 for item in GarbageItem.objects.all(): aliases [a.strip() for a in item.aliases.split(,) if a.strip()] if text in aliases: return {category: item.category, name: item.name} # 第三层拼音模糊匹配抵消同音字错误 spoken_py .join(lazy_pinyin(text)) best_item None best_score 0.0 for item in GarbageItem.objects.all(): name_py .join(lazy_pinyin(item.name)) score SequenceMatcher(None, spoken_py, name_py).ratio() if score best_score: best_score score best_item item if best_item and best_score 0.8: return {category: best_item.category, name: best_item.name} return {category: 未识别, name: text}第一层直接查name字段走索引速度最快。第二层遍历别名表适合“蛋壳 vs 鸡蛋壳”这种说法差异。第三层是这张表的灵魂——把文本转成拼音再做相似度比较比如 Vosk 识别出“抱子”转成拼音baozi跟“报纸”的baozhi相似度很高阈值 0.8 时能命中。阈值 0.8 不是拍脑袋定的。我试过 0.9Vosk 同音错误稍微多一点就漏了试过 0.7会把“香蕉皮”匹配成“香蕉”。0.8 是个折中点。实际项目里你可以在后台看到识别记录收集一段时间后把误匹配数据攒着按自己词库规模微调这个值。这里有人会想到训练一个分类模型把语音文本丢给朴素贝叶斯或 BERT。对一个演示级项目来说没有这个必要规则引擎足够应付常见垃圾名称而且每个分支都能解释清楚答辩时不会被问倒。想深入语音识别训练模型并推理的教程那是另一条赛道别在这个项目里铺太开。4.2 分类结果保存与积分逻辑匹配完成之后记录一条历史。修改views.py里的process_voice加保存逻辑# voice_garbage/views.py在 classify 之后追加 from .models import GarbageItem, RecognizeRecord # 接前面的 classify_garbage 调用 result classify_garbage(text) RecognizeRecord.objects.create(texttext, resultresult[category]) # 命中标准名称时给用户加分演示用 if result[category] ! 未识别: item GarbageItem.objects.filter(nameresult[name]).first() if item: score item.points else: score GarbageItem.objects.filter( categoryresult[category] ).first().points if GarbageItem.objects.filter(categoryresult[category]).exists() else 1 else: score 0 return JsonResponse({ text: text, category: result[category], name: result[name], points: score, })积分逻辑很简单命中的词条有自己定义的积分没命中但分类成功时取分类下任意词条的积分兜底。代码写到这里时你会发现一件事——垃圾类别的积分最好是只从GarbageItem里取不要把积分字段写在另一个表里否则两边数据不一致后面改词库时会很痛苦。4.3 前端结果展示局部刷新不整页跳转前端拿到 JSON 后只在结果卡片上更新内容不要让页面刷新。整页刷新会导致录音状态丢失、麦克风权限重新申请体验很差。// index.html 里的提交函数 function submitAudio(audioBase64) { const form new FormData(); form.append(audio_base64, audioBase64); fetch(/voice_garbage/process/, { method: POST, body: form }) .then(response response.json()) .then(data { const card document.getElementById(resultCard); card.style.display block; document.getElementById(categoryText).innerText data.category; document.getElementById(nameText).innerText data.name; document.getElementById(pointsText).innerText data.points || 0; // 分类成功时给卡片加不同的颜色 const colors { 可回收: #4CAF50, 厨余垃圾: #FF9800, 有害垃圾: #F44336, 其他垃圾: #607D8B, 未识别: #9E9E9E }; card.style.borderLeftColor colors[data.category] || #9E9E9E; }); }这个代码块里的视觉反馈是可选的但建议保留——你给评委演示时按类别变色能让人一眼看出系统“懂”了。如果前端报跨域错误就是前面 2.2 里说的django-cors-headers没配好回到 settings 里检查CORS_ALLOWED_ORIGINS。4.4 Django admin后台直接维护词库运营入口别靠 SQL词库管理是日常维护最频繁的地方给 admin 注册好表格就能在后台里直接增删改查# voice_garbage/admin.py from django.contrib import admin from .models import GarbageItem, RecognizeRecord admin.register(GarbageItem) class GarbageItemAdmin(admin.ModelAdmin): list_display (name, category, aliases, points) list_filter (category,) search_fields (name, aliases) admin.register(RecognizeRecord) class RecognizeRecordAdmin(admin.ModelAdmin): list_display (text, result, created_at) list_filter (result,)list_filter按垃圾类别过滤方便你查看某个类别下有多少词条search_fields填name和aliases这样搜索“蛋壳”也能找到“鸡蛋壳”那条记录。如果觉得 Django 原生后台太素可以装django-unfold换个现代的 admin 皮肤纯视觉增强不改业务代码。我个人的习惯是词库数据主要靠 CSV 批量导入管理admin 只用来做单条修正因为批量操作脚本比手工点击可靠得多。5. Django 语音识别联动避坑4 个高频翻车点与排查命令5.1 Model not found模型路径随着启动目录变化现象服务能启动但一调用识别函数就报Model not found或者识别结果永远是空的。原因Vosk 的Model()接收的是相对路径字符串而 Django 项目在python manage.py runserver启动时工作目录可能是你执行命令的目录不一定是项目根目录。我在项目根目录下能跑通换个终端从intelligent_garbage/子目录再跑一次同样的代码就找不到模型了。这就是典型的环境依赖问题不是代码逻辑错。解决不要用相对路径用settings.BASE_DIR拼绝对路径。同时建议在settings.py里加一个VOSK_MODEL_PATH配置项# settings.py import os from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent VOSK_MODEL_PATH os.environ.get(VOSK_MODEL_PATH, str(BASE_DIR / model / vosk-model-small-cn-0.22))然后在recognizer.py里读这个配置。这样部署到服务器时模型放别处也能通过环境变量指定路径不用改代码。5.2 转码失败webm 只是容器编码格式才是真坑现象前端录音传上来后后端 ffmpeg 转码报错或者生成的 wav 文件是 0 字节识别接口 500。原因MediaRecorder 在不同浏览器里产出的 webm 内部编码不同。Chrome 默认用 opusSafari 可能产出不同的音频轨道参数。ffmpeg 只给输入文件路径时面对某些 opus 参数组合会解析失败。另外一种情况是 ffmpeg 根本没安装命令行执行直接报No such file or directory。解决给 ffmpeg 加上显式的输出参数同时第一时间检查 ffmpeg 是否可用# 第一步确认 ffmpeg 装了 ffmpeg -version | head -1 # 第二步手动用一条命令验证转码 ffmpeg -y -i input.webm -ar 16000 -ac 1 -c:a pcm_s16le output.wav在代码里还要加异常捕获ffmpeg 失败时返回一个明确的错误信息而不是让整个接口 500。日志里打出capture_output的内容能看到 ffmpeg 具体报了什么错——我就是靠这条日志发现是输出路径权限问题临时目录不可写导致转码失败。5.3 同音字识别不稳定Vosk 输出受环境和语速影响大现象同一句话“报纸”上午识别成“报纸”下午识别成“抱子”同一个人说快一点结果就变。这是语音识别小模型最折腾人的地方。原因Vosk 小模型的语言模型权重是概率性的同音词的选择依赖上下文约束和声学特征的置信度。单个名词没有上下文模型天然会摇摆在同音词之间。这不是 bug是模型能力的边界。解决把容错放到分类层不要试图让 ASR 输出完美文本。拼音模糊匹配派上用场了——3.3 里的classify_garbage第三层就是干这个的。另外可以维护一份词库的拼音索引启动时预计算好放内存别每次请求都调lazy_pinyin扫全表词库多了以后会拖慢响应。我通常在GarbageItem里加一个name_pinyin字段导入时算好存进去匹配时只算用户文本的拼音。5.4 CORS 拦截接口通了但浏览器不认现象前端页面在localhost:8080Django 在localhost:8000POST 请求发出去后浏览器报CORS policy: No Access-Control-Allow-Origin。接口本身是通的curl 能调通浏览器不行。原因浏览器的同源策略拦截了跨域请求。Django 默认不返回 CORS 响应头所以前端拿不到响应。用 curl 测试没有这个限制这就是为什么很多人 curl 正常、页面里死活不通。解决装django-cors-headers并在 settings 里配置白名单别用CORS_ALLOW_ALL True图省事# settings.py INSTALLED_APPS [ corsheaders, # ... ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, # ... ] CORS_ALLOWED_ORIGINS [ http://localhost:8080, http://127.0.0.1:8080, ]CorsMiddleware要放在所有能返回响应的中间件前面否则 Django 处理完请求返回响应时头已经写死了加不上去。这个坑我排过半小时最后是看中间件顺序才发现的。5.5 排查命令合集从接口到数据库一条线查下去调试这类前后端联动的项目我习惯从外往里查。先确认请求到没到 Django再确认识别函数有没有输出最后确认词库里有没有数据# 看请求是否到达后端以及数据库查询被触发了多少条 python manage.py runserver --print-sql # 绕过前端直接用 curl 传 base64 音频测识别接口 curl -X POST http://127.0.0.1:8000/voice_garbage/process/ \ -F audio_base64$(base64 -w 0 test.webm) # 或直接测分类逻辑不经过音频 curl -X POST http://127.0.0.1:8000/voice_garbage/process/ \ -d text鸡蛋壳 # 检查词库数据 python manage.py shell -c from voice_garbage.models import GarbageItem; print(list(GarbageItem.objects.values(name,category))) # 误导入了脏数据时删除后再重新导入 python manage.py shell -c from voice_garbage.models import GarbageItem; GarbageItem.objects.filter(category未知).delete() python manage.py import_garbage garbage_data.csv--print-sql是 Django 自带日志会把每条 ORM 查询打印出来。如果一次请求打出几百条 SQL说明你的循环里在反复查库——比如 4.1 那个遍历全表的循环启动时预加载到内存能省掉 99% 的查询。这个习惯对新手特别有用能让你看到 ORM 到底执行了什么而不是黑匣子一样跑着。6. 用测试脚本给语音分类链路体检从单元测试到真机验证项目做到这里功能链路已经通了。但要验证它是不是真稳定得写自动化测试。Django 自带的TestCase可以直接测分类逻辑不用起服务# voice_garbage/tests/test_pipeline.py from django.test import TestCase from voice_garbage.classifier import classify_garbage from voice_garbage.models import GarbageItem class ClassifierPipelineTest(TestCase): 分类链路自动化测试覆盖精确/别名/拼音三层匹配 def setUp(self): GarbageItem.objects.create( name鸡蛋壳, category厨余垃圾, aliases蛋壳,鸡蛋白 ) GarbageItem.objects.create( name报纸, category可回收, aliases ) def test_exact_match(self): result classify_garbage(鸡蛋壳) self.assertEqual(result[category], 厨余垃圾) def test_alias_match(self): result classify_garbage(蛋壳) self.assertEqual(result[category], 厨余垃圾) def test_pinyin_fallback(self): # 模拟 Vosk 把“报纸”识别成“抱子”的情形 result classify_garbage(抱子) self.assertEqual(result[category], 可回收) def test_pinyin_wrong_threshold(self): # 相似度不够时不应误匹配 result classify_garbage(香蕉) self.assertNotEqual(result[category], 可回收)跑测试python manage.py test voice_garbage这个测试文件的价值在于你以后往词库里加词条、改匹配逻辑、调整阈值跑一下就知道有没有把原来的功能弄坏。我习惯在改完classifier.py后立刻跑一遍再顺手补一个新词的测试用例。语音识别引擎永远是黑匣子输出不稳定但我们的匹配层必须有确定性——这正是自动化测试能锁住的底线。测试通过后再做一次真机验证手机连到和服务器同一局域网用浏览器打开页面按住录音说三句——“鸡蛋壳”“旧报纸”“遥控器”看三次结果是否都正确。这一步不要用电脑模拟器电脑麦克风降噪和手机完全不一样Vosk 在手机上的表现更能暴露识别层的短板。开发到上线这步我吃过亏的就是直接拿笔记本演示现场环境嘈杂识别一连错三次场面尴尬。所以我现在每做一个语音项目必先准备一张离线词库的备用方案——用户在页面输入关键词也能触发分类语音挂了演示不至于整个翻车。另外如果之后想把识别结果实时推送而不依赖前端轮询可以考虑用 Django Channels 接 WebSocket把每次识别记录推给正在浏览的页面。这个方向属于实时推送优化核心链路是 Django 侧发消息、前端接 WebSocket 更新卡片和当前这套分类逻辑完全解耦可以放二期做。当前版本用fetch单次请求已经足够撑起演示和课程设计要求。整个项目做完你可以自豪的一点是从录音、转码、识别到分类整条链路每一段你都知道它怎么工作、坏了怎么查。如果以后有人问我智能垃圾分类怎么做我会先让他把这个链路跑通再谈 AI 模型和算法优化——链路不稳模型再聪明也白搭。希望这篇实战笔记帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。