ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

光纤激光器数值模拟:用MATLAB搞定长度与掺杂浓度的参数扫描

光纤激光器数值模拟:用MATLAB搞定长度与掺杂浓度的参数扫描 简介面向光通信与光电子领域的研究人员与工程师一套 MATLAB 数值模拟资源聚焦光纤激光器在不同长度与掺杂浓度下的性能研究基于麦克斯韦方程组与量子统计力学建立模型重点分析光场分布、增益谱、阈值电流及光输出功率等关键参数适用于科研验证与工业应用。压缩包为 zip 格式共 11 个文件包含 9 个 .m 代码文件覆盖主流程搭建、核心计算与结果后处理另有 1 篇 docx 论文文档阐述建模原理与结果分析以及 1 张示意图辅助理解。包体仅 140KB轻量便捷目前已有 160 人学习下载。通过运行源码并对照文档读者可复现不同设计条件下的激光器性能曲线掌握从方程离散、数值求解到参数优化的完整链条为提升激光器效率和稳定性提供可落地的 MATLAB 实现参考。1. 光纤激光器数值模拟长度和掺杂浓度为什么是绕不开的两个旋钮做光纤激光器实验的人大概率都有这种经历增益光纤从 80cm 换到 2m输出功率不是单调上升而是先涨后跌换一批掺杂浓度不同的光纤阈值又变了。这时候如果只靠熔接机一次次试一次实验就是半天。用 MATLAB 对光纤激光器做数值模拟本质就是把你手上的泵浦功率、光纤长度、掺杂浓度、腔镜反射率先放进方程里跑一遍在动手之前就知道哪个长度区间值得切、哪个浓度范围值得下单。这个方向适合做光纤激光器设计、光纤放大器预研和课题开题的人仿真跑通之后实验就变成验证而不是盲猜。2. 先立模型再写代码从速率方程到两点边值问题光纤激光器数值模拟的第一步不是打开 MATLAB 写循环而是先把物理模型写到能算的程度。光纤激光器种类很多连续波掺铒光纤激光器是最经典、也最适合入门的一种。铒离子在 980nm 泵浦下可以简化为二能级系统泵浦把离子从基态抽运到上能级信号光通过受激发射获得增益。这个简化足够描述大多数连续波场景先把它算明白之后再做脉冲、调 Q 才有基础。2.1 二能级系统的速率方程把上能级粒子数写成闭合形式写速率方程之前要先定几个物理量。N0 是掺杂浓度单位是 m^-3你买光纤时供应商给的 ppm 数最后都要换算成这个N1 和 N2 分别是基态和上能级粒子数密度N1 N2 N0。泵浦光和信号光在纤芯里不是均匀分布的所以要用重叠因子 Gamma_p 和 Gamma_s 来折算有效面积。980nm 泵浦下上能级粒子数的稳态方程可以写成dN2/dt sigma_ap * Phi_p * N1 sigma_as * Phi_s * N1 - sigma_es * Phi_s * N2 - N2 / tau 0这里 Phi_p 和 Phi_s 是光子通量单位是 m^-2 s^-1等于光功率除以光子能量再除以纤芯有效面积sigma_ap 是泵浦吸收截面sigma_es 是信号受激发射截面sigma_as 是信号重吸收截面tau 是上能级寿命。把 N1 N0 - N2 代进去解出稳态 N2N2 N0 * (sigma_ap * Phi_p sigma_as * Phi_s) / (sigma_ap * Phi_p (sigma_as sigma_es) * Phi_s 1/tau)这个闭合形式是后面所有代码的核心。仔细看它的分子和分母会发现两个工程规律泵浦越强N2 越高信号功率越大受激发射越强上能级被耗尽这就是增益饱和。很多仿真翻车的根本原因就是只用了小信号增益公式 g0 sigma_es * N2 * Gamma_s然后直接在 ODE 里固定增益系数完全不考虑信号功率对粒子数的反作用。只要信号一强仿真出来的输出功率就离谱。2.2 行波方程与谐振腔边界为什么不能直接 ode45光在光纤里有三个功率分量要跟踪泵浦功率 Pp 沿 z 方向传播信号正向功率 Ps_plus 沿 z 方向传播信号反向功率 Ps_minus 沿 -z 方向传播。三个量的传输方程如下dPp/dz -Gamma_p * sigma_ap * N1 * Pp dPs_plus/dz Gamma_s * (sigma_es * N2 - sigma_as * N1) * Ps_plus 自发辐射项 dPs_minus/dz -Gamma_s * (sigma_es * N2 - sigma_as * N1) * Ps_minus - 自发辐射项看起来是一组一阶常微分方程但这里有个新手最容易踩的坑边界条件不在同一边。泵浦功率在 z0 处注入这是初始条件而信号光要满足谐振腔边界Ps_plus(0) R1 * Ps_minus(0) Ps_minus(L) R2 * Ps_plus(L)R1 和 R2 是两端腔镜的反射率z0 是泵浦注入端zL 是输出端。这是一个典型的两点边值问题。很多人第一次写代码直接给 Ps_plus 一个初值然后 ode45 往 L 跑算完才发现另一端的 Ps_minus 根本对不上边界条件这就是翻车现场。常见的解决思路有两种一是用 MATLAB 内置的 bvp4c二是用打靶法。bvp4c 在强非线性增益下对初始猜测很敏感经常不收敛我一般用打靶法把它变成一个求根问题用 fzero 去迭代可控性好得多哪里不对也容易调试。2.3 打靶法求根用 fzero 包住 ode45 的完整实现打靶法的思路很直接先猜一个 z0 处的正向信号功率 Ps0由边界条件推出 z0 处的反向功率然后正向积分到 zL检查 Ps_minus(L) 和 R2 * Ps_plus(L) 的残差再用 fzero 迭代。下面是两个核心函数。第一个定义微分方程function dydz laser_eqs(z, y, p) % y(1)Pp, y(2)Ps_plus, y(3)Ps_minus, 单位均为W Pp y(1); Ps_p y(2); Ps_m y(3); h 6.626e-34; c 2.998e8; nu_p c / p.lambda_p; nu_s c / p.lambda_s; A_core p.A_core; % 光子通量功率 / 光子能量 / 有效纤芯面积再乘重叠因子 Phi_p p.Gamma_p * Pp / (h * nu_p * A_core); Phi_s p.Gamma_s * (Ps_p Ps_m) / (h * nu_s * A_core); % 二能级稳态上能级粒子数闭合形式见2.1节 N0 p.N0; N2 N0 * (p.sigma_ap * Phi_p p.sigma_as * Phi_s) ... / (p.sigma_ap * Phi_p (p.sigma_as p.sigma_es) * Phi_s 1/p.tau); N1 N0 - N2; % 泵浦沿z方向被吸收 dPp_dz -p.Gamma_p * p.sigma_ap * N1 * Pp; % 信号净增益系数负值表示重吸收占主导 g p.Gamma_s * (p.sigma_es * N2 - p.sigma_as * N1); % 正向信号沿z增加反向信号沿z减小 dPs_p_dz g * Ps_p p.ase; dPs_m_dz -g * Ps_m - p.ase; dydz [dPp_dz; dPs_p_dz; dPs_m_dz]; end这里 p 是一个结构体所有物理参数都通过它传进去避免全局变量污染。ase 是自发辐射耦合进光纤的等效功率密度量级很小但必须保留它有两个作用一是让激光器在没有信号注入时也能启动二是打破打靶法的平凡解。如果你把 ase 设成 0在阈值附近 fzero 会一直找到零解因为零功率永远满足边界条件这是一个很隐蔽的坑。下面这个函数计算边界残差function F shoot_residual(Ps0, p, L) % 根据z0处边界Ps_plus(0)Ps0, Ps_minus(0)R1*Ps0 y0 [p.Pp0; Ps0; p.R1 * Ps0]; opts odeset(RelTol, 1e-8, AbsTol, 1e-10); [~, Y] ode45((z,y) laser_eqs(z, y, p), [0 L], y0, opts); Ps_p_end Y(end, 2); Ps_m_end Y(end, 3); % 右端边界残差Ps_minus(L) 应等于 R2 * Ps_plus(L) F Ps_m_end - p.R2 * Ps_p_end; endode45 默认容差是 1e-3对激光器这种增益敏感问题不够我习惯把 RelTol 压到 1e-8、AbsTol 压到 1e-10。AbsTol 不能设太大不然功率接近 0 的区间截断误差会被放大。打靶法能不能收敛取决于方程里有没有 ase 项以及 fzero 的初始猜测给得合不合理。模型参数是数值模拟的地基下面是连续波掺铒光纤激光器的一组典型参数可以直接作为初始值跑通之后再根据自己的光纤调整参数含义典型值lambda_p泵浦波长980e-9 mlambda_s信号波长1550e-9 msigma_ap泵浦吸收截面2.3e-25 m^2sigma_as信号重吸收截面1.0e-25 m^2sigma_es信号发射截面6.0e-25 m^2tau上能级寿命10e-3 sGamma_p / Gamma_s泵浦/信号重叠因子0.8 / 0.7A_core纤芯有效面积7.0e-12 m^2R1 / R2腔镜反射率0.98 / 0.90注意掺杂浓度 N0 和泵浦功率 Pp0 不在这里面因为它们是后面要扫描的变量。先把单个长度、单个浓度下的求解跑通再谈扫描。3. 扫描不同长度与掺杂浓度搭一个可复现的参数扫描脚本有了单次求解的打靶法下一步就是把它封装成一个黑匣子输入光纤长度 L 和掺杂浓度 N0返回输出功率。这一步做了之后扫描长度和掺杂浓度就只是双循环的事不再需要每次改方程或改边界条件。3.1 封装单次求解输入 L 和 N0返回输出功率激光器的输出功率是右端镜透射部分等于 (1 - R2) * Ps_plus(L)。我习惯把参数设置、fzero 调用、后处理都包到一个函数里这样扫参数时只改入口参数。下面是一个可用的封装function P_out simulate_fiber_laser(L, N0, p_base) % 复制基础参数避免污染外部结构体 p p_base; p.N0 N0; p.L L; % 初始猜测参考实际输出功率量级单位W Ps0_guess 1e-3; % fzero找打靶残差零点使用options控制显示 opts optimset(Display, off, TolFun, 1e-14); [Ps0, ~, exitflag] fzero((x) shoot_residual(x, p, L), Ps0_guess, opts); if exitflag 0 % fzero没找到根说明当前参数低于阈值输出近似0 P_out 0; return; end % 用找到的Ps0重新积分取右端正向功率 y0 [p.Pp0; Ps0; p.R1 * Ps0]; [~, Y] ode45((z,y) laser_eqs(z, y, p), [0 L], y0, odeset(RelTol,1e-8,AbsTol,1e-10)); P_out (1 - p.R2) * Y(end, 2); end这个函数的逻辑很直接先猜 Ps0fzero 迭代收敛后重新积分一次拿到 P_out。有一点需要解释为什么 fzero 的初猜是 1e-3 而不是 1e-9因为 fzero 找的是变号零点初值给得太小函数在微小功率区间里可能没有变号它就跑到别的根上去了给一个中等量级初值更接近激光器实际输出功率收敛概率高。如果 exitflag 返回负值大概率是当前参数组合在阈值以下直接给 0 并 return不要继续积分否则会得到一个噪声级的小数干扰后面的二维图。3.2 双循环扫描与结果矩阵别把参数写死在循环里扫描时最重要的一件事是把参数列表和结果矩阵先定义好矩阵的行列对应关系写清楚。我一般让行对应掺杂浓度列对应光纤长度这样画热力图时不用转置。代码如下% 长度扫描范围0.2m 到 5m取49个点 L_list linspace(0.2, 5, 49); % 掺杂浓度扫描范围1e24 到 5e25 m^-3取37个点 N0_list linspace(1e24, 5e25, 37); P_out_mat zeros(length(N0_list), length(L_list)); P0_guess 1e-3; % 用上一次的收敛解做初值减少fzero失败率 for i 1:length(N0_list) for j 1:length(L_list) P_out_mat(i, j) simulate_fiber_laser(L_list(j), N0_list(i), p_base); if P_out_mat(i, j) 1e-9 P0_guess 2 * P_out_mat(i, j) / (1 - p_base.R2); end end end这个双循环里有一个延续技巧值得说明P0_guess 会随着扫描推进更新因为相邻参数组合的解通常很接近用上一个收敛解作为下一个初猜比每次都从固定的 1e-3 开始更稳。特别是当长度接近阈值边界时固定的初猜很容易让 fzero 掉到平凡解或者 ASE 噪声区。浓度和长度都变化时最好先固定浓度扫长度再换浓度这样相邻长度点之间功率变化平缓初值延续最有效。结果矩阵形成之后先别急着画图。建议先在命令行里随机抽几个点和实验或者文献对一下数量级。数量级对不上后面所有分析都是白算。3.3 网格与收敛性验证步长选多大才不是玄学ode45 本身是自适应步长很多人以为这就万事大吉实际上自适应步长只控制局部误差不保证物理量不出现伪振荡。我的检查习惯是固定一组参数把 RelTol 从 1e-6 改到 1e-10看输出功率变化量L_test 2.0; N_test 1e25; P_prev 0; for tol [1e-6, 1e-8, 1e-10] opts odeset(RelTol, tol, AbsTol, tol * 1e-2); [~, Y] ode45((z,y) laser_eqs(z, y, p_base), [0 L_test], ... [p_base.Pp0; 1e-3; p_base.R1*1e-3], opts); P_now (1 - p_base.R2) * Y(end, 2); fprintf(RelTol%.1e, P_out%.6f W\n, tol, P_now); P_prev P_now; end如果三次结果在小数点后三位没有变化基本可以认为数值收敛了。如果 P_out 随容差漂移超过 1%那问题多半不是容差而是方程里的 ase 项设置过大或者信号功率被 AbsTol 截断得太狠。ASE 项的量级应该是输出功率的 10^-6 以下它只负责启动不应该影响最终稳态结果。还有一个隐蔽点扫描矩阵里如果某些点不收敛不能直接删掉要在图上标出来否则二维图会在那个位置出现一个假凹陷让你误判为最佳长度。4. 把仿真结果画成能决策的图长度-浓度热图与阈值提取仿真矩阵跑出来之后真正值钱的是怎么把这些数字变成设计决策。二维热图能一眼看出最佳参数区阈值长度和斜率效率则是给报告和后续实验用的硬指标。这一章只讲两件事怎么画图以及怎么从矩阵里自动提取特征量。4.1 长度-浓度输出功率热图pcolor 比 imagesc 更合适MATLAB 里画热图常见的有 imagesc 和 pcolor 两种。imagesc 的行列对应像素坐标横纵轴是索引而不是真实物理量容易在坐标标签上犯错pcolor 直接接受真实坐标矩阵网格不均匀也能画。我这边的习惯用法figure; pcolor(L_list, N0_list, log10(P_out_mat 1e-12)); shading interp; colorbar; xlabel(光纤长度 (m)); ylabel(掺杂浓度 (m^{-3})); title(光纤激光器输出功率随长度与掺杂浓度变化 (log10 W)); set(gca, YScale, log);这里加了三个细节。第一log10(P_out_mat 1e-12) 是为了把跨好几个数量级的功率压到可视图范围同时避免 0 取对数报 NaN。第二pcolor 默认显示网格线shading interp 之后颜色过渡平滑不会出现马赛克。第三YScale 设置为 log因为浓度扫描如果用 linspace 均匀取点低浓度区会被压缩看不清阈值边界。浓度轴建议改成 logspace 重新扫描或者直接画图时用对数坐标两种做法都行关键是让图上能明显看出一个最优长度脊线。这张图读起来有一个诀窍先找颜色最亮的那条竖直方向脊线它对应的就是不同浓度下的最佳光纤长度。如果脊线在某个浓度处断裂那个点附近大概率存在数值不收敛回到 3.3 节的收敛性检查去排查。4.2 从矩阵自动提取阈值长度与最佳浓度热图只能看趋势写报告还是需要具体数值。下面这段代码沿长度方向逐行扫描找出输出功率首次超过 1mW 的位置作为该浓度下的阈值长度threshold 1e-3; % 1mW作为激光阈值判据 L_th zeros(length(N0_list), 1); for i 1:length(N0_list) P_row P_out_mat(i, :); idx find(P_row threshold, 1, first); if ~isempty(idx) L_th(i) L_list(idx); else L_th(i) NaN; end end figure; plot(N0_list, L_th, o-, LineWidth, 1.5); xlabel(掺杂浓度 (m^{-3})); ylabel(阈值长度 (m)); title(不同掺杂浓度下的激光阈值长度);注意这个阈值长度不是严格物理定义它依赖你选的 1mW 判据。如果 ASE 太强低于阈值的功率也可能超过 1mW所以判断前先看最低功率点的数量级确定阈值判据比噪声底高一个数量级以上。不同浓度下阈值长度一般服从一个近似的反比关系浓度高吸收强较短长度就能达到阈值浓度低需要更长光纤积累增益。如果你的曲线不符合这个趋势重点检查模型里有没有写错泵浦吸收截面。真正用于工程设计的“最佳长度”不应该是阈值长度而是在固定浓度下输出功率最大的长度。这个提取更简单直接用 max 和 findP_max_per_row max(P_out_mat, [], 2); for i 1:length(N0_list) [~, idx_opt] max(P_out_mat(i, :)); L_opt(i) L_list(idx_opt); end4.3 单位换算把浓度从 m^-3 换回 ppm仿真里面用 m^-3 是因为所有截面数据都是国际单位物理公式直接套用不会出错。但实验人员和光纤供应商习惯用 ppm换个单位就容易差出几个数量级。Er3 掺杂浓度换算的经验公式是1 ppm 质量掺杂约等于 1.53e23 m^-3 的离子数密度具体数值取决于基质材料密度。比如 1000ppm 掺铒光纤对应的 N0 大约 1.5e26 m^-3但这个估算误差很大不同厂家差别可能到 30%。仿真完成之后如果最终要用在实验上我建议直接找供应商要掺杂光纤的典型 N0 值而不是自己拿 ppm 硬换算。拿不到的就用两个常见浓度典型值各跑一遍看最佳长度是否在可切范围内。单位这一点属于典型的“平时没人提错一次查三天”的问题值得在参数表旁边专门标注。5. 光纤激光器仿真常见问题与避坑不收敛、负功率、平凡解数值模拟代码写起来也就是几十行但跑出来的结果对不对全靠踩坑经验撑着。这一章列几个我在光纤激光器仿真里遇到的高频问题全部按现象、原因、解决三步写方便直接对照。5.1 ode45 跑得像蜗牛爬刚性方程的识别与切换现象程序能跑但 ode45 在接近稳态时步长被压到 1e-8 以下几米光纤要跑好几分钟甚至死循环。原因信号功率和泵浦功率在增益区里变化量级差距很大而且 N2 的闭合形式随泵浦和信号功率快速变化方程在局部呈现刚性。ode45 是显式 RK 方法刚性问题是它的短板它只能不停缩小步长来维持稳定。解决直接把 ode45 换成 ode15s。打靶函数里的积分器和主函数里的积分器要一起换否则两边精度不一致。改法就一行把 ode45 换成 ode15s容差保持不变。如果 ode15s 仍然慢再检查是不是 ase 项设置得太小导致方程在信号功率接近 0 时梯度很大可以适当把 ase 调大到 1e-8 左右激光器物理上始终存在自发辐射不会影响稳态精度。5.2 输出功率出现负值或者波动的非物理曲线现象扫描矩阵里某个浓度下输出功率随长度变化不是光滑曲线而是出现向下尖峰甚至 P_out 为负。原因fzero 没有收敛到真实根返回的是一个 ASE 区附近的伪根。另一种可能是 AbsTol 设置过大信号功率在低值区间被截断成负值ODE 积分器在代数值上出错。解决先检查 simulate_fiber_laser 里的 exitflag把失败的组合打印出来。然后把 AbsTol 降到 1e-12 或 1e-14 再看。如果还有尖峰重点检查那个参数组合的 fzero 初始猜测用 3.2 节的延续策略让初始猜测跟随上一个解而不是固定值。负功率在物理上不存在只要代码里出现负值一定是数值问题而不是激光器问题。5.3 光纤越长输出功率反而暴跌高浓度下的信号重吸收现象在掺杂浓度最高的那几行热图显示最优长度非常短超过之后输出功率迅速下降。原因这不是数值错误而是物理真实。信号重吸收截面 sigma_as 让反转不足的区域变成吸收体长光纤末端泵浦耗尽N1 占主导信号光被重新吸收。浓度越高重吸收越强最佳长度越短。解决这属于模型在正常工作不需要修。但要注意仿真时如果把 sigma_as 设成 0就看不到这个效应做出来的结果在低浓度下勉强能用高浓度下会高估最佳长度。工程上如果确定用的是高浓度掺铒光纤保留 sigma_as 是必须的。5.4 fzero 永远返回 0平凡解陷阱与 ASE 种子现象不管初始猜测给什么P_out 始终是 0或者矩阵里大半是 0。原因这是打靶法最经典的问题。如果 laser_eqs 里的 ase 项被设成 0那么 Ps_plus(0)0 就是边界条件的严格解fzero 必然找到这个零根。非零解因为增益小于损耗而根本不存在程序无法区分“低于阈值”和“找到零根”。解决在信号方程里保留自发辐射项让方程在零功率时也有一个微小的源平凡解自然消失。ase 的值取多大以不改变稳态输出功率为准。还有一个辅助手段fzero 不要传单点初值改传区间 [1e-6, 1e-1]fzero 会在区间内找变号点避开零根。但区间法在部分参数组合下会找不到括号而报错所以我更推荐保留 ase 项让问题从根上消失。5.5 MATLAB 中文注释在部分版本里乱码编码设置问题现象代码在旧版本 MATLAB 里写的中文注释拿到 MATLAB 2023b 打开后变成乱码严重的时候直接编译报错。原因不同版本默认编码不一致旧版本常用 GBK新版本默认 UTF-8读取时按错误编码解析中文注释和字符串就一起崩了。解决工程代码的注释统一不用中文是根治办法。哪怕你再想解释物理意义也只在 README 里写中文在 .m 文件里用英文或拼音缩写。如果已经乱码了用记事本打开 .m 文件另存为 UTF-8或者用 MATLAB 的 Preferences 里修改 MATLAB Editor 的 encoding 设置。这个问题和物理仿真无关但足以让调试心态崩掉属于典型的“小事卡半天”。6. 不算止步于连续波从静态模型走向时间动态与代理模型连续波模型只是光纤激光器仿真里最基础的一档。如果你已经能把长度和掺杂浓度扫明白下一步可以往两个方向走。第一个方向是时间相关模型把 N2 从稳态代数值改成微分变量加进速率方程里变成初值问题用 ode15s 做时间积分就能看到弛豫振荡峰甚至模拟调 Q 脉冲建立过程。物理上改动不大但信息量完全不同。第二个方向是用代理模型替代在线仿真我见过有人在 MATLAB 里用 PINN 搭正向代理模型输入长度、浓度、泵浦功率直接输出输出功率训练好之后参数优化速度比反复打靶快两个数量级。这个路子适不适合你的课题取决于你是否需要跑成千上万次参数评估如果只是设计阶段扫一遍传统打靶法仍然是可靠选择。仿真结果和实验对不上时很常用也很有效的顺序是先对比零泵浦下的插入损耗再对比阈值泵浦功率最后对比斜率效率。三个指标分开查一般能定位是损耗算错、截面参数不准还是边界条件给错。有一点是我一直保留的习惯每个仿真版本都把参数表单独存成一个 .mat 文件连同代码一起归档因为 MATLAB 里魔法数字的危害远大于注释缺失跑完一周回来再想复盘参数表是最可靠的后悔药。希望这个方向的经验能帮到你也祝你的仿真第一次跑出来就能对上实验。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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