C 无缓冲的易出错测量样本的平均值

C 无缓冲的易出错测量样本的平均值,c,average,microcontroller,adc,C,Average,Microcontroller,Adc,我得到了一个µC,它通过一个带ADC的传感器来测量温度。由于各种情况,可能会出现读数为0(-30°C)或不可能的大值(500-1500°C)的情况。我无法解释这些读数如此糟糕的原因(时间关键的ISR,有时是错误的接线),所以我必须用一段聪明的代码来解决 我想到了这个(代码在ISR中被调用过采样次数): #定义过采样16//读取值16次 #定义温度有效变化0.15//15%读数变化是可能的 //浮动原始参数床值=; //ADC=; 如果(temp_count>1){//temp_count=读取的

我得到了一个µC,它通过一个带ADC的传感器来测量温度。由于各种情况,可能会出现读数为0(-30°C)或不可能的大值(500-1500°C)的情况。我无法解释这些读数如此糟糕的原因(时间关键的ISR,有时是错误的接线),所以我必须用一段聪明的代码来解决

我想到了这个(代码在ISR中被调用过采样次数):

#定义过采样16//读取值16次
#定义温度有效变化0.15//15%读数变化是可能的
//浮动原始参数床值=;
//ADC=;
如果(temp_count>1){//temp_count=读取的样本量,则会在其他位置增加
浮动平均值=原始温度床值/温度计数;
float diff=(avgRaw>ADC?avgRaw-ADC:ADC-avgRaw)/(avgRaw==0?1:avgRaw);//拉出以缩短SO的线
如果(差异>温度有效变化*((过采样-温度计数)/过采样))//后续读数的耐受性较小
原始温度床值+=平均值;
其他的
原始温度床值+=ADC;
}否则{
原始温度床值=ADC;
}
其中,
raw\u temp\u bed\u value
是一个静态全局值,当ISR被触发16次时,它会被读取和处理

如你所见,我检查当前平均值和新读数之间的差值是否小于15%。如果是,我接受读数,如果不是,我拒绝它,而是加上当前的平均值。 但是,如果第一次阅读是不可能的,那么这种情况就会严重恶化

我想到的一个解决方案是: 在最后一行中,原始温度床值重置为第一个ADC读数。最好将其重置为
原始温度值/过采样值
。所以我不会出现“第一次阅读错误”


你有更好的解决办法吗?我认为有些解决方案采用移动平均线,并使用移动平均线的平均值,但这需要额外的阵列/内存/周期,我们希望防止这种情况

我经常在抽样中使用我称之为变化率的东西。使用一个变量,该变量表示达到某个值所需的样本数,如20。然后继续将样本差异添加到变量除以变化率。您仍然可以使用阈值来过滤不太可能的值

float RateOfChange = 20;
float PreviousAdcValue = 0;
float filtered = FILTER_PRESET;

while(1)
{
    //isr gets adc value here        
    filtered = filtered + ((AdcValue - PreviousAdcValue)/RateOfChange);
    PreviousAdcValue = AdcValue;
    sleep();
}
请注意,这与低通滤波器不同,它的响应速度更快,最后增加的值最重要。但是,如果单个值出现太多变化,则变化不会太大,这取决于变化率

您还可以将过滤后的值预设为合理的值。这可以防止疯狂的启动行为

要达到一个稳定的值,需要改变样本的比率。例如,您可能希望使用计数器来计算采样数,以确保在此之前未使用过滤值。如果计数器低于变化率,跳过处理温度控制

对于更高级的(温度)控制程序,我强烈建议查看PID控制回路。这些增加了大量的功能,以获得快速、稳定的响应,并有效地将某些东西保持在特定的温度,并将振荡保持在最低限度。我已经在我自己的项目中使用了Marlin固件中使用的那个,并且工作得相当好

float RateOfChange = 20;
float PreviousAdcValue = 0;
float filtered = FILTER_PRESET;

while(1)
{
    //isr gets adc value here        
    filtered = filtered + ((AdcValue - PreviousAdcValue)/RateOfChange);
    PreviousAdcValue = AdcValue;
    sleep();
}