ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MATLAB实现HEED算法仿真:无线传感器网络分簇协议从原理到实战

MATLAB实现HEED算法仿真:无线传感器网络分簇协议从原理到实战 简介面向无线传感器网络研究者与MATLAB仿真学习者聚焦HEED分层节能分布式聚类算法用于解决WSN中簇头选举、数据聚合与能量效率问题。算法基于节点剩余能量与邻居位置信息动态选举簇头采用层次化分簇结构相比LEACH更注重簇头的均衡选择从而延长整体网络生命周期。压缩包内包含1个m脚本文件大小仅4KB代码精简便于直接阅读、运行与二次修改适合以此为基础开展算法参数调试和仿真性能验证。已有195人学习下载。通过该脚本可快速搭建HEED算法仿真框架观察簇头轮换、能量消耗等核心逻辑并可调整节点数量、部署区域、能量阈值等参数比较不同配置下的网络生存时间、能量均衡度与数据传递成功率。仿真过程可记录首节点死亡时间、半数节点死亡时间等寿命指标直观输出节点能耗分布曲线帮助深入理解分层分簇机制也为进一步优化WSN路由策略提供可运行的入门范例。1. 用MATLAB复现HEED算法别让无线传感器网络仿真死在第一轮我最早把HEED算法用MATLAB跑起来时整个无线传感器网络第一轮就冒出四十多个簇头第六十轮开始成片死亡曲线图难看得像地震记录。后来才意识到HEED看着只有两个判据真正落地时从邻居发现到能量扣减全是细节。HEED全称是Hybrid Energy-Efficient Distributed clustering混合能量高效分布式分簇协议解决的是能量受限的无线传感器网络里“谁当簇头、普通节点挂靠谁”的问题。这篇笔记不抄协议原文只讲我实际写仿真时怎么拆逻辑、怎么定数据结构、参数调到多少才不翻车。适合做课程设计、毕业设计或者正在搭WSN科研仿真平台的从业者直接照着改。2. HEED算法为什么值得自己写一遍分簇逻辑与两个核心判据2.1 和LEACH相比HEED解决的是“随机轮换”的公平性问题无线传感器网络里节点通常靠电池供电部署在野外或楼宇夹层里换电池的成本比再扔一百个节点还高。所有路由协议的设计都围绕同一个目标省着点用别让某一个节点先死。分簇是其中最经典的手段——普通节点把数据短距离发给簇头簇头做数据融合后再长距离发给基站sink。这样长距离通信只有少数簇头承担普通节点每次只花小功率网络整体寿命能拉长好几倍。LEACH是这类分簇协议的鼻祖它的选簇头方式是纯随机轮换每轮按固定比例随机抽节点当簇头抽中谁就是谁。这在节点能量完全均匀的假设下没问题但实际里某个节点可能上一轮刚转发过大量数据剩余能量已经很低这轮又被随机抽中当簇头结果就是它很快死亡而它覆盖区域里的节点会一起变成孤儿。HEED的核心改动就是把“随机抽”改成“按能量概率抽 按通信代价归属”两个判据同时起作用簇头的分布和剩余能量挂钩不再完全看脸。我一般会把这两个判据拆成两句话记住能不能当簇头看剩余能量当上簇头后管多大范围看通信代价。前者决定候选资格后者决定最终入选整个协议是“候选—竞争—收敛”的过程而不是LEACH那种一锤子买卖。这也是为什么HEED常被叫“hybrid”——它把节点自身的能量信息和链路的代价信息混在一起决策。2.2 CHprob与AMRP剩余能量和通信代价怎么进入公式HEED里最核心的公式是候选概率CHprob max(Cprob * Eresidual / Einitial, pmin)Cprob是初始候选概率通常取0.05含义是理想情况下希望5%的节点成为簇头Eresidual是节点当前剩余能量Einitial是初始能量。两者相除得到一个0到1之间的比例因子能量越充足CHprob越大。pmin是最小概率下限一般取1e-4防止某些能量极低的节点完全失去候选资格后它周围的普通节点找不到簇头可挂。AMRPAverage Minimum Reachability Power平均最小可达功率是第二个判据。每个非簇头节点会选择向哪个候选簇头发起入簇请求选择依据就是到自己通信半径内所有候选簇头的发射功率取平均值最小的那个。这个值低说明簇头离自己近、通信链路质量好、发送同样的数据包消耗的能量少。注意AMRP不是只看距离而是看功率两个不同节点的接收灵敏度差异也会体现在功率上仿真里我们一般用自由空间模型的接收功率公式换算。选举过程是一个迭代循环。每个节点先以CHprob概率自荐为临时簇头然后所有节点根据AMRP选择自己要加入的临时簇头如果发现邻居里有人比自己更适合当簇头即对方AMRP更低自己就退出竞选否则从临时簇头升级为最终簇头。一轮迭代做完再进入下一轮子迭代直到连续若干轮没有节点状态变化或者到达预设的最大迭代次数。这个收敛机制保证了簇头的数量和质量不会靠一次随机就定死。2.3 一轮完整仿真包含四个阶段完整的HEED仿真一轮要经历四个阶段初始化、迭代建簇、稳定传输、轮结束。初始化只做一次包括节点部署、能量赋值、邻居发现迭代建簇就是刚才说的CHprob和AMRP循环稳定传输阶段簇头给成员分配TDMA时隙成员按自己的时隙发数据簇头做数据融合后发给sink轮结束阶段统一扣减能量、统计存活节点数然后进入下一轮。HEED协议本身不强制限制每轮建簇的迭代次数但工程上通常会设一个上限我一般设20次子迭代。原因很直接建簇阶段的控制报文也要消耗能量如果每轮都做50次子迭代网络还没把数据发完能量先耗在选簇头上。实际运行里多数场景10次以内就能收敛20次是个安全边界。下面这张表是我做仿真时常用的默认参数可以覆盖大部分课程设计和科研验证场景。参数常用取值作用说明节点数 n100部署在200m×200m区域初始能量 E00.5 J太小看不到趋势太大仿真跑太久Cprob0.05理想簇头比例pmin1e-4候选概率下限最大迭代次数20建簇阶段的子迭代上限Eelec50 nJ/bit收发电路能耗Efs10 pJ/bit/m²自由空间模型功放系数Emp0.0013 pJ/bit/m⁴多径衰减模型功放系数数据包大小4000 bit成员发给簇头融合后包大小4000 bit簇头发给sink融合不省包长但省节点能耗这套参数不是拍脑袋定的是几十次仿真后调到“不慢、不死太快、能看到完整生命周期”的折中。如果你发现自己的网络50轮就死光了先把E0调到1J再看趋势如果2000轮还一个节点没死基本都是能量扣减逻辑出了问题参见第五章。3. MATLAB实现HEED算法数据结构、邻居发现与迭代选举3.1 环境与初始化先装好MATLAB再用矩阵思路建场环境方面HEED仿真实在有MATLAB就能跑工具箱都不需要额外装。R2016a到R2023b我都跑过没有任何版本绑定如果你正在参照matlab安装教程装新版本装R2023b就行稳定性好。很多老代码在R2023b里打开会出现中文注释乱码这个坑放到第五章细说这里先建议你新建脚本时统一用英文注释省得后面乱码影响心情。初始化阶段的核心是“矩阵化”思维。节点坐标、剩余能量、邻居关系全用向量和矩阵表达比用struct数组快一个量级而且后续做向量化运算时不用写一堆for循环。下面这段代码是建场和初始化的基础% 场景参数 n 100; % 节点数 area 200; % 区域边长 200m sink [100, 250]; % sink节点坐标放在区域上方 E0 0.5; % 初始能量 0.5J Cprob 0.05; % 初始候选概率 pmin 1e-4; % 最小候选概率 % 节点部署在200x200区域内均匀随机分布 pos area * rand(n, 2); % 初始能量 E E0 * ones(n, 1); % 通信半径影响邻居数量 range 100; % 距离矩阵dist(i,j)表示节点i到j的距离 dist sqrt((pos(:,1) - pos(:,1)).^2 ... (pos(:,2) - pos(:,2)).^2);这段代码里的核心是dist矩阵它是后面所有逻辑的地基。pos(:,1) - pos(:,1)利用MATLAB的隐式扩展一次性算出所有节点对的横坐标差纵坐标同理然后开方就得到完整的距离矩阵。注意dist(i,i)是0后面找邻居时要做过滤。E0单位是焦耳0.5J对100个节点来说能撑几百轮够观察完整生命周期。3.2 邻居发现不建邻接表后面的迭代全是空中楼阁HEED选举里每个节点都要知道“我能跟谁通信”这个信息从哪来从邻居表来。邻居发现不是单独跑一个协议而是根据通信半径直接计算只要两个节点距离小于range就互为邻居。MATLAB里用逻辑索引和数组操作就能高效完成% 初始化邻居元胞数组每个元素存一个节点的邻居编号 neighbor cell(n, 1); for i 1:n % 距离大于0说明不是自己小于range说明在通信半径内 neighbor{i} find(dist(i,:) 0 dist(i,:) range); end % 邻居数直方图快速评估range设置是否合理 neighbor_cnt cellfun(numel, neighbor); fprintf(平均邻居数: %.1f, 最小邻居数: %d\n, ... mean(neighbor_cnt), min(neighbor_cnt));邻居发现的坑不在代码本身而在range的取值。range设太小比如40m统计最小邻居数可能直接是0后面迭代建簇时这些孤立节点谁也联系不上数据永久发不出去range设太大比如200m所有节点互为邻居AMRP失去区分度簇头会在小范围内扎堆。我调试时的经验是100个节点在200m×200m区域里range取80到120m之间保证平均邻居数在10到20之间网络连通性最好。诊断时不需要猜看上面打印的平均邻居数和最小邻居数两个统计量就够。3.3 迭代选举的MATLAB实现把协议伪代码翻成状态机迭代选举是HEED最核心也最绕的部分。我的实现里给每个节点定义了四种状态0表示未定、1表示临时簇头、2表示最终簇头、3表示普通节点。每轮子迭代分三步走先按CHprob概率把一部分未定节点提升为临时簇头然后所有节点计算到当前临时簇头的AMRP选代价最小的挂靠最后对比自己和邻居的AMRP决定是留在临时簇头状态还是升级为最终簇头。function [head_flag, cluster_id] heed_elect(E, E0, ... pos, neighbor, Cprob, pmin, max_iter) n numel(E); state zeros(n, 1); % 0未定, 1临时簇头, 2最终簇头 cluster_id zeros(n, 1); % 记录每个节点挂靠的簇头编号 active E 0; % 死亡节点不参与选举 % 候选概率与剩余能量成正比 CHprob max(Cprob * E(:) / E0, pmin); CHprob(~active) 0; for it 1:max_iter % 第一步未定节点按概率成为临时簇头 rnd rand(n, 1); become_temp state 0 active rnd CHprob; state(become_temp) 1; % 收集当前所有临时簇头编号 temp_heads find(state 1); if isempty(temp_heads) warning(第 %d 次迭代无临时簇头提前终止, it); break; end % 第二步计算每个节点到各临时簇头的可达功率 % 使用自由空间模型P k * d^2k为常数这里只比较相对值 for i 1:n if ~active(i), continue; end if state(i) 2, continue; end % 最终簇头不用挂靠 d sqrt(sum((pos(i,:) - pos(temp_heads,:)).^2, 2)); [min_p, idx] min(d.^2); % 功率与距离平方成正比 cluster_id(i) temp_heads(idx); state(i) 3; % 先标记为普通节点 end % 第三步临时簇头之间互相比代价代价低的升级 for h temp_heads if ~active(h), continue; end % 找同竞争区内的其它临时簇头 rivals intersect(neighbor{h}, temp_heads); rivals(rivals h) []; if isempty(rivals) state(h) 2; % 没有竞争者直接当选 continue; end % 计算本簇头到竞争者的代价比自己到任何竞争者都低则当选 d_self sqrt(sum((pos(h,:) - pos(rivals,:)).^2, 2)); if all(d_self.^2 min(d_self.^2)) state(h) 2; end end % 收敛检测如果所有存活节点都处于最终簇头或普通状态 temp_left sum(state 1 active); if temp_left 0 break; end end head_flag (state 2); end这个函数有几个地方值得展开说。第一CHprob(~active) 0这行是必须的死亡节点绝对不能参与选举否则会在随机数阶段被选为临时簇头浪费一轮子迭代。第二第三步的升级条件我做了简化节点只和自己的邻居集合里的临时簇头竞争这是HEED原始思想里的局部竞争而不是全网络比较。全网络比较会让簇头过于集中失去分布式协议的意义。第三min(d_self.^2)这个比较在节点自身距离为0时会恒成立所以代码里先通过rivals h排除了自己否则每个临时簇头都会以为自己代价最低。3.4 稳定传输与能量扣减先接收再发送别重复扣建簇完成后进入稳定传输阶段。每个普通节点在分配到的TDMA时隙内把4000bit数据包发给自己的簇头簇头接收所有成员的数据后做融合再统一转发给sink。这一阶段唯一的复杂点就是能量模型的扣减逻辑。无线传感器网络仿真里常用一阶无线模型发送方能耗由电路能耗和功放能耗组成收方能耗只有电路能耗。Eelec 50e-9; % 50 nJ/bit收发电路 Efs 10e-12; % 10 pJ/bit/m^2自由空间功放 Emp 0.0013e-12; % 0.0013 pJ/bit/m^4多径功放 EDA 5e-9; % 数据融合能耗 pkt_len 4000; % 数据包长度 4000 bit d0 sqrt(Efs / Emp); % 距离阈值约 87m % 发送4000bit到距离d处的节点 function cost tx_energy(d, pkt_len) if d d0 cost pkt_len * Eelec pkt_len * Efs * d^2; else cost pkt_len * Eelec pkt_len * Emp * d^4; end end % 接收4000bit function cost rx_energy(pkt_len) cost pkt_len * Eelec; endd0 sqrt(Efs / Emp)约等于87m这是自由空间模型和多径衰减模型的切换点。距离小于87m时信号衰减按d²算大于87m时地面反射等多径效应开始主导衰减按d⁴算。这个阈值不是常数它会随Efs和Emp的取值变化所以代码里用公式动态算而不是硬编码87。扣能量时的顺序我坚持一条原则先处理完所有接收再处理所有发送最后扣融合。如果一边收一边扣会出现节点能量在轮中降到0后还在发数据的情况能量变负都发现不了。4. HEED仿真结果的四个评估指标网络寿命、能耗均衡与簇头数4.1 第一个死亡节点轮数FND网络寿命最容易翻车的定义网络生命周期这个指标在论文和代码里经常被不同定义搞混。有的用“第一个节点死亡轮数”缩写FND有的用“10%节点死亡轮数”还有的定义成“最后一个节点死亡轮数”。这三个数字放到同一份仿真结果里能差出50%以上所以你在报告或论文里写“网络寿命提升了20%”之前必须先声明你用哪个定义。我在自己的仿真里同时记录FND、HND一半节点死亡和LND最后一个节点死亡。统计方法很简单每轮结束后检查存活节点数记录第一次小于某个阈值的轮数alive E 0; alive_cnt sum(alive); % 记录首个死亡节点轮数 if alive_cnt n fnd 0 fnd r; end % 记录半数死亡轮数 if alive_cnt n * 0.5 hnd 0 hnd r; end % 记录全部死亡轮数 if alive_cnt 0 lnd 0 lnd r; endFND对网络设计者意义很大因为很多应用场景下死一个节点就代表监测区域出现盲区LND则反映协议的极限性能。HEED做得好不好主要看FND和LND之间的间隔间隔越小说明能量消耗越均匀没有出现某个区域节点成群死亡的情况。4.2 总剩余能量曲线与能耗标准差判断能量是否“用均匀了”只看寿命长短不够还要看能量怎么耗尽。总剩余能量曲线是一条从n×E0单调下降到0的折线曲线的形状能暴露问题如果下降速度越来越快说明后期有大量节点在长距离通信功放能耗占据主导如果接近线性说明网络负载比较均匀。我习惯每轮算一次sum(E)画出来观察趋势。更敏感的是能耗标准差。每一轮记录所有节点的剩余能量那这些标准差的含义是节点间能量的不均衡度。标准差越大说明节点们消耗速率差异越大有些节点可能从第一轮到最后一轮都是邻居的“接盘侠”。每轮计算方式energy_std(r) std(E);正常情况下HEED的能耗标准差应该远小于LEACH这是它的卖点。如果跑出来的结果标准差很大先怀疑建簇阶段有没有收敛再看簇头到sink的距离分布是否均匀。4.3 每轮簇头数量选举收敛性的直接证据簇头数量这个指标很容易被忽视但它最能反映选举机制的收敛质量。理论期望值大约是n×Cprob也就是100个节点乘0.05等于5个。实际运行中会有波动但波动范围应该在±3以内。如果某轮突然冒出15个簇头或者连续几轮只有1个簇头都说明选举逻辑异常。画法很简单plot(1:rounds, head_count_hist, LineWidth, 1); xlabel(轮数); ylabel(簇头数); ylim([0, 20]);正常结果里簇头数曲线应该像一条在5附近波动的噪声带偶尔有尖峰但能快速回落到正常范围。如果尖峰频繁出现且居高不下回头检查heed_elect里的CHprob更新逻辑——概率应该随能量下降而递减不递减说明Eresidual/Einitial那部分写成了常数。另一个常见误用是Cprob取值太大0.2以上的Cprob会让每轮簇头数长期在15到30之间能量消耗速度成倍增长。4.4 把主循环串起来一份可改参数的完整运行骨架前面几节是散件这里把它们组装成完整主循环。这份代码设计成可以直接新建脚本粘贴运行跑完自动画出三条关键曲线rng(42); % 固定随机种子保证结果可复现 % 参数区 n 100; area 200; sink [100, 250]; E0 0.5; Cprob 0.05; pmin 1e-4; max_iter 20; rounds 2000; range 100; % 初始化 pos area * rand(n, 2); E E0 * ones(n, 1); dist sqrt((pos(:,1) - pos(:,1)).^2 ... (pos(:,2) - pos(:,2)).^2); neighbor cell(n, 1); for i 1:n neighbor{i} find(dist(i,:) 0 dist(i,:) range); end % 统计数组 alive_hist zeros(rounds, 1); energy_std_hist zeros(rounds, 1); head_count_hist zeros(rounds, 1); % 主循环 for r 1:rounds % 建簇 [head_flag, cluster_id] heed_elect(E, E0, pos, ... neighbor, Cprob, pmin, max_iter); % 稳定传输普通节点发给簇头簇头发给sink for i 1:n if E(i) 0, continue; end if head_flag(i) % 簇头向sink发送融合后数据 d2sink sqrt(sum((pos(i,:) - sink).^2)); E(i) E(i) - tx_energy(d2sink, pkt_len); else % 普通节点发给自己的簇头 h cluster_id(i); if h 0 E(h) 0 d2head sqrt(sum((pos(i,:) - pos(h,:)).^2)); E(i) E(i) - tx_energy(d2head, pkt_len); E(h) E(h) - rx_energy(pkt_len); end end end % 统计 alive_hist(r) sum(E 0); energy_std_hist(r) std(E); head_count_hist(r) sum(head_flag); % 能量下限保护 E(E 0) 0; end % 绘图 subplot(3,1,1); plot(1:rounds, alive_hist); ylabel(存活节点数); subplot(3,1,2); plot(1:rounds, head_count_hist); ylabel(簇头数); subplot(3,1,3); plot(1:rounds, energy_std_hist); xlabel(轮数); ylabel(能量标准差);这个主循环里的一个细节是E(h) 0判断它放在普通节点发送前。如果簇头已经死亡成员节点不会再向它发数据否则能量会扣到一个负数上破坏整条曲线。实际协议里成员节点会等待簇头广播确认但仿真里为了效率直接用条件判断。head_flag和cluster_id两个返回值贯穿整轮是统计簇头数和判断挂靠关系的依据。5. HEED算法仿真避坑5条血泪经验5.1 中文注释乱码MATLAB 2023b编码不一致的坑现象代码注释里中文全部变成乱码字符串里如果有中文直接报错。我在R2023b上打开以前用R2016a写的脚本时遇到过一次满屏的“鈥斺€斺€”让我以为脚本被损坏了。原因老版本MATLAB默认用系统区域编码Windows下常见GBK保存.m文件R2023b默认用UTF-8读取。编码不一致导致解码错乱。解决在MATLAB编辑器里重新打开文件用“另存为”编码选UTF-8如果乱码已经发生没有后悔药只能找备份。更省事的方案是新建脚本时全部用英文注释避免一切编码问题。初学者做毕业设计如果非要中文注释把MATLAB更新后的编辑器编码默认值改成“系统区域”也能缓解但我后来还是全英文注释省心。5.2 首轮簇头爆炸Cprob和迭代次数同时背锅现象100个节点的网络第一轮簇头数统计出来有42个远超期望的5个。后续每轮簇头数持续偏高网络寿命比论文里的结果短40%以上。原因Cprob设得过大或者迭代次数上限太小。Cprob0.3时每轮大约30%的节点有资格自荐为临时簇头即使竞争之后会淘汰一部分最终簇头数依然远高于预期。迭代次数太小节点还没收敛到最终状态就被强制进入稳定传输阶段导致大量临时簇头直接转正。解决把Cprob从0.05起步观察前10轮的平均簇头数目标在3到8之间迭代次数至少要15次我统一设20。调试时在heed_elect里加一行fprintf打印每次迭代的临时簇头数如果连续多轮不减少大概率是第三步的竞争条件写错了。5.3 孤立节点兜底逻辑没有它覆盖率和数据量双双失真现象存活节点数曲线下降得很慢看起来网络状态良好但统计发送到sink的数据包数量发现每轮平均只收到70多个包而不是期望的100个。原因部分节点在建簇阶段没能加入任何簇头也没能自荐为簇头变成孤立节点。孤立节点不会消耗能量也没法发送数据它出现在存活曲线里只会让你误判网络健康。解决在heed_elect函数末尾加一段兜底逻辑——查找所有既不是簇头也没有簇头归属的节点让它们加入距离最近的簇头如果整个网络都不存在簇头就让剩余能量最高的孤立节点自封簇头。代码就几行% 兜底未入簇节点加入最近簇头 orphan find(active cluster_id 0 ~head_flag); if ~isempty(orphan) heads find(head_flag); if ~isempty(heads) for o orphan d sqrt(sum((pos(o,:) - pos(heads,:)).^2, 2)); [~, idx] min(d); cluster_id(o) heads(idx); end else % 无簇头时能量最高的孤立节点自封 [~, idx] max(E(orphan)); head_flag(orphan(idx)) true; cluster_id(orphan) orphan(idx); end end这段代码能把网络的实际覆盖率拉回到100%但它也会多消耗一些能量所以它不能算优化而是仿真真实性的必要组成部分。真实网络里孤岛节点会加大发射功率直接连sink暂时不讨论这个概念但至少仿真得给出合理的覆盖数量。5.4 剩余能量成负数能量扣减顺序与死亡节点清理现象跑完2000轮后检查E向量发现好几个节点的能量是-0.02J。负能量在物理上不存在它代表某个节点的能量扣减次数超过了它实际参与的活动次数。原因最常见的场景是普通节点发送数据时簇头已经在前一轮死亡但普通节点的邻居表里还保留着这个簇头的编号。第二个常见原因是能量扣减顺序颠倒——先扣了发送再扣接收结果发送节点在接收事件来临之前能量已经归零按负数继续扣。解决主循环里每轮对E(E 0) 0做一次下限保护但这只是治标。治本的方案是给每个节点维护一个active状态节点能量归零的瞬间就从邻居表和轮转列表里移除。heed_elect里已经用active E 0过滤了死亡节点但主循环里的簇头选择没有这个保护需要加。5.5 不固定随机种子同参数两次运行寿命差一半现象同样的参数第一次跑FND是102轮第二次重跑却变成147轮第三次又变成88轮。每个指标抖得跟股票一样论文里根本没法写。原因MATLAB的全局随机数流没有固定种子每次运行rand生成的点位分布和概率判定结果都不同。HEED选举本身带随机性再加上节点部署的随机性两次运行的网络拓扑可能完全不一样。解决脚本第一行写rng(42)固定种子。42这个数字没有特殊含义可以随便换但一旦定下来跑对比实验就不要再改。严谨一点的做法是在所有参数固定的基础上跑10次每次用不同种子最终报告平均值和标准差。我给自己定的规矩是调参阶段用rng(42)快速迭代出结果阶段注释掉种子跑多组取均值这样既能复现又能体现随机性。6. 用验证方法收尾给HEED仿真加一个自检6.1 参数扫描发现拐点收敛性看簇头数仿真跑通以后最值得做的验证是参数扫描。固定其他所有参数把Cprob从0.02逐步调到0.1记录每个取值下的FND和能耗标准差。你会看到FND曲线有一个先升后降的峰值峰顶对应的Cprob就是当前场景下的最优配置。Cprob太小簇头数不足普通节点长距离传输导致能耗不均太大簇头自身开销膨胀同样缩短寿命。这个拐点不画图靠猜是猜不出来的。另一个自检手段是观察迭代收敛性。给heed_elect加一个可选输出参数返回每轮的收敛次数画出来看绝大多数轮应该在第3到第8次迭代内收敛超过15次的轮占比不应超过5%。如果收敛次数整体偏高说明AMRP竞争条件太激烈回头检查邻居表的规模和Cprob的配合。6.2 三行自检代码让错误在运行期炸出来仿真最怕的不是报错而是错误被悄悄吞掉。我习惯在主循环末尾放三条断言assert(all(E 0), 存在负能量节点); assert(sum(head_flag) 1, 当前轮无簇头); assert(length(unique(cluster_id(cluster_id 0))) n, 簇头编号越界);第一行防负能量第二行防全网无簇头的灾难场景第三行防数组索引越界。断言失败时程序会直接中断并带出错误信息比事后翻曲线图找异常高效得多。这些断言在最终跑批次实验时可以注释掉但调参阶段必须保留。这套HEED仿真代码我从头到尾重写过三次每次重写都因为调参时发现某个环节的逻辑和协议原始思想差了一点。做这类仿真最值钱的经验就是不要盲目抄别人的主循环按“状态机”的方式组织自己的选举逻辑然后让指标曲线帮你检查实现是否正确。希望帮到你。本文还有配套的精品资源点击获取
返回列表