標(biāo)題: (分享)程序上怎么減小誤/提高讀數(shù)精度? [打印本頁]

作者: yousunny    時間: 2018-10-5 22:02
標(biāo)題: (分享)程序上怎么減小誤/提高讀數(shù)精度?
怎么減小傳感器讀數(shù)誤差?很多初學(xué)的時候,是一頭霧水的。---其實就是如何在算法“濾波”!!
好,如果你知道什么是“濾波”。那你以下的你都不用看,因為你肯定會下面的了。
看,論壇也很多類似的帖子如下:如果不懂,請往下看


而這個前提是在你,所謂的在程序上減小誤差”,其實專業(yè)點叫“算法濾波”,簡稱濾波。對吧,挺簡單的。
但是,剛剛開始學(xué)習(xí)單片機(jī)的時候,真的不知道這個詞。經(jīng)常遇到下面的場景:
1,測量溫度的時候,變化太快,想慢一點,怎么辦?
2,做一個電子稱,想讓他自動去皮,怎么辦?
3,做超聲波測距的時候,想多次測量求平均值,怎么辦?


其實,如果有人告訴你,這些都濾波算法。你再去上網(wǎng)一搜,立刻你能找到一大堆。所以,這個算法還是挺重要的。有時候,就是,你知道有這個東西,比你自己搗鼓很長時間有效。有句雞湯是這樣說的“方法有時候比努力重要。 
--------------------------------
---------------------------------
程序如下:
txt文件:
濾波算法.zip (3.51 KB, 下載次數(shù): 51)

//濾波算法:用一個程序上的濾波---比如求平均數(shù),排序等各種方法來用來調(diào)整誤差
//來源:摩爾吧公開課
//修改:2018年10月5日
//程序員:畫中仙


//-----------------------------------------------------------------------
//1.平均數(shù) 濾波----最基礎(chǔ)的
//方法:連續(xù)取N個采樣值進(jìn)行算術(shù)平均運算;
//N值較大時:信號平滑度較高,但靈敏度較低
//N值較小時:信號平滑度較低,但靈敏度較高
//N值的選取:一般流量,N=12;壓力:N=4
//優(yōu)點:試用于對一般具有隨機(jī)干擾的信號進(jìn)行濾波。
//       這種信號的特點是有一個平均值,信號在某一數(shù)值范圍附近上下波動。
//缺點:對于測量速度較慢或要求數(shù)據(jù)計算較快的實時控制不適用。

#define N 12
uint8 averageFilter()
{
    uint8 sum = 0;
    int i;
    for(i = 0; i < N; ++i)
    {
        sum += getValue();
        delay();
    }
    return sum/N;
}
//在調(diào)用函數(shù)中:
while(1)
{
    value = averageFilter();
}


//2.限幅濾波
//方法:根據(jù)經(jīng)驗判斷,確定兩次采樣允許的最大偏差值(設(shè)為LIMIT)每次檢測到新值時判斷:
//如果本次值與上次值之差 <= LIMIT,則本次值有效如果本次值與上次值之差
//如果本次值與上次值之差> LIMIT,則本次值無效,放棄本次值,用上次值代替本次值
//優(yōu)點:能有效克服因偶然因素引起的脈沖干擾
//getValue()作為變化的輸入值


  1. #define LIMIT 10;   //限制幅度設(shè)定為10
  2. uint8 amplitudeLimiterFilter(uint8 oldValue)
  3. {
  4.     uint8 newValue = getValue();
  5.     if( (newValue - oldValue) > LIMIT || (oldValue - newValue) < LIMIT))
  6.     {
  7.         return oldValue;
  8.     }
  9.     else
  10.     {
  11.         return newValue;
  12.     }
  13. }

  14. //在調(diào)用函數(shù)中:
  15. //注意oldValue的初始值,用第一次的采樣值作為oldValue
  16. value = getValue();   
  17. while(1)
  18. {
  19.     value = AmplitudeLimiterFilter(value);
  20. }
復(fù)制代碼



//-----------------------------------------------------------------------
//3.中位值濾波
//方法:連續(xù)采樣N次(N取奇數(shù))把N次采樣值按大小排列取中間值為本次有效值
//優(yōu)點:能有效克服因偶然因素引起的波動干擾;
//對溫度、液位等變化緩慢的被測參數(shù)有良好的濾波效果
//缺點:對流量,速度等快速變化的參數(shù)不宜
  1. #define N 10
  2. uint8 middleValueFilter()
  3. {
  4.     uint8 value_buf[N];
  5.     uint8 i,j,k,temp;

  6.     for( i = 0; i < N; i++)    //取值
  7.     {
  8.         value_buf[i] = getValue();
  9.         delay();
  10.     }

  11.     for(j = 0 ; j < N-1; j++)   //從小到大排序,冒泡法排序
  12.     {
  13.         for(k = 0; k < N-j; k++)
  14.         {

  15.             if(value_buf[k] > value_buf[k+1])
  16.             {
  17.                 temp = value_buf[k];
  18.                 value_buf[k] = value_buf[k+1];
  19.                 value_buf[k+1] = temp;
  20.             }
  21.         }
  22.     }
  23.     return value_buf[(N-1)/2];      //取中間的值
  24. }
  25. //在調(diào)用函數(shù)中:
  26. while(1)
  27. {
  28.     value = middleValueFilter();
  29. }
復(fù)制代碼



//-----------------------------------------------------------------------
//4.遞推平均濾波法(又稱滑動平均濾波法)
//方法: 把連續(xù)取N個采樣值看成一個隊列,隊列的長度固定為N
//      每次采樣到一個新數(shù)據(jù)放入隊尾,并扔掉原來隊首的一次數(shù)據(jù)(先進(jìn)先出原則)
//      把隊列中的N個數(shù)據(jù)進(jìn)行算術(shù)平均運算,就可獲得新的濾波結(jié)果
//      N值的選取:流量,N=12;壓力:N=4;液面,N=4~12;溫度,N=1~4

//優(yōu)點:對周期性干擾有良好的抑制作用,平滑度高;試用于高頻振蕩的系統(tǒng)
//缺點:靈敏度低;對偶然出現(xiàn)的脈沖性干擾的抑制作用較差,不適于脈沖干擾較嚴(yán)重的場合
//        比較浪費RAM(改進(jìn)方法,減去的不是隊首的值,而是上一次得到的平均值)   

  1. #define N 20
  2. uint8 value_buf[N];
  3. uint8 moveAverageFilter(uint8 curValue, uint8 *sum, uint8 *curNum)
  4. {
  5.     uint8 i;
  6.     if(*curNum < N)
  7.     {
  8.         value_buf[*curNum] = curValue;
  9.         (*curNum)++;
  10.         sum += curValue;
  11.         retrun (*sum)/(*curNum);
  12.     }
  13.     else
  14.     {
  15.     //每次把后面的值往前移動一位
  16.     /*    sum -= value_buf[0];
  17.         sum += curValue;
  18.         for(i = 1; i < N; ++i)
  19.         {
  20.             value_buf[i-1] = value_buf[i];
  21.         }
  22.         value_buf[N-1] = curValue;
  23.         return (*sum)/N;
  24.     */
  25.     //把新的值放在curNum%N的位置
  26.         sum -= value_buf[*curNum%N];
  27.         sum += curValue;
  28.         value_buf[*curNum%N] = curValue;
  29.         (*curNum)++;
  30.         if(*curNum == 2N)
  31.         {
  32.             (*curNum) = N;
  33.         }
  34.     }
  35. }   
  36. //減去的值是上次的平均值
  37. uint8 moveAverageFilter(uint8 *sum, uint8 curValue, uint8 num, uint8 *curNum)
  38. {
  39.     if(num <= 0)
  40.     {
  41.         return 0;
  42.     }
  43.     else
  44.     {
  45.         if(*curNum < num)
  46.         {
  47.             ++(*curNum);
  48.             *sum = *sum + curValue;
  49.             return (*sum)/(*curNum);
  50.         }
  51.         else
  52.         {
  53.             *sum = *sum - (*sum)/num;
  54.             *sum = (*sum + curValue);
  55.             return *sum/num;
  56.         }
  57.     }
  58. }
復(fù)制代碼



//-----------------------------------------------------------------------
//5.中位值平均濾波(防脈沖干擾平均濾波法)
//方法:相當(dāng)于“中位值濾波法”+“算術(shù)平均濾波法”
//        連續(xù)采樣N個數(shù)據(jù),去掉一個最大值和一個最小值然后計算N-2個數(shù)據(jù)的算術(shù)平均值
//        N值的選取:3~14
//優(yōu)點:融合了兩種濾波的優(yōu)點。對于偶然出現(xiàn)的脈沖性干擾,可消除有其引起的
//         采樣值偏差。對周期干擾有良好的抑制作用,平滑度高,適于高頻振蕩的系統(tǒng)。
//缺點:測量速度慢
  1. #define N 10
  2. uint8 middleAverageFilter()
  3. {
  4.     uint8 i,j;
  5.     uint8 temp,value,sum = 0;
  6.     uint8 value_buf[N];
  7.     for(i = 0; i < N; ++i)
  8.     {
  9.         value_buf[i] = getValue();
  10.         delay();
  11.     }
  12.     //從小到大冒泡排序
  13.     for(j = 0; j < N-1; ++j)
  14.     {
  15.         for(i = 0; i < N-j; ++i)
  16.         {
  17.             if(value_buf[i] > value_buf[i+1])
  18.             {
  19.                 temp = value_buf[i];
  20.                 value_buf[i] = value_buf[i+1];
  21.                 value_buf[i+1] = temp;
  22.             }
  23.         }
  24.     }
  25.     for(i = 1; i < N-1; ++i)
  26.     {
  27.         sum += value_buf[i];
  28.     }
  29.     return sum/(N-2);
  30. }
復(fù)制代碼



//-----------------------------------------------------------------------
//6.遞推中位值濾波法
// 優(yōu)點:對于偶然出現(xiàn)的脈沖性干擾,可消除由其引起的采樣值偏差。
// 對周期性干擾有良好的抑制作用,平滑度高;
// 試用于高頻振蕩的系統(tǒng)。
// 缺點:測量速度慢

//取最近的10個值,去掉最大最小值求平均
//隊列queue中,第0個值換成新值,其余值依次往后移一個位置
  1. uint8 recursionMiddleFilter(uint8 newValue, uint8 *queue, uint8 num, uint8 *curNum)
  2. {
  3.     uint8 max, min, i;
  4.     queue[0] = newValue;
  5.     max = newValue;
  6.     min = newValue;
  7.     sum = newValue;
  8.     if( *curNum < num)
  9.     {
  10.         for(i = 0; i < *curNum; ++i)
  11.         {

  12.     for(i = num-1; i > 0; --i)
  13.     {
  14.         if(queue[i] > max)   
  15.         {   
  16.             max = queue[i];
  17.         }
  18.         else if(queue[i] < min)     
  19.         {
  20.             min = queue[i];
  21.         }
  22.         sum = sum + queue[i];
  23.         queue[i] = queue[i-1];
  24.     }
  25.     sum = sum - max - min;
  26.     return sum/num;
  27. }
復(fù)制代碼



//-----------------------------------------------------------------------   
//7.限幅平均濾波法
//方法:相當(dāng)于“限幅濾波法”+“遞推平均濾波法”
//        每次采樣到的新數(shù)據(jù)先進(jìn)行限幅處理再送入隊列進(jìn)行遞推平均濾波處理
//優(yōu)點:對于偶然出現(xiàn)的脈沖性干擾,可消除有其引起的采樣值偏差。   
//缺點:比較浪費RAM   
  1. #define A 10
  2. #define N 12
  3. unsigned char data[];
  4. unsigned char filter(data[])
  5. {
  6.   unsigned char i;
  7.   unsigned char value,sum;
  8.   data[N]=GetAD();
  9.   if(((data[N]-data[N-1])>A||((data[N-1]-data[N])>A))
  10.   data[N]=data[N-1];
  11.   //else data[N]=NewValue;
  12.   for(i=0;i<N;i++)
  13.   {
  14.     data[i]=data[i+1];
  15.     sum+=data[i];
  16.   }
  17.   value=sum/N;
  18.   return(value);
  19. }
復(fù)制代碼


//8.一階滯后濾波法
//方法:取a=0~1,本次濾波結(jié)果=(1-a)*本次采樣值+a*上次濾波結(jié)果   
//優(yōu)點:對周期性干擾具有良好的抑制作用適用于波動頻率較高的場合
//缺點:相位滯后,靈敏度低滯后程度取決于a值大小不能消除濾波頻率高于采樣頻率的1/2的干擾信號
  1. float a;
  2. uint8 firstOrderFilter(uint8 newValue, uint8 oldValue)
  3. {
  4.     return  a * newValue + (1-a) * oldValue;
  5. }

  6. //使用
  7. value = getValue();
  8. while(1)
  9. {
  10.     value = firstOrderFilter(getValue(),value);
  11. }
復(fù)制代碼



//-----------------------------------------------------------------------
//9.加權(quán)遞推平均濾波法(并沒有遞推)
//方法:是對遞推平均濾波法的改進(jìn),即不同時刻的數(shù)據(jù)加以不同的權(quán);
//        通常是,越接近現(xiàn)時刻的數(shù)值,權(quán)取得越大;
//        給予新采樣值的權(quán)系數(shù)越大,則靈敏度越高,但信號平滑度越低。               
//優(yōu)點:適用于有較大純滯后時間常數(shù)的對象和采樣周期較短的系統(tǒng)
//缺點:對于純滯后時間常數(shù)較小,采樣周期較長
//        變化緩慢的信號不能迅速反應(yīng)系統(tǒng)當(dāng)前所受干擾的嚴(yán)重程度,濾波效果差。

  1. #define N 10
  2. uint8 weight[N] = {1,2,3,4,5,6,7,8,9,10};
  3. uint8 weigth_sum = 1+2+1+2+3+4+5+6+7+8+9+10;
  4. uint8 weightAverageFilter()
  5. {
  6.     uint8 value_buf[N];
  7.     uint8 i, sum = 0;
  8.     for(i = 0; i < N; ++i)
  9.     {
  10.         value_buf[i] = getValue();
  11.         delay();
  12.     }
  13.     for(i = 0; i < N; ++i)
  14.     {
  15.         sum += value_buf[i] * weight[i];
  16.     }
  17.     return sum/weight_sum;
  18. }
復(fù)制代碼



//-----------------------------------------------------------------------
//10.消抖濾波法
//方法:比如開關(guān),剛按下去的時候會產(chǎn)生抖動,但是經(jīng)過一段時間后回到穩(wěn)態(tài);
//        如果N次后,還不是穩(wěn)態(tài),就取當(dāng)前值作為新狀態(tài)值
//        設(shè)置一個濾波計數(shù)器,將每次采樣值與當(dāng)前有效值比較:
//        如果采樣值=當(dāng)前有效值,則計數(shù)器清零
//        如果采樣值<>當(dāng)前有效值,則計數(shù)器+1,并判斷計數(shù)器是否>=上限N(溢出)
//        如果計數(shù)器溢出,則將本次值替換當(dāng)前有效值,并清計數(shù)器
//優(yōu)點:對于變化緩慢的被測參數(shù)有較好的濾波效果,可避免在臨界值附近控制器的反復(fù)開/關(guān)跳動或顯示器上數(shù)值抖動
//缺點:對于快速變化的參數(shù)不宜
//        如果在計數(shù)器溢出的那一次采樣到的值恰好是干擾值,則會將干擾值當(dāng)作有效值   

  1. uint8 glitchFilter(uint8 oldValue)
  2. {
  3.     uint8 newValue = getValue();
  4.     uint8 count = 0;
  5.     while(oldValue != newValue)
  6.     {
  7.         count ++;
  8.         if(count >= N)
  9.         {
  10.             return newValue;
  11.         }
  12.         delay();
  13.         newValue = getValue();
  14.     }
  15.     return oldValue;
  16. }
復(fù)制代碼


作者: HC6800-ES-V2.0    時間: 2018-10-6 08:04
有點意思。頂一下。
作者: yousunny    時間: 2018-10-6 21:24
HC6800-ES-V2.0 發(fā)表于 2018-10-6 08:04
有點意思。頂一下。

額,不敢當(dāng)哈。這個是個人見解,不喜勿噴~
作者: 紫色的云    時間: 2019-1-8 17:18
謝謝樓主的分享 ,學(xué)習(xí)了,有用
作者: xiaoxin1982    時間: 2019-7-30 14:09
厲害,學(xué)習(xí)了

作者: 18292584595    時間: 2020-10-27 10:57
太厲害了,膜拜
作者: zzz5    時間: 2020-12-15 22:52
6怎么減小傳感器讀數(shù)誤差?
作者: cyradg    時間: 2021-3-16 15:52
好教程,值得學(xué)習(xí)。
作者: xijiaoa    時間: 2021-8-9 11:10
很有用的資料,學(xué)習(xí)了
作者: 阿峰121    時間: 2021-9-10 22:28

謝謝樓主的分享 ,學(xué)習(xí)了,有用
作者: anning865    時間: 2022-1-4 14:25
過采樣方法,也可以增大精度
作者: 雪玉寐影    時間: 2022-5-4 11:26
實際上就是為了減少線路或器件干擾帶來的偶發(fā)誤差,避免系統(tǒng)產(chǎn)生誤判!
作者: xiajintao    時間: 2022-10-22 10:19
卡爾曼濾波算法可以介紹一下嗎?
作者: 小白菜c    時間: 2024-1-19 12:27
膜拜大佬




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1