2. 从仿真的视角重新审视WSN路由协议的价值
做无线传感器网络(WSN)方向的人,大概率都绕不过LEACH这个名字。作为分簇路由协议的鼻祖,LEACH从1990年代末被提出至今,几乎成了所有WSN路由研究论文的基线。但如果你只把它当作一个基线,那就浪费了它真正的价值——LEACH系列协议是理解整个WSN节能路由思想脉络的最佳入口。
我自己跑完LEACH、LEACH-C和TS-I-LEACH三个协议的Matlab对比仿真之后,最大的感受是:这三个协议放在一起看,本质上就是一条清晰的演进线——从“分布式随机选簇头”到“集中式全局优化选簇头”,再到“引入TS模糊逻辑的动态阈值调整”。理解这条线,比单纯调参数背结果重要得多。
这篇博文我会把三个协议的核心机制、改进逻辑、Matlab实现思路和对比实验设计完整拆开讲,给出可以直接跑通的代码框架和参数配置,也会聊一些仿真过程中容易被忽略的坑。适合正在做WSN路由研究、需要快速搭仿真环境跑基线对比的同学参考。
3. 为什么是LEACH、LEACH-C和TS-I-LEACH?先从选型逻辑说起
在做任何仿真对比之前,先想清楚一个问题:你为什么要选这几个协议作为对比对象?如果只是“文献里大家都这么比”,那你很难从结果里读出有深度的结论。
3.1 三个协议代表的三个技术方向
LEACH(Low-Energy Adaptive Clustering Hierarchy)解决的核心问题是:如何让全网节点能耗尽量均匀,避免某些节点因转发任务过重而提前死亡。它的答案是“轮替”——每一轮随机选一批簇头,让能耗负担在所有节点间轮流承担。但随机带来的副作用是,簇头分布可能不均匀,某些簇头可能离基站极远、能耗过高。
LEACH-C(LEACH-Centralized)的改进思路很直接:把簇头选举从“节点自己拍板”改成“基站统一规划”。基站收集全网节点的位置和剩余能量信息,用模拟退火等算法找出一组更优的簇头组合。这个思路的本质是把分布式决策集中化,换取更优的全局结果。
TS-I-LEACH走的则是另一条路。名字里的“TS”通常对应Takagi-Sugeno模糊逻辑系统,I代表Improvement。它的核心思想是:不再用固定阈值来决定节点是否成为簇头,而是把节点的剩余能量、到基站的距离、邻居密度等因素输入模糊推理系统,动态计算每个节点当簇头的概率。这是从“单一随机变量决策”向“多因素综合决策”的跨越。
3.2 选择这三个做对比的底层逻辑
这三者恰好构成一个递进关系:
- LEACH是最原始的分布式随机决策,计算开销最小,但结果稳定性差;
- LEACH-C引入全局信息和集中计算,决策质量更高,但代价是每轮需要节点向基站上报状态,额外通信开销不可忽略;
- TS-I-LEACH保留了分布式决策的低开销优势,同时引入了更精细的决策机制,尝试在“全局最优”和“分布式代价”之间找平衡点。
所以用这三个协议做对比,能清晰回答一个问题:在不同网络规模、不同基站位置、不同初始能量条件下,你到底应该选“简单随机”“集中优化”还是“智能决策”?
4. 动手之前必须先吃透三个协议的核心机制
很多同学喜欢先跑代码再读论文,结果调了半天参数不知道在调什么。我个人建议反过来:先用一两小时把协议流程梳理清楚,再上手仿真,效率高得多。
4.1 LEACH的基本流程和阈值公式
LEACH以“轮”(Round)为周期运行,每轮分为建簇阶段(Set-up Phase)和稳态传输阶段(Steady-state Phase)。
建簇阶段的核心是簇头选举。每个节点生成一个0到1之间的随机数,如果这个数小于当前轮次的计算阈值,则该节点当选簇头。阈值公式为:
T(n) = p / (1 - p * (r mod (1/p))),当 n ∈ G T(n) = 0,其他情况其中p是期望的簇头比例(通常取0.05),r是当前轮数,G是最近1/p轮内未当选过簇头的节点集合。
这个公式的巧妙之处在于:随着轮次推进,未当选过的节点集合越来越小,阈值越来越大,从而保证每个节点在一轮周期内都有机会担任簇头——这就是“轮替公平性”的来源。
当选簇头后,节点广播ADV消息通告身份,普通节点根据信号强度选择加入最近的簇头,簇头为簇内成员分配TDMA时隙,进入稳态阶段,簇头聚合数据后单跳发送给基站。
4.2 LEACH-C如何用集中式选簇代替随机选簇
LEACH-C的核心变化在第1轮开始前的“Setup”阶段。每个节点将自己的位置(通过GPS或定位算法获得)和剩余能量上报给基站。基站收集所有节点信息后,用模拟退火算法求解一个优化问题:在满足簇头数量要求的前提下,找到一组使得所有节点到各自簇头距离平方和最小的簇头组合。
模拟退火的目标函数通常取:
f = Σ(min(d(i, CH_j))²) + 能量约束惩罚项其中d(i, CH_j)是节点i到簇头j的距离。
这个方案的优势是选出的簇头组合在空间分布上更均匀,避免了LEACH中“几个簇头挤在一起,大片区域没有簇头”的尴尬局面。但代价是协议依赖基站做全局计算,且每轮都要额外通信上报状态,当网络规模变大或基站距网络较远时,这部分开销会显著上升。
4.3 TS-I-LEACH的模糊逻辑决策机制
TS-I-LEACH对簇头选举做了更细粒度的改造。它不再用单一阈值T(n)与随机数比对,而是将多个影响因素作为模糊系统的输入:
- 剩余能量水平:节点当前能量占初始能量的比例;
- 到基站的距离:距离越近,当簇头的倾向越高;
- 局部节点密度:该节点通信半径内邻居数量,密度越大越适合做簇头。
这三个输入经过模糊化、规则推理、去模糊化后,输出一个介于0~1之间的“簇头竞选倾向值”。该值再与基础阈值T(n)结合,决定节点最终是否当选簇头。
模糊规则的设计举例(以两输入简化版为例):
| 剩余能量 | 到基站距离 | 簇头倾向 |
|---|---|---|
| 高 | 近 | 很高 |
| 高 | 远 | 中 |
| 低 | 近 | 中 |
| 低 | 远 | 很低 |
每轮结束后,节点根据本轮实际能耗重新计算剩余能量,更新模糊输入,进入下一轮选举。也就是说,TS-I-LEACH把“静态阈值决策”变成了“动态多因素决策”,让簇头选举更贴近每轮网络的真实状态。
4.4 三个协议的关键差异都在哪
把三者放在同一张表里看更直观:
| 对比维度 | LEACH | LEACH-C | TS-I-LEACH |
|---|---|---|---|
| 簇头选择方式 | 分布式随机阈值 | 基站集中式模拟退火 | 分布式模糊逻辑决策 |
| 每轮额外开销 | 低 | 高(节点上报状态) | 低 |
| 决策输入因素 | 单一随机数 | 位置+能量 | 能量+位置+密度 |
| 全局信息依赖 | 无 | 强依赖 | 弱依赖 |
| 实现复杂度 | 低 | 中 | 较高 |
| 网络寿命影响 | 基准 | 优于LEACH(通常) | 取决于模糊规则设计 |
仿真中你会看到,LEACH-C的网络稳定期通常比LEACH长,但每轮的能耗步长可能不均衡;TS-I-LEACH的能耗曲线更平缓,网络死亡时间点延迟明显,但编程实现的坑也多不少。
5. Matlab仿真实现:从零搭一套三协议对比环境
这一节是全文里我可以直接“抄作业”价值最高的部分。我会按我实践过的流程,把代码结构和关键参数设计讲清楚。你拿到这套框架,改一改就能用在其他改进协议上。
5.1 实验场景和参数初始化
我的实验场景设置是:100个节点随机分布在100m x 100m区域内,基站位于坐标(50, 150),即区域上方50m处。节点初始能量0.5J,数据包大小4000bit,控制包大小100bit,能耗参数采用经典无线信道模型参数。
初始化代码示意:
% 网络参数 numNodes = 100; % 节点总数 SinkX = 50; SinkY = 150; % 基站坐标 AreaSize = 100; % 区域边长 initEnergy = 0.5; % 初始能量(Joule) packetLength = 4000; % 数据包大小(bit) ctrlPacketLength = 100; % 控制包大小(bit) p = 0.05; % 期望簇头比例 roundMax = 5000; % 最大仿真轮数 % 节点结构初始化 nodes = struct('id', [], 'x', [], 'y', [], 'energy', [], 'isCH', false, ... 'cluster', 0, 'alive', true); for i = 1:numNodes nodes(i).id = i; nodes(i).x = rand * AreaSize; nodes(i).y = rand * AreaSize; nodes(i).energy = initEnergy; end需要特别注意初始随机种子。仿真结果对随机数极其敏感,同一个协议换不同的随机种子,网络寿命可能相差20%以上。所以对比实验必须使用相同的节点分布和相同的随机数种子,否则你比较的根本不是协议优劣,而是随机噪声。
建议的做法是:在主脚本开头设定rng固定种子,所有协议共用这份节点初始布局,再用蒙特卡洛多次运行取平均值。单次仿真结果说服力很弱。
5.2 LEACH主循环实现
LEACH的仿真主循环大致如下:
for r = 1:roundMax % 每轮初始:判断存活节点数量 aliveCount = sum([nodes.alive]); if aliveCount < numNodes * 0.1 break; % 网络死亡判定 end % 簇头选举 electedCHs = []; for i = 1:numNodes if nodes(i).alive && nodes(i).energy > 0 if nodes(i).energy < 0.01 * initEnergy continue; % 能量过低的节点不应担任簇头 end randomVal = rand; threshold = computeThreshold(i, r, p, nodes); if randomVal <= threshold nodes(i).isCH = true; electedCHs = [electedCHs i]; end end end % 普通节点选择簇头:按距离最近原则 for i = 1:numNodes if ~nodes(i).isCH && nodes(i).alive minDist = inf; chosenCH = 0; for j = electedCHs d = sqrt((nodes(i).x-nodes(j).x)^2 + (nodes(i).y-nodes(j).y)^2); if d < minDist minDist = d; chosenCH = j; end end nodes(i).cluster = chosenCH; end end % 能耗计算:发送、接收、聚合、传输 for i = 1:numNodes if nodes(i).alive if nodes(i).isCH % 簇头能耗:接收簇成员数据 + 聚合 + 发给基站 numMembers = sum([nodes.cluster] == i); energyRecv = numMembers * packetLength * Eelec; energyAgg = (numMembers + 1) * packetLength * EDA; distToSink = sqrt((nodes(i).x-SinkX)^2 + (nodes(i).y-SinkY)^2); energySend = packetLength * Eelec + packetLength * Efs * distToSink^2; nodes(i).energy = nodes(i).energy - (energyRecv + energyAgg + energySend); else % 普通节点能耗:发送数据给簇头 distToCH = sqrt((nodes(i).x-nodes(chosenCH).x)^2 + (nodes(i).y-nodes(chosenCH).y)^2); energySend = packetLength * Eelec + packetLength * Efs * distToCH^2; nodes(i).energy = nodes(i).energy - energySend; end if nodes(i).energy <= 0 nodes(i).alive = false; end end end % 记录数据:每轮存活数、网络剩余总能量、首个节点死亡轮数等 end阈值计算函数要特别注意“轮替窗口”逻辑:
function T = computeThreshold(nodeId, r, p, nodes) % G集合:最近1/p轮未当过簇头的存活节点 global lastCHRound; if r - lastCHRound(nodeId) >= 1/p && nodes(nodeId).alive T = p / (1 - p * mod(r, 1/p)); else T = 0; end end最后一个细节值得写清楚:普通节点的能耗公式中使用d^2路径损耗模型是合理的,因为簇内通信距离较短,信道模型接近自由空间;而簇头到基站的距离可能很大,这时候使用d^4衰减模型更符合实际。在两距离公式中需要一个阈值交叉距离d0来切换,这个参数在仿真中很关键。
5.3 LEACH-C的集中式实现要点
LEACH-C多了一个“基站执行模拟退火选簇头”的阶段。每轮开始时,基站收集所有节点的位置和能量信息,通过模拟退火搜索最优簇头组合:
function [bestCHs] = simulatedAnnealing(nodes, numCHs, SinkX, SinkY) currentNodeSet = randsample(find([nodes.alive]), numCHs); currentCost = computeCost(currentNodeSet, nodes); temperature = 100; coolingRate = 0.95; while temperature > 1 % 随机交换一个候选簇头 newNodeSet = currentNodeSet; swapIdx = randi(numCHs); candidatePool = find([nodes.alive] & ~ismember(1:numNodes, newNodeSet)); newNode = randsample(candidatePool, 1); newNodeSet(swapIdx) = newNode; newCost = computeCost(newNodeSet, nodes); delta = newCost - currentCost; if delta < 0 || exp(-delta/temperature) > rand currentNodeSet = newNodeSet; currentCost = newCost; end temperature = temperature * coolingRate; end bestCHs = currentNodeSet; end function c = computeCost(CHs, nodes) % 代价函数:所有节点到最近簇头距离平方和 + 能量惩罚 c = 0; for i = 1:length(nodes) if nodes(i).alive minD = inf; for j = CHs d = sqrt((nodes(i).x-nodes(j).x)^2 + (nodes(i).y-nodes(j).y)^2); if d < minD, minD = d; end end c = c + minD^2; % 剩余能量低于平均值的节点若被选为簇头,增加惩罚 if ismember(i, CHs) && nodes(i).energy < 0.2 * initEnergy c = c + 500; % 惩罚系数需要根据场景调 end end end end这里切记:模拟退火迭代次数太少会导致搜索结果不稳定,太多则每轮仿真耗时急剧上升。我实践下来的平衡点是:温度初值100,冷却率0.9~0.95,约迭代100~200次即可收敛到较优解。如果你的网络节点较多,可以适当增大初始温度和冷却次数。
5.4 TS-I-LEACH模糊逻辑部分怎么落地
TS-I-LEACH的核心代码在模糊推理系统。Matlab自带Fuzzy Logic Toolbox可以用,但缺点是仿真循环里反复调用evalfis性能较差,而且工具箱的隶属度函数/规则设置不够透明。我更推荐直接用隶属度函数公式手写推理过程。
简化的三输入模糊系统可以分两步走:
第一步,定义隶属度函数。以剩余能量(normalized至0~1)为例,可以使用梯形/三角形隶属度函数:
% 剩余能量: Low [0 0 0.25 0.4], Medium [0.25 0.45 0.65], High [0.6 0.8 1 1] % 到基站距离: Near [0 0 0.3 0.45], Medium [0.25 0.5 0.7], Far [0.6 0.85 1 1] % 节点密度: Sparse [0 0 0.3 0.45], Medium [0.25 0.5 0.7], Dense [0.6 0.85 1 1]第二步,实现去模糊化输出。以重心法为例:
mamdani_output = sum(ruleStrength .* centroidValue) / sum(ruleStrength); % 将输出映射到[0,1],再与阈值T(n)结合: threshold = T_base * (0.5 + 0.5 * fuzzyOutput); if rand <= threshold node当选簇头; end这里要特别提醒:TS-I-LEACH的“改进”效果高度依赖你的模糊规则表设计和隶属度函数参数。如果规则设计不合理,性能甚至可能比原版LEACH更差——这不是协议的问题,是你参数的问题。建议先用直觉设计规则(能量高倾向选,距离近倾向选,密度大倾向选),跑通流程后再做参数敏感性分析,把规则和隶属度函数的边界参数作为变量去扫,看网络寿命的响应曲线。
5.5 输出指标和曲线绘制
对比仿真至少需要输出四类数据:
- 存活节点数随轮数变化曲线:观察网络稳定期和衰退期的差异;
- 网络剩余总能量随轮数变化曲线:反映能耗速率;
- 首个节点死亡轮数(FND,First Node Dies):衡量协议能否保护网络覆盖完整性;
- 半数节点死亡轮数(HND,Half Node Dies)和全部节点死亡轮数(LND):综合判断网络寿命。
绘图建议用subplot将四条曲线放在同一图内,横轴统一为轮数,纵轴分别对应各指标。对比时颜色区分三个协议,图例标注清晰。
6. 仿真结果怎么分析才叫有说服力
很多论文里的仿真结果就是“三条线下行,我的协议在最上面”。这远远不够。审稿人真正关心的是:你能不能说清楚为什么你的协议在最上面?代价是什么?在什么条件下这个优势会消失?
6.1 三类典型结果信号
第一类信号:FND轮数提前但LND延期。这说明协议更注重保护“边缘节点”或“低能量节点”,让它们不至于过早耗尽。代价是可能牺牲部分高能量节点的使用效率。
第二类信号:网络剩余总能量曲线斜率平稳。这代表能耗速率均匀,没有明显的突发大能耗轮次。TS-I-LEACH的目标就是这类效果。
第三类信号:簇头数量波动大。LEACH每轮簇头数可能从2个跳到12个,而LEACH-C因为基站全局规划,波动通常较小。簇头数量不稳定会直接导致能耗不均衡,这是LEACH的先天短板。
6.2 一个值得警惕的误区
LEACH-C虽然每轮的簇头组合更优,但不代表它一定比LEACH更节能。原因在于:LEACH-C需要每个节点每轮上报“位置+能量”信息,这个通信开销在小数据包场景下占比非常高。当数据包只有100bit而控制包也是100bit时,控制开销占了总流量的一半。所以你会看到,在部分参数设定下LEACH-C的每轮平均能耗反而更高,只是因为它选出的簇头位置好,数据转发的能耗节省抵消了控制开销。
你在分析结果时一定要把“控制开销”单独算出来,否则会把LEACH-C的优势或劣势归因到错误的原因上。我的建议是,在代码里单独维护一个controlCost变量,每轮控制包收发都累加,这样出结果时直接可以拆解“数据能耗”和“控制能耗”两部分。
6.3 实验参数敏感性分析
只跑一组参数所得出的结论不可靠。至少要做三组敏感性分析:
- 节点数量变化:50、100、150个节点,观察三个协议在不同密度下的表现排序是否稳定;
- 基站位置变化:区域中心、区域外部、区域角落,观察距离因素对协议的差异化影响;
- 初始能量变化:0.25J、0.5J、1J,观察能耗速率绝对值变化时协议的相对优劣势是否改变。
我实践中发现:基站离网络区域越远,LEACH-C的相对优势越明显,因为它选出的簇头能更有效地压缩到基站的数据传输距离;而TS-I-LEACH的距离因子如果没设计好,在基站远置时模糊系统会把“距离远”的判断过重,反而降低簇头竞选意愿,导致簇头数量不足,网络更快死亡。
7. 仿真避坑指南与实操经验复盘
7.1 随机种子和重复实验
系数对齐是仿真里最容易忽略但影响最大的坑。同一个协议用不同随机种子跑:FND轮数可能相差100轮以上,而你协议之间的差异可能只有50轮。这种情况下,单次仿真结论毫无统计意义。
我的标准做法是:每个协议跑20次蒙特卡洛,取存活数的平均值、标准误差,画带置信区间的曲线图。论文正文用平均曲线,误差棒放入附录支撑材料。
7.2 死亡节点处理的边界条件
“节点能量小于等于0算死亡”听起来很明确,但写代码时容易漏掉两个边界:
第一,节点在当选簇头的当轮消耗能量到负数,应该先按负能量记录,但在本轮它仍然承担簇头转发任务,不能中途退出;第二,死亡节点的数据包不能被其他节点转发,这会影响下一轮簇头接收数据数量的计算。建议每一轮结束后统一更新alive状态,下一轮开始前重新统计。
7.3 无线信道模型的选取
默认的两级信道模型在d0处有跳变,而d0的计算公式为:
d0 = sqrt(Efs / Emp)代入常见参数Efs = 10pJ/bit/m^2,Emp = 0.0013pJ/bit/m^4,d0约等于87.7m。如果基站距离节点超过87.7m,必须使用d^4衰减模型,否则能耗估计会严重偏小。你可以在代码里显式判断distToSink,如果大于d0就切换能耗公式。
7.4 簇头数量与p值的关系
p=0.05意味着每轮期望簇头数约5个(100节点时)。但实际中簇头数量会在2~10之间波动。p值过大会导致簇头过多、簇内成员过少、控制消息泛滥;p值过小会导致簇头负载过重、提前耗能死亡。3协议对比时,建议先用p=0.05做基准,再跑p=0.1和p=0.025两个变体,体察p值对不同协议的不同影响幅度。
7.5 仿真性能优化的两个建议
Matlab跑5000轮100节点的仿真,三个协议循环跑会很慢。两个优化技巧:
一是向量化。节点状态用结构体数组存,能耗更新尽量用矩阵运算。例如所有节点到簇头距离的计算,用一次矩阵广播替代for循环嵌套,能提速5~10倍。
二是预分配所有数组。循环中动态扩展数组会触发频繁内存重分配,仿真轮数多时影响不可忽略。建议所有记录指标数组一次性预分配为zeros(1, roundMax)。
7.6 一个对比实验的典型程序流程
写清楚总体的实验程序流程:
- 初始化网络:随机生成节点位置,固定随机种子,记录初始能量;
- 协议A(LEACH)循环运行直到网络死亡:执行簇头选举、建簇、数据收发能耗更新、状态统计;
- 协议B(LEACH-C)重复相同轮次:每轮基站收集信息,模拟退火选簇头,后续流程与LEACH相同;
- 协议C(TS-I-LEACH)重复相同轮次:节点用模糊系统计算簇头倾向阈值,后续流程与LEACH相同;
- 输出并保存三类指标序列;
- 更换随机种子,重复2-5步;
- 汇总多次运行结果,计算平均值与方差,绘图。
8. 关于这个仿真项目的一些补充思考
以我的实际体会来说,跑通三个协议的对比仿真只是第一步,真正的价值在于你能否通过仿真结果反过来修正对协议机制的理解。比如LEACH-C你写代码前可能会觉得集中式肯定比分布式好,但跑完数据后发现控制开销的占比可能超出预期;TS-I-LEACH你以为加个模糊逻辑就万事大吉,但模糊规则和隶属度函数的参数稍有偏差就可能导致簇头数量不足。带着这些具体的“意外”再去读文献里的改进方案,你会发现自己的判断力高了不止一个档次。
如果你后续要继续做多跳路由方向,这套Matlab框架可以直接扩展——把节点间单跳通信改成按最短路径树转发,或者加入移动节点模型,甚至和基于强化学习的路由决策做一个对比,都是很有价值的延伸方向。这套仿真框架本身就是后续所有路由实验的“地基”。
最后一个个人建议:做仿真实验,一定要及时保存每一组参数对应的原始数据,不要只留一张图。因为写论文时你可能需要换坐标系、换作图方式、补做某个指标的误差线,这些都是正式写作阶段经常会发生的事。有原始数据在手,就不需要重跑实验,能省下大量时间,还不容易出错。