不知道你有没有遇到过这样的场景上课讲电偶极子书上画的等势线挺漂亮可自己在草稿纸上推了半天脑子里还是那块球形电场绕来绕去的样子转不过弯来。我当时学电磁学就是这样公式能背题会算但要让我说说电偶极子在空间里到底长什么样电场线从哪出发到哪结束脑子里一片空白。后来我干脆写了个MATLAB仿真把电位和电场全部画出来才算是把电偶极子彻底“看”明白了。这篇文章我就把这个思路和完整源码分享出来不光是贴代码连每一步的数学原理、画图参数、常见报错都一起聊清楚保证你能直接运行出图也能看懂每行代码背后的道理。这套内容适合三类人正在学大学物理或电磁场理论的学生想用MATLAB做电磁场可视化但不知道怎么下手的初学者以及要做科研演示、课程报告的朋友。你不需要有多深的MATLAB功底只要会基本语法跟着思路走就能跑通。1. 电偶极子模拟的整体设计与数学基础1.1 电偶极子的物理模型回顾电偶极子说白了就是两个电荷量相等、符号相反的点电荷距离靠得很近。一个带正电q一个带负电-q它们之间的距离是d。这个系统最重要的物理量叫电偶极矩记作p大小是 p q·d方向从负电荷指向正电荷。很多教材里也会用p q·a来表示其中a是从负电荷到正电荷的位移矢量。之所以要把“间距很小”这个条件明确出来是因为电偶极子的很多性质都是在小间距近似下才成立的。比如说远处某点的电位就近似为[ V(\mathbf{r}) \frac{1}{4\pi\varepsilon_0} \cdot \frac{\mathbf{p} \cdot \hat{\mathbf{r}}}{r^2} ]这个式子的含义很直观某点的电位与偶极矩在该点方向上的投影成正比与距离的平方成反比。对比单个点电荷的电位 V q/(4πε₀r)偶极子的电位随距离衰减得更快这是很多教材强调的重点。但注意这个公式是远场近似。如果场点离偶极子很近比如距离和电荷间距d一个量级那就不能再用这个近似公式了必须用两个点电荷电位的直接叠加[ V(x,y) \frac{1}{4\pi\varepsilon_0} \left( \frac{q}{r_} - \frac{q}{r_-} \right) ]其中r₊是场点到正电荷的距离r₋是场点到负电荷的距离。我们仿真时优先采用这种精确表达式因为它能画出近场区域的真实分布远场自然也会退化为近似公式。具体到二维平面比如x-y平面假设正电荷在 (d/2, 0)负电荷在 (-d/2, 0)那么空间任意一点 (x, y) 的电位就是[ V(x,y) \frac{1}{4\pi\varepsilon_0} \left( \frac{q}{\sqrt{(x-d/2)^2 y^2}} - \frac{q}{\sqrt{(xd/2)^2 y^2}} \right) ]1.2 为什么要用MATLAB做场模拟市面上能画电磁场的软件不少COMSOL、Ansys这些专业有限元工具功能强大但它们的定位是工程计算建模流程繁琐而且初学者很难在半小时内把结果跑出来。MATLAB做这件事的优势在于“脚本即实验”你写几行代码马上能看到结果改参数重新运行思路连续不断。另一个关键点是MATLAB的矩阵运算天然适配网格化场计算。电场模拟的本质就是在空间离散点上计算物理量这正好是矩阵的高维运算。你不需要写for循环一个个点去算用向量化操作一次就能算出整个平面的数据速度快代码也简洁。还有一点是MATLAB的可视化工具相当成熟。contour、quiver、surf这几个函数分别对应等势线、矢量场、三维曲面配合起来可以一图看全电偶极子的所有信息。这是做物理演示时特别实用的功能。1.3 模拟方案的选型思路我见过不少人一上来就搞三维体积渲染把空间划分成三维网格然后每个点算电位。这个方案视觉效果好但计算量大而且三维图里等势面不好观察。我更推荐的方案是先做二维平面模拟把x-y平面上的电位分布画出来再叠加电场矢量箭头。这个方案的好处有三个第一二维图信息完整。电偶极子具有轴对称性在包含偶极矩方向的平面里已经包含了所有关键信息另一个垂直平面的情况几乎相同只是旋转了一下。第二contour和quiver两种图可以叠加在同一坐标轴上等势线和电场线的正交关系一眼就能看出来这是理解电磁场的重要直观抓手。第三计算量小普通笔记本完全没压力。三维图可以作为补充展示但没必要一开始就上。2. 核心代码实现从参数设置到电位计算2.1 参数设置与网格生成先看完整的初始化和网格生成代码% 电偶极子参数 q 1e-9; % 电荷量单位C这里取1nC d 0.1; % 电荷间距单位m epsilon0 8.854187817e-12; % 真空介电常数 % 正负电荷位置 pos_charge [d/2, 0]; neg_charge [-d/2, 0]; % 网格范围与密度 x_range [-0.5, 0.5]; y_range [-0.5, 0.5]; N 200; % 网格点数点数越多图越细腻 % 生成二维网格 x linspace(x_range(1), x_range(2), N); y linspace(y_range(1), y_range(2), N); [X, Y] meshgrid(x, y);这里有两个参数很容易调错我提醒一下。第一个是网格点数N。点数太少等势线会又粗又毛糙点数太多计算时间会明显上升而且生成的图片文件也大。200左右是经验值够细腻且运行快。如果你要出印刷级别的图可以调到400或500但普通演示200就行。第二个是电荷间距d和网格范围的比例。d如果取得过大比如0.2而网格范围只有 -0.5 到 0.5那正负电荷之间会出现剧烈的电位变化画图时颜色映射会比较难看等势线间距也不均匀。我的经验是让电荷间距占整个网格范围的五分之一到十分之一这样既能看清近场细节又能看到远场的衰减趋势。在这个例子里d0.1网格范围是 [-0.5, 0.5]比例是1:10效果正好。2.2 电位计算的精确公式实现有了网格和电荷位置直接算每个格点到两个电荷的距离再叠加电位% 计算每个网格点到正负电荷的距离 r_plus sqrt((X - pos_charge(1)).^2 (Y - pos_charge(2)).^2); r_minus sqrt((X - neg_charge(1)).^2 (Y - neg_charge(2)).^2); % 防止距离为零导致除零错误 r_plus(r_plus 1e-6) 1e-6; r_minus(r_minus 1e-6) 1e-6; % 电位叠加 V (1 / (4 * pi * epsilon0)) * (q ./ r_plus - q ./ r_minus);这一小段是整个模拟的核心原理就是静电场的叠加定理空间中任意一点的电位等于各电荷单独存在时产生电位的代数和。电位是标量所以直接加减就行。q 取 1e-91纳库仑d取0.1米算出来V的量级在几伏到几百伏之间画图时需要处理一下数值范围不然大部分区域的颜色会非常接近细节看不清。常用的办法是只画有限范围的等势线或者对电位做符号处理后再画。后面可视化部分我会专门说。关于除零错误的处理虽然理论上电荷只在两个孤立的点上有值但网格点有可能恰好落在电荷所在坐标上。一旦出现距离为0除以0会得到Inf后面整个图都会出现异常。用r_plus(r_plus 1e-6) 1e-6这句话把过于小的距离替换掉是常规做法。注意阈值不能设太大否则会扭曲电荷附近的真实电位。2.3 电场计算的两种方式画电场线需要知道每个点的电场矢量E。有两种方式可以获得我建议都用一下各有用处。第一种方式是从电位梯度计算( \mathbf{E} -\nabla V )。在数值上可以用gradient函数实现% 用电位梯度计算电场 [Ex, Ey] gradient(-V, x(2) - x(1), y(2) - y(1));注意这里gradient的第一个参数是 -V因为电场是电位的负梯度。x(2)-x(1) 是x方向的步长y(2)-y(1) 是y方向的步长这两个参数不能省不然梯度计算的尺度不对画出来的箭头方向虽然正确但长度会失真。第二种方式是从叠加定理直接计算% 直接叠加两个点电荷的电场 Ex zeros(size(X)); Ey zeros(size(Y)); % 正电荷贡献 Ex Ex (1/(4*pi*epsilon0)) * q * (X - pos_charge(1)) ./ r_plus.^3; Ey Ey (1/(4*pi*epsilon0)) * q * (Y - pos_charge(2)) ./ r_plus.^3; % 负电荷贡献方向指向负电荷所以是减法 Ex Ex - (1/(4*pi*epsilon0)) * q * (X - neg_charge(1)) ./ r_minus.^3; Ey Ey - (1/(4*pi*epsilon0)) * q * (Y - neg_charge(2)) ./ r_minus.^3;这里用到的公式是点电荷电场公式正电荷产生的电场方向从电荷指向场点负电荷产生的电场方向从场点指向电荷所以代码里有一个减号。实测下来两种方式的远场结果几乎一致近场会有一点点差别因为梯度法在电位变化特别剧烈的地方会有数值误差。如果只是画矢量场看方向直接用梯度法快且省事如果要做定量分析建议用叠加法。2.4 电场强度幅值与归一化画quiver图的时候有个视觉陷阱如果直接用真实的电场幅值来画箭头靠近电荷的地方箭头会特别长远离电荷的地方箭头几乎看不见整张图的信息会严重失衡。解决办法是归一化处理只保留方向信息统一箭头长度% 计算电场强度幅值 E_mag sqrt(Ex.^2 Ey.^2); % 归一化用于绘制箭头方向 Ex_norm Ex ./ E_mag; Ey_norm Ey ./ E_mag;归一化之后每个箭头的长度都一样视觉上看的是方向分布。要显示幅值大小我会用背景色或者箭头颜色来表示这个后面会讲到。补充一句E_mag这个变量不要扔掉后面画三维曲面、展示电场强度分布的时候还要用。3. 可视化方案一图看懂电偶极子的全部信息3.1 等势线contour绘制技巧画等势线最直接的方式是contour(X, Y, V, levels)其中levels是一个向量指定要画的等势线数值。但这里有个坑V的范围跨度很大正电荷附近能到几千伏远处只剩下几伏直接用线性刻度画远处的等势线会全部挤在一起。我的解决方法是把电位用sigmoid函数压缩一下% 对电位做非线性映射压缩动态范围 V_scaled 2 ./ (1 exp(-V / 50)) - 1;这个公式把V映射到 (-1, 1) 区间V0 处映射为0。这样做的好处是近处的大电位不会把颜色标尺撑爆远处的细微变化也能显示出来。这里的50是缩放因子具体值取决于你算出的V的量级。我试下来V_max大约几百伏的情况下50效果不错如果V_max是几万伏就要把缩放因子调大到500左右。你可以先跑一下看V的范围再调这个参数。然后画出等势线figure(Color, w); % 等势线 [C, h] contour(X, Y, V_scaled, 30); % 30条等势线 clabel(C, h, FontSize, 8, LabelSpacing, 150); xlabel(x (m)); ylabel(y (m)); title(电偶极子等势线分布); axis equal; grid on;clabel函数会在等势线上标注数值LabelSpacing控制标注之间的距离数值越大标注越稀疏避免标签重叠。这里有个很容易忽略的点axis equal 必须加不然坐标轴的刻度比例不一致圆形的等势线会被拉成椭圆形物理图像就错了。3.2 电场矢量场quiver绘制画电场方向用quiver。为了图面清晰我不会在每一个网格点上都画箭头那样200×200个箭头会把整个图涂黑。通常每隔几个点采样一次% 每隔4个点取一个箭头避免箭头过密 skip 4; figure(Color, w); quiver(X(1:skip:end, 1:skip:end), ... Y(1:skip:end, 1:skip:end), ... Ex_norm(1:skip:end, 1:skip:end), ... Ey_norm(1:skip:end, 1:skip:end), ... 0.5); % 0.5是箭头缩放因子 hold on;这个0.5的缩放因子很关键。quiver默认会按数据自动缩放箭头长度但归一化之后箭头的“长度”信息已经没意义了你再让它自动缩放就会得到一堆莫名其妙的短箭头或者长箭头。手动给定缩放因子之后箭头长度基本一致只表达方向。你可以根据实际效果调0.3到0.8之间。另外注意quiver画出来的箭头只是方向不是电场线的连续轨迹。要得到真正的电场线从正电荷出发到负电荷结束的连续曲线得用streamline函数或者自己写流线追踪算法。这个我放在扩展内容里讲基础版先用quiver就够了。3.3 三维电位曲面直观感受“势阱”与“势垒”除了二维图三维曲面图也是理解电位分布的好帮手。surf可以把电位值映射成曲面高度和颜色figure(Color, w); surf(X, Y, V, EdgeColor, none); colormap(jet); colorbar; xlabel(x (m)); ylabel(y (m)); zlabel(电位 V (V)); title(电偶极子电位三维曲面); view(35, 45);从这张图能明显看到两个“尖峰”正电荷处电位冲向正无穷图中显示为红色高峰负电荷处电位跌向负无穷蓝色深谷远处电位逐渐趋于零。中间电中性的地方电位为零形成一个“鞍点”这个鞍点的位置在原点是很多教材里讲的重点三维图上看得特别清晰。surf默认会画出网格线数据点一多会显得杂乱所以加上EdgeColor, none关掉网格线只保留颜色映射图面会干净很多。3.4 合并画图等势线电场矢量一图流把等势线和电场矢量画在同一张图上是最推荐的展示方式figure(Color, w); [C, h] contour(X, Y, V_scaled, 30); clabel(C, h, FontSize, 8, LabelSpacing, 150); hold on; quiver(X(1:skip:end, 1:skip:end), ... Y(1:skip:end, 1:skip:end), ... Ex_norm(1:skip:end, 1:skip:end), ... Ey_norm(1:skip:end, 1:skip:end), ... 0.5, Color, [0.6, 0.2, 0.2]); hold off; xlabel(x (m)); ylabel(y (m)); title(电偶极子等势线与电场矢量分布); axis equal; grid on;这张图能非常直观地验证两个基本定理第一电场矢量和等势线处处正交。你会发现箭头的方向总是垂直于等势线的切线方向这正是电场与等势面的几何关系。第二电场从正电荷出发、终止于负电荷。箭头的方向在正电荷附近是向外辐射的在负电荷附近是指向电荷的整张图呈现出从正到负的“流线”感。我把quiver箭头的颜色设成暗红色和黑色的等势线区分开图面层次更分明。如果你喜欢其他配色也可以根据实际需要调整RGB值。3.5 电荷位置标记图上最好把正负电荷的位置标出来直观对应plot(pos_charge(1), pos_charge(2), r, MarkerSize, 12, LineWidth, 2); plot(neg_charge(1), neg_charge(2), bo, MarkerSize, 12, LineWidth, 2);这里用红色加号表示正电荷蓝色圆圈表示负电荷。如果你的图上已经有quiver的红色箭头了可以给正电荷换一个颜色比如用绿色五角星避免同色混淆。根据自己的审美调整就行。4. 常见问题与调试技巧实录4.1 等势线图“糊成一团”怎么办这是出现频率最高的问题。原因几乎都是V的动态范围太大电荷附近电位极高远处的电位在颜色映射中被压到同一个色阶。解决办法就是我在3.1节说的非线性映射V_scaled 2 ./ (1 exp(-V / 50)) - 1。这个公式处理之后的等势线在近处和远处都能清晰区分。如果你觉得 sigmoid 映射还是不够好也可以试另一个思路只在某个有限的电位范围内画等势线。比如V_min -100; V_max 100; levels linspace(V_min, V_max, 20); contour(X, Y, V, levels);这种方式会把超出范围的区域留白等于只截取中间电位区域来观察。对于想看近场细节的场景这个方案更直接。4.2 quiver箭头方向“乱跳”是什么原因箭头方向看起来乱多数情况下是因为箭头采样过密或者缩放因子设置不当。skip参数取4、5、6都可以尝试要保证箭头之间有足够间距。如果箭头还是看起来乱可能是纯数值假象在E_mag极小的地方比如电位鞍点附近归一化之后的Ex_norm、Ey_norm方向会发生突变因为真实的电场幅值趋近于零方向本身就不稳定再除以很小的E_mag就会放大误差。处理方法是给E_mag设一个下限低于下限的点不画箭头valid E_mag 1e-3; % 阈值根据实际数据调整 Ex_norm(~valid) NaN; Ey_norm(~valid) NaN;把无效点设为NaNquiver就不会在这些位置画箭头了。这个技巧在等势线密集的区域特别有用能让图面干净很多。4.3 电位数值太大或太小colorbar显示不正常如果电位量级是几千伏或者微伏前面说的V/50这个缩放因子就需要调整。你可以先跑一行代码查看范围fprintf(V min %.3e, V max %.3e\n, min(V(:)), max(V(:)));然后根据量级设置缩放因子。我一般是让缩放因子等于V_max的十分之一左右然后再微调。这个值的大小直接影响等势线的疏密分布值得花一点时间调到视觉效果最佳。4.4 跑得慢怎么办如果N取得很大比如500以上计算量会明显增加。其实没必要优化代码最简单的方法就是先把N设小一点150左右快速调试确认代码没问题、图形符合预期后再调大N出最终图。开发流程上这叫“先跑通再跑好”不要一开始就用高精度参数。如果需要经常调整参数你可以把代码封装成函数function plot_dipole(q, d, N, x_range, y_range) % 画电偶极子的等势线、电场矢量、三维曲面 ... end后续只要改参数调用函数就行不用每次都对着脚本改来改去。4.5 单位制混乱导致结果不对这个问题一般不报错但对错完全看不出来。我见过有人把电荷间距写成厘米电荷量写成微库仑导致最后画的电场分布比例失衡。建议一开始就用国际单位制电荷用库仑C长度用米m介电常数用真空介电常数8.854187817e-12 F/m。全都统一不要混用。如果你做的是理论示意而非真实物理量计算也可以把常数简化。比如令1/(4πε₀) 1只看场分布的形状。这样数值上更清爽画出来的图形形状完全一样。但简化会失去物理量级的参考用于课程展示时最好保留真实值方便算具体电压。5. 深度扩展从静态图到真正可用的研究工具5.1 用streamline画真正的电场线quiver展示的是离散的矢量场信息而电场线是连续的曲线。MATLAB的streamline函数可以根据矢量场数据追踪流线效果类似于手绘的电场线% 设置起始点围绕正电荷一圈 theta linspace(0, 2*pi, 16); start_x pos_charge(1) 0.02 * cos(theta); start_y 0.02 * sin(theta); figure(Color, w); hold on; streamline(X, Y, Ex, Ey, start_x, start_y); contour(X, Y, V_scaled, 20, LineWidth, 0.5); hold off;这里的start_x和start_y是流线的起点坐标我取的是以正电荷为中心、半径0.02米的圆周上的16个点。streamline会从这些点出发沿着矢量场的方向追踪电场线。由于电场线从正电荷出发终止于负电荷所以这些流线最终都会收束到负电荷附近。这个效果比quiver更有“物理味道”推荐你要做演示时用。5.2 电偶极子的动态旋转演示静态图看多了可以把偶极子旋转起来观察空间场分布随方向的变化。只需要在函数外加一层角度循环把电荷坐标用旋转矩阵更新一下for angle 0:5:180 theta_rad deg2rad(angle); rot [cos(theta_rad), -sin(theta_rad); sin(theta_rad), cos(theta_rad)]; pos_charge rot * [d/2; 0]; neg_charge rot * [-d/2; 0]; % 重算电位和电场重新绘图 % ... drawnow; end这样就能生成一个动态旋转的偶极子场分布。如果保存成gif还可以直接放到PPT里当课件动画。5.3 偶极子阵列的应用实际工程问题里单个偶极子很少更多是偶极子阵列。比如天线阵列中多个偶极子叠加形成特定的辐射方向图。MATLAB里实现阵列叠加热别简单只要把每个偶极子的电位相加就行function V_total dipole_array_potential(X, Y, charges) % charges 是 Nx3 矩阵每行表示一个偶极子的 [x, y, theta] % 其中 x, y 是偶极子中心位置theta 是偶极子方向角 V_total zeros(size(X)); for i 1:size(charges, 1) cx charges(i, 1); cy charges(i, 2); theta charges(i, 3); % 计算局部坐标 X_local (X - cx) * cos(theta) (Y - cy) * sin(theta); Y_local -(X - cx) * sin(theta) (Y - cy) * cos(theta); % 叠加该偶极子的电位 V_total V_total single_dipole_potential(X_local, Y_local); end end这种叠加逻辑对理解天线阵列的工作原理很有帮助。5.4 加上介质材料后的变化如果在偶极子附近放置一块介质材料电位分布会改变原因是介质在电场作用下发生极化在表面产生束缚电荷。严格计算需要求解拉普拉斯方程并考虑不同区域的介电常数差异。MATLAB可以用PDE工具箱处理这类问题但代码复杂度明显上升不是一两个脚本能解决的。如果你只是想在课上展示介质的影响可以用近似模拟方式在介质区域人为添加一些等效的极化电荷观察等势线因极化电荷扭曲的效果。这个方案物理上不是特别精确但视觉上很有说服力。做科研时还是老老实实上专业工具吧。6. 好习惯与收尾经验最后分享几个我在写这类仿真代码时的习惯能省下不少调试时间。第一个习惯是尽量变量化所有参数。q、d、N、范围、缩放因子全部定义在文件最前面不要算到一半发现想改电荷间距还得去代码中间找。这看起来像废话但实际操作中很多人偷懒写着写着又回到硬编码的老路上。第二个习惯是保留V和E的原始数据不要只用画图处理之后的数据。有些时候你需要回到原始数据做定量分析比如计算某个点的精确电场值或者沿着某条线提取电位分布。如果原始数据被覆盖了就得重跑一遍。第三个习惯是注释要写“为什么”不要写“是什么”。比如r_plus(r_plus 1e-6) 1e-6旁边注释应该写“防止网格点落在电荷上导致除零”而不是写“赋值1e-6”。做代码重构或者回头看旧代码的时候这类注释能救你命。第四个习惯是把成品代码保存成函数加上默认参数。我的做法是写一个demo_dipole()函数内部调用plot_dipole(q, d, N, range)这样既能一键运行看效果又能灵活改参数做实验。这些习惯一开始坚持起来会觉得麻烦但坚持一个学期之后你会发现自己写代码的速度和调试效率都上来了。回到电偶极子本身真正把这个模型跑通、画清楚之后我对电磁场的理解确实上了一个台阶。很多东西光靠公式推导确实抽象但一旦把等势线和电场矢量画在屏幕上脑子里就建立起图像了后面处理更复杂的电磁问题也会更有底气。希望这套代码和思路对你也有帮助。如果你跑的过程中遇到报错或者图形不符合预期欢迎评论交流我看见了会回复。