目录
一、冒泡排序
二、平均滤波
1.中位值平均滤波法
2.递推平均滤波法
3.算术平均滤波法
一、冒泡排序
1.定义
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来,直重复这个过程,直到没有任何两个相邻元素可以交换,就表明完成了排序。
假设需要排列的数组为a[4]={7,23,6,8}
用冒泡排序对这四个数排序,需要进行(4-1)轮比较,每轮都对相邻的两个元素进行排序。第一轮可以将最大元素,并入已排序序列中;第一轮可以将第二大元素,并入已排序序列中;以此类推,直到待排序序列中仅剩 1 个元素,无论再进行相邻元素的比较,因此直接将其并入已排序序列中。
例:
#include <stdio.h>
//交换 a 和 b 的位置的函数
#define N 5
int a[N] = { 5,1,4,2,8 };
void swap(int *a, int *b);
//这是带输出的冒泡排序实现函数,从输出结果可以分析冒泡的具体实现流程
void BubSort_test();
//这是不带输出的冒泡排序实现函数,通过此函数,可直接对数组 a 中元素进行排序
void BubSort_pro();
int main()
{
BubSort_test();
return 0;
}
void swap(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
//这是带输出的冒泡排序实现函数,从输出结果,可以看到冒泡的具体实现流程
void BubSort_test() {
for (int i = 0; i < N; i++) {
//对待排序序列进行冒泡排序
for (int j = 0; j + 1 < N - i; j++) {
//相邻元素进行比较,当顺序不正确时,交换位置
if (a[j] > a[j + 1]) {
swap(&a[j], &a[j + 1]);
}
}
//输出本轮冒泡排序之后的序列
printf("第%d轮冒泡排序:", i + 1);
for (int i = 0; i < N; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
}
//这是不带输出的冒泡排序实现函数,通过此函数,可直接对数组 a 中元素进行排序
void BubSort_pro() {
for (int i = 0; i < N; i++) {
//对待排序序列进行冒泡排序
for (int j = 0; j + 1 < N - i; j++) {
//相邻元素进行比较,当顺序不正确时,交换位置
if (a[j] > a[j + 1]) {
swap(&a[j], &a[j + 1]);
}
}
}
}
运行结果为:
第1轮冒泡排序:1 4 2 5 8
第2轮冒泡排序:1 2 4 5 8
第3轮冒泡排序:1 2 4 5 8
第4轮冒泡排序:1 2 4 5 8
第5轮冒泡排序:1 2 4 5 8
二、平均滤波
1.中位值平均滤波法
说明:中位值滤波算法特别适用于会偶然出现异常值的系统。
优点:相比于平均值滤波算法,中位值滤波算法能够有效滤除偶然的脉冲干扰。
缺点:与平均值滤波算法相同,中位值滤波算法也存在反应速度慢、滞后的问题。
方法①
1、连续采样N次(N取奇数)
2、把N次采样值按大小排列(多采用冒泡法)
3、去掉最大最小值取平均值为本次有效值
float data[10];
float middleFilter(float in_data)
{
float sum = 0;
float temp[10];
float change;
int i,j;
//记录数据
for(i=0; i<9; i++)
{
data[i]=data[i+1];
}
data[9] = in_data;
//复制数据
for(i=0; i<10; i++)
temp[i] = data[i];
//冒泡法排序
for(i=1; i<10; i++)
for(j=0; j<10-i; j++)
{
if(temp[j] > temp[j+1])
{
change = temp[j];
temp[j] = temp[j+1];
temp[j+1] = change;
}
}
//求和
for(i=2; i<9; i++)
sum = sum + temp[i];
//返回平均值
return(sum/8);
}
方法②
1.找出最大最小值
2.去掉最小最大值
3.求平均值
void MindiaValnafiltering(u16 *buf,u8 len)
{
unsigned short int max,min;
unsigned short long sum;
unsigned char i;
max=buf[0];
min=max;
sum=max+((len-2)>>1)
for(i=1;i<len;i++)
{
if(max<buf[i]) max=buf[i];
if(min>buf[i]) min=buf[i];
sum=sum+buf[i];
}
return MindiaVal=(sum-max-min)/(len-2)
}
下面比较一下中值滤波和算术平均值滤波的差异
红色曲线为中值滤波后的曲线
红色曲线为算术平均滤波后的曲线
2.递推平均滤波法(又称滑动平均滤波法)
队列定义:队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
方法
1、把连续取N个采样值看成一个队列,队列的长度固定为N
2、每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据.(先进先出原则)
3、把队列中的N个数据进行算术平均运算,就可获得新的滤波结果(以保证队列中的N个数据都为最新数据)
4、N值的选取:流量,N=12;压力:N=4;液面,N=4~12;温度,N=1~4;
优点:
1、对周期性干扰有良好的抑制作用
2、平滑度高
缺点:
1、灵敏度低
2、对偶然出现的脉冲性干扰的抑制作用较差
3、不易消除由于脉冲干扰所引起的采样值偏差
4、不适用于脉冲干扰比较严重的场合
5、比较浪费RAM
#define TARGET_tYPE int
#define N 8
//队列
TARGET_tYPE value_buf[N];
//初始化队列
void Init_Queue()
{
for(int i = 0; i < N; i++)
{
value_buf[i] = 0;
}
}
//插入队列
void Add_Queue(TARGET_tYPE value)
{
//数组元素右移
for(int i = N-1; i > 0; i--)
{
value_buf[i] = value_buf[i-1];
}
//插入新元素
value_buf[0] = value;
}
//求队列和
TARGET_tYPE Sum_Queue()
{
TARGET_tYPE ADDER = 0;
for(int i = 0; i < N; i++)
{
ADDER = ADDER + value_buf[i];
}
return ADDER;
}
TARGET_tYPE Smoothing_arithmetic_mean_FILTER()
{
//采集数据插入队列
Add_Queue(get_data());
return Sum_Queue()/N;
}
3.算术平均滤波法
方法
1、连续取N个采样值进行算术平均运算
2、N值较大时:信号平滑度较高,但灵敏度较低
3、N值较小时:信号平滑度较低,但灵敏度较高
4、N值的选取:一般流量,N=12;压力:N=4;
优点:
对滤除混杂在被测信号上的随机干扰信号非常有效。这样信号的特点是有一个平均值,信号在某一数值范围附近上下波动
缺点:
1、不易消除脉冲干扰引起的误差。
2、对于采样速度较慢或要求数据更新率较高的实时控制不适用
3、比较浪费RAM
#define N 12
char value_buf[N];
char i=0;
char filter()
{
char cnt;
int sum=0;
value_buf[i++] = get_ad();
if ( i == N ) i = 0;
for ( cnt=0;cnt < N-1;cnt++)
sum = value_buf[cnt];
return (char)(sum/N);
}