自动录入表单 V2 --- 技术实现框架
农户表单自动录入系统 · 移动端 H5 应用
版本:T2(2026-07-20)
适用场景:巡检员移动端表单录入
源码打包可自取:https://download.csdn.net/download/m0_64997730/93160021
目录
1. 项目概述
1.1 业务场景
面向北方玉米/大豆主产区巡检员的移动端 H5 表单工具,解决田间巡检数据录入慢、易错、难统计的问题。巡检员在田间通过拍照 OCR 或语音 ASR 两种方式快速填报,系统自动识别并填入表单对应字段。
1.2 核心能力
| 能力 | 说明 |
|---|---|
| 拍照识别 | 拍纸质单据 → PaddleOCR 识别 → 自动填表 |
| 语音录入 | 实时长语音 → Web Speech API 转写 → 实时填表 |
| 精确解析 | 三层漏斗:正则 → 同音字+拼音模糊 → LLM 语义兜底 |
| 多表单支持 | 玉米/大豆 × 4 次巡检 = 8 种表单模板 |
| 采样区 | 16 个采样点,每个点支持左/中/右株数统计 |
| 离线暂存 | localStorage 暂存,断网不丢数据 |
1.3 设计原则
- 单文件 HTML:无依赖,便于分发部署
- 离线优先:核心功能断网可用,云端能力降级而非崩溃
- 零安装:手机浏览器扫码即用,无需下载 App
- 低门槛:面向农户,UI 极简,语音/拍照为主输入方式
2. 技术栈
2.1 前端
| 技术 | 用途 | 说明 |
|---|---|---|
| 原生 HTML + CSS + JS | 主体实现 | 单文件 4000+ 行,无任何框架依赖 |
| CSS Variables | 主题系统 | 主色调 #007979,圆角/阴影/间距统一 |
| Flexbox + Grid | 布局 | 卡片式布局,移动端优先 |
| Web Speech API | 实时语音识别 | SpeechRecognition 中文模式,interimResults |
| MediaRecorder | 音频录制 | 录制 m4a/webm 上传到云端 ASR 做精确转写 |
| localStorage | 数据暂存 | 表单数据、API 配置、采样区数据 |
| File API | 拍照上传 | <input type="file" capture="environment"> |
2.2 后端(Python)
| 技术 | 用途 | 说明 |
|---|---|---|
| Python 3.x | 运行环境 | 标准库为主 |
http.server.ThreadingHTTPServer |
HTTP 服务 | 多线程解决 ASR 阻塞页面加载 |
ssl |
HTTPS 支持 | 自签证书,手机端需信任 |
gzip |
响应压缩 | HTML 121KB → 31KB,节省 73.6% |
urllib.request |
外部 API 代理 | 代理到 PaddleOCR / 小米 ASR / 小米 LLM |
base64 |
音频编码 | 音频转 base64 发到小米 ASR |
imageio_ffmpeg |
音频格式转换 | m4a/webm → wav |
2.3 外部服务
| 服务 | 用途 | 部署方式 |
|---|---|---|
| PaddleOCR | 图片文字识别 | 本地部署,端口 8090 |
| 小米 Mimo ASR | 精确语音转写 | 云端 API |
| 小米 Mimo LLM | 语义解析兜底 | 云端 API |
3. 系统架构
3.1 整体架构图

3.2 请求时序
拍照识别流程
手机浏览器 PC Server PaddleOCR
│ │ │
│ 1.拍照得到图片 │ │
│───────POST /ocr───────>│ │
│ │ 2.转发图片 │
│ │──────POST /ocr───────>│
│ │ │
│ │ 3.返回识别文本 │
│ │<─────JSON resp────────│
│ 4.返回识别结果 │ │
│<──────JSON resp────────│ │
│ │ │
│ 5.parseOCRText() 解析 │ │
│ 6.fillField() 填表 │ │
│ │ │
语音录入流程(传统)

语音录入流程(本项目)

传统方案 vs 异步处理方案对比
传统方案工作流:
说话(30秒) → 录完 → 上传音频(2秒) → ASR处理(3秒) → 返回文本 → 才开始解析填表
↑ 用户在这 5 秒里只能干等 ↑
我们的工作流:
说话 → 0.5秒后第一个interim文本出现 → 立即预填表单 → 继续说 → final段立即确认填入 → ...
↑ 全程无等待 ↑
① 架构模式:串行流水线 vs 双通道并行
| 维度 | 传统 ASR(图9) | 我们的方案(图8) |
|---|---|---|
| 模块关系 | 5 模块串行依赖,前一步必须完成才能下一步 | 两条通道并行独立,互不等待 |
| 数据流向 | 音频 → 特征 → 声学模型 → 语言模型 → 解码(单向链) | 实时通道+录音通道同时跑,最后汇合 |
| 关键瓶颈 | 任一模块慢,全链阻塞 | 一条通道慢不影响另一条 |
② 处理时机:事后批处理 vs 实时增量
| 维度 | 传统 ASR | 我们的方案 |
|---|---|---|
| 何时出结果 | 录完整段音频才开始处理 | 边说边出结果,1-3秒一个片段 |
| 用户感知 | 说完→等几秒→看到全部文本 | 边说边填表单,所见即所得(类似流式输出) |
| 延迟构成 | 整段音频传输+特征提取+模型推理+解码 | 单片段推送即解析,零等待累积 |
③ 精度策略:单一精度 vs 双模式覆盖(最关键区别)
| 阶段 | 传统 ASR | 我们的方案 |
|---|---|---|
| 实时阶段 | 无 | Web Speech API(精度~85%),realtime 模式填入 |
| 最终阶段 | 云端ASR一次性出结果 | 小米ASR(精度~95%),confirmed 模式覆盖 |
| 用户可干预 | 否,必须等最终结果 | 是,实时阶段用户可手动改,不会被覆盖 |
confirmed 覆盖的精妙之处:
javascript
// 实时阶段:只填未填字段,尊重用户手动修改
fillField(k, v, 'realtime')
// 最终阶段:云端精度更高,强制覆盖
fillField(k, v, 'confirmed')
传统方案如果 ASR 错了,用户只能等结果出来再改;我们方案是实时阶段用户就能改,云端结果只覆盖用户没动过的字段。
④ 防抖策略:无防抖 vs 差异化防抖
| 维度 | 传统 ASR | 我们的方案 |
|---|---|---|
| 防抖机制 | 不需要(整段处理) | 差异化防抖 |
| final 段 | 不适用 | 0ms 立即解析(语义完整,不浪费) |
| interim 段 | 不适用 | 80ms 防抖(不稳定,避免抖动) |
| 上下文窗 | 整段音频 | 80字符(final)/ 40字符(interim) |
传统方案不需要防抖,因为它等整段说完才处理,没有"中间不稳定结果"的问题,但也因此失去了实时性。我们需要差异化防抖是因为实时通道会推送不稳定的 interim 结果,80ms 防抖保证连续推送只解析最后一次。
⑤ 解析策略:声学语言融合 vs 三层漏斗
| 维度 | 传统 ASR | 我们的方案 |
|---|---|---|
| 文本后处理 | 语言模型约束输出 | 三层漏斗解析 |
| 第一层 | - | 精确正则(快,命中已知模式) |
| 第二层 | - | 同音字+拼音模糊(解决农户口音) |
| 第三层 | 语言模型 | LLM 语义兜底(理解口语表达) |
| 字段映射 | 需额外开发 | 内置字段匹配引擎+子字段关联 |
传统方案的"语言模型"是用来约束ASR输出 的,在解码阶段就融合了;我们的"LLM兜底"是在ASR输出文本之后做语义解析,目的是把口语化文本映射到表单字段。两者解决的不是同一个问题。
总结
传统 ASR 是"录音→处理→出文本"的串行流水线,精度高但不实时;我们用"双通道并行+差异化防抖+confirmed覆盖"打破了串行依赖,既保留了实时体验,又用云端ASR保证了最终精度------这是浏览器端能做到的最优解。
核心创新点有三个:双通道并行、差异化防抖、confirmed覆盖。这三个机制配合,让浏览器端 H5 也能做到原生App级的语音录入体验。
4. 目录结构
6a599561381b931b665b5e69/
├── index.html # 主应用(单文件,4000+ 行)
├── server.py # Python HTTPS 服务端
├── cert.pem # SSL 自签证书
├── key.pem # SSL 私钥
├── e2e_m4a.py # 端到端测试脚本(6 条 m4a 音频)
├── test_*.py / perf_*.py # 其他测试脚本(可选)
└── .trae/documents/ # 文档目录
├── PRD.md
└── Technical-Architecture.md
4.1 index.html 内部结构
index.html (约 4000 行)
├── <style> # CSS 样式(约 800 行)
│ ├── :root 变量定义 # 主题色、圆角、阴影、间距
│ ├── 全局样式 # body, button, input
│ ├── 布局组件 # .screen, .navbar, .content
│ ├── 卡片样式 # .form-card-section, .form-row
│ ├── 表单字段 # .form-field, .input-wrap, .option-pills
│ ├── 语音 Modal # .voice-overlay, .voice-sheet, .wave-wrap
│ ├── 两列布局 # .two-col-row, .two-col-field(第四次报告)
│ ├── 子字段 # .subs-row, .suffix-row
│ └── 动画 # @keyframes slideDown, highlightFlash
│
├── <body> # HTML 结构
│ ├── #app # 应用根容器(max-width: 480px)
│ ├── #screen-home # 首页(田小勤)
│ ├── #screen-crops # 作物选择
│ ├── #screen-inspections # 巡检次数选择
│ ├── #screen-form # 表单页(核心)
│ ├── #screen-settings # 系统设置
│ ├── #voice-overlay # 语音录入 Modal
│ ├── #toast # 提示条
│ └── #processing # 加载遮罩
│
└── <script> # JS 逻辑(约 2800 行)
├── 常量定义 # CROPS, INSPECTIONS
├── STATE 全局状态 # 当前作物、巡检、表单数据
├── HOMOPHONE_TABLE # 同音字映射表
├── FIELD_KEYWORDS # 字段→关键词映射
├── getTemplate() # 8 种表单模板
├── 渲染函数 # renderForm, renderField, renderFieldWithSubs, renderTwoColFields
├── 导航函数 # navTo, navBack
├── 拍照 OCR # triggerCamera, parseOCRText
├── 语音录入 # openVoiceModal, startRecording, endRecording
├── 实时转写 # onRealtimeTranscript, realtimeParseAndFill
├── 解析引擎 # parseTranscriptLocal, parseWithHomophone
├── 字段填充 # fillField, fillMultiple, selectOption
├── 采样区 # renderSampling, fillSamplingZone
├── 表单提交 # collectFormData, saveForm, submitForm
└── 工具函数 # toast, normalizeText, preprocessSpokenText
4.2 server.py 内部结构
server.py (约 500 行)
├── 配置常量 # PADDLEOCR_URL, XIAOMI_ASR_KEY 等
├── LLM_PARSE_PROMPT # LLM 解析提示词模板
├── Handler(http.server.SimpleHTTPRequestHandler)
│ ├── end_headers() # CORS 头
│ ├── do_GET() # 静态文件服务 + gzip + 缓存
│ ├── do_POST() # API 路由
│ ├── handle_ocr() # 代理到 PaddleOCR
│ ├── handle_asr() # 代理到 Paraformer(可选)
│ ├── handle_xiaomi_asr() # 代理到小米 ASR(含音频格式转换)
│ └── handle_llm_parse() # 代理到小米 LLM
└── main() # 启动 ThreadingHTTPServer + SSL
5. 前端实现
5.1 页面结构
应用采用多屏幕单页 架构,5 个屏幕通过 navTo/navBack 切换:
home(首页) → crops(作物) → inspections(巡检) → form(表单)
↓
settings(设置)
每个屏幕是一个 <div class="screen">,通过 position: absolute + transform: translateX 实现滑动切换动画。
5.2 表单模板系统
getTemplate(cropId, inspectionId) 返回 8 种表单模板,每个模板包含:
javascript
{
title: '第X次田间作物生长报告(玉米)',
sampling: { // 采样区配置(可选)
title: '采样区数据(5米株数)',
columns: [{key, label}, ...],
zones: 16
},
layout: 'two-col', // 可选:两列布局(第四次报告)
fields: [
{
key: 'plot_name',
label: '地块名称',
type: 'text' | 'number' | 'option' | 'date' | 'ridge_range',
unit: '亩', // 可选:单位
required: true, // 可选:是否必填
options: [...], // type=option 时
span: 2, // 可选:占整行(两列布局)
subs: [ // 可选:子字段(如问题面积/备注)
{key, label, type, unit}
],
suffix: { // 可选:后缀字段(同行)
key, label, type, unit
}
},
...
]
}
5.3 字段渲染
| 渲染函数 | 应用场景 |
|---|---|
renderField(f) |
标准字段(label + input/option-pills) |
renderRidgeRange(f) |
大垄双行(起 + 至 两个 input) |
renderFieldWithSuffix(f, suffix) |
主字段 + 后缀字段同行 |
renderFieldWithSubs(f) |
主字段 + 子字段(问题面积/备注) |
renderTwoColFieldBlock(f) |
两列布局单个字段块 |
renderTwoColFields(fields) |
两列布局字段组(第四次报告) |
5.4 语音 Modal 设计
采用底部卡片模式,不遮挡表单区域:
┌─────────────────────────┐
│ │ ← 表单区域(可滑动)
│ [表单字段...] │
│ [表单字段...] │
│ │
├─────────────────────────┤
│ 00:01:23 │ ← 计时器
│ 录音中可滑动上方表单 │ ← 提示
│ [转写结果实时显示] │ ← transcript-area
│ │
│ ●●●●●●● │ ← 波形动画
│ │
│ 🎤 │ ← 录音按钮(点击切换)
└─────────────────────────┘
关键 CSS:
voice-overlay:position: fixed; bottom: 0,不覆盖全屏voice-sheet:pointer-events: none,默认不拦截触摸voice-overlay.show .voice-sheet:pointer-events: auto,仅卡片本身拦截
录音按钮:点击切换模式(点一下开始,再点一下结束),释放双手可滑动表单。
5.5 实时语音录入链路
MediaRecorder/Web Speech API
│
▼
onRealtimeTranscript(finalText, interimText)
│
├─ final 到达 → 立即解析(零防抖)
│ ├─ 取最近 80 字符累积 final 文本
│ └─ realtimeParseAndFill(text, 'final-increment')
│
└─ interim 到达 → 80ms 防抖
├─ 取最近 40 字符 final + interim
└─ realtimeParseAndFill(text, 'interim-preview')
│
▼
parseTranscriptLocal(text, template)
│
▼
fillField(key, value, 'realtime')
(input-wrap 添加 .realtime-filled 类,绿色高亮动画)
5.6 状态管理
全局 STATE 对象:
javascript
const STATE = {
currentScreen: 'home',
screenHistory: [],
selectedCrop: null,
selectedInspection: null,
currentTemplate: null,
formData: {}, // 表单数据
samplingZones: [], // 采样区数据
voice: {
recording: false,
recognition: null, // Web Speech API 实例
mediaRecorder: null, // MediaRecorder 实例
audioChunks: [],
finalTranscript: '', // 累积 final 文本
transcriptLog: [],
realtimeDebounce: 80, // 防抖时间
_parseTimer: null,
}
};
6. 后端实现
6.1 服务启动
python
class Handler(http.server.SimpleHTTPRequestHandler):
# 自定义 GET/POST 处理
...
server = http.server.ThreadingHTTPServer(('0.0.0.0', 8080), Handler)
server.socket = ssl.wrap_socket(server.socket, certfile='cert.pem', keyfile='key.pem', ssl_version=ssl.PROTOCOL_TLS)
server.serve_forever()
关键优化:
ThreadingHTTPServer替代HTTPServer:每个请求独立线程,ASR 长耗时(1-2s)不阻塞页面加载- 自签 SSL:手机端 Web Speech API 要求 HTTPS 环境
6.2 API 端点
| 路由 | 方法 | 功能 | 后端服务 |
|---|---|---|---|
/ |
GET | 静态文件服务 | 本地文件 |
/ocr |
POST | OCR 识别 | PaddleOCR (本地:8090) |
/asr |
POST | 语音转写(可选) | Paraformer (本地:8000) |
/xiaomi-asr |
POST | 精确语音转写 | 小米 Mimo ASR (云端) |
/llm-parse |
POST | LLM 语义解析 | 小米 Mimo LLM (云端) |
6.3 音频格式转换
小米 ASR 只接受 wav/mp3,但 MediaRecorder 录制的是 webm/m4a。handle_xiaomi_asr 中通过 magic number 自动检测格式,用 imageio_ffmpeg 转换:
python
if audio_data[:4] == b'\x1aE\xdf\xa3': # webm
needs_convert = True
elif audio_data[4:8] == b'ftyp': # m4a/mp4
needs_convert = True
if needs_convert:
ffmpeg_exe = imageio_ffmpeg.get_ffmpeg_exe()
subprocess.run([ffmpeg_exe, '-y', '-i', tmp_in,
'-ar', '16000', '-ac', '1', '-f', 'wav', tmp_out])
6.4 响应优化
- gzip 压缩:仅对 >1KB 的文本类资源(.html/.js/.css)压缩,compresslevel=6
- Cache-Control :
no-cache, must-revalidate(开发期)或public, max-age=3600(生产期) - CORS :
Access-Control-Allow-Origin: *
6.5 LLM 解析提示词
handle_llm_parse 中使用 LLM_PARSE_PROMPT 模板,传入:
- 字段定义
{fields_desc}:JSON 格式的字段 key/label/options - 转写文本
{transcript}:ASR 返回的文本
LLM 返回纯 JSON,前端直接 JSON.parse 后 fillMultiple。
7. 数据流转
7.1 表单数据生命周期
用户操作 数据位置 说明
─────────────────────────────────────────────────────────
点击字段输入 STATE.formData 内存中
(JS 对象)
─────────────────────────────────────────────────────────
点击提交 localStorage 持久化到浏览器
key: form_{crop}_{insp}_{timestamp}
─────────────────────────────────────────────────────────
下次打开 localStorage 读取历史记录
(可手动清空)
7.2 表单数据结构
javascript
// localStorage 中的存储格式
{
"templateId": "corn_2",
"createdAt": "2026-07-20T15:30:00.000Z",
"data": {
"plot_name": "横道村",
"area_mu": "200",
"pest_disease": "有",
"pest_disease_area": "5",
"pest_disease_remarks": "轻度发生",
"weeding_status": "良",
"weeding_status_area": "3",
"weeding_status_remarks": "田间杂草较多",
...
},
"sampling": [
{zone:1, left_count:"25", right_count:"23"},
{zone:2, left_count:"24", right_count:"26"},
...16个采样点
],
"source": "manual" | "voice" | "ocr"
}
7.3 API 配置存储
javascript
// localStorage
{
"ocr_url": "http://127.0.0.1:8090/ocr",
"asr_key": "tp-xxxx",
"llm_key": "tp-yyyy"
}
8. 核心算法
8.1 解析引擎三层漏斗

8.2 同音字模糊匹配 parseWithHomophone
核心思路:找关键词 → 定位位置 → 提取值

8.3 选项匹配 matchRadioOption
支持多层匹配:
- 完全匹配 :
text === option - 包含匹配 :
text.includes(option) - 同音字匹配 :
HOMOPHONE_TABLE[option]变体匹配 - 分数归一化 :
三分之一 → 1/3、二分之一 → 1/2 - 特殊兜底:肥料效果(未脱肥/脱肥)、成熟度(乳线1/3/1/2以上/完熟)
8.4 口语化预处理 preprocessSpokenText
7 步预处理,大幅提升正则命中率:
- 语气词清理:
那个/这个/然后/嗯/啊/哦/呃 - "多少亩200" → "200亩"
- 数字+单位间多余字符清理:
200 多亩→200多亩 - 连接词替换:
面积是200→面积:200 - "X 来亩" → "X亩"
- "X 亩地/X 斤重" → "X亩/X斤"
- "病虫害没有" → "病虫害无"
8.5 中文数字转阿拉伯 chineseNumToArabic
支持 零一二三四五六七八九十百千万亿两,逐位转换后数学计算:
"十八" → 1*10 + 8 = 18
"二百五" → 2*100 + 50 = 250
"一千二" → 1*1000 + 200 = 1200
8.6 日期解析
支持多种日期格式:
2026-07-20/2026/7/20/2026.7.202026年7月20日二六年五月二十六日(中文年份逐字转数字)5月26日(自动补当前年份)
9. 关键模块详解
9.1 采样区系统
第 1/3 次巡检包含采样区,16 个采样点,每个点支持:
- 玉米:5米株数(左/右)
- 大豆:1米株数(左/中/右)
javascript
STATE.samplingZones = [
{zone:1, left_count:"25", right_count:"23"},
{zone:2, left_count:"24", right_count:"26"},
...16个
];
支持语音录入:"第一采样点左25右23" → 自动填入 zone0。
9.2 大垄双行解析
特殊字段 large_ridge_range,渲染为「起 input 至 input」两个输入框。
解析逻辑:
javascript
// "大垄双行0到60" → start=0, end=60
let m = valueText.match(/(\d+)\s*[-到至~]\s*(\d+)/);
// 中文数字:"大垄双行零到六十"
if (!m) {
m = valueText.match(/([零一二三四五六七八九十百千]+)\s*到\s*([零一二三四五六七八九十百千]+)/);
if (m) m = [m[0], chineseNumToArabic(m[1]), chineseNumToArabic(m[2])];
}
if (m) {
result.large_ridge_start = m[1];
result.large_ridge_end = m[2];
}
9.3 子字段上下文关联匹配
第 2 次报告 5 个主字段(病虫害/除草情况/苗情长势/追肥效果/自然灾害)各有子字段(问题面积/备注)。
关键 bug 修复:用户语音只说「问题面积5亩」,不会带主字段前缀。通过上下文关联:主字段已匹配后,在其 valueText 里用通用正则找子字段。
9.4 两列布局
第 4 次报告采用两列布局,通过 layout: 'two-col' 标记:
span: 2的字段占整行(地块名称、地块面积、成熟度、产量评估、调查人、调查日期)- 其他字段两两拼成一行(灾害名称+灾害面积、病害名称+病害面积、虫害名称+虫害面积)
9.5 实时语音防抖策略
| 文本类型 | 防抖时间 | 解析范围 | 目的 |
|---|---|---|---|
| final(已确认) | 0ms(立即) | 最近 80 字符累积 | 跨片段匹配被 ASR 拆分的语句 |
| interim(临时) | 80ms | 最近 40 字符 final+interim | 实时预览,避免频繁重解析 |
9.6 字段高亮动画
填入字段时通过 CSS 类实现高亮:
| 类名 | 触发场景 | 动画 |
|---|---|---|
.highlight |
示例数据填充 | @keyframes highlightFlash 1.5s |
.realtime-filled |
实时语音填入 | @keyframes realtimeFlash 2.5s 绿色闪烁 |
.confirmed |
精确转写确认 | 绿色背景持久化 |
:focus-within |
用户手动聚焦 | 绿色边框 |
10. 部署运行
10.1 环境要求
- PC 端:Windows/Mac/Linux + Python 3.8+
- 手机端:支持 Web Speech API 的浏览器(Chrome/Safari/Edge)
- 网络:手机与 PC 同一局域网
10.2 启动步骤
bash
# 1. 启动 PaddleOCR(本地:8090)
cd paddleocr && python ocr_server.py
# 2. 启动主服务(HTTPS:8080)
cd 6a599561381b931b665b5e69
python server.py
# 3. 手机访问
# https://<PC_IP>:8080/index.html
# 需要信任自签证书
10.3 自签证书生成
bash
openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj "/CN=localhost"
10.4 手机端配置
- 访问
https://<PC_IP>:8080/index.html - 浏览器提示证书不安全 → 高级 → 继续访问
- 首次使用 Web Speech API 需授权麦克风
- 设置页配置 OCR/ASR/LLM API Key(已内置默认值)
11. 已知限制与未来规划
11.1 已知限制
| 限制 | 影响 | 解决方案 |
|---|---|---|
| Web Speech API 中文 interim 慢 | 实时填表延迟 1-3s | 接入本地流式 ASR(FunASR) |
| localStorage 容量 5-10MB | 大量数据会撑爆 | 接入后端数据库 |
| 无用户登录系统 | 多人共用无隔离 | 加登录页 + 后端用户系统 |
| 无云端同步 | 换设备数据丢失 | 加 /api/submit 上报 |
| 自签证书需手动信任 | 用户体验差 | 申请正式证书或部署到 HTTPS 域名 |
11.2 未来规划

附录
A. 字段关键词映射表(FIELD_KEYWORDS)
每个字段配置 5-9 个关键词变体,覆盖:
- 标准名称:
地块名称 - 简称:
地块、名称 - 口语化:
地块儿、地块叫、地块是 - 带疑问:
几亩、多少亩
B. 同音字映射表(HOMOPHONE_TABLE)
针对 ASR 常见误识别:
垄 → 龙/拢/笼肥 → 非/费熟 → 孰/淑面积 → 面鸡/面集/面及
C. 选项值同音字
针对单选/选项字段:
未脱肥 → 未脱非/未脱费/未拖肥/没脱肥乳线1/3以上 → 乳线三分之一以上/乳线三之一以上完熟 → 完孰/晚熟/挽熟/完淑
D. 性能指标
- HTML 文件大小:121KB(gzip 后 31KB,节省 73.6%)
- 页面加载时间:< 500ms(局域网)
- PaddleOCR 识别:1-2s/张
- 小米 ASR 转写:平均 1669ms/条
- Web Speech API interim 延迟:1-3s(中文模式)
E. 测试覆盖
e2e_m4a.py:6 条 m4a 音频端到端测试,准确率 100%- 覆盖场景:玉米第 1-4 次巡检、大豆第 1 次巡检
- 测试字段:地块名称、面积、密度、产量、大垄双行、采样区株数等
文档版本 :T2(2026-07-20)
作者 :阿海
最后更新:2026-07-20