对一阶二阶低通滤波器推导,并用IMU数据验证算法效果

article/2025/10/21 11:10:23

文章背景

一直想学习一下数字信号处理算法,而不是每次遇到数据处理就求平均,求最值,看容差,做滑动窗。。。
数字信号处理算法已经很成熟了,但网上大部分还是用matlab跑仿真,设计几个不同频率的sin信号相加,来验证算法的有效性。但是实际工程中该如何去使用这个算法,资料还是比较少的,这篇文章重心放在实际工程,对于工程中遇到的常见信号做处理。

一阶算法推导

学习一个算法,掌握其思想和推导是必不可少的路。这里先以一阶低通滤波算法为例,在硬件电路中我们可以通过RC电路实现低通滤波功能,利用的便是电容原件的充放电特性。在代码里如何实现这个硬件电路呢?

这里推导大体思路如下:

  1. 根据物理模型(也就是电路状态变量之间的关系)推导出电路的时域关系和频域关系,也就是电路的传递函数H(s)。
  2. 有了传递函数H(s)就可以通过一阶差分方程(双线性变换)将函数从s域变换到z域,对应的含义便是将函数从连续的频域转化为离散的频域。
  3. 这个时候我们得到了H(z),然后通过z变换求出关于X(输入)和Y(输出)的离散时域函数,最终我们调用的便是这个函数。

具体的推导过程懒得手写,这里直接上图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

matlab 验证算法

首先我们通过stm32抓取一组IMU数据,这里IMU用的是博世的BMI160,先只抓取z轴的重力加速度。这里上一下matlab代码:

// 
//	create by wxc on 2021.5.9
//  e-mail:550791785@qq.com
//>> clear
>> fs  = 200;
>> dt = 1 / fs;
>> N = length(acc_z);
>> n = 5240;
>> y = fft(acc_z,n);
>> y_abs = abs(y);
>> f = (0:n-1)*fs/n;
>> subplot(211);plot(acc_z);axis([0 6000 9.9 10.1]);
>> subplot(212);plot(f(1:n/2),y_abs(1:n/2));axis([0 100 0 5]);

这里只是仿真了原始数据的波形和对应的幅频曲线。
在这里插入图片描述

由图中可以看出,在35-40Hz,45-50Hz,55Hz均由较大的扰动需要滤除,幅值较低的可以认为是白噪声干扰。

这里我们添加一个截止频率为15Hz的低通滤波器,看一下效果

>> y_lowpass = fft(Mix_LowPass,5240);
>> y_lowpass_abs = abs(y_lowpass);
>> lpf0 = 0;
>> lpf1 = 0;
>> for a = 1:1:length(acc_z)
lpf1 = lpf0+(1 / (1 + 1 / (2.0 * 3.14 * (1 / fs) * 15))) * (acc_z(a) - lpf0);
lpf0 = lpf1;
Mix_LowPass(a) = lpf1;
end
>> subplot(211);plot(Mix_LowPass);axis([0 6000 9.9 10.1]);
>> subplot(212);plot(f(1:n/2),y_lowpass_abs(1:n/2);axis([0 100 0 30]);

在这里插入图片描述

可以明显看到高频噪声被抑制了,而且原始波形也能看出在相同尺度下,振幅明显减小。这里波形稍微有点向上倾斜是因为采集数据的时候手拿着难免有移动,并且为了制造噪声干扰我还拍了几下桌子。

二阶算法推导

二阶低通滤波器的推导思路基本一致,就是由电路推导出传递函数这一步我们直接查表获取,具体步骤见图:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

matlab 验证算法

对于加速度计z轴的原始数据图形这里不再展示,下面直接上matlab二阶低通滤波器处理后的时域波形和频域的幅频特性曲线。

>> fc = 15;
>> fs = 200;
>>> n = 5240;
>> f = (0:n-1)*fs/n;
>> [B,A] = butter(2,(2*pi*fc*2)/(2*pi*fs));
>> my_iir_2_low_pass = filter(B,A,acc_z);
>> subplot(211);plot(my_iir_2_low_pass);axis([0 6000 9.9 10.1]);
>> y_iir2 = fft(my_iir_2_low_pass); 
>> y_iir2_abs = abs(y_iir2);
>> subplot(212);plot(f(1:n/2),y_iir2_abs(1:n/2));axis([0 100 0 50]);

在这里插入图片描述
这里直接用matlab自带的巴特沃斯滤波器求出iir二阶低通滤波器系数,然后对原始数据做滤波。

然后我们用刚才推导的公式去求取滤波后的幅频曲线和原始图形

>> fs = 200;
>> fc = 15;
>> fr = fs / fc;
>> MY_PI = 3.1415926;
>> ohm = tan(MY_PI/fr);
>> c = 1.0 + 2 * cos(MY_PI/4)*ohm+ohm*ohm;
>> b0 = ohm * ohm / c;
>> b1 = 2 * b0;
>> b2 = b0;
>> a1 = 2 * (ohm * ohm - 1) / c; 
>> a2 = (1 - 2 * cos(MY_PI / 4) * ohm + ohm * ohm ) / c;
>> delay_1 = 0;
>> delay_2 = 0; 
>> for i = 1 : 1 : length(acc_z)
delay_element_0 = acc_z(i) - delay_1 * a1 - delay_2 * a2;
out = delay_element_0 * b0 + delay_1 * b1 + delay_2 *b2;
delay_2 = delay_1;
delay_1 = delay_element_0;
acc_z_lowpass(i)=out;
end
>> mag_y = abs(fft(acc_z_lowpass,length(acc_z)));
>> f = (0:length(acc_z)-1)*fs/length(acc_z);
>> subplot(211);plot(acc_z_lowpass);axis([0 6000 9.9 10.1]);
>> subplot(212);plot(f(1:length(acc_z)/2),mag_y(1:length(acc_z)/2));axis([0 100 0 40]);

其求出图形如下:
在这里插入图片描述
这里可以看出求出的图形和matlab推导出来的图形基本一致,证明算法的有效性!!!

这里用stm32实现这个滤波器,并把原始波形和滤波波形打印出来对比滤波前后效果。如下图所示:

这个波形是IMU平放桌子上,拍打几次桌子,观察震动波形
在这里插入图片描述
下图这个是晃动观察波形跟随性
在这里插入图片描述
放大这个图片可以看出,波形相比原信号少了很多毛刺部分,但是确实有几个周期的滞后现象。
在这里插入图片描述

总结

iir滤波器全称为无限长冲击响应滤波器,如它的名字而已,这个滤波器的结果与信号整个的生命周期相关,同时也与滤波器本身的输出有关。这个特性可以理解成PID控制器的中积分器,优点是冲击信号不易对系统产生较大影响,缺点是会把每次冲击都引入滤波器中,如果冲击量大,则滤波效果较差。这个特性还可以使滤波后的信号更加平滑,即最大平坦度。

fir滤波器全称为有限长冲击响应滤波器,也即它滤波的结果只与最近的几次结果有关,缺点就是滤波后的结果会有旁瓣产生,而且无论通带还是阻带都有旁瓣,对原信号还原性没有iir那么好。我们平时代码中使用的求平均,比如采集5个数据取平均,就是某一个截止频率的fir滤波器,加权平均也是如此,因为它们的结果都与最近的几个数据相关。


http://chatgpt.dhexx.cn/article/Fxvxbwe6.shtml

相关文章

二阶低通滤波器IIR的五个参数推导过程

最近在研究飞控代码看到了二阶低通滤波器IIR的软件代码,由于之前没有了解过二阶滤波器的原理,对代码十分懵逼,经过学习,特将学习成果发布,有兴趣的小伙伴看看,如果哪里不对,请提出来&#xff0c…

利用MATLAB生成软件二阶低通滤波器(绝对靠谱)

利用MATLAB生成软件二阶低通滤波器 嵌入式的软件滤波中,除了均值和限幅,我们常用的也就是一阶或者二阶,这些都是可以通过c语言代码来实现的。其过程包括分母A和分子B参数的求取,再者通过公式得出想要的输出结果。 文章目录 利用MA…

第八篇,滤波:二阶低通滤波、卡尔曼滤波

目录 1.引言 2.理解与demo 2.1 二阶低通滤波 2.1.1 LP_2Order的个人理解 2.1.2 refer 2.1.3 demo 2.2 卡尔曼滤波 2.2.1 理解 2.2.2 refer 2.2.3 卡尔曼滤波的几个tips 2.2.4 demo 3.其它 1.引言 相比于上一篇,这篇会写的简单很多,可能因为…

【模电】0007 有源滤波器2(二阶有源滤低通波器)

滤波器的阶数越高,则可以得到越陡峭的频率响应,使得在通带内尽量不衰减信号,而在阻带内尽可能多地衰减信号。 上一节我们讲的一阶有源滤波器,在对数坐标系上,其阻带内的衰减是20dB/十倍频程;如果想要更好的…

什么是二阶滤波器?有什么优点?

原文来自公众号:工程师看海 滤波器是常见的信号调理电路,其中低通滤波器最为普遍,我们常听说一阶滤波器、二阶滤波器,二者有什么差别呢? 低通滤波器有3个重要参数:通带、阻带和过度带,理想的滤…

【滤波器】5. 三种类型二阶低通滤波器

滤波器的品质因数 Q,也称为滤波器的截止特性系数。其值决定于 f f 0 ff_0 ff0​ 附近的频率特性。按照 f f 0 ff_0 ff0​ 附近频率特性的特点,可将滤波器分为 巴特沃斯 ( Butterworth)、切比雪夫(Chebyshev) 和 贝塞尔(Bessel) 三种类型。下图是这三种…

二阶低通有源滤波器设计与仿真测试

前言 传感器输出的测量信号中,除了有用的信息外,往往还包含许多噪声以及其他与被测量无关的信号,从而影响测量精度。这冲噪声般随机性很强,难于从时域中直分离出来,但限于其产生的物理机理、噪声功率是有限的&#xf…

带农历万年历C语言程序,c语言万年历程序代码

本篇文章介绍了使用c语言实现万年历程序的代码,希望对学习c语言的朋友有帮助! c语言万年历程序代码 C语言实现万年历程序的代码如下:#include int year(int y) { if ((y%40) && (y%100!0) || y%4000) return 366; else return 365; …

Java万年历程序

【程序说明】该程序实现了输出任意一年的日历,并输出该年中任意一天是该年的第几周。 【规定】①闰年366天,2月29天;平年365天,2月28天。 ②周日为某月的第一天,周六为该月的最后一天。 ③某年的1月1号为该年的第一天…

Java 实现万年历

通过Java的基本语法来实现我们的万年历 请看代码 public class CalDate{public static void main(String[] args){Scanner sc new Scanner(System.in);System.out.println("请输入年:");int year sc.nextInt();System.out.println("请…

C++万年历程序

C万年历 文章目录 C万年历一、运行结果二、源代码 一、运行结果 二、源代码 #include<iostream> #include<stdio.h> #include<string.h> #include<iomanip> using namespace std;class Calendar{ public :Calendar(){TotalDays 0;MonthDays 0;};voi…

编写万年历程序时的一些意外收获

前些天在CSDN每日一练上做到了万年历程序的一道题&#xff0c;觉得很有意思&#xff0c;于是便尝试自己写写看&#xff0c;结果遇到了“公元1年1月1日是星期几”这个问题。拿着手机翻华为日历翻了半天&#xff0c;找到了这一天是星期六&#xff1a; 然而我发现&#xff0c;根据…

C语言编写小程序——万年历

一、杂谈 大一学了C之后一直困惑&#xff0c;C到底怎么用&#xff1f;它不像HTML那么直观&#xff0c;也没有SQL那么常用&#xff0c;更没有Java那么功能强大&#xff0c;那他为何还存在&#xff0c;并依然火热呢&#xff1f; 答案很简单&#xff1a;编程语言是一家&#xff0c…

前端:运用js制作一个万年历程序

前端&#xff1a;运用js制作一个万年历程序 1.HTML代码 首先&#xff0c;依旧是一个套路&#xff0c;先写HTML代码&#xff0c;就好比如建一座楼先建地基和楼的结构一样。 外部这个class属性值为time的div标签是为了让整个内容处于居中地位。 class属性值为head1的div标签是…

用Java写一个万年历程序

从控制台输入指定年份&#xff08;在1900年至2099年之内&#xff09;和月份&#xff0c;输出当月的日历。要求效果如下图所示 思路分析如下&#xff1a; 假设输出2020年5月份的日历。那么要求得1900年1月1日到2020年5月1日前一天的天数总和 再求总天数余7的值&#xff0c;结果…

C++编写万年历,公元后日历程序,考虑了1582年前后以及该年的特殊情况。

目录 一&#xff0c;万年历的基本要求前言&#xff1a;万年历的创建思路1.公元天数与日期1.日期输入问题2.闰年与平年的数目 3.日历的输出问题1.日数转化为星期2.日历月份开头 二&#xff0c;万年历正确性的额外补丁1.1582年闰年定义的变更解决2.1582年消失的十天 三&#xff0…

C语言 万年历 三种版本

C语言 万年历 万年历 一、第1版&#xff1a; 制作一个万年历&#xff08;阳历版&#xff09;。程序从键盘读入年份和月份&#xff0c;然后输出该年该月的月历。 【实现提示】&#xff1a; 本问题的关键是确定所求月份的第一天是星期几。如我们想确定2009年12月1日是星期几&a…

编制万年历的历程

初入编程之道的学子大都试编过万年历。万年历有二种&#xff1a;一为只有西历的月历&#xff0c;另一为有农历对照的月历或日历。编写万年历程序可以练练手&#xff0c;加深对编程语言的理解。记得我初入此道是在1994年&#xff0c;我那时刚买了486电脑&#xff0c;也刚开始有视…

显示万年历的程序(汇编语言实现,附源代码)

运行环境&#xff1a;Masm for Windows 集成实验环境 2015 一、课题内容和要求 课题内容&#xff1a; 用汇编语言编写一个有简单界面显示的日历&#xff0c;要求输入年月日后&#xff0c;将该月的完整日历显示出来&#xff0c;包括星期几&#xff0c;且每月的星期六&#xff…

c语言编写万年历程序

这个程序最核心的地方在于计算当前日期是周几&#xff0c;然后才好显示万年历&#xff0c;因为输入只知道月&#xff0c;所以默认是1号。 通过这个日期我们就可以计算总天数&#xff0c;通过总天数进行%7运算&#xff0c;就能得到周几。 通过这个周几&#xff0c;在结合这个月有…