
1. 从一束激光说起为什么需要复参数q和ABCD矩阵如果你做过激光光路搭建或者光学系统仿真大概率遇到过这样的场景激光器出射的是一束标准的高斯光束经过一个透镜聚焦、再经过一段自由空间传输、又碰到一个反射镜折转最后打到某个探测器或者加工面上。这时候你想知道最终到达目标位置的光斑半径是多少波前曲率半径变成多少了束腰位置又跑到哪里去了最笨的办法是老老实实套高斯光束的传输公式一段一段地算。自由空间传输一个公式经过薄透镜一个公式经过球面镜再一个公式。光路稍微复杂一点比如五六个元件串起来公式套公式算到后面自己都不记得哪个R(z)对应哪一段了。更麻烦的是一旦某个元件的位置微调了所有中间结果全部作废得从头再来一遍。ABCD定律配合复参数q就是专门解决这个痛点的。它的核心思想非常优雅把高斯光束在某一截面上的全部信息——光斑半径w和波前曲率半径R——打包成一个复数q然后每一种光学元件自由空间、薄透镜、球面镜、介质界面等都对应一个2×2的矩阵。光束经过一个元件只需要做一次矩阵乘法q参数就更新了。多个元件串联就把各自的ABCD矩阵按顺序乘起来得到一个总的矩阵再作用到初始q上一步到位算出最终结果。这套方法在激光谐振腔设计、光束整形系统、光纤耦合光路里几乎是标配工具。我见过不少做光学实验的朋友明明光路不复杂却因为不会用ABCD定律每次调光路都靠试错效率很低。其实只要把复参数q和几个常用矩阵记牢配合MATLAB写个小脚本整个光路的光束演化可以秒算出来而且改参数、扫范围都非常方便。这篇内容主要面向三类人一是正在学激光原理或工程光学课程、被q参数和ABCD矩阵搞得有点晕的学生二是做激光实验或光路设计、需要快速估算光束参数的工程师三是想用MATLAB把光学计算自动化、但不知道从哪里下手的朋友。我会从复参数q的定义讲起把物理意义说清楚然后把自由空间、薄透镜、球面镜、介质界面这几个最常用的ABCD矩阵逐一列出来每个都配上MATLAB代码和验证算例。最后给一个多元件串联的完整例子让你能直接抄作业。提示这篇是系列的第一篇重点在复参数q和常用光学系统ABCD矩阵的MATLAB实现。后续会讲高斯光束在谐振腔中的自洽条件、q参数的自再现、以及更复杂的多元件系统优化。2. 复参数q到底在描述什么物理意义拆解2.1 从高斯光束的两个核心参数说起高斯光束在传播方向上任意一个截面z处最关心的两个量是光斑半径w(z)和波前曲率半径R(z)。光斑半径描述的是光束有多粗波前曲率半径描述的是等相位面弯曲得有多厉害。这两个量随z变化的公式学过激光原理的应该都见过w(z) w0 * sqrt(1 (z/zR)^2)R(z) z * (1 (zR/z)^2)其中w0是束腰半径zR pi * w0^2 / lambda是瑞利长度lambda是波长。这两个公式本身不复杂但问题是它们都是z的函数而且R(z)在束腰处是无穷大在别的地方又可能是正也可能是负处理起来不太方便。复参数q的巧妙之处在于它把这两个实数参数合并成一个复数1/q(z) 1/R(z) - i * lambda / (pi * w(z)^2)这个定义式是整套ABCD定律的基石。你仔细看实部1/R对应波前曲率虚部-lambda/(pi*w^2)对应光斑大小。一个复数同时携带了两个实数的信息而且后面你会发现它在经过光学系统时的变换规则极其简洁。2.2 为什么用1/q而不是q本身这里有个细节值得说一下。定义式写的是1/q而不是直接定义q。为什么因为用1/q的话实部和虚部都有明确的物理意义而且变换规则是线性的。如果直接定义q变换公式会变成分式形式虽然也能用但不如1/q来得直观。从1/q出发可以反解出q的表达式q(z) z i * zR这个形式非常漂亮。在束腰处z0q i*zR纯虚数。随着传播距离增加q的实部就是z虚部保持zR不变。也就是说自由空间传播对q的影响就是实部加上传播距离这个结论后面会反复用到。从q反推w和R也很方便1/R Re(1/q) lambda / (pi * w^2) -Im(1/q)所以只要知道q光斑和波前曲率就全有了。MATLAB里用real(1/q)和imag(1/q)两行代码就能提取出来。2.3 q参数在束腰处的特殊性和符号约定束腰处R趋于无穷大1/R 0所以1/q是纯虚数q i*zR也是纯虚数。这时候光斑半径最小等于w0。这个位置在光路计算里非常重要因为很多光学系统的设计目标就是把束腰放在特定位置或者让束腰半径达到特定值。关于符号约定不同教材可能略有差异。我这里采用的是最常见的约定波前曲率半径R以光束传播方向为正方向发散光束R为正会聚光束R为负。虚部方面1/q的虚部是负的对应q的虚部为正。这个约定和大多数激光原理教材一致MATLAB代码里也是按这个来的。注意如果你参考的教材或论文用的是相反的符号约定ABCD矩阵里某些元素的符号可能需要调整。建议在开始计算前先确认一下符号约定避免后面结果对不上。2.4 用MATLAB验证q参数的基本性质光说不练假把式。先用几行MATLAB代码验证一下q参数的基本性质顺便熟悉一下后面要用的函数结构。% 基本参数设置 lambda 1064e-9; % 波长单位米 w0 0.5e-3; % 束腰半径单位米 zR pi * w0^2 / lambda; % 瑞利长度 % 在束腰处 z 0; q0 z 1i * zR; fprintf(束腰处 q %.4f %.4fi\n, real(q0), imag(q0)); fprintf(束腰处 1/q %.4e %.4ei\n, real(1/q0), imag(1/q0)); % 传播一段距离后 z1 0.5; % 传播0.5米 q1 z1 1i * zR; w1 sqrt(lambda / (pi * -imag(1/q1))); R1 1 / real(1/q1); fprintf(传播0.5m后 w %.4f mm, R %.4f m\n, w1*1e3, R1); % 用公式直接验证 w1_direct w0 * sqrt(1 (z1/zR)^2); R1_direct z1 * (1 (zR/z1)^2); fprintf(公式验证 w %.4f mm, R %.4f m\n, w1_direct*1e3, R1_direct);跑一下这段代码你会发现两种方法算出来的w和R完全一致。这说明q参数的定义和反推公式是对的。这个验证步骤看起来简单但实际项目中我建议每次都做一下尤其是换了符号约定或者单位之后花几秒钟确认一下能避免后面大量返工。3. 常用光学系统的ABCD矩阵从自由空间到球面镜3.1 自由空间传播矩阵及其MATLAB实现自由空间传播距离L的ABCD矩阵是最简单的一个M_free [1, L; 0, 1]这个矩阵作用到q上的效果是q_out q_in L也就是实部增加L虚部不变。这和我们前面说的“自由空间传播对q的影响就是实部加上传播距离”完全一致。MATLAB里定义一个自由空间矩阵的函数function M freeSpace(L) % 自由空间传播矩阵 % 输入L - 传播距离米 % 输出2x2 ABCD矩阵 M [1, L; 0, 1]; end这个函数简单到几乎不需要注释但把它单独封装成函数的好处是后面串联多个元件时代码更清晰。我见过有人把所有矩阵硬编码在主脚本里光路一复杂就乱成一团改一个参数要翻半天。封装成函数之后主脚本里就是一行一个元件可读性完全不一样。3.2 薄透镜矩阵与厚透镜的区别薄透镜的ABCD矩阵是M_lens [1, 0; -1/f, 1]其中f是透镜焦距会聚透镜f为正发散透镜f为负。这个矩阵的物理意义是经过薄透镜后光束的位置不变所以左上角是1右上角是0但波前曲率发生变化左下角是-1/f。厚透镜和薄透镜的区别在于厚透镜要考虑透镜本身的厚度和两个表面的曲率。如果透镜厚度d远小于焦距f用薄透镜近似完全够用。但如果厚度和焦距可比比如一些短焦距的显微物镜就需要用厚透镜矩阵M_thick [1 - d/(nf2), d/n; -1/f1 - 1/f2 d/(nf1f2), 1 - d/(nf1)]其中n是透镜材料折射率f1和f2分别是前后表面的焦距d是透镜厚度。这个公式看起来复杂但实际用的时候直接套就行。大多数激光光路里的透镜都是薄透镜厚透镜主要出现在精密成像系统里。function M thinLens(f) % 薄透镜矩阵 % 输入f - 焦距米会聚为正发散为负 M [1, 0; -1/f, 1]; end3.3 球面镜矩阵和介质界面的处理球面镜的ABCD矩阵和薄透镜形式一样只是焦距用R/2代替M_mirror [1, 0; -2/R, 1]其中R是球面镜的曲率半径凹面镜R为正会聚凸面镜R为负发散。这里要注意符号约定如果镜面朝向光束传播方向弯曲R取正反之取负。实际光路里反射镜通常会让光束折转所以计算时要把光路展开成直线反射镜就等效于一个焦距为R/2的薄透镜。介质界面的矩阵稍微特殊一点。当光束从折射率n1的介质进入折射率n2的介质且界面是平面时M_interface [1, 0; 0, n1/n2]如果是球面界面曲率半径为R从n1侧看则M_spherical [1, 0; (n1-n2)/(n2*R), n1/n2]这个矩阵在光纤耦合和固体激光器里经常用到因为光束要从空气进入晶体或者从晶体进入空气。function M sphericalMirror(R) % 球面镜矩阵 % 输入R - 曲率半径米凹面镜为正 M [1, 0; -2/R, 1]; end function M flatInterface(n1, n2) % 平面介质界面矩阵 % 输入n1 - 入射侧折射率n2 - 出射侧折射率 M [1, 0; 0, n1/n2]; end3.4 常用ABCD矩阵速查表把上面几个矩阵整理成表格方便随时查阅光学元件ABCD矩阵关键参数典型应用场景自由空间[1, L; 0, 1]L传播距离空气光路、真空腔薄透镜[1, 0; -1/f, 1]f焦距聚焦、准直、扩束球面镜[1, 0; -2/R, 1]R曲率半径谐振腔、光束折转平面界面[1, 0; 0, n1/n2]n1,n2折射率空气-晶体界面球面界面[1, 0; (n1-n2)/(n2R), n1/n2]R曲率半径透镜表面、光纤端面这张表建议存下来实际计算时直接查。我自己的习惯是在MATLAB脚本开头把这些矩阵都定义好后面串联光路时直接调用函数代码干净且不容易出错。4. 用MATLAB串联多个元件从单透镜到复杂光路4.1 矩阵乘法的顺序问题最容易搞错的地方多个元件串联时总的ABCD矩阵是各元件矩阵按顺序相乘。但这里有个关键细节矩阵乘法的顺序是从右到左。也就是说如果光束先经过元件1再经过元件2再经过元件3总矩阵是M_total M3 * M2 * M1而不是M1 * M2 * M3。这个顺序问题我见过太多人搞错包括我自己早期也犯过。原因在于矩阵作用到列向量上时最右边的矩阵最先作用。q参数可以看作一个列向量[ q; 1 ]的某种变换所以顺序不能反。在MATLAB里写的时候建议按光路顺序从右往左乘或者用循环从最后一个元件开始往前乘。我个人的习惯是% 假设有n个元件矩阵存在cell数组里 M_total eye(2); for k n:-1:1 M_total M_total * M_list{k}; end这样写不容易出错。如果你习惯从左往右写那就把循环改成从1到n但乘法顺序要反过来M_total eye(2); for k 1:n M_total M_list{k} * M_total; end两种写法等价选一种自己不容易搞混的就行。4.2 单透镜聚焦经典案例的完整计算单透镜聚焦是最经典的案例高斯光束从束腰处出发传播距离d1到达透镜经过焦距f的透镜再传播距离d2到目标面。总矩阵是M freeSpace(d2) * thinLens(f) * freeSpace(d1)然后q_out (Aq_in B) / (Cq_in D)其中A,B,C,D是总矩阵的四个元素。% 参数设置 lambda 1064e-9; w0 0.5e-3; zR pi * w0^2 / lambda; q_in 1i * zR; % 束腰处 d1 0.3; % 束腰到透镜距离 f 0.1; % 透镜焦距 d2 0.15; % 透镜到目标面距离 % 构建总矩阵 M freeSpace(d2) * thinLens(f) * freeSpace(d1); A M(1,1); B M(1,2); C M(2,1); D M(2,2); % 计算输出q q_out (A*q_in B) / (C*q_in D); % 提取光斑和波前曲率 w_out sqrt(lambda / (pi * -imag(1/q_out))); R_out 1 / real(1/q_out); fprintf(输出光斑半径 w %.4f mm\n, w_out*1e3); fprintf(输出波前曲率半径 R %.4f m\n, R_out);这个例子里你可以试着改一下d2的值看看光斑半径怎么变化。当d2等于某个值时光斑最小那就是聚焦点。用MATLAB扫一遍d2画个曲线聚焦位置一目了然。这种扫描在实验前做一次能省下大量调光路的时间。4.3 两透镜扩束系统矩阵方法 vs 逐段计算两透镜扩束系统是激光光路里非常常见的配置通常是一个短焦距透镜和一个长焦距透镜组合用来把光束直径放大或者缩小。用ABCD矩阵算这个系统特别方便。假设透镜1焦距f1透镜2焦距f2间距d光束从透镜1前某个位置入射。总矩阵是M freeSpace(d) * thinLens(f2) * freeSpace(d) * thinLens(f1)等等这里间距是d但两个透镜之间只有一段自由空间所以应该是M freeSpace(d) * thinLens(f2) * thinLens(f1)不对透镜1和透镜2之间有一段距离d所以顺序是先经过透镜1再传播d再经过透镜2。总矩阵M thinLens(f2) * freeSpace(d) * thinLens(f1)这个矩阵乘出来之后你可以分析扩束比、准直条件等。比如当d f1 f2时系统对入射平行光出射也是平行光这就是典型的开普勒式扩束。如果d f1 f2且f2 f1扩束比就是f2/f1。用MATLAB算这个系统可以很快验证不同d值下的输出光束参数。我做过一个对比用矩阵方法算一遍只要几毫秒而逐段套公式手算至少要十几分钟还容易出错。尤其是需要扫描多个参数的时候矩阵方法配合MATLAB循环效率提升非常明显。4.4 谐振腔中的往返矩阵ABCD定律的进阶用法谐振腔是ABCD定律最重要的应用场景之一。光在腔内往返一次等效于经过一个往返矩阵。如果光束在腔内稳定传播那么往返一次后q参数应该自再现即q (Aq B)/(Cq D)。这个方程解出来就是腔的稳定条件和本征模式。往返矩阵的构建方法是从某个参考面出发沿光路走一圈回到参考面把沿途所有元件的矩阵按顺序乘起来。比如一个简单的两镜腔镜1曲率半径R1镜2曲率半径R2腔长L从镜1出发的往返矩阵是M_rt sphericalMirror(R1) * freeSpace(L) * sphericalMirror(R2) * freeSpace(L)注意这里镜1的矩阵出现了两次因为往返一次会经过镜1两次一次是出发时一次是返回时。实际上更准确的写法是从镜1表面出发经过自由空间到镜2被镜2反射再经过自由空间回到镜1被镜1反射。所以M_rt sphericalMirror(R1) * freeSpace(L) * sphericalMirror(R2) * freeSpace(L)这个矩阵算出来之后解自再现方程就能得到腔的基模参数。稳定条件通常写成|(AD)/2| 1这个条件在腔设计里是必须满足的否则光束会跑出腔外。% 两镜腔往返矩阵示例 R1 0.5; % 镜1曲率半径 R2 0.5; % 镜2曲率半径 L 0.3; % 腔长 M_rt sphericalMirror(R1) * freeSpace(L) * sphericalMirror(R2) * freeSpace(L); A M_rt(1,1); B M_rt(1,2); C M_rt(2,1); D M_rt(2,2); % 稳定条件 stability abs((A D)/2); fprintf(稳定因子 %.4f\n, stability); if stability 1 fprintf(腔是稳定的\n); else fprintf(腔不稳定\n); end这段代码可以直接用来快速判断一个腔设计是否稳定。实际做腔设计时我通常会扫一遍L或者R的值画出稳定区间的图这样能直观看到哪些参数组合是可行的。5. 实操中容易踩的坑和验证技巧5.1 单位不统一最常见的低级错误光学计算里单位问题特别容易出错。波长通常用纳米束腰半径用毫米传播距离用米焦距用毫米。如果不统一成同一套单位算出来的结果会差好几个数量级。我的建议是全部统一成国际单位制波长用米束腰用米距离用米焦距用米。MATLAB里输入的时候写成科学计数法比如1064e-9、0.5e-3。输出的时候再转换成毫米或者微米显示。这样中间计算过程不会因为单位问题出错。% 推荐的单位处理方式 lambda 1064e-9; % 米 w0 0.5e-3; % 米 d 0.3; % 米 f 0.1; % 米 % 输出时转换 fprintf(w %.4f mm\n, w*1e3); fprintf(R %.4f m\n, R);5.2 符号约定不一致导致的“结果对不上”前面提过符号约定的问题这里再强调一下。不同教材对R的正负定义可能不同对虚部符号的定义也可能不同。如果你发现用MATLAB算出来的结果和手算或者文献对不上第一件事就是检查符号约定。一个简单的验证方法是自由空间传播后光斑半径应该增大波前曲率半径应该从无穷大变成有限值。如果算出来光斑变小了那肯定是符号或者公式有问题。另一个验证方法是经过会聚透镜后波前曲率半径应该变成负的会聚如果算出来是正的也要检查符号。5.3 用能量守恒和极限情况做交叉验证ABCD定律本身是线性变换不涉及能量损失除非有光阑或者吸收元件。所以计算前后总功率应该守恒。虽然q参数不直接包含功率信息但光斑半径的变化趋势应该合理经过会聚透镜后光斑先变小后变大经过发散透镜后光斑持续变大。极限情况也是很好的验证手段。比如焦距趋于无穷大时薄透镜矩阵应该趋于单位矩阵传播距离趋于零时自由空间矩阵应该趋于单位矩阵。把这些极限情况代进去检查一下能发现很多潜在的错误。5.4 复数运算中的数值稳定性问题MATLAB处理复数运算一般没问题但当q的虚部非常大或者非常小的时候可能会出现数值精度问题。比如束腰半径非常小的时候zR pi*w0^2/lambda会很小q的虚部就很小1/q的虚部会很大计算w的时候可能出现舍入误差。解决办法是尽量避免直接计算1/q的虚部再开方而是用q的模和相位来算。不过对于大多数实际光路w0在0.1mm到几mm之间zR在几厘米到几米之间数值稳定性完全够用。只有做微纳光学或者极端参数计算时才需要特别注意。5.5 把常用计算封装成可复用函数最后分享一个我自己的习惯把ABCD定律的核心计算封装成一个函数输入初始q和元件矩阵列表输出最终q和中间结果。这样每次算新光路只需要改元件列表不用重复写计算代码。function [q_out, q_list] propagateQ(q_in, M_list) % 计算高斯光束经过多个元件后的q参数 % 输入q_in - 初始q参数 % M_list - 元件矩阵的cell数组按光路顺序排列 % 输出q_out - 最终q参数 % q_list - 每个元件后的q参数用于调试 n length(M_list); q_list zeros(1, n1); q_list(1) q_in; q_current q_in; for k 1:n M M_list{k}; A M(1,1); B M(1,2); C M(2,1); D M(2,2); q_current (A*q_current B) / (C*q_current D); q_list(k1) q_current; end q_out q_current; end这个函数虽然简单但用起来非常顺手。你可以把光路上每个元件的矩阵按顺序放进cell数组一行调用就能得到所有中间结果。调试的时候把q_list打出来看看每一步的变化很容易定位问题。6. 一个完整算例从激光器到加工面的全链路计算6.1 光路描述和参数设置假设一个光纤激光器输出波长为1064nm束腰半径0.3mm束腰位于输出端面。光路依次经过自由空间传播0.5m经过焦距为50mm的准直透镜再传播0.2m经过焦距为100mm的聚焦透镜最后传播0.15m到达加工面。求加工面上的光斑半径和波前曲率半径。这个光路在激光加工里非常典型先准直再聚焦。用ABCD矩阵算一遍看看最终光斑能不能满足加工要求。% 参数设置 lambda 1064e-9; w0 0.3e-3; zR pi * w0^2 / lambda; q_in 1i * zR; % 光路元件 M_list { freeSpace(0.5), % 自由空间0.5m thinLens(0.05), % 准直透镜f50mm freeSpace(0.2), % 自由空间0.2m thinLens(0.1), % 聚焦透镜f100mm freeSpace(0.15) % 自由空间0.15m }; % 计算 [q_out, q_list] propagateQ(q_in, M_list); % 提取结果 w_out sqrt(lambda / (pi * -imag(1/q_out))); R_out 1 / real(1/q_out); fprintf(加工面光斑半径 w %.4f mm\n, w_out*1e3); fprintf(加工面波前曲率半径 R %.4f m\n, R_out);6.2 结果解读和参数扫描跑完这段代码你会得到一个光斑半径。如果这个值比预期的大可以试着调整聚焦透镜的焦距或者最后一段自由空间的距离。用MATLAB写个循环扫一遍d2画出光斑半径随d2变化的曲线聚焦位置就一目了然了。% 扫描最后一段距离 d2_range linspace(0.05, 0.3, 100); w_range zeros(size(d2_range)); for k 1:length(d2_range) M_list_scan { freeSpace(0.5), thinLens(0.05), freeSpace(0.2), thinLens(0.1), freeSpace(d2_range(k)) }; [q_out, ~] propagateQ(q_in, M_list_scan); w_range(k) sqrt(lambda / (pi * -imag(1/q_out))); end plot(d2_range*1e3, w_range*1e3, LineWidth, 1.5); xlabel(最后一段距离 (mm)); ylabel(光斑半径 (mm)); title(光斑半径随加工面位置的变化); grid on;这条曲线会显示一个最小值对应的d2就是最佳聚焦位置。实际调光路的时候这个曲线能告诉你聚焦透镜应该放在哪里、加工面应该放在哪里。我每次做新光路设计都会先跑一遍这个扫描心里有数之后再动手调效率高很多。6.3 从计算结果反推实验调整方向如果算出来的光斑半径比目标值大有几个调整方向一是换焦距更短的聚焦透镜二是调整准直透镜和聚焦透镜之间的距离三是调整激光器输出端的束腰位置。每个调整方向都可以用ABCD矩阵快速评估效果不用反复试错。比如把聚焦透镜焦距从100mm换成50mm重新跑一遍计算看看光斑能小多少。如果还不够再试试调整两个透镜之间的距离。这种参数扫描在MATLAB里就是改几个数字的事几秒钟就能得到结果。相比在实验台上反复拆装透镜效率完全不是一个量级。提示实际光路中透镜有像差高斯光束的ABCD定律只适用于理想薄透镜近似。如果光斑接近衍射极限或者透镜数值孔径很大像差的影响不能忽略这时候需要用更复杂的物理光学仿真工具。但对于大多数常规光路ABCD定律的精度完全够用。7. 从这一篇到下一篇q参数自再现和谐振腔稳定性把复参数q和常用ABCD矩阵用熟之后你会发现很多光学系统的问题都可以归结为矩阵乘法。自由空间、透镜、反射镜、介质界面这几个基本元件组合起来能覆盖大多数激光光路的计算需求。下一篇会讲q参数在谐振腔中的自再现条件也就是q (Aq B)/(Cq D)这个方程怎么解以及怎么用MATLAB判断腔的稳定性和计算基模参数。那部分内容在激光器设计里非常核心尤其是做固体激光器或者光纤激光器谐振腔的时候稳定区间的计算直接决定了腔长和镜片曲率的选择。如果你现在手头有正在设计的光路建议先把这篇里的MATLAB函数跑一遍把自由空间、薄透镜、球面镜这几个矩阵用熟。然后试着把你自己的光路用矩阵串联起来算一遍和实验值对比一下。如果对得上说明你的符号约定和单位处理都没问题如果对不上检查一下矩阵乘法的顺序和符号约定大概率是这两个地方出了问题。我自己刚开始用ABCD定律的时候最大的收获不是学会了几个矩阵而是建立了一种“把光路变成矩阵链”的思维方式。一旦习惯了这种思维方式再复杂的光路都能拆解成基本元件的组合计算起来心里特别有底。希望这篇内容能帮你建立起同样的直觉。