巴特沃斯滤波算法深度解析

基本概念


巴特沃斯滤波器由英国工程师 Stephen Butterworth 提出,属于 IIR 滤波器。

IIR:无限冲激响应,输出不仅依赖当前、历史输入,还依赖历史输出,存在反馈回路。FIR 无反馈,仅使用输入数据。

关键术语:

  • 阶数 N:滤波器阶数,阶数越高阻带衰减越快,计算开销越大,相位失真越严重。
  • 截止频率:-3dB 截止频率,幅度下降到通带 0.707 倍处的频率点。
  • 采样频率:原始信号采样频率。
  • 双线性变换:模拟滤波器转数字滤波器手段,会产生频率畸变,需要预畸变校正截止频率。
  • 预畸变:补偿双线性变换带来的频率扭曲,保证实际截止频率和设计目标一致。
  • a 系数、b 系数:IIR 滤波器差分方程分母、分子系数。
  • 零相位滤波 filtfilt:正向滤波完成后反向再次滤波,抵消 IIR 带来相位偏移,适合离线数据,不可用于实时。

注意:巴特沃斯本身是滤波器设计算法,不是直接滤波函数;需要先算出 a、b 系数,再运行差分方程完成滤波计算。

历史背景


1930 年,Stephen Butterworth 在论文《On the Theory of Filter Amplifiers》首次提出巴特沃斯模拟滤波器。 在模拟电路时代使用电阻电容电感搭建;数字信号处理发展后,通过双线性变换把模拟原型转化为数字滤波器,广泛应用。 对比同期滤波器:切比雪夫 I/II、椭圆滤波器牺牲通带 / 阻带平坦换取更快衰减;贝塞尔滤波器优先保证线性相位。巴特沃斯选择通带最大平坦作为核心设计目标,折中衰减速度与波纹。

核心原理


模拟巴特沃斯幅度平方函数

模拟域幅度平方响应:

  • N:滤波器阶数
  • :模拟角频率
  • :模拟截止角频率 (-3dB)

极点全部分布在 s 平面左半平面的单位圆上,取左半平面极点构造稳定传递函数。

双线性变换 & 预畸变

模拟 S 域转数字 Z 域,双线性变换公式:

采样周期。 双线性变换会压缩频率轴,必须做预畸变校正截止频率

把目标数字截止频率映射回模拟域,再做变换,保证最终数字滤波器截止频率准确。

数字 IIR 传递函数

差分方程(滤波运算核心)

时域递归计算公式:

  • :输入原始信号
  • :滤波输出信号
  • :分子系数
  • :分母系数

实时滤波:只能正向递归,会产生相位滞后; 离线零相位滤波:正向滤波→将输出序列反转→再次滤波→再反转得到结果,相位偏移完全消除。

执行流程


完整巴特沃斯数字滤波完整流程:

  • 输入参数:滤波器阶数 N,采样频率,截止频率,原始输入信号数组。
  • 预畸变计算模拟截止角频率,校正双线性变换频率扭曲。
  • 求解模拟巴特沃斯原型极点。
  • 通过双线性变换将模拟极点映射为 Z 域极点。
  • 计算得到数字滤波器分子系数 b、分母系数 a。
  • 系数归一化,保证直流增益为 1。
  • 滤波运算:
    • 实时模式:正向递归差分方程计算输出;
    • 离线模式:执行 filtfilt 双向零相位滤波。
  • 返回滤波完成信号。

⚠️注意:阶数不要设置过大,一般工程 2~8 阶;阶数过高系数精度敏感,容易造成滤波器不稳定。

算法性能分析


  • 时间复杂度

    和 FIR 相比:达到同等阻带衰减,IIR 巴特沃斯需要阶数远小于 FIR,计算开销更低。

    • 系数求解:,N 为滤波器阶数;
    • 滤波运算:每一个采样点运算量 ;信号长度 M,总复杂度
  • 空间复杂度,存储 a、b 系数与历史输入输出缓存。

  • 稳定性

    • 只要极点全部落在 Z 平面单位圆内,滤波器稳定;
    • 阶数过高、浮点精度不足会造成极点漂移,出现震荡发散。
  • 相位特性

    • 普通正向巴特沃斯:非线性相位,信号不同频率分量时间延迟不一致,波形会发生相位畸变;实时采集(传感器)场景要重视。
    • filtfilt 双向滤波:零相位,但延迟等于全部信号长度,只适合离线。
  • 频率特性

    • ✅通带:幅度响应最大平坦,无波纹;
    • ❌阻带衰减速度不如切比雪夫滤波器;阶数每提升 1 阶,阻带增加 20dB / 十倍频衰减。

C# 完整代码(无任何第三方库)


功能:巴特沃斯低通滤波器;包含:系数计算、正向实时滤波、filtfilt 零相位双向滤波。 仅使用.NET 原生 System,不引用信号处理库。

cs 复制代码
using System;
using System.Collections.Generic;

namespace ButterworthFilter
{
    /// <summary>
    /// 巴特沃斯低通IIR滤波器,无第三方库
    /// </summary>
    public class ButterworthLowpass
    {
        /// <summary>
        /// a系数 分母 a[0]=1
        /// </summary>
        public double[] A { get; private set; }
        /// <summary>
        /// b系数 分子
        /// </summary>
        public double[] B { get; private set; }

        private readonly int _order;
        private readonly double _fs;
        private readonly double _fc;

        /// <summary>
        /// 构造巴特沃斯低通
        /// </summary>
        /// <param name="order">阶数,建议2~8</param>
        /// <param name="fs">采样频率 Hz</param>
        /// <param name="fc">-3dB截止频率 Hz</param>
        public ButterworthLowpass(int order, double fs, double fc)
        {
            if (fc <= 0 || fs <= 0) throw new ArgumentException("频率必须大于0");
            if (fc >= fs / 2) throw new ArgumentException("截止频率必须小于奈奎斯特频率 fs/2");
            _order = order;
            _fs = fs;
            _fc = fc;
            ComputeCoefficients();
        }

        /// <summary>
        /// 计算巴特沃斯数字滤波器系数:双线性变换+预畸变
        /// </summary>
        private void ComputeCoefficients()
        {
            double T = 1.0 / _fs;
            // ========== 1.预畸变截止频率 ==========
            double wd = 2 * Math.PI * _fc;
            double wa = (2.0 / T) * Math.Tan(wd * T / 2.0);

            // ========== 2.模拟巴特沃斯极点 ==========
            List<Complex> sPoles = new List<Complex>();
            for (int k = 0; k < _order; k++)
            {
                double angle = Math.PI * (_order + 2 * k + 1) / (2 * _order);
                double real = -wa * Math.Cos(angle);
                double imag = wa * Math.Sin(angle);
                sPoles.Add(new Complex(real, imag));
            }

            // ==========3.双线性变换 s→z ==========
            List<Complex> zPoles = new List<Complex>();
            foreach (var s in sPoles)
            {
                // z = (2/T + s) / (2/T - s)
                Complex twoOverT = new Complex(2.0 / T, 0);
                Complex z = (twoOverT + s) / (twoOverT - s);
                zPoles.Add(z);
            }

            // ==========4.由极点构造分母多项式A ==========
            Complex[] aPoly = { new Complex(1, 0) };
            foreach (var p in zPoles)
            {
                aPoly = PolyMultiply(aPoly, new[] { new Complex(1, 0), -p });
            }

            // ==========5.低通分子多项式:全部零点位于 z=-1 ==========
            Complex[] bPoly = { new Complex(1, 0) };
            for (int i = 0; i < _order; i++)
            {
                bPoly = PolyMultiply(bPoly, new[] { new Complex(1, 0), new Complex(1, 0) });
            }

            // ==========转为double数组,归一化增益==========
            A = Array.ConvertAll(aPoly, c => c.Real);
            B = Array.ConvertAll(bPoly, c => c.Real);

            // 直流增益归一化 z=1代入 H(z)
            double gainB = 0;
            double gainA = 0;
            foreach (var v in B) gainB += v;
            foreach (var v in A) gainA += v;
            double gain = gainB / gainA;
            for (int i = 0; i < B.Length; i++) B[i] /= gain;
        }

        /// <summary>
        /// 多项式复数乘法
        /// </summary>
        private Complex[] PolyMultiply(Complex[] p1, Complex[] p2)
        {
            Complex[] res = new Complex[p1.Length + p2.Length - 1];
            for (int i = 0; i < res.Length; i++) res[i] = new Complex(0, 0);
            for (int i = 0; i < p1.Length; i++)
                for (int j = 0; j < p2.Length; j++)
                    res[i + j] += p1[i] * p2[j];
            return res;
        }

        /// <summary>
        /// 正向滤波(实时可用,带相位延迟)
        /// </summary>
        /// <param name="input">输入信号数组</param>
        /// <returns>滤波输出</returns>
        public double[] FilterForward(double[] input)
        {
            int n = input.Length;
            double[] output = new double[n];
            int lenA = A.Length;
            int lenB = B.Length;

            for (int i = 0; i < n; i++)
            {
                double sumB = 0;
                for (int k = 0; k < lenB; k++)
                {
                    int idx = i - k;
                    if (idx >= 0) sumB += B[k] * input[idx];
                }
                double sumA = 0;
                for (int k = 1; k < lenA; k++)
                {
                    int idx = i - k;
                    if (idx >= 0) sumA += A[k] * output[idx];
                }
                output[i] = sumB - sumA;
            }
            return output;
        }

        /// <summary>
        /// filtfilt 零相位双向滤波,仅适合离线数据
        /// 正向滤波→反转→滤波→反转
        /// </summary>
        public double[] FilterFiltFilt(double[] input)
        {
            double[] y1 = FilterForward(input);
            Array.Reverse(y1);
            double[] y2 = FilterForward(y1);
            Array.Reverse(y2);
            return y2;
        }

        #region 内部复数结构体,不依赖System.Numerics
        private struct Complex
        {
            public double Real { get; }
            public double Imag { get; }
            public Complex(double real, double imag)
            {
                Real = real;
                Imag = imag;
            }
            public static Complex operator +(Complex a, Complex b)
            {
                return new Complex(a.Real + b.Real, a.Imag + b.Imag);
            }
            public static Complex operator -(Complex a, Complex b)
            {
                return new Complex(a.Real - b.Real, a.Imag - b.Imag);
            }
            public static Complex operator *(Complex a, Complex b)
            {
                double r = a.Real * b.Real - a.Imag * b.Imag;
                double i = a.Real * b.Imag + a.Imag * b.Real;
                return new Complex(r, i);
            }
            public static Complex operator /(Complex a, Complex b)
            {
                double den = b.Real * b.Real + b.Imag * b.Imag;
                double r = (a.Real * b.Real + a.Imag * b.Imag) / den;
                double i = (a.Imag * b.Real - a.Real * b.Imag) / den;
                return new Complex(r, i);
            }
        }
        #endregion
    }

    // 测试示例
    class Program
    {
        static void Main(string[] args)
        {
            int fs = 1000;     //采样频率1000Hz
            double fc = 10;    //截止频率10Hz
            int order = 4;     //4阶巴特沃斯低通

            ButterworthLowpass filter = new ButterworthLowpass(order, fs, fc);

            //构造测试信号:5Hz+50Hz高频噪声
            List<double> signal = new List<double>();
            for (int t = 0; t < 2000; t++)
            {
                double s1 = Math.Sin(2 * Math.PI * 5 * t / fs);
                double s2 = 0.4 * Math.Sin(2 * Math.PI * 50 * t / fs);
                signal.Add(s1 + s2);
            }

            double[] resForward = filter.FilterForward(signal.ToArray());
            double[] resFiltFilt = filter.FilterFiltFilt(signal.ToArray());

            Console.WriteLine("A系数:" + string.Join(",", filter.A));
            Console.WriteLine("B系数:" + string.Join(",", filter.B));
            Console.WriteLine("完成滤波");
        }
    }
}

代码说明

  • 内置自定义Complex复数结构体,不引入 System.Numerics,完全原生;
  • 实现双线性变换 + 预畸变,正确生成巴特沃斯低通 a、b 系数;
  • FilterForward:正向递归滤波,适合嵌入式、实时传感器数据流;
  • FilterFiltFilt:零相位双向滤波,离线数据分析,消除相位偏移;
  • Main 函数提供测试案例:混合 5Hz 有效信号 + 50Hz 噪声演示滤波效果。

扩展提示:高通、带通、带阻可以在模拟原型完成频率变换后再执行双线性变换。

优缺点


优点

  • 通带幅度最大平坦,通带无波纹,这是巴特沃斯最核心优势;
  • ✅同等衰减条件下,对比 FIR 滤波器阶数低,计算量小;
  • ✅设计直观:仅需要阶数、截止频率两个核心参数;
  • ✅模拟电路历史积累成熟,数字实现工程普及度高。

缺点

  • ❌IIR 固有非线性相位,实时滤波会造成波形相位畸变;
  • ❌阻带衰减速率一般,同阶数衰减弱于切比雪夫滤波器;
  • ❌阶数过高,浮点精度问题会带来滤波器不稳定风险;
  • ❌filtfilt 零相位滤波不能用于实时流数据,需要完整全部输入序列;
  • ❌瞬态响应差,信号起始位置会有暂态震荡。

适用场景


✅适合场景

  • 传感器离线数据处理:加速度、压力、温度等信号降噪,使用 filtfilt 零相位;
  • 对通带波纹要求严格,不希望通带内信号幅度起伏;
  • 资源受限嵌入式设备,希望低计算开销实现滤波,能够接受相位滞后;
  • 音频信号预处理。

❌不适合场景

  • 需要严格线性相位的实时系统(推荐 FIR 滤波器);
  • 需要陡峭阻带衰减、资源充足场景(优先切比雪夫);
  • 高阶实时闭环控制,相位偏移会影响闭环稳定性。

总结


巴特沃斯滤波属于IIR 数字信号处理算法 ,分为滤波器系数设计算法与差分方程递归滤波运算两部分。核心设计目标是通带幅度最大平坦。工程选型权衡点:通带无波纹换取阻带衰减能力,同时牺牲相位线性度。 实时场景使用正向滤波,接受相位延迟;离线分析优先 filtfilt 双向零相位滤波;阶数工程推荐 2‑8 阶。C# 原生实现无需第三方库,可直接迁移到嵌入式仿真、上位机信号解析项目。

相关推荐
北域码匠23 天前
互补滤波算法完整详解(C# 原生实现,无第三方库)
惯性导航·互补滤波·c# 算法实现·imu 姿态解算·传感器融合算法·嵌入式滤波算法·平衡小车算法