完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547
本章节讲解中值滤波器实现,适用于噪声和脉冲的过滤。
目录
48.1 初学者重要提示
48.2 中值滤波器介绍
48.3 中值滤波器原理
48.4 Matlab中值滤波器实现
48.5 中值滤波器设计
48.5.1 函数MidFilterBlock
48.5.2 函数MidFilterRT
48.5.3 宏定义设置 (重要)
48.5.4 整块数据中值滤波测试
48.5.5 逐个数据中值滤波测试 (支持实时滤波)
48.6 实验例程说明(MDK)
48.7 实验例程说明(IAR)
48.8 总结
1、 ARM DSP库没有提供中值滤波器,所以本章的实现是根据中值滤波器原理做了两个函数,一个函数是一块数据的滤波器实现,另一个函数是实时的逐点滤波实现。
中值滤波器是一种非线性数字过滤技术,通常用于消除图像或信号中的噪声。中值滤波器在数字图像处理中被广泛使用。在信号处理中也有应用,通过丢弃所有可疑测量结果来抑制脉冲干扰。有几个输入数据,筛选器计算中值值。
这里我们通过一个实例来理解中值滤波器。比如我们要对如下五个数据求中值:
x = [14 18 16 21 11]
我们将滤波阶数设置为5,即y = medfilt1(x, 5),表示每5个采样值求一次中值。原理和实现如下:
函数是取x(k-2),x(k-1), x(k), x(k+1), x(k+2)的中值作为输出y(k)。对于y(1),只有x(1), x(2), x(3)存在数值,之前的不存在,对于不存在的补0。每5个数按从小到大排列后取中值有:
y(1)的计算是从[0 0 14 16 18]中取中值是14。
y(2)的计算是从[0 14 16 18 21]中取中值是16。
y(3)的计算是从[11 14 16 18 21]中取中值是16。
y(4)的计算是从0 11 16 18 21]中取中值是16。
y(5)的计算是从[0 0 11 16 21]中取中值是11。
首先创建两个混合信号,便于更好测试滤波器效果。
混合信号Mix_Signal_1 = 信号Signal_Original_1+白噪声。
混合信号Mix_Signal_2 = 信号Signal_Original_2+白噪声。
Fs = 1000; %采样率 N = 1000; %采样点数 n = 0:N-1; t = 0:1/Fs:1-1/Fs; %时间序列 Signal_Original_1 =sin(2*pi*10*t)+sin(2*pi*20*t)+sin(2*pi*30*t); Noise_White_1 = [0.3*randn(1,500), rand(1,500)]; %前500点高斯分部白噪声,后500点均匀分布白噪声 Mix_Signal_1 = Signal_Original_1 + Noise_White_1; %构造的混合信号 Signal_Original_2 = [zeros(1,100), 20*ones(1,20), -2*ones(1,30), 5*ones(1,80), -5*ones(1,30), 9*ones(1,140), -4*ones(1,40), 3*ones(1,220), 12*ones(1,100), 5*ones(1,20), 25*ones(1,30), 7 *ones(1,190)]; Noise_White_2 = 0.5*randn(1,1000); %高斯白噪声 Mix_Signal_2 = Signal_Original_2 + Noise_White_2; %构造的混合信号
滤波代码实现如下:
%**************************************************************************************** % % 信号Mix_Signal_1 和 Mix_Signal_2 分别作中值滤波 % %*************************************************************************************** %混合信号 Mix_Signal_1 中值滤波 Signal_Filter=medfilt1(Mix_Signal_1,10); subplot(4,1,1); %Mix_Signal_1 原始信号 plot(Mix_Signal_1); axis([0,1000,-5,5]); title('原始信号 '); subplot(4,1,2); %Mix_Signal_1 中值滤波后信号 plot(Signal_Filter); axis([0,1000,-5,5]); title('中值滤波后的信号'); %混合信号 Mix_Signal_2 中值滤波 Signal_Filter=medfilt1(Mix_Signal_2,10); subplot(4,1,3); %Mix_Signal_2 原始信号 plot(Mix_Signal_2); axis([0,1000,-10,30]); title('原始信号 '); subplot(4,1,4); %Mix_Signal_2 中值滤波后信号 plot(Signal_Filter); axis([0,1000,-10,30]); title('中值滤波后的信号');
Matlab运行效果:
本章的实现是根据中值滤波器原理做了两个函数,一个函数是一块数据的滤波器实现,另一个函数是实时的逐点滤波实现。
函数原型:
void MidFilter(float32_t *pSrc, float32_t *pDst, uint32_t blockSize, uint32_t order)
函数描述:
这个函数用于一段数据的中值滤波。
函数参数:
函数定义如下:
void MidFilterRT(float32_t *pSrc, float32_t *pDst, uint8_t ucFlag, uint32_t order)
函数描述:
这个函数用于逐个数据的实时滤波。
函数参数:
用到两个宏定义,大家根据自己的应用进行设置:
#define TEST_LENGTH_SAMPLES 1024 /* 采样点数 */
#define MidFilterOrder 16 /* 滤波阶数 */
第1个宏定义:采样点数用于整块数据滤波,一次性滤波的点数。
第2个宏定义:设置滤波阶数。
适用于分段数据滤波,测试波形是由原始信号+高斯白噪声+均匀白噪声。
/* ********************************************************************************************************* * 函 数 名: MidFilterBlockTest * 功能说明: 整块数据滤波测试 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void MidFilterBlockTest(void) { MidFilterBlock((float32_t *)&testdata[0], &DstDate[0], TEST_LENGTH_SAMPLES, MidFilterOrder); for(int i = 0; i < TEST_LENGTH_SAMPLES; i++) { printf("%f, %f\r\n", testdata[i], DstDate[i]); } }
滤波器效果,红色是原始波形,杏黄色是滤波后效果:
适用于逐个数据的实时滤波,测试波形是由原始信号+高斯白噪声+均匀白噪声。
/* ********************************************************************************************************* * 函 数 名: MidFilterOneByOneTest * 功能说明: 逐个数据滤波测试 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void MidFilterOneByOneTest(void) { float32_t *inputF32, *outputF32; inputF32 = (float32_t *)&testdata[0]; outputF32 = &DstDate[0]; /* 从头开始,先滤第1个数据 */ MidFilterRT(inputF32 , outputF32, 1, MidFilterOrder); /* 逐次滤波后续数据 */ for(int i = 1; i < TEST_LENGTH_SAMPLES; i++) { MidFilterRT(inputF32 + i , outputF32 + i, 0, MidFilterOrder); } for(int i = 0; i < TEST_LENGTH_SAMPLES; i++) { printf("%f, %f\r\n", testdata[i], DstDate[i]); } }
滤波器效果,红色是原始波形,杏黄色是滤波后效果:
配套例子:
V7-233_中值滤波器实现,适用于噪声和脉冲过滤(支持逐点实时滤波)
实验目的:
实验内容:
使用AC6注意事项
特别注意附件章节C的问题
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
RTT方式打印信息:
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIC优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到400MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ bsp_InitTimer(); /* 初始化滴答定时器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ bsp_InitLed(); /* 初始化LED */ }
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
/* ********************************************************************************************************* * 函 数 名: MPU_Config * 功能说明: 配置MPU * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU属性为关闭读Cache和写Cache */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT _BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT _CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 数 名: CPU_CACHE_Enable * 功能说明: 使能L1 Cache * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程序实现如下操作:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参: 无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按键代码 */ uint16_t i; bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程信息到串口1 */ PrintfHelp(); /* 打印操作提示信息 */ bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */ /* 进入主程序循环体 */ while (1) { bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */ if (bsp_CheckTimer(0)) /* 判断定时器超时时间 */ { /* 每隔100ms 进来一次 */ bsp_LedToggle(2); /* 翻转LED的状态 */ } ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1键按下,整块数据滤波测试 */ MidFilterBlockTest(); break; case KEY_DOWN_K2: /* K2键按下,逐个数据滤波器测试 */ MidFilterOneByOneTest(); break; default: /* 其它的键值不处理 */ break; } } } }
配套例子:
V7-233_中值滤波器实现,适用于噪声和脉冲过滤(支持逐点实时滤波)
实验目的:
实验内容:
使用AC6注意事项
特别注意附件章节C的问题
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
RTT方式打印信息:
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIC优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到400MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ bsp_InitTimer(); /* 初始化滴答定时器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ bsp_InitLed(); /* 初始化LED */ }
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
/* ********************************************************************************************************* * 函 数 名: MPU_Config * 功能说明: 配置MPU * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 数 名: CPU_CACHE_Enable * 功能说明: 使能L1 Cache * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程序实现如下操作:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参: 无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按键代码 */ uint16_t i; bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程信息到串口1 */ PrintfHelp(); /* 打印操作提示信息 */ bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */ /* 进入主程序循环体 */ while (1) { bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */ if (bsp_CheckTimer(0)) /* 判断定时器超时时间 */ { /* 每隔100ms 进来一次 */ bsp_LedToggle(2); /* 翻转LED的状态 */ } ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1键按下,整块数据滤波测试 */ MidFilterBlockTest(); break; case KEY_DOWN_K2: /* K2键按下,逐个数据滤波器测试 */ MidFilterOneByOneTest(); break; default: /* 其它的键值不处理 */ break; } } } }
本章节主要讲解了中值滤波器的实现,非常时候噪声滤除场景。