STM32實戰七 數字濾波

數字濾波是數據處理是常用、靈活、有效的方法。前面的按鍵程序已經用到了濾波,屬於開關量濾波,這裏要討論的是模擬量濾波程序,包括最常用的兩種方法,中值濾波和平均值濾波。

中值濾波的原理是,每次取最近幾個數的中間值作爲輸出數據,每個波形的最高和最低幾個數被濾掉,優點是基本保留原有數據,能有效抑制大幅值低頻尖峯干擾,俗稱椒鹽噪聲。

平均值濾波,就是對最近一些數求平均,是最常用最簡單的方法,對高頻低幅值隨機噪聲有效,缺點是會損失原始數據中的高頻分量,對高幅值干擾會擴大影響。下面的程序中應用了移位平均算法,效率高,且不受求平均的數據數目大小的影響。兩種濾波方式都有一定延時。

AverageFilter.h

#ifndef __AVERAGEFILTER__
#define __AVERAGEFILTER__

extern "C" {				// 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368		//消除 warning:  #368-D: class "<unnamed>" defines no constructor to initialize the following:

#include "stm32f10x.h"

#pragma diag_default 368	// 恢復368號警告
}

#define AF_MAXWINDOW 150	// 最大窗寬150

class AverageFilter
{
// Construction
public:
	AverageFilter( s32 ini, u16 nNum );

// Properties
public:
	u16 m_number;				// 指定平均的個數,最多150
	u16 m_seek;					// 遊標
	s32 m_summation;			// 指定數組的總和
	s32 m_input[AF_MAXWINDOW];	// 數據緩存

private:

// Methods
public:
	s32 filter( s32 vi );		// 濾波算法
		
// Overwrite
public:
};

#endif

AverageFilter.cpp

/**
  ******************************************************************************
  * @file		AverageFilter.cpp
  * @author		Mr. Hu
  * @version	V1.0.0 STM32F103VET6
  * @date		06/06/2019
  * @brief		均值濾波
  ******************************************************************************
  * @remarks
  *		採用移位平均法,大大提高效率,不受數據數量的影響
  */ 

/* Includes ------------------------------------------------------------------*/
extern "C" {	// 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368			//消除 warning:  #368-D: class "<unnamed>" defines no constructor to initialize the following:

#pragma diag_default 368	// 恢復368號警告
}

#include "AverageFilter.h"

/**
  * @date	06/06/2019
  * @brief  均值濾波
  * @param	ini 初始值,避免前幾個輸出數偏差太大
  * @param	nNum 濾波數量,最大150,越大效果越好,但延時較多
  * @retval None
*/
AverageFilter::AverageFilter( s32 ini, u16 nNum )
: m_number(nNum)
, m_seek(0)
, m_summation(0)
{
	if( m_number > AF_MAXWINDOW )	// 控制在數組範圍內
		m_number = AF_MAXWINDOW;
	
	// 初始化數組
	for( int i = 0; i < AF_MAXWINDOW; i++ )
	{
		m_input[i] = ini;
	}
	
	// 初始化總和
	m_summation = ini * m_number;
}

/**
  * @date	06/06/2019
  * @brief  濾波算法,採用移位算法,減去第一個,加上最後一個。
  * @param	vi 輸入數據
  * @retval 濾波後輸出數據
*/
s32 AverageFilter::filter( s32 vi )
{
	m_summation += ( vi - m_input[m_seek] );	// 總和中減去最早的數,加上新數
	
	// 用循環方法記錄輸入數據
	assert_param(m_number <= AF_MAXWINDOW);
	m_input[m_seek] = vi;
	if (++m_seek >= m_number)
		m_seek = 0;
	
	// 返回平均值
	return m_summation / m_number;
}

MedianFilter.h

#ifndef __MEDIANFILTER__
#define __MEDIANFILTER__

extern "C" {	// 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368			//消除 warning:  #368-D: class "<unnamed>" defines no constructor to initialize the following:

#include "stm32f10x.h"

#pragma diag_default 368	// 恢復368號警告
}

#define DF_MAXWINDOW 21		// 最大窗寬,奇數,太大了反而不好

class MedianFilter
{
// Construction
public:
	MedianFilter( s32 ini, u16 nNum );

// Properties
public:
	u16 m_dfMedian;				// 半窗寬,最大10
	u16 m_seek;					// 輸入數據指針
	s32 m_input[DF_MAXWINDOW];	// 輸入數據,循環使用,不用移位,提高效率
	s32 m_sort[DF_MAXWINDOW];	// 排序數據,最近輸入的數據排序,取中間值輸出

private:

// Methods
public:
	s32 filter( s32 vi );		// 濾波算法
		
// Overwrite
public:
};

#endif

MedianFilter.cpp

/**
  ******************************************************************************
  * @file		MedianFilter.cpp
  * @author		Mr. Hu
  * @version	V1.0.0 STM32F103VET6
  * @date		06/05/2019
  * @brief		中值濾波
  ******************************************************************************
  * @remarks
  *		中值濾波的原理是,每次取最近幾個數的中間值作爲輸出數據,每個波形的最高和最低
  *	幾個數被濾掉,優點是基本保留原有數據,相當於去掉幾個最高數,去掉幾個最低數,能有
  * 效抑制大幅值低頻尖峯干擾,俗稱椒鹽噪聲。
  */ 

/* Includes ------------------------------------------------------------------*/
extern "C" {	// 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368			//消除 warning:  #368-D: class "<unnamed>" defines no constructor to initialize the following:

#pragma diag_default 368	// 恢復368號警告
}

#include "MedianFilter.h"

/**
  * @date	06/05/2019
  * @brief  中值濾波
  * @param	ini 初始值,前幾個輸出數都是這個值
  * @param	nNum 濾波數,最大10,越大效果越好,但延時較多
  * @retval None
*/
MedianFilter::MedianFilter( s32 ini, u16 nNum )
: m_dfMedian(nNum)
, m_seek(0)
{
	if( m_dfMedian > (DF_MAXWINDOW - 1) / 2 )	// 中值限制在數組範圍內
		m_dfMedian = (DF_MAXWINDOW - 1) / 2;
	
	// 初始化兩個數組
	for( int i = 0; i < DF_MAXWINDOW; i++ )
	{
		m_input[i] = ini;
		m_sort[i] = ini;
	}
}

/**
  * @date	06/05/2019
  * @brief  濾波算法
  * @param	vi 輸入數據
  * @retval 濾波後輸出數據
*/
s32 MedianFilter::filter( s32 vi )
{
	u8 w1 = m_dfMedian * 2;		// 窗寬-1
	
	assert_param(w1 < DF_MAXWINDOW);
	
	// 計算將要移除的值在排序數組中的位置
	u8 j = 0;
	for (; j <= w1 && m_input[m_seek] != m_sort[j]; j++);

	// 移除最早的數據並把新數據插入到適當的位置
	// 如果新數據在較小半段,數據後移,否則數據前移,實現排序
	// 只處理大於和小於情況,等於時不動
	if (vi < m_input[m_seek])
	{	// 向前移
		while (j > 0 && vi < m_sort[j-1])
		{
			j--;
			m_sort[j + 1] = m_sort[j];
		}
	}
	else if (vi > m_input[m_seek])
	{	// 向後移
		while (j < w1 && vi > m_sort[j+1])
		{
			j++;
			m_sort[j - 1] = m_sort[j];
		}
	}

	// 加入新值
	assert_param(j >= 0 && j < w);
	m_sort[j] = vi;

	// 用循環方法記錄輸入數據,高效
	m_input[m_seek] = vi;
	if (++m_seek > w1)
		m_seek = 0;

	// 返回中間值
	return m_sort[m_dfMedian];
}

STM32實戰系列源碼,按鍵/定時器/PWM/ADC/DAC/DMA/濾波
STM32實戰一 初識單片機
STM32實戰二 新建工程
STM32實戰三 C++ IO.cpp
STM32實戰四 定時器和按鍵
STM32實戰五 板載LED顯示數據
STM32實戰六 PWM加移相正交
STM32實戰七 數字濾波
STM32實戰八 DAC/ADC
STM32實戰九 編碼器
STM32開發過程的常見問題

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章