ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

位移速度加速度信号互转实战:时域积分微分与FFT频域处理

位移速度加速度信号互转实战:时域积分微分与FFT频域处理

简介:围绕位移、速度与加速度信号互转的Matlab学习资源,面向信号处理与数据分析初学者,重点演示通过微分与积分在角位移、角速度及加速度等物理量之间进行转换。压缩包共4个文件,包括3个Matlab脚本与1个数据文件,分别提供正弦信号生成、加速度与速度互转、角速度与角位移换算等典型实现,并附带示例数据便于实战,整体仅有23KB,轻量便捷且易用。已有6120人浏览学习,适合需要快速掌握信号微分积分操作或验证相关算法的读者,也适合作为Matlab课程设计参考资料。通过脚本可掌握diff、cumsum、filter等函数的实际用法,并熟悉数据导入、预处理、转换、绘图检查与结果保存的完整流程,有助于提升理论与实践结合能力;代码结构清晰,便于二次修改与复用。

1. 位移、速度、加速度信号互转:振动测试里最被低估的一步

做振动台扫频验收时,某设备厂家给了一整份加速度时程,验收标准里却要求位移幅值,我和同事盯着数据看了半天,最后还是重新回到位移、速度、加速度信号互转这条老路上来。这三个量本质上是同一个运动过程的三种描述,差一个积分或微分,教科书里是一页纸的公式,放到实测信号上却处处是坑:积分会漂移,微分会放大噪声,频域法又带来端点效应。这篇文章把我在振动标定、结构监测和运动控制上反复用到的互转套路拆开讲,包含可以直接跑的代码、必须调的参数和踩过的坑。适合要做信号互转的工程师,也适合刚拿到加速度计数据却不知道下一步怎么算的朋友。

2. 从加速度到位移的时域积分:漂移从哪来,怎么消

2.1 直接 cumsum 会得到一个灾难性的位移曲线

加速度到位移的物理关系是 a(t)→v(t)→x(t),对应两次积分。数字域里最朴素的想法是直接用 numpy 的 cumsum 把加速度累加一遍,再累加一遍得到位移。第一次这么干的人几乎都会得到一条灾难性曲线:位移随时间越来越大,永远停不下来,峰值比真实振动幅值大几个数量级。

问题出在累积误差。加速度信号里只要残留一点点直流偏置或低频噪声,积分一次就变成线性趋势,积分两次就变成二次曲线。我用一个数字说明严重程度:假设加速度计零偏为 0.01g,也就是约 0.098 m/s²,这个偏差连续作用 10 秒,理论位移误差就是 0.5×0.098×10² ≈ 4.9 米。振动测试的位移通常只有毫米级,等于说结果完全没有参考价值。

所以时域积分的第一步不是选算法,而是承认噪声不可消除,只能靠预处理和分步积分压住它。常见做法是去均值、去趋势、必要时高通滤波,然后积一次,积完对结果再抠一次趋势。下面按这个顺序给出一套能直接用的流程。

2.2 梯形积分加初值修正:最少代码的可靠做法

时域积分有矩形法、梯形法和辛普森法。我一般用梯形法,它把每个采样间隔内的信号看作线性变化,对平滑信号的误差足够小,代码也简单。下面是一个带初值的实现:

import numpy as np def trapezoid_integral(signal: np.ndarray, dt: float, y0: float = 0.0) -> np.ndarray: """梯形法数值积分,返回与输入等长的积分结果。 signal : 输入信号,如加速度(m/s^2) dt : 采样间隔,单位秒 y0 : 积分初值,默认 0 """ n = len(signal) out = np.zeros(n) out[0] = y0 for i in range(1, n): out[i] = out[i - 1] + 0.5 * (signal[i - 1] + signal[i]) * dt return out

代码逻辑:每一步把上一时刻的累积值加上“当前梯形面积”,梯形面积是相邻两点平均后乘以时间间隔 dt。初值 y0 在物理上对应初始速度或初始位移,振动测试里通常未知,默认取 0 会让开头一小段偏差较大,但配合后面的去趋势操作,整体影响可控。循环版本容易读,但十万点以上建议改成向量化写法:

def trapezoid_integral_fast(signal: np.ndarray, dt: float, y0: float = 0.0) -> np.ndarray: """向量化梯形积分,长信号推荐。""" diff = 0.5 * (signal[1:] + signal[:-1]) * dt out = np.zeros(len(signal)) out[0] = y0 out[1:] = y0 + np.cumsum(diff) return out

两个版本结果一致,向量化版本在百万点数据上能快一个数量级。参数 dt 必须与采集设备一致,单位用秒;如果数据里是毫秒时间戳,先转成秒再传入。积出来的单位由输入单位乘以秒决定:加速度 m/s² 积一次得到 m/s,再积一次得到 m。

2.3 去均值与去趋势:积分前的两个强制预处理

直接拿原始加速度去积分,哪怕用了梯形法,结果照样飘。第一步是去均值,把整段数据的平均直流分量减掉,这一步能解决零偏带来的线性趋势,但解决不了缓慢变化的低频漂移。第二步用多项式拟合趋势并扣除:

def remove_polynomial_trend(signal: np.ndarray, order: int = 1) -> np.ndarray: """拟合多项式趋势并扣除。 order=1 去掉线性趋势,order=2 去掉二次趋势。 阶次不是越高越好,过高会误伤真实低频成分。 """ idx = np.arange(len(signal)) coeff = np.polyfit(idx, signal, order) trend = np.polyval(coeff, idx) return signal - trend

完整处理流程如下:

dt = 0.001 # 采样周期 1 kHz acc = np.loadtxt("acc.csv") # 采集卡导出的加速度 acc = acc - acc.mean() # 去直流 vel = trapezoid_integral_fast(acc, dt) vel = remove_polynomial_trend(vel, 1) # 速度层去一次线性趋势 disp = trapezoid_integral_fast(vel, dt) disp = remove_polynomial_trend(disp, 1) # 位移层修正残余基线

这里的顺序有讲究:先积速度,在速度层扣掉线性趋势,再积位移。如果一步直接积两次,速度层的线性漂移会被二次积分放大,到位移层变成二次曲线,只靠去除线性趋势压不住。去趋势的 order 一般取 1,只有当速度曲线明显呈抛物线时才考虑 order=2,取高了会把真实速度里的低频运动一起抹掉。

如果信号里有明显的周期性强低频干扰,比如 0.2 Hz 的温漂,多项式去趋势压不住,就要上高通滤波。我用 Butterworth 高通,截止频率尽量贴近目标信号的最低频率,一般取目标频率下限的一半左右。比如关心 2 Hz 以上的振动,高通截止设 1 Hz 左右;设太低滤不掉漂移,设太高会把 2~5 Hz 的真实振动幅值压小。

2.4 分步积分不是慢办法,是防漂移的关键设计

有人会问:速度层去一次趋势,位移层又去一次趋势,是不是有点“硬掰”?我的看法是,目标是长时程监测的位移时程时,这样做是被噪声逼出来的必要修正。加速度计的低频响应天生弱,直流和趋势项在积分里不断累积,不在中间层拦住它,它会在最终结果里冲出边界。

分步积分的代价是多了两次数组遍历,在现在硬件上几乎可以忽略。换来的好处是结果在零线附近波动,而不是跑到几十米外回不来。实际项目里,与其调一套复杂的自适应滤波,不如先用“去均值-积速度-去趋势-积位移-去趋势”这套流程跑通,再根据剩余漂移决定要不要上更高阶处理。

3. 从位移到速度和加速度:微分噪声放大怎么压

3.1 一阶差分这个最简单算法为什么一算一个崩

反向互转同样常见:编码器输出位置,伺服环需要速度;激光位移计测了振动位移,报告里还想给速度峰值。最土的实现是相邻两点一阶差分,写成公式就是 (x[n] - x[n-1]) / dt。问题是,差分对高频噪声的放大倍数约等于 1/dt。

举个例子:位移传感器分辨率 0.01 mm,采样率 5 kHz,噪声峰峰按 ±0.01 mm 算,差分后的速度噪声就是 ±0.01 / 0.0002 = ±50 mm/s。如果被测运动的真实速度只有几十 mm/s,噪声已经把信号完全淹没。即便传感器没这么差,电机换向、采集卡抖动这些毛刺也足够让差分结果变成锯齿状。我见过有人把位置信号做一阶差分得到速度曲线,波形像一团乱麻,后来发现源头不是运动本身,是采集噪声。

还要注意,一阶差分对真实的高频运动分量存在幅度畸变,频率越接近奈奎斯特频率,幅值偏差越大。所以微分这条路,优先考虑的不是“提高差分点数”,而是先想清楚怎么在动微分之前把噪声压住。

3.2 中心差分和 Savitzky-Golay:精度与平滑的折中

如果信号质量已经不错,中心差分是最常用的升级版。它用前后两个邻居的均值差除以 2dt,截断误差从 O(dt) 降到 O(dt²),对高频噪声的放大也小一些。

def central_diff(signal: np.ndarray, dt: float) -> np.ndarray: """中心差分求一阶导数,两端用前向/后向差分补齐。""" d = np.zeros_like(signal) d[0] = (signal[1] - signal[0]) / dt d[-1] = (signal[-1] - signal[-2]) / dt d[1:-1] = (signal[2:] - signal[0:-2]) / (2.0 * dt) return d

中心差分同样要求等间隔采样,时间轴有丢点时不能用。两端各缺一个点,我用单向差分补齐,但这意味着两端数据在后续使用中要谨慎,尤其是要做积分或统计峰值时。

当实测信号噪声明显,我更推荐直接上 Savitzky-Golay 滤波器的导数模式,它相当于在移动窗口里做多项式最小二乘拟合,再取该点的解析导数。scipy 里一行就能用:

from scipy.signal import savgol_filter # window_length 必须是奇数,且大于 polyorder+1 vel = savgol_filter(disp, window_length=15, polyorder=2, deriv=1, delta=dt)

参数说明:window_length 是平滑窗内的点数,15 对应 15 个采样点,窗口越大噪声压得越低,但会把真实速度里的快速变化拉平;polyorder 是拟合多项式阶次,我一般取 2,取 3 时对尖峰保留更好,但噪声也更大;delta 必须设置为 dt,否则导数结果的量纲会错。这个函数的好处是平滑和微分一步完成,不用先滤波再差分,坏处是窗口边缘的导数可信度低,所以我会把两端各十几点扔掉,或者给数据前后补一段缓冲。

3.3 先滤波再微分,顺序不能反

不同场景对“实时”要求不一样。离线分析里,正确的顺序一定是先做零相位低通滤波,再做中心差分或 SG 微分。先差分把高频噪声放大,再滤波虽然也能压回去,但被放大的毛刺会在滤波窗口内扩散,效果远不如先滤后微。零相位滤波用 scipy.signal.filtfilt,不要用 lfilter,后者会带来与频率相关的相位偏移,微分再加相移,最终速度相位会明显对不齐。

from scipy.signal import butter, filtfilt def zero_phase_lowpass(signal: np.ndarray, fs: float, cutoff: float, order: int = 4) -> np.ndarray: """零相位 Butterworth 低通滤波。""" nyq = fs / 2.0 b, a = butter(order, cutoff / nyq, btype="lowpass") return filtfilt(b, a, signal)

截止频率怎么定是这里的关键。我按关心运动的最高频率来设,比如结构监测关心 0.5~20 Hz,低通截止设在 30 Hz 或 50 Hz 都行,太高没滤掉噪声,太低把 20 Hz 附近的真实加速度衰减了。判断是否过度滤波的办法:滤完再算一次原始信号与滤波信号在关心频带内的幅值比,衰减超过 5% 就该提高截止频率。这一段属于逐案调整的部分,不抄参数直接上几乎都会翻车。

在线实时系统不能用 filtfilt,因为它是非因果的,需要整段数据。在线做法是普通 Butterworth 低通,配合群延迟补偿,或者改用移动平均和一阶低通。最稳妥的是用固定采样率的因果低通滤波,然后测量它的群延迟,在后续控制里做相位补偿,否则速度和位移之间的相位关系会乱。

4. 频域互转一招通吃:FFT 实现积分与微分

4.1 频域互转的数学:乘除以 jω 就是积分与微分

时域里的微分和积分,在频域就是乘或除以一个虚数因子。对信号做傅里叶变换后,每个频率分量的幅值乘以 jω 得到微分频谱,除以 jω 得到积分频谱,其中 ω = 2πf。这样做的最大优势是积分不再一步一个点地累积误差,而是对整段频谱做一次全局换算,低频噪声虽然同样会被放大,但不会产生随积分时长增长的“二次方漂移”。

这段数学看起来干净,落地时有两个现实问题。第一个是直流分量,频率等于 0 时 jω 等于 0,除法直接除零,所以频域积分必须把直流分量置零,等效于去均值。第二个是端点效应,FFT 内在假设信号是周期延拓的,实测信号首尾不连续,换算后两端往往出现大幅摆动。这两个问题处理不好,频域方法照样翻车。

4.2 FFT 互转的 Python 实现:含去趋势和端点处理

下面是我常用的频域积分函数:

def fft_integrate(signal: np.ndarray, dt: float, order: int = 1) -> np.ndarray: """频域积分,order=1 得速度,order=2 得位移。 直流分量被自动置零,等效于去均值。 """ n = len(signal) f = np.fft.rfftfreq(n, d=dt) spec = np.fft.rfft(signal) # 只对 f>0 的分量做除法,避免除零 mask = f > 0.0 out_spec = np.zeros_like(spec) out_spec[mask] = spec[mask] / (2j * np.pi * f[mask]) ** order return np.fft.irfft(out_spec, n)

频域微分只是把除号换成乘号:

def fft_derivative(signal: np.ndarray, dt: float, order: int = 1) -> np.ndarray: """频域微分,order=1 得速度,order=2 得加速度。""" n = len(signal) f = np.fft.rfftfreq(n, d=dt) spec = np.fft.rfft(signal) out_spec = spec * (2j * np.pi * f) ** order # 直流分量为 0,天然被置零 return np.fft.irfft(out_spec, n)

注意 order 是幂次,不是“积几次”的循环次数。f 的单位是 Hz,乘上 2jπ 后对应角频率;rfft 只取正频率,irfft 负责拼回实信号,往返之间能量是守恒的。调用之前,我一般会给原始信号两端做镜像延拓,每端延伸原长度 5%~10%,处理完再截掉,能明显压住端点摆动。

def mirror_extend(signal: np.ndarray, ratio: float = 0.05) -> tuple: """镜像延拓,返回延拓后的列表和应截取的区间。""" n = len(signal) k = max(1, int(n * ratio)) ext = np.concatenate([signal[k-1::-1], signal, signal[:-k-1:-1]]) return ext, (k, k + n)

逻辑说明:前后各补一段镜像数据,让首尾不连续变成局部的对称连续。做完 FFT 积分或微分后,取中间那段返回。延拓比例不是越大越好,5% 对大多数振动信号够用,延太长了等于给频谱多加人为低频成分,反而污染结果。

4.3 时域法与频域法怎么选

频域法适合离线、整段数据分析,比如试验后处理、数据回放。它无法边采集边实时输出,因为一次要处理整段数据。时域法可以做成流式处理,配合滤波也能扛住正常噪声,但漂移的根治能力不如频域。我把选型建议整理成表:

场景推荐方法原因
离线振动后处理,要求位移时程FFT 积分 + 镜像延拓全局换算,漂移远小于时域累积
实时伺服控制,编码器位置求速度低通滤波 + 中心差分或 SG因果性好,相位可补偿
已有大量时域业务逻辑,只想改小梯形积分 + 去趋势改动小,每一步可解释
频带窄、周期长的大地测量类数据时域积分配合高通FFT 对长周期分量泄漏更敏感

我的习惯是短数据段优先用 FFT,长时程监测用分步时域积分。频域法在频谱泄漏明显时,比如数据长度不是目标周期整数倍时,结果会出现栅栏效应,这时候先加窗再互转反而会压低两端真实值,所以要权衡。

5. 信号互转避坑指南:6 个实测踩坑记录

下面几条都是我在测振动台、处理位移数据和调运动控制环时亲手踩过的坑,有些是同事的教训,有些是我自己反复折腾总结出的经验。每个问题按现象、原因、解决三步写,方便对照排查。

5.1 坑一:积分后位移曲线整体漂走

现象:加速度积分得到的位移没有在零线附近波动,而是整体朝一个方向越走越远,十几秒后偏移到几十毫米甚至几百毫米。

原因:加速度计低频响应和零偏导致信号里存在直流分量或准直流漂移。积分一次变成速度的线性趋势,积分两次变成位移的抛物线,短时间内看着就是一条漂走的曲线。这是时域积分的头号翻车原因。

解决:积分前先减均值,再对速度层做一次线性去趋势,最后对位移层修正残余基线。如果还有缓慢漂移,用高通滤波把低于目标频带下限的周期压掉。高通截止频率不要超过目标信号最低频率的一半,否则会把真实振动一起滤掉。

5.2 坑二:微分后毛刺比信号还大

现象:位置数据做差分后,速度曲线像锯齿,毛刺幅度比理论速度还大,拿去做闭环控制会让电机嗡嗡响。

原因:一阶差分对高频噪声放大 1/dt。采样率越高,dt 越小,放大倍数越夸张。采集噪声里只要有一点高频尖峰,差分后就会变成大毛刺。

解决:先做零相位低通滤波再做中心差分,或直接用 Savitzky-Golay 的导数模式。低通截止频率按目标信号带宽选,先看频谱再定截止,不要盲选。如果已经入闭环,还要确认滤波引入的相位延迟。

5.3 坑三:FFT 互转端点处飞掉

现象:频域积分的结果大部分区间看着正常,但开头和结尾几十个点出现大幅摆动,峰值明显不真实。

原因:FFT 把信号当作周期延拓,实测数据的首尾一般不连续,周期性假设在端点处制造了突变。频域里的积分和微分对高频突变非常敏感,于是端点飞出去。

解决:互转前对两端做镜像延拓,处理完再截掉;或者对两端加 Tukey 窗,但注意加窗会压低端点幅值。倾向用镜像延拓,因为它不改变有效段幅值。延拓比例取 5%~10% 即可。

5.4 坑四:采样率不够导致结果面目全非

现象:互转后的速度或加速度波形呈阶梯状,幅值和理论对不上,局部出现振荡。

原因:采样率接近奈奎斯特频率,波形本身畸变,积分和微分运算又会放大这种畸变。位置、速度、加速度三者带宽逐级扩大,对采样率的需求也是逐级变高。

解决:采集时按目标最高频率的 10 到 20 倍设采样率。如果数据已经采完,只能先做带限插值再微分,但插值本身是假设,不能恢复已经丢失的高频信息。这一条很多新人会忽略,等发现时已经没法补救。

5.5 坑五:直流分量让位移结果叠了一条斜线

现象:积分后位移基准明显倾斜,即使去均值了还是有一条斜线。

原因:去均值只能去掉整个采样时长内的平均直流,不能去掉时变缓慢趋势,比如传感器热漂移、调理电路的低频扰动。残留的时变直流被积分后变成速度斜坡,再积一次就变成位移斜线或抛物线。

解决:在速度层做更高阶趋势扣除,配合高通滤波。如果斜线仍然明显,说明低频干扰周期接近数据时长,需要更长的采样记录或更陡的高通滤波器。还有一种做法,是对匀速区间标定基准斜率再扣除,适合恒定速度工况。

5.6 坑六:互转后相位和原信号对不上

现象:速度和位移的波形形状没问题,但和原始力信号或其他通道对比有明显时间差,控制环调试时相位裕度怎么都对不上。

原因:普通 IIR 滤波是有相移的,lfilter 处理后相位随频率改变;微分和积分本身还各带 ±90° 相移,初值又没设置对的话,相位就更乱。

解决:离线处理全部用 filtfilt 做零相位滤波;在线只能用因果滤波时,单独测群延迟并在下游补偿。积分初值和微分初值能测就测,测不到就取中间稳定段截取数据,避开开头初值影响区。

6. 用正弦信号闭环验证:把互转结果锁在误差范围内

6.1 从已知位移出发,转一圈回来看误差

互转做完,第一件事不是直接报告结果,而是先用一组理论已知的数据做闭环验证。方法是构造位移正弦信号 x = A·sin(2πft),解析求导得到速度和加速度,再把加速度按前面的流程积回速度、位移,对比积分结果与原始信号的误差。

import numpy as np fs = 1000 # 采样率 1000 Hz dt = 1.0 / fs t = np.arange(0, 2, dt) A, f0 = 0.002, 5.0 # 振幅 2 mm,频率 5 Hz x = A * np.sin(2 * np.pi * f0 * t) v = 2 * np.pi * f0 * A * np.cos(2 * np.pi * f0 * t) a = -(2 * np.pi * f0) ** 2 * A * np.sin(2 * np.pi * f0 * t) v_est = trapezoid_integral_fast(a, dt) v_est = remove_polynomial_trend(v_est, 1) x_est = trapezoid_integral_fast(v_est, dt) x_est = remove_polynomial_trend(x_est, 1) # 只统计中间 1 秒,避开端点效应 seg = slice(int(fs * 0.5), int(fs * 1.5)) rms_err = np.sqrt(np.mean((x_est[seg] - x[seg]) ** 2)) print("位移 RMS 误差:", rms_err)

运行后位移 RMS 误差应该在零点几毫米到几十微米量级,取决于去趋势和端点处理细节。如果误差数量级接近主信号幅值,说明某一步预处理没生效,多半是速度层的线性趋势没扣干净,或者积分顺序反了。这个验证的价值在于把互转算法的性能从“看起来没问题”变成“误差可量化”。

6.2 现场验证:用已知频率和幅值的标定信号校验

正弦验证通过后,再到现场找一个可复现的输入作参照。常见做法是给传感器一个已知位移幅值的低频正弦激励,比如在振动台上设定一个 5 Hz、2 mm 峰值的扫频,采集加速度和位移两个通道,互转后互相比对。这里不需要两通道量程一样,频域幅值一致即可,顺便还能校出采集卡的通道间延迟和滤波器相位。

每次换采样率或改滤波截止频率,我都会重跑一遍闭环验证。得益于这个习惯,有几次在现场发现是采集卡的等效采样率设置错了,而不是互转算法的问题。建议你把这段验证模板存成脚本,数据一进来先跑它,再决定要不要继续处理。

希望这篇实战拆解能帮你在做位移、速度、加速度信号互转时少走弯路。我自己的体会是,这一行没有万能参数,真正能依赖的是先验证、再处理、看趋势、调截止这几步固定动作,跑通了再往报告里放。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进