1. 从一次海试数据异常说起:升沉测量中的相位滞后问题
如果你做过船舶或海洋平台的升沉测量,大概率遇到过这样一种情况:加速度计积分出来的升沉位移,在长周期涌浪下看着还行,可一旦海况变得复杂、波浪周期变短,测量结果就开始"对不上"——不是幅值偏小,就是相位明显超前或滞后于真实运动。更让人头疼的是,当你回头去查算法文档,发现里面关于高通滤波器相位滞后的描述,和实际跑出来的数据根本对不上号。
这不是个例。升沉测量本质上是从加速度信号出发,经过两次积分得到位移,而积分过程会引入低频漂移,必须用高通滤波器把低频分量滤掉。问题就出在这个高通滤波器上:它确实能抑制漂移,但同时也会在通带边缘引入相位滞后。很多技术资料在描述这个滞后时,要么一笔带过,要么给出的公式和实际离散实现不一致,导致工程人员在补偿相位时用错了模型,最终测量精度上不去。
这篇内容就是围绕"升沉测量中高通滤波器相位滞后描述错误"这个具体问题展开的。我会把相位滞后到底怎么产生的、常见描述错在哪里、怎么用正确的方式去建模和补偿,以及实际工程中怎么验证,一层层拆开讲清楚。适合做船舶运动测量、惯性导航、海洋工程信号处理的同行参考,也适合刚接触升沉算法、想搞明白"为什么我的相位总是对不上"的朋友。
先说结论:相位滞后描述错误,通常不是滤波器本身设计错了,而是描述模型和实际实现不匹配。模拟域的相位公式直接套到离散域,或者忽略了滤波器阶数、采样率、截止频率之间的耦合关系,都会让补偿量算错。下面我从原理到实操,把这件事讲透。
2. 高通滤波器在升沉测量里到底扮演什么角色
2.1 为什么升沉测量非要用高通滤波器
升沉测量的一般流程是:加速度计测到垂向加速度,第一次积分得到垂向速度,第二次积分得到垂向位移。理想情况下,如果加速度信号是纯正弦且没有零偏,两次积分就能还原位移。但现实里加速度计有零偏、有噪声,积分会把零偏变成随时间线性增长的速度漂移,再积分就变成二次增长的位移漂移。几分钟下来,位移可能漂到几百米,完全不可用。
所以必须在积分链路里加高通滤波器,把低频和直流分量压掉。高通滤波器的截止频率通常选在波浪主频以下,比如0.02Hz到0.05Hz这个量级,具体取决于你关心的最低波浪周期。这样一来,长周期的漂移被抑制,而真正关心的波浪运动频段能通过。
但高通滤波器不是理想砖墙,它在截止频率附近有一个过渡带,过渡带内既有幅值衰减,也有相位变化。这个相位变化就是相位滞后的来源。对于升沉测量来说,相位误差直接对应时间上的延迟,如果滞后0.5秒,在周期8秒的波浪里就是22.5度的相位差,换算成位移误差可能达到幅值的百分之几到十几,高精度场景下这是不能接受的。
2.2 相位滞后不是"错误",描述错误才是问题
这里要澄清一个概念:高通滤波器有相位滞后,这是物理规律,不是bug。一阶高通滤波器在截止频率处的相位是+45度(注意符号,取决于定义),在远高于截止频率处趋近于0度,在远低于截止频率处趋近于+90度。二阶高通滤波器相位变化更陡,截止频率处可能达到+90度甚至更多。
真正的问题是,很多文档在描述这个相位特性时,用的是模拟域的连续传递函数,比如一阶高通写成:
H(s) = s / (s + ωc)然后直接给出相位公式:
φ(ω) = 90° - arctan(ω / ωc)这个公式本身没错,但它描述的是模拟滤波器。实际工程里,滤波器是在数字域实现的,用的是双线性变换或者冲激响应不变法离散出来的差分方程。离散化之后,相位特性会发生畸变,尤其是在截止频率附近和高频段。如果你拿模拟域的相位公式去算补偿量,就会引入系统性误差。
更隐蔽的一种错误是:有些资料把相位滞后的符号写反了,或者把高通滤波器的相位说成"超前"。这会导致补偿方向搞错,越补越差。我在实际项目里就见过,有人把补偿量加反了,结果升沉测量的相位误差从原来的10度变成了20度。
2.3 相位滞后对升沉测量的实际影响有多大
为了让大家有个直观感受,我列一个简单的对照表。假设波浪周期为8秒,对应频率0.125Hz,高通滤波器截止频率设为0.03Hz,采用二阶巴特沃斯高通。
| 滤波器类型 | 截止频率 | 在0.125Hz处的相位滞后 | 对应时间延迟 | 对升沉幅值的影响 |
|---|---|---|---|---|
| 一阶高通 | 0.03Hz | 约13.5度 | 约0.3秒 | 幅值衰减约1% |
| 二阶高通 | 0.03Hz | 约26度 | 约0.58秒 | 幅值衰减约2% |
| 四阶高通 | 0.03Hz | 约50度 | 约1.1秒 | 幅值衰减约5% |
从表里能看出来,阶数越高,相位滞后越明显。四阶高通在8秒周期处已经滞后50度,时间延迟超过1秒。对于动力定位、直升机甲板运动补偿这类应用,1秒的延迟是致命的。所以升沉测量里通常不会用太高阶的高通,二阶已经算比较激进,一阶更常见,但一阶的滚降太慢,抑制漂移能力有限,需要配合其他手段。
这里的关键点是:你必须知道自己用的滤波器在关心频段上的真实相位,而不是想当然地套公式。描述错误往往就发生在这一步。
3. 相位滞后描述错误的几种典型表现与根因
3.1 模拟域公式直接套用到离散域
这是最常见的一类错误。很多教材和论文在推导升沉算法时,为了简洁,直接用模拟域的传递函数分析相位,然后给出补偿公式。但实际代码里跑的是离散滤波器,比如用双线性变换得到的二阶节。
双线性变换有一个特性:它会把模拟频率轴非线性地映射到数字频率轴,也就是所谓的频率翘曲。在低频段,这种翘曲很小,可以忽略;但在截止频率附近,翘曲会变得明显。举个例子,采样率50Hz,截止频率0.03Hz,归一化频率是0.0006,翘曲几乎为零,这时候模拟公式和离散结果差别不大。但如果采样率降到5Hz,归一化截止频率变成0.006,翘曲开始显现,相位误差可能有几度。
更麻烦的是,有些实现用的是预畸变双线性变换,也就是在离散化之前先把截止频率预畸变一下,让离散后的截止频率正好落在设计值上。这种实现下,相位特性又和普通双线性变换不同。如果你不知道代码里用的是哪种,直接套模拟公式就会错。
我个人的经验是:只要采样率不是特别高,或者截止频率不是特别低,就不要偷懒用模拟公式,老老实实从离散传递函数算相位。离散传递函数可以写成:
H(z) = (b0 + b1*z^-1 + b2*z^-2) / (a0 + a1*z^-1 + a2*z^-2)令z = e^(jωT),其中T是采样周期,ω是角频率,然后求H(e^(jωT))的相角,就是真实相位。这个计算在MATLAB或Python里几行代码就能搞定,没必要省这个事。
3.2 忽略滤波器阶数和级联带来的相位叠加
另一个常见错误是只考虑单个二阶节的相位,忽略了级联。比如四阶高通通常是两个二阶节级联,总相位是两个节相位之和。如果你只算了一个节的相位,补偿量就少了一半。
还有一种情况是,升沉算法里除了高通滤波器,还有低通滤波器、积分器、甚至陷波器。这些环节都会贡献相位,必须全部算进去。我见过一个项目,工程师只补偿了高通滤波器的相位,结果发现总相位还是对不上,后来一查,积分器本身在低频段有-90度相位,虽然在高频段趋近于-90度但和频率有关,加上低通滤波器的相位,总误差就出来了。
所以正确的做法是:把整个信号链的传递函数写出来,从加速度输入到位移输出,逐环节相乘,然后求总相位。不要只盯着高通滤波器看。
3.3 相位符号和补偿方向搞反
这个错误听起来很低级,但实际中真不少见。高通滤波器的相位,在不同定义下符号可能不同。如果你看的是H(s) = s/(s+ωc),当ω远大于ωc时,H(jω) ≈ 1,相位趋近0;当ω远小于ωc时,H(jω) ≈ jω/ωc,相位趋近+90度。所以高通滤波器的相位是正的,意味着输出超前输入。
但注意,这是"超前"。而在升沉测量里,我们通常说"相位滞后",是因为从加速度到位移的整个链路里,积分器贡献了-90度每级的相位,高通滤波器的正相位只能部分抵消积分器的负相位,净效果仍然是滞后。如果你把高通滤波器的相位当成滞后去补偿,方向就反了。
更绕的是,有些文献定义高通滤波器为H(s) = ωc/(s+ωc),那是低通,不是高通。如果看错了传递函数,相位符号自然就错了。我建议在代码里统一用"输出相对输入的相位差"来描述,并且用实际数据验证符号,不要靠记忆。
3.4 用群延迟代替相位滞后
群延迟的定义是相位对频率的负导数,它描述的是信号包络的延迟,而不是单一频率正弦波的相位延迟。对于窄带信号,群延迟和相位延迟近似相等;但对于宽带信号,比如包含多个波浪频率的升沉信号,群延迟和相位延迟差别很大。
有些资料为了简化,直接用群延迟来近似相位滞后,然后在全频段用同一个延迟值去补偿。这在窄带情况下勉强能用,但升沉信号通常是宽带的,涌浪、风浪、涌浪叠加,频率成分丰富,用单一群延迟补偿会导致某些频率补偿过度,某些频率补偿不足。
正确的做法是:在频域逐频率计算相位,然后设计一个全通滤波器或者分数延迟滤波器来补偿。如果精度要求不高,至少也要在主要能量频段内做分段补偿,而不是全频段一个值。
4. 如何正确建模和补偿高通滤波器的相位滞后
4.1 从离散传递函数出发计算真实相位
第一步,拿到你实际使用的滤波器系数。假设你用Python的scipy设计了一个二阶巴特沃斯高通,截止频率0.03Hz,采样率50Hz:
from scipy import signal import numpy as np fs = 50.0 fc = 0.03 b, a = signal.butter(2, fc/(fs/2), btype='high')拿到b和a之后,计算幅频和相频响应:
w, h = signal.freqz(b, a, worN=4096, fs=fs) phase_deg = np.angle(h, deg=True)这样得到的phase_deg就是离散滤波器在0到fs/2频率范围内的真实相位。注意,freqz默认返回的相位是包裹在[-180, 180]度之间的,对于高通滤波器,低频段相位接近+90度,高频段接近0度,一般不会出现包裹问题,但如果你级联了多个环节,总相位可能超过180度,需要unwrap。
我通常会把关心的频段单独拿出来看,比如0.01Hz到0.5Hz,这是升沉测量的典型频段。然后在这个频段内,把相位拟合成一条曲线,方便后续补偿。
4.2 全通滤波器补偿的设计思路
知道了真实相位之后,补偿的思路是设计一个全通滤波器,它的相位特性正好是滤波器相位的相反数。全通滤波器的幅频响应恒为1,只改变相位,非常适合做相位补偿。
设计全通滤波器的方法有几种:
- 一阶全通:只有一个极点和一个零点,相位从0度变化到-180度(或+180度),适合补偿简单的相位曲线。
- 二阶全通:相位变化范围更大,可以拟合更复杂的相位曲线。
- 级联多个一阶或二阶全通:通过优化算法调整零极点位置,拟合目标相位。
在升沉测量里,如果只是补偿高通滤波器的相位,通常一个二阶全通就够了。但如果整个信号链的相位比较复杂,可能需要三到四个二阶节级联。
设计的时候,我习惯用最小二乘拟合:先设定全通滤波器的结构,然后把系数作为优化变量,目标函数是补偿后总相位在关心频段内尽可能接近0。用scipy.optimize.minimize就能做,初始值可以根据经验给。
这里有个坑:全通滤波器在补偿相位的同时,会引入自己的群延迟,如果群延迟太大,实时性会受影响。所以设计完之后要检查一下群延迟,确保在可接受范围内。
4.3 实时实现中的注意事项
离线处理的时候,你可以用filtfilt做零相位滤波,也就是正向滤一遍再反向滤一遍,相位正好抵消。但升沉测量通常是实时的,不能用filtfilt,只能用单向滤波,所以相位滞后必然存在,必须补偿。
实时补偿全通滤波器的时候,要注意状态变量的初始化。如果滤波器刚启动,状态变量是0,输出会有暂态,相位补偿也会不准。我通常的做法是:在正式输出之前,先让滤波器跑一段预热数据,或者用前几个采样点做状态初始化。预热时间至少要是滤波器时间常数的5到10倍。
另外,如果采样率不是固定的,比如有些系统会根据负载动态调整采样率,那滤波器的相位特性也会变,补偿系数需要跟着变。这种情况我建议要么固定采样率,要么把补偿系数做成采样率的函数,实时查表。
还有一个实际问题是定点运算。如果升沉算法跑在嵌入式DSP上,用的是定点数,滤波器系数的量化误差会影响相位。特别是全通滤波器,零极点位置对系数很敏感,量化误差可能导致相位补偿偏差几度。所以定点实现时,系数要用足够的位宽,并且做仿真验证。
5. 实测验证:怎么确认相位补偿真的对了
5.1 用仿真信号做闭环验证
最直接的方法是用仿真信号验证。构造一个已知频率和相位的正弦加速度信号,比如:
t = np.arange(0, 100, 1/fs) f_wave = 0.125 # 8秒周期 acc = np.sin(2*np.pi*f_wave*t)然后把这个加速度信号通过你的升沉算法(积分+高通+相位补偿),得到位移输出。理论上,位移应该是加速度的二次积分,即:
disp_true = -1/(2*pi*f_wave)^2 * np.sin(2*np.pi*f_wave*t)比较算法输出和理论位移的相位差,如果补偿正确,相位差应该接近0。如果还有明显偏差,说明补偿量不对。
我一般会扫频验证,从0.02Hz到0.5Hz,每个频率点都测一下相位误差,画成曲线。如果全频段误差都在正负2度以内,说明补偿合格。如果某些频段误差大,就要回去检查滤波器模型。
5.2 用实际海试数据做交叉验证
仿真验证通过之后,还要用实际数据验证。实际数据的好处是包含真实的噪声、多频率成分和非线性因素。验证方法可以是:
- 用高精度参考传感器(比如光学测量或者GNSS升沉)同步采集,和算法输出对比。
- 如果没有参考传感器,可以用两个不同原理的算法交叉验证,比如一个用高通+补偿,一个用自适应滤波,看两者输出是否一致。
- 还可以做一致性检验:把算法输出再微分两次,看能不能还原加速度信号,如果相位补偿错了,微分后的信号和原始加速度会有相位差。
我在实际项目里发现,仿真验证通过但海试数据对不上的情况,多半是因为实际信号的频率成分比仿真复杂,或者滤波器在实际实现中有额外的延迟(比如通信延迟、计算延迟)。这些延迟也要算进总相位里。
5.3 常见问题排查清单
如果你发现相位补偿后还是不对,可以按下面这个清单逐项排查:
| 排查项 | 可能问题 | 检查方法 |
|---|---|---|
| 滤波器系数 | 系数和设计不符 | 打印实际使用的b、a,和设计值对比 |
| 采样率 | 采样率设置错误 | 确认代码里的fs和实际硬件一致 |
| 相位符号 | 补偿方向反了 | 用单频正弦测试,看补偿后误差是变大还是变小 |
| 级联环节 | 漏算了某个环节的相位 | 列出完整信号链,逐环节算相位 |
| 状态初始化 | 暂态未消除 | 检查预热时间是否足够 |
| 定点量化 | 系数精度不够 | 用浮点仿真对比定点结果 |
| 额外延迟 | 通信或计算延迟 | 测量从采样到输出的总时间 |
这个清单我用了很多次,基本上能覆盖90%以上的相位问题。剩下的10%可能是多径效应、传感器安装偏差等,那就要具体问题具体分析了。
6. 几个容易踩的坑和我的实操心得
6.1 不要迷信"零相位滤波"这个词
很多资料说filtfilt可以实现零相位滤波,这话没错,但filtfilt只适合离线处理。实时系统里用不了,因为你需要未来的数据。有些工程师为了追求零相位,在实时系统里用filtfilt,结果引入更大的延迟,得不偿失。
实时系统里,正确的思路是"单向滤波+相位补偿",而不是追求零相位。补偿做得好,残余相位误差可以控制在1度以内,完全够用。
6.2 截止频率不是越低越好
有人觉得截止频率越低,抑制漂移越彻底,升沉测量越准。但实际上,截止频率越低,滤波器在波浪频段的相位滞后越大,补偿难度越高。而且截止频率太低,滤波器的时间常数变大,启动暂态变长,实时性变差。
我的经验是:截止频率选在主要波浪频率的1/5到1/10之间比较合适。比如主要波浪周期8秒,频率0.125Hz,截止频率选0.015Hz到0.025Hz。这样既能抑制漂移,又不会在波浪频段引入太大相位滞后。
6.3 相位补偿要留余量
实际系统里,除了滤波器相位,还有传感器延迟、AD转换延迟、计算延迟等。这些延迟加起来可能有几十毫秒。如果你只补偿滤波器相位,总相位还是会有偏差。所以补偿的时候要留一定余量,或者把已知的额外延迟也建模进去。
我通常会在补偿后留2到3度的余量,然后通过实测微调。微调的方法很简单:给系统一个已知频率的激励,看输出相位,然后调整补偿系数,直到误差最小。
6.4 文档和代码要同步更新
相位滞后描述错误,很多时候是因为文档写的是旧版本,代码已经改了。比如滤波器阶数从二阶改成四阶,但文档里的相位公式还是二阶的。这种不同步会导致后续维护的人用错模型。
我的做法是:把滤波器系数和相位补偿系数都写在配置文件里,代码和文档都从配置文件读取,这样就不会出现描述和实现不一致的问题。另外,每次修改滤波器参数,都要重新跑一遍相位验证,确保补偿系数跟着更新。
7. 写在最后:一点个人体会
升沉测量里的相位问题,说到底是一个"模型和现实匹配"的问题。高通滤波器的相位滞后本身不复杂,复杂的是整个信号链的相位叠加,以及离散实现带来的偏差。我做了这么多年,最大的体会是:不要相信任何没有经过实测验证的相位公式,包括我自己写的。每次换滤波器、换采样率、换硬件平台,都要重新验证一遍相位。
另外,相位补偿不是越精确越好,而是要和控制系统的需求匹配。有些应用对相位不敏感,补偿到5度以内就够了;有些应用要求1度以内,那就得用更复杂的补偿结构。关键是搞清楚你的应用到底需要多高的相位精度,然后把精力花在刀刃上。
如果你正在被升沉测量的相位问题困扰,建议先从仿真入手,把信号链的每个环节都单独测一遍相位,找到误差最大的那个环节,重点突破。大部分情况下,问题都出在离散滤波器的相位描述上,把这块搞对了,整体精度会有明显提升。