跳转至

说明

概述

架构1使用环形缓冲区实现 Producer-Consumer 模式,用于实时传感器数据采集和处理。该架构针对中等采样频率(0.1 - 1000 Hz,推荐:100 Hz - 1 kHz)设计,提供简单、易于理解的实现模式。

架构原理

Producer-Consumer 模式

该架构使用经典的 Producer-Consumer 模式,包含以下组件:

  • Producer 任务(高优先级):以固定间隔读取传感器数据并写入环形缓冲区
  • Consumer 任务(中等优先级):定期从环形缓冲区读取并处理数据
  • 环形缓冲区:基于 PSRAM 的缓冲区,允许在满时覆盖旧数据(FIFO 覆盖)
  • 互斥锁同步:保护 Producer 和 Consumer 任务之间的缓冲区访问

关键设计决策

  1. 环形缓冲区而非队列:使用环形缓冲区以提高内存效率和更简单的实现
  2. FIFO 覆盖:当缓冲区满时,覆盖最旧的数据(Producer 不跟踪数据丢失,但 Consumer 可以检测覆盖)
  3. 周期性 Consumer 处理:Consumer 每 50ms 处理一次缓冲区,读取最多 10 个最新样本
  4. 任务通知:ESP 定时器回调通过 xTaskNotify 通知 Producer 任务,避免阻塞定时器回调

实现细节

基于定时器的采样

  • ESP 定时器:创建周期定时器,周期 = 1,000,000 / sampling_frequency_hz 微秒
  • 定时器回调:在定时器上下文中执行,通过 xTaskNotify 通知 Producer 任务
  • 立即首次采样:在启动周期定时器之前执行一次采样

Producer 任务

优先级:10(高优先级,用于及时采集)

功能

  1. 通过 xTaskNotifyWait 等待定时器通知
  2. 读取传感器数据(加速度和温度)
  3. 准备带时间戳的样本结构
  4. 在互斥锁保护下写入环形缓冲区
  5. 更新采集统计

缓冲区写入逻辑

  • 以 10ms 超时获取互斥锁
  • 写入当前写指针位置
  • 推进写指针(环形)
  • 如果缓冲区满(写指针追上读指针),推进读指针(FIFO 覆盖)
  • 更新覆盖计数用于监控

Consumer 任务

优先级:8(中等优先级,低于 Producer 以确保采集优先级)

功能

  1. 等待处理间隔(50ms)
  2. 获取互斥锁以读取缓冲区快照
  3. 从读指针读取最多 10 个最新样本
  4. 推进读指针
  5. 处理样本(特征提取、检测等)
  6. 更新处理统计
  7. 输出结果(串口、MQTT、LCD)

处理逻辑

  • 计算可用样本(读指针和写指针之间的距离)
  • 读取最多 CONSUMER_PROCESS_SAMPLE_COUNT(10)个样本
  • 处理环形缓冲区环绕
  • 如果样本不足,用零填充

环形缓冲区

大小:512 样本(可通过 CIRCULAR_BUFFER_SIZE 配置)

内存:从 PSRAM 分配以支持大缓冲区

同步:互斥锁保护的访问

行为

  • 满时 FIFO 覆盖
  • 读指针随 Consumer 读取而推进
  • 写指针随 Producer 写入而推进
  • 跟踪覆盖计数用于监控

环形缓冲区动画

图:环形缓冲区操作示意图,显示 Producer 写入和 Consumer 读取,以及 FIFO 覆盖行为

加速度检测

启用后,Consumer 任务对处理的样本执行加速度检测:

条件

  • |x| > 0.5g OR |y| > 0.5g OR |z| < 0.5g

LCD 反馈

  • 满足条件时显示红色
  • 否则显示白色
  • 颜色保持:0.3 秒
  • 最小更新间隔:100ms(避免 SPI 总线冲突)

数据流

ESP 定时器 → 定时器回调 → xTaskNotify → Producer 任务
                                    读取传感器(SPI)
                                    环形缓冲区(互斥锁)
                                    Consumer 任务(50ms 间隔)
                                    处理样本
                          ┌───────────────────┴───────────────────┐
                          ↓                   ↓                    ↓
                       串口                MQTT                  LCD

配置

默认配置

#define RT_PC_QUEUE_SIZE_DEFAULT 50
#define RT_PC_PRODUCER_PRIORITY 10
#define RT_PC_CONSUMER_PRIORITY 8
#define RT_PC_PRODUCER_STACK_SIZE 4096
#define RT_PC_CONSUMER_STACK_SIZE 8192
#define CIRCULAR_BUFFER_SIZE 512
#define CONSUMER_PROCESS_INTERVAL_MS 50
#define CONSUMER_PROCESS_SAMPLE_COUNT 10

配置参数

  • 采样频率:0.1 - 1000 Hz(初始化时验证)
  • 队列大小:用于配置结构兼容性(环形缓冲区模式下不使用)
  • 环形缓冲区大小:512 样本(编译时固定)
  • Consumer 处理间隔:50ms(编译时固定)
  • Consumer 处理样本数:10 样本(编译时固定)

功能特性

优势

  • 实现简单:易于理解的 Producer-Consumer 模式
  • 采集与处理解耦:Producer 和 Consumer 独立运行
  • 内存高效:具有覆盖能力的环形缓冲区
  • 低开销:最小同步开销
  • 适用于中等频率:针对 100 Hz - 1 kHz 范围优化

限制

  • 缓冲区可能成为瓶颈:如果 Consumer 无法跟上,数据可能被覆盖
  • 固定处理间隔:Consumer 以固定 50ms 间隔处理
  • 仅限于最新样本:每个周期仅处理最后 10 个样本
  • 无 DMA 支持:使用标准 SPI 传输(CPU 绑定)

性能特征

适用频率范围

  • 有效范围:0.1 - 1000 Hz(初始化时验证)
  • 推荐:100 Hz - 1 kHz
  • 最大:最高 1 kHz(验证限制)
  • 最小:0.1 Hz(实际限制)

资源使用

  • 内存:环形缓冲区(512 样本 × 32 字节 = 16 KB,来自 PSRAM)
  • CPU:中等使用(Producer 和 Consumer 任务)
  • 同步:单个互斥锁用于缓冲区访问

使用注意事项

  1. 初始化顺序:必须在 arch_pc_set_sensor_handle() 之前调用 arch_pc_init()
  2. 传感器句柄:启动前必须设置
  3. 缓冲区监控:监控统计中的 overwrite_count 以检测 Consumer 是否落后
  4. 处理延迟:Consumer 每 50ms 处理一次,因此处理延迟最多为 50ms
  5. 样本计数:Consumer 每个周期处理最多 10 个样本以实现实时响应

错误处理

  • 互斥锁超时:如果无法在 10ms 内获取互斥锁,Producer 丢弃样本
  • 缓冲区未初始化:如果缓冲区未就绪,丢弃样本
  • 传感器读取失败:记录日志但不停止处理
  • 任务创建失败:返回错误,清理资源

线程安全

  • 互斥锁保护:所有缓冲区访问由互斥锁保护
  • 任务隔离:Producer 和 Consumer 任务正确隔离
  • 统计:在互斥锁保护的部分内原子更新