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测量误差数据的处理——莱特、格拉布斯准则剔除异常数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MT6763/MT6763T处理器参数比
- 下一篇: Python人脸识别考勤打卡系统