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