检信ALLEMOTION 认知评估分析器 · WebSocket 推送数据文档
分片流式实时方案 · 分析器侧协议规范
版本 V1.1 · 2026-09-16 · 检信认知矫正系统
一、概述
本次升级将分析器从「录完→整段分析→约等一分钟→返回结果」改造为「边录边析」分片流式模式:系统端(检测页)使用 getUserMedia 边录边切,每积满一个分片(默认约 5 秒,粒度可配)主动 POST 给分析器;分析器按 sessionId 归组排序,逐片分析并实时推送即时指标(progress 帧),收到末片(isLast=1)后做全局整合,经同一条 WebSocket 通道推送完整权威数据(final 帧),由系统端转发 PHP 落库。
核心边界:分片发送方是系统端,分析器是被动接收方(绝不主动拉取);最终数据只走 WebSocket 推送,不新增 HTTP 回调;落库职责由系统 PHP 承担。
|--------|--------------|--------------------------------------------|
| 端口 | 协议 | 用途 |
| 8000 | HTTP (MJPEG) | 摄像头视频流/单帧/健康检查(原有,不变) |
| 8893 | WebSocket | key@value 指令协议(原有)+ 新增 JSON 订阅与推送通道(本文档主题) |
| 8894 | HTTP (REST) | 报告输出 API(原有,不变) |
| 8895 | HTTP (REST) | 分片接收管理服务(新增):POST /analyzer/chunk |
二、会话标识与分片元数据
每次检测开始时由系统端生成唯一 sessionId(复用 course_no / rst)。所有分片、WS 实时帧、WS 终稿都携带它,用于归属、组合与并发隔离。多视频并发 = 不同 sessionId,天然隔离互不干扰。
|-----------|----------------|-------------------|
| 字段 | 类型 | 含义 |
| sessionId | string(1..128) | 归属:同一视频的所有分片相同 |
| seq | int (从0递增) | 片序号:组合顺序 |
| offset_ms | int (≥0) | 该片起始时间偏移(毫秒):时序定位 |
| isLast | 0 / 1 | 是否末片:全局整合触发信号 |
双指标约定:progress 帧携带的片级即时指标仅用于页面实时展示(反应快、较粗),落库时不采纳;final 帧携带的整段最终指标是唯一落库/报告的权威结果。
三、分片接收 HTTP 接口(8895)
3.1 POST /analyzer/chunk
multipart/form-data 上传单个视频分片(mp4 / webm,FFMPEG 解码)。
|-----------|----------------|--------|------------------|
| 字段 | 类型 | 必填 | 说明 |
| sessionId | text | 是 | 会话唯一标识,1..128 字符 |
| seq | text(整数) | 是 | 片序号,从 0 递增 |
| offset_ms | text(整数≥0) | 是 | 片起始偏移毫秒 |
| isLast | text(0/1) | 是 | 末片标记 |
| file | file(mp4/webm) | 是 | 分片视频二进制 |
成功响应(HTTP 200):
{"ok": true, "sessionId": "course_001", "seq": 2, "is_last": false,
"received_at_ms": 1720000000000}
错误响应(HTTP 4xx,JSON):
|---------|------------------------------------------------------|
| 状态码 | 场景 |
| 400 | 缺少字段 / sessionId 非法 / seq 非法 / isLast 非法 / 无 file 字段 |
| 409 | seq 重复(同会话内该 seq 已存在) |
| 413 | 分片超过 MaxChunkBytes 上限 |
| 429 | 并发会话数超过 MaxConcurrentSessions(新 sessionId 首片时判定) |
| 500 | 落盘 / 内部异常 |
3.2 GET /analyzer/health
{"ok": true, "sessions": 2, "collecting": 1, "finalized": 1, "workers_busy": 1}
会话状态机:collecting(收片中)→ finalizing(isLast 已到,整合中)→ finalized(final 已推,保留 FinalRetentionSec)→ 过期清理;aborted(超时/异常,推 error 帧后清理)。乱序到达容忍:worker 按到达顺序逐片分析推 progress,整合时按 seq 顺序合并。SessionTimeoutSec 内无新片则 abort 会话。
四、WebSocket 推送协议(8893)
原有 key@value 指令协议(get_ip / AC_ME / MODE@ / TM@ / RST@ 等)完全不变;新增 JSON 通道与其共存:消息文本以「{」开头判定为 JSON 帧,否则按既有 key@value 解析。
JSON 帧用 type 区分;所有 progress / final 帧必须带 sessionId。采用订阅制:检测页订阅自己 sessionId 的通道,只消费自己的帧;未订阅的客户端收不到任何分片流数据。客户端断开时自动退订其全部订阅。
4.1 订阅 / 退订(客户端→分析器)
{"cmd": "subscribe", "sessionId": "course_001"}
→ {"cmd": "sub_ok", "sessionId": "course_001"}
→ {"cmd": "sub_err", "sessionId": "course_001", "error": "sessionId 非法"}
{"cmd": "unsubscribe", "sessionId": "course_001"}
→ {"cmd": "unsub_ok", "sessionId": "course_001"}
订阅即重放:若该会话已 final 化且仍在保留期内(FinalRetentionSec),订阅成功后立即补发一次 final 帧------供 PHP 侧 WS 兜底客户端在检测页中途关闭时补收结果。
4.2 progress 帧(分析器→客户端,片级即时指标)
每片分析完成即推送。用途:检测页实时刷新展示,不落库。
|------------|--------|------------------------------------------------|
| 字段 | 类型 | 说明 |
| type | string | 固定 "progress" |
| sessionId | string | 会话标识 |
| seq | int | 本片序号 |
| offset_ms | int | 本片起始偏移毫秒 |
| is_last | bool | 本片是否末片 |
| ts_ms | int | 分析完成时间戳(epoch 毫秒) |
| chunk | object | 片级即时指标(见表后说明) |
| cumulative | object | 会话级累计(frame_count / chunk_count / duration_ms) |
chunk 子对象字段:frame_count(本片帧数)、duration_ms(本片时长)、emotions(12 维情绪均值:aggression/stress/tension/suspicion/balance/confidence/energy/self_regulation/inhibition/neuroticism/depression/happiness)、composite_mean、risk_level、risk_desc、vitals(heart_rate_bpm/hrv_rmssd/respiratory_rate_brpm/spo2_estimate/ppg_signal_quality/bp_sbp_trend/bp_dbp_trend)、happiness_index(HI/PER/SF_score/ERT_score/ert_recovered/ert_recovery_seconds/frustration_events)、signal_energy(可跨片累加能量类中间量)、fallback(bool,本片是否回退数据)。
{
"type": "progress",
"sessionId": "course_001",
"seq": 2,
"offset_ms": 10000,
"is_last": false,
"ts_ms": 1720000000000,
"chunk": {
"frame_count": 125,
"duration_ms": 5000,
"emotions": {
"aggression": 48.2,
"stress": 52.1,
"tension": 50.3,
"suspicion": 49.8,
"balance": 51.0,
"confidence": 50.6,
"energy": 49.1,
"self_regulation": 52.4,
"inhibition": 48.9,
"neuroticism": 50.2,
"depression": 49.5,
"happiness": 51.7
},
"composite_mean": 51.2,
"risk_level": "一般",
"risk_desc": "情绪状态平稳",
"vitals": {
"heart_rate_bpm": 72.1,
"hrv_rmssd": 35.4,
"respiratory_rate_brpm": 15.2,
"spo2_estimate": 97.3,
"ppg_signal_quality": 50.0,
"bp_sbp_trend": 0.0,
"bp_dbp_trend": 0.0
},
"happiness_index": {
"HI": 55.0,
"PER": 52.0,
"SF_score": 55.0,
"ERT_score": 60.0,
"ert_recovered": true,
"ert_recovery_seconds": 0.0,
"frustration_events": 0
},
"signal_energy": {
"high_freq_energy_sum": 123.4,
"fullband_total_energy_sum": 4567.8
},
"fallback": false
},
"cumulative": {
"frame_count": 375,
"chunk_count": 3,
"duration_ms": 15000
}
}
4.3 final 帧(分析器→客户端,整段权威数据)
末片整合完成后推送。唯一落库/报告权威结果;系统端收到后转发本系统 PHP 落库接口(绑 course_id)写库并存 report。
|--------------|--------|--------------------------|
| 字段 | 类型 | 说明 |
| type | string | 固定 "final" |
| sessionId | string | 会话标识 |
| seq_total | int | 总片数 |
| chunk_count | int | 已收片数 |
| duration_ms | int | 整段时长毫秒 |
| frame_count | int | 整段分析帧数 |
| ts_ms | int | 时间戳(epoch 毫秒) |
| rst | object | 完整三包数据(落库主体,见表后说明) |
| accumulators | object | 可跨片累加中间量汇总(信号能量累加/帧间统计量) |
rst 子对象字段:report_filename(章程规范报告文件名,含人群前缀 elderly_/child_/adult_)、rst_packet(RST@ 综合报告包原文,37 字段)、emo12_packet(EMO12@ 十二维 60 统计值原文)、fh256_packet(FH256@ 频谱 256 频点原文)、state_packet(STATE@ 状态识别,无数据为 null)、elderly_packet(ELDERLY@ 老年认知 9 维剖面,无数据为 null)、population_mode(adult/elderly/child)、meta(age/gender/detect_date/duration_sec/frame_count)。三个 *_packet 为章程三包原文,可直接按现有落库逻辑入库。
FH256 曲线说明(V1.1 更新):fh256_packet 的 256 个值采用收缩混合公式 density = α·KDE(实测分数, 带宽下限1.0) + (1−α)·N(μ实测, σ先验=8),α 缺省 0.70;全部数值由本场检测实测分数推导,确定性可复算(非 AI 生成)。曲线首/尾各约 10 个 bin(0~4 分与 96~100 分)为真实零值,中部约 177 个 bin 有值。参数经 Config.ini Output FH256Alpha / FH256PriorSigma / FH256BandwidthFloor 可配,α=1.0 即回退旧版纯 KDE 算法。
{
"type": "final",
"sessionId": "course_001",
"seq_total": 5,
"chunk_count": 5,
"duration_ms": 25000,
"frame_count": 750,
"ts_ms": 1720000000000,
"rst": {
"report_filename": "elderly_20260915143000123__70__未_0123456789.adf",
"rst_packet": "RST@elderly_...adf,allV@78.5,...(37字段)",
"emo12_packet": "EMO12@50.12|1.23|...(60值)",
"fh256_packet": "FH256@0.003901|...(256值)",
"state_packet": null,
"elderly_packet": "ELDERLY@{...9维剖面}",
"population_mode": "elderly",
"meta": {
"age": 70,
"gender": "未",
"detect_date": "2026-09-15",
"duration_sec": 25.0,
"frame_count": 750
}
},
"accumulators": {
"emotions_sum": {
"aggression": 36150.0,
"stress": 39075.0
},
"emotions_count": 750,
"signal_energy_sum": {
"high_freq_energy": 9250.0,
"fullband_total_energy": 342585.0
},
"composite_sum": 38415.0,
"vitals_sum": {
"heart_rate_bpm": 54075.0
}
}
}
4.4 error 帧(分析器→客户端)
{"type": "error", "sessionId": "course_001", "error": "session timeout",
"ts_ms": 1720000000000}
触发场景:会话超时 abort、分片解码失败等。仅推送给该会话订阅者。
五、端到端时序
检测页(系统端) 分析器(被动接收) PHP(系统端)
1 生成 sessionId(复用 course_no)
2 WS 订阅 {"cmd":"subscribe"} ──────► 注册订阅
3 边录边切(默认~5s/片) 每片:
POST /analyzer/chunk ──────────────► 管理服务按 sessionId 归组
(sessionId+seq+offset_ms+isLast) → 该会话 worker 片级分析
4 ◄── WS progress 帧(带 sessionId) ──── 实时刷新展示
5 末片发出(isLast=1) ─────────────────► worker 全局整合(合并可跨片累加中间量)
6 ◄── WS final 帧(sessionId+rst) ───── 收到 final
7 ── 转发本系统 PHP 落库接口 ──► 写库+存 report
防御选项:若担心检测页中途关闭导致 WS 断开丢失 final,可在 PHP 侧备一个 WS 客户端作兜底接收落库(利用 4.1 的「订阅即重放」机制补收 final);常规路径走检测页转发即可。
六、系统端对接指南(检测页)
// 1) 开始检测: 生成 sessionId 并订阅
const sessionId = courseNo; // 复用 course_no/rst
const ws = new WebSocket('ws://<host>:8893');
ws.send(JSON.stringify({cmd:'subscribe', sessionId}));
// 2) 边录边切: MediaRecorder 按 ~5s 切片, 每片主动 push
let seq = 0, startTs = Date.now();
const rec = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp8'});
rec.ondataavailable = async e => {
if (!e.data || !e.data.size) return;
const fd = new FormData();
fd.append('sessionId', sessionId);
fd.append('seq', String(seq));
fd.append('offset_ms', String(Date.now() - startTs));
fd.append('isLast', String(rec.state === 'inactive' ? 1 : 0));
fd.append('file', e.data, 'chunk' + seq + '.webm');
await fetch('http://<host>:8895/analyzer/chunk', {method:'POST', body:fd});
seq++;
};
rec.start(5000); // 每 5s 出片 (ts 与时长可配置)
// 3) 收帧: 只消费自己 sessionId 的帧
ws.onmessage = ev => {
const msg = JSON.parse(ev.data);
if (msg.sessionId !== sessionId) return; // 订阅制双保险
if (msg.type === 'progress') { renderRealtime(msg.chunk); }
if (msg.type === 'final') { forwardToPhp(msg); } // 转发 PHP 落库
if (msg.type === 'error') { showError(msg.error); }
};
// 4) forwardToPhp: 把 final 帧(rst 三包原文) POST 到本系统落库接口, 绑 course_id
编码提示:分片粒度不固定(默认约 5s,可配置);是否需重叠(滑窗)保证衔接待实测;WebM / MP4 均已支持(FFMPEG 解码,冒烟验证通过)。
七、分析器配置(Config.ini 新增)
|--------------------------------|---------------|------------------------------|
| 配置项 | 默认值 | 说明 |
| Base ChunkPort | 8895 | 分片接收 HTTP 端口 |
| EnableChunkStream | 1 | 启用分片流式接收(0=关闭,只跑老链路) |
| MaxChunkBytes | 20971520 | 单片大小上限(字节,默认 20MB) |
| SessionTimeoutSec | 120 | 会话无新片超时(秒),超时 abort |
| FinalRetentionSec | 300 | final 保留期(秒),期内新订阅立即重放 final |
| MaxConcurrentSessions | 4 | 最大并发会话数 |
| ChunkTempDir | ./data/chunks | 分片临时落盘目录 |
| WorkerThreads | 2 | worker CPU 分析线程池大小 |
| Output FH256Alpha | 0.70 | FH256收缩混合:实测KDE权重(1.0=旧算法) |
| Output FH256PriorSigma | 8.0 | FH256收缩混合:群体先验标准差(>0) |
| Output FH256BandwidthFloor | 1.0 | FH256收缩混合:KDE带宽下限(>0,旧值0.5) |
八、兼容性与回滚
兼容:老链路(摄像头整段检测、key@value 指令、TM@ 实时推送、RST@/EMO12@/FH256@ 三包广播、报告 REST API)行为完全不变;JSON 通道为纯增量,检测页未订阅时不受任何影响。
回滚:原版 exe 已备份(backup_streaming_pre_20260915),现役 exe 原位保留;新版 exe 以独立文件名发布,切换即回滚。
九、发布形态与生产部署(V1.1 更新)
9.1 发布形态
|----------|-----------------------------------------------------------------|-------------------------------------|
| 发布形态 | 文件 | 接口说明 |
| 明文版 | dist\VibrationAI_Elderly_Service_v3.2_streaming.exe | 本协议全文适用 |
| 加密狗封装版 | dist\protected\VibrationAI_Elderly_Service_v3.2_streaming.exe | 接口与明文版完全一致(加密只作用于程序本体,WS/HTTP 协议不变) |
两种发布形态的 WebSocket / HTTP 接口行为完全相同;客户端无需感知加密与否。
9.2 生产部署(2026-09-16 10:12 已割接上线)
|----------|-------------------------------------------------------------------------------|
| 项目 | 值 |
| 部署位置 | E:\检信ALLEMOTION开发者平台\upgrade_smoke\engine_pool\inst1(托管引擎池,看门狗每2分钟巡检自愈) |
| 引擎版本 | 3.2-streaming |
| 端口 | 8000 MJPEG / 8893 WS / 8894 报告API / 8895 分片接收 |
| exe 同步机制 | 池启动脚本按 MD5 从 D:\VibrationAI_DJ_老年版\dist\VibrationAI_Elderly_Service.exe 同步 |
| 摄像头 | DSHOW0-USB 640x480,就绪 |
| FH256 参数 | α=0.70 / σ先验=8.0 / 带宽下限=1.0 |
十、验证记录
端到端冒烟测试(3 名测试工程师,沙箱隔离 + 备用端口)结果:
|-----------|---------|--------|--------|
| 测试工程师 | 用例数 | 通过 | 失败 |
| TE1 | 5 | 5 | 0 |
| TE2 | 5 | 5 | 0 |
| TE3 | 7 | 7 | 0 |
结论:全部用例 100% 通过,主链路(分片→progress→final)、并发隔离、乱序/重复/超时/保留重放、mp4/webm 双编码、异常包防御、老协议回归均验证达标。
FH256 收缩混合专项回归(V1.1 新增):15/15 通过------非零bin 18→178、首尾零保留(各≥8 bin)、曲线积分=1.0000、确定性复算一致、α=1.0 正确退化为旧算法、回退分支不变。
生产割接后验证(V1.1 新增,2026-09-16 10:12):四端口全部监听、摄像头就绪(capture_fps≈20)、老协议 get_ip→IP@、新协议 subscribe→sub_ok / unsubscribe→unsub_ok、引擎日志确认 v3.2-streaming 与分片流式服务启动,全部通过。