模拟信号与质量事件:为什么工程工具不能只依赖真实设备调试
复盘 RespiraScope 的内置模拟传感器、预设呼吸场景和 signal_quality 事件,解释它们如何服务算法调参和异常复盘。
背景问题
真实传感器调试有一个天然问题:你很难稳定复现同一段呼吸。正常呼吸、浅呼吸、屏气、咳嗽、体动伪影、低幅噪声都可能出现,但它们不一定在你调算法时刚好出现。
如果项目只能依赖真实设备,滤波参数和峰谷识别就很难做系统验证。RespiraScope 因此内置了模拟 TCP 传感器和多种呼吸预设,让后端像连接真实设备一样连接 mock server。
设计方案对比
| 方案 | 优点 | 问题 | 项目判断 |
|---|---|---|---|
| 只接真实设备 | 最接近现场 | 不可重复,异常场景难制造 | 不利于算法调参 |
| 离线 CSV 回放 | 可重复,容易测试 | 和 TCP 实时链路有差异 | 可作为补充 |
| 内置模拟 TCP server | 复用真实采集链路,实时可控 | 模拟不等于真实生理信号 | 当前采用 |
| 复杂生理模型 | 更真实 | 实现和参数解释成本高 | 当前不需要 |
内置 mock 的关键不是“假装真实”,而是“让算法和前端可以被重复刺激”。它服务的是工程验证,不是生理建模。
当前方案
MockBreathConfig 定义了模拟信号参数:
| 参数 | 含义 |
|---|---|
scenario | 预设名称 |
bpm | 基础呼吸频率 |
amplitude | 呼吸幅度 |
baseline | 基线值 |
noise | 随机噪声强度 |
drift | 慢速基线漂移 |
irregularity | 呼吸节律波动 |
apnea_interval_sec / apnea_duration_sec | 屏气或周期性减弱 |
artifact_chance / artifact_amplitude | 突发伪影概率和幅度 |
sample_interval_ms | 采样间隔 |
BreathWaveform.value_at() 根据这些参数生成当前时刻的值。AsyncSimulateSensor 再把值编码成和真实传感器一样的 4 字节包,持续写给 TCP client。
信号质量事件
滤波和峰谷识别关注“呼吸周期”,但前端还需要知道信号是否可靠。SignalQualityAnalyzer 负责检测:
| quality | 触发含义 |
|---|---|
good | 当前窗口没有明显异常 |
low_amplitude | 波形跨度过低,可能是弱信号或屏气 |
artifact | 单点突变或相对基线突变过大 |
noisy | 相邻差分中位数过大 |
gap | sequence 出现明显断流 |
这类事件不会替代滤波,而是给前端和外部系统一个解释层。比如 BPM 变乱时,用户可以看到是否同时出现 artifact 或 noisy。
为什么这样选
模拟信号复用真实 TCP 链路,这是设计重点。如果 mock 只在前端生成一条曲线,它只能测试图表,不能测试 AsyncSocketClient、DataReceiver、SignalProcessor、DataSender 这条完整链路。现在 mock server 输出同样的 4 字节数据包,后端无需知道数据来自真实设备还是模拟器。
质量事件和模拟场景互相配合。cough_artifact、motion_artifact、weak_noisy 这类场景可以专门触发异常逻辑,用来检查滤波、峰谷识别和 UI 提示。
代价和边界
模拟信号不是医学真实数据。它使用正弦组合、漂移、噪声和伪影来构造“有用的测试刺激”,不能代表真实患者的全部呼吸形态。任何基于模拟数据调出的参数,都还需要在真实设备上验证。
质量事件也只是启发式检测。它根据幅度、突变、噪声和 gap 判断状态,不会自动区分咳嗽、体动、传感器脱落或患者真实异常。它的价值是提示“这段信号不适合无脑相信指标”。
关键函数表
| 函数或类 | 作用 | 设计点 |
|---|---|---|
MockBreathConfig | 模拟参数 dataclass | 让每个场景可配置、可序列化 |
SCENARIO_PRESETS | 预设场景表 | 覆盖正常、浅呼吸、屏气、伪影等调试场景 |
BreathWaveform.value_at() | 按时间生成模拟值 | 组合周期、漂移、噪声和伪影 |
encode_sensor_value() | 编码成 4 字节传感器包 | 复用真实 TCP 读取链路 |
AsyncSimulateSensor.start() | 启动 mock TCP server | 后端无需区分真实或模拟源 |
SignalQualityAnalyzer.handle_data() | 分析 raw 点质量 | 输出低幅、伪影、噪声和断流事件 |
analyze_signal_quality_batch() | 批量分析测试样本 | 单元测试可直接验证异常检测 |
读源码建议
先读 mock_sensor/breath_simulator.py,理解每个场景如何由参数组成。再读 mock_sensor/async_sensor.py,看模拟值如何变成 4 字节 TCP 包。最后读 signal_quality.py,重点看 artifact、low amplitude、noisy 和 gap 的触发条件。