You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实时追踪带DC漂移的光电探测器信号最小值的优化方案问询

光电信号DC漂移下的最值追踪与触发优化方案

问题背景

  • 经硬件低通+高通滤波放大后的光电探测器模拟信号存在DC漂移(DC walk),导致实时最小值追踪困难
  • 当前每50次ADC读数将最小值上调1%的方案引入过多噪声,干扰触发逻辑(触发需满足:1. 信号处于负斜率;2. 数据低于平均值;3. 数据为最小值上方5%)
  • 信号周期范围0.6Hz-2Hz,周期波动±20%,无法通过历史周期预判触发时机;阀门动作延迟较长,需在脉冲波负斜率段确定稳定触发点
  • 调整窗口大小等尝试后,最值追踪效果仍不理想

现有核心代码

最值追踪核心片段

//Determine is value is outside max or min
if(data > max) max = data;
if(data < min) min = data;

//Reset function to bring the bounds in every 50 cycles
if(rstCntr>=50){ 
    rstCntr=0;
    max = max/1.01;
    min = min*1.01;
    if(min <= 1200) min = 1200; 
    if(max >= 1900) max = 1900; 
}

完整主函数

int main(void) {
    while (1)
    {
       
        //******************************************************************************
        //** Process analog sensor data, calculate HR, and trigger solenoids
        //** At some point this should probably be moved to a function call in System.c,
        //** but I don't want to mess with it right now since it works (Adam 11/23/2022)
        //******************************************************************************
        
        //Read Analog Data for Sensor
        data = ADC1_ReadChannel(7);  
        
        //Buffer the sensor data for peak/valley detection
        for(int buf=3;buf>0;buf--){
            dataBuffer[buf] = dataBuffer[buf-1];
        }
        dataBuffer[0] = data;
        
        //Look for a valley
        //Considered a valley is the 3 most recent data points are increasing
        //This helps avoid noise in the signal
        uint8_t count = 0;
        for(int buf=0;buf<3;buf++) {
            if(dataBuffer[buf]>dataBuffer[buf+1]) count++;
        }
        if(count >= 3) currentSlope = true; //if the last 3 points are increasing, we just passed a valley
        else currentSlope = false; //not a valley
        
        // Track the data stream max and min to calculate a signal average
        // The signal average is used to determine when we are on the bottom end of the waveform. 
        if(data > max) max = data;
        if(data < min) min = data;
        
        if(rstCntr>=50){ //Make sure we are tracking the signal by moving min and max in every 200 samples
            rstCntr=0;
            max = max/1.01;
            min = min*1.01;
            if(min <= 1200) min = 1200; //average*.5; //Probably finger was removed from sensor, move back up 
            if(max >= 1900) max = 1900; //Need to see if this really works consistently
        }
        rstCntr++;
        average = ((uint16_t)min+(uint16_t)max)/2;
        trigger = min; //Variable is only used for debug output, resetting each time around
              
        if(data < average &&
            currentSlope == false && //falling edge of signal
            data <= (((average-min)*.03)+min) && //Threshold above the min
        {            
            FireSolenoids();    
        }
    }
    return 1; 
}

优化方案

1. 自适应滑动窗口最值追踪

针对信号周期波动的特点,用滑动窗口限定最值追踪的时间范围,既覆盖完整信号周期,又避免DC漂移的长期影响:

  • 先通过最近的峰谷间隔估算当前周期,将窗口大小设为周期×ADC采样率×0.8(确保覆盖大部分有效信号段)
  • 窗口内维护环形队列,每次新数据入队时移除最旧数据,实时更新窗口内的max和min;若移除的是当前max/min,则重新遍历窗口获取新的最值
  • 代码示例:
#define MAX_WINDOW_SIZE 200 // 根据ADC采样率调整,比如100Hz采样时,2Hz信号周期为50点,窗口设80-100点足够
uint16_t windowBuffer[MAX_WINDOW_SIZE];
uint8_t windowHead = 0;
uint16_t currentWindowMax = 0;
uint16_t currentWindowMin = 4095; // 假设为12位ADC,满量程4095

void updateWindow(uint16_t newData) {
    uint16_t oldData = windowBuffer[windowHead];
    windowBuffer[windowHead] = newData;
    windowHead = (windowHead + 1) % MAX_WINDOW_SIZE;

    // 更新窗口最大值
    if (newData > currentWindowMax) {
        currentWindowMax = newData;
    } else if (oldData == currentWindowMax) {
        // 移除了旧最大值,重新遍历窗口获取新最大值
        currentWindowMax = 0;
        for (int i = 0; i < MAX_WINDOW_SIZE; i++) {
            if (windowBuffer[i] > currentWindowMax) currentWindowMax = windowBuffer[i];
        }
    }

    // 更新窗口最小值
    if (newData < currentWindowMin) {
        currentWindowMin = newData;
    } else if (oldData == currentWindowMin) {
        // 移除了旧最小值,重新遍历窗口获取新最小值
        currentWindowMin = 4095;
        for (int i = 0; i < MAX_WINDOW_SIZE; i++) {
            if (windowBuffer[i] < currentWindowMin) currentWindowMin = windowBuffer[i];
        }
    }
}

2. 指数平滑最值追踪

通过指数平滑算法对max和min进行滤波,兼顾DC漂移跟踪与噪声抑制:

  • 对新数据的峰值/谷值进行加权更新,非峰值/谷值时缓慢调整最值以跟踪漂移
  • alpha参数建议取0.01-0.05,可根据实际信号调试调整;初始化时用前20个数据的均值作为初始值
  • 代码示例:
float alpha = 0.02; // 可调参数,平衡漂移跟踪速度与噪声抑制
float smoothedMax = 0.0;
float smoothedMin = 4095.0;

// 初始化平滑最值
void initSmoothing() {
    uint32_t sum = 0;
    for (int i = 0; i < 20; i++) {
        uint16_t data = ADC1_ReadChannel(7);
        sum += data;
    }
    smoothedMax = sum / 20.0;
    smoothedMin = sum / 20.0;
}

// 更新平滑最值
void updateSmoothedMaxMin(uint16_t data) {
    if (data > smoothedMax) {
        smoothedMax = alpha * data + (1 - alpha) * smoothedMax;
    } else {
        // 缓慢衰减最大值,跟踪DC漂移
        smoothedMax = (1 - alpha/2) * smoothedMax;
    }

    if (data < smoothedMin) {
        smoothedMin = alpha * data + (1 - alpha) * smoothedMin;
    } else {
        // 缓慢抬升最小值,跟踪DC漂移
        smoothedMin = (1 + alpha/2) * smoothedMin;
    }

    // 保留原有边界限制
    if (smoothedMin <= 1200) smoothedMin = 1200;
    if (smoothedMax >= 1900) smoothedMax = 1900;
}

3. 触发逻辑优化

减少噪声误触发,确保在负斜率段稳定触发:

  • 扩展斜率判断的数据点数量(从3点增至5点),只有连续4次下降才判定为负斜率
  • 增加触发计数机制,连续3次满足所有触发条件才执行阀门动作,避免单次噪声干扰
  • 代码示例:
uint8_t triggerCount = 0;
#define TRIGGER_THRESHOLD_COUNT 3 // 连续满足次数阈值

// 优化斜率判断
uint8_t count = 0;
for (int buf = 0; buf < 4; buf++) { // 5个数据点,4次比较
    if (dataBuffer[buf] > dataBuffer[buf+1]) count++;
}
currentSlope = (count >= 4); // 连续4次下降,判定为负斜率

// 计算触发阈值与判断
float average = (smoothedMin + smoothedMax) / 2.0;
float triggerLevel = smoothedMin + (average - smoothedMin) * 0.05; // 最小值上方5%

if (data < average && !currentSlope && data <= triggerLevel) {
    if (triggerCount < TRIGGER_THRESHOLD_COUNT) {
        triggerCount++;
    } else {
        FireSolenoids();
        triggerCount = 0; // 触发后重置计数
    }
} else {
    triggerCount = 0; // 不满足条件时重置计数
}

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 09:15:32