简介:面向受激布里渊散射(SBS)仿真需求的MATLAB代码资源,适用于非线性光学、光纤通信与光子学领域的研究生及科研人员,也适合对SBS现象感兴趣的初学者进行概念验证。压缩包内仅含1个.m脚本,用于生成三维受激布里渊散射谱的仿真图形,包体大小仅1KB,轻量精悍,无需复杂配置即可运行,便于读者直接阅读代码逻辑并进行参数调整。SBS源于光波与声子的相互作用,通过该脚本可观察泵浦光频率移动、斯托克斯与反斯托克斯分量分布等谱图特征,帮助理解布里渊频移、增益随参数的变化规律;也可结合自身需求修改泵浦功率、光纤长度等输入,快速对比不同条件下的SBS响应。资源当前已有287人学习下载,若希望建立SBS仿真基础并在此基础上开展扩展研究,这份代码提供了直观的入门参考和二次开发起点。
1. 受激布里渊散射谱:做光纤传感和微波光子的人为什么都绕不开它
做分布式光纤传感的人,几乎每天都要跟受激布里渊散射谱打交道——不管是 BOTDA 还是 BOFDA,最终解调出来的都是一条 SBS 增益谱或损耗谱,谱峰对应的频率就是布里渊频移,温度和应变都藏在它的漂移里。这也就是标题里 "sbs.rar" 这类资源常见的真实用途:一段 SBS 谱数据,一套仿真或实测的处理流程,拿来练手、做算法验证、搭系统原型。这个方向不挑基础,只要懂一点光学和信号处理,就能把谱线拟合、频移提取、噪声抑制这一整条链路跑通。对刚接触的人,最值得先做的事不是去翻光学教科书,而是把一个仿真 SBS 谱在本地生成出来,再亲手加上噪声、拟合、看参数怎么影响谱形——这套功夫练熟,再看实测数据心里就有底。
2. SBS 谱的物理图像与仿真建模:从布里渊频移到洛伦兹线型
2.1 布里渊频移怎么算:声速、折射率和泵浦波长三者的关系
受激布里渊散射本质上是泵浦光、斯托克斯光和声波场三者的参量耦合。泵浦光子湮灭,产生一个声子(声波)和一个频率下移的斯托克斯光子。这个下移量就是布里渊频移 ν_B,它的大小由光纤材料决定,近似公式是:
ν_B = 2 n V_a / λ_p
其中 n 是有效折射率,V_a 是光纤中的声速,λ_p 是泵浦波长。在标准单模光纤里,1550 nm 泵浦对应的 ν_B 大约在 10.8~11 GHz,这个数你记牢,后面仿真和实测都能拿来做 sanity check。
有些教程会把公式写成 ν_B = 2 n V_a / λ_p 并强调“2”来自动量匹配条件——泵浦和斯托克斯光沿同向传播时声波也沿该方向,反向时布里渊频移公式一样但增益谱形状会有区别。实际做 BOTDA 时常用受激损耗或增益配置,泵浦和探测光方向不同,但频移只取决于材料参数,与方向无关。温度每变化 1 ℃,ν_B 大约漂移 1 MHz;应变每变化 1 με,ν_B 大约漂移 0.05 MHz。这两个系数是传感解调的标尺,实测前最好先自己标定,因为掺杂、纤芯几何都会让系数偏离典型值。
2.2 洛伦兹线型与增益谱:为什么 SBS 谱不是高斯也不是 Voigt
SBS 增益谱的形状由声波阻尼决定,声波衰减服从指数衰减,对应频域里就是洛伦兹线型。这也是 SBS 和拉曼散射最大的区别之一——拉曼谱宽且非对称,SBS 谱窄且对称。洛伦兹线型的表达式是:
g(ν) = g_0 · (Δν_B/2)² / [(ν - ν_B)² + (Δν_B/2)²]
这里的 Δν_B 是增益谱的半高全宽(FWHM),室温下标准单模光纤大约 30~50 MHz。如果你用高斯线型去拟合 SBS 谱,中心频移还算准,但线宽会偏大,拟合残差也会呈现明显的系统偏差。用 Voigt 线型虽然更通用,但参数增多,收敛稳定性下降,而且物理上 SBS 的均匀加宽主导,洛伦兹足够。只有少数情况——比如光纤长度极短或者泵浦脉宽极窄导致频谱加宽——才需要考虑高斯卷积。
纯洛伦兹模型有个隐含假设:增益远小于阈值,没有明显的泵浦消耗。一旦泵浦功率超过受激布里渊散射阈值,谱会出现增益饱和,甚至出现多个斯托克斯阶,这时候单条洛伦兹塞不住,得用多峰叠加模型。做仿真时先控制泵浦功率在阈值以下,保证谱形干净,再去逐步加复杂度。
2.3 仿真参数表:一套能直接跑的室温石英光纤参数
写仿真前先列一组参考参数,这些不是编造的“官方参数”,而是常见的实验起始值,我自己做系统仿真时也这么设:
| 参数 | 符号 | 典型值 | 备注 |
|---|---|---|---|
| 泵浦波长 | λ_p | 1550 nm | C 波段常用 |
| 有效折射率 | n | 1.45 | 单模光纤典型值 |
| 声速 | V_a | 5945 m/s | 石英光纤室温附近 |
| 布里渊频移 | ν_B | 约 10.85 GHz | 由上式算出 |
| 增益线宽 FWHM | Δν_B | 35 MHz | 洛伦兹线宽 |
| 峰值增益系数 | g_0 | 5e-11 m/W | 数值仅作仿真设定 |
| 光纤长度 | L | 10 m | 短光纤避免阈值效应 |
注意 2.1 节公式用这组参数算出 ν_B = 2 × 1.45 × 5945 / 1550e-9 ≈ 11.1 GHz,比实测 10.85 GHz 略高。差异来自声速和折射率在不同掺杂下的实际值不同,所以仿真时不要纠结公式算出的精确值,直接用你实验环境里的标称频移就行。这个偏差本身就是第一个值得记下来的坑:材料参数都是“典型值”,不是“标定值”。
3. 用 Python 从零搭建 SBS 谱仿真:泵浦-探测增益谱与噪声基底
3.1 最小可运行代码:洛伦兹增益谱 + 自发辐射噪声
仿真 SBS 谱的第一步是生成一条干净的洛伦兹增益曲线,然后加上探测器噪声和自发辐射噪声。我一般会用 Python 的 numpy 生成频率轴,中心频率设在 10.85 GHz,扫描范围 200 MHz,这样能完整看到谱的基底和两侧。下面这段代码可以直接存成sbs_sim.py运行,只依赖 numpy 和 matplotlib。
import numpy as np # 仿真参数 nu_B = 10.85e9 # 布里渊频移,单位 Hz,标准单模光纤 1550 nm 附近 delta_nu_B = 35e6 # 增益谱 FWHM,单位 Hz g0 = 1.0 # 峰值增益,归一化,单位可任意 freq = np.linspace(10.70e9, 11.00e9, 2001) # 频率扫描范围,200 MHz 跨度 # 洛伦兹线型增益谱 gamma = delta_nu_B / 2 gain = g0 * (gamma**2) / ((freq - nu_B)**2 + gamma**2) # 添加自发辐射噪声:高斯随机噪声,幅度为峰值增益的 0.5% noise_amp = 0.005 * g0 np.random.seed(42) gain_noisy = gain + noise_amp * np.random.randn(len(freq)) # 保存仿真数据,后续拟合可以直接用 np.savetxt('sbs_sim_spectrum.txt', np.column_stack((freq, gain_noisy))) print('仿真谱已生成,峰值位置:', freq[np.argmax(gain_noisy)])这段代码的核心逻辑是先用洛伦兹公式生成一条无噪声谱,再叠加上一个与信号无关的高斯白噪声。需要注意noise_amp设置成峰值的 0.5%,这个比例来自我做系统仿真时的经验:实际光电探测器的噪声基底通常比信号峰值低 20 dB 以上,0.5% 相当于 -46 dB,已经偏保守,用来测试拟合算法的鲁棒性刚好。生成的数据保存为两列文本,第一列是频率(Hz),第二列是归一化增益,后面用任何语言都能读。
3.2 扫频测量怎么模拟:从频域响应到时域采样
真实的光纤传感系统不是一次性拿到整条谱的,它通过扫频逐点测量。比如 BOTDA 系统每步改变微波频率,记录该频率下的增益值;BOTDR 则是用光谱分析仪或相干探测得到整条谱。仿真时模拟扫频的一个关键点:频率步进和扫描范围决定了能分辨的最小频移和最大可测范围。
假如布里渊频移在 10.85 GHz 附近,温度和应变引起的漂移最多几十 MHz,扫频范围至少覆盖 ±100 MHz 才够。步进一般取 0.5~1 MHz,太小浪费时间,太大则拟合误差增大。下面给出模拟扫描采样并叠加随机测量误差的代码:
# 模拟扫频测量:非均匀频点 + 测量抖动 scan_freq = np.arange(10.70e9, 11.00e9, 0.5e6) # 步进 0.5 MHz true_nu_B = 10.851e9 delta = 17.5e6 gain_truth = (delta**2) / ((scan_freq - true_nu_B)**2 + delta**2) # 每次测量有随机幅度抖动,模拟系统重复性 meas_noise = 0.01 * np.random.randn(len(scan_freq)) gain_meas = gain_truth + meas_noise # 随机丢点,模拟扫描过程中信号丢失或坏点 drop_mask = np.random.rand(len(scan_freq)) > 0.95 gain_meas[drop_mask] = np.nan # 保存为带无效点的数据,验证拟合算法对缺失数据的处理 np.savetxt('sbs_scan_measurement.txt', np.column_stack((scan_freq, gain_meas)), header='freq_Hz gain_au', comments='')这里的丢点模拟很重要。实测时激光器跳模、微波源失锁、探测器饱和都会产生异常点,如果不提前在仿真里引入,拟合脚本一碰真实数据就崩。我用np.nan保存异常点,后续拟合时需要用np.isfinite过滤,这也是一线处理数据的常见做法。
3.3 参数扫描:线宽、频移、泵浦功率对谱形的影响
仿真最大的价值是能快速做参数扫描,看清每个参数对最终提取结果的影响。这里给出一个实际示例:扫描线宽从 20 MHz 到 80 MHz,观察拟合中心频移的误差变化。代码如下:
from scipy.optimize import curve_fit def lorentz(x, nu0, gamma, amp, offset): return amp * (gamma**2) / ((x - nu0)**2 + gamma**2) + offset true_nu = 10.85e9 for width in np.arange(20e6, 85e6, 5e6): freq_s = np.linspace(10.70e9, 11.00e9, 1001) y = lorentz(freq_s, true_nu, width, 1.0, 0.001) y += 0.002 * np.random.randn(len(freq_s)) # 拟合时需要给合理的初始值,否则容易收敛到局部极小 p0 = [10.84e9, 30e6, 1.0, 0.0] try: popt, _ = curve_fit(lorentz, freq_s, y, p0=p0, bounds=([10.80e9, 5e6, 0.5, -0.1], [10.90e9, 100e6, 2.0, 0.1])) err = (popt[0] - true_nu) / 1e6 print(f'linewidth={width/1e6:.1f} MHz, fitted_freq_err={err:.3f} MHz') except RuntimeError: print(f'linewidth={width/1e6:.1f} MHz, fit failed')你会发现一个规律:线宽越大,频移拟合的不确定性越大。因为洛伦兹峰变宽后,峰值附近区域比较平坦,噪声对峰值位置的影响更敏感。实际系统里如果 SBS 谱线宽被展宽(比如温度梯度导致的多重频移叠加),提取频移的精度就会下降。这解释了为什么很多高精度系统都在想办法压缩增益线宽,而不是单纯提高信噪比。
4. SBS 谱实测时的常见坑:频移漂移、双峰叠加与偏振衰落
4.1 温度/应变耦合导致的频移漂移:现象与补偿
现象:同一根光纤,白天测的布里渊频移是 10.862 GHz,晚上变成 10.853 GHz。你没改任何设置,谱形也没变,只是整体平移。新手常常以为是系统不稳定,反复校准设备,白忙一场。
原因:光纤所处的环境温度和应变都变了。布里渊频移对温度和应变是同时敏感的,两者耦合在一起,单靠一条 SBS 谱无法区分——这是布里渊传感的固有难题。典型的温度系数约 1 MHz/℃,应变系数约 0.05 MHz/με,所以 10 ℃ 的温度变化和你施加 200 με 的应变效果几乎一样。
解决:工程上有两个方向。一是参考光纤法:在传感光纤旁放一根不受应变的松弛光纤,只感受温度,用它做温度补偿,把测得的频移减去温度贡献,剩下的就是应变。二是双参量测量法:同时测布里渊频移和布里渊谱的功率或线宽,利用两个参量对温度和应变的响应系数不同来解耦,但实现复杂。仿真阶段最简单的方法是把测试环境温度稳定在 ±0.1 ℃,然后在算法里记录系统初始频移作为零点,所有解调结果都用相对频移表示。
4.2 多峰 SBS 谱:为什么光纤里有多个布里渊峰
现象:实测 SBS 谱长得不像一条干净洛伦兹,而是有肩膀、有双峰,甚至三峰。你用单峰拟合,残差大得离谱,中心频移在两个峰之间飘,怎么调初始值都没用。
原因:光纤不是理想的均匀介质。多模光纤里存在多个声学模式,每个模式对应一个不同的声速和一个布里渊峰;即使单模光纤,如果纤芯掺杂浓度沿径向变化,也会出现多个声学分支。最常见的是标准单模光纤里的某个弱峰叠加在主峰旁边,幅度差十几 dB,不仔细看就是谱线不对称。
解决:先分辨峰的数量,再决定拟合模型。简单的方法是看谱的一阶导数过零点数量,或者直接用多峰洛伦兹拟合,峰数从 1 加到 3,用 AIC/BIC 判断哪一档最合适。工程上更实用的做法是只关注主峰,把拟合范围限制在主峰半高以下的 ±60 MHz 内,避开侧峰干扰。如果你做的是高精度温度传感,最好选单模且声学模式干净的 G.652 光纤,少用保偏光纤——保偏光纤的应力区会引入额外的布里渊峰。
4.3 偏振衰落与偏振平均:三次测量取平均的法子
现象:同一系统,同一光纤,每次扫频得到的 SBS 增益幅度不同,有时差 40%,但峰的频率位置几乎不变。起初你以为是激光器功率抖了,检查发现功率稳定得很。
原因:受激布里渊散射的增益与泵浦光和斯托克斯光的偏振相对方向有关。普通单模光纤不保偏,光在传输中偏振态随机演化,导致有效增益随位置和时间波动,这就是偏振衰落。严重时增益接近零,谱直接从噪声里消失了。
解决:工程上最常用的手段是偏振分集接收,或者让泵浦光的偏振态周期性切换。仿真里最简单有效的办法是模拟三次不同偏振态下的增益谱,做逐点平均:
# 模拟偏振平均:三次测量的增益谱取平均 freq_avg = np.linspace(10.70e9, 11.00e9, 1001) gain_sum = np.zeros_like(freq_avg) for state in range(3): # 每次的峰值增益随偏振态波动 0.6~1.0 倍 gain_factor = 0.6 + 0.4 * np.random.rand() gy = lorentz(freq_avg, 10.852e9, 35e6, gain_factor, 0.002) gy += 0.001 * np.random.randn(len(freq_avg)) gain_sum += gy gain_polar_avg = gain_sum / 3这个平均不是简单的降噪,关键是把随机偏振衰落带来的幅度起伏压平,避免后续拟合把幅度小、噪声大的频点当成有效数据。注意平均次数不必太多,三次已经能把增益标准差的波动从 20% 降到 10% 以下;再多平均会延长测量时间,对传感实时性有害。
5. 从仿真到实测:SBS 谱数据处理的最小流程
5.1 数据预处理:去基线、去直流、归一化
实测 SBS 谱往往不是从零开始的,探测器有暗电流,光路上有残余瑞利散射,谱线下面垫着一个缓慢变化的基底。直接拟合会得到偏置的频移和过大的线宽。我一般先做三步:去直流、去斜基、归一化幅值。
去直流最简单:取扫描范围两端各 10% 频率点的平均值,作为基底值减去。去斜基是因为部分系统里增益谱基底随频率线性变化,用线性函数拟合整个频谱的基底部分再扣掉。归一化则把谱峰最大值设为 1,方便不同功率条件下比较。这个过程不适合用代码硬算,用一个小函数封装:
def preprocess_sbs_spectrum(freq, raw_gain): # 取左右两端各 10% 点估计基底 n = len(freq) edge = int(n * 0.1) baseline = np.concatenate([raw_gain[:edge], raw_gain[-edge:]]) base_val = np.mean(baseline) # 去直流 gain_centered = raw_gain - base_val # 线性抛基:对边缘点作线性拟合,再扣除 edge_idx = np.concatenate([np.arange(edge), np.arange(n-edge, n)]) slope, intercept = np.polyfit(freq[edge_idx], gain_centered[edge_idx], 1) gain_flat = gain_centered - (slope * freq + intercept) # 归一化 gain_norm = gain_flat / np.max(gain_flat) return gain_norm这个函数的关键在于只利用边缘点估计基底,而不是用整条谱拟合——因为整条谱里有洛伦兹峰的贡献,拟合基底会把峰翼也带进去,导致基底估计偏高。边缘点选 10% 是经验值,如果扫描范围只有 100 MHz 而线宽 50 MHz,边缘点就不够纯,需要把范围拉宽。
5.2 洛伦兹拟合:用 scipy curve_fit 提取中心频移
数据预处理完成后,进入最核心的频移提取。常见做法是用curve_fit拟合洛伦兹函数,但直接扔数据进去容易翻车。先给初值一个相对靠谱的估计:用np.argmax找峰值位置作为频移初值,用谱的半峰宽度的一半作为线宽初值。同时设置边界条件,避免拟合器跑到荒谬的地方去。
from scipy.optimize import curve_fit # 读取仿真生成的带噪声谱 data = np.loadtxt('sbs_sim_spectrum.txt') freq_data = data[:, 0] gain_data = data[:, 1] # 初值估计:用峰值位置和半高宽度 peak_idx = np.argmax(gain_data) nu0_guess = freq_data[peak_idx] half_max = np.max(gain_data) / 2 # 找左半高和右半高点,差值为 FWHM 的近似 left_idx = np.where(gain_data[:peak_idx] < half_max)[0][-1] right_idx = peak_idx + np.where(gain_data[peak_idx:] < half_max)[0][0] fwhm_guess = freq_data[right_idx] - freq_data[left_idx] gamma_guess = fwhm_guess / 2 def lorentz_fit(x, nu0, gamma, amp, offset): return amp * (gamma**2) / ((x - nu0)**2 + gamma**2) + offset p0 = [nu0_guess, gamma_guess, np.max(gain_data) - np.min(gain_data), np.min(gain_data)] bounds = ([nu0_guess - 20e6, gamma_guess * 0.5, 0, -0.1], [nu0_guess + 20e6, gamma_guess * 2.0, 2.0, 0.1]) popt, pcov = curve_fit(lorentz_fit, freq_data, gain_data, p0=p0, bounds=bounds) fit_nu0 = popt[0] fit_dnu = 2 * popt[1] print(f"拟合布里渊频移:{fit_nu0/1e9:.6f} GHz,线宽:{fit_dnu/1e6:.2f} MHz")这里一定要加边界,尤其频移初值上下浮动 20 MHz 足够覆盖温度和应变引起的漂移。如果不加边界,curve_fit可能把频移收敛到扫描范围边缘,特别是当噪声很大、谱形不对称时。pcov是对角线为参数方差,建议每次记录pcov[0][0]的平方根作为频移的不确定度,这是衡量系统精度的直接指标。
5.3 验证仿真与实测一致性:残差与 Q 因子
拟合完不能只看图形重合,要量化残差。最常见的方法是计算标准化残差:
residual = (实测值 - 拟合值) / 噪声标准差
如果残差在 ±3 之间随机分布,说明模型合适;如果残差呈现系统性波浪形,说明模型有问题,比如实际谱不是单洛伦兹。另一个实用指标是拟合优度 Q 因子,Q = 频移估计的标准差和线宽的比值,Q 越大越好。工程经验是 Q 大于 200 时,温度解调精度可以达到 0.1 ℃ 量级。我经常在仿真里故意加入模型失配(比如把线宽改大 10% 再用原模型拟合),来看 Q 因子如何下降,这样能提前感知实测时模型失配的信号。
实验上还有一个好习惯:对同一组数据,随机抽取 80% 的点重复拟合 50 次,观察频移估计的标准差。这叫 bootstrap 验证,比单纯看pcov更贴近真实测量波动,因为数据里可能还有未建模的相关噪声。bootstrap 标准差的求法也不复杂,在数据索引上有放回抽样就行,实测数据点数够多时很有效。
6. 最后聊一个实用技巧:用互相关代替拟合快速估计布里渊频移
洛伦兹拟合虽然准,但要给初值、要调边界、要检查收敛,数据量大时速度也慢。如果你只需要快速跟踪频移变化——比如动态应变监测,每秒几百次扫描——可以考虑用互相关做粗估计。方法是把实测谱和一个已知的模板谱做互相关,峰值对应的位移就是频移偏移量。模板谱可以先做一次高精度拟合,把拟合出的光滑洛伦兹保存下来作为模板。
def coarse_shift_by_correlation(measured, template_center_freq, delta_freq): # 假设模板已按相同频率轴生成 # 对模板做插值,生成一系列频移后的谱,与实测求相关 best_shift = 0 best_corr = -np.inf for shift_mhz in np.arange(-30, 30, 0.5): shifted_freq = template_center_freq + shift_mhz * 1e6 # 这里简化为相同频率轴下的循环移位 template_shifted = np.interp(freq_axis, shifted_freq, template) corr = np.corrcoef(measured, template_shifted)[0, 1] if corr > best_corr: best_corr = corr best_shift = shift_mhz return best_shift实际循环移位不如插值精确,所以我用np.interp把模板移动到目标位置。粗估计精度能到 1 MHz 量级,然后用这个小范围再做洛伦兹拟合,既快又稳。这个两步法的思路我在很多实时系统里用过,效果都很好。另外有一个血泪经验:别把模板固定得太死,温度变化会让模板的线宽也跟着变,最好每 10 分钟重新拟合一次模板。互相关的优势是抗噪声,因为相关运算天然累积所有点的贡献,比单纯找峰值稳定得多。希望这个技巧能帮你在处理 SBS 谱时省下一些调参时间,把精力留给真正影响精度的硬件问题上。
本文还有配套的精品资源,点击获取