失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Arduino测量误差数据的处理——莱特 格拉布斯准则剔除异常数据

Arduino测量误差数据的处理——莱特 格拉布斯准则剔除异常数据

时间:2020-01-17 22:37:29

相关推荐

Arduino测量误差数据的处理——莱特 格拉布斯准则剔除异常数据

我们在用各种测量模块或设备进行测量时,或多或少会受到各种干扰的影响,使得到的数据与真实情况有所偏差,甚至与正确的数值差之千里。通常我们采用统计学的方法来获得相对正确的值,最基本的就是用平均值方法,即将大量数据进行累加再除以数据个数而获得均值。不过这个方法的前提是大量数据,而我们用Arduino这类工控设备进行动态测量和控制时,会受到采样频率和响应时间的限制,不可能进行数万次的采样后再进行处理。

那如果我们采样几十个数据后就马上求取均值是不是可以呢?答案是肯定的,不过不能采用简单的直接求均值方法,因为在样本数据比较少的情况下,即使只有极少量的异常数据,都会造成均值与正确值的较大偏离。例如我们有10个人,其中一个是马云,另9人年薪在15万左右,结果一求平均年薪,我们每人的年薪都被平均到10亿了。在测量中,我们也会碰到这种情况,由于外界偶发的干扰以及测量设备内部产生的电子干扰等,都会出现异常的数据。那怎么办呢?我们自然想到先将这些异常数据剔除掉,然后再求均值。这种方法的基本思想是:给定一置信概率,确定相应的置信区间,凡超过置信区间的误差就认为是粗差,应予以剔除。用于粗大误差剔除的常见方法有莱特检验法和格拉布斯检验法。

我们先简单的了解下莱特检验法和格拉布斯检验法。首先我们假定测量获得的数据呈正态分布,通俗点讲,就是测量获得的数据基本散布在正确值(真值)的附近,越靠近中心数据个数越多。这里我们先导出一个“残差”的概念,设第i次获得的数据为Xi,它与真值的差,我们称之为“残差”,不过我们根本不知道真值是多少,因此我们就用均值Xbar来代替真值,利用残差和数据量N,我们可以用贝塞尔公式求得σ(x),请参看下图。莱特准则就是残差|Vi|>3σ(x)时就作为粗大误差数据,予以剔除。在测量数据分布不能确定,对测量次数没有太大要求的情况下,通常取2更有适用性。看下图:

我们再来看格拉布斯准则为:|Vi|>g(n,a)σ(x)时就作为粗差数据,格拉布斯准则中的g(n,a)是一个取决于测量次数n和置信概率a,置信概率就是测量数据落在这个区间中的概率,格拉布斯准则中最常用置信概率为95%和99%,即如果采用a=95%,意味着有95%的数据是落在可信的区间里。这里特别要注意的是,置信概率越大,则置信区间越大,似乎是大一点好,其实并不然,置信区间越大,最大偏差的范围也就越大,那异常数据落入这个区间的概率也越大,即置信概率大的情况下,异常数据就可能没有被排除掉,反而造成我们的平均值偏离了正确值。g(n,a)的值我们可以通过下面的表来查看:

下面说下程序的实现,本程序建立了一个Detection,包含了莱特准则、格拉布斯95%、格拉布斯99%准则,还可以自定义贝塞尔公式前的系数(我称之为准则量),我们只需提供必要的数据后,调用该子程序就可以获得剔除粗差数据后的均值:

double Detection(double data[],double baddata[],int datanum,int badnum,int rule)

Detection子程序返回的是剔除粗差数据后的平均值;参数data输入时为原始测量数据,返回时,前datanum个数据为有效数据(即剔除粗差数据后的数据);参数baddata无输入数据,输出为被剔除的数据;参数datanum输入为原始测量数据个数;参数datanum无输入数据,输出为剔除的数据个数;参数rule为莱特or格拉布斯准则选择,3为莱特准则,4为格拉布斯95%,5为格拉布斯99%,小于3为自定义准则量。

程序采用了循环检测方式,即选定某个准则后,根据该准则剔除粗差数据后,再进行检测,如果还有粗差数据,再次剔除该粗差数据,依次循环,直到没有可剔除的粗差数据为止(或有效数据不足6个)。

此外,子程序中的Serial.print和Serial.println语句是用于调试和观察粗差数据的处理过程用,在正式使用该子程序时,应该注解掉或直接删除这些Serial.print语句。

本程序在Arduino UNO板上测试运行过,只需直接下载到Arduino UNO板即可,下面是完整的程序:

/*本程序建立了一个Detection子程序,包含了莱特准则、格拉布斯95%、格拉布斯99%准则,还可以自定义贝塞尔公式前的系数(我称之为准则量),我们只需提供必要的数据后,调用该子程序就可以获得剔除粗差数据后的均值*/const int DATAN=15; //原始测试数据的个数double dt[DATAN]={21.35,20.36,20.37,20.34,20.31,20.2,20.32,20.4,20.33,20.38,20.36,20.29,20.40,20.41,20.95};//原始测试数据double bdt[DATAN];//存放被剔除的数据int dn=DATAN;//数据个数int bdn;//被剔除的数据个数void setup() {Serial.begin(9600); //设置串口波特率9600Serial.println(Detection(dt,bdt,dn,bdn,4));//最后一个参数4,表示采用格拉布斯95%准则,可以用其他值进行测试}void loop() {// put your main code here, to run repeatedly:}//误差数据剔除程序,返回有效数据的平均值//参数data输入为原始测量数据,返回时,前datanum个为有效数据//参数baddata无输入数据,输出为被剔除的数据//参数datanum输入为原始测量数据个数//参数badnum无输入数据,输出为剔除的数据个数//参数rule为莱特or格拉布斯准则选择,3为莱特准则,4为格拉布斯95%,5为格拉布斯99%,小于3为自定义准则量double Detection(double data[],double baddata[],int datanum,int &badnum,int rule){double data_b[datanum];//临时存放保留的数据double v[datanum]; //残差double g95[]={1.15,1.46,1.67,1.82,1.94,2.03,2.11,2.18,2.23,2.29,2.33,2.37,2.41,2.44,2.47,2.50,2.53,2.56,2.58,2.60,2.62,2.64,2.66,2.74,2.81,2.87,2.96,3.17};//格拉布斯95%double g99[]={1.16,1.49,1.75,1.94,2.10,2.22,2.32,2.41,2.48,2.55,2.61,2.66,2.71,2.75,2.79,2.82,2.85,2.88,2.91,2.94,2.96,2.99,3.01,3.10,3.18,3.24,3.34,3.58};//格拉布斯99%double bsl; //贝塞尔公式结果double maxdev; //有效的莱特 or 格拉布斯准则的最大偏差double sum; //累加临时存储double average; //平均值int badindex;//某次剔除数据数int validNum=0;//有效数据数int proindex=0;//循环的次数double lg;//莱特 or 格拉布斯准则的系数int i;if (rule<=3) //当rule小于等于3时,直接用莱特系数3或自定义的rule值lg=rule;else if(rule>5) //当rule大于5时,强制设为莱特准则lg=3;badnum=0;while(1){if(rule==4) //格拉布斯95%{if(datanum>=100) lg=g95[27];//数据个数大于100个时else if(datanum>=50) lg=g95[26];else if(datanum>=40) lg=g95[25];else if(datanum>=35) lg=g95[24];else if(datanum>=30) lg=g95[23];else if(datanum>=25) lg=g95[22];else lg=g95[datanum-3];}else if(rule==5)//格拉布斯99%{if(datanum>=100) lg=g99[27];else if(datanum>=50) lg=g99[26];else if(datanum>=40) lg=g99[25];else if(datanum>=35) lg=g99[24];else if(datanum>=30) lg=g99[23];else if(datanum>=25) lg=g99[22];else lg=g99[datanum-3];}proindex++;Serial.println("**It is the times to cal.**");Serial.println(proindex);sum=0;for(i=0;i<datanum;i++)sum+=data[i];average=sum/datanum; //计算平均值Serial.println("Your data are: ");sum=0;for(i=0;i<datanum;i++){Serial.println(data[i]);v[i]=data[i]-average; //计算残差sum+=v[i]*v[i]; //计算残差平方和}Serial.println("the residual are:");for(i=0;i<datanum;i++)Serial.println(v[i]);bsl=sqrt(sum/(datanum-1)); //计算贝塞尔公式标准差maxdev=lg*bsl; //计算最大偏差//输出相关信息Serial.print("The average is: ");Serial.println(average);Serial.print("The Bessel Formula is: ");Serial.println(bsl);Serial.print("The Laite or Grubs level is:");Serial.println(lg);Serial.print("The Max deviation is: ");Serial.println(maxdev);//剔除坏值,即剔除粗差数据validNum=0;badindex=0;for(i=0;i<datanum;i++)if(fabs(v[i])>=maxdev && maxdev!=0) //当|Vi|>准则偏差值时{baddata[badnum++]=data[i];//将该Xi作为粗差数据,放入坏数据数组badindex++;}else data_b[validNum++]=data[i];//否则将效数数据暂存到data_b数组for(i=0;i<validNum;i++) //将暂存的效数数据送回数据数组datadata[i]=data_b[i];datanum=validNum;//将当前有效数据个数作为数据个数if(datanum>5)//有效数据大于5个,则继续进行处理{if(badindex==0) //若没有可剔除的粗差数据{Serial.print("There is no baddata , end.");Serial.println(proindex);break;//跳出循环,即粗差数据处理完毕}else//否则,即有粗差数据,继续循环处理{Serial.print("You have got baddata in the times cal!!! ");Serial.println(proindex);Serial.print("You have got baddata number is: ");Serial.println(badindex);Serial.println("the bad data is ");for(i=0;i<badnum;i++)Serial.println(baddata[i]);}}else break;//有效数据小于等于5个,直接跳出循环}//误差数据处理完毕Serial.println("**Summary**");//输出剩余数据Serial.println("the valid data are: ");for(i=0;i<validNum;i++)Serial.println(data_b[i]);//输出所有坏值Serial.println("the bad data are: ");for(i=0;i<badnum;i++)Serial.println(baddata[i]);Serial.print("The last average is: ");Serial.println(average);Serial.print("The last Max deviation is: ");Serial.println(maxdev);return average;//子程序返回有效数据的均值}

如果觉得《Arduino测量误差数据的处理——莱特 格拉布斯准则剔除异常数据》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。