☰
无线传感器网络LEACH协议仿真:簇头选举、能耗模型与避坑指南
2026/10/22 3:33:07 网站建设 项目流程

简介:LEACH(低能量自适应分层聚类)是无线传感器网络中经典的节能路由协议,这份MATLAB源码包面向通信工程、物联网相关专业的学生与研究者,旨在帮助复现并通过仿真分析该算法的分簇与能耗平衡机制,解决有限电池寿命下网络生命周期短与能量不均衡等核心问题。压缩包共含4个文件,全部为.m格式源码,整体仅8KB,文件体积小、结构集中;按主程序和功能脚本拆分为簇首选举、簇形成、数据聚合及簇首轮换等关键模块,运行后可观察各轮簇首选取、节点入簇及数据上传过程。已有276人学习下载,可作为课程设计、毕业设计或论文中路由对比实验的入门素材。读者通过修改节点数、轮次或能量参数,即可复现LEACH在环境监测、灾害预警等场景下的工作过程,也可将源码扩展为LEACH-C等改进算法,获得可量化的性能曲线和能耗数据。

1. LEACH 路由算法:凭什么靠随机轮转就能省出整网寿命

LEACH 路由算法应该是无线传感器网络方向最容易被轻视的基线协议:它诞生时间早,逻辑听起来也简单,一句话概括就是“全网分成若干簇,簇头轮着当”。但真正动手在仿真环境里复现过的人都清楚,簇头选举阈值怎么算、能量模型按哪个距离分界、一轮周期设多长,任何一个参数理解偏了,结果都会差出好几倍。这篇文章不讲泛泛的原理,只讲把 LEACH 从公式跑成活网络时需要走通的路径、需要盯紧的参数和容易踩的坑。适合三类读者:要拿 LEACH 当基线做对比的研究者、要给无线传感方案做能耗验证的工程人员,以及第一次接触分簇路由、想在本地跑出生命曲线的学生。

2. 簇头选举与轮转:LEACH 在一轮生命周期里的四步执行顺序

LEACH 与传统平面路由最大的区别不是“分簇”,而是“簇头会轮换”。如果簇头固定不变,那几个簇头会早早被累死,网络生命周期反而比不分簇更短。所以理解 LEACH,关键是理解一轮一轮之间簇头怎么选出来、数据怎么传、能耗怎么摊开。

2.1 簇头阈值公式 T(n) 的贪心逻辑:概率加权为什么不是纯随机

LEACH 的簇头选举核心是阈值公式:

T(n) = P / (1 - P × (r mod 1/P))

其中 P 是期望的簇头比例,r 是当前轮号,1/P 是轮换周期。每轮开始时,每个存活节点生成一个 [0, 1) 的随机数,如果这个数小于 T(n) 且该节点在最近 1/P 轮内没有当选过,它就成为本轮簇头。

这个公式的巧妙之处在于分母随时间变化。假设 P = 0.05,轮换周期为 20 轮。第 1 轮时 r mod 20 = 0,阈值正好是 0.05,大约 5% 的节点当选;第 10 轮时阈值抬高到约 0.095;第 19 轮时阈值接近 1,几乎所有还没当选过的节点都会被选中。这样设计的目的是保证在一个轮换周期内,全网绝大多数正常存活的节点都能轮到一次簇头,把转发能耗均匀摊到所有节点头上。

需要特别提醒的是,这个“均匀”只是概率意义上的均匀,它完全不看节点当前的剩余能量。一个快没电的节点和满电节点被选中的概率完全相同,所以 LEACH 本质上是一种概率加权而不是能量加权。这既是它实现简单的底气,也是后来所有改进算法的切入点。如果你把阈值公式删掉、完全随机选簇头,最容易翻车:随机数会连续好几轮集中在少数几个节点上,这几个节点会被活活累死,整网生命周期直接崩塌。

2.2 簇建立与稳态传输:TDMA 时隙让成员节点“发完就睡”

一轮生命周期分两个阶段。簇建立阶段做三件事:首先簇头节点广播 ADV 消息声明自己当选;普通节点收到多个簇头的广播后,选择一个信号最强的簇头回发 JOIN 消息;簇头收到所有 JOIN 后,生成一张 TDMA 时隙表并通过广播下发给成员节点。稳态传输阶段做两件事:成员节点严格按自己的时隙醒来发送数据,非本节点时隙一律进入睡眠;簇头收集齐成员数据后做一次聚合,再把聚合结果单跳发给基站。

TDMA 调度是 LEACH 省电的关键一环。如果没有时隙机制,成员节点每轮都要全程监听信道,空闲监听耗电会被白白浪费掉。有了时隙表之后,一个成员节点每轮只需要在自己那一小段时隙里醒来,发完 4000 bit 的数据就继续睡,其余时间几乎零耗电。这也是 LEACH 对比所有节点直发基站方案的优势来源:普通成员到簇头的距离短、路径损耗小,而长距离传输只发生在少数簇头身上。

这里要划一条边界:LEACH 的簇头到基站是单跳长距离传输。基站放得越远,簇头发送时越多地触发四次方路径损耗,生命周期下滑速度会非常明显。这不是 bug,是 LEACH 的适用范围问题。做仿真时如果你把基站丢在几百米外,LEACH 的表现可能还不如简单的直传方案,所以对比实验里基站位置必须在一个合理的、对双方都不偏袒的距离上。

2.3 簇头比例 P 的选型:5% 与 10% 背后的网络寿命权衡

P 是 LEACH 里最敏感的超参数。P = 0.05 时,100 个节点的网络每轮期望选出 5 个簇头;P = 0.1 时选 10 个。看似只差一倍,能耗形态完全不同。

P 偏大时,簇头数量太多,每个簇头只能分到很少的成员,数据聚合带来的“多份数据压缩成一份”的收益变小,而每个簇头自己还要承担广播、接收、聚合、长距离发送四重开销,等于把分簇省下来的能量又加倍还了回去。P 偏小时,每个簇头要带二三十个成员,簇头既要接收全部成员消息,又要做聚合,还要把聚合结果发送到基站,很容易成为整网的能量黑洞。所以在经典配置里,P 取 0.05 是一个稳妥的均衡点。

做改进协议时,你可以把 P 改成随网络密度自适应,但基线 LEACH 必须固定一个值,否则对比结果说不清是算法改进的功劳还是 P 不同导致的差异。我一般还会顺便验证一下 FND(第一个节点死亡轮数)随 P 的变化曲线,它通常是单峰形的,峰值大致出现在 0.03 到 0.1 之间。如果你的仿真里 FND 随 P 单调上升或单调下降,说明广播开销、聚合开销或者距离模型的实现有问题,先回去排查,不要急着改协议。

3. 复现 LEACH 的最小仿真路径:用 Python 把全网节点跑出生命曲线

理论看完了直接上手。常见做法是先写一个 Python 快速原型做能量与寿命验证,确认逻辑闭环后再迁移到离散事件仿真器里做高精度事件级仿真。原型不需要界面的花活,只需要把选举、入簇、传输、计费、死亡判定这五件事跑对。

3.1 一份能直接运行的 LEACH 原型代码:选举、入簇、能耗、轮转

import math import random # ---------- 能耗模型参数(经典配置) ---------- E_ELEC = 50e-9 # 收发电路损耗 50 nJ/bit EPS_FS = 10e-12 # 自由空间功放 10 pJ/bit/m^2 EPS_MP = 0.0013e-12 # 多径功放 0.0013 pJ/bit/m^4 EDA = 5e-9 # 数据聚合损耗 5 nJ/bit L = 4000 # 消息长度 4000 bit # ---------- 网络参数 ---------- N_NODES = 100 # 节点数量 FIELD_SIZE = 100 # 区域边长 100m SINK = (50, 175) # 基站坐标,放在区域上方 P = 0.05 # 簇头比例 E0 = 0.5 # 节点初始能量 0.5J ROUND_MAX = 2000 # 最大仿真轮数 CYCLE = int(1 / P) # 簇头轮换周期 = 20 轮 d0 = math.sqrt(EPS_FS / EPS_MP) # 临界距离约 87.7m class Node: def __init__(self, idx, x, y): self.idx = idx self.x = x self.y = y self.energy = E0 self.alive = True self.last_ch = -CYCLE # 最近一次当选簇头的轮号 def distance(ax, ay, bx, by): return math.sqrt((ax - bx) ** 2 + (ay - by) ** 2) def tx_cost(d): # 发送 L bit 到距离 d 的节点 if d < d0: return L * E_ELEC + L * EPS_FS * d * d else: return L * E_ELEC + L * EPS_MP * d ** 4 def rx_cost(): return L * E_ELEC random.seed(42) nodes = [] for i in range(N_NODES): nodes.append(Node(i, random.uniform(0, FIELD_SIZE), random.uniform(0, FIELD_SIZE))) fnd = hnd = lnd = 0 # 第一个/一半/最后节点死亡轮号 for r in range(1, ROUND_MAX + 1): # 1. 簇头选举:阈值随本轮在周期内的位置变化 threshold = P / (1 - P * (r % CYCLE)) chs = [] for nd in nodes: if not nd.alive: continue if r - nd.last_ch < CYCLE: # 周期内已当选过则跳过 continue if random.random() < threshold: chs.append(nd) nd.last_ch = r # 兜底:随机数导致本轮没有簇头时,强制补选一个,防止整轮空转 if not chs: candidates = [nd for nd in nodes if nd.alive] if candidates: ch = random.choice(candidates) chs.append(ch) ch.last_ch = r # 2. 普通节点加入距离最近的簇头 members = {ch.idx: [] for ch in chs} for nd in nodes: if not nd.alive or nd in chs: continue nearest = min(chs, key=lambda c: distance(nd.x, nd.y, c.x, c.y)) members[nearest.idx].append(nd) # 3. 稳态传输:成员发给簇头,簇头聚合后发给基站 for ch in chs: for m in members[ch.idx]: ch.energy -= rx_cost() # 簇头接收成员消息 m.energy -= tx_cost(distance(m.x, m.y, ch.x, ch.y)) ch.energy -= EDA * L * len(members[ch.idx]) # 聚合开销 ch.energy -= tx_cost(distance(ch.x, ch.y, SINK[0], SINK[1])) # 4. 死亡判定与周期指标记录 alive_before = sum(1 for nd in nodes if nd.alive) for nd in nodes: if nd.alive and nd.energy <= 0: nd.alive = False alive_after = sum(1 for nd in nodes if nd.alive) if alive_before == N_NODES and not fnd and alive_after < N_NODES: fnd = r if alive_before >= N_NODES // 2 and not hnd and alive_after < N_NODES // 2: hnd = r if alive_after == 0 and not lnd: lnd = r print(f"FND={fnd}, HND={hnd}, LND={r}") break if not lnd: print(f"FND={fnd}, HND={hnd}, LND=not_dead({ROUND_MAX}轮内)")

逻辑说明:每一轮先选举簇头,再让普通节点选择距离最近的簇头入簇;稳态阶段只统计两类能量消耗,成员发送消息给簇头、簇头接收并聚合后发送给基站;轮末统一做死亡判定。三个指标 fnd、hnd、lnd 分别在第一个节点死亡、存活数降到一半、全网归零时记录。代码里“兜底补选”的几行是为了防止随机数极端情况导致某一轮没有簇头,造成整轮空转、能量统计失真,实际协议实现里也常见这种最坏情况保底策略。

参数说明:阈值公式里的 CYCLE 由 P 决定,P 取 0.05 则每 20 轮为一个轮换周期,周期内当选过的节点会被跳过,周期结束后重新获得参选资格。d0 不是手工指定的,它由 EPS_FS 和 EPS_MP 推导出来,约 87.7 米,发送距离小于 d0 用平方律功放,大于等于 d0 用四次方功放。初始能量 0.5J、消息长度 4000bit、区域 100m×100m 都是经典配置,和文献里的 LEACH 复现口径基本一致,这样你后面跟其他方案对比时不会被人质疑参数基准。

提示:这份代码在随机数上做了固定 seed,你本地跑会得到确定性的结果。后面做对比实验时不要改这个 seed,要让所有对比算法都吃同一份随机拓扑。

3.2 四个必须按下不表的仿真参数:初始能量、消息长度、区域边长、基站位置

初始能量 E0 取 0.5J 而不是 5J 或 10J。原因是能量余量太大时,能耗差异要在上千轮之后才会拉开,仿真轮数来不及覆盖,结果就是直传方案和 LEACH 的 FND 几乎一样,改进算法全被能量余量淹没。0.25J 到 0.5J 是比较容易看出差异的区间。

消息长度 L 取 4000bit 是 LEACH 文献里的常用口径。不要随手改成 100bit 或者 1Mbit,消息长度变了,收发电路损耗和功放损耗同时等比例变化,但聚合收益的绝对量也跟着变,你跑出来的生命周期绝对值就和所有历史对比数据不在一个量级上了。

区域边长 100m 是单跳模型的安全区。如果区域放大到 500m,节点之间的距离和簇头到基站的距离都会频繁触发四次方路径损耗,LEACH 会非常吃亏。你当然可以把区域改大,但要意识到那是在测 LEACH 的边界表现而不是基线表现。

基站位置放在 (50, 175) 意味着它离区域边缘 75m。这个距离保证相当一部分簇头到基站的传输超过 d0,多径模型会被用到,能耗计算才不会所有传输都走自由空间模型那样“过于乐观”。如果把基站放在区域正中心,几乎所有簇头都在 d0 以内发送,能耗会大幅偏低,FND 能凭空多出几百轮。

这四个参数是耦合的:初始能量决定了总预算,消息长度决定了单次开销,区域边长和基站位置决定了距离分布。改动任何一个,其他三个的合理性都要重新评估。我见过不少翻车案例是把基站挪近了 20m,然后声称自己的改进算法比 LEACH 好 3 倍——那不是算法赢了,是参数偏了。

4. 能耗模型与生命周期指标:LEACH 的能量账在哪些参数下算不清

LEACH 的生命周期数字全由一阶无线能量模型决定。这个模型不复杂,但复现时最容易把功放系数或者临界距离算错,导致整条能量曲线失真。

4.1 一阶无线能量模型:d0 决定你用平方律还是四次方律

发送 l bit 数据到距离 d 的节点,公式分两段:

E_Tx(l, d) = l × E_elec + l × ε_fs × d²(d < d0)

E_Tx(l, d) = l × E_elec + l × ε_mp × d⁴(d ≥ d0)

接收 l bit 数据:

E_Rx(l) = l × E_elec

聚合 k 个数据包后再发送:

E_DA = l × EDA × (k + 1)

临界距离 d0 = sqrt(ε_fs / ε_mp),代入上面代码里的系数约等于 87.7m。物理意义是:传感器贴近地面,短距离传输信号衰减接近自由空间模型,损耗指数为 2;距离超过 d0 后,地面反射、散射和吸收效应开始主导,路径损耗指数升到 4。这不仅仅是一个数学分界,它直接决定了大功率发送有多么昂贵。

LEACH 省电的本质就在于把 100 次长距离传输压成了 5 次:普通节点只需要短距离发给簇头,传输代价是平方律;只有簇头做一次长距离发送,按四次方计费。如果所有节点都直发基站,那就是 100 次四次方传输,再多的分簇技巧也救不回来。所以你看任何一个 LEACH 改进方案,本质上都在优化“哪几个节点来承担那少数几次四次方传输”的问题。

4.2 FND/HND/LND:为什么协议对比只看第一个节点死亡时间

复现 LEACH 时,统计指标就三个,但含义完全不同。FND(First Node Dies)是第一个节点死亡的轮号,代表网络出现第一个覆盖盲区的时间,无线传感器网络的使命是感知覆盖,所以 FND 是衡量可靠性的第一指标。HND(Half Nodes Die)是存活节点数降到一半的轮号,反映网络整体寿命的中位数水平。LND(Last Node Dies)是最后一个节点死亡的轮号,代表网络彻底耗尽的时间。

做协议对比时必须以 FND 为首选观察量。原因是网络的生命价值在于持续提供数据,第一个节点的死亡往往意味着某块区域开始失去感知能力,服务质量从此劣化。LND 恰恰是最不稳定的:最后几个节点往往待在基站附近,距离近、损耗低,能比其他节点多活很多轮,这个数字很容易被少数几个位置特殊的节点操控。我曾经见过一个改进算法,LND 比 LEACH 多出 40%,但 FND 反而更差,仔细看原因就是基站旁边有几个节点一直没被选中当簇头,最后靠它们把 LND 撑高了,实际覆盖能力并没有变好。

所以建议的对比口径是 FND 为主、HND 为辅、LND 做参考。当 FND 和 HND 都优于基线,才说明改进真的有效;如果只有 LND 好看,大概率是极端节点在撒谎。

4.3 LEACH 仿真参数参考表:一组经过验证的默认取值

参数推荐取值说明
E_ELEC50 nJ/bit收发电路损耗,偏大偏小都会影响绝对寿命
EPS_FS10 pJ/bit/m²自由空间功放系数,决定短距离发送成本
EPS_MP0.0013 pJ/bit/m⁴多径功放系数,决定长距离发送成本
EDA5 nJ/bit数据聚合损耗,簇头每聚合一份数据都要付这笔钱
d0约 87.7m由 EPS_FS / EPS_MP 推导,不要手动指定
E00.5 J初始能量,0.25~0.5J 之间更易看出协议差异
L4000 bit消息长度,经典口径,别轻易改
P0.05簇头比例,对应 100 节点选 5 个簇头
区域100m × 100m单跳模型安全范围
基站(50, 175)离区域上方 75m,保证多径模型被触发

这套取值不是我拍的,它几乎是所有 LEACH 复现工作里的公共默认值。优点是拿它做基线跑出来的 FND、HND 量级与公开规律可比,不会因为你的 E_ELEC 比别人大三倍而导致曲线形态完全变形。先跑通这套默认值,再谈改参数。

5. LEACH 仿真避坑指南:五条会让你白跑一星期的血泪教训

LEACH 的代码量不大,坑全藏在细节里。以下五条是我在复现和对比过程中真实遇到过的,每一条都按“现象 → 原因 → 解决”拆开,供你对照排查。

5.1 随意换随机种子,FND 直接波动二十个百分点

现象:同一份代码,只把 random.seed(42) 改成 random.seed(17),FND 从 800 轮掉到 640 轮,HND 变化更夸张。

原因:随机种子改变了节点坐标分布。有些拓扑里节点密集在区域中部,簇头离基站近、能耗低;有些拓扑里角落节点被选中当簇头时,单次传输距离远超 d0,能耗陡增。这不是算法 BUG,而是单次随机实验的方差太大。

解决:做对比实验前,先把节点坐标生成出来并保存成独立的拓扑文件,所有算法共用同一份拓扑文件,用同一个 seed。同一配置至少跑 5 个不同 seed,取平均值和方差再下结论。单次跑出来的绝对数字没有任何说服力。

5.2 P 设得太大:簇头比成员还多,广播开销吃掉收益

现象:把 P 设成 0.5,每轮选出 50 个簇头,半数节点都在广播 ADV,每个簇头只有一两个成员,能耗反而比不分簇更糟。

原因:簇头比例偏离开最优区间后,分簇的聚合收益小于控制开销。每个簇头的广播、接收、聚合、发送是四重成本,簇头越多,这四重成本叠加越严重。

解决:基线仿真 P 固定在 0.05~0.1 之间。如果觉得簇头数量不够导致部分节点找不到近距离簇头,正确的做法是增加节点总数或缩小区域边长,而不是硬拉 P。P 是与网络规模绑定的比例参数,不是随手调的控制旋钮。

5.3 初始能量给太高:任何协议都死得一样慢,改进全被淹没

现象:E0 设成 10J 时,直传方案、LEACH、某个改进算法三条曲线几乎重合,FND 都在 1800 轮附近。

原因:能量预算太大时,能耗差异在 2000 轮仿真内根本不体现。就好比给所有人发了这辈子花不完的钱,省不省钱就看不出来了。

解决:E0 取 0.25~0.5J,保证全网能量在几百到两千轮范围内逐渐见底。能量压到接近耗尽时,协议之间的能耗效率差异才会在 FND 和 HND 上明显分开。

5.4 事件驱动仿真中轮时长与 TDMA 帧长错位,能耗统计虚高

现象:从 Python 原型换到离散事件仿真器之后,统计全网剩余能量比 Python 账面上少了一大截,感觉能量凭空消失了。

原因:节点在“非自己时隙”没有真正进入睡眠状态,接收机一直开着监听信道,这部分空闲监听功耗没被计进模型;或者簇建立阶段的广播事件被安排在了上一轮 TDMA 帧中间,两个阶段的时间戳互相穿插,导致事件计费顺序错乱。

解决:给节点显式建模 sleep 状态,非本节点时隙一律关闭接收机;统计能耗时遍历每条收发事件按事件计费,不要在轮末统一扣减一个固定值。广播帧和 TDMA 数据帧在时间轴上分窗口排布,不要让控制消息插入数据帧中间。

5.5 广播半径小于区域半径,边缘节点集体变成“孤儿”

现象:区域角落里的节点始终收不到簇头的 ADV 广播,无法加入任何簇,只能自己把数据直发基站,能量快速耗尽,FND 大幅提前。

原因:仿真场景里只给节点配置了感知半径,没有把簇建立广播的覆盖范围设成全网可达。LEACH 在原型里假设所有节点都在簇头一跳通信范围内,这个假设在代码里被 min 函数悄悄掩盖了——选择最近簇头时,隐含了所有簇头都可达的前提。

解决:让广播半径大于区域对角线的一半,或者为簇头显式实现多跳广播路由。如果是 100m×100m 的区域,通信半径至少要覆盖约 70m 才能保证角落节点不在“服务盲区”。做改进算法时,这往往是和中心化方案对比最容易翻车的地方。

6. 进阶路线:能量加权选举与集中式选簇头,哪种改进先做才划算

6.1 分布式选举改集中式:代价是每轮多一轮全网上报

最常见的改进线路是把簇头选择权从节点收回到基站,基站掌握全网位置和剩余能量,再集中计算每轮最优簇头集合。优点很明显:簇头空间分布均匀,不会出现随机选举扎堆的情况。代价是每轮簇建立阶段都要先做一轮全网状态上报,节点越多,上报开销越大。我一般只在网络规模不超过 200 个节点时推荐走这条路。

6.2 异构节点场景:簇头概率按剩余能量做加权的两种做法

第二种改进是保留分布式选举,但给阈值增加能量因子。做法一是给 T(n) 乘一个剩余能量比例,能量充足的节点更容易当选;做法二是先筛掉能量低于平均值的节点,只在高出平均值的节点里做阈值选举。两种都能抑制低能量节点被反复选中的问题,但加权幅度不要太大,否则少数高能量节点会在一个周期内被连续选中,热点转移问题只是被推迟了。

6.3 复现后必做的验证:用“曲线形态+量级规律”而不是单一数值去对论文

拿到一套复现结果,别急着跟论文里的绝对数值比对,因为大家的初始能量、区域大小、消息长度经常不一致,绝对数字不可比。我的习惯是先扫一遍 P:从 0.02 到 0.2 每个值跑一次,画出 FND 随 P 变化的曲线,它应该呈单峰形。如果曲线单调上升或单调下降,说明广播或聚合的实现有漏洞。再检查 HND 与 FND 的比值,LEACH 通常在 1.5 到 2.5 倍之间;小于 1 说明第一个节点死后全网快速雪崩,大于 3 说明最后几个节点的能量分配异常。把这两个规律验证通过,再谈改进才有底气。我踩过的坑是改完算法后只盯 LND,被基站旁边几个节点的长寿命骗了,白跑两星期。现在每次跑仿真,我都会把 seed、拓扑文件、能量参数存成一份参数清单,三天后再回溯也知道跑的是哪一版。希望帮到你。

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

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

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

立即咨询