当前位置: 首页 > news >正文

C/C++编程-算法学习-数字滤波器

数字滤波器

  • 一阶低通滤波器
    • 结论
    • 推导1
      • 1. 基本公式推导
      • 2. 截止频率 和 采样频率 推导
    • 实现
  • 二阶低通滤波器
    • 实现1
    • 实现2
    • 推导1
    • 推导2

一阶低通滤波器

结论

其基本原理基于以下公式:
o u t p u t [ n ] = α ∗ i n p u t [ n ] + ( 1 − α ) ∗ o u t p u t [ n − 1 ] output[n] = α * input[n] + (1 - α) * output[n - 1] output[n]=αinput[n]+(1α)output[n1]

  • output[n] 是当前的输出值
  • input[n] 是当前的输入值
  • α是滤波系数,取值范围在 0 到 1 之间。α值越大,对输入的响应越迅速,但滤波效果相对较弱;α值越小,滤波效果越强,但对输入的响应越慢
  • output[n - 1] 是上一次的输出值
    例如,如果 alpha = 0.1,输入值在短时间内快速变化,由于 (1 - alpha) 的权重较大,上一次的输出值对当前输出值的影响较大,从而起到平滑和抑制高频变化的作用。

推导1

1. 基本公式推导

对应电路模型一阶RC滤波器
Y ( s ) / X ( s ) = H ( s ) = 1 r c s + 1 = 1 r ⋅ j w c + 1 = 1 τ s + 1 Y(s)/X(s)=H(s) = \frac{1}{rcs +1} = \frac{1}{r·jwc + 1} =\frac{1}{\tau s + 1} Y(s)/X(s)=H(s)=rcs+11=rjwc+11=τs+11
在自控中称为一阶惯性环节
转成时域方程
X ( s ) = Y ( s ) / H ( s ) = Y ( s ) ( τ s + 1 ) X(s) = Y(s)/H(s) = Y(s)(\tau s +1) X(s)=Y(s)/H(s)=Y(s)(τs+1)
x ( t ) = τ y ′ ( t ) + y ( t ) x(t) = \tau y'(t) + y(t) x(t)=τy(t)+y(t)
将导数拆开(使用一阶后向差分法,对上面微分方程进行离散化)
x ( t ) = τ ( y ( t ) − y ( t − T ) T ) + y ( t ) x(t) = \tau (\frac {y(t) - y(t-T)}{T}) + y(t) x(t)=τ(Ty(t)y(tT))+y(t)
整理成可递归迭代函数
y ( t ) = ( 1 − T T + τ ) ⋅ y ( t − T ) + T T + τ x ( t ) y(t) = (1-\frac {T}{T+\tau})·y(t-T) + \frac{T}{T+\tau}x(t) y(t)=(1T+τT)y(tT)+T+τTx(t)
a = T T + τ a = \frac{T}{T+\tau} a=T+τT 则可得一般表达式
y ( t ) = ( 1 − a ) y ( t − T ) + a x ( t ) y(t) = (1-a)y(t-T)+ax(t) y(t)=(1a)y(tT)+ax(t)

2. 截止频率 和 采样频率 推导

从模电课本即可知,截止频率处幅值衰减-3db。

f c = 1 2 π R C = 1 2 π τ f_c = \frac{1}{2\pi RC} = \frac{1}{2\pi \tau} fc=2πRC1=2πτ1
τ = 1 2 π f c \tau = \frac{1}{2\pi f_c} τ=2πfc1带入滤波器系数 a a a 可得:
a = T T + τ = 1 1 + f 2 π f c a = \frac{T}{T+ \tau} = \frac{1}{1+\frac{f}{2\pi f_c}} a=T+τT=1+2πfcf1
其中, f = 1 T f = \frac{1}{T} f=T1为采样频率。

实现

#include <stdio.h>
#include <stdlib.h>// 一阶低通滤波器函数
float lowPassFilter(float input, float prevOutput, float alpha) {return alpha * input + (1 - alpha) * prevOutput;
}int main() {float input = 10.0;  // 输入值float prevOutput = 5.0;  // 上一次的输出值float alpha = 0.2;  // 滤波系数for(int i=0; i<20; i++){float output = lowPassFilter(input, prevOutput, alpha);prevOutput = output;printf("filter current result: %f\n", output);}system("pause");return 0;
}

运行结果
在这里插入图片描述

二阶低通滤波器

实现1

#include <stdio.h>
// #include </lib/gcc/x86_64-linux-gnu/9/math.h>
#include <math.h>// 二阶低通滤波器参数
#define SAMPLING_FREQ 1000  // 采样频率
#define CUTOFF_FREQ 100  // 截止频率// 计算滤波器系数
void calculateFilterCoefficients(double *a, double *b) {double omega = 2 * M_PI * CUTOFF_FREQ / SAMPLING_FREQ;double alpha = sin(omega) / (2 * 0.707);double beta = cos(omega);double a0 = 1 + alpha;double a1 = -2 * beta;double a2 = 1 - alpha;double b0 = (1 - beta) / 2;double b1 = 1 - beta;double b2 = (1 - beta) / 2;*a = a0;*(a + 1) = a1;*(a + 2) = a2;*b = b0;*(b + 1) = b1;*(b + 2) = b2;
}// 二阶低通滤波函数
double lowPassFilter(double input, double *prevInputs, double *prevOutputs, double *a, double *b) {double output = *b * input + *b * prevInputs[0] + *b * prevInputs[1] - *a * prevOutputs[0] - *a * prevOutputs[1];prevInputs[1] = prevInputs[0];prevInputs[0] = input;prevOutputs[1] = prevOutputs[0];prevOutputs[0] = output;return output;
}int main() {double a[3], b[3];calculateFilterCoefficients(a, b);double prevInputs[2] = {0};double prevOutputs[2] = {0};double input = 10;  // 输入值,可根据实际情况修改double filteredOutput = lowPassFilter(input, prevInputs, prevOutputs, a, b);printf("滤波后的输出: %f\n", filteredOutput);return 0;
}/**
如果你在使用gcc编译含数学函数的 C 程序时,出现undefined reference to 'sin'、undefined reference to 'cos'等错误,一般是由于缺少库造成的。因为在 Ubuntu 系统中,gcc的数学函数(如sin、cos等)是定义在libm.so里面的,而数学库不在默认路径下。通过添加-lm选项,就可以告诉编译器到正确的库中查找这些函数。注意:在使用cmake进行编译时,需要添加命令target_link_libraries(your_target_name m)来链接数学库,其中your_target_name是你的目标名称。
*/

经我实际验证ubuntu20,的math库在如下路径
dpkg -l | grep math
在这里插入图片描述

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

实现2

#include <stdio.h>
#include <math.h>// 二阶低通滤波器系数
typedef struct {double a0, a1, a2, b1, b2;
} FilterCoefficients;// 计算二阶低通滤波器系数
void calculateFilterCoefficients(double cutoffFrequency, double samplingFrequency, FilterCoefficients *coefficients) {double omega = 2.0 * 3.14159 * cutoffFrequency / samplingFrequency;double cosOmega = cos(omega);double sinOmega = sin(omega);double alpha = sinOmega / (2.0 * 0.707);double a0 =  1 + alpha;double a1 = -2 * cosOmega;double a2 =  1 - alpha;double b1 = -2 * cosOmega;double b2 =  1 - alpha;coefficients->a0 = 1.0 / a0;coefficients->a1 = a1 / a0;coefficients->a2 = a2 / a0;coefficients->b1 = b1 / a0;coefficients->b2 = b2 / a0;
}// 二阶低通滤波器函数
void secondOrderLowPassFilter(double input[], double output[], int length, FilterCoefficients coefficients) {output[0] = input[0];output[1] = coefficients.a0 * input[1] + coefficients.a1 * input[0] + coefficients.b1 * output[0];for (int i = 2; i < length; i++) {output[i] = coefficients.a0 * input[i] + coefficients.a1 * input[i - 1] + coefficients.a2 * input[i - 2]- coefficients.b1 * output[i - 1] - coefficients.b2 * output[i - 2];}
}int main() {double cutoffFrequency = 10.0;  // 截止频率double samplingFrequency = 50.0;  // 采样频率FilterCoefficients coefficients;calculateFilterCoefficients(cutoffFrequency, samplingFrequency, &coefficients);double input[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};double output[10];int length = 10;secondOrderLowPassFilter(input, output, length, coefficients);for (int i = 0; i < length; i++) {printf("Output[%d] = %f\n", i, output[i]);}return 0;
}

实验结果:

在这里插入图片描述

推导1

二阶数字低通滤波器的时域上的开环传递函数为:
G ( s ) = w c 2 s 2 + 2 ξ w c s + w c 2 G(s) = \frac{w_c^2}{s^2+2\xi w_c s + w_c^2} G(s)=s2+2ξwcs+wc2wc2
其中 w c w_c wc是低通滤波器的截止频率, ξ \xi ξ为阻尼比。
采用双线性变换法 将 线性非时变系统 在 连续时域系统的传递函数 转换 成线性且平移不变滤波器 在 离散时域的传递函数。
双线性变换法: S = 2 T Z − 1 Z + 1 S = \frac{2}{T}\frac{Z-1}{Z+1} S=T2Z+1Z1
可得
G ( s ) = w c 2 T s w 2 ( z 2 + 2 z + 1 ) z 2 ( w c 2 T s w 2 + 4 ξ w c T s w + 4 ) + z ( 2 w c 2 T s w 2 − 8 ) + ( w c 2 T s w 2 − 4 ξ w c T s w + 4 ) G(s) = \frac{w_c^2T_{sw}^2(z^2+2z+1)}{z^2(w_c^2T_{sw}^2 + 4\xi w_cT_{sw}+4)+z(2w_c^2T_{sw}^2-8)+(w_c^2T_{sw}^2-4\xi w_c T_{sw} + 4)} G(s)=z2(wc2Tsw2+4ξwcTsw+4)+z(2wc2Tsw28)+(wc2Tsw24ξwcTsw+4)wc2Tsw2(z2+2z+1)

令系数为:
b 0 = w c 2 b_0 = w_c^2 b0=wc2
a 0 = 4 T 2 + 4 T ξ w c + w c 2 a_0 = \frac{4}{T^2 }+\frac{4}{T}\xi w_c +w_c^2 a0=T24+T4ξwc+wc2
a 1 = 2 w c 2 − 8 T 2 a_1 = 2w_c^2-\frac{8}{T^2} a1=2wc2T28
a 2 = w 2 − 2 ξ w c 2 T + 4 T 2 a_2 = w_2-2\xi w_c \frac{2}{T}+\frac{4}{T^2} a2=w22ξwcT2+T24
等效为:
b 0 = w c 2 T b_0 = w_c^2T b0=wc2T
a 0 = w c 2 T 2 + 4 ξ w c T + 4 a_0 = w_c^2T^2 + 4\xi w_c T + 4 a0=wc2T2+4ξwcT+4
a 1 = 2 w c 2 T 2 − 8 a_1 = 2w_c^2T^2 - 8 a1=2wc2T28
a 2 = w 2 T 2 − 4 ξ w c T + 4 a_2 = w_2T^2-4\xi w_c T+ 4 a2=w2T24ξwcT+4

根据Z变换的时移性质
分母: Y ( n − 1 ) Y ( n ) = z − 1 \frac {Y(n-1)}{Y(n)} = z^{-1} Y(n)Y(n1)=z1, Y ( n − 2 ) Y ( n ) = z − 2 \frac{Y(n-2)}{Y(n)} = z^{-2} Y(n)Y(n2)=z2;
分子: X ( n − 1 ) X ( n ) = z − 1 \frac{X(n-1)}{X(n)} = z^{-1} X(n)X(n1)=z1 X ( n − 2 ) X ( n ) = z − 2 \frac{X(n-2)}{X(n)} = z^{-2} X(n)X(n2)=z2;
进一步可得:
Y ( n ) = b 0 X ( n ) + b 1 X ( n − 1 ) + b 2 X ( n − 2 ) − a 1 Y ( n − 1 ) − a 2 Y ( n − 2 ) a 0 Y(n) = \frac {b_0X(n) + b_1X(n-1) + b_2X(n-2)-a_1Y(n-1)-a_2Y(n-2)}{a_0} Y(n)=a0b0X(n)+b1X(n1)+b2X(n2)a1Y(n1)a2Y(n2)

其中, T s w T_{sw} Tsw为数据采样频率周期, ξ \xi ξ为阻尼比,取值0.707, w c = 2 π f c w_c = 2\pi f_c wc=2πfc (截止频率)

推导2

【推导方法1】
【推导方法2】

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 日常踩坑---ljmp在BIOS中的使用
  • pytest-html报告修改与汉化
  • CTF-NSSCTF[NISACTF 2022]
  • 探索PostgreSQL的GUI工具:提升数据库管理效率
  • 小阿轩yx-部署 KVM 虚拟化平台
  • Elasticsearch跨集群搜索
  • 搜维尔科技:Haption Virtuose 6D遥操作控制人形机器人操作
  • 【Linux-IMX6ULL-阻塞与非阻塞】
  • org.eclipse.jgit 简单总结
  • 电测量数据交换DLMSCOSEM组件第53部分:DLMSCOSEM应用层(下)
  • 3.5.4、查找和排序算法-排序算法下
  • 安全与加密常识(2)TLS/SSL安全协议
  • 数字孪生赋能农业生产:智慧农业的未来之路
  • 计网 - 传统的类网络划分 vs 无类别域间路由CIDR
  • Java连接Redis和SpringBoot整合Redis
  • [译]CSS 居中(Center)方法大合集
  • 【个人向】《HTTP图解》阅后小结
  • 【剑指offer】让抽象问题具体化
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • dva中组件的懒加载
  • ES6 ...操作符
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • JavaScript 奇技淫巧
  • MySQL-事务管理(基础)
  • supervisor 永不挂掉的进程 安装以及使用
  • vue学习系列(二)vue-cli
  • 缓存与缓冲
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • 阿里云移动端播放器高级功能介绍
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (2022 CVPR) Unbiased Teacher v2
  • (3) cmake编译多个cpp文件
  • (c语言)strcpy函数用法
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (转载)hibernate缓存
  • .NET 药厂业务系统 CPU爆高分析
  • .netcore如何运行环境安装到Linux服务器
  • .NET开发者必备的11款免费工具
  • /usr/local/nginx/logs/nginx.pid failed (2: No such file or directory)
  • @RunWith注解作用
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [ C++ ] STL_list 使用及其模拟实现
  • [ C++ ] STL_vector -- 迭代器失效问题
  • [ C++ ] 继承
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [20160902]rm -rf的惨案.txt
  • [Android] 修改设备访问权限
  • [ARC066F]Contest with Drinks Hard
  • [FBCTF2019]RCEService1
  • [git]git命令如何取消先前的配置