全程纯 C# 实现,无任何第三方库;所有代码与数据均已在 .NET 9 下编译运行实测验证。
基本概念
频域视角
任何信号都可以分解为不同频率的正弦波的叠加(傅里叶分析的基本思想)。频率 ff(单位 Hz)表示信号每秒完成多少次周期变化:
- 低频分量:变化缓慢的成分,如直流偏置、缓慢漂移、温度漂移、趋势项;
- 高频分量:变化剧烈的成分,如快速波动、边沿跳变、高频噪声。
什么是滤波
滤波的本质是按频率对信号的不同分量进行"选择性通过 / 选择性衰减"。它可以发生在模拟域(RC/LC/运放电路),也可以发生在数字域(差分方程、FFT)。
高通滤波的定义
高通滤波是一种让"频率高于 fcfc 的成分通过、频率低于 fcfc 的成分被抑制"的运算,fcfc 称为截止频率(cutoff frequency)。
与之相对:
- 低通滤波(LPF):让低频通过、抑制高频;
- 带通(BPF)/ 带阻(BSF):只让某个频带通过 / 抑制。
关键参数
| 参数 | 含义 |
|---|---|
| 截止频率 fcfc | 增益下降到 -3dB(0.707 倍)的频率点 |
| 通带 | f>fcf>fc 的区域,增益 ≈ 1(0dB) |
| 阻带 | f<fcf<fc 的区域,增益趋于 0 |
| 过渡带 | fcfc 附近的渐变区,越窄越接近理想 |
| 滚降斜率 | 阻带内每倍频程(octave)衰减的 dB 数:一阶 ≈ 6dB/oct,二阶 ≈ 12dB/oct |
| 相位 / 群延迟 | 滤波引入的时移,IIR 一般是非线性相位 |
模拟 vs 数字
- 模拟高通:RC 电路、运算放大器,连续时间,受器件精度与温漂影响;
- 数字高通:差分方程或 FFT,离散时间,可编程、精度高、可复现、无温漂,是现代嵌入式的主流选择。
IIR 与 FIR
- IIR(无限冲激响应):有反馈回路(递归),用低阶就能实现陡峭滚降,计算量小,但相位非线性、需要保证稳定性;
- FIR(有限冲激响应):无反馈,可严格线性相位,但达到相同滚降需要很高阶数,计算量大。
本文主推 IIR(一阶 / 二阶)与 FFT 两种路径。
采样与奈奎斯特
数字信号能表示的最高频率为奈奎斯特频率。高通截止频率必须满足
,否则无法实现。
历史背景
- 1807 年:傅里叶提出"任意周期信号可分解为不同频率正弦波之和",为一切滤波理论奠定数学基础;
- 1915 年:贝尔实验室的 George Campbell 与德国的 Karl Wagner 几乎同时提出"滤波器"概念,用于电话多路复用,这是滤波器工程的起点;
- 1930 年:Butterworth 提出"最大平坦"幅频响应滤波器设计;
- 此后 Chebyshev(等波纹响应)、Bessel(最平坦群延迟)等不同逼近准则陆续出现;
- 1965 年:Cooley 与 Tukey 发表快速傅里叶变换(FFT)算法,把 O(N2)O(N2) 的 DFT 降到 O(NlogN)O(NlogN),频域滤波从理论走向工程;
- 1970 年代起:微处理器与专用 DSP 芯片普及,数字滤波全面取代模拟滤波,成为信号处理的标准方法;
- 现代:在 MEMS 传感器(加速度计/陀螺仪)、生物电信号(心电/脑电)、音频、振动监测、图像处理等嵌入式场景中,高通滤波是"去直流漂移、去趋势项"的标配算法。
核心原理
理想高通滤波器
理想高通滤波器的频率响应为:
它在 fcfc 处发生 0→1 的阶跃。但这样的理想响应在物理上不可实现(需要无限阶、非因果),因此实际工程用有限阶近似,必然存在过渡带与有限滚降。
一阶数字高通(DC 阻塞型)的推导
模拟原型:RC 高通
一阶 RC 高通电路传递函数为:
对应的微分方程(输入 xx、输出 yy):
差分离散化
用一阶差分近似导数:
其中 Ts=1/fsTs=1/fs 为采样周期。整理:
令,得到一阶高通(DC 阻塞)滤波器的标准差分方程:
传递函数与稳定性
对差分方程做 z 变换:
- 零点在 z=1z=1(对应直流 f=0f=0),故 H(1)=0H(1)=0,直流被完全阻塞;
- 极点在 z=αz=α,由于 0<α<10<α<1,极点始终位于单位圆内,滤波器无条件稳定。
系数示例
取 fs=1000Hz, fc=5Hzfs=1000Hz, fc=5Hz:
频率响应特性
幅频响应:
- f→0f→0:增益 → 0(直流被滤除);
- f=fcf=fc:增益 ≈ 0.707(-3dB);
- f→fs/2f→fs/2:增益 → 1。
一阶高通的阻带滚降约为6dB/octave(20dB/decade)。
二阶 Butterworth 高通(Biquad)
当需要更陡的滚降时使用二阶。二阶 Butterworth 高通模拟原型:
经双线性变换离散化后,得到双二阶(Biquad)标准型:
采用 RBJ Audio EQ Cookbook 系数公式:
再统一除以 a0a0 归一化。二阶 Butterworth 阻带滚降约12dB/oct(40dB/decade)。
频域(FFT)高通
另一种"更接近理想"的思路是频域处理:
- 对整段信号做 FFT 得频谱 X[k]X[k];
- 把对应低频
及对称负频区的 bin 置零;
- 做 IFFT 还原时域信号。
由于直接"硬切"频率分量,其幅频特性接近理想阶跃,但存在频谱泄漏(非整周期截断导致)和块边缘效应(Gibbs 现象),实践中常配合窗函数(如汉宁窗)与重叠相加(overlap-add)使用。
与低通的关系:互补性
一个重要而优美的事实:高通 ≈ 原信号 − 低通。
这正是互补滤波的思想基础,也是传感器融合(如陀螺仪高通 + 加速度计低通融合姿态)的出发点。
执行流程
设计阶段
- 确定采样率 fsfs(满足
感兴趣的最高频率,即奈奎斯特定理);
- 根据需求选定截止频率
;
- 选择滤波器类型与阶数(一阶简单 / 二阶陡峭 / FFT 锐截止)。
初始化
- 计算系数:一阶算 αα;二阶算
- 状态变量清零:
(二阶还有
)。
流式实时处理(每来一个样本执行一次)
- 读取新样本
;
- 一阶:
- 更新状态:
- 输出
该流程在单片机 ADC 中断、音频回调中逐样本执行,每样本仅几次乘加运算。
批处理(整段)
对一段完整数据一次性滤波(如离线分析、FFT 法),注意起始瞬态区。
后处理与验证
- 检查输出均值是否≈0(验证直流被滤除);
- 观察起始段瞬态(滤波器"充放电"过程,约数个 ττ 后进入稳态);
- 用频率响应扫描验证截止频率。
算法性能分析
时间复杂度
| 实现 | 每样本 | 整段 N 点 |
|---|---|---|
| 一阶 IIR | O(1)(1 乘 2 加) | O(N) |
| 二阶 IIR | O(1)(5 乘 4 加) | O(N) |
| FFT 频域 | O(N) 分摊 | O(N log N) |
空间复杂度
- 一阶 / 二阶 IIR:O(1),仅需 2~4 个状态变量;
- FFT:O(N),需整段缓冲。
实时性
一阶高通每样本仅1 次乘法、2 次加法,可在低端 MCU 的定时器中断 / ADC 回调里逐样本执行,延迟约等于零。
稳定性
- 一阶:极点
,恒稳定;
- 二阶:RBJ 公式在
下极点恒在单位圆内,稳定;
- 工程上用定点实现时需留意系数量化后极点是否越出单位圆。
数值精度
- 浮点(double):精度足够,本文演示采用;
- 定点(嵌入式 Q15/Q31):需做系数定标,注意乘法溢出与量化噪声。
相位特性(重要代价)
IIR 滤波器相位是非线性的,会造成波形畸变;若应用对相位敏感(如 QRS 波检测、高保真音频),需考虑零相位滤波(正反各过一次)或改用 FIR。
实测数据(本文验证,fs=1000Hz,fc=5Hz)
| 频率(Hz) | 一阶增益(dB) | 二阶增益(dB) |
|---|---|---|
| 0.5 | -20.04 | -40.00 |
| 1.0 | -14.15 | -27.97 |
| 2.0 | -8.62 | -16.03 |
| 5.0 | -3.08 | -3.01← 截止点 |
| 10.0 | -1.08 | -0.26 |
| 50.0 | -0.22 | -0.09 |
| 100.0 | -0.02 | +0.17 |
验证结论:
- 5Hz 处增益≈-3dB,与理论 -3.01dB 吻合;
- 阻带滚降:一阶 0.5→1Hz 衰减 5.89dB ≈6dB/oct;二阶衰减 12dB ≈12dB/oct,与理论完全一致。
完整代码
完整演示程序(Program.cs)
以下为完整可运行程序,包含三种高通实现与验证逻辑。在项目目录执行dotnet run -c Release即可运行。
using System; namespace HighPassFilterDemo { /// <summary> /// 高通滤波算法:完整演示程序 /// 包含三种实现:一阶IIR高通、二阶Butterworth高通、频域(FFT)高通 /// 纯 C# 实现,无任何第三方库 /// </summary> public static class Program { public static void Main() { double fs = 1000.0; // 采样率 1000 Hz double fc = 5.0; // 截止频率 5 Hz int n = 512; // 样本数(2的幂,供FFT使用) // ============ 1. 生成测试信号 ============ // 直流偏置 2.5V + 1Hz 低频(幅1.0) + 50Hz 高频(幅0.5) + 白噪声(±0.1) double[] x = new double[n]; var rnd = new Random(42); for (int i = 0; i < n; i++) { double t = (double)i / fs; x[i] = 2.5 // 直流 + 1.0 * Math.Sin(2 * Math.PI * 1.0 * t) // 1 Hz 低频 + 0.5 * Math.Sin(2 * Math.PI * 50.0 * t) // 50 Hz 高频 + 0.1 * (rnd.NextDouble() * 2 - 1); // 噪声 } // ============ 2. 一阶 IIR 高通 ============ var hp1 = new OnePoleHighPass(fs, fc); double[] y1 = new double[n]; for (int i = 0; i < n; i++) y1[i] = hp1.Process(x[i]); // ============ 3. 二阶 Butterworth 高通 ============ var hp2 = new ButterworthHighPass2(fs, fc); double[] y2 = new double[n]; for (int i = 0; i < n; i++) y2[i] = hp2.Process(x[i]); // ============ 4. 频域(FFT)高通 ============ double[] y3 = FrequencyDomainHighPass.Process(x, fs, fc); // ============ 5. 逐点输出对比 ============ Console.WriteLine("i 输入 x[i] 一阶 y1[i] 二阶 y2[i] FFT y3[i]"); for (int i = 0; i < n; i += 32) { Console.WriteLine($"{i,3} {x[i],12:F4} {y1[i],12:F4} {y2[i],12:F4} {y3[i],12:F4}"); } // ============ 6. 关键指标:直流是否被滤除 ============ Console.WriteLine(); Console.WriteLine($"输入均值 : {Mean(x),12:F6} (含直流,应≈2.5)"); Console.WriteLine($"一阶输出均值: {Mean(y1),12:F6} (应≈0,直流已滤除)"); Console.WriteLine($"二阶输出均值: {Mean(y2),12:F6} (应≈0,直流已滤除)"); Console.WriteLine($"FFT输出均值 : {Mean(y3),12:F6} (应≈0,直流已滤除)"); // ============ 7. 输出能量(高频分量是否保留) ============ Console.WriteLine(); Console.WriteLine($"输入RMS : {Rms(x),12:F4}"); Console.WriteLine($"一阶输出RMS : {Rms(y1),12:F4} (低频/直流被抑制)"); Console.WriteLine($"二阶输出RMS : {Rms(y2),12:F4} (低频/直流被抑制)"); Console.WriteLine($"FFT输出RMS : {Rms(y3),12:F4} (低频/直流被抑制)"); } static double Mean(double[] a) { double s = 0; for (int i = 0; i < a.Length; i++) s += a[i]; return s / a.Length; } static double Rms(double[] a) { double s = 0; for (int i = 0; i < a.Length; i++) s += a[i] * a[i]; return Math.Sqrt(s / a.Length); } } /// <summary> /// 一阶 IIR 高通滤波器(DC 阻塞型) /// 差分方程:y[n] = α·(y[n-1] + x[n] − x[n-1]) /// 系数:α = τ/(τ+Ts),τ = 1/(2π·fc),Ts = 1/fs /// 特性:单极点、零点在 z=1(直流处),直流增益为 0 /// </summary> public class OnePoleHighPass { private readonly double _alpha; private double _xPrev; private double _yPrev; public OnePoleHighPass(double sampleRate, double cutoffHz) { double tau = 1.0 / (2.0 * Math.PI * cutoffHz); double ts = 1.0 / sampleRate; _alpha = tau / (tau + ts); } public double Process(double x) { double y = _alpha * (_yPrev + x - _xPrev); _xPrev = x; _yPrev = y; return y; } public void Reset() { _xPrev = 0; _yPrev = 0; } } /// <summary> /// 二阶 Butterworth 高通滤波器(双二阶 IIR,Biquad) /// 采用 RBJ Audio EQ Cookbook 系数公式,Q = 1/√2 保证巴特沃斯最平坦幅频特性 /// 差分方程:y[n] = b0·x[n] + b1·x[n-1] + b2·x[n-2] − a1·y[n-1] − a2·y[n-2] /// </summary> public class ButterworthHighPass2 { private double _b0, _b1, _b2, _a1, _a2; private double _x1, _x2, _y1, _y2; public ButterworthHighPass2(double sampleRate, double cutoffHz) { double w0 = 2.0 * Math.PI * cutoffHz / sampleRate; double cosw0 = Math.Cos(w0); double sinw0 = Math.Sin(w0); double q = 1.0 / Math.Sqrt(2.0); // Butterworth: Q = 1/√2 double alpha = sinw0 / (2.0 * q); double a0 = 1.0 + alpha; _b0 = (1.0 + cosw0) / 2.0 / a0; _b1 = -(1.0 + cosw0) / a0; _b2 = (1.0 + cosw0) / 2.0 / a0; _a1 = -2.0 * cosw0 / a0; _a2 = (1.0 - alpha) / a0; } public double Process(double x) { double y = _b0 * x + _b1 * _x1 + _b2 * _x2 - _a1 * _y1 - _a2 * _y2; _x2 = _x1; _x1 = x; _y2 = _y1; _y1 = y; return y; } public void Reset() { _x1 = _x2 = _y1 = _y2 = 0; } } /// <summary> /// 频域高通滤波:FFT → 低频 bin 置零 → IFFT /// 自带基2迭代 FFT 实现(原位运算),无第三方库 /// </summary> public static class FrequencyDomainHighPass { public static double[] Process(double[] x, double sampleRate, double cutoffHz) { int n = x.Length; if ((n & (n - 1)) != 0) throw new ArgumentException("输入长度必须是 2 的幂"); var re = (double[])x.Clone(); var im = new double[n]; Fft(re, im, false); // 正变换 // 计算截止频率对应的 bin 索引 int cutoffBin = (int)Math.Floor(cutoffHz / sampleRate * n); // 低频区(含直流)置零 for (int k = 0; k <= cutoffBin && k < n; k++) { re[k] = 0; im[k] = 0; } // 负频率对称区置零 for (int k = n - cutoffBin; k < n; k++) { re[k] = 0; im[k] = 0; } Fft(re, im, true); // 逆变换 double[] y = new double[n]; for (int i = 0; i < n; i++) y[i] = re[i] / n; // 逆变换归一化 return y; } /// <summary>基2迭代 FFT(原位),inverse = true 时做未归一化逆变换</summary> public static void Fft(double[] re, double[] im, bool inverse) { int n = re.Length; // 位反转重排 for (int i = 1, j = 0; i < n; i++) { int bit = n >> 1; for (; (j & bit) != 0; bit >>= 1) j ^= bit; j ^= bit; if (i < j) { (re[i], re[j]) = (re[j], re[i]); (im[i], im[j]) = (im[j], im[i]); } } for (int len = 2; len <= n; len <<= 1) { double ang = 2.0 * Math.PI / len * (inverse ? 1.0 : -1.0); double wr = Math.Cos(ang), wi = Math.Sin(ang); for (int i = 0; i < n; i += len) { double curR = 1.0, curI = 0.0; for (int k = 0; k < len / 2; k++) { int a = i + k, b = i + k + len / 2; double tr = curR * re[b] - curI * im[b]; double ti = curR * im[b] + curI * re[b]; re[b] = re[a] - tr; im[b] = im[a] - ti; re[a] += tr; im[a] += ti; double nxtR = curR * wr - curI * wi; curI = curR * wi + curI * wr; curR = nxtR; } } } } } }实际运行结果(.NET 9,Release)
i 输入 x[i] 一阶 y1[i] 二阶 y2[i] FFT y3[i] 0 2.5336 2.4564 2.4780 -0.0793 32 2.3431 0.6285 -0.2259 -0.3372 64 3.2806 0.9065 -0.0154 0.4420 96 2.4956 -0.2374 -0.9127 -0.5441 128 3.4986 0.4474 0.0390 0.2695 160 3.3563 0.2181 0.0231 -0.0129 192 3.2049 -0.1551 -0.3134 -0.2442 224 3.9318 0.5102 0.4261 0.4509 256 2.9395 -0.5048 -0.5684 -0.5440 288 3.7986 0.2703 0.2286 0.3332 320 3.4998 0.0936 0.1271 0.0810 352 3.0956 -0.3241 -0.2996 -0.2309 384 3.5992 0.3208 0.4184 0.4202 416 2.4672 -0.6726 -0.5655 -0.5224 448 3.0700 0.0283 0.1585 0.2720 480 2.5988 -0.1757 0.0140 -0.0587 输入均值 : 3.126135 (含直流,应≈2.5) 一阶输出均值: 0.165944 (应≈0,直流已滤除) 二阶输出均值: -0.009911 (应≈0,直流已滤除) FFT输出均值 : 0.000000 (应≈0,直流已滤除) 输入RMS : 3.1619 一阶输出RMS : 0.6013 (低频/直流被抑制) 二阶输出RMS : 0.5208 (低频/直流被抑制) FFT输出RMS : 0.3557 (低频/直流被抑制)结果解读:
- 输入信号均值 3.13(含 2.5V 直流 + 1Hz 分量在 0.512s 窗口内的残余),滤波后三种实现输出均值都归零附近,直流被干净滤除(FFT 法因直流 bin 被直接置零,均值精确为 0);
- 输出 RMS 大幅下降:输入 3.16 → 一阶 0.60 / 二阶 0.52 / FFT 0.36;
- FFT 输出 RMS 0.3557 ≈ 0.5/√2 = 0.3536,正好等于 50Hz 分量的理论 RMS 值——说明高频分量被完整保留,低频与直流被滤除,验证算法正确。
频率响应实测代码
using System; namespace FreqRespTest { /// <summary> /// 频率响应实测:扫描不同频率正弦,测滤波前后稳态幅度比(增益 dB) /// 验证 5Hz 截止频率处增益 ≈ -3dB(0.707) /// </summary> public static class Program { public static void Main() { double fs = 1000.0; double fc = 5.0; double[] freqs = { 0.5, 1, 2, 3, 5, 8, 10, 20, 50, 100 }; int n = 8000; // 8 秒,足够长以测稳态增益 Console.WriteLine("频率(Hz) 一阶增益(dB) 二阶增益(dB) 理论-3dB点"); foreach (double f in freqs) { double[] x = new double[n]; for (int i = 0; i < n; i++) x[i] = Math.Sin(2 * Math.PI * f * i / fs); var hp1 = new HighPassFilterDemo.OnePoleHighPass(fs, fc); double[] y1 = new double[n]; for (int i = 0; i < n; i++) y1[i] = hp1.Process(x[i]); var hp2 = new HighPassFilterDemo.ButterworthHighPass2(fs, fc); double[] y2 = new double[n]; for (int i = 0; i < n; i++) y2[i] = hp2.Process(x[i]); double g1 = 20 * Math.Log10(SteadyAmp(y1) / SteadyAmp(x)); double g2 = 20 * Math.Log10(SteadyAmp(y2) / SteadyAmp(x)); Console.WriteLine($"{f,6:F1} {g1,10:F2} {g2,10:F2} {(f == fc ? "<-- 应≈-3.01" : "")}"); } } /// <summary>稳态段幅度估计:取后 1/4 样本的峰峰值一半</summary> static double SteadyAmp(double[] a) { int start = a.Length * 3 / 4; double max = double.MinValue, min = double.MaxValue; for (int i = start; i < a.Length; i++) { if (a[i] > max) max = a[i]; if (a[i] < min) min = a[i]; } return (max - min) / 2.0; } } }优缺点
优点
- 有效去除直流 / 趋势项:能彻底阻塞直流分量(零点在 z=1);
- 实现简单:一阶仅需 1 个系数、1 个状态变量;
- 计算量小、实时性好:每样本 O(1),适合 MCU 中断级处理;
- 参数少:只需知道采样率 fsfs 和截止频率 fcfc;
- 一阶无条件稳定:极点恒在单位圆内;
- 纯 C# / 原生实现:无第三方依赖,可轻松移植到 C / 嵌入式。
缺点
- 非线性相位(IIR 通病),对相位敏感场景需额外处理;
- 阻带衰减有限:一阶仅 6dB/oct,对很靠近 0Hz 的残留抑制不足,需更高阶;
- 起始瞬态:滤波器需要"预热",起始几个样本不准;
- 过渡带较宽:非理想锐截止;
- FFT 法存在频谱泄漏、块边缘效应,且需整段缓冲、延迟大;
- 截止频率附近会有幅度 / 相位过渡,选频不当会误伤有用信号。
适用场景
| 场景 | 用途 |
|---|---|
| 惯性传感器(加速度计 / 陀螺仪) | 去除直流偏置、重力分量、温漂 |
| 心电 ECG / 脑电 EEG / 肌电 EMG | 去除基线漂移与接触伪迹 |
| 音频系统 | 去除 ADC 直流偏置、保护喇叭 |
| 振动 / 转速监测 | 提取交流振动分量、剔除静态分量 |
| 图像处理 | 高通 = 边缘 / 纹理提取(拉普拉斯等) |
| 传感器数据预处理 | 去除趋势项(去趋势)后再做后续算法 |
| 姿态融合 | 与低通构成互补滤波 |
| 测速 / 编码器 | 滤除缓慢漂移,稳定速度反馈 |
总结
- 高通滤波是信号处理家族的基础算法,解决"如何提取高频、剔除直流 / 低频"这一普适问题;
- 数字实现有清晰递进的三条路径:一阶 IIR(最简单、最省资源)→ 二阶 Butterworth(更陡滚降)→ FFT 频域(最接近理想);
- 一阶公式
是嵌入式"DC 阻塞器"的标准写法,建议熟记;
- 高通与低通互补
,是互补滤波等高级算法的基石;
- 本文所有 C# 代码已在 .NET 9 下编译运行并实测验证:5Hz 截止点 -3dB、一阶 6dB/oct / 二阶 12dB/oct 滚降、输出均值≈0 直流滤除。
一句话总结:高通滤波 = 用几行代码、O(1) 的计算成本,让信号"去粗存精",剔除直流漂移、留下动态信息——嵌入式与信号处理工程师的必备基础算法。