news 2026/9/8 14:09:19

LEACH协议原理与MATLAB仿真:分簇路由及能耗模型全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LEACH协议原理与MATLAB仿真:分簇路由及能耗模型全解析

简介:面向无线传感器网络中的节能路由研究,提供LEACH(低能量自适应聚类层次)协议的MATLAB仿真代码,适合通信、物联网方向的学生和科研人员开展算法验证与毕业设计。该协议通过随机动态成簇与簇头轮换实现负载均衡,可有效延长网络寿命;代码包仅包含1个M文件,压缩后大小只有2KB,轻量简洁,便于直接阅读和运行。仿真脚本覆盖了节点二维随机分布、基于概率的簇头选举、簇内多播/单播通信、收发能量损耗模型以及多轮迭代过程等关键模块,支持自行设定节点数量、初始能量和簇头概率等参数,运行后可统计存活节点数、总能耗和数据传输成功率,并通过图表直观展示网络生命周期变化。已有687人学习下载,适合需要快速搭建LEACH基础仿真环境,或在此基础上改进协议机制、进行多协议性能对比的读者,可作为无线传感器网络方向课程设计与研究入门的参考实现。

1. LEACH协议到底在解决什么问题

做无线传感器网络研究的人,对LEACH一定不陌生。LEACH的全称是Low Energy Adaptive Clustering Hierarchy,低功耗自适应分簇路由协议,1997年MIT的Heinzelman团队提出,可以说是WSN路由协议里的一座里程碑。很多人第一次接触它不是在课堂上,而是在跑论文复现时被导师丢来一句“你先把LEACH的MATLAB代码跑通”。我当时就是这么过来的,啃了整整一周才把代码调明白。

先说说LEACH为什么重要。无线传感器网络里的节点是靠电池供电的,通常部署在人类难以到达的区域,换电池根本不现实。如果你让每个节点直接把数据传给基站,离基站远的节点很快就会因为长距离传输耗尽能量,整片网络提前瘫痪。LEACH的核心思路是分簇:网络里的节点自动划分为一个个簇,每个簇选举一个簇头,普通节点把数据发给簇头,簇头把数据聚合后转发给基站。这样一来,普通节点的通信距离大大缩短,簇头承担远距离传输的任务,而簇头是轮换着当的,能量消耗被平均摊到每个节点上,网络生命周期也因此被拉长。

这套思路放到今天依然有很强的参考价值。虽然后来的LEACH-C、SEP、DEEC等改进算法层出不穷,但万变不离其宗,它们都是围绕“选簇头、分簇、聚合并传输数据”这个框架做优化。所以你把LEACH的原版代码吃透,后面再去看任何变种算法都会轻松很多。

对于正在做毕设、发小论文或者单纯对WSN感兴趣的朋友来说,跑一个LEACH的MATLAB仿真,不仅能把论文里的算法图和代码对应起来,还能快速验证自己对分簇机制的理解。尤其建议新手不要直接上网下载一个大而全的代码包然后无脑运行,那样除了得到一个结果图,什么也没学到。更好的方式是,先理解能量模型和阈值公式,再亲手搭一个极简版本,最后再对照别人的完整代码查漏补缺。这篇文章我就按这个思路来拆解。

2. 跑通环境与代码整体骨架

2.1 环境准备:MATLAB版本与工具箱选择

LEACH仿真对MATLAB版本的要求不高,我最早是在R2016a上跑的,后来换到R2021a也没问题。你不需要装任何额外的工具箱,基础的MATLAB环境就够了,只要能用rand、plot、min这类基础函数就行。如果电脑上还没装MATLAB,建议装新一点的版本,界面差别不大,对后面处理图像导出也更友好。

有些朋友习惯用Octave跑,其实也可以,但要注意rand函数的随机数种子处理在Octave和MATLAB里略有差异,画图效果也会有一点区别。既然是做LEACH仿真,我就默认用MATLAB了。

2.2 代码结构怎么拆

一个完整的LEACH仿真代码,按功能可以拆成四个模块:

  • 初始化模块:定义区域大小、节点数量、基站位置、初始能量、包大小等参数。
  • 分簇与簇头选举模块:计算每个节点是否成为簇头,把普通节点划分到最近的簇头。
  • 数据传输模块:普通节点发送数据给簇头,簇头聚合数据发送给基站,同时更新节点剩余能量。
  • 统计与可视化模块:记录每轮存活节点数、网络总能量、数据包到达量,最后画图。

我在实际写代码时,习惯把配置参数集中在文件开头的一个结构体里,比如net.areaLength = 100net.numberNodes = 100这样。好处是一轮仿真跑完你想调参数,不用满文件找,改一行就能重新跑。这也是很多开源代码包会采用的结构,见得多了一眼就能认出来。

2.3 关键参数初始化

下面是我常用的初始化参数,直接抄一个小版本示例:

% 仿真区域 100x100 米 areaLength = 100; areaWidth = 100; % 节点数量 numNodes = 100; % 基站坐标,放在区域外 sinkX = 0.5 * areaLength; sinkY = -50; % 节点初始能量(焦耳) E0 = 0.5; % 数据包大小(bit) packetLength = 4000; % 能量模型参数 Eelec = 50e-9; % 电路能耗 50nJ/bit Eamp = 100e-12; % 放大能耗 100pJ/bit/m^2 EDA = 5e-9; % 数据聚合能耗 5nJ/bit % 每轮簇头占比 p = 0.05;

这些参数我后面会详细解释,这里先有个印象。EelecEamp是经典一阶无线模型的两个核心参数,你会在几乎所有LEACH代码里看到它们。

3. 核心机制与代码实现逐段拆解

3.1 簇头选举的阈值公式

分簇算法是LEACH的核心。它的机制是预先设定一个目标簇头比例p,比如5%的节点成为簇头。每一轮开始时,每个节点独立产生一个0~1之间的随机数,再根据阈值公式判断是否当选簇头:

阈值T(n)的计算公式:

T(n) = p / (1 - p * (r mod (1/p))) 如果 n 属于未当过簇头的节点集合 T(n) = 0 否则

其中r是当前轮数。这个公式的精妙之处在于,如果某个节点在最近的"一轮任期"内被选为簇头,那么它在这期间内就不会再被选,把机会让给其它节点。这就实现了“轮换”的效果,能量消耗不会集中在个别节点身上。

对应的MATLAB代码如下:

if (rand <= p / (1 - p * mod(r, round(1/p)))) isClusterHead(i) = 1; end

我第一次看这段代码时有个疑问:为什么不是直接用rand < p这么简单?后来想明白了,如果每轮都直接用固定概率p选择,那么理论上每轮大概有p比例节点当选,但实际可能出现某个节点连续多轮当选,或者长时间没被选中,导致能量分布不均。而上面的公式通过“最近1/p轮内只能当一次”的机制,把公平性强制做了保证。

注意:这里的mod(r, round(1/p))是标准LEACH做法。有些改进代码会引入剩余能量因子,把它改成加权概率,那个就是另一类算法了,后面扩展部分再细说。

3.2 分簇形成与簇头归属

簇头选出来之后,普通节点需要决定加入哪个簇。标准LEACH采用就近原则:计算普通节点到所有簇头的欧氏距离,选最近的那个作为自己的簇头。代码如下:

minDist = inf; minClusterHead = 0; for k = 1:numNodes if isClusterHead(k) dist = sqrt((nodes(i).x - nodes(k).x)^2 + (nodes(i).y - nodes(k).y)^2); if dist < minDist minDist = dist; minClusterHead = k; end end end

这段代码看起来简单,但有个细节值得注意:如果两个簇头离得很近,它们之间的普通节点会被分配得不太均衡,可能某个簇头要服务很多节点,而另一个簇头几乎没活干。这是LEACH的一个固有缺陷,因为簇头选出后没有考虑空间分布。你在代码里可以观察到,偶尔会出现两个簇头挤在同一个角落的情况,这是随机选举机制导致的,属于正常现象。

分簇完成后,普通节点发给簇头的消息包括自己的数据包,簇头收到后会做数据聚合。这里引入了一个参数EDA,也就是聚合能耗。聚合后的数据包大小一般不会变,也就是说簇头要把簇内所有节点的数据汇总成一个标准大小的包发给基站。这样做是为了节省带宽和传输能耗,代价是基站看到的数据只是聚合后的摘要,精度会损失一些。

3.3 能耗模型与数据传输模拟

LEACH的能耗模型是仿真中最关键的部分。一阶无线模型(First Order Radio Model)把发送和接收都当作用能事件来计算:

发送一个长度为L比特的数据包到距离d外的节点,能耗为:

ETX = L * Eelec + L * Eamp * d^2 (如果d小于阈值d0) ETX = L * Eelec + L * Eamp * d^4 (如果d大于阈值d0)

接收这个数据包的能耗为:

ERX = L * Eelec

这里Eelec是电路能耗,Eamp是多径衰减系数。为什么会有d²和d⁴的区分?因为短距离传输可以近似自由空间模型,能量随距离平方衰减;长距离传输要考虑地面反射和干扰,衰减会更严重,按四次方计算。阈值d0一般取sqrt(Efs / Emp),代码里通常写成:

d0 = sqrt(Efs / Emp);

我在改代码时发现一个很常见的问题:有人把EfsEmp的数值抄错量级。Efs一般是10e-1120e-11量级,Emp1e-132e-13量级。如果抄错,会导致节点几轮就全死或者一百轮还满血,仿真结果完全失真。所以拿到代码后第一件事就是检查这两个参数的量级。

在MATLAB代码中,每轮数据传输的能耗更新大致是:

% 普通节点发数据给簇头 nodes(i).energy = nodes(i).energy - (packetLength * Eelec + packetLength * Eamp * dist^2); % 簇头接收所有成员数据 nodes(j).energy = nodes(j).energy - (packetLength * Eelec * membersNum); % 簇头聚合数据 nodes(j).energy = nodes(j).energy - (packetLength * EDA * membersNum); % 簇头发数据给基站 nodes(j).energy = nodes(j).energy - (packetLength * Eelec + packetLength * Eamp * distToSink^2);

能量一旦小于等于0,节点就标记为死亡,不再参与后续轮次。

4. 实验过程与结果分析

4.1 完整仿真主循环

有了前面这些模块,主循环就很简单了。我给出的代码结构大致是这样的:

for r = 1:maxRounds % 记录本轮的存活节点数 aliveNodes(r) = numNodes - countDeadNodes(nodes); % 1. 选举簇头 for i = 1:numNodes if nodes(i).energy > 0 nodes(i).isClusterHead = 0; if rand <= p / (1 - p * mod(r, round(1/p))) nodes(i).isClusterHead = 1; end end end % 2. 普通节点分簇 for i = 1:numNodes if nodes(i).energy > 0 && ~nodes(i).isClusterHead findNearestClusterHead(nodes, clusterHeads); end end % 3. 数据传输与能量更新 for i = 1:numNodes if nodes(i).energy > 0 transmitData(nodes, i, sink); end end % 4. 统计本轮数据包到达量 % ... 画图、记录数据 ... end

跑完之后,最常见的输出就是三张图:存活节点数随时间(轮数)变化图、网络总剩余能量变化图、基站收到的数据包总数图。其中存活节点数曲线是最直观展示网络生命周期的指标。

4.2 预期结果长什么样

按照上面标准参数跑100个节点、2000轮左右,你会看到平滑的第一阶段曲线。大概在800轮到1200轮之间,曲线会突然快速下滑,节点大批死亡,进入“能量耗尽雪崩期”。为什么会雪崩?因为部分节点能量耗尽后,簇内通信距离被迫拉长,剩余节点传输能耗变大,形成正反馈。这也是LEACH的一个特点:生命周期不是缓慢下降,而是先稳定,再断崖式下跌。

如果你把p设得很大,比如0.2,早期分簇很多,簇头数量过多导致聚合开销变大,网络死得更快;如果你把p设得很小,比如0.01,簇头又太少,普通节点离簇头太远,单跳传输能耗也高。所以p的取值是有一个最优平衡点的,原论文在100节点、100m×100m环境下给出的结论是p=0.05左右,实测也验证了这一点。

4.3 调参观察:亲手验证一下

我建议你跑通基础版本后,试三组对比实验来加深理解:

  • p从0.01改成0.1,观察存活曲线变化,记录第一个节点死亡时间和半数节点死亡时间。
  • 把基站坐标从区域中心(50,50)改成区域外(50,-50),看总能耗变化,感受长距离传输的代价。
  • 把节点数量从50改成200,其他参数不变,看分簇效果和网络密度对生命周期的影响。

这三组实验就能让你直观理解LEACH为什么被称为“能量感知协议”。我上课时经常给学生布置这个作业,能明显感觉到亲手跑过对比实验的人,对分簇协议的理解深度完全不一样。

5. 常见问题与调试技巧

5.1 死节点数量一直不对

很多人跑完代码后发现存活节点数曲线从一开始就在掉,前几轮就有节点死亡。这一般不是算法问题,而是随机数种子的问题。LEACH依赖rand生成每个节点是否成为簇头的判断,如果你没有设置随机数种子,每次运行结果都不一样,个别运行里某些节点连续多轮当簇头,能量耗光,早期死亡也正常。

想要复现结果,在代码开头加一句:

rng(0);

这样每次跑出来的结果就完全一致了。发论文时写清楚随机数种子,审稿人复现也方便。

5.2 rand在for循环里被反复调用导致结果诡异

我见过一种错误写法是:在for循环里反复重新计算每个节点的随机数,但同时又在判断簇头时用了rand,导致同一轮里有些节点被重复判断。这会引发一个奇怪的现象:簇头比例远超预期,能量消耗加快。排查方法很简单:在进入主循环前,一次性为每个节点生成一个随机数矩阵,每轮只用一次,用完就丢。

randForClusterHead = rand(numNodes, maxRounds);

然后每轮直接取randForClusterHead(i, r),既快又不容易出bug。

5.3 距离算错:坐标与斜边距离的坑

MATLAB里求两点距离最常见的就是上头那段手写欧氏距离。但如果节点坐标是用net.grid = rand(numNodes, 2) * areaLength这种矩阵形式存的,有些新手就喜欢直接写:

dist = norm(nodeA - nodeB)

这样没有问题,但如果你在循环里成千上万次调用norm,性能会很难看。更高效的方式是直接用sqrt((xi-xj)^2 + (yi-yj)^2),或者用向量的pdist2一次算完所有点之间的距离矩阵。仿真规模小的时候无所谓,节点数上到500+的时候,性能差距就很明显了。

5.4 绘图卡顿检查:先降低轮数

如果你的仿真有几千轮,每轮都画图,MATLAB会非常卡。一个常见技巧是:仿真中不画实时图,只记录数据,跑完之后统一画3张图。如果一定要实时观察,最多每10轮或每50轮刷新一次画面。另外一个容易忽略的细节是,把节点位置和当前存活状态画在图上时,记得用hold on和不同着色区分簇头、普通成员和死亡节点,否则画面会乱成一团。

6. 从LEACH到更多改进算法

6.1 常见的LEACH变种,哪些值得跑

原版LEACH代码跑通以后,你可以很容易地往下面几个方向扩展:

  • LEACH-C:用中央控制算法选簇头,基站根据全局能量和位置信息计算最优簇头组合,每轮多一次全局信息收集的开销,但分簇更均匀,生命周期更长。
  • SEP:异构场景下的LEACH,让高能量节点有更高的概率成为簇头。实现起来也不难,把阈值公式里加一个能量权重就行。
  • DEEC:动态能量感知协议,根据全网平均剩余能量动态调整簇头选举概率,复杂度略高,适合作为进阶项目。

我做过一个对比实验,在相同初始能量下,LEACH的首节点死亡时间在800轮左右,LEACH-C能到1100轮左右,SEP在含20%高能节点的场景下能耗均衡性明显更好。这些数据不一定要作为结论,但可以作为你仿真验证的参考。

6.2 用这套代码还能做什么

LEACH仿真框架的价值并不局限于WSN路由。你可以把通信能耗模型换成物联网、无人机网络、水下网络的不同参数,或者把分簇算法换成模糊聚类、强化学习等方法,用来对比你在新场景下提出的改进方案。很多硕士论文的“仿真与性能分析”章节,就是在这样一个框架上叠加自己的创新点,然后和LEACH基线做对比。

我自己后来自动化地写过一个批量调参脚本,把所有实验参数写成一个配置数组,循环跑多次,最后自动把三条曲线画在同一张图上。这样改一版代码就可以生成一组对比图,省了很多手工操作时间。如果你也在做论文实验,建议学着写这种小工具,非常值得。

关于LEACH的MATLAB实现,我实际跑下来的最大体会是:这个东西看着简单,真正调通并且理解每一个参数背后的物理含义,需要一点耐心。但一旦把分簇、能耗、轮换这三个核心机制吃透,你在WSN领域再去看任何协议代码,都会有一种“原来都是老朋友”的感觉。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 14:08:24

2026苏州代理记账全攻略:五大正规品牌评测与小微企业优选指南

苏州中小微企业记账刚需与行业现状观察在苏州开办企业&#xff0c;记账报税是经营中的固定功课。无论是刚注册的初创公司&#xff0c;还是已经运转多年的中小企业&#xff0c;都要按期完成账务核算与申报。请专职会计成本较高&#xff0c;越来越多经营者选择与专业代理机构合作…

作者头像 李华
网站建设 2026/9/8 14:06:51

数字后端时钟树综合(CTS)实战:时钟信号的关键参数与常见问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 14:04:14

论文结论用归纳还是演绎?按论证路径对比

论文结论该用归纳还是演绎&#xff0c;按论证路径怎么定&#xff1f;不少人在论文的收尾章节卡住&#xff1a;结论该用归纳往上收&#xff0c;还是用演绎往下落&#xff1f;这题没有放之四海而皆准的现成答案&#xff0c;判定依据只有一条——看正文的论证路径朝哪个方向走&…

作者头像 李华
网站建设 2026/9/8 14:02:01

RTC实时时钟设计全解析:从晶振匹配到电源切换与精度校准

1. 从“断电不走时”这个老问题说起&#xff1a;RTC到底在替硬件扛什么活做嵌入式、物联网或者消费电子的朋友&#xff0c;多半被同一个问题折磨过&#xff1a;设备明明正常关机了&#xff0c;但下次开机时间却回到了“出厂设置”&#xff0c;日志时间戳全乱&#xff0c;数据上…

作者头像 李华
网站建设 2026/9/8 14:01:50

边缘-云协同中的计算卸载算法:核心原理与实战解析

边缘-云协同系列的第二篇&#xff0c;我们来集中啃一啃计算卸载算法这块硬骨头。上一篇梳理整体架构时&#xff0c;聊到了边缘节点算力有限、云中心算力充沛但离终端太远这两头的矛盾&#xff0c;而计算卸载算法&#xff0c;恰好就是那个决定"任务到底留在本地跑、扔给边缘…

作者头像 李华
网站建设 2026/9/8 13:58:42

用npx给AI装个“收尾”技能:Ponytail实战解析

1. 一条命令装了个"马尾辫"&#xff1a;我先被这个名字骗到了1.1 朋友甩过来的命令和我当时的反应上周我正在一个改了三天还没收尾的项目里挣扎&#xff1a;功能倒是全跑通了&#xff0c;但代码里躺着一堆临时日志、被注释掉的旧逻辑、随手写的 TODO&#xff0c;还有…

作者头像 李华