
做无线传感器网络方向的同学应该对这几个痛点特别有共鸣节点电池有限发射功率不可能一直加覆盖范围受不了直传必须靠中继一跳一跳把数据收敛到汇聚节点更麻烦的是物理层安全里总有一个“无处不在”的窃听者它不主动干扰就安安静静在某个位置偷听转发信号。这三件事叠在一起构成了一个非常典型的研究问题在带窃听者的多跳WSNs中怎么挑出一条端到端安全容量最大的转发路径同时还要考虑真实收发机里不可避免的硬件噪声用Matlab把这套方案完整跑通并和常见基准方案对比。这个项目就是围绕这个组合问题展开的。它属于无线通信物理层安全与路由层交叉方向的仿真工作核心是用“路径选择”这种低成本手段增强多跳传输的保密性适合正在做WSNs安全方向毕业设计、或者想快速上手物理层安全仿真的同学直接参考。全文会从系统模型、安全容量公式、路径搜索算法、Matlab代码实现到仿真结果分析整套过一遍最后把我在实际调试中踩过的坑也一并列出来。1. 项目背景与核心问题拆解1.1 多跳WSNs为什么需要路径选择无线传感器网络里存在一个天然的矛盾单个节点功率有限和数据汇聚节点之间的距离往往超过单跳可靠通信范围所以“多跳收集-传输”几乎是必须的。所谓多跳收集-传输就是数据从源节点开始经过若干中间传感器节点的存储-转发逐跳传递到最终的数据收集端。这个过程中每一跳都在“暴露”数据。如果所有数据都走同一条固定路由那窃听者只要守在那个路径的关键位置上就能以很低的成本持续窃听多跳通信内容。而如果我们在路由层面做“路径选择”让数据每次尽量绕开窃听风险高的区域就能在不增加额外发射功率、不部署协作干扰节点的情况下提升安全性能。这也是为什么“路径选择”在WSNs安全增强里被反复研究——它不需要改动物理层信号处理方案只改一下路由策略就能见效在工程上部署成本很低。这个项目采用的正是这种思路在一个存在窃听者监听的网络场景中把所有可达路径都找出来算每条路径的端到端安全容量选最大值对应的路径作为当前最优路径。你可以把路径选择理解成“安全版的路由优化”传统的路由优化目标是跳数最少或时延最短而现在把优化目标换成“保密容量最大”。1.2 窃听者与硬件噪声安全传输面临的双重威胁先说说窃听者。这个角色在物理层安全研究中太常用了它是被动节点不发送任何信号不干扰任何合法通信唯一做的事情就是监听某个发射节点的信号。因为它不主动发射合法网络无法通过能量检测等手段轻易发现它。在多跳网络中每一跳都会有一个发射节点窃听者监听的是当前发射节点的信号。如果窃听者离某一跳的发射节点很近那这一跳的信噪比就会很高安全容量会被严重压缩。再来说硬件噪声这个点是很多初做物理层安全仿真的人最容易忽略的。早期文献里默认收发机是理想器件接收端只有热噪声。但真实设备里射频前端、功率放大器、本振都会引入非线性失真这种失真和信号功率成正比——信号越强硬件带进来的失真也越强。学术上通常用加性高斯失真来建模这类“硬件损伤Hardware Impairments”。它的影响有多直接在没有硬件噪声的理想模型里我们可以通过无限增大发射功率来让信道容量无限增长安全容量也随之上升。但一旦加入硬件损伤模型信噪比会达到一个上限这个上限就是硬件损伤系数的平方倒数功率加得再多也没用。这等于告诉我们在真实系统里“无脑加功率”是行不通的安全性能的瓶颈会从“热噪声限制”变成“器件质量限制”。所以在研究路径选择性能时必须把硬件噪声放进去否则结论会偏乐观。这个项目把这些因素全部纳入同一条链路进行建模和仿真这就比单纯比较理想条件下的路由算法更有工程参考价值。2. 系统建模与路径选择算法设计2.1 网络模型与基本假设设计系统模型的第一步是把仿真场景抽象成可以“量化计算”的形式。这个项目采用的最基本网络模型如下网络部署在一个100m乘100m的方形区域内包含一个源节点S、一个数据汇聚节点D、多个中继节点R以及一个窃听节点E。源节点S和汇聚节点D的位置固定中继节点在区域内随机部署。只有距离不超过通信半径R_com的节点之间才能建立可直接通信的链路这符合实际传感器节点的通信能力约束。窃听者E是固定的、被动的它只窃听每一跳发射节点的信号不参与合法通信。合法链路采用半双工模式每一跳使用不同的时隙或正交信道因此跳间干扰忽略不计。信道同时包含大尺度路径损耗和小尺度瑞利衰落。这个模型的合理性在于它把问题简化成了“路径拓扑选择”问题而不是复杂的跨层联合优化问题。干扰被正交隔离能量模型被统一为发射功率安全性能完全由路径的几何拓扑和信道衰落决定这样得到的结论更清晰适合做理论分析。另外值得一提的是项目中假设窃听者的瞬时信道状态信息CSI是已知的。有人会问窃听者是被动的合法网络怎么可能知道它的CSI这在真实场景中确实做不到但这是物理层安全性能分析的标准做法相当于在计算一个“性能上界”。如果在这个最有利的假设下路径选择方案依然明显优于基准方案那说明该方案值得进一步研究。2.2 安全容量与硬件噪声模型多跳路径的安全容量是整个仿真的核心指标需要先把它定义清楚。单条链路的瞬时安全容量定义为合法信道容量与窃听信道容量之差即C_s max(0, log2(1 γ_legit) - log2(1 γ_eve))其中γ_legit是合法接收端的信噪比γ_eve是窃听接收端的信噪比。这里的max(0, ·)表示信道不安全时安全容量取0网络安全层将无法保证保密性。对于多跳路径需要把每一跳的安全容量都算出来。设路径P {S, R1, R2, ..., D}共有L跳则路径的安全容量定义为所有跳安全容量的最小值C_path min(C_s1, C_s2, ..., C_sL)为什么取最小值而非平均值因为端到端保密性的本质是“整个消息串中任何一比特都不能被泄密”。只要某一跳被窃听者完整截获整条路径就不安全了。所以最弱一跳决定了整条路径的安全性这与“木桶效应”是一个道理。硬件噪声对信噪比的影响需要单独建模。这里采用目前文献中常用的残余硬件损伤模型在接收端信号中额外引入一个与信号功率成正比的加性高斯噪声项η。设发射功率为P信道增益为G噪声功率为N0硬件损伤系数为k则接收端的等效信噪比为γ P * G / (N0 k² * P * G)当k0时模型退化为理想硬件场景。当k0时无论P如何增大信噪比都趋于1/k²。这个公式非常关键直观说明硬件噪声限制了信噪比天花板。项目中合法节点硬件损伤系数k_legit和窃听者设备系数k_eve是分开设置的。这样做的原因是实际场景里窃听者可能使用更高质量的接收设备比如专门窃听的专业设备它的失真系数往往比普通传感器节点更低。这两者的差值直接决定了高信噪比下系统的最终安全收益。2.3 路径选择方法的核心思路路径选择算法的目标可以一句话概括在所有从S到D的可行路径中选择一条C_path最大的路径作为数据传输路径。具体流程分三步第一步枚举所有可行路径。在连通图里从源节点出发遍历所有邻接节点直到到达目的节点记录下所有简单路径。这个用深度优先搜索DFS就能实现。实话说这个步骤在小规模网络中完全够用但中继节点数量超过10个以后路径数量可能会快速膨胀需要设置路径上限或改用其他策略。第二步对每条路径计算C_path。这里有个细节每条路径上每一跳的发射节点到汇聚节点、到窃听者的距离都不同加上独立的小尺度衰落系数所以每条路径的安全容量都会不一样。第三步取C_path最大者作为当前快照下的最优路径。为了性能对比项目中还设置了两个基准方案最短路径方案即跳数最少但完全不考虑安全风险的路径直传方案即源节点直接向汇聚节点发射不经过任何中继。如果网络规模变大导致路径枚举开销过高一个替代方案是贪心逐跳选择从源节点开始每次在邻居中选择使当前跳安全容量最大的节点作为下一跳直到到达汇聚节点。这种方案不是全局最优但计算复杂度低许多。在仿真中基线方案通常包括这种贪心策略。3. Matlab完整实现与关键代码解析3.1 仿真代码的整体架构做仿真不能上来就写一大段脚本先理清代码结构。我把整个程序拆成了四个模块第一个模块是参数设置统一管理发射功率范围、噪声功率、路径损耗指数、硬件损伤系数、通信半径等所有仿真参数。全部放在文件开头方便改参数后重跑。第二个模块是网络拓扑生成。每次蒙特卡洛快照都会重新随机部署中继节点位置然后根据通信半径生成邻接矩阵再用深度优先搜索检查源节点到汇聚节点是否连通。不连通就重新生成节点位置直到得到一张可用的连通图。第三个模块是路径枚举与安全容量计算。先用DFS得到所有从源到汇的简单路径然后对每条路径的每一跳计算瞬时安全容量再取最小值得到路径安全容量。第四个模块是蒙特卡洛主循环。固定一组网络快照和信道参数循环多次对随机信道衰落求平均最终输出安全容量曲线和安全中断概率曲线。3.2 信道增益与安全容量计算函数先写信道增益函数。路径损耗采用单斜率模型参考距离设为1米瑞利衰落幅度平方服从指数分布function G channel_gain(d, h, PL_exp, ref_dist) % d: 通信距离(m) % h: 瑞利衰落系数(复数) % PL_exp: 路径损耗指数 % ref_dist: 参考距离(m) PL (ref_dist / max(d, 1e-3))^PL_exp; G PL * (abs(h)^2); end接着写考虑硬件噪声的跳安全容量计算函数。这个函数需要传入发射功率P、合法链路信道增益、窃听链路信道增益、噪声功率、合法节点损伤系数和窃听者损伤系数function C_hop hop_secrecy_capacity(P, G_legit, G_eve, N0, k_legit, k_eve) % 合法链路等效信噪比 gamma_legit P * G_legit / (N0 k_legit^2 * P * G_legit); % 窃听链路等效信噪比 gamma_eve P * G_eve / (N0 k_eve^2 * P * G_eve); % 该跳瞬时安全容量 C_hop max(0, log2(1 gamma_legit) - log2(1 gamma_eve)); end这个函数里的log2就是信息论中常用的比特/秒/赫兹单位。写的时候必须注意gamma_legit和gamma_eve都是线性信噪比不是dB值计算容量前不需要做任何单位转换。3.3 路径枚举实现DFS路径枚举用迭代栈实现更稳不推荐递归版本因为递归深了容易超栈。核心函数如下function paths find_all_paths(adj, src, dst) % adj: 邻接矩阵 % src: 源节点编号 % dst: 目的节点编号 paths {}; stack {src}; while ~isempty(stack) path stack{end}; stack(end) []; current path(end); neighbors find(adj(current, :)); for nb neighbors if nb dst paths{end1} [path, dst]; %#okAGROW elseif ~ismember(nb, path) stack{end1} [path, nb]; %#okAGROW end end end end注意这里“路过的节点不重复”是一个隐式约束利用~ismember(nb, path)排除了回路保证枚举的是简单路径。在实际仿真中我将路径上限设置为1000条超过上限时报错提示防止网络拓扑过于密集导致路径爆炸。3.4 蒙特卡洛主循环实现下面给出主程序核心代码。这里做了一个关键设计每条路径每一跳的合法信道和窃听信道都独立生成瑞利衰落系数保证统计独立性。clc; clear; close all; rng(42); % ---- 系统参数 ---- area_len 100; % 区域边长(m) R_com 40; % 通信半径(m) num_relay 6; % 中继节点数量 num_eve 1; % 窃听者数量 PL_exp 2.7; % 路径损耗指数 ref_dist 1; % 参考距离(m) N0_dBm -90; % 噪声功率(dBm) N0 10^(N0_dBm/10) / 1000; % 噪声功率(W) k_legit 0.05; % 合法节点硬件损伤系数 k_eve 0.02; % 窃听者硬件损伤系数 P_dBm -10:5:25; % 发射功率范围 P_W 10.^(P_dBm/10) / 1000; % 发射功率(W) num_trials 2000; % 蒙特卡洛次数 Rs 0.5; % 目标安全速率 % ---- 节点位置固定部分 ---- src_pos [20, 50]; dst_pos [80, 50]; src_id 1; dst_id 2 num_relay; num_total 2 num_relay; eve_pos [55, 45]; % ---- 预分配存储 ---- C_opt_avg zeros(length(P_W), 1); C_short_avg zeros(length(P_W), 1); C_direct_avg zeros(length(P_W), 1); Pout_opt zeros(length(P_W), 1); Pout_short zeros(length(P_W), 1); % ---- 蒙特卡洛循环 ---- for tr 1:num_trials % 随机生成中继直到网络连通 while true relay_pos rand(num_relay, 2) * area_len; node_pos [src_pos; relay_pos; dst_pos]; adj zeros(num_total); for ii 1:num_total for jj ii1:num_total if norm(node_pos(ii,:) - node_pos(jj,:)) R_com adj(ii, jj) 1; adj(jj, ii) 1; end end end if ~isempty(find_all_paths(adj, src_id, dst_id)) break; end end % 枚举所有路径 all_paths find_all_paths(adj, src_id, dst_id); % 找出跳数最少的路径作为基准方案 hop_counts cellfun(length, all_paths) - 1; [~, idx_min_hop] min(hop_counts); short_path all_paths{idx_min_hop}; % 对每个发射功率取值计算 for pi 1:length(P_W) P P_W(pi); C_opt -inf; C_short -inf; % 遍历每条路径 for pp 1:length(all_paths) path all_paths{pp}; num_hop length(path) - 1; C_hops zeros(1, num_hop); for hh 1:num_hop tx path(hh); rx path(hh1); h_legit sqrt(1/2) * (randn 1i*randn); h_eve sqrt(1/2) * (randn 1i*randn); d_legit norm(node_pos(rx,:) - node_pos(tx,:)); d_eve norm(eve_pos - node_pos(tx,:)); G_legit channel_gain(d_legit, h_legit, PL_exp, ref_dist); G_eve channel_gain(d_eve, h_eve, PL_exp, ref_dist); C_hops(hh) hop_secrecy_capacity(P, G_legit, G_eve, N0, k_legit, k_eve); end C_path min(C_hops); if C_path C_opt C_opt C_path; end if isequal(path, short_path) C_short C_path; end end C_opt_avg(pi) C_opt_avg(pi) C_opt / num_trials; C_short_avg(pi) C_short_avg(pi) C_short / num_trials; % 直传链路 h_direct sqrt(1/2) * (randn 1i*randn); h_direct_eve sqrt(1/2) * (randn 1i*randn); d_direct norm(dst_pos - src_pos); d_direct_eve norm(eve_pos - src_pos); G_direct channel_gain(d_direct, h_direct, PL_exp, ref_dist); G_direct_eve channel_gain(d_direct_eve, h_direct_eve, PL_exp, ref_dist); C_direct hop_secrecy_capacity(P, G_direct, G_direct_eve, N0, k_legit, k_eve); C_direct_avg(pi) C_direct_avg(pi) C_direct / num_trials; % 统计安全中断概率 if C_opt Rs Pout_opt(pi) Pout_opt(pi) 1; end if C_short Rs Pout_short(pi) Pout_short(pi) 1; end end end Pout_opt Pout_opt / num_trials; Pout_short Pout_short / num_trials; % ---- 绘图 ---- figure; plot(P_dBm, C_opt_avg, -o, LineWidth, 1.6); hold on; plot(P_dBm, C_short_avg, -s, LineWidth, 1.6); plot(P_dBm, C_direct_avg, -^, LineWidth, 1.6); grid on; xlabel(发射功率 (dBm)); ylabel(平均安全容量 (bit/s/Hz)); legend(最优路径选择, 最短路径, 直传, Location, best);上面代码在单次仿真中可以原地运行。建议先用num_trials200跑一遍验证逻辑确认无误后再加长蒙特卡洛次数。注意在每次循环里每条链路的信道系数都是实时重新生成的而不是一次生成后反复使用这样才能体现随机衰落对安全容量的统计影响。写到这里有一点需要特别提醒这段代码里的每条路径计算都会重复生成随机信道系数。当所有路径都需要遍历时如果路径很多随机数生成开销会非常大。一个优化技巧是预先为每条可能的链路生成一组独立的小尺度衰落然后在计算跳容量时直接调用这样既能保证随机性又能避免重复生成开销实际跑起来速度会快不少。4. 仿真结果与性能对比分析4.1 仿真场景与参数设置下面是我在本项目中实际使用的一组仿真参数表。不同场景下结果会有差异但整体趋势稳定。参数取值说明仿真区域100m x 100m正方形区域中继数量6随机部署通信半径40m决定拓扑连通边路径损耗指数2.7典型地面传感网环境噪声功率-90dBm对应接收机底噪合法节点损伤系数0.05普通传感器器件水平窃听者损伤系数0.02假设窃听设备较好发射功率范围-10dBm到25dBm覆盖低功耗到中高功耗目标安全速率0.5 bit/s/Hz中断概率阈值蒙特卡洛次数2000保证统计平均稳定中继数量选6是因为这个规模既能体现多跳路径的多样性又不会让路径枚举爆掉。窃听者放在源节点和汇聚节点连线的中偏侧位置制造一个“窃听热区”这样路径选择的多跳绕行效果更容易凸显出来。4.2 核心结果解读运行仿真后可以明显看到三层规律。第一层路径选择方案的平均安全容量在整个发射功率范围内都优于最短路径方案更明显优于直传方案。差距在中等发射功率时最大可以拉开到1到2 bit/s/Hz。原因很好理解路径选择绕开了靠近窃听者的区域让每一跳的窃听信噪比都相对较低而直传方案只有一跳源节点到窃听者的距离可能很近安全容量容易被压得很低。第二层随着发射功率提高三条曲线的增速都在变缓到最后几乎饱和。这个现象就是用硬件损伤模型之后最核心的变化。因为信噪比被1/k²卡住继续增大功率不再有用。把k_legit从0.05改成0.01之后重跑会发现饱和点明显右移、饱和容量更高这就验证了硬件器件质量对安全性能的硬约束。第三层从安全中断概率来看路径选择方案的中断概率在较低发射功率下就有明显下降。以目标安全速率Rs0.5 bit/s/Hz为例路径选择方案在0dBm左右就能把中断概率压到5%以下而最短路径方案需要接近10dBm才能达到同样水平。这个差距对实际系统设计非常有参考价值意味着通过路径选择可以节省可观察量的发射功率预算对电池供电的传感器节点来说这是非常宝贵的能量节省。如果不加入硬件噪声模型把k_legit和k_eve都设为0仿真结果会显示安全容量随发射功率无限近似线性增长路径选择方案的增益在高功率区反而更明显。但这和真实系统不符。加入硬件噪声后高功率区的安全容量增益被压缩反而更能说明“路径选择在高信噪比区间的增益更体现在中断概率上而不是容量绝对值上”。5. 实际运行中的常见问题与调试经验5.1 路径枚举的爆炸与拓扑连通问题这个坑我在第一次写代码时就踩过。中继节点数量一多、通信半径一调大路径数量会指数级增长。比如num_relay10、R_com60时简单路径数量可能上万条每轮蒙特卡洛都枚举一遍会让仿真慢到难以接受。我的处理经验是在小规模网络下用穷举路径确保算法正确性当网络规模扩大时把路径枚举替换为贪心逐跳策略。另外随机生成拓扑后必须检查是否存在从源到汇的连通路径否则后面会算出一堆空路径。我用的办法是while循环重新生成节点位置直到找到至少一条路径才跳出循环。注意这个检查不能只查邻接矩阵非空必须真的做一次DFS搜索。5.2 复现结果时的固定随机种子蒙特卡洛仿真最怕的就是结果不稳定。如果每次运行结果差异大很可能不是代码问题而是没有固定随机数种子。我在代码开头固定rng(42)这样所有随机数序列可以复现调试时对照修改前后的效果非常方便。但固定种子也有另一个坑如果你在循环里调整了参数的顺序随机数消耗会变化同一种子下的结果也不会完全一致。所以正确做法是先固定结构再调参数。不要这边加一段调试代码那边又减一段调试代码否则你看到的曲线变化很难判断是代码改动引起的还是随机性引起的。5.3 瑞利衰落生成的两个细节生成瑞利衰落系数时要保证实部和虚部都是均值为0、方差为1/2的高斯随机变量模的平方才服从均值为1的指数分布。代码里用sqrt(1/2) * (randn 1i*randn)就是干这个的。如果忘掉sqrt(1/2)幅度的平均功率会变成2信道增益整体偏大得出的安全容量会偏高整个对比曲线的基准就不对了。另一个细节是dB与线性单位的转换。噪声功率N0我习惯先用10^(N0_dBm/10)/1000转换成瓦特发射功率同理。换算错误在这种仿真里特别常见因为dBm是相对毫瓦的对数单位10^(x/10)之后还要再除1000。我用表格的形式把这个转换过程写进代码注释里避免下次忘记。5.4 性能结果偏低的排查顺序如果仿出来的安全容量曲线比预期低很多我会按下面的顺序排查先检查信道增益是否正常单独打印某条链路的G值看路径损耗在100米量级是否符合预期。再检查信噪比公式尤其是硬件噪声项是否错误地加到了分子上。然后检查路径安全容量的聚合方式确认是否用了最小值而非平均值。最后检查窃听者位置如果窃听者恰好离所有发射节点都很远安全容量会偏高但这不代表算法无效只说明该场景下窃听威胁小需要调整窃听者位置重新对比。实际调试中我一般先用一个3中继的小拓扑跑通逻辑打印出每跳安全容量、路径安全容量、最终选择路径逐行核对确认无误后再扩展到完整场景。这种做法比直接全量仿真再回头找bug高效得多。这个仿真项目本身也是一个很好的组合型教学案例。它把路由层、物理层安全、硬件非理想性三个方向的知识点串在了一起从问题建模到算法设计再到代码实现走完整个流程之后你会对这些公式的物理含义理解得更踏实。后续如果想继续扩展可以考虑加入协作干扰节点、功率分配联合优化、移动窃听者模型甚至把路径选择和网络编码结合起来做方向都是通的。