☰
无线传感器网络覆盖优化:MATLAB虚拟力算法与PSO/GA代码实战
2026/10/20 11:46:25 网站建设 项目流程

简介:这份MATLAB代码合集面向无线传感器网络(WSN)覆盖优化方向的学习者与研究者,适合具备一定MATLAB基础、希望快速复现并对比多种智能优化算法效果的高年级本科生、研究生及科研人员。资源围绕WSN节点部署与覆盖增强问题,整合了人工蜂群、人工鱼群、灰狼、粒子群、蝠鲼觅食、蛇优化、麻雀搜索、鲸鱼优化、虚拟力以及樽海鞘共10种常见算法的求解实现,可满足算法对比实验与课程设计需求。压缩包共41个文件,以39个m脚本为主,辅以2个caj文献,整体约5.73MB,脚本按算法分目录组织,注释较为详细,下载后可直接运行。目前已有209人学习。读者可借此获得一套完整的WSN覆盖优化实验框架,涵盖适应度计算、覆盖度评估与绘图等模块,便于理解各算法的寻优流程与参数设置,也能为论文复现、算法改进和性能对比提供可参考的代码基础与排错思路。

1. 无线传感器网络覆盖优化:这套 MATLAB 代码合集到底能解决什么问题

如果你做过无线传感器网络(WSN)的课题或者工程项目,大概率遇到过这样的场景:一片监测区域里随机撒下几十上百个传感器节点,初始覆盖率高高低低,有的地方重叠冗余,有的地方出现感知空洞,导师或者甲方要求你把覆盖率从 70% 提到 90% 以上,还得画出收敛曲线和覆盖效果图。这时候纯靠手调参数基本是玄学,你需要的是能跑的优化算法代码。

这套 WSN 覆盖优化 MATLAB 代码合集,核心就是围绕传感器节点的部署与覆盖问题,提供多种优化算法的可运行实现,包括虚拟力算法、粒子群优化、遗传算法等常见方案。它适合正在做 WSN 相关课题的研究生、需要快速验证覆盖优化思路的工程师,以及想拿一套能跑的基线代码做二次开发的开发者。你拿到手就能在 MATLAB 里跑出覆盖分布图和迭代收敛曲线,不用从零推导公式再一行行敲。

2. 技术选型与整体架构:为什么是 MATLAB 加虚拟力算法

2.1 为什么 WSN 覆盖优化偏爱 MATLAB

WSN 覆盖优化本质是一个带约束的连续域或离散域优化问题。你需要定义感知模型、计算覆盖率、迭代更新节点位置、可视化结果。MATLAB 在这类任务上有天然优势:矩阵运算快、绘图函数现成、优化工具箱成熟,而且大部分 WSN 领域的经典论文都附带 MATLAB 代码,方便对照复现。

这套代码合集选择 MATLAB 作为唯一运行环境,意味着你不需要配置 Python 环境、不需要装深度学习框架,只要有 MATLAB R2016b 及以上版本就能跑。常见做法是把每个算法写成一个独立的.m脚本或函数文件,主脚本负责参数初始化和结果展示,算法文件负责迭代逻辑,这样结构清晰,改起来也方便。

2.2 虚拟力算法的核心思路

虚拟力算法(Virtual Force Algorithm, VFA)是这套合集里的重点。它的直觉很朴素:把每个传感器节点看作一个带电粒子,节点之间根据距离产生引力和斥力,节点与边界之间也有约束力,合力驱动节点移动,最终达到一个覆盖更均匀的布局。

具体来说,当两个节点距离小于感知半径时,它们之间产生斥力,避免过度重叠;当距离大于通信半径时,产生引力,避免网络断裂;节点靠近边界时,边界给它一个斥力,把它推回监测区域。每次迭代计算每个节点受到的合力,然后按步长移动,直到合力小于阈值或达到最大迭代次数。

这套代码里,虚拟力算法的实现通常包含以下几个关键参数:

参数名含义典型取值
R_s传感器感知半径10~30 m
R_c通信半径2 × R_s
d_th斥力/引力切换距离阈值0.5 × R_s
eta引力系数0.01~0.1
rho斥力系数0.1~1
step节点移动步长0.1~0.5 m
max_iter最大迭代次数100~500

这些参数不是拍脑袋定的,感知半径和通信半径通常由硬件决定,斥力系数和步长需要根据区域大小和节点数量调。我一般会先把步长设小一点,比如 0.2,跑 200 次迭代看收敛情况,如果收敛太慢再适当加大。

2.3 代码合集的目录组织

虽然压缩包里的具体文件结构可能因版本而异,但这类 WSN 覆盖优化代码合集通常按算法类型分目录,常见结构如下:

WSN_Coverage_Optimization/ ├── VFA/ │ ├── main_vfa.m % 虚拟力算法主脚本 │ ├── compute_force.m % 合力计算函数 │ ├── update_position.m % 位置更新函数 │ └── plot_coverage.m % 覆盖效果绘图 ├── PSO/ │ ├── main_pso.m % 粒子群优化主脚本 │ ├── fitness_function.m % 覆盖率适应度函数 │ └── pso_update.m % 速度和位置更新 ├── GA/ │ ├── main_ga.m % 遗传算法主脚本 │ ├── crossover.m % 交叉操作 │ └── mutation.m % 变异操作 ├── utils/ │ ├── init_nodes.m % 随机初始化节点位置 │ ├── calc_coverage.m % 计算覆盖率 │ └── draw_sensor_field.m % 绘制监测区域和节点 └── README.md % 运行说明

这种分法好处是每个算法独立,你可以只跑 VFA,也可以对比 PSO 和 GA 的效果。utils目录放公共函数,避免重复代码。如果你要加自己的算法,照着现有目录新建一个文件夹,把主脚本和核心函数放进去就行。

3. 跑起来:从解压到出图的完整步骤

3.1 环境准备与文件检查

先把压缩包解压到一个不含中文和空格的路径下,比如D:\WSN_Coverage。MATLAB 对中文路径的支持时好时坏,尤其是老版本,路径里有中文可能导致load或save报错。解压后检查一下有没有.m文件缺失,有些压缩包在传输过程中会丢文件,尤其是utils目录下的公共函数。

打开 MATLAB,把当前工作目录切换到解压后的根目录。你可以用命令行:

% 切换到项目根目录 cd('D:\WSN_Coverage'); % 把所有子目录加入搜索路径 addpath(genpath(pwd)); % 检查路径是否添加成功 path;

addpath(genpath(pwd))这行很关键,它会把所有子文件夹都加入 MATLAB 的搜索路径,这样你在主脚本里调用utils下的函数时不会报 “未定义函数”。如果你只addpath(pwd),子目录里的函数就找不到。

3.2 运行虚拟力算法主脚本

以 VFA 为例,打开VFA/main_vfa.m,你会看到类似下面的代码结构:

%% 虚拟力算法主脚本 clear; clc; close all; % 监测区域参数 L = 100; % 区域边长,单位:米 N = 30; % 传感器节点数量 Rs = 15; % 感知半径 Rc = 30; % 通信半径 max_iter = 200; % 最大迭代次数 step = 0.3; % 移动步长 % 初始化节点位置(随机均匀分布) nodes = init_nodes(N, L); % 记录初始覆盖率 coverage_initial = calc_coverage(nodes, L, Rs); fprintf('初始覆盖率:%.2f%%\n', coverage_initial * 100); % 迭代优化 coverage_history = zeros(max_iter, 1); for iter = 1:max_iter % 计算每个节点受到的合力 force = compute_force(nodes, L, Rs, Rc); % 更新节点位置 nodes = update_position(nodes, force, step, L); % 计算当前覆盖率 coverage_history(iter) = calc_coverage(nodes, L, Rs); end % 绘制结果 figure; plot(coverage_history, 'b-', 'LineWidth', 1.5); xlabel('迭代次数'); ylabel('覆盖率'); title('虚拟力算法覆盖率收敛曲线'); grid on; figure; draw_sensor_field(nodes, L, Rs); title('优化后的传感器节点分布');

这段代码的逻辑很直白:先随机撒点,算初始覆盖率,然后进入迭代循环,每次算合力、移动节点、记录覆盖率,最后画收敛曲线和节点分布图。你直接点运行,正常情况下几秒钟就能出图。

3.3 关键参数怎么调

跑通之后,你大概率想调参看效果。几个核心参数的影响如下:

N是节点数量,增加 N 覆盖率肯定上升,但边际效益递减,而且节点多了计算量变大。我一般从 30 开始试,如果覆盖率不够再加到 50。

Rs是感知半径,这个参数对覆盖率影响最大。Rs 翻倍,单节点覆盖面积变成四倍,覆盖率会显著提升。但实际项目中 Rs 由硬件决定,不能随便改,调参时要注意别脱离实际。

step是移动步长,太小收敛慢,太大容易震荡不收敛。经验值是区域边长的 0.2%~0.5%,比如 L=100 时 step 取 0.2~0.5。

max_iter是最大迭代次数,一般看收敛曲线,如果 100 次就平了,设 200 足够;如果还在上升,加到 500。

提示:调参时每次只改一个参数,改完记录覆盖率和收敛代数,这样才能看出哪个参数影响最大。

3.4 对比不同算法的效果

如果你想对比 VFA、PSO、GA 的效果,可以分别跑三个主脚本,把coverage_history保存下来画在同一张图上:

% 假设已经分别运行了三个算法,得到 coverage_vfa, coverage_pso, coverage_ga figure; plot(coverage_vfa, 'b-', 'LineWidth', 1.5); hold on; plot(coverage_pso, 'r--', 'LineWidth', 1.5); plot(coverage_ga, 'g-.', 'LineWidth', 1.5); legend('VFA', 'PSO', 'GA'); xlabel('迭代次数'); ylabel('覆盖率'); title('三种算法覆盖率收敛对比'); grid on;

这种对比图放在论文或报告里很有说服力。注意 PSO 和 GA 的迭代次数可能和 VFA 不一样,画图前统一截断到相同长度,或者用xlim限制横轴范围。

4. 核心模块拆解:合力计算与覆盖率统计

4.1 合力计算函数 compute_force.m

这是虚拟力算法最核心的部分,直接决定节点怎么移动。代码通常长这样:

function force = compute_force(nodes, L, Rs, Rc) % nodes: N x 2 矩阵,每行是一个节点的 (x, y) 坐标 % L: 监测区域边长 % Rs: 感知半径 % Rc: 通信半径 % force: N x 2 矩阵,每个节点受到的合力 N = size(nodes, 1); force = zeros(N, 2); eta = 0.05; % 引力系数 rho = 0.5; % 斥力系数 d_th = Rs / 2; % 距离阈值 for i = 1:N fx = 0; fy = 0; for j = 1:N if i == j continue; end dx = nodes(j, 1) - nodes(i, 1); dy = nodes(j, 2) - nodes(i, 2); d = sqrt(dx^2 + dy^2); if d == 0 continue; end % 单位方向向量 ux = dx / d; uy = dy / d; if d < d_th % 斥力:距离太近,互相推开 magnitude = rho * (1/d - 1/d_th); fx = fx - magnitude * ux; fy = fy - magnitude * uy; elseif d > Rc % 引力:距离太远,互相拉近 magnitude = eta * (d - Rc); fx = fx + magnitude * ux; fy = fy + magnitude * uy; end end % 边界斥力:防止节点跑出监测区域 boundary_force = 2.0; if nodes(i, 1) < Rs fx = fx + boundary_force; elseif nodes(i, 1) > L - Rs fx = fx - boundary_force; end if nodes(i, 2) < Rs fy = fy + boundary_force; elseif nodes(i, 2) > L - Rs fy = fy - boundary_force; end force(i, :) = [fx, fy]; end end

逐段说明:外层循环遍历每个节点,内层循环遍历其他节点计算两两之间的力。d < d_th时产生斥力,力度随距离减小而增大;d > Rc时产生引力,力度随距离增大而增大。边界斥力是硬约束,节点靠近边界时被推回来,保证所有节点始终在监测区域内。

参数eta和rho控制引力和斥力的相对强度。如果斥力太大,节点会散得太开,边缘覆盖好但中间可能出现空洞;如果引力太大,节点会聚成一团,重叠严重。我一般先让rho略大于eta,比如rho=0.5, eta=0.05,这样节点倾向于均匀分布而不是聚团。

4.2 覆盖率计算函数 calc_coverage.m

覆盖率是评价优化效果的硬指标,计算逻辑是网格法:把监测区域划分成小网格,统计被至少一个节点覆盖的网格点比例。

function coverage = calc_coverage(nodes, L, Rs) % 用网格法计算覆盖率 % 网格分辨率越高,计算越准但越慢 grid_step = 1; % 网格间距,单位:米 x = 0:grid_step:L; y = 0:grid_step:L; [X, Y] = meshgrid(x, y); covered = false(size(X)); N = size(nodes, 1); for i = 1:N % 计算每个网格点到节点 i 的距离 dist = sqrt((X - nodes(i,1)).^2 + (Y - nodes(i,2)).^2); % 距离小于感知半径则认为被覆盖 covered = covered | (dist <= Rs); end coverage = sum(covered(:)) / numel(covered); end

grid_step取 1 米时,100×100 的区域有 10201 个网格点,计算量可以接受。如果你要更精确的结果,把grid_step改成 0.5,网格点变成 40401 个,计算时间大约翻四倍。实际写论文时,我一般用 1 米网格,然后在论文里说明网格分辨率,审稿人不会揪这个。

covered是一个逻辑矩阵,covered | (dist <= Rs)这行用了逻辑或的短路特性,只要之前已经被某个节点覆盖,后面就不再重复计算,但 MATLAB 的向量化操作会全部算一遍,所以这里其实没有短路优化。如果你追求速度,可以在循环里加个判断,但一般没必要。

4.3 位置更新函数 update_position.m

合力算出来之后,节点按步长移动。这个函数很简单,但有几个细节要注意:

function nodes = update_position(nodes, force, step, L) % 按合力方向移动节点,并限制在监测区域内 N = size(nodes, 1); for i = 1:N fx = force(i, 1); fy = force(i, 2); f_mag = sqrt(fx^2 + fy^2); if f_mag > 0 % 归一化后乘以步长,保证每次移动距离不超过 step nodes(i, 1) = nodes(i, 1) + step * fx / f_mag; nodes(i, 2) = nodes(i, 2) + step * fy / f_mag; end % 边界截断,防止节点跑出区域 nodes(i, 1) = max(0, min(L, nodes(i, 1))); nodes(i, 2) = max(0, min(L, nodes(i, 2))); end end

这里做了归一化,保证每个节点每次移动的距离恰好是step,而不是合力越大移动越远。如果不归一化,合力大的节点会一步跳很远,容易震荡。边界截断是兜底措施,正常情况下边界斥力已经能阻止节点跑出去,但数值计算可能有误差,加个max/min更保险。

5. 避坑与常见问题:跑代码时容易翻车的地方

5.1 报错 “未定义函数或变量”

现象:运行主脚本时提示某个函数找不到,比如compute_force未定义。

原因:MATLAB 当前工作目录不在项目根目录,或者子文件夹没有加入搜索路径。

解决:先cd到项目根目录,然后执行addpath(genpath(pwd))。如果还不行,检查函数文件名和调用名是否一致,MATLAB 对大小写敏感,Compute_Force.m和compute_force会被当成两个不同的东西。

5.2 覆盖率不升反降

现象:迭代过程中覆盖率曲线先升后降,或者一直下降。

原因:步长step太大,节点在最优位置附近来回震荡;或者斥力系数rho过大,节点被推得太散,边缘覆盖好了但中间出现空洞。

解决:先把step减小到原来的 1/3,看曲线是否变平滑。如果还不行,把rho调小,比如从 0.5 降到 0.2,让节点更倾向于聚拢。我一般会同时打印每次迭代的覆盖率,观察是在哪个阶段开始下降的。

5.3 节点跑出监测区域

现象:绘图时发现有些节点在区域边界外面。

原因:边界斥力太小,或者步长太大导致节点一步跨过边界。

解决:把boundary_force从 2.0 加大到 5.0,或者在update_position里加硬截断。硬截断虽然简单粗暴,但能保证不出界,代价是节点可能贴着边界不动,影响覆盖率。更好的做法是边界斥力随距离增大而增大,形成一个软约束。

5.4 运行速度太慢

现象:迭代 200 次要跑好几分钟。

原因:覆盖率计算用了双重循环,或者网格分辨率太高。

解决:把calc_coverage里的grid_step从 0.5 改成 1 或 2,计算量会大幅下降。另外,compute_force里的双重循环是 O(N²),如果 N 超过 100,可以考虑用pdist2一次性算距离矩阵,然后向量化计算合力。不过对于 N=30~50 的典型场景,双重循环已经够快,没必要过度优化。

5.5 不同算法结果差异大

现象:VFA 跑出来覆盖率 92%,PSO 只有 85%,怀疑代码有问题。

原因:不同算法的参数没有调到各自最优,直接对比不公平。

解决:每个算法单独调参,记录各自最佳覆盖率,再放在一起比。PSO 的惯性权重、学习因子,GA 的交叉率、变异率,都需要根据问题规模调整。我一般会给每个算法跑 10 次取平均,避免单次随机性影响结论。

6. 二次开发与改造:把这套代码变成你自己的东西

6.1 换感知模型:从布尔模型到概率模型

这套代码默认用的是布尔感知模型:距离小于 Rs 就算覆盖,否则不算。实际传感器有探测概率,距离越远探测概率越低。你可以把calc_coverage改成概率模型:

function coverage = calc_coverage_prob(nodes, L, Rs, lambda) % 概率感知模型 % lambda: 衰减系数,越大衰减越快 grid_step = 1; x = 0:grid_step:L; y = 0:grid_step:L; [X, Y] = meshgrid(x, y); prob = zeros(size(X)); N = size(nodes, 1); for i = 1:N dist = sqrt((X - nodes(i,1)).^2 + (Y - nodes(i,2)).^2); % 概率模型:距离小于 Rs 时概率为 exp(-lambda * dist) p = exp(-lambda * dist); p(dist > Rs) = 0; % 多个节点覆盖时取最大概率(或按独立事件算联合概率) prob = max(prob, p); end coverage = sum(prob(:)) / numel(prob); end

lambda控制衰减速度,典型值 0.05~0.2。这个改动会影响优化结果,因为节点不再追求“刚好覆盖”,而是追求“高概率覆盖”,布局会更紧凑。

6.2 加能耗约束:让节点别跑太远

虚拟力算法有个实际问题:节点移动消耗能量,移动距离越大能耗越高。你可以在update_position里加一个累计移动距离限制:

function [nodes, total_move] = update_position_energy(nodes, force, step, L, max_move) N = size(nodes, 1); total_move = 0; for i = 1:N fx = force(i, 1); fy = force(i, 2); f_mag = sqrt(fx^2 + fy^2); if f_mag > 0 dx = step * fx / f_mag; dy = step * fy / f_mag; % 检查是否超过最大移动距离 if total_move + sqrt(dx^2 + dy^2) <= max_move nodes(i, 1) = nodes(i, 1) + dx; nodes(i, 2) = nodes(i, 2) + dy; total_move = total_move + sqrt(dx^2 + dy^2); end end nodes(i, 1) = max(0, min(L, nodes(i, 1))); nodes(i, 2) = max(0, min(L, nodes(i, 2))); end end

max_move是所有节点累计移动距离的上限,根据节点电池容量和移动能耗模型来定。这个约束会让优化结果偏向“少移动、多覆盖”的方案,更贴近实际部署。

6.3 验证改造效果:三步确认法

改完代码后,别急着下结论,按这三步验证:

第一步,固定随机种子,跑三次看结果是否一致。MATLAB 里用rng(42)固定种子,如果三次结果差异很大,说明算法随机性太强,需要增加迭代次数或调整参数。

第二步,和原始代码对比。把改造前后的覆盖率曲线画在一起,看改造是提升了覆盖率还是降低了。如果降低了,分析是约束太紧还是模型不合理。

第三步,换几组不同节点数量和区域大小,看改造后的代码是否稳定。我一般会跑 N=20、30、50 三组,L=50、100、200 三组,共九种组合,确保代码不是只对特定参数有效。

6.4 一个实用技巧:把结果导出成 CSV

论文里经常需要把覆盖率数据做成表格,手动抄容易出错。在 MATLAB 里加一行:

% 导出覆盖率历史到 CSV csvwrite('coverage_history.csv', coverage_history);

csvwrite简单直接,但只能写数值矩阵。如果你要带表头,用writetable:

T = table((1:max_iter)', coverage_history, 'VariableNames', {'Iteration', 'Coverage'}); writetable(T, 'coverage_history.csv');

这样导出的 CSV 可以直接拖进 Excel 或 Origin 画图,省去手动整理的时间。

注意:csvwrite在较新版本的 MATLAB 中不推荐使用,如果报 warning,换成writematrix即可。

从那以后我每次改完算法参数,都强制走一遍“固定种子跑三次 + 对比原始曲线 + 换参数组合验证”的流程,虽然多花十几分钟,但能避免在论文里写出经不起推敲的结论。希望这套代码和上面的改造思路能帮到你,拿到压缩包后先跑通 VFA,再逐步试 PSO 和 GA,最后按自己的课题需求改感知模型或加约束,基本就能覆盖 WSN 覆盖优化的大部分场景了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询