怎么減小傳感器讀數誤差?很多初學的時候,是一頭霧水的。---其實就是如何在算法“濾波”!!
好,如果你知道什么是“濾波”。那你以下的你都不用看,因為你肯定會下面的了。
看,論壇也很多類似的帖子如下:如果不懂,請往下看
誤差算法.png (71.65 KB, 下載次數: 110)
下載附件
2018-10-5 21:48 上傳
而這個前提是在你,所謂的“在程序上減小誤差”,其實專業點叫“算法濾波”,簡稱濾波。對吧,挺簡單的。
但是,剛剛開始學習單片機的時候,真的不知道這個詞。經常遇到下面的場景:
1,測量溫度的時候,變化太快,想慢一點,怎么辦?
2,做一個電子稱,想讓他自動去皮,怎么辦?
3,做超聲波測距的時候,想多次測量求平均值,怎么辦?
其實,如果有人告訴你,這些都濾波算法。你再去上網一搜,立刻你能找到一大堆。所以,這個算法還是挺重要的。有時候,就是,你知道有這個東西,比你自己搗鼓很長時間有效。有句雞湯是這樣說的“方法有時候比努力重要!!”
--------------------------------
---------------------------------
程序如下:
txt文件:
濾波算法.zip
(3.51 KB, 下載次數: 51)
2018-10-5 22:00 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
//濾波算法:用一個程序上的濾波---比如求平均數,排序等各種方法來用來調整誤差
//來源:摩爾吧公開課
//修改:2018年10月5日
//程序員:畫中仙
//-----------------------------------------------------------------------
//1.平均數 濾波----最基礎的
//方法:連續取N個采樣值進行算術平均運算;
//N值較大時:信號平滑度較高,但靈敏度較低
//N值較小時:信號平滑度較低,但靈敏度較高
//N值的選取:一般流量,N=12;壓力:N=4
//優點:試用于對一般具有隨機干擾的信號進行濾波。
// 這種信號的特點是有一個平均值,信號在某一數值范圍附近上下波動。
//缺點:對于測量速度較慢或要求數據計算較快的實時控制不適用。
#define N 12
uint8 averageFilter()
{
uint8 sum = 0;
int i;
for(i = 0; i < N; ++i)
{
sum += getValue();
delay();
}
return sum/N;
}
//在調用函數中:
while(1)
{
value = averageFilter();
}
//2.限幅濾波
//方法:根據經驗判斷,確定兩次采樣允許的最大偏差值(設為LIMIT)每次檢測到新值時判斷:
//如果本次值與上次值之差 <= LIMIT,則本次值有效如果本次值與上次值之差
//如果本次值與上次值之差> LIMIT,則本次值無效,放棄本次值,用上次值代替本次值
//優點:能有效克服因偶然因素引起的脈沖干擾
//getValue()作為變化的輸入值
- #define LIMIT 10; //限制幅度設定為10
- uint8 amplitudeLimiterFilter(uint8 oldValue)
- {
- uint8 newValue = getValue();
- if( (newValue - oldValue) > LIMIT || (oldValue - newValue) < LIMIT))
- {
- return oldValue;
- }
- else
- {
- return newValue;
- }
- }
- //在調用函數中:
- //注意oldValue的初始值,用第一次的采樣值作為oldValue
- value = getValue();
- while(1)
- {
- value = AmplitudeLimiterFilter(value);
- }
復制代碼
//-----------------------------------------------------------------------
//3.中位值濾波
//方法:連續采樣N次(N取奇數)把N次采樣值按大小排列取中間值為本次有效值
//優點:能有效克服因偶然因素引起的波動干擾;
//對溫度、液位等變化緩慢的被測參數有良好的濾波效果
//缺點:對流量,速度等快速變化的參數不宜
- #define N 10
- uint8 middleValueFilter()
- {
- uint8 value_buf[N];
- uint8 i,j,k,temp;
- for( i = 0; i < N; i++) //取值
- {
- value_buf[i] = getValue();
- delay();
- }
- for(j = 0 ; j < N-1; j++) //從小到大排序,冒泡法排序
- {
- for(k = 0; k < N-j; k++)
- {
- if(value_buf[k] > value_buf[k+1])
- {
- temp = value_buf[k];
- value_buf[k] = value_buf[k+1];
- value_buf[k+1] = temp;
- }
- }
- }
- return value_buf[(N-1)/2]; //取中間的值
- }
- //在調用函數中:
- while(1)
- {
- value = middleValueFilter();
- }
復制代碼
//-----------------------------------------------------------------------
//4.遞推平均濾波法(又稱滑動平均濾波法)
//方法: 把連續取N個采樣值看成一個隊列,隊列的長度固定為N
// 每次采樣到一個新數據放入隊尾,并扔掉原來隊首的一次數據(先進先出原則)
// 把隊列中的N個數據進行算術平均運算,就可獲得新的濾波結果
// N值的選取:流量,N=12;壓力:N=4;液面,N=4~12;溫度,N=1~4
//優點:對周期性干擾有良好的抑制作用,平滑度高;試用于高頻振蕩的系統
//缺點:靈敏度低;對偶然出現的脈沖性干擾的抑制作用較差,不適于脈沖干擾較嚴重的場合
// 比較浪費RAM(改進方法,減去的不是隊首的值,而是上一次得到的平均值)
- #define N 20
- uint8 value_buf[N];
- uint8 moveAverageFilter(uint8 curValue, uint8 *sum, uint8 *curNum)
- {
- uint8 i;
- if(*curNum < N)
- {
- value_buf[*curNum] = curValue;
- (*curNum)++;
- sum += curValue;
- retrun (*sum)/(*curNum);
- }
- else
- {
- //每次把后面的值往前移動一位
- /* sum -= value_buf[0];
- sum += curValue;
- for(i = 1; i < N; ++i)
- {
- value_buf[i-1] = value_buf[i];
- }
- value_buf[N-1] = curValue;
- return (*sum)/N;
- */
- //把新的值放在curNum%N的位置
- sum -= value_buf[*curNum%N];
- sum += curValue;
- value_buf[*curNum%N] = curValue;
- (*curNum)++;
- if(*curNum == 2N)
- {
- (*curNum) = N;
- }
- }
- }
- //減去的值是上次的平均值
- uint8 moveAverageFilter(uint8 *sum, uint8 curValue, uint8 num, uint8 *curNum)
- {
- if(num <= 0)
- {
- return 0;
- }
- else
- {
- if(*curNum < num)
- {
- ++(*curNum);
- *sum = *sum + curValue;
- return (*sum)/(*curNum);
- }
- else
- {
- *sum = *sum - (*sum)/num;
- *sum = (*sum + curValue);
- return *sum/num;
- }
- }
- }
復制代碼
//-----------------------------------------------------------------------
//5.中位值平均濾波(防脈沖干擾平均濾波法)
//方法:相當于“中位值濾波法”+“算術平均濾波法”
// 連續采樣N個數據,去掉一個最大值和一個最小值然后計算N-2個數據的算術平均值
// N值的選取:3~14
//優點:融合了兩種濾波的優點。對于偶然出現的脈沖性干擾,可消除有其引起的
// 采樣值偏差。對周期干擾有良好的抑制作用,平滑度高,適于高頻振蕩的系統。
//缺點:測量速度慢
- #define N 10
- uint8 middleAverageFilter()
- {
- uint8 i,j;
- uint8 temp,value,sum = 0;
- uint8 value_buf[N];
- for(i = 0; i < N; ++i)
- {
- value_buf[i] = getValue();
- delay();
- }
- //從小到大冒泡排序
- for(j = 0; j < N-1; ++j)
- {
- for(i = 0; i < N-j; ++i)
- {
- if(value_buf[i] > value_buf[i+1])
- {
- temp = value_buf[i];
- value_buf[i] = value_buf[i+1];
- value_buf[i+1] = temp;
- }
- }
- }
- for(i = 1; i < N-1; ++i)
- {
- sum += value_buf[i];
- }
- return sum/(N-2);
- }
復制代碼
//-----------------------------------------------------------------------
//6.遞推中位值濾波法
// 優點:對于偶然出現的脈沖性干擾,可消除由其引起的采樣值偏差。
// 對周期性干擾有良好的抑制作用,平滑度高;
// 試用于高頻振蕩的系統。
// 缺點:測量速度慢
//取最近的10個值,去掉最大最小值求平均
//隊列queue中,第0個值換成新值,其余值依次往后移一個位置
- uint8 recursionMiddleFilter(uint8 newValue, uint8 *queue, uint8 num, uint8 *curNum)
- {
- uint8 max, min, i;
- queue[0] = newValue;
- max = newValue;
- min = newValue;
- sum = newValue;
- if( *curNum < num)
- {
- for(i = 0; i < *curNum; ++i)
- {
- for(i = num-1; i > 0; --i)
- {
- if(queue[i] > max)
- {
- max = queue[i];
- }
- else if(queue[i] < min)
- {
- min = queue[i];
- }
- sum = sum + queue[i];
- queue[i] = queue[i-1];
- }
- sum = sum - max - min;
- return sum/num;
- }
復制代碼
//-----------------------------------------------------------------------
//7.限幅平均濾波法
//方法:相當于“限幅濾波法”+“遞推平均濾波法”
// 每次采樣到的新數據先進行限幅處理再送入隊列進行遞推平均濾波處理
//優點:對于偶然出現的脈沖性干擾,可消除有其引起的采樣值偏差。
//缺點:比較浪費RAM
- #define A 10
- #define N 12
- unsigned char data[];
- unsigned char filter(data[])
- {
- unsigned char i;
- unsigned char value,sum;
- data[N]=GetAD();
- if(((data[N]-data[N-1])>A||((data[N-1]-data[N])>A))
- data[N]=data[N-1];
- //else data[N]=NewValue;
- for(i=0;i<N;i++)
- {
- data[i]=data[i+1];
- sum+=data[i];
- }
- value=sum/N;
- return(value);
- }
復制代碼
//8.一階滯后濾波法
//方法:取a=0~1,本次濾波結果=(1-a)*本次采樣值+a*上次濾波結果
//優點:對周期性干擾具有良好的抑制作用適用于波動頻率較高的場合
//缺點:相位滯后,靈敏度低滯后程度取決于a值大小不能消除濾波頻率高于采樣頻率的1/2的干擾信號
- float a;
- uint8 firstOrderFilter(uint8 newValue, uint8 oldValue)
- {
- return a * newValue + (1-a) * oldValue;
- }
- //使用
- value = getValue();
- while(1)
- {
- value = firstOrderFilter(getValue(),value);
- }
復制代碼
//-----------------------------------------------------------------------
//9.加權遞推平均濾波法(并沒有遞推)
//方法:是對遞推平均濾波法的改進,即不同時刻的數據加以不同的權;
// 通常是,越接近現時刻的數值,權取得越大;
// 給予新采樣值的權系數越大,則靈敏度越高,但信號平滑度越低。
//優點:適用于有較大純滯后時間常數的對象和采樣周期較短的系統
//缺點:對于純滯后時間常數較小,采樣周期較長
// 變化緩慢的信號不能迅速反應系統當前所受干擾的嚴重程度,濾波效果差。
- #define N 10
- uint8 weight[N] = {1,2,3,4,5,6,7,8,9,10};
- uint8 weigth_sum = 1+2+1+2+3+4+5+6+7+8+9+10;
- uint8 weightAverageFilter()
- {
- uint8 value_buf[N];
- uint8 i, sum = 0;
- for(i = 0; i < N; ++i)
- {
- value_buf[i] = getValue();
- delay();
- }
- for(i = 0; i < N; ++i)
- {
- sum += value_buf[i] * weight[i];
- }
- return sum/weight_sum;
- }
復制代碼
//-----------------------------------------------------------------------
//10.消抖濾波法
//方法:比如開關,剛按下去的時候會產生抖動,但是經過一段時間后回到穩態;
// 如果N次后,還不是穩態,就取當前值作為新狀態值
// 設置一個濾波計數器,將每次采樣值與當前有效值比較:
// 如果采樣值=當前有效值,則計數器清零
// 如果采樣值<>當前有效值,則計數器+1,并判斷計數器是否>=上限N(溢出)
// 如果計數器溢出,則將本次值替換當前有效值,并清計數器
//優點:對于變化緩慢的被測參數有較好的濾波效果,可避免在臨界值附近控制器的反復開/關跳動或顯示器上數值抖動
//缺點:對于快速變化的參數不宜
// 如果在計數器溢出的那一次采樣到的值恰好是干擾值,則會將干擾值當作有效值
- uint8 glitchFilter(uint8 oldValue)
- {
- uint8 newValue = getValue();
- uint8 count = 0;
- while(oldValue != newValue)
- {
- count ++;
- if(count >= N)
- {
- return newValue;
- }
- delay();
- newValue = getValue();
- }
- return oldValue;
- }
復制代碼
|