├── LEACH.py └── README.md /LEACH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | '''================================================= 4 | @Title :Leach for WSN 5 | @Author :Kay 6 | @Date :2019-09-27 7 | ==================================================''' 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | #如遇中文显示问题可加入以下代码 12 | from pylab import mpl 13 | mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体 14 | mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题 15 | 16 | class WSN(object): 17 | """ The network architecture with desired parameters """ 18 | xm = 200 # Length of the yard 19 | ym = 200 # Width of the yard 20 | n = 100 # total number of nodes 21 | sink = None # Sink node 22 | nodes = None # All sensor nodes set 23 | # Energy model (all values in Joules) 24 | # Eelec = ETX = ERX 25 | ETX = 50 * (10 ** (-9)) # Energy for transferring of each bit:发射单位报文损耗能量:50nJ/bit 26 | ERX = 50 * (10 ** (-9)) # Energy for receiving of each bit:接收单位报文损耗能量:50nJ/bit 27 | # Transmit Amplifier types 28 | Efs = 10 * (10 ** (-12)) # Energy of free space model:自由空间传播模型:10pJ/bit/m2 29 | Emp = 0.0013 * (10 ** (-12)) # Energy of multi path model:多路径衰减空间能量模型:0.0013pJ/bit/m4 30 | EDA = 5 * (10 ** (-9)) # Data aggregation energy:聚合能量 5nJ/bit 31 | f_r = 0.6 # fusion_rate:融合率 , 0代表完美融合 32 | # Message 33 | CM = 32 # 控制信息大小/bit 34 | DM = 4096 # 数据信息大小/bit 35 | # computation of do 36 | do = np.sqrt(Efs / Emp) # 87.70580193070293 37 | 38 | # 恶意传感器节点 39 | m_n = 3 # the number of malicious sensor nodes 40 | 41 | # Node State in Network 42 | n_dead = 0 # The number of dead nodes 43 | flag_first_dead = 0 # Flag tells that the first node died 44 | flag_all_dead = 0 # Flag tells that all nodes died 45 | flag_net_stop = 0 # Flag tells that network stop working:90% nodes died 46 | round_first_dead = 0 # The round when the first node died 47 | round_all_dead = 0 # The round when all nodes died 48 | round_net_stop = 0 # The round when the network stop working 49 | 50 | def dist(x, y): 51 | """ 判断两个节点之间的一维距离 """ 52 | distance = np.sqrt(np.power((x.xm - y.xm), 2) + np.power((x.ym - y.ym), 2)) 53 | return distance 54 | 55 | def trans_energy(data, dis): 56 | if dis > WSN.do: 57 | energy = WSN.ETX * data + WSN.Emp * data * (dis ** 4) 58 | else: # min_dis <= do 59 | energy = WSN.ETX * data + WSN.Efs * data * (dis ** 2) 60 | return energy 61 | 62 | def node_state(r): 63 | nodes = WSN.nodes 64 | n_dead = 0 65 | for node in nodes: 66 | if node.energy <= Node.energy_threshold: 67 | n_dead += 1 68 | if WSN.flag_first_dead == 0 and n_dead == 1: 69 | WSN.flag_first_dead = 1 70 | WSN.round_first_dead = r - Leach.r_empty 71 | if WSN.flag_net_stop == 0 and n_dead >= (WSN.n * 0.9): 72 | WSN.flag_net_stop = 1 73 | WSN.round_net_stop = r - Leach.r_empty 74 | if n_dead == WSN.n - 1: 75 | WSN.flag_all_dead = 1 76 | WSN.round_all_dead = r - Leach.r_empty 77 | WSN.n_dead = n_dead 78 | 79 | class Node(object): 80 | """ Sensor Node """ 81 | energy_init = 0.5 # initial energy of a node 82 | # After the energy dissipated in a given node reached a set threshold, 83 | # that node was considered dead for the remainder of the simulation. 84 | energy_threshold = 0.001 85 | 86 | def __init__(self): 87 | """ Create the node with default attributes """ 88 | self.id = None # 节点编号 89 | self.xm = np.random.random() * WSN.xm 90 | self.ym = np.random.random() * WSN.ym 91 | self.energy = Node.energy_init 92 | self.type = "N" # "N" = Node (Non-CH):点类型为普通节点 93 | # G is the set of nodes that have not been cluster-heads in the last 1/p rounds. 94 | self.G = 0 # the flag determines whether it's a CH or not:每一周期此标志为0表示未被选为簇头,1代表被选为簇头 95 | self.head_id = None # The id of its CH:隶属的簇, None代表没有加入任何簇 96 | 97 | def init_nodes(): 98 | """ Initialize attributes of every node in order """ 99 | nodes = [] 100 | # Initial common node 101 | for i in range(WSN.n): 102 | node = Node() 103 | node.id = i 104 | nodes.append(node) 105 | # Initial sink node 106 | sink = Node() 107 | sink.id = -1 108 | sink.xm = 0.5 * WSN.xm # x coordination of base station 109 | sink.ym = 50 + WSN.ym # y coordination of base station 110 | # Add to WSN 111 | WSN.nodes = nodes 112 | WSN.sink = sink 113 | 114 | def init_malicious_nodes(): 115 | """ Initialize attributes of every malicious node in order """ 116 | for i in range(WSN.m_n): 117 | node = Node() 118 | node.id = WSN.n + i 119 | WSN.nodes.append(node) 120 | 121 | def plot_wsn(): 122 | nodes = WSN.nodes 123 | n = WSN.n 124 | m_n = WSN.m_n 125 | # base station 126 | sink = WSN.sink 127 | plt.plot([sink.xm], [sink.ym], 'r^',label="基站") 128 | # 正常节点 129 | n_flag = True 130 | for i in range(n): 131 | if n_flag: 132 | plt.plot([nodes[i].xm], [nodes[i].ym], 'b+',label='正常节点') 133 | n_flag = False 134 | else: 135 | plt.plot([nodes[i].xm], [nodes[i].ym], 'b+') 136 | # 恶意节点 137 | m_flag = True 138 | for i in range(m_n): 139 | j = n + i 140 | if m_flag: 141 | plt.plot([nodes[j].xm], [nodes[j].ym], 'kd',label='恶意节点') 142 | m_flag = False 143 | else: 144 | plt.plot([nodes[j].xm], [nodes[j].ym], 'kd') 145 | plt.legend() 146 | plt.xlabel('X/m') 147 | plt.ylabel('Y/m') 148 | plt.show() 149 | 150 | class Leach(object): 151 | """ Leach """ 152 | # Optimal selection probablitity of a node to become cluster head 153 | p = 0.1 # 选为簇头概率 154 | period = int(1/p) # 周期 155 | heads = None # 簇头节点列表 156 | members = None # 非簇头成员列表 157 | cluster = None # 簇类字典 :{"簇头1":[簇成员],"簇头2":[簇成员],...} 158 | r = 0 # 当前轮数 159 | rmax = 5#9999 # default maximum round 160 | r_empty = 0 # 空轮 161 | 162 | def show_cluster(): 163 | fig = plt.figure() 164 | # 设置标题 165 | # 设置X轴标签 166 | plt.xlabel('X/m') 167 | # 设置Y轴标签 168 | plt.ylabel('Y/m') 169 | icon = ['o', '*', '.', 'x', '+', 's'] 170 | color = ['r', 'b', 'g', 'c', 'y', 'm'] 171 | # 对每个簇分类列表进行show 172 | i = 0 173 | nodes = WSN.nodes 174 | for key, value in Leach.cluster.items(): 175 | cluster_head = nodes[int(key)] 176 | # print("第", i + 1, "类聚类中心为:", cluster_head) 177 | for index in value: 178 | plt.plot([cluster_head.xm, nodes[index].xm], [cluster_head.ym, nodes[index].ym], 179 | c=color[i % 6], marker=icon[i % 5], alpha=0.4) 180 | # 如果是恶意节点 181 | if index >= WSN.n: 182 | plt.plot([nodes[index].xm], [nodes[index].ym], 'dk') 183 | i += 1 184 | # 显示所画的图 185 | plt.show() 186 | 187 | def optimum_number_of_clusters(): 188 | """ 完美融合下的最优簇头数量 """ 189 | N = WSN.n - WSN.n_dead 190 | M = np.sqrt(WSN.xm * WSN.ym) 191 | d_toBS = np.sqrt((WSN.sink.xm - WSN.xm) ** 2 + 192 | (WSN.sink.ym - WSN.ym) ** 2) 193 | k_opt = (np.sqrt(N) / np.sqrt(2 * np.pi) * 194 | np.sqrt(WSN.Efs / WSN.Emp) * 195 | M / (d_toBS ** 2)) 196 | p = int(k_opt) / N 197 | return p 198 | 199 | def cluster_head_selection(): 200 | """ 根据阈值选择簇头节点 """ 201 | nodes = WSN.nodes 202 | n = WSN.n # 非恶意节点 203 | heads = Leach.heads = [] # 簇头列表, 每轮初始化为空 204 | members = Leach.members = [] # 非簇成员成员列表 205 | p = Leach.p 206 | r = Leach.r 207 | period = Leach.period 208 | Tn = p / (1 - p * (r % period)) # 阈值Tn 209 | print(Leach.r, Tn) 210 | for i in range(n): 211 | # After the energy dissipated in a given node reached a set threshold, 212 | # that node was considered dead for the remainder of the simulation. 213 | if nodes[i].energy > Node.energy_threshold: # 节点未死亡 214 | if nodes[i].G == 0: # 此周期内节点未被选为簇头 215 | temp_rand = np.random.random() 216 | # print(temp_rand) 217 | # 随机数低于阈值节点被选为簇头 218 | if temp_rand <= Tn: 219 | nodes[i].type = "CH" # 此节点为周期本轮簇头 220 | nodes[i].G = 1 # G设置为1,此周期不能再被选择为簇头 or (1/p)-1 221 | heads.append(nodes[i]) 222 | # 该节点被选为簇头,广播此消息 223 | # Announce cluster-head status, wait for join-request messages 224 | max_dis = np.sqrt(WSN.xm ** 2 + WSN.ym ** 2) 225 | nodes[i].energy -= WSN.trans_energy(WSN.CM, max_dis) 226 | # 节点有可能死亡 227 | if nodes[i].type == "N": # 该节点非簇头节点 228 | members.append(nodes[i]) 229 | m_n = WSN.m_n 230 | for i in range(m_n): 231 | j = n + i 232 | members.append(nodes[j]) 233 | # 如果本轮未找到簇头 234 | if not heads: 235 | Leach.r_empty += 1 236 | print("---> 本轮未找到簇头!") 237 | # Leach.cluster_head_selection() 238 | print("The number of CHs is:", len(heads), (WSN.n - WSN.n_dead)) 239 | return None # heads, members 240 | 241 | def cluster_formation(): 242 | """ 进行簇分类 """ 243 | nodes = WSN.nodes 244 | heads = Leach.heads 245 | members = Leach.members 246 | cluster = Leach.cluster = {} # 簇类字典初始化 247 | # 本轮未有簇头,不形成簇 248 | if not heads: 249 | return None 250 | # 如果簇头存在,将簇头id作为cluster字典的key值 251 | for head in heads: 252 | cluster[str(head.id)] = [] # 成员为空列表 253 | # print("只有簇头的分类字典:", cluster) 254 | # 遍历非簇头节点,建立簇 255 | for member in members: 256 | # 选取距离最小的节点 257 | min_dis = np.sqrt(WSN.xm ** 2 + WSN.ym ** 2) # 簇头节点区域内的广播半径 258 | head_id = None 259 | # 接收所有簇头的信息 260 | # wait for cluster-head announcements 261 | member.energy -= WSN.ERX * WSN.CM * len(heads) 262 | # 判断与每个簇头的距离,加入距离最小的簇头 263 | for head in heads: 264 | tmp = WSN.dist(member, head) 265 | if tmp <= min_dis: 266 | min_dis = tmp 267 | head_id = head.id 268 | member.head_id = head_id # 已找到簇头 269 | # 发送加入信息,通知其簇头成为其成员 270 | # send join-request messages to chosen cluster-head 271 | member.energy -= WSN.trans_energy(WSN.CM, min_dis) 272 | # 簇头接收加入消息 273 | # wait for join-request messages 274 | head = nodes[head_id] 275 | head.energy -= WSN.ERX * WSN.CM 276 | cluster[str(head_id)].append(member.id) # 添加到出簇类相应的簇头 277 | # 为簇中每个节点分配向其传递数据的时间点 278 | # Create a TDMA schedule and this schedule is broadcast back to the nodes in the cluster. 279 | for key, values in cluster.items(): 280 | head = nodes[int(key)] 281 | if not values: 282 | # If there are cluster members, the CH sends schedule by broadcasting 283 | max_dis = np.sqrt(WSN.xm ** 2 + WSN.ym ** 2) 284 | head.energy -= WSN.trans_energy(WSN.CM, max_dis) 285 | for x in values: 286 | member = nodes[int(x)] 287 | # wait for schedule from cluster-head 288 | member.energy -= WSN.ERX * WSN.CM 289 | # print(cluster) 290 | return None # cluster 291 | 292 | def set_up_phase(): 293 | Leach.cluster_head_selection() 294 | Leach.cluster_formation() 295 | 296 | def steady_state_phase(): 297 | """ 簇成员向簇头发送数据,簇头汇集数据然后向汇聚节点发送数据 """ 298 | nodes = WSN.nodes 299 | cluster = Leach.cluster 300 | # 如果本轮未形成簇,则退出 301 | if not cluster: 302 | return None 303 | for key, values in cluster.items(): 304 | head = nodes[int(key)] 305 | n_member = len(values) # 簇成员数量 306 | # 簇中成员向簇头节点发送数据 307 | for x in values: 308 | member = nodes[int(x)] 309 | dis = WSN.dist(member, head) 310 | member.energy -= WSN.trans_energy(WSN.DM, dis) # 簇成员发送数据 311 | head.energy -= WSN.ERX * WSN.DM # 簇头接收数据 312 | d_h2s = WSN.dist(head, WSN.sink) # The distance of from head to sink 313 | if n_member == 0: # 如果没有簇成员,只有簇头收集自身信息发送给基站 314 | energy = WSN.trans_energy(WSN.DM, d_h2s) 315 | else: 316 | new_data = WSN.DM * (n_member + 1) # 加上簇头本身收集的数据,进行融合后的新的数据包 317 | E_DA = WSN.EDA * new_data # 聚合数据的能量消耗 318 | if WSN.f_r == 0: # f_r为0代表数据完美融合 319 | new_data_ = WSN.DM 320 | else: 321 | new_data_ = new_data * WSN.f_r 322 | E_Trans = WSN.trans_energy(new_data_, d_h2s) 323 | energy = E_DA + E_Trans 324 | head.energy -= energy 325 | 326 | def leach(): 327 | Leach.set_up_phase() 328 | Leach.steady_state_phase() 329 | 330 | def run_leach(): 331 | for r in range(Leach.rmax): 332 | Leach.r = r 333 | nodes = WSN.nodes 334 | # 当新周期开始时,G重置为0 335 | if (r % Leach.period) == 0: 336 | print("==============================") 337 | for node in nodes: 338 | node.G = 0 339 | # 当每一轮开始时,节点类型重置为非簇头节点 340 | for node in nodes: 341 | node.type = "N" 342 | Leach.leach() 343 | WSN.node_state(r) 344 | if WSN.flag_all_dead: 345 | print("==============================") 346 | break 347 | Leach.show_cluster() 348 | 349 | def main(): 350 | Node.init_nodes() 351 | Node.init_malicious_nodes() 352 | Node.plot_wsn() 353 | Leach.run_leach() 354 | # print("The first node died in Round %d!" % (WSN.round_first_dead)) 355 | # print("The network stop working in Round %d!" % (WSN.round_net_stop)) 356 | # print("All nodes died in Round %d!" % (WSN.round_all_dead)) 357 | 358 | if __name__ == '__main__': 359 | main() 360 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LEACH 2 | LEACH routing protocol in WSN 3 | --------------------------------------------------------------------------------