HEED分簇协议MATLAB仿真:无线传感器网络能效与生命周期优化指南
2026/9/23 18:39:54 网站建设 项目流程

简介:基于 MATLAB 的无线传感器网络 HEED 算法实现,面向 WSN 研究者、通信专业学生及算法仿真爱好者,用于解决分簇路由中簇头均衡选举与网络能效优化问题。该算法的核心是根据节点剩余能量与邻居分布动态选举簇头,以延长网络生命周期,与 LEACH 相比更注重簇头选择的均衡性。压缩包仅含 1 个 .m 文件(HEED 算法主程序),文件体积 4KB,代码紧凑,便于直接运行或二次修改。已有 195 人学习使用。该实现覆盖节点初始化、簇头选举、分簇以及数据聚合等核心步骤,用户可通过 MATLAB 绘图直观分析能量消耗曲线、网络覆盖率变化和网络生存时间等关键指标;同时可调整参数模拟不同信道条件,与 LEACH 算法对比,深入理解层次化节能路由机制,为 WSN 协议研究和课程设计提供可复现的仿真参考。

1. 无线传感器网络能效仿真,为什么 HEED 比 LEACH 更能托住网络生命周期

一个典型的场景:你已用 MATLAB 跑通了 LEACH,画出了存活节点数曲线,结果发现每轮随机选簇头的"运气成分"大得离谱,头几轮就可能让低能量节点当簇头,能量很快被拖死。这时把目光转向 HEED(Hybrid Energy-Efficient Distributed,混合能量高效分布式分簇)是顺理成章的:它把节点剩余能量和簇内通信代价同时纳入簇头竞选,正是无线传感器网络节能方向最常见的 matlab 仿真课题之一。这篇文章不讨论安装也不讨论工具箱,把 HEED 从公式到 MATLAB 逐行摊开:CHprob 主选概率、AMRP 最小发射功率代价、迭代竞选主循环,以及真正拖垮你调试进度的五个常见坑。适合准备复现论文、交付课程设计或想把手头 LEACH 代码升级成 HEED 的从业者。

2. HEED 分簇机制拆解:主选概率、AMRP 与簇头竞选规则

开始动代码之前,得先把手里的协议逻辑和仿真需求对应上。HEED 是一套分布式迭代算法,它的设计目标不是选出一个全局最优簇头集合,而是在不知道全局信息的条件下,让每个节点“自己判断该不该当簇头”,并且让这个判断尽快收敛。

2.1 为什么选 HEED:和 LEACH 的对比决定了你该不该入这个坑

LEACH 的分簇逻辑是“每个节点按固定概率 P 当选簇头,且保证每轮恰好约 P*N 个簇头”。它最大的问题是完全不看剩余能量——一个濒死的节点可能在下轮被选中当簇头,结果发几个数据包就阵亡,整个簇跟着失效。HEED 的改进思路很直接:竞选不再掷骰子,而是把“剩余能量占比”作为主选概率,再用“簇内通信代价”做二次筛选。

HEED 属于分布式算法,不需要基站集中计算,每个节点只需要和自己一跳内的邻居交换状态,这让它在网络规模变大时不会出现单点瓶颈。对 MATLAB 仿真而言,这意味着你不需要维护一个全局调度器(这一点会在 4.1 的代码里体现),主循环写成“每轮所有活节点同时参与竞选”即可。理论代价是收敛轮数增加,但相比 LEACH 每轮重选一次簇头,HEED 的收敛开销可以控制在一轮分簇前的几次迭代内。

对比项LEACHHEED
簇头选定依据固定概率随机剩余能量×通信代价
是否需要邻居信息需要一跳邻居
分簇质量随机性强、簇大小波动大簇更均匀、能量方差更小
MATLAB 代码改动量低到中等,主要是加一轮迭代
典型适用场景能量充足、短期采集长时间监测、能量受限

这张表只给一个直观结论:如果你的仿真目标是“网络生命周期越长越好”,HEED 的收敛代价换来的是更平均的能量消耗,值得投入。若只是演示分簇概念,LEACH 更快,但你要接受它的曲线上布满了随机噪声。

2.2 主选概率 CHprob 与最大最小能量约束

HEED 的第一个关键参数是主选概率 CHprob(Cluster Head probability)。每个节点 i 在第 r 轮开始时按公式计算:

CHprob(i) = max( Cprob * E_residual(i) / E_max, pmin )

其中 Cprob 是初始概率,常见取值 0.05;E_residual(i) 是节点 i 当前剩余能量;E_max 是网络初始能量中最大的那个值(或用统一初始能量);pmin 是下限概率,通常取 1e-4 量级。这个公式保证了两个方向:剩余能量高的节点有更高的概率进入簇头候选,而 pmin 的存在确保能量很低的节点“理论上仍有机会当选”,不至于让某些区域彻底没有簇头。

这里有个容易误解的点:CHprob 不是“掷一次骰子决定当不当”,而是作为竞选过程中的一个阈值基线。在 HEED 的迭代中,每次检测到自己是临时簇头时,节点以 CHprob 与一个均匀随机数的比较来决定是否对外宣告;若未宣告且未听到更优簇头,CHprob 会翻倍(或乘以固定因子)后进入下一轮迭代。这个“翻倍”的机制保证算法能在有限轮内收敛——每轮未决节点的主选概率至少翻倍,很快会超过 1,必然进入宣告分支。

提示:MATLAB 的 rand 在每个迭代轮里都会重新产生随机数,若你在一个节点循环里多次调用 rand,会让“决定是否宣布簇头”这一事件变得不可复现。后面第 5 章会专门讲这类翻车。

2.3 簇内通信代价 AMRP 与最终簇头决策

仅有能量还不够:如果两个能量接近的节点竞争,HEED 会偏向“簇内通信代价更小”的那一个。HEED 原文用的是 AMRP(Average Minimum Reachability Power,平均最小可达发射功率),即节点到所有一跳邻居的最小合法发射功率的平均值。仿真里通常不建模具体功率等级表,而是用距离平方 d^2 的归一化值代替:

AMRP(i) = (1 / |Neighbor(i)|) * Σ_{j ∈ Neighbor(i)} d(i, j)^2

其中 d(i, j) 是两个节点间的欧氏距离。AMRP 越小,意味着该节点周边邻居越近,簇头收集簇内数据时耗能越低。

最终簇头决策是在“能量优先、代价次之”的顺序下完成的:某节点临时被选为簇头后,会广播一个候选消息;其他临时簇头收到后若发现自己的综合代价(主选概率归一化值与 AMRP 的加权或排名)大于对方,就退出竞争,加入对方簇。这个“比你强就让位”的规则,让最终簇头在能量和通信代价两个维度上都近似局部最优。

3. 用 MATLAB 搭 WSN 仿真地基:节点部署与能耗模型

这一章开始落代码。这里不讨论 grid 网络,直接用最常见的随机部署(uniform random)来预设下一步,因为 HEED 的 AMRP 计算在分布不均的网络里更能体现“代价”的作用。先定义节点结构体数组,再写出能量模型函数,最后说明每轮结算顺序。

3.1 节点、基站与消息字段的结构体设计

需要的数据字段要一次想全:坐标、能量、状态、簇头归属、竞选相关字段、消息次数。用 struct 数组而不是单独一组向量,后面在参数传递和调试时能省不少事。

rng(42); % 固定随机种子,保证仿真可复现 N = 100; % 节点总数 field_len = 100; % 正方形部署区域边长,单位 m Emax = 0.5; % 初始能量,单位 J bs = [50, 150]; % 基站坐标,放在区域外上方 nodes = struct([]); for i = 1:N nodes(i).x = rand * field_len; nodes(i).y = rand * field_len; nodes(i).E = Emax; nodes(i).alive = 1; nodes(i).role = 0; % 0=普通节点,1=簇头,2=孤立节点 nodes(i).head_id = i; % 归属簇头 id,默认是自己 nodes(i).CHprob = 0; % 本轮主选概率,每轮计算 nodes(i).tmp_heard = 0; % 本轮是否听到过候选簇头 nodes(i).tmp_better = 0; % 是否被更优簇头覆盖 nodes(i).tested = 0; % 是否已被测试为临时簇头(调试用) end

逻辑说明:struct 数组的每个元素承载一个节点的完整状态,字段名全部用下划线风格,方便在 MATLAB 的 Workspace 里查看。基站坐标固定在区域外,这是 HEED 常见的仿真设定——簇头把聚合后的数据直接发给远处基站,因此簇内代价低的节点当簇头更有优势。

参数说明:rng(42) 是这一章最关键的“后悔药”。WSN 仿真对随机部署和竞选过程都依赖随机数,没有固定种子,你两次跑出的曲线不完全一样,后面调参时根本分不清是参数影响还是运气影响。节点数 N 默认 100,区域 100m×100m 是常见的组合,密度大约为每平方米 0.01 个节点,AMRP 计算时邻居数适中。

3.2 一阶无线能耗模型的两个可调参数

WSN 能量仿真最常用的是经典一阶无线模型:发送 k 比特到距离 d 的节点,耗能为 Eeleck + εfskd^2(近距离)或 Eeleck + εmpkd^4(远距离);接收 k 比特耗能 Eeleck;合并 k 比特耗能 EDAk。阈值 d0 = sqrt(εfs / εmp),大约 87m 左右。

function Etx = tx_energy(k, d, Eelec, Efs, Emp, d0) if d < d0 Etx = Eelec * k + Efs * k * d^2; else Etx = Eelec * k + Emp * k * d^4; end end function Erx = rx_energy(k, Eelec) Erx = Eelec * k; end

逻辑说明:这两行函数是整个仿真耗能的唯一来源。发送能量的自由空间段 εfs 和多径段 εmp 之间有一个 d0 切换,d 小于 d0 用平方损耗,大于等于 d0 用四次方损耗。HEED 里簇头到基站的距离通常远超 d0(本仿真基站距离区域约 50-60m,若按跑通默认看也足够触发四阶),而簇内节点到簇头大多在 d0 内,所以簇内采用平方模型,簇头到基站常走四次方模型,这本身也是 HEED 簇头选择中 AMRP 偏向近邻的原因之一。

参数说明:Eelec 一般取 50nJ/bit,εfs 取 10pJ/bit/m^2,εmp 取 0.0013pJ/bit/m^4,EDA 取 5nJ/bit。这些数值来自 Heinzelman 的经典参数表,直接抄即可。真正需要你关心的是 d0 的计算:如果 Efs、Emp 比例不对,d0 会变成十几米甚至几百米,直接决定簇内和簇外的模型切换点,仿真曲线会明显不同。

3.3 死节点判定与每轮能量扣减顺序

能量扣减顺序是 WSN 仿真里最常见的逻辑翻车点:如果先判定“能量不足”再扣能量,会多扣一次;如果先扣再判定,则可能出现负能量节点。我一般用“先扣减、后判定”的顺序,并在每轮结束后统一把能量低于阈值的节点标记为死亡。

function [nodes] = deduct_energy(nodes, i, d_energy) nodes(i).E = nodes(i).E - d_energy; if nodes(i).E <= 0 nodes(i).alive = 0; nodes(i).role = 0; nodes(i).head_id = i; nodes(i).E = 0; % 能量归零,避免负值污染绘图 end end

逻辑说明:函数接收节点结构体数组、节点下标和本次扣减量,先扣后判,防止一次性扣费把能量扣成负数。置 E=0 是有意为之:后面绘图和统计剩余能量时,负能量会拉偏总体曲线,也会让 CHprob 计算出现负值从而被 max() 强制拉回 pmin,掩盖了真实问题。

参数说明:能量阈值默认 0,即能量耗尽才算死。如果你要模拟“低于阈值就休眠”的场景,可以把阈值改成 0.001J 之类的值在调用处传入。这个函数在后续每轮结算中会被大量调用,所以保持它足够小且只做一件事,方便单步断点调试。

4. HEED 分簇轮询的 MATLAB 实现:从迭代选举到数据上报

这一章是核心。先写 HEED 迭代竞选主循环,再给出簇头任命与普通节点加入规则,最后写一轮数据收集后的能量结算。代码要在 MATLAB 2019b 以上能直接运行。

4.1 HEED 竞选的迭代主循环

HEED 的竞选不是单轮完成的。第 r 轮开始时,所有活节点计算自己的 CHprob,然后进入最多 max_iter 次迭代:每一轮中,若节点听到某个临时簇头的综合代价比自己的低,它就把 tmp_better 置 1;若没有听到更优簇头且自己的 CHprob 足够大(或随机判定通过),则宣布自己为临时簇头。每轮迭代结束后,未决节点把 CHprob 翻倍,进入下一轮。

function [nodes] = heed_election(nodes, N, max_iter, pmin, Cprob) for r = 1:max_iter for i = 1:N if nodes(i).alive == 0 continue; end % 主选概率:剩余能量占比,下限 pmin nodes(i).CHprob = max(Cprob * nodes(i).E / Emax, pmin); % 已确认降级的节点不再参与宣告 if nodes(i).tmp_better == 1 continue; end % 当前轮的宣告阈值,随着迭代翻倍快速逼近收敛 if nodes(i).tmp_heard == 0 || nodes(i).CHprob >= rand nodes(i).role = 1; % 暂时当簇头 else nodes(i).role = 0; end end % 竞选出局判定:本轮有更优簇头覆盖则放弃 for i = 1:N if nodes(i).alive == 0 continue; end if nodes(i).role == 1 for j = 1:N if i == j || nodes(j).alive == 0 continue; end if nodes(j).role == 1 && ... amrp_less(nodes(j), nodes(i)) == 1 nodes(i).tmp_better = 1; break; end end end end % 未决节点概率翻倍,继续下一轮 for i = 1:N if nodes(i).alive == 1 && nodes(i).role ~= 1 && nodes(i).tmp_better == 0 nodes(i).CHprob = min(nodes(i).CHprob * 2, 1); end end end end

逻辑说明:外层循环是 HEED 论文中的“迭代层”,内层第一段处理宣告,第二段处理比优,第三段翻倍。注意 tmp_heard 表示该节点是否听过任何簇头消息,若从没听过,它直接按 CHprob 与 rand 的比较来决定是否宣告;若听过且自己不是更优,则保持普通角色。这个例子中对“听到”的建模是简化版——真实 HEED 中节点需要解析消息里的代价字段,这里改成用 amrp_less 比较两个候选簇头的 AMRP 是否更小,目的相同但代码更短。

参数说明:max_iter 一般取 5 到 10。HEED 原文证明迭代数与节点度相关,但在 MATLAB 仿真里,超过 10 轮的收敛收益几乎为 0,反而拖慢每轮耗时。pmin 取 1e-4,Cprob 取 0.05,这两组参数可以在第 6 章的敏感性分析里看清楚它们的作用。翻倍策略的上限设为 1,防止概率溢出。

下面的 amrp_less 函数负责比较两个临时簇头谁的综合代价更小:

function flag = amrp_less(a, b) % 比较两个临时簇头谁的综合代价更小,这里用 AMPR 与能量的归一化加权 da = calc_amrp(a); db = calc_amrp(b); score_a = 0.7 * da + 0.3 * (1 - a.E / Emax); score_b = 0.7 * db + 0.3 * (1 - b.E / Emax); flag = score_a < score_b; end

说明:权重 0.7/0.3 是经验值,意思是 AMRP 占主导、能量作辅助。HEED 原文不这样加权,它用次数排序:先比能量等级,再比 AMRP。但在 MATLAB 里用加权标量更直观,也方便你画代价场来看哪些节点胜出。

4.2 簇头任命与普通节点的加入规则

迭代结束后,role 为 1 的节点就是本轮簇头。接下来普通节点选择要加入的簇:最简单的规则是自己找 AMRP 最小的簇头;若找不到任何簇头,该节点标记为孤立节点,直接与基站通信。这一步在代码里可以合并到一个全局循环里完成。

for i = 1:N if nodes(i).alive == 0 continue; end if nodes(i).role == 0 best_cost = inf; best_id = i; % 默认自己当簇头,即孤立处理 for j = 1:N if nodes(j).role ~= 1 || nodes(j).alive == 0 continue; end d = norm([nodes(i).x - nodes(j).x, nodes(i).y - nodes(j).y]); if d^2 < best_cost best_cost = d^2; best_id = j; end end nodes(i).head_id = best_id; if best_id == i nodes(i).role = 2; % 孤立节点,直接发基站 end else nodes(i).head_id = i; end end

逻辑说明:普通节点遍历所有候选簇头,用欧氏距离平方作为代价(等价于最小发射功率的近似),选中代价最小的簇头。若没有任何簇头在通信范围,head_id 保持自己,role 置 2,后续对基站直传。这个“默认自己当簇头”的兜底逻辑是必要的,否则未分配节点的数据会在上报阶段丢失,导致能量扣减和统计出现空洞。

参数说明:这里其实隐藏了通信半径的概念——代码默认所有节点都能到达任何簇头。如果你想限制簇的覆盖范围,可以在判断条件里加一个通信半径 radius,只有 d <= radius 的簇头才能成为候选。限制后会显著增加孤立节点比例,你需要同时调整 Cprob 来增加簇头数量。

4.3 一轮数据收集后的能量结算

数据收集过程按角色分开结算:普通节点把 k 比特数据发给簇头,扣发送能量与接收能量;簇头把聚合后的 k 比特数据发给基站,扣发送能量;孤立节点直接发基站,扣发送能量。

k = 2000; % 每个数据包 bit 数 Eelec = 50e-9; % 电路耗能 50nJ/bit Efs = 10e-12; Emp = 0.0013e-12; EDA = 5e-9; d0 = sqrt(Efs / Emp); packet_size = k; % 第1步:簇内成员向簇头发数据 for i = 1:N if nodes(i).alive == 0 || nodes(i).role == 1 continue; end h = nodes(i).head_id; if h == i continue; % 孤立节点单独处理 end d = norm([nodes(i).x - nodes(h).x, nodes(i).y - nodes(h).y]); nodes(i).E = nodes(i).E - tx_energy(packet_size, d, Eelec, Efs, Emp, d0); nodes(h).E = nodes(h).E - rx_energy(packet_size, Eelec); end % 第2步:簇头聚合后发向基站 for i = 1:N if nodes(i).alive == 0 || nodes(i).role ~= 1 continue; end d2bs = norm([nodes(i).x - bs(1), nodes(i).y - bs(2)]); nodes(i).E = nodes(i).E - tx_energy(packet_size, d2bs, Eelec, Efs, Emp, d0); end % 第3步:孤立节点直传基站 for i = 1:N if nodes(i).alive == 0 || nodes(i).role ~= 2 continue; end d2bs = norm([nodes(i).x - bs(1), nodes(i).y - bs(2)]); nodes(i).E = nodes(i).E - tx_energy(packet_size, d2bs, Eelec, Efs, Emp, d0); end

逻辑说明:把结算拆成三个循环是为了让每一类角色的能量流向都清晰可见。第 1 步中簇头接收能量按成员数累加——注意这里没有显式扣族头的发射能量和成员接收能量,因为这类消息在 HEED 竞选阶段才会产生,数据上报阶段只有成员给簇头、簇头给基站、孤立直传三类流量。基站不消耗能量,不需要模拟。

参数说明:k=2000 是每个节点每轮上报的数据量,对应 LEACH 论文里的经典值;EDA 表示聚合的能耗,簇头发射时扣一次合并费即可,不需要乘以成员数——聚合后仍然是一份 k 比特的包。如果你要模拟“融合率”,可以在这里把第二个循环的 packet_size 改成 k * (1 + 成员数*0.1) 之类,但那就偏离 HEED 的标准设定了。

5. HEED 仿真避坑:5 个翻车现场及对应的 MATLAB 补救

从原理到代码都齐了,但仿真调试比写原理更难。下面 5 条是我实际跑 HEED 时踩过的坑,按概率从高到低排,每条都按“现象→原因→解决”写。这些坑在别人分享的代码里很难看出来,因为都是写错了也能跑通、但结果经不起推敲的类型。

5.1 随机数状态没有固定,两次仿真的曲线对不上

现象:同一个脚本跑两次,存活节点数和剩余总能量的曲线在某一轮开始分叉,甚至 FND(第一个节点死亡轮数)差出几十轮。

原因:MATLAB 的 rng 在每次脚本启动时按系统时间初始化。你在节点部署、HEED 竞选里用了 rand 和 randperm 多次,这些随机流的顺序在不同启动之间是不同的。更隐蔽的情况是你在某个循环里调用了 rand 但循环次数取决于上一轮能量的结果,于是随机流的消耗节奏也不同,导致同一次运行内部每轮使用的随机数数量都在变。

解决:在脚本最开头加 rng(42)(或任何固定种子);如果你的代码里有多个独立随机过程(部署、竞选、数据包传输概率),分开为每个过程单独固定种子会更好:部署用rng(42, 'twister'),竞选用rng(7, 'twister')。我自己习惯在主脚本入口留一个rng(seed)参数,这样跑敏感性时可以方便地换种子确认结论是否稳定。

5.2 CHprob 初始值与 pmin 组合不当,簇头数量失控

现象:一轮分簇结束后统计 role==1 的节点数,有时只有 2 个簇头,有时有 20 多个,簇大小严重不均,剩余能量曲线提前崩塌。

原因:这是最经典的参数玄学。Cprob=0.05 时,初始概率均匀分布在 0.05*E/Emax 附近;若 pmin=1e-3,则 E 低于 2% 的节点依然能以 0.05 的基线上限参与竞选,簇头数被拉高。反过来,Cprob 太小(如 0.01)则平均簇头数偏少,簇头到基站的远距离传输能耗剧增。HEED 的论文里 pmin 取 1e-4 是有意为之,目的就是让低能量节点在极低概率下才能竞选,而不是成为“保底高频节点”。

解决:先固定 pmin=1e-4,把 Cprob 从 0.03 到 0.1 之间扫一遍,画出“簇头数随轮数的曲线”,找到稳定区间。我一般直接看第 100 轮的簇头数,如果超过 N/Cprob 的 2 倍,就调小;如果低于 1/3,就调大。这个调试过程可以写成脚本自动化,第 6 章的敏感性分析会给出代码框架。

5.3 孤立节点无人认领,出现“光杆簇头”

现象:运行结束画分簇图时发现某些簇头周围没有任何成员节点,同时远处有一个 role==2 的孤立节点直接把数据发到基站,能量消耗失衡。

原因:普通节点加入簇的逻辑是“选择距离最近的簇头”,但你的通信半径限制(如果加了)或簇头数量过少会使得某些节点在半径内找不到任何簇头。若不把这类节点纳入某个簇,它们直接和基站通信的距离往往超过 d0,采用四次方模型,一次发送就消耗大量能量,导致曲线在第 1 轮就出现断崖式下跌。

解决:代码里对孤立节点的兜底已经在 4.2 里写了:找不到簇头就自己当簇头、直连基站。但更要紧的是控制簇头数不要过少——在 Cprob 调参时就应把孤立节点比例压到 5% 以下。脚本里加一行统计很方便:每次分簇后计算sum([nodes.role] == 2),若大于 N*0.05,就把 Cprob 调大重跑。不要指望 HEED 的迭代机制会自己解决这个问题,它负责收敛,不负责覆盖。

5.4 能耗模型判断 d0 边界写反,UR 曲线中途跳变

现象:剩余总能量曲线在某几轮突然下探,然后恢复正常斜率,波形上出现“台阶”或“尖刺”,单独看一次运行很像随机事件,但在多个种子下都在同一轮附近出现。

原因:节点能量下降到某个水平后,原本在平方损耗范围内的簇内通信距离因节点死亡、簇成员重新分配而跨越 d0,触发四次方模型,单次发送耗能剧增。看似是模型切换,其实是你的 d0 计算值不对——如果你手算了d0 = sqrt(Efs/Emp)但用了错误的量纲(Efs 取 10e-12,Emp 取 0.0013e-12,d0 ≈ 87m),而实际仿真区域边长只有 100m,几乎一半的簇内通信都会超过 d0,确实会有这种跳变。

解决:在初始化脚本里直接打印 d0 并做合理性检查。对于 100m 区域,把 d0 配置在 60~90m 之间比较合适。更稳妥的做法是给能耗函数加个断言:运行时若发现 d > d0 且 d0 小于某个阈值(比如 30m),就报错提示你检查 Efs/Emp 的单位。这个断言在别人复现你的代码时尤其值钱,能帮你挡掉一半的“为什么波形不对”的提问。

5.5 消息交互写成全联接 O(N^2),大规模网络卡死

现象:节点数从 100 提高到 500 后,单轮 HEED 竞选耗时长到无法接受,甚至 MATLAB 假死,内存占用飙升。

原因:HEED 的竞选代码如果按最直白的方式写——每个节点遍历所有其他节点比较 AMRP——复杂度是 O(N^2)。这个复杂度在 N=100 时是 1 万次距离计算,没感觉;到 N=500 时变成 25 万次,加上每轮迭代 max_iter=8,就是 200 万次距离计算和结构体字段访问,MATLAB 的解释型循环立刻现原形。

解决:两个优化方向。一是先用 pdist2 预计算一次 N×N 距离矩阵,竞选阶段直接查矩阵而不是重复计算距离;二是在竞选阶段只把“听到的簇头”限制在通信半径内,即先按距离矩阵滤出一跳邻居,再在邻居范围内做比优。把这两步做完,500 节点一整套分簇可以在 0.1 秒量级跑完,代码复杂度也从 O(N^2) 降为 O(N×d)(d 是平均邻居数)。注意预计算距离矩阵是用内存换时间,N=1000 时 8MB 矩阵完全可接受,但 N=5000 时就要分块处理,别无脑预计算。

6. 验证 HEED 是否真的省电:三个指标与两种参数敏感性观察

写完代码只是第一步。真正说服评审、同事或自己的,是曲线和数字。这一章介绍三个硬指标以及两轮参数实验,用来验证“HEED 比 LEACH 生命周期更稳”这个结论是否可复现。

6.1 生命周期的三个硬指标:FND、存活节点数与剩余能量

FND(First Node Dies)是第一个节点死亡的轮数,它是 WSN 路由协议最敏感的生命周期指标:谁先死、在第几轮死,直接反映能量均衡策略是否生效。另一个常用指标是网络 10% 节点死亡(10%ND)以及最后 1% 节点死亡的时间,但对 HEED 这种分簇协议,FND 和 50% 存活轮数更具区分度。

% 主循环中每轮记录三个统计量 round_energy(r) = sum([nodes.E]); round_alive(r) = sum([nodes.alive]); if first_dead_round == 0 && round_alive(r) < N first_dead_round = r; end

计算剩余总能量要小心:已死节点的 E 已被归零,求和不会引入偏差;但如果你在绘图时把全部节点的 E 都画出来,会出现死后不变的水平线,掩盖能量消耗速率,所以画图时只看 sum 或只取 alive 节点的均值。能量标准差更直观,它描述能量分布的分散程度:HEED 的预期效果是让所有节点剩余能量尽量接近,曲线越低越平摊;LEACH 的能量标准差在后期会急剧上升,因为总有节点被反复选中当簇头。

6.2 Cprob 与 pmin 的敏感性:跑一组对比后你会重新理解参数

参数敏感性实验不是玄学推演,而是用控制变量法找出每个参数对输出的影响方向。最省事的做法是把主循环包成一个函数,输入 Cprob 和 pmin,输出 FND 和 500 轮存活节点数,然后循环调用。

cfg_list = struct('Cprob', {0.03, 0.05, 0.10, 0.20}, ... 'pmin', {1e-4, 1e-4, 1e-4, 1e-3}); for k = 1:length(cfg_list) [fnd(k), alive_curve(k, :)] = run_heed( ... 'Cprob', cfg_list(k).Cprob, 'pmin', cfg_list(k).pmin); end

经验结论:Cprob 越大,初始簇头候选越多,分簇更均匀、FND 推后,但每轮竞选交互相应更多;pmin 取 1e-3 时低能量节点的保底概率升高,尾部存活拉长但前期 FND 反而提前——这是一个非常反直觉的结果,值得亲手复现一遍。如果你的复现结果总是 FND 在第 1~5 轮内出现,先查是不是能耗模型扣费顺序写反;如果 FND 稳定但存活曲线跟 LEACH 差异不大,注意看是不是你的 AMRP 权重设得太低,让代价因子形同虚设。

调参到 FND 稳定、50% 存活轮数比 LEACH 提升 20% 以上时,这份 HEED 仿真就可以作为交付起点。我自己的习惯是保留一组“基线参数 + 一个对比参数”跑双份数据,留好种子和脚本入口,方便后续换场景时追回当初结论。WSN 协议仿真最怕的不是参数不对,而是不知道哪些参数组合会大幅改变结论——所以建议把 Cprob、pmin、max_iter 三个变量专门拆成 config 结构体,让每一轮实验的结论都能对回配置。这些调试习惯比代码本身更能节省时间,希望帮到你。

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

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

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

立即咨询