☰
MATLAB highpass函数详解:从滤波原理到工程实战
2026/10/14 1:44:12 网站建设 项目流程

拿到一列采集回来的信号,波形叠了一堆乱七八糟的东西:整体有个缓慢抬升的趋势,中间夹着一段比较稳定的波动,还有一些毛刺一样的噪声。很多人第一反应是调一下、平滑一下,或者直接塞给滤波器,但真正动手才发现,最该先干的事是“把不该要的低频先拿掉”。如果只让我从MATLAB信号处理工具箱里挑一个最省心的高通滤波入口,我首选highpass函数。它解决的问题很直接:把低于指定截止频率的成分压下去,留下高频部分。这篇文章会把这个函数的用法、参数背后的设计逻辑、实操中的坑,以及和周边方案的选型差异全部讲透。不管你是刚接触信号处理的学生,还是已经写了几年数据处理脚本的工程师,看完应该都能直接拿去用。

1. highpass函数解决什么问题——从信号里的“杂讯”说起

1.1 高通滤波到底在滤什么

所谓高通,就是“让高频通过、让低频滚蛋”。信号里的频率成分不是抽象概念,它对应着波形变化的快慢:变化很慢的漂移属于低频,来回快速振荡属于高频,完全不动的直流分量则是频率为0的特殊存在。实际采集到的原始数据,往往是好几种频率成分叠加在一起的结果。比如一个传感器输出的电压信号,真实有价值的变化可能是每秒几十次的振荡,但电路温漂和环境影响又会让基线慢慢上下移动,这个移动在频域里就是零点几赫兹的低频分量。

高通滤波就是把这两类东西拆开的工具。你可以把它想象成一个筛子:低频的“大颗粒”被挡在筛子上面,高频的“细颗粒”顺利漏下去。highpass函数正是MATLAB里帮你完成这一步的封装函数。它内部会设计出合适的高通滤波器,然后对数据完成滤波,全程只需要你告诉它“截止频率定在哪”。截止频率这个参数很关键,它决定了筛子的孔径:设定得越高,被滤掉的低频成分就越多,剩下的信号也就越“干净”,但同时也可能误伤你原本关心的低频信息。

1.2 什么时候一定得用高通

高通滤波不是所有场景都要上,但它有自己的甜蜜点。我平时用highpass最多的地方,是处理传感器数据里的基线漂移。比如加速度计静止放在桌面上时,输出本应是一条平线,但实际测出来会缓慢上下漂移;这时候如果直接分析微小的振动分量,漂移会彻底淹没真正的信号。用高通滤掉零点几赫兹以下的漂移,整个波形立马回到相对平稳的状态。

再比如生物电信号处理,心电、脑电、肌电这类信号里经常混着呼吸、肢体运动带来的低频干扰,这些干扰的频率往往低于目标信号的频段,高通滤波几乎是标配。音频处理里也常见:一些录音设备会引入直流偏置,或者房间里存在低频轰鸣声,用一个几十赫兹的高通就能让声音清爽很多。振动监测、电力谐波分析、设备故障诊断这些领域就更不用说了,低频趋势不去掉,后面的特征提取全都会被带偏。

2. 语法与参数拆解:一次性把输入输出讲透

2.1 最小调用形式:只给截止频率

最简单的用法是y = highpass(x, wpass),这里x是输入的信号,wpass是归一化截止频率,取值在0到1之间。注意这里的“归一化”三个字,它表示截止频率相对于奈奎斯特频率的比例,奈奎斯特频率等于采样率的一半。如果不提供采样率,函数只能按默认的数学频率来理解wpass,这是很多新手踩坑的地方,后面我会专门展开。

举个小例子,如果数据采样率是200Hz,奈奎斯特频率就是100Hz。想让10Hz以下的成分被滤掉,归一化截止频率就是 10/100 = 0.1。此时调用y = highpass(x, 0.1)就能完成滤波,输出y和输入x长度一样。这种形式的好处是简洁,适合在不同采样率的数据里只需要“按比例”滤波的场景,比如你想统一把所有信号的最高频率以下5%的部分都去掉,那wpass写死就行,不用关心每条数据采样率是多少。

2.2 带采样率的调用:回到物理世界

实际工程里,我们更关心的往往是物理频率,也就是赫兹。这时候用带采样率的版本:y = highpass(x, wpass, fs),其中fs是采样率,单位Hz,而wpass这个参数此时也变回普通频率,单位同样为Hz。这句代码的意思是:把x中低于wpassHz的成分滤掉。

比如做心电信号处理时,想滤掉0.5Hz以下的基线漂移,采样率是250Hz,就写y = highpass(ecg, 0.5, 250)。这种写法最直观,也最容易跟实际场景对应,是我在项目里最常用的形式。有了采样率,函数内部会自动把物理频率换算成内部所需的归一化频率,然后完成滤波,不需要你自己做任何换算。

2.3 两个关键的进阶参数

默认参数在很多场景下够用,但highpass还提供两个重要的名称-值参数,熟读它们能让滤波质量上一个台阶。

第一个是'Steepness',表示过渡带的陡峭程度,默认0.85,取值在0到1之间。过渡带是截止频率附近“从通过到阻挡”的过渡区间,这个区间越窄,滤波器就越接近理想状态,对截止点附近的频率区分就越清晰。代价是滤波器阶数会明显变高,计算变慢,而且信号边缘更容易出现振铃。实际操作中,我一般不会一上来就调到0.95,除非测试确认需要。

第二个是'StopbandAttenuation',默认60dB,表示阻带里低频成分被压低的程度。默认60dB已经意味着阻带幅度降到原始值的千分之一左右,绝大多数场景下足够。如果你希望低频被压得更狠,比如要做严格的前置处理,再调高到80dB,但要注意阶数也会跟着涨。这两个参数可以一起设置,比如:

y = highpass(x, 10, fs, 'Steepness', 0.9, 'StopbandAttenuation', 80);

2.4 把滤波器对象拿到手

highpass还有一个经常被忽略的调用方式:[y, d] = highpass(x, wpass, fs)。多算出来的d是一个digitalFilter对象,它代表函数内部实际设计出的那个滤波器。拿到它之后能做很多事,比如用fvtool(d)打开滤波器可视化工具,看幅频响应曲线;用freqz(d)直接画幅度和相位响应;甚至拿它继续处理其他数据:y2 = filter(d, x2)。

我特别建议在正式使用highpass之前,先把d拿出来看一眼。因为highpass对你隐藏了滤波器设计的细节,你只给了一个截止频率,但实际设计出来的滤波器长什么样、过渡带有多宽、阻带压制到多少,这些直接决定滤波效果。把d拿出来,等于把函数背后的“设计图”摊开在你面前,调试起来会踏实很多。

3. 设计逻辑与工作原理:把“为什么这样设计”说清楚

3.1 归一化频率的来龙去脉

数字信号处理里,频率这个概念经常要做归一化。离散信号采样后,能表示的最高频率是奈奎斯特频率,也就是采样率的一半。超过这个频率的信息已经被混叠污染,根本无法恢复。因此很多滤波器设计函数干脆以奈奎斯特频率为单位,把频率值归一化到0到1之间,1就代表奈奎斯特频率。

所以highpass(x, wpass)里的wpass=0.5,对应的实际截止频率是fs/4,而不是“信号幅度的一半”。如果采样率是1000Hz,那0.5对应250Hz。很多人第一次用,以为0.5就是0.5Hz,结果滤波后低频全部消失,高频也受到影响,完全不是预期效果,这就是归一化造成的理解偏差。解决办法很简单:能带采样率就带采样率,用物理频率写,既清晰又避免换算错误。

3.2 滤波器阶数、过渡带与波纹的权衡

理想的高通滤波器在频域里应该是一个完美的阶跃:低于截止频率的一段全部置零,高于截止频率的一段全部保留,中间没有过渡。但现实中不存在完美阶跃,因为那对应着无限长的滤波器脉冲响应,没法用有限数据实现。所有实际滤波器都是用有限阶数去逼近这个理想形状,上帝视角里就会看到三个现象:过渡带、通带波纹、阻带衰减。

过渡带的宽度由Steepness控制,越陡则越接近理想,但阶数也随之上升。阻带衰减由StopbandAttenuation控制,它决定低频被压低的程度。通带波纹则相对隐性,通常在设计过程中已经被自动控制住,不需要你显式指定。这三个特性是跷跷板:总希望滤波器又陡、又深、又短,但物理上做不到,必须在某个指标上妥协。highpass的默认参数选取的是一个比较平衡的组合,这就是为什么默认值大多数时候够用。

3.3 零相位滤波:为什么输出没有相位失真

我对highpass最满意的一点,是它默认采用零相位滤波。普通滤波器会让信号产生相位偏移,波形特征会沿着时间轴移动,这在分析波形形态时非常致命。比如心电信号的R波峰值位置一旦被移偏,后面算出的心率和间期全部是错的。零相位滤波的做法是先把信号正向过一遍滤波器,再把这结果倒过来过一遍,两次方向相反的相位偏移互相抵消,最终净相位偏移为零。

代价也有:这种滤波不是因果的,等于说它在处理某一点的时候用到了“未来的数据”,所以严格来说只能在离线处理时使用。另一个代价是边缘效应,信号开头和结尾那一小段,因为双向滤波会经历额外的瞬态过程,可能出现幅度畸变或振铃。实际操作中,很多人看到滤波结果开头结尾不正常就以为代码写错了,其实这是零相位滤波的正常现象,处理方式我后面会专门讲。

4. 实操演示:构造信号、调用函数、对比效果

4.1 构造一段模拟信号

为了把highpass的行为看明白,我建议你手里先有一段能“看见”各种频率成分的信号。下面这段代码在1000Hz采样率下构造了2秒数据,里面同时包含:一个2V直流分量,一个0.5幅值、3Hz的缓慢漂移,一个幅值1、30Hz的有效信号,以及一点随机噪声。

fs = 1000; % 采样率 1000 Hz t = (0:2*fs-1)/fs; % 2 秒时间轴 x = 2 ... % 直流分量 + 0.5*sin(2*pi*3*t) ... % 3 Hz 低频漂移 + sin(2*pi*30*t) ... % 30 Hz 有效信号 + 0.1*randn(size(t)); % 随机噪声

这段信号的时域波形看起来会是“一条上下漂移的波浪线”,30Hz的真实信号被低频漂移“架”在了上面。如果直接观察原始波形,你会觉得它很不稳定,像是有一种慢速起伏在主导整个形态,这正是低频分量在视觉上的表现。

4.2 第一次调用highpass

现在用10Hz作为截止频率做高通滤波,目的是把直流分量和3Hz漂移一起去掉,同时保留30Hz信号。

y = highpass(x, 10, fs);

滤波完成后再对比x和y的频谱,你会看到几个非常直观的变化:频谱最左侧代表直流的那个尖峰消失了,3Hz处原本耸立的谱线被压到几乎看不见,而30Hz附近的谱峰保持完好。如果只看时域波形,y会在0附近振荡,不再有明显的整体漂移,看起来干净很多。

画频谱对比可以用下面这段代码:

N = length(x); f = (0:N-1)*fs/N; X = abs(fft(x)); Y = abs(fft(y)); subplot(2,1,1); plot(f, X); title('滤波前频谱'); subplot(2,1,2); plot(f, Y); title('滤波后频谱');

这个流程是理解高通滤波效果最直接的方式:先构造已知成分的信号,滤波后看时域和频域的变化。

4.3 调整Steepness对比差异

同一个截止频率下,Steepness不同会产生肉眼可见的差别。我用同一个x分别做了两种滤波:

y_soft = highpass(x, 10, fs, 'Steepness', 0.6); y_sharp = highpass(x, 10, fs, 'Steepness', 0.95);

0.6的滤波器过渡带比较宽,3Hz附近被压制的程度不会那么极端,滤波结果相对柔和,信号边缘也更平滑。0.95的滤波器则非常“锋利”,3Hz几乎被彻底清除,但代价是滤波器阶数明显更高,信号的起始和结束段会出现更明显的振铃现象。如果不关心边缘那一点点畸变,只是想最大程度清除低频,0.95没问题;如果后续还要做精细特征分析,我建议先用默认0.85,看到结果再决定要不要调。

4.4 用fvtool检查滤波器特性

调参前最好先看滤波器本身的幅频响应。执行下面代码:

[y, d] = highpass(x, 10, fs); fvtool(d);

弹出的滤波器可视化工具会显示一条从低频到高频的幅度曲线。你能清楚看到10Hz以下被迅速压低,10Hz以上逐渐恢复到接近0dB。这条曲线上的斜坡就是过渡带,斜坡越陡,说明Steepness越高。观察这条曲线,你会发现所谓截止频率并不是一条垂直的线,而是一个平缓下降的区域。理解这一点之后,你对“10Hz高通”的直觉会更准确:它不是10Hz以上完全无损,而是10Hz附近的频率在过渡带上被部分衰减。这个工具调试阶段非常有用,尤其是当你怀疑某个频段滤得不够干净时,先看滤波器曲线,再看数据频谱,定位很快。

5. 踩坑清单:这些坑我基本都踩过

5.1 截止频率传错单位

这是用highpass最经典的坑。y = highpass(x, 50)没给采样率时,50会被当成归一化频率,而归一化频率必须小于1,所以MATLAB直接报错。真正危险的是传一个小于1但不符合预期的值,比如你想滤掉0.5Hz,写了y = highpass(x, 0.5),代码不会报错,但实际滤掉的是奈奎斯特频率的50%。如果采样率是1000Hz,那滤掉的是250Hz以下,信号几乎被滤光了。这种错误很隐蔽,因为程序跑得很顺利,结果却完全不可用。我的习惯是:传输中数据一律带上采样率参数,写成highpass(x, 0.5, fs),让函数按赫兹解释,从源头上杜绝单位混淆。

5.2 边缘瞬态:两头波形异常

使用零相位滤波后,信号开头和结尾的样本会经历一段瞬态响应,表现是波形在一头或两头出现明显的凹陷、突起或振荡。你可能会看到滤波后信号开头比中间低一大截,或者结尾处多出一段不应有的波动。这不是bug,是双向滤波的必然结果。

处理方案有三种。第一,如果数据足够长,直接把前5%和后5%的样本当作预热区舍弃,只分析中间平稳段。第二,对数据两端做延拓,比如用反射延拓把信号先扩展一段,滤波后再把延拓部分剪掉。注意不要用零填充延拓,因为高通滤波器会把零填充本身当成一个巨大的低频阶跃,反而引入更严重的边缘问题。第三,如果边缘段含有重要信息,被迫要在边缘附近取分析结果,那就得考虑放弃零相位滤波,改用带相位补偿的因果滤波方案。

5.3 输出长度与数据类型的细节

`highpass` 输出和输入 `x` 长度一定相同,这一点不必担心,但它对输入类型有要求。如果输入是 double 或 single,输出保持同样的类型。如果输入是时间表,函数会保留时间信息,对时间表中的每个数据变量分别滤波。如果输入是矩阵,函数默认对每一列分别滤波。如果你传入的是整数类型,比如 int16,函数可能会报错或提示转换,最稳妥的做法是先转成 double 再处理。

另外,如果x里包含NaN,滤波结果对应位置也会出现NaN,因为滤波器会把这些缺失值当作真实数据参与运算,NaN会一路传播。拿到数据后先检查有没有NaN,有就先插值或剔除,不要直接丢给highpass。

5.4 FIR还是IIR:实时滤波别直接用它

highpass默认的零相位处理对离线数据非常友好,但它不是实时的。所谓“实时”,意思是每来一个新样本就立刻输出一个滤波结果,而零相位滤波要等整段数据都拿到之后才能开始运算,这在概念上就不可能用于实时系统。

如果你的目标是把滤波算法部署到嵌入式设备或在线数据流里,正确做法是:先用highpass返回的滤波器对象d,再用filter(d, x)做因果滤波;或者直接用designfilt按需设计一个FIR或IIR滤波器,再用filter或dsp.HighpassFilter处理流式数据。这样会产生相位延迟,但换来的是实时可用性。选择FIR还是IIR也要看场景:FIR线性相位稳定、数值表现好,但阶数高;IIR阶数低、效率高,但相位非线性、调试时要留意稳定性。

6. 横向对比:highpass和其他滤波方案怎么选

6.1 与designfilt+filter对比

designfilt是更底层的滤波器设计入口,可以指定设计方法、滤波器类型、阶数、通带衰减参数等。用highpass相当于“傻瓜式”自动设计:适合快速验证和批量处理,缺点是内部设计过程被藏了起来,想精确控制某些指标时不够灵活。

如果需求是这样的——指定要一个100阶FIR高通滤波器,截止频率30Hz,采样率1000Hz——用designfilt写出来就是:

d = designfilt('highpassfir', ... 'FilterOrder', 100, ... 'CutoffFrequency', 30, ... 'SampleRate', 1000); y = filter(d, x);

两种途径没有优劣,关键在于需求。追求开发速度和代码可读性,选highpass;需要严格按指标设计滤波器、部署到实时系统或做滤波器对比研究,用designfilt。实际开发里,我喜欢先用highpass快速看清效果,确认截止频率合理之后,再用designfilt把最终方案固定下来。

6.2 与FFT频域截断对比

手工FFT滤波的思路是:对信号做FFT,把低频段的频谱系数直接置零,再IFFT回来。这种方法的优点是速度快、直觉上“想滤哪段滤哪段”,但它有两个明显问题。一是频谱置零相当于在频域乘了一个矩形窗,IFFT回到时域会在边缘产生振铃,也就是Gibbs现象。二是它对非平稳信号的处理很差,因为整段数据的FFT把时间信息抹平了,某一段出现的瞬态低频成分会被平均到整个时间段里。

highpass走的是正经的滤波器设计路线,有平滑的过渡带,不会出现矩形窗那么严重的振铃。相比之下,FFT截断适合一次性去直流或剔除特定窄带干扰,比如去掉50Hz工频,这种明确窄带目标用FFT更快。但通用滤波和形态分析,我基本都用highpass,尤其是在信号边缘形态比较重要的时候。

6.3 与medfilt1、smoothdata等去趋势手段对比

有时候你其实不需要“高通滤波”,只是想把毛刺和趋势去掉。中值滤波medfilt1对脉冲噪声特别有效,smoothdata系列方法可以通过滑动平均、局部回归等方式把曲线磨平。它们的共同点是属于非线性或启发式方法,没有明确的截止频率概念,但你没办法告诉它“保留10Hz以上的部分”。

这里有一个选型判断:如果你关心信号的频率成分,想知道“多少赫兹以上的信息被保留”,那必须用高通滤波器这类频域方法。如果你只是视觉上想去掉漂移、抹平毛刺,对频率没有严格定义,那用medfilt1或smoothdata反而更简单,而且对瞬态特征更友好。一句话总结:频域问题用频域工具,时域形态问题用时域工具。

7. 实战经验片段

7.1 传感器低频漂移消除的完整流程

我处理过不少传感器数据,最典型的流程是这样的:拿到信号后先画频谱图,用pwelch看一下低频部分到底有多大的能量。比如加速度计信号,通常在0.1Hz以下有一大团缓慢漂移的能量,而真正关心的振动频率可能在5Hz以上。这时候我先把截止频率设在1Hz左右,用highpass(x, 1, fs)滤一遍,看看时域波形是否回归到平稳;如果漂移还有残余,就把截止频率往上调到2Hz、3Hz,直到时域波形不再有明显基线起伏。

调截止频率时一定要克制:截止频率越高,滤掉的信息越多,如果真实信号里也有低频成分,会被一并摧毁。比如做倾斜角测量时,加速度计输出的重力分量本身就在0Hz附近,高通滤波会把这种“极低频有效信息”当作漂移滤掉,导致角度计算完全错误。所以每次调整截止频率前,我都会先问一句:这段信号里,哪些低频是噪声,哪些低频是有用的?边界不清时,宁可截止频率设低一点,再配合其他去噪手段。

7.2 音频去直流与低频轰鸣的注意事项

音频处理里我也经常用highpass。麦克风采集的音频如果有直流偏置,波形整体会偏离零线,听起来可能只是轻微的闷响,但对后续的自动增益控制、音量归一化都会造成干扰。用highpass(audio, 20, fs)可以把直流和20Hz以下的低频轰鸣一起去掉,声音立刻干净不少。

但音频领域用高通要格外小心。第一,人耳对相位失真比较敏感,零相位滤波虽然能消除相位偏移,但双向滤波带来的边缘处理在音频流式播放场景中不可行,实时播放入口还是要选因果FIR。第二,截止频率不宜设置过高,比如设在100Hz以上,会把贝斯、低音鼓这些原本有意义的低频内容切掉,声音会变得又干又薄。第三,立体声双声道信号做滤波时,最好对左右声道用完全相同的滤波器结构,避免左右声道相位差发生变化,否则声像定位会受影响。

我在实际使用中还有一个习惯:每次滤波完不会只看一眼时域波形就结束,一定会在频谱上确认“该衰减的确实衰减了,该保留的确实保留了”。同时把滤波前后的边缘段拉出来单独看一眼,确认没有因为零相位处理引入明显的异常。这三步做完,我对最终结果才算是有信心。highpass这个函数本身不复杂,复杂的是你怎么理解手里的信号,以及怎么判断滤波结果到底对不对。把参数含义吃透,把边缘效应和单位陷阱记住,它就是一个可以长期信赖的顺手工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询