%% 实验二:LEACH路由协议仿真 - 军用无线传感器网络
% 功能:实现LEACH分簇算法,分析网络能量消耗和生存周期
% 作者:根据实验指导书要求编写
% 日期:2024年
clear; close all; clc;
%% 参数设置
num_nodes = 100; % 节点数量
area_size = 100; % 区域大小 (100×100)
p = 0.05; % 簇头概率
num_rounds = 500; % 最大仿真轮数
k_rounds = 10; % 记录前k轮的分簇情况
% 能量参数
initial_energy = 1000; % 初始能量
energy_threshold = 0; % 能量耗尽阈值
% 通信能量消耗(根据实验指导书)
energy_receive_CH_info = 1; % 簇成员:接收候选簇头信息
energy_send_join_msg = 2; % 簇成员:通知簇头成为其成员
energy_CH_send_info = 2; % 候选簇头:发送信息
energy_CH_receive_join = 1; % 候选簇头:被通知成为簇头
energy_member_send_data = 2; % 稳定阶段:成员发送数据
energy_CH_receive_data = 1; % 稳定阶段:簇头接收数据
data_trans_per_round = 10; % 每轮数据传输次数
%% 初始化节点
nodes = struct();
for i = 1:num_nodes
nodes(i).id = i;
nodes(i).x = rand * area_size;
nodes(i).y = rand * area_size;
nodes(i).type = 'N'; % 'N':普通节点, 'C':簇头
nodes(i).selected = false; % 是否当选过簇头(本轮)
nodes(i).ever_selected = false;% 是否曾经当选过簇头
nodes(i).cluster_head = -1; % 所属簇头ID
nodes(i).energy = initial_energy;
nodes(i).temp_rand = rand; % 随机数
nodes(i).times_CH = 0; % 成为簇头的次数
nodes(i).alive = true; % 节点是否存活
nodes(i).last_CH_round = -1; % 上次成为簇头的轮数
end
%% 存储结果
cluster_history = cell(k_rounds, 1);
first_dead_round = -1;
energy_history = zeros(num_rounds, 1);
alive_nodes_history = zeros(num_rounds, 1);
cluster_head_history = zeros(num_rounds, 1);
%% LEACH算法主循环
fprintf('开始LEACH协议仿真...\n');
fprintf('节点数量: %d, 区域大小: %dx%d, 簇头概率: %.2f\n', ...
num_nodes, area_size, area_size, p);
for r = 1:num_rounds
% 检查是否所有节点都死亡
alive_nodes = sum([nodes.alive]);
alive_nodes_history(r) = alive_nodes;
if alive_nodes == 0
fprintf('所有节点在第 %d 轮全部死亡!\n', r-1);
break;
end
% 重置节点类型和簇头归属
for i = 1:num_nodes
if nodes(i).alive
nodes(i).type = 'N';
nodes(i).cluster_head = -1;
nodes(i).selected = false;
nodes(i).temp_rand = rand;
end
end
%% 簇头选择阶段
cluster_heads = [];
for i = 1:num_nodes
if ~nodes(i).alive
continue;
end
% 计算阈值 T(n) - 修正的LEACH阈值公式
if nodes(i).ever_selected
T = p / (1 - p * mod(r, round(1/p)));
else
T = p;
end
% 检查是否成为簇头
if nodes(i).temp_rand <= T
nodes(i).type = 'C';
nodes(i).selected = true;
nodes(i).ever_selected = true;
nodes(i).times_CH = nodes(i).times_CH + 1;
nodes(i).last_CH_round = r;
cluster_heads = [cluster_heads, i];
end
end
cluster_head_history(r) = length(cluster_heads);
%% 簇形成阶段 - 普通节点选择最近的簇头
for i = 1:num_nodes
if nodes(i).alive && nodes(i).type == 'N' && ~isempty(cluster_heads)
min_dist = inf;
nearest_CH = -1;
% 找到最近的存活簇头
for j = 1:length(cluster_heads)
ch_index = cluster_heads(j);
if nodes(ch_index).alive
dx = nodes(i).x - nodes(ch_index).x;
dy = nodes(i).y - nodes(ch_index).y;
dist = sqrt(dx*dx + dy*dy);
if dist < min_dist
min_dist = dist;
nearest_CH = ch_index;
end
end
end
if nearest_CH ~= -1
nodes(i).cluster_head = nearest_CH;
end
end
end
%% 能量消耗计算
current_round_energy = 0;
for i = 1:num_nodes
if ~nodes(i).alive
continue;
end
energy_consumption = 0;
if nodes(i).type == 'C' % 簇头节点
% 计算该簇头的成员数量
member_count = 0;
for j = 1:num_nodes
if nodes(j).alive && nodes(j).cluster_head == i
member_count = member_count + 1;
end
end
% 簇头能量消耗(根据实验指导书)
% 簇建立阶段:发送信息(-2) + 接收加入消息(成员数 × -1)
energy_consumption = energy_CH_send_info + member_count * energy_CH_receive_join;
% 稳定阶段:接收数据(成员数 × 10 × -1)
energy_consumption = energy_consumption + member_count * data_trans_per_round * energy_CH_receive_data;
else % 普通节点
if nodes(i).cluster_head ~= -1 % 有归属簇头
% 簇建立阶段:接收簇头信息(-1) + 发送加入消息(-2)
energy_consumption = energy_receive_CH_info + energy_send_join_msg;
% 稳定阶段:发送数据(10 × -2)
energy_consumption = energy_consumption + data_trans_per_round * energy_member_send_data;
else % 没有归属簇头,只接收信息
energy_consumption = energy_receive_CH_info * length(cluster_heads);
end
end
nodes(i).energy = nodes(i).energy - energy_consumption;
current_round_energy = current_round_energy + energy_consumption;
% 检查节点是否死亡
if nodes(i).energy <= energy_threshold
nodes(i).alive = false;
nodes(i).energy = 0;
% 记录第一个节点死亡
if first_dead_round == -1
first_dead_round = r;
fprintf('第一个节点在第 %d 轮能量耗尽!\n', r);
end
end
end
energy_history(r) = current_round_energy;
%% 记录前k轮的分簇情况
if r <= k_rounds
round_data = struct();
round_data.round = r;
round_data.cluster_heads = cluster_heads;
round_data.node_states = [];
for i = 1:num_nodes
node_state = struct();
node_state.id = nodes(i).id;
node_state.type = nodes(i).type;
node_state.cluster_head = nodes(i).cluster_head;
node_state.energy = nodes(i).energy;
node_state.x = nodes(i).x;
node_state.y = nodes(i).y;
node_state.alive = nodes(i).alive;
round_data.node_states = [round_data.node_states, node_state];
end
cluster_history{r} = round_data;
end
% 显示进度
if mod(r, 50) == 0
fprintf('已完成 %d 轮仿真,存活节点: %d\n', r, alive_nodes);
end
end
actual_rounds = r; % 实际仿真轮数
%% 可视化前k轮的分簇情况
fprintf('\n生成前%d轮分簇可视化...\n', k_rounds);
figure('Position', [100, 100, 1400, 800], 'Name', 'LEACH分簇过程');
for r = 1:min(k_rounds, actual_rounds)
if r > length(cluster_history) || isempty(cluster_history{r})
continue;
end
subplot(2, 5, r);
hold on;
grid on;
axis equal;
round_data = cluster_history{r};
% 绘制普通节点
normal_nodes_x = [];
normal_nodes_y = [];
cluster_head_x = [];
cluster_head_y = [];
for i = 1:length(round_data.node_states)
node = round_data.node_states(i);
if node.alive
if node.type == 'N'
if node.cluster_head == -1
% 孤立节点
plot(node.x, node.y, 'k^', 'MarkerSize', 6, 'MarkerFaceColor', 'black');
else
% 有簇头的普通节点
normal_nodes_x = [normal_nodes_x, node.x];
normal_nodes_y = [normal_nodes_y, node.y];
end
else
% 簇头节点
cluster_head_x = [cluster_head_x, node.x];
cluster_head_y = [cluster_head_y, node.y];
end
end
end
% 绘制有簇头的普通节点
if ~isempty(normal_nodes_x)
plot(normal_nodes_x, normal_nodes_y, 'b*', 'MarkerSize', 6);
end
% 绘制簇头节点
if ~isempty(cluster_head_x)
plot(cluster_head_x, cluster_head_y, 'ro', 'MarkerSize', 10, 'LineWidth', 2, 'MarkerFaceColor', 'red');
end
% 绘制连接线
for i = 1:length(round_data.node_states)
node = round_data.node_states(i);
if node.alive && node.type == 'N' && node.cluster_head ~= -1
CH_id = node.cluster_head;
if CH_id <= length(round_data.node_states) && round_data.node_states(CH_id).alive
CH_node = round_data.node_states(CH_id);
plot([node.x, CH_node.x], [node.y, CH_node.y], 'g-', 'LineWidth', 0.5);
end
end
end
title(sprintf('第 %d 轮 - 簇头: %d个', r, length(round_data.cluster_heads)));
xlabel('X坐标');
ylabel('Y坐标');
xlim([0 area_size]);
ylim([0 area_size]);
end
sgtitle('LEACH协议前10轮分簇情况');
%% 显示详细的分簇信息
fprintf('\n=== 前%d轮分簇详情 ===\n', k_rounds);
for r = 1:min(k_rounds, length(cluster_history))
if ~isempty(cluster_history{r})
round_data = cluster_history{r};
fprintf('\n第%d轮: 簇头%d个\n', round_data.round, length(round_data.cluster_heads));
% 统计每个簇头的成员数量
CH_members = containers.Map();
isolated_nodes = 0;
for i = 1:length(round_data.node_states)
node = round_data.node_states(i);
if node.alive && node.cluster_head ~= -1
CH_key = num2str(node.cluster_head);
if isKey(CH_members, CH_key)
CH_members(CH_key) = CH_members(CH_key) + 1;
else
CH_members(CH_key) = 1;
end
elseif node.alive && node.cluster_head == -1 && node.type == 'N'
isolated_nodes = isolated_nodes + 1;
end
end
% 显示簇头及其成员数
CH_keys = keys(CH_members);
fprintf(' 簇头详情:\n');
for j = 1:length(CH_keys)
fprintf(' 节点%s: %d个成员\n', CH_keys{j}, CH_members(CH_keys{j}));
end
fprintf(' 孤立节点: %d个\n', isolated_nodes);
end
end
%% 绘制性能分析图
fprintf('\n生成性能分析图...\n');
figure('Position', [100, 100, 1200, 900], 'Name', 'LEACH协议性能分析');
% 截取实际仿真轮数的数据
x_rounds = 1:actual_rounds;
subplot(2, 3, 1);
plot(x_rounds, alive_nodes_history(1:actual_rounds), 'b-', 'LineWidth', 2);
hold on;
if first_dead_round ~= -1
plot([first_dead_round, first_dead_round], [0, num_nodes], 'r--', 'LineWidth', 1.5);
% 修正文本标注,避免使用换行符
text(first_dead_round, num_nodes/2, sprintf('第一个节点死亡 第%d轮', first_dead_round), ...
'HorizontalAlignment', 'center', 'BackgroundColor', 'white', 'FontSize', 8);
end
xlabel('轮数');
ylabel('存活节点数');
title('网络生存情况');
grid on;
legend('存活节点数', 'Location', 'northeast');
subplot(2, 3, 2);
plot(x_rounds, energy_history(1:actual_rounds), 'r-', 'LineWidth', 2);
xlabel('轮数');
ylabel('每轮总能量消耗');
title('网络能量消耗趋势');
grid on;
subplot(2, 3, 3);
plot(x_rounds, cluster_head_history(1:actual_rounds), 'g-', 'LineWidth', 2);
xlabel('轮数');
ylabel('簇头数量');
title('每轮簇头数量变化');
grid on;
% 计算网络平均能量
avg_energy_history = zeros(actual_rounds, 1);
for r_idx = 1:actual_rounds
alive_indices = find([nodes.alive]);
if ~isempty(alive_indices)
avg_energy_history(r_idx) = mean([nodes(alive_indices).energy]);
else
avg_energy_history(r_idx) = 0;
end
end
subplot(2, 3, 4);
plot(x_rounds, avg_energy_history, 'm-', 'LineWidth', 2);
xlabel('轮数');
ylabel('平均能量');
title('网络平均能量变化');
grid on;
% 绘制节点能量分布(第1轮和第k_rounds轮)
subplot(2, 3, 5);
if ~isempty(cluster_history) && ~isempty(cluster_history{1})
energies_round1 = [cluster_history{1}.node_states.energy];
histogram(energies_round1, 20, 'FaceColor', 'blue', 'FaceAlpha', 0.7);
xlabel('节点能量');
ylabel('节点数量');
title('第1轮节点能量分布');
grid on;
end
subplot(2, 3, 6);
last_round = min(k_rounds, actual_rounds);
if last_round <= length(cluster_history) && ~isempty(cluster_history{last_round})
energies_last = [cluster_history{last_round}.node_states.energy];
histogram(energies_last, 20, 'FaceColor', 'red', 'FaceAlpha', 0.7);
xlabel('节点能量');
ylabel('节点数量');
title(sprintf('第%d轮节点能量分布', last_round));
grid on;
end
%% 输出最终结果
fprintf('\n=== 仿真结果汇总 ===\n');
fprintf('节点总数: %d\n', num_nodes);
fprintf('仿真区域: %d×%d\n', area_size, area_size);
fprintf('簇头概率: %.2f\n', p);
fprintf('初始能量: %d\n', initial_energy);
fprintf('实际仿真轮数: %d\n', actual_rounds);
fprintf('第一个节点死亡轮数: %d\n', first_dead_round);
fprintf('最终存活节点数: %d\n', alive_nodes_history(actual_rounds));
fprintf('网络生存周期: %.1f%%\n', (alive_nodes_history(actual_rounds)/num_nodes)*100);
% 计算成为簇头的平均次数
alive_indices = find([nodes.alive]);
if ~isempty(alive_indices)
avg_CH_times = mean([nodes(alive_indices).times_CH]);
fprintf('节点平均成为簇头次数: %.2f\n', avg_CH_times);
end
%% 保存结果
results = struct();
results.parameters.num_nodes = num_nodes;
results.parameters.area_size = area_size;
results.parameters.initial_energy = initial_energy;
results.parameters.cluster_head_prob = p;
results.first_dead_round = first_dead_round;
results.final_alive_nodes = alive_nodes_history(actual_rounds);
results.total_rounds = actual_rounds;
results.cluster_history = cluster_history;
results.energy_history = energy_history(1:actual_rounds);
results.alive_nodes_history = alive_nodes_history(1:actual_rounds);
results.cluster_head_history = cluster_head_history(1:actual_rounds);
save('leach_simulation_complete.mat', 'results', 'nodes');
fprintf('\n仿真结果已保存到 leach_simulation_complete.mat\n');
fprintf('\nLEACH协议仿真完成!\n');
修改代码MATLAB
最新发布