Julia 教程 / Julia 信号处理与傅里叶变换
Julia 信号处理与傅里叶变换
DSP.jl 是 Julia 的信号处理标准库,提供 FFT、滤波器设计、频谱分析等功能。本文从基础的傅里叶变换到高级的时频分析,全面覆盖数字信号处理的核心内容。
1. DSP.jl 模块
using DSP
# DSP.jl 提供的主要功能:
# - FFT / IFFT
# - 窗函数(Window Functions)
# - FIR / IIR 滤波器设计
# - 卷积
# - 频谱分析
# - 重采样
# 同时使用 FFTW(DSP.jl 的 FFT 后端)
using FFTW
| 子模块 | 功能 |
|---|---|
DSP.Windows | 窗函数 |
DSP.Filters | 滤波器 |
DSP.Periodograms | 周期图/频谱估计 |
DSP.Util | 工具函数 |
FFTW | 快速傅里叶变换 |
2. FFT / IFFT
using FFTW
# 生成信号:两个正弦波叠加
fs = 1000 # 采样率 1000 Hz
t = 0:1/fs:1-1/fs # 1 秒时间序列
f1, f2 = 50, 120 # 50 Hz 和 120 Hz
signal = sin.(2π * f1 .* t) + 0.5 * sin.(2π * f2 .* t)
signal .+= 0.3 * randn(length(t)) # 添加噪声
# FFT
Y = fft(signal)
# 频率轴
freqs = fftfreq(length(t), fs) # 自动处理频率排列
# 功率谱
power = abs2.(Y) / length(t)^2
# 只取正频率部分
n = length(t) ÷ 2
freqs_pos = freqs[1:n]
power_pos = 2 * power[1:n] # ×2 补偿负频率
# IFFT(逆变换)
signal_reconstructed = real(ifft(Y))
using Plots
# 绘制时域信号
p1 = plot(t[1:200], signal[1:200],
xlabel="时间 (s)", ylabel="振幅",
title="时域信号", label="原始信号")
# 绘制频谱
p2 = plot(freqs_pos, power_pos,
xlabel="频率 (Hz)", ylabel="功率",
title="频谱", label="功率谱",
xlims=(0, 200))
plot(p1, p2, layout=(2,1), size=(800, 500))
⚠️ 注意: FFT 输出的频率排列(由 fftfreq 返回)是 [0, 正频率…, 负频率…],不是按频率从小到大排列。使用 fftshift 可以将零频率移到中心。
3. 窗函数
using DSP.Windows
N = 64 # 窗长度
# 常用窗函数
rect_win = rect(N) # 矩形窗
hann_win = hanning(N) # Hanning 窗
hamming_win = hamming(N) # Hamming 窗
blackman_win = blackman(N) # Blackman 窗
kaiser_win = kaiser(N, 5.0) # Kaiser 窗(β=5)
gauss_win = gaussian(N, 0.4) # 高斯窗
tukey_win = tukey(N, 0.5) # Tukey 窗
| 窗函数 | 主瓣宽度 | 旁瓣衰减 | 适用场景 |
|---|---|---|---|
| 矩形 | 最窄 | -13 dB | 频率分辨率优先 |
| Hanning | 中等 | -31 dB | 通用 |
| Hamming | 中等 | -43 dB | 通用 |
| Blackman | 较宽 | -58 dB | 旁瓣抑制优先 |
| Kaiser | 可调 | 可调 | 灵活设计 |
| 高斯 | 可调 | 可调 | 时频分析 |
using Plots
# 窗函数对比
N = 64
plot(rect(N), label="矩形", linewidth=2)
plot!(hanning(N), label="Hanning", linewidth=2)
plot!(hamming(N), label="Hamming", linewidth=2)
plot!(blackman(N), label="Blackman", linewidth=2)
plot!(kaiser(N, 5.0), label="Kaiser(β=5)", linewidth=2)
xlabel!("样本点"); ylabel!("幅度"); title!("窗函数对比")
💡 提示: 选择窗函数是一门权衡艺术:主瓣越窄,频率分辨率越高;旁瓣越低,频谱泄漏越少。Kaiser 窗通过调节 β 参数可以在这两者之间灵活权衡。
4. 频谱分析
using DSP, DSP.Periodograms
fs = 1000
t = 0:1/fs:5-1/fs
signal = sin.(2π * 50 .* t) + 0.5 * sin.(2π * 120 .* t) + randn(length(t)) * 0.3
# 周期图(Periodogram)
pgram = periodogram(signal; fs=fs)
freqs_p = freq(pgram)
power_p = power(pgram)
# Welch 方法(分段平均,减少方差)
pgram_welch = welch_pgram(signal; fs=fs, nfft=256)
freqs_w = freq(pgram_welch)
power_w = power(pgram_welch)
# 短时傅里叶变换(STFT)
S = spectrogram(signal, 256, 128; fs=fs) # 窗长 256, 重叠 128
t_stft = time(S)
f_stft = freq(S)
S_power = power(S)
using Plots
# Welch 频谱
plot(freqs_w, 10 .* log10.(power_w),
xlabel="频率 (Hz)", ylabel="功率 (dB)",
title="Welch 功率谱密度", label="PSD",
xlims=(0, 200))
# 时频图(Spectrogram)
heatmap(t_stft, f_stft, 10 .* log10.(S_power'),
xlabel="时间 (s)", ylabel="频率 (Hz)",
title="频谱图", color=:viridis)
5. FIR 滤波器设计
using DSP
# 低通 FIR 滤波器
fs = 1000
f_cutoff = 100 # 截止频率 100 Hz
N = 64 # 滤波器阶数
# 窗函数法设计
lpf = digitalfilter(Lowpass(f_cutoff; fs=fs), FIRWindow(hamming(N)))
# lpf 是滤波器系数向量
# 高通滤波器
hpf = digitalfilter(Highpass(200; fs=fs), FIRWindow(hamming(N)))
# 带通滤波器
bpf = digitalfilter(Bandpass(50, 200; fs=fs), FIRWindow(hamming(N)))
# 带阻滤波器
bsf = digitalfilter(Bandstop(80, 120; fs=fs), FIRWindow(hamming(N)))
# 滤波
filtered_signal = filt(lpf, signal)
# 频率响应
H = freqresp(PolynomialRatio(lpf, [1.0]))
6. IIR 滤波器设计
using DSP
fs = 1000
# Butterworth 低通
lpf_butter = digitalfilter(Lowpass(100; fs=fs), Butterworth(4)) # 4 阶
# Chebyshev Type I(通带纹波)
lpf_cheby1 = digitalfilter(Lowpass(100; fs=fs), Chebyshev1(4, 1)) # 4 阶, 1dB 纹波
# Chebyshev Type II(阻带纹波)
lpf_cheby2 = digitalfilter(Lowpass(100; fs=fs), Chebyshev2(4, 40)) # 4 阶, 40dB 衰减
# 椭圆滤波器
lpf_ellip = digitalfilter(Lowpass(100; fs=fs), Elliptic(4, 1, 40)) # 4 阶, 1dB/40dB
# 应用滤波
y = filt(lpf_butter, signal)
| 滤波器类型 | 优势 | 劣势 |
|---|---|---|
| Butterworth | 平坦通带 | 滚降较慢 |
| Chebyshev I | 快速滚降 | 通带有纹波 |
| Chebyshev II | 快速滚降 | 阻带有纹波 |
| Elliptic | 最快滚降 | 通带和阻带都有纹波 |
⚠️ 注意: IIR 滤波器可能存在稳定性问题。设计高阶 IIR 滤波器时,建议使用二阶节(SOS)级联实现。使用 SecondOrderSections 替代直接形式。
# 二阶节级联(更稳定)
sos = SecondOrderSections(lpf_butter)
y_stable = filt(sos, signal)
7. 卷积
using DSP
# 线性卷积
x = [1, 2, 3, 4]
h = [1, 0, -1]
y = conv(x, h) # [1, 2, 2, 2, -4, -4]
# 2D 卷积(图像处理)
img = rand(100, 100)
kernel = ones(3, 3) / 9 # 3×3 均值滤波
filtered = conv(img, kernel)
# 互相关(用于延迟检测)
x = randn(100)
y = circshift(x, 5) + randn(100) * 0.1 # 延迟 5 个样本
r = xcorr(y, x)
delay = argmax(r) - length(x)
8. 信号采样与插值
using DSP
# 重采样(改变采样率)
fs_old = 44100 # CD 品质
fs_new = 16000 # 语音处理常用
# 上采样 + 抗混叠滤波 + 下采样
ratio = gcd(fs_old, fs_new)
up = fs_new ÷ ratio
down = fs_old ÷ ratio
signal_44k = sin.(2π * 440 .* (0:1/fs_old:1-1/fs_old))
signal_16k = resample(signal_44k, up // down)
# 插值
using Interpolations
t_orig = 0:0.1:5
y_orig = sin.(t_orig)
# 线性插值
itp_linear = LinearInterpolation(t_orig, y_orig)
y_fine = itp_linear.(0:0.01:5)
# 三次样条插值
itp_cubic = CubicSplineInterpolation(t_orig, y_orig)
y_fine_cubic = itp_cubic.(0:0.01:5)
9. 短时傅里叶变换(STFT)
using DSP
# STFT 参数
fs = 1000
t = 0:1/fs:2-1/fs
# 生成变频信号(chirp)
f_inst = 50 .+ 200 .* t # 瞬时频率从 50 Hz 到 250 Hz
signal = sin.(2π .* cumsum(f_inst) / fs)
# STFT
window = hamming(256)
S = spectrogram(signal, 256, 192; fs=fs, window=window)
# 绘制时频图
using Plots
heatmap(time(S), freq(S), 10 .* log10.(power(S)'),
xlabel="时间 (s)", ylabel="频率 (Hz)",
title="Chirp 信号 STFT",
color=:inferno)
ISTFT(逆短时傅里叶变换)
# 从 STFT 重建信号
function istft(S, hop; nfft=size(S, 1))
n_frames = size(S, 2)
signal_len = (n_frames - 1) * hop + nfft
signal = zeros(signal_len)
window_sum = zeros(signal_len)
w = hamming(nfft)
for i in 1:n_frames
idx = (i - 1) * hop + 1
signal[idx:idx+nfft-1] .+= real(ifft(S[:, i]))[1:nfft] .* w
window_sum[idx:idx+nfft-1] .+= w .^ 2
end
return signal ./ max.(window_sum, 1e-10)
end
10. 实际应用
10.1 音频处理
using WAV, DSP, FFTW
# 读取音频文件
audio, fs = wavread("audio.wav")
# 如果是立体声,取单声道
if ndims(audio) > 1
audio = audio[:, 1]
end
# 降噪:频域门限法
Y = fft(audio)
power = abs2.(Y)
# 设定门限
threshold = median(power) * 3
Y_denoised = copy(Y)
Y_denoised[power .< threshold] .= 0 # 抑制低功率成分
audio_clean = real(ifft(Y_denoised))
# 低通滤波去除高频噪声
lpf = digitalfilter(Lowpass(4000; fs=fs), Butterworth(6))
audio_filtered = filt(lpf, audio_clean)
# 保存
wavwrite(audio_filtered, "audio_clean.wav"; Fs=fs)
10.2 通信信号分析
using DSP, FFTW
# AM 调幅信号解调
fs = 10000
t = 0:1/fs:0.1-1/fs
# 消息信号
msg = sin.(2π * 100 .* t)
# 载波信号
carrier_freq = 1000
carrier = cos.(2π * carrier_freq .* t)
# AM 调制
m = 0.5 # 调制指数
am_signal = (1 .+ m .* msg) .* carrier
# 包络检波(简单解调)
# 全波整流 + 低通滤波
rectified = abs.(am_signal)
lpf = digitalfilter(Lowpass(200; fs=fs), Butterworth(4))
demodulated = filt(lpf, rectified)
# 绘制
using Plots
p1 = plot(t[1:500], msg[1:500], title="消息信号", label="msg")
p2 = plot(t[1:500], am_signal[1:500], title="AM 信号", label="AM")
p3 = plot(t[1:500], demodulated[1:500], title="解调信号", label="解调")
plot(p1, p2, p3, layout=(3,1), size=(800, 600))
10.3 振动信号分析(机械故障诊断)
using DSP, FFTW
# 模拟轴承振动信号
fs = 10000
t = 0:1/fs:2-1/fs
# 正常信号 + 故障特征频率
normal = 0.1 * randn(length(t))
fault_freq = 150 # 故障特征频率 150 Hz
fault = 0.5 * sin.(2π * fault_freq .* t) .* (mod.(t, 0.01) .< 0.001)
signal = normal + fault
# 频谱分析
pgram = welch_pgram(signal; fs=fs, nfft=1024)
# 包络分析(用于轴承故障检测)
analytic = hilbert(signal) # 解析信号
envelope = abs.(analytic) # 包络
env_spectrum = welch_pgram(envelope; fs=fs, nfft=1024)
using Plots
p1 = plot(freq(pgram), 10 .* log10.(power(pgram)),
title="频谱", label="PSD", xlims=(0, 500))
p2 = plot(freq(env_spectrum), 10 .* log10.(power(env_spectrum)),
title="包络谱", label="包络 PSD", xlims=(0, 500))
plot(p1, p2, layout=(2,1), size=(800, 500))
11. 小波变换简介
using Wavelets
# 离散小波变换(DWT)
signal = randn(128)
# Daubechies 小波
wt = wavelet(WT.db4) # 4 阶 Daubechies
coeffs = dwt(signal, wt)
# 逆变换
signal_recon = idwt(coeffs, wt)
# 多层分解
level = 3
coeffs_multi = dwt(signal, wt, level)
# 小波去噪
# 软阈值法
function wavelet_denoise(signal, wt; level=4, threshold_factor=1.5)
coeffs = dwt(signal, wt, level)
# 估计噪声标准差
σ = median(abs.(coeffs)) / 0.6745
threshold = threshold_factor * σ
# 软阈值
coeffs_thresh = sign.(coeffs) .* max.(abs.(coeffs) .- threshold, 0)
return idwt(coeffs_thresh, wt, level)
end
扩展阅读
- DSP.jl 官方文档
- FFTW.jl 文档
- Wavelets.jl — 小波变换
- Oppenheim & Schafer, Discrete-Time Signal Processing
- Proakis & Manolakis, Digital Signal Processing
- IEEE Signal Processing Magazine