检信ALLEMOTION 认知评估分析器 · WebSocket 推送数据文档

检信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 与分片流式服务启动,全部通过。

相关推荐
OpsEye1 小时前
公司内部全员使用 AI,如何保证会话合规、行为可审计?
javascript·ai编程
变与不变8062 小时前
JS事件机制精讲
前端·javascript
沉迷...2 小时前
Git skip-worktree 使用笔记(本地忽略配置文件)
javascript·git
志尊宝4 小时前
Vue3 零基础每日笔记(032):Suspense 与异步组件——懒加载与顶层 await 终极方案
前端·javascript·vue.js·笔记·html5
是立不是利5 小时前
前端分页逻辑与边界场景实战解析
前端·javascript
流水白开6 小时前
JavaScript的原型链、类与类的继承
前端·javascript
YHL6 小时前
🚰流式输出:把 LLM 的每个字,像水一样送到浏览器
前端·javascript
触底反弹6 小时前
🔥 从执行上下文到 React 陷阱——彻底搞懂 JavaScript 闭包
javascript·react.js
秋天的一阵风6 小时前
⚡2026 年了,十万级表格还只会「虚拟滚动」?难怪你的页面照样卡顿
前端·javascript·面试