【信号滤波 (补充)】二阶陷波滤波代码推导过程(C++)

目录

  • 二阶陷波滤波器计算实例
    • 一、 传递函数的参数推导
    • 二、将传递函数转化成差分方程
      • 2.1 传递函数写成输入输出形式
      • 2.2 Z域转化为时域
    • 三、将差分方程转化成C++代码
      • 3.1 改写输入输出变量
      • 3.2 用循环改写差分方程的递推过程
      • 3.3 转化成C++后的代码

在信号滤波 (中) 这篇中对于二阶陷波滤波器的传递函数到C++代码的转化过程还有人有疑问,本篇一步步推导整个过程。
信号滤波 (中)

二阶陷波滤波器计算实例

在信号滤波 (中)这篇里已经通过Z域构建了传递函数。
H ( z ) = b 0 + b 1 z − 1 + b 2 z − 2 1 + a 1 z − 1 + a 2 z − 2 H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}} H(z)=1+a1z1+a2z2b0+b1z1+b2z2
式中的未知数是怎么求解出来的呢?接下来一步步推导。

一、 传递函数的参数推导

1. 首先 b 0 , b 1 , b 2 b_0, b_1, b_2 b0,b1,b2是怎么推导出来的?

b 0 , b 1 , b 2 b_0, b_1, b_2 b0,b1,b2的设置是基于前人的经验值,我们把这几个参数带入传递函数分子计算。

  • b 0 = 1 b_0 = 1 b0=1
  • b 1 = − 2 cos ⁡ ( ω 0 ) b_1 = -2 \cos(\omega_0) b1=2cos(ω0)
  • b 2 = 1 b_2 = 1 b2=1

所谓零点就是 H ( z ) H(z) H(z)的分子等于零,带入参数后 1 − 2 c o s ( ω 0 ) z − 1 + z − 2 = 0 {1 - 2cos( \omega_0) z^{-1} + z^{-2}} = 0 12cos(ω0)z1+z2=0.
z z z的解,大学知识就可以:
1.1. 两边同乘以 z 2 z^2 z2
z 2 − 2 cos ⁡ ( ω 0 ) z + 1 = 0 z^2 - 2 \cos(\omega_0) z + 1 = 0 z22cos(ω0)z+1=0
1.2. 使用求根公式:
z = − b ± b 2 − 4 a c 2 a z = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} z=2ab±b24ac
1.3. 带入求根公式计算:
z = − ( − 2 cos ⁡ ( ω 0 ) ) ± ( − 2 cos ⁡ ( ω 0 ) ) 2 − 4 ⋅ 1 ⋅ 1 2 ⋅ 1 z = \frac{-(-2 \cos(\omega_0)) \pm \sqrt{(-2 \cos(\omega_0))^2 - 4 \cdot 1 \cdot 1}}{2 \cdot 1} z=21(2cos(ω0))±(2cos(ω0))2411
z = 2 cos ⁡ ( ω 0 ) ± 4 cos ⁡ 2 ( ω 0 ) − 4 2 z = \frac{2 \cos(\omega_0) \pm \sqrt{4 \cos^2(\omega_0) - 4}}{2} z=22cos(ω0)±4cos2(ω0)4
z = 2 cos ⁡ ( ω 0 ) ± 4 ( cos ⁡ 2 ( ω 0 ) − 1 ) 2 z = \frac{2 \cos(\omega_0) \pm \sqrt{4 (\cos^2(\omega_0) - 1)}}{2} z=22cos(ω0)±4(cos2(ω0)1)
z = 2 cos ⁡ ( ω 0 ) ± 4 ( − sin ⁡ 2 ( ω 0 ) ) 2 z = \frac{2 \cos(\omega_0) \pm \sqrt{4 (-\sin^2(\omega_0))}}{2} z=22cos(ω0)±4(sin2(ω0))
z = 2 cos ⁡ ( ω 0 ) ± 2 j sin ⁡ ( ω 0 ) 2 z = \frac{2 \cos(\omega_0) \pm 2j \sin(\omega_0)}{2} z=22cos(ω0)±2jsin(ω0)
1.4.求得两个解:
z = cos ⁡ ( ω 0 ) ± j sin ⁡ ( ω 0 ) z = \cos(\omega_0) \pm j \sin(\omega_0) z=cos(ω0)±jsin(ω0)
欧拉公式转换如下:
z 1 = e j ω 0 , z 2 = e − j ω 0 z_1 = e^{j \omega_0}, \quad z_2 = e^{-j \omega_0} z1=ejω0,z2=ejω0
1.5.结论:
可以看到,带入经验值求得的共轭复根,位于复平面的单位圆上,更具体的说是位于 ω 0 \omega_0 ω0(陷波频率)的单位圆上。
零点的作用就是使目标频率完全衰减。

同样 a 0 , a 1 , a 2 a_0, a_1, a_2 a0,a1,a2也是是基于前人的经验值来设置的,保持极点在收敛域内又接近零点。

2. 带入实际值求解

假定初始参数如下:

  • 陷波频率 f 0 = 50 Hz f_0 = 50 \, \text{Hz} f0=50Hz
  • 采样频率 f s = 1000 Hz f_s = 1000 \, \text{Hz} fs=1000Hz
  • 品质因子 Q = 30 Q = 30 Q=30

2.1. 计算 ω 0 \omega_0 ω0:
ω 0 = 2 π 50 1000 = 0.31416 角频率/样本 \omega_0 = 2 \pi \frac{50}{1000} = 0.31416 \, \text{角频率/样本} ω0=2π100050=0.31416角频率/样本
2.2.计算 α \alpha α:
α = sin ⁡ ( 0.31416 ) 2 × 30 = 0.005235 \alpha = \frac{\sin(0.31416)}{2 \times 30} = 0.005235 α=2×30sin(0.31416)=0.005235
2.3. 计算参数:
b 0 = 1 , b 1 = − 2 cos ⁡ ( 0.31416 ) = − 1.9021 , b 2 = 1 b_0 = 1, \quad b_1 = -2 \cos(0.31416) = -1.9021, \quad b_2 = 1 b0=1,b1=2cos(0.31416)=1.9021,b2=1
a 0 = 1 + 0.005235 = 1.005235 , a 1 = − 2 cos ⁡ ( 0.31416 ) = − 1.9021 , a 2 = 1 − 0.005235 = 0.994765 a_0 = 1 + 0.005235 = 1.005235, \quad a_1 = -2 \cos(0.31416) = -1.9021, \quad a_2 = 1 - 0.005235 = 0.994765 a0=1+0.005235=1.005235,a1=2cos(0.31416)=1.9021,a2=10.005235=0.994765
2.4. 归一化:
b 0 = 1 1.005235 = 0.99477 , b 1 = − 1.9021 1.005235 = − 1.8934 , b 2 = 1 1.005235 = 0.99477 b_0 = \frac{1}{1.005235} = 0.99477, \quad b_1 = \frac{-1.9021}{1.005235} = -1.8934, \quad b_2 = \frac{1}{1.005235} = 0.99477 b0=1.0052351=0.99477,b1=1.0052351.9021=1.8934,b2=1.0052351=0.99477
a 1 = − 1.9021 1.005235 = − 1.8934 , a 2 = 0.994765 1.005235 = 0.98953 a_1 = \frac{-1.9021}{1.005235} = -1.8934, \quad a_2 = \frac{0.994765}{1.005235} = 0.98953 a1=1.0052351.9021=1.8934,a2=1.0052350.994765=0.98953
2.5. 求得传递函数的最终公式:
H ( z ) = 0.99477 − 1.8934 z − 1 + 0.99477 z − 2 1 − 1.8934 z − 1 + 0.98953 z − 2 H(z) = \frac{0.99477 - 1.8934 z^{-1} + 0.99477 z^{-2}}{1 - 1.8934 z^{-1} + 0.98953 z^{-2}} H(z)=11.8934z1+0.98953z20.994771.8934z1+0.99477z2

3. 验证上述的传递函数

验证方法就是1.1的计算过程,首先将分子分母才开单独求根:
H ( z ) = A ( z ) B ( z ) = 0.99477 z 2 − 1.8934 z + 0.99477 z 2 − 1.8934 z + 0.98953 H(z) = \frac{A(z)}{B(z)} = \frac{0.99477z^2 - 1.8934z + 0.99477}{z^2 - 1.8934z + 0.98953} H(z)=B(z)A(z)=z21.8934z+0.989530.99477z21.8934z+0.99477

0.99477 z 2 − 1.8934 z + 0.99477 = 0 0.99477z^2 - 1.8934z + 0.99477 = 0 0.99477z21.8934z+0.99477=0带入求根公式
z = − ( − 1.8934 ) ± ( − 1.8934 ) 2 − 4 ( 0.99477 ) ( 0.99477 ) 2 ( 0.99477 ) z = \frac{-(-1.8934) \pm \sqrt{(-1.8934)^2 - 4(0.99477)(0.99477)}}{2(0.99477)} z=2(0.99477)(1.8934)±(1.8934)24(0.99477)(0.99477)
z = 1.8934 ± − 0.3752 1.98954 z = \frac{1.8934 \pm \sqrt{-0.3752}}{1.98954} z=1.989541.8934±0.3752
分子部分的根(也就是零点)为:
z = 0.9516 ± 0.3083 i z = 0.9516 \pm 0.3083i z=0.9516±0.3083i

同理分母 z 2 − 1.8934 z + 0.98953 = 0 z^2 - 1.8934z + 0.98953 = 0 z21.8934z+0.98953=0带入求根公式
z = − ( − 1.8934 ) ± ( − 1.8934 ) 2 − 4 ( 1 ) ( 0.98953 ) 2 ( 1 ) z = \frac{-(-1.8934) \pm \sqrt{(-1.8934)^2 - 4(1)(0.98953)}}{2(1)} z=2(1)(1.8934)±(1.8934)24(1)(0.98953)
z = 1.8934 ± − 0.37922 2 z = \frac{1.8934 \pm \sqrt{-0.37922}}{2} z=21.8934±0.37922
分母部分的根(也就是极点)为:
z = 0.9467 ± 0.3070 i z = 0.9467 \pm 0.3070i z=0.9467±0.3070i

画出零点和极点在Z域上的图像,可以看到零点在单位圆上,极点在单位圆内侧(收敛域内,在外侧就发散了),极点和零点挨得很近。
在这里插入图片描述

二、将传递函数转化成差分方程

2.1 传递函数写成输入输出形式

将传递函数可以看成Z域上输入X(z)和输出Y(z)的比值 H ( z ) = Y ( z ) X ( z ) H(z) = \frac{Y(z)}{X(z)} H(z)=X(z)Y(z)
将输出Y(z)放到等式左边得 Y ( z ) = H ( z ) ⋅ X ( z ) Y(z) = H(z) \cdot X(z) Y(z)=H(z)X(z),展开后如下:
Y ( z ) = ( b 0 + b 1 z − 1 + b 2 z − 2 1 + a 1 z − 1 + a 2 z − 2 ) ⋅ X ( z ) Y(z) = \left( \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}} \right) \cdot X(z) Y(z)=(1+a1z1+a2z2b0+b1z1+b2z2)X(z)
把分母去掉得到:
( 1 + a 1 z − 1 + a 2 z − 2 ) ⋅ Y ( z ) = ( b 0 + b 1 z − 1 + b 2 z − 2 ) ⋅ X ( z ) (1 + a_1 z^{-1} + a_2 z^{-2}) \cdot Y(z) = (b_0 + b_1 z^{-1} + b_2 z^{-2}) \cdot X(z) (1+a1z1+a2z2)Y(z)=(b0+b1z1+b2z2)X(z)

2.2 Z域转化为时域

z − n z^{-n} zn 代表在n时刻的采样, z − 1 z^{-1} z1代表在n-1时刻的采样, z − 2 z^{-2} z2代表在n-1时刻的采样,以此类推。
因此转化为时域后的差分方程如下:
y [ n ] + a 1 y [ n − 1 ] + a 2 y [ n − 2 ] = b 0 x [ n ] + b 1 x [ n − 1 ] + b 2 x [ n − 2 ] y[n] + a_1 y[n-1] + a_2 y[n-2] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] y[n]+a1y[n1]+a2y[n2]=b0x[n]+b1x[n1]+b2x[n2]
整理后如下:
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 ] 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] y[n]=b0x[n]+b1x[n1]+b2x[n2]a1y[n1]a2y[n2]

三、将差分方程转化成C++代码

3.1 改写输入输出变量

在C++中,用x_n表示输入x[n],y_n代表输出y[n],x0输入来自于i时刻电压采样voltageData[i]。y_n是经过差分方程计算后的输出。
同理,前一个时刻的输入x[n-1]用x_n_1表示,前两个个时刻的输入x_n_2用x[n-2]表示,前一个时刻的输出y[n-1]用y_n_1表示,前两个时刻的输出y[n-2]用y_n_2表示。
转化后如下:

double y_n = b0 * x_n + b1 * x_n_1 + b2 * x_n_2 - a1 * y_n_1 - a2 * y_n_2;

滤波后的变量存储在filteredData[i]中。

3.2 用循环改写差分方程的递推过程

差分方程其实就是一个递推过程,依据当前输入、前一时刻输入和输出、前两时刻输入和输出,推出当前输出。
当循环完成一次后执行下面的代码把当前的输入输出变成前一时刻的输入输出:

x_n_2 = x_n_1;
x_n_1 = x_n;
y_n_2 = y_n_1;
y_n_1 = y_n;

其中

  • x_n_1x_n_2: 前时刻的采样 x[n-1] 和 x[n-2]。
  • y_n_1y_n_2: 前时刻采样的输出 y [ n − 1 ] y[n-1] y[n1] y [ n − 2 ] y[n-2] y[n2]

3.3 转化成C++后的代码

下面用QT C++写applyNotchFilter滤波函数:

QVector<double> applyNotchFilter(const QVector<double> &voltageData, double sampleRate, double notchFreq, double qualityFactor)
{QVector<double> filteredData(voltageData.size());double w0 = 2.0 * M_PI * notchFreq / sampleRate;double alpha = sin(w0) / (2.0 * qualityFactor);double b0 = 1.0;double b1 = -2.0 * cos(w0);double b2 = 1.0;double a0 = 1.0 + alpha;double a1 = -2.0 * cos(w0);double a2 = 1.0 - alpha;b0 /= a0;b1 /= a0;b2 /= a0;a1 /= a0;a2 /= a0;double x_n_1 = 0.0, x_n_2 = 0.0; double y_n_1 = 0.0, y_n_2 = 0.0; for (int i = 0; i < voltageData.size(); ++i) {double x_n = voltageData[i];double y_n = b0 * x_n + b1 * x_n_1 + b2 * x_n_2 - a1 * y_n_1 - a2 * y_n_2;filteredData[i] = y_n;x_n_2 = x_n_1;x_n_1 = x_n;y_n_2 = y_n_1;y_n_1 = y_n;}return filteredData;
}

完整代码的应用见信号滤波 (上) 3.3部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502968.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++进阶——用Hash封装unordered_map和unordered_set

目录 前言 源码怎么说 为什么要兼容&#xff1f; 兼容的具体做法&#xff1f; 为什么要把Key转为整数&#xff08;HashFcn&#xff09;&#xff1f; 模拟实现 一、建立框架 二、迭代器 运算符重载 迭代器兼容大法 三、[ ]重载 四、实现不能修改key 实现及测试代码 …

安装MySQL的五种方法(Linux系统和Windows系统)

一.在Linux系统中安装MySQL 第一种方法:在线YUM仓库 首先打开MySQL官网首页 www.mysql.com 找到【DOWNLOADS】选项&#xff0c;点击 下拉&#xff0c;找到 【MySQL Community(GPL) Downloads】 在社区版下载页面中&#xff0c;【 MySQL Yum Repository 】链接为在线仓库安装…

极客说|微软 Phi 系列小模型和多模态小模型

作者&#xff1a;胡平 - 微软云人工智能高级专家 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&am…

封装/前线修饰符/Idea项目结构/package/impore

目录 1. 封装的情景引入 2. 封装的体现 3. 权限修饰符 4. Idea 项目结构 5. package 关键字 6. import 关键字 7. 练习 程序设计&#xff1a;高内聚&#xff0c;低耦合&#xff1b; 高内聚&#xff1a;将类的内部操作“隐藏”起来&#xff0c;不需要外界干涉&#xff1b…

【C++】P5733 【深基6.例1】自动修正

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述&#x1f4af;解题思路概述&#x1f4af;第一种实现方式&#xff1a;直接使用字符ASCII值计算代码实现代码分析 &#x1f4af;第二种实现方式&#xff1a;直接修改…

【Elasticsearch】文档操作:添加、更新和删除

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

【insert 插入数据语法合集】.NET开源ORM框架 SqlSugar 系列

系列文章目录 &#x1f380;&#x1f380;&#x1f380; .NET开源 ORM 框架 SqlSugar 系列 &#x1f380;&#x1f380;&#x1f380; 文章目录 系列文章目录一、前言 &#x1f343;二、插入方式 &#x1f4af;2.1 单条插入实体2.2 批量 插入实体2.3 根据字典插入2.4 根据 Dat…

权限掩码umask

1 、 设置新建文件或目录的默认权限 在 Linux 系统中&#xff0c;当用户创建一个新的文件或目录时&#xff0c;系统都会为新建的文件或目录分配默认的权限&#xff0c;该默认权限与umask 值有关&#xff0c;其具体关系是&#xff1a; 新建文件的默认权限 0666-umask 值 新建…

202-01-06 Unity 使用 Tip1 —— UnityHub 模块卸载重装

文章目录 1 卸载模块2 更新配置文件3 重启 UnityHub 起因&#xff1a; ​ WebGL 平台打包程序报错&#xff0c;懒得修复了&#xff0c;因此粗暴地删了重装。但是 UnityHub 不支持卸载模块&#xff0c;因此手动配置。 1 卸载模块 ​ 以 Unity 6000.0.26f1c1 为例&#xff0c;其…

打造三甲医院人工智能矩阵新引擎(二):医学影像大模型篇--“火眼金睛”TransUNet

一、引言 1.1 研究背景与意义 在现代医疗领域,医学影像作为疾病诊断与治疗的关键依据,发挥着不可替代的作用。从传统的X射线、CT(计算机断层扫描)到MRI(磁共振成像)等先进技术,医学影像能够直观呈现人体内部结构,为医生提供丰富的诊断信息,涵盖疾病识别、病灶定位、…

国产编辑器EverEdit - 两种删除空白行的方法

1 使用技巧&#xff1a;删除空白行 1.1 应用场景 用户在编辑文档时&#xff0c;可能会遇到很多空白行需要删除的情况&#xff0c;比如从网页上拷贝文字&#xff0c;可能就会存在大量的空白行要删除。 1.2 使用方法 1.2.1 方法1&#xff1a; 使用编辑主菜单 选择主菜单编辑 …

李宏毅机器学习笔记-Transformer

目录 1. Seq2seq 2. encoder Transformer 中的 Block 结构 3. Decoder 4.Encoder和Decoder间的信息传递 5.Training 6.Tips 1. Seq2seq Transformer 是一个seq2seq的model。Seq2seq指的是input是一个序列&#xff0c;输出也是一个序列&#xff0c;输出的长度是由机器自己…

GitLab集成Runner详细版--及注意事项汇总【最佳实践】

一、背景 看到网上很多用户提出的runner问题其实实际都不是问题&#xff0c;不过是因为对runner的一些细节不清楚导致了误解。本文不系统性的介绍GitLab-Runner&#xff0c;因为这类文章写得好的特别多&#xff0c;本文只汇总一些常几的问题/注意事项。旨在让新手少弯路。 二、…

指针 const 的组合

1、首先来了解一下常量 const int num 5&#xff1b; 那么num的值是5&#xff0c; num的值不可修改 2、来了解一下指针 int value 5; int* p &value; 我喜欢吧指针和类型放一起&#xff0c;来强调p是一个指针类型&#xff0c; 而赋值的时候就得赋值一个int类型的地址…

《C++11》各种初始化方式的详细列举与对比

在 C 中&#xff0c;初始化对象的方式多种多样。随着 C 标准的演进&#xff0c;特别是 C11 的引入&#xff0c;初始化方式得到了显著的扩展和改进。本文将详细列举 C 中的各种初始化方式&#xff0c;并对它们进行对比&#xff0c;帮助开发者更好地理解和应用这些特性。 1. C98…

前端小案例——520表白信封

前言&#xff1a;我们在学习完了HTML和CSS之后&#xff0c;就会想着使用这两个东西去做一些小案例&#xff0c;不过又没有什么好的案例让我们去练手&#xff0c;本篇文章就提供里一个案例——520表白信封 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主…

【Vim Masterclass 笔记05】第 4 章:Vim 的帮助系统与同步练习(L14+L15+L16)

文章目录 Section 4&#xff1a;The Vim Help System&#xff08;Vim 帮助系统&#xff09;S04L14 Getting Help1 打开帮助系统2 退出帮助系统3 查看具体命令的帮助文档4 查看帮助文档中的主题5 帮助文档间的上翻、下翻6 关于 linewise7 查看光标所在术语名词的帮助文档8 关于退…

10-C语言项目池

C语言项目池 《个人通讯录》 《火车订票系统》 管理员用户1录入火车票信息区间查询/购票2显示火车票信息打印购票信息3查询火车票信息退票4修改火车票信息5添加火车票信息 《学生学籍管理系统》 1录入学生信息2添加学生信息3显示学生信息4查找学生信息5删除学生信息6修改学…

Android 绘制学习总结

1、刷新率介绍 我们先来理一下基本的概念&#xff1a; 1、60 fps 的意思是说&#xff0c;画面每秒更新 60 次 2、这 60 次更新&#xff0c;是要均匀更新的&#xff0c;不是说一会快&#xff0c;一会慢&#xff0c;那样视觉上也会觉得不流畅 3、每秒 60 次&#xff0c;也就是 1…

每日一题:BM1 反转链表

文章目录 [toc]问题描述数据范围示例 C代码实现使用栈实现&#xff08;不符合要求&#xff0c;仅作为思路&#xff09; 解题思路 - 原地反转链表步骤 C语言代码实现 以前只用过C刷过代码题目&#xff0c;现在试着用C语言刷下 问题描述 给定一个单链表的头结点 pHead&#xff…