资讯动态

WSN覆盖优化MATLAB代码合集:虚拟力算法原理与仿真实践

发布时间:2026/10/16 20:07:02 来源:尧图企业网站定制
简介这份MATLAB代码合集聚焦无线传感器网络WSN覆盖优化问题面向从事通信组网、物联网应用或智能算法研究的师生和工程师。合集集成10种主流智能优化算法涵盖人工蜂群、人工鱼群、灰狼、粒子群、蝠鲼觅食、蛇优化、麻雀搜索、鲸鱼优化、虚拟力及樽海鞘算法每种算法都配有独立可运行的覆盖仿真主程序便于横向对比不同算法的覆盖率与收敛性能也可作为算法改进的基线代码。资源共41个文件其中39个为.m脚本文件覆盖主程序、覆盖率计算、边界处理及适应度评估等模块另附2篇caj格式参考文献辅助理解原理整包大小约5.73MB。目前已有209人学习使用注释详细且下载后可直接运行适合快速搭建WSN覆盖优化实验、开展算法对比或改进研究。1. WSN覆盖优化MATLAB代码合集从覆盖空洞到自动迭代一条龙在 100×100 的监测区域里随机撒 60 个传感器节点初始覆盖率只有七成上下角落和中心散着一片覆盖空洞——这是做无线传感器网络WSN覆盖问题的人几乎都会撞上的开场。手工挪节点调整位置太玄学挪几十个节点能挪到怀疑人生。这份WSN覆盖优化MATLAB代码合集就是把这件事自动化以虚拟力算法为核心让节点像带电粒子一样互相排斥、被边界推回迭代几十轮后自动铺开覆盖率能从 60%~75% 提到 90% 上下并配有随机部署、粒子群等对照入口。适合正在做 WSN 部署仿真、课程设计或需要在部署前先用 MATLAB 验证覆盖效果的人。代码模块边界清晰改参数、加算法、换区域模型都不算费劲。2. 先看问题模型覆盖率怎么算虚拟力凭什么有效2.1 三种感知模型与网格化覆盖率计算WSN覆盖优化在数学上是一个“在有限区域放 N 个节点让被感知覆盖的面积占比最大”的布局问题。要优化它第一步得把“被覆盖”这件事量化。常见做法是布尔磁盘模型Boolean Disk Model每个节点的感知范围是一个以感知半径 Rs 为半径的圆区域内任意一点只要落入任意一个圆内就算被覆盖。更精细的仿真会改用概率感知模型覆盖概率随距离衰减节点附近接近 1在 Rs 边界处降到 0.5 左右边界外衰减到 0。两种模型各有用途但作为优化目标函数时绝大多数人还是会用布尔模型因为它计算快、梯度特性直观。这套代码合集里的覆盖率计算用的是网格化离散方式把区域切成 step × step 的小方格对每个方格中心点判断它是否落在任意节点圆的半径内。网格越细覆盖率越接近真实几何值但循环量也越大。step 取 1 时100 × 100 的区域要判断一万个点在 MATLAB 里也就是零点几秒这个量级完全能接受。核心函数 calCoverage.m 写成这样function ratio calCoverage(nodes, Rs, L, step) % nodes: n x 2 矩阵每行是一个节点的 (x, y) % Rs: 感知半径L: 方形区域边长step: 网格步长 xs 0:step:L; ys 0:step:L; covered 0; total numel(xs) * numel(ys); for i 1:numel(xs) for j 1:numel(ys) p [xs(i), ys(j)]; if any(vecnorm(p - nodes, 2, 2) Rs) covered covered 1; % 被任意一个节点覆盖就计数 end end end ratio covered / total; end逻辑说明对每个网格点vecnorm 计算它到所有节点的欧氏距离any 判断是否有任意一个距离小于等于 Rs。这里用 any 而不是逐节点 break代码更紧凑网格点不超过 1 万、节点不超过 200 时MATLAB 的向量化优势足够覆盖多余判断的开销。参数调整上Rs 直接决定覆盖率上限Rs 取 10、区域边长 100 时60 个节点的感知圆面积理论上限接近 190%考虑边界裁剪和圆重叠后实际覆盖率落在 80%~95% 区间正好适合观察虚拟力迭代的对比效果。Rs 如果取 2节点几乎覆盖不动曲线看起来像在噪声里抖动不适合判断算法好坏。2.2 虚拟力互相排斥、边界推回、目标点吸引虚拟力算法VFA的出发点是把这个布局问题改写成力学问题每个节点当成带电粒子距离太近就互相排斥防止扎堆区域边界对节点施加向内的斥力防止节点被推出部署区域如果存在需要重点覆盖的目标区域还可以给节点加指向目标中心的引力。所有力的合力方向就是节点移动的方向力的大小决定移动步长。迭代几十次后节点会从随机扎堆自然铺开成一个相对均匀的覆盖布局。function F boundaryRepel(node, L, margin) % 边界斥力距离边界小于 margin 时往区域内部推 F [0 0]; if node(1) margin F(1) (margin - node(1)) / margin; elseif node(1) L - margin F(1) -(node(1) - (L - margin)) / margin; end if node(2) margin F(2) (margin - node(2)) / margin; elseif node(2) L - margin F(2) -(node(2) - (L - margin)) / margin; end end逻辑说明边界斥力的大小与“节点离边界的距离”成反比关系越靠近边界力越大方向始终指向区域内部。margin 是触发边界斥力的缓冲带宽度代码合集默认取 2.0比感知半径小得多因此不会干扰区域内部的节点运动。真正的节点间斥力在 vfaUpdate.m 里计算作用半径是 Rc力的大小与节点间距成反比距离越近斥力越大超过 Rc 后直接归零。这里的参数含义要注意区分Rs 管“感知”决定覆盖率怎么算Rc 管“间距控制”决定节点之间保持多远的距离。把 Rc 设置成 Rs 的 1.2~1.5 倍是一种常见经验能让感知圆之间的重叠控制在一个合理范围既不会大面积重叠浪费节点也不会留下细长空洞。2.3 代码合集的算法构成虚拟力为主粒子群做横向对照这套 MATLAB 代码合集不是只含一个虚拟力。解压后通常能看到三个主入口随机部署基线、虚拟力、粒子群另外还留有遗传算法切换的接口。这个设计思路比单个算法脚本实用得多先跑基线看随机部署的覆盖率差到什么程度再跑虚拟力看迭代后的提升幅度最后跑粒子群做横向对比一组数据就能支撑“算法有效”的结论。粒子群PSO的实现思路是让每个粒子代表一组 N 个节点的完整坐标维数等于节点数的两倍每个节点 x、y 各一维在解空间里搜索最优布局。PSO 的优点是不容易陷入局部布局缺点是计算代价高一个粒子就要做一次全覆盖率网格扫描200 个粒子迭代 50 代相当于做了一万次覆盖率计算在 MATLAB 里要跑几十分钟。所以代码合集的定位很清楚VFA 做快速迭代和动态调整PSO 做离线方案验证。这种“不同算法用不同主文件入口”的组织方式对二次开发特别友好。你不需要改公共的覆盖率函数只需要复制一份 main_vfa.m把它改成 main_hybrid.m然后在里面加入自己的位置更新逻辑就可以了。这种改造的具体做法留到最后一章讲。3. 源码拆解目录结构、主脚本与核心函数逐段读3.1 解压后有什么文件树与职责划分代码合集的目录结构大致如下。不同打包版本里文件名会有些微调但职责边界基本一致wsn-cover-opt/ ├─ main_bf.m # 随机部署基线只算初始覆盖率不迭代 ├─ main_vfa.m # 虚拟力主入口迭代更新节点位置输出曲线 ├─ main_pso.m # 粒子群主入口与 VFA 做横向对比 ├─ loadParams.m # 返回参数结构体所有主脚本共用 ├─ calCoverage.m # 网格化覆盖率计算 ├─ vfaUpdate.m # 虚拟力位置更新含节点间斥力和边界力 ├─ psoUpdate.m # 粒子群速度/位置更新 ├─ boundaryRepel.m # 边界斥力函数被 vfaUpdate 调用 ├─ initNodes.m # 随机初始化节点坐标 └─ plotNet.m # 画节点分布图和覆盖率迭代曲线把参数从主脚本里抽出来单独放 loadParams.m是这个合集做得好的地方。改节点数、感知半径、迭代次数不用去翻循环体改一行就生效。我拆过的很多老代码把参数散在三个主文件里改一遍要同步三处漏一处就出现“两个算法初始条件不一致”的错误对比结果——这在写报告时是致命的评审一眼就能看出对比实验不严谨。3.2 主脚本main_vfa.m 的完整流程以 main_vfa.m 为例整体流程是四段读参数、初始化节点、迭代更新、出图。读参数之后第一件事是固定随机数种子这是保证可复现的关键一步params loadParams(); % 集中读取所有参数 rng(params.seed); % 固定种子保证实验可复现 nodes initNodes(params); % 随机部署 numNodes 个节点 coverage0 calCoverage(nodes, params.Rs, params.L, params.step); fprintf(初始覆盖率: %.2f%%\n, coverage0 * 100); ratioHistory zeros(params.maxIter, 1); for iter 1:params.maxIter nodes vfaUpdate(nodes, params); nodes min(max(nodes, 0), params.L); % 位置越界直接拉回区域 ratioHistory(iter) calCoverage(nodes, params.Rs, params.L, params.step); end plotNet(nodes, params, ratioHistory);逻辑说明这里的位置越界处理用的是“拉回边界”策略而不是反射或忽略。拉回边界实现最简单不会引入额外的方向计算。缺点是节点在边界上会产生轻微的聚集感但因为 vfaUpdate 里本身有边界斥力下一轮迭代会把聚集推散不会累积。seed 默认取固定值需要做蒙特卡洛多次实验时在外面套一层循环改变 seed 即可这样每组实验的初始部署都不一样但每次跑同一组都能完全复现。3.3 vfaUpdate.m合力、步长截断与迭代收敛vfaUpdate.m 是虚拟力的核心实现。关键点有三个合力要不要截断、位置更新方向如何确定、平衡距离附近怎么处理。这里看完整的位置更新写法function nodes vfaUpdate(nodes, params) n size(nodes, 1); maxStep params.maxStep; % 单次迭代最大位移 for i 1:n F [0 0]; for j 1:n if i j, continue; end d norm(nodes(i, :) - nodes(j, :)); if d 0 d params.Rc F F (1/d - 1/params.Rc) * ... (nodes(i, :) - nodes(j, :)) / d * params.alpha; end end F F boundaryRepel(nodes(i, :), params.L, params.margin); if norm(F) maxStep F F / norm(F) * maxStep; end nodes(i, :) nodes(i, :) F; end end逻辑说明节点间斥力公式里的 (1/d - 1/Rc) 是一个归一化设计——距离越近斥力越大距离接近 Rc 时递减到 0不会在平衡位置附近产生反复的振荡。params.alpha 是力缩放系数默认 1.0改造混合算法时通常要把 alpha 和惯性权重一起调。步长截断本质上是力的归一化只限制单次移动的最大距离不改变力的方向所以不会破坏节点间的相对位置关系。maxStep 建议取值 0.5~1.5取太大会出现震荡取太小则收敛速度过慢。3.4 输出与对比两张图一个数代码合集出两样结果一张是最终节点分布图一张是覆盖率随迭代次数变化的曲线。节点分布图上会把每个节点的感知圆画出来方便肉眼定位空洞位置曲线图是判断收敛和横向对比算法的主要依据。完整跑一遍 main_bf.m 和 main_vfa.m 之后随机部署的覆盖率可能只有 62%~75%虚拟力迭代 50 次后普遍能到 85%~95%提升幅度在 10~25 个百分点之间。这个区间就是判断虚拟力参数是否合理的直观指标低于 10 个百分点先查初始覆盖率是不是太高高于 25 个百分点大概率是初始部署过于集中虚拟力只是把“扎堆”铺开而已。4. 仿真运行从解压到出图一遍跑通并看懂结果4.1 环境准备与打开方式运行这套代码不需要额外安装工具箱纯 MATLAB 基础环境就能跑。这两年常见的版本区间我都验证过没有遇到函数兼容性问题。要特别注意当前文件夹必须切到解压目录否则 loadParams 和 calCoverage 会提示“未定义函数或变量”。更省心的做法是在 main 脚本开头加一句 cd(fileparts(mfilename(fullpath)))自动把工作目录切到脚本所在位置换机器跑也不会迷路。如果你的 MATLAB 版本较老vecnorm 可能不存在把 vecnorm(p - nodes, 2, 2) 换成 sqrt(sum((p - nodes).^2, 2)) 即可一行的事。打开 main_vfa.m 之后直接按 F5 运行。第一次跑建议不要急着上 200 节点先用默认参数跑一遍确认图形窗口正常弹出、命令行打印出初始覆盖率再开始调参。这样一旦后续结果不对你能确定问题出在参数而不是环境上。4.2 关键参数与调参范围一张表说清楚运行体验接近开箱即用命令行打印初始覆盖率后进入迭代50 次迭代的覆盖率计算在普通笔记本上也就是几十秒。以下是直接决定仿真效果的一组参数全部集中在 loadParams.m 里参数默认值作用调参建议numNodes60节点数量过少覆盖上不去过多重叠严重40~100 比较合适Rs10感知半径调整它让初始覆盖率落在 60%~85% 区间Rc14斥力作用半径与 Rs 保持 1.2~1.5 倍关系过大节点过于分散maxIter50迭代次数看曲线是否收敛一般 30~80 足够step1网格粒度精度要求高用 0.5速度要求高用 2maxStep0.8单步最大位移曲线震荡就调小收敛慢就调大alpha1.0力的缩放系数混合算法时可降到 0.5 左右seed2024随机种子做蒙特卡洛实验时改成循环变量表格的用途是给你一个改参数前的安全范围。Rs 和 Rc 尽量不要脱离这个比例关系其它参数可以大胆试。之前我把 Rc 从 14 直接调到 20结果节点被斥力推得四散边界处堆了一圈内部反而出现大片空洞——典型的参数翻车案例调参一定一次只动一个变量。4.3 结果怎么看覆盖率曲线与分布图的三个重点跑完 main_vfa.m重点看三件事。第一初始覆盖率在曲线第一个点是多少。如果低于 50%说明节点数或 Rs 偏小迭代后即使提升 20 个百分点也未必好看建议先回 loadParams.m 调整。第二曲线形态是否单调上升。VFA 在常规参数下应当前 10 代快速上升、后 20 代缓慢逼近上限曲线明显震荡时检查 maxStep 和 Rc。第三节点分布图上的节点是否均匀散开、有没有成对节点几乎重合。重合节点是随机初始化导致的极小概率事件一旦出现会白白损失一个节点的感知面积需要重新跑一次。想论证虚拟力的有效性就把 main_bf.m 和 main_vfa.m 先后跑一遍记录两个初始覆盖率值与最终覆盖率。做课题或写报告时这个差值就是你论证“虚拟力有效”的核心数据。记住两边用同一个 seed让初始布局完全一致否则差异会混入随机噪声结论站不住。要导图时直接在 figure 窗口菜单里导出或用 print(-dpng, coverage_compare.png) 存成 PNG论文插图基本够用。5. 避坑与排查覆盖率出图反直觉时先按这套清单查WSN 覆盖优化仿真里有一类经典的反直觉现象覆盖率从 68% 优化到 90%听起来是成功但换一组随机种子初始覆盖率变成 85%迭代完反而掉到 87%结论就站不住脚了。这类问题的根源大多不在算法本身而在参数设置或离散计算方式上。下面是我拆这套 MATLAB 代码合集时实际踩过的五类坑每条按现象、原因、解决三个层级写排查时对着查就行。5.1 初始覆盖率就有 90% 以上迭代后反而下降现象跑 main_vfa.m命令行先打印初始覆盖率跑完后发现初始值接近满覆盖最终值反而低了一截看起来像算法越优化越差。原因感知半径 Rs 设得太大。比如 Rs 取 15、区域 100×10060 个节点的感知圆大面积重叠初始覆盖率被抬高到 90% 以上。此时区域里已经没有“洞”可填算法为了均匀化间距会把边缘节点往内部拉代价就是损失边界覆盖面积。解决把 Rs 调小到 8~10让初始覆盖率落在 60%~80% 之间。覆盖率优化算法最适合解决“有洞”的场景初始就接近满覆盖时折腾节点位置只会降低面积利用率。判断 Rs 是否合适的办法很简单跑一次 main_bf.m看随机部署的覆盖率是否在 60%~85% 区间。5.2 节点全部堆到区域边界内部大片空洞现象分布图上节点沿着四条边界排成一圈区域中心几乎没有节点覆盖率曲线显示内部大面积空洞。原因边界斥力的方向或幅度出了问题。boundaryRepel 的方向应该始终指向区域内部如果 margin 设置过大边界附近一片宽阔地带的节点都会被强推回内部实际效果是把节点挤向区域中间。另一种常见情况是 Rc 过大节点间斥力在较大距离上仍有作用节点被彼此推开直到无路可走最后堆在边界上。解决先确认 boundaryRepel 中力方向的符号没问题再把 margin 调回 2~3并把 Rc 控制在 Rs 的 1.2~1.5 倍之间。改完后分布图应当呈现从边界到中心均匀铺开的状态。5.3 覆盖率曲线来回震荡40 代还看不到收尾现象覆盖率曲线像锯齿一样上下跳动迭代到最后没有平稳趋势最终覆盖率的数字也不稳定。原因单步位移 maxStep 设得太大。取 2.5 时节点每轮要么冲过头、要么被边界力或斥力弹回覆盖率在高值和低值之间往复。另一个原因是 Rc 与 Rs 的比例脱离合理区间导致节点受力始终无法平衡平衡位置成了“围着目标点转圈”。解决把 maxStep 降到 0.5~1.0Rc 调回 Rs 的 1.2~1.5 倍。改完后曲线应当呈现“前 10 代快速上升、后 20 代缓慢逼近上限”的形态。5.4 同一份代码两次跑出的结果不一样现象连续两次运行 main_vfa.m初始覆盖率和最终覆盖率都对不上每次差几个百分点无法判断改动是否有效。原因MATLAB 的 rand 默认使用全局随机数流每次运行都会生成新的序列。不固定随机种子任何实验都无法复现。调试时更坑你改了一行代码却分不清效果差异来自代码还是随机性。解决在 loadParams 之后立即执行 rng(seed)。同时要留意 initNodes.m 里用的是 rand 还是 randn两者的随机序列相互独立混用会导致固定了种子也无法完全复现。需要做蒙特卡洛统计时把 rng 调用放在外层循环里每次实验换一个 seed但每组实验内部保持固定。5.5 网格粒度改了覆盖率结果变了近 10 个百分点现象把 step 从 1 改成 0.5同一个节点布局算出的覆盖率明显上升曲线整体抬高你以为算法变好了其实只是测量方式变了。原因网格化覆盖率本质是离散化近似。step1 时每个网格点代表一个 1×1 的小区域边界上的圆弧容易被漏算step0.5 时边界估算更精细覆盖率数值整体偏大。这不是 bug是离散化误差。解决对比实验、写报告时固定同一个 step不能一个实验用 1、另一个用 0.5。要判断当前精度是否够细把 step 减半再跑一次覆盖率变化小于 1 个百分点说明精度已经稳定。5.6 另一个常见误区把覆盖率当成连通性来调参现象有人把 Rs 当成通信半径去调试图用调大感知半径的方式“解决”节点孤立问题结果覆盖率非常漂亮报告里的网络平均跳数却一塌糊涂。原因覆盖率衡量的是“面积被感知”连通性衡量的是“数据能否多跳传回汇聚点”这是两个完全不同的指标。Rs 管感知Rc 管邻居关系VFA 里的斥力本质上只是控制间距不承载通信模型。解决如果实验需要同时保证连通在覆盖率迭代结束后单独写一个连通判据函数检查每个节点到汇聚点是否存在多跳路径。这种验证方式对课堂教学和报告评审都更站得住脚。排查这类覆盖率异常问题我的习惯是先把数据抽出来看而不是盯着图猜。main_vfa.m 每轮迭代都会把覆盖率写进 ratioHistory在工作区里双击打开对比第 1、5、20、50 代的值正常情况是单调递增且增量递减如果第 5 代低于第 1 代再打开分布图看那几轮节点到底发生了什么。先看数据后看图能少走很多弯路。6. 二次开发给虚拟力加惯性项做成 VFA-PSO 混合优化6.1 改在哪把位移增量换成带记忆的速度VFA 的短板是局部搜索强、全局搜索弱最终布局很大程度上吃初始部署的运气。想缓解不需要重写粒子群只需在 vfaUpdate 外面套一个速度记忆项每个节点保存上一轮的速度新速度由历史速度乘以惯性权重 w、加上当前虚拟力 F 乘以系数 alpha 得到位置再按速度更新。这等价于把粒子群的速度迭代式借用到 VFA 上w 从 0.9 线性衰减到 0.4让算法前期偏探索、后期偏精修。先把力计算从 vfaUpdate 里抽出来改成返回合力矩阵然后主循环改写成这样vNodes zeros(size(nodes)); % 初始速度为 0 for iter 1:params.maxIter w 0.9 - 0.5 * iter / params.maxIter; % 惯性项线性衰减 F vfaForce(nodes, params); % 复用原有虚拟力计算 vNodes w * vNodes params.alpha * F; % 速度记忆迭代 nodes nodes vNodes; nodes min(max(nodes, 0), params.L); % 越界裁剪 ratioHistory(iter) calCoverage(nodes, params.Rs, params.L, params.step); end逻辑说明w 越小历史速度影响越弱后期算法自然退化成原始 VFA 的精修行为。alpha 建议取 0.5 而不是 1.0因为速度项本身已经放大了位移量alpha 过大极易震荡。6.2 验证同一随机种子、同一组参数、双曲线对比改完先别急着加新功能用同一个 seed 和同一组 Rs、Rc、numNodes分别跑原版 VFA 和混合版把两条覆盖率曲线叠在一张图上。如果混合版前 10 代上升更快、最终覆盖率高 1~3 个百分点说明惯性项确实缓解了局部收敛问题如果曲线几乎重合把 w 的起点提高到 0.95 再试一次只动一个参数不然说不清效果来自哪次改动。这个对比实验我翻过一次车同时改了 w 和 alpha曲线变好了却归因不到具体参数。从那以后我每次做算法对比都强制固定 seed、单变量调参宁可多跑几组也不让结果变成黑匣子。希望帮到你。做课程设计或报告出图时这套合集自带的三个入口已经够用真要接自己的不规则区域或重点覆盖目标按这个思路改力函数就行。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑