简介:面向水下传感器网络(UWSNs)的研究者、教师和系统设计者,这份压缩包提供了一整套可运行的仿真代码与配套资料,聚焦声波通信、能量管理、自组织定位、MAC与路由协议等核心议题。包内共131个文件,核心为30个C++实现文件(.cc)及对应头文件(.h),配合14个Tcl网络场景脚本、awk统计工具、PDF理论文档以及能量/延迟/吞吐量等结果文件,既可以支撑协议算法验证,也便于课堂实验演示。整包约22.23MB,已有122人学习使用。通过结合代码与文档,读者能快速搭建水下多跳网络仿真环境,复现不同参数配置下的网络性能,也可在现有模块基础上编写新协议,比较端到端延时、丢包率与节点生存时间等指标。对于初涉UWSNs的学者,这是一条直观的入门路径;对于工程师,则是评估网络部署方案和优化协议参数的有力工具。
1. 打开这个压缩包前,先搞清水下传感器网络仿真要解决什么
如果你以前跑过地面无线传感器网络的仿真,第一次打开这类水下传感器网络仿真代码和资料包时会明显蒙住:节点多了一堆麦克风一样的调制解调器模块,信道模型不再是你熟悉的自由空间衰减,跑完一个Ping的端到端延迟居然可能超过一秒。这个包解决的不是“怎么仿真”的问题,而是“怎么把水下声学场景仿真得不像空中无线网”的问题——它整合了声学信道、传播延迟、能量消耗和路由协议,让你把精力花在调协议参数上,而不是从零发明一套仿真框架。
适合两类人:一类是刚接手水下监测课题的在校生,需要快速搭出可复现的仿真环境;另一类是做海洋物联网预研的工程团队,想先通过仿真圈定可行路线。这套资料的价值在于仿真门槛高,水下场景盲区多,资料的代码框架能省下大量绕路时间。但要清楚一点:拿到代码不等于跑通,跑通不等于结果可信,水下仿真每一项参数背后都是物理约束,盲目沿用默认配置大概率翻车。
2. 水下声学信道为什么让地面仿真的经验全部作废
2.1 电磁波在水里走不远:声学链路的物理差异
地面无线传感器网络的仿真里,信道模型往往基于射频电波传播。这个假设在水下一开始就不成立:电磁波在海水中的衰减系数随频率急剧上升,要实现深海几公里级的通信,需要的发射功率和天线尺寸远超设备能承受的范围。于是水下数据链路转向声学,声速约为1500米每秒,比电磁波低了近五个量级,这就是为什么水下网络所有协议都要重新设计。
声学链路的第二个特征是收发双端的强不对称性。很多水下节点是电池供电的自主设备,而岸基或水面网关往往有充足电力,这导致链路预算、功率控制策略和地面节点间对称假设完全不同。更关键的是,声波在水中的传播不遵循各向同性,它受温度、盐度、压力分层的折射影响,同样的发射功率在不同的水层可能覆盖半径相差数倍。对仿真来说,这意味着信道模型的参数不是随便填的,一套可用的水下传感器网络仿真代码,核心价值就藏在这些物理模型的选择上。
2.2 传播损失与Thorp公式:把你的信道模型从F式换成水下经验式
地面仿真常见的对数距离路径损耗模型,在水下只能覆盖一些浅水短距的简单场景。深水声传播损失通常用经验公式近似,最常见的写法是Thorp公式描述吸收系数的频率依赖关系。吸收系数的典型形式为:
\alpha = \frac{0.11{f^2}}{1+f^2} + \frac{44{f^2}}{4100+f^2} + 2.75\times10^{-4}f^2 + 0.003
其中频率 f 的单位是kHz,\alpha 的单位是dB/km。从公式能看出,频率越高吸收越强,所以在能完成数据率的条件下大家都尽量压低载波频率。仿真中如果把吸收项设成0,结果会乐观得没有任何参考价值——数据包全都能到达,路由协议比不出好坏。
除了吸收,传播损失还要计算几何扩展损失。球形扩展大约为20log10(R) dB,而实际中深水信道介于球形和柱形之间,很多仿真代码里用20log10(R)到10log10(R)之间可配置的扩展系数。我一般建议把扩展系数作为参数暴露在仿真配置里,不要写死在物理层代码里,因为你需要在不同的水体深度场景间做敏感性分析。把Thorp吸收、几何扩展、叠加环境噪声三项都同时激活,这样才能看到水下链路对距离和频率的真实敏感度。
2.3 传播延迟与多径:为什么一个Ping要等3秒
电磁波的传播延迟在仿真里通常被忽略,但水下不行,这绝对是新手最容易栽的地方。1500米每秒的声速意味着一个千量级的包在1公里距离上往返就要约1.3秒,如果经过路由还要多跳,端到端延迟轻松到秒级。很多地面无线网络仿真器默认的传播延迟在微秒量级,如果你直接借用力模型而不换声学传播速度,仿真结果会有数量级的偏差,整套介质访问控制策略都建立在错误基础上。
另一个问题是多径和时变衰落。浅水信道中声波经海面、海底反射,形成显著的多径扩展,直射路径和反射路径时延差可能达数百毫秒。许多水下协议在MAC层加入重传、握手机制,都是因为这里误码率不像地面那么稳定。仿真里不能假定信道是静态的,至少要把节点移动和轻微的声速剖面变化纳入考量。理解了这些差异,再去看代码包里的信道模块,你就明白为什么大家用专门的UAN模块而不是复用WiFi模型了。
3. 用NS-3搭一个最小水下网络:从信道到收发端的完整代码
3.1 最小可运行仿真脚本:两个节点、一条声学信道
我见过太多人一上手就摆几十个节点,结果出了问题根本定位不到。最稳妥的做法是先搭建两个节点的最小系统,确认信道、物理层、MAC、应用层全部打通,再扩展规模。NS-3里可以基于UAN模块来搭建,下面的脚本片段展示了建立一条声学信道并放上两个节点的核心结构。注意不同版本的NS-3类名可能有细微差异,我给出的是较为常见的API写法。
// mini-uan.cc #include "ns3/core-module.h" #include "ns3/network-module.h" #include "ns3/mobility-module.h" #include "ns3/uan-module.h" #include "ns3/applications-module.h" using namespace ns3; NS_LOG_COMPONENT_DEFINE("MiniUan"); int main (int argc, char *argv[]) { // 固定随机种子,保证结果可复现 SeedManager::SetSeed(7); CommandLine cmd; double simTime = 100.0; cmd.AddValue("simTime", "仿真时长(秒)", simTime); cmd.Parse (argc, argv); // 创建水下信道:使用Thorp损耗模型+理想物理层误码(便于先跑通链路) Ptr<UanPropModelThorp> propModel = CreateObject<UanPropModelThorp> (); Ptr<UanChannel> channel = CreateObject<UanChannel> (); channel->SetPropagationModel (propModel); // 两个节点,坐标在水下几米处 NodeContainer nodes; nodes.Create (2); MobilityHelper mobility; mobility.SetPositionAllocator ("ns3::GridPositionAllocator", "MinX", DoubleValue (0), "MinY", DoubleValue (0), "DeltaX", DoubleValue (1000), "DeltaY", DoubleValue (0)); mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); mobility.Install (nodes); // 给节点安装水下网络设备 UanHelper uan; uan.SetMac ("ns3::UanMacAloha"); uan.SetPhy ("ns3::UanPhyGen", "PhyMode", UanTxMode::Create (UanTxMode::FSK, 8000, 4000, UanTxMode::CONST), "TxPowerDb", DoubleValue (185.0), // 水下发射声源级约185dB "RxGainDb", DoubleValue (0)); NetDeviceContainer devices = uan.Install (nodes); // 从节点0向节点1周期发送一个较小负载 OnOffHelper app (ns3::UdpSocketFactory, InetSocketAddress (Ipv4Address ("10.0.0.2"), 9)); app.SetConstantRate (DataRate ("100bps")); app.SetAttribute ("PacketSize", UintegerValue (64)); ApplicationContainer apps = app.Install (nodes.Get (0)); apps.Start (Seconds (1.0)); apps.Stop (Seconds (100.0)); Simulator::Stop (Seconds (simTime)); Simulator::Run (); Simulator::Destroy (); return 0; }这段脚本的逻辑很直白:先构造信道传播模型,再创建两个节点,其中一个周期性发包。关键参数有三处,第一是声源级TxPowerDb,水下通常用声源级而不是毫瓦,因为它反映的是声学辐射强度,185分贝只是一个浅水短距离的典型起点,实际通信距离会随这个值剧烈变化;第二是UanTxMode里设定的载波频率8000赫兹和数据速率4000比特每秒,频率直接决定Thorp吸收的大小;第三是包大小与发送速率,这两个值共同决定信道负载,观测吞吐量拐点时就靠调它们。
3.2 信道模型与物理层参数设置
跑通最小系统后,要把物理层配置换成更实际的模型,否则结果没有任何参考价值。UAN模块常用的信道模型除了Thorp外还有理想模型和Range模型,理想模型忽略传播损失,只能在功能测试时用,一旦开始跑协议对比,就必须切回Thorp,否则所有协议性能曲线都是平的。物理层部分则要配置接收灵敏度、误码率计算方式和信噪比计算模型,这些参数在资料包里通常集中在配置文件里,不要散落在各个源文件里。
参数名称会随NS-3版本波动,但配置思路是一致的:物理层要能算出每个分组的接收功率,并判定是否接收成功。常用的信号干扰噪声比计算方法,会把环境噪声底和同频干扰并入公式。环境噪声级在水下仿真里极其关键,它跟海况和航运活跃度相关,有的代码包把噪声级固定为某个常数,这会让仿真结果失去场景代表性。你可以把噪声级从常量改成一个随海况等级变化的配置项,至少留出高噪声与低噪声两组实验对照。
3.3 数据收发验证与轨迹输出
很多新手跑完脚本只看终端打印就认为仿真完成,这远远不够。你需要确认数据包确实从物理层发给对方并被成功接收,最简单的办法是在仿真脚本里挂上Capture,或者在代码中加入按节点打印收发统计的逻辑。UAN模块通常会提供追踪源,比如PhyRxDrop、PhyTxBegin等,建议把每个关键事件输出到独立的trace文件,而不是打印在屏幕上,因为场景扩大后终端输出会让问题淹没在数据里。
另外要把仿真配置与结果文件分离。我习惯在脚本里只保留仿真逻辑,把坐标、发射功率、频率、包长、仿真时长全部通过命令行参数传入。这样做有两个好处,一是批量实验时只需一个外层循环脚本改参数,二是每次结果文件都带着配置摘要,方便事后追溯。至于结果数据的整理,可以统一把每个节点收到的包序号、时间戳、信号强度导出成文本,后续用Python一类的脚本汇总成吞吐量和延迟表格。
4. 让仿真接近真实:MAC、路由与能量模型的参数怎么配
4.1 MAC协议选型:T-Lohi、SFAMA与ALOHA仿真参数
水下MAC协议选择直接影响网络容量,资料包里通常会包含几种MAC的实现,常见的有ALOHA、SFAMA和T-Lohi。它们的区别在于信道接入方式和对传播延迟的处理。ALOHA最简单,节点直接发包,冲突后按概率重传,但它只适合低负载和延迟不敏感的场景,仿真配置中只需关注重传概率与最大重传次数;SFAMA利用握手预约信道,能显著降低冲突,但每次握手都要付出传播延迟的代价,节点间距越大效率越低。在仿真中你会发现,节点距离从500米增加到3000米时,SFAMA的吞吐量下滑比ALOHA更快,因为控制报文占据了太多时间。
T-Lohi是专门为水下场景设计的基于音调的协议,节点在发送数据前先发一个短音调竞争信道。它适合节点数比较多或突发流量明显的网络,但音调检测对物理层信噪比要求较高,仿真里如果简化物理层,T-Lohi性能会偏乐观。一个常见做法是在仿真代码里跑同一流量下三种MAC的吞吐量对比,而不是只看绝对数值。参数调节时优先关注最大传播距离、RTS重发次数、发送间隙这三项,因为它们决定协议的时间预算。
4.2 移动模型与拓扑:水流把节点推走怎么办
水下节点不是静止的,尤其在海域场景里,洋流会把节点推动,导致拓扑持续变化。许多人把节点设成ConstantPosition模型,这会省事,但实验结果无法反映真实网络的断链和重路由代价。合适的做法是给节点设置一个低漂移速度的随机游走模型或水流向量模型,例如速度0.2米每秒左右,方向缓慢变化,每秒钟更新一次位置。如果你的目标是分析路由稳定性,这个移动模型就不能省。
拓扑本身也有讲究。传统网格拓扑在地面网络中常用,但水下监测任务多采用星型或链型部署,网关浮在水面,水下节点分布在几个深度层。这个差异直接影响路由协议的转发策略:网格里节点有大量邻居可供选择,链型拓扑里转发路径几乎没有冗余。我建议仿真时至少设置一组稀疏链型、一组密集随机布点,对比协议在不同拓扑下的表现。部署坐标不能拍脑袋,应基于目标水域规模和通信半径计算,保证平均节点度在3到6之间,低于这个值很多协议会频繁进入无路由状态。
4.3 能量模型:给每个节点挂上电池与电量轨迹
水下节点的能量约束比地面更严苛,因为换电池的成本极高,很多场景是一次性部署。如果资料包里没有能量跟踪,仿真就缺了最关键的评价维度。给节点挂能量模型时要同时配置能量源与各状态的功耗:发送状态功耗最高、接收状态其次、空闲状态也不可忽略。有的资料包只统计发送和接收,空闲功耗为0,导致很多协议看起来能耗很漂亮,换到实际设备却撑不了多久。
在NS-3中安装能量模型时,要确保它感知到物理层的每次状态切换,而不是简单地在应用层发包时扣一次电量。一个可行的做法是把能量模型挂到网络设备而不是应用层。如果你发现仿真结束后所有节点剩余能量百分比完全相同,那大概率是能量模型没有接入物理层,这个坑在下一章还会展开。能量分析要看曲线而不是终值,至少要记录节点电量随时间的变化轨迹,观察网络是否出现能量空洞,因为某些路由协议会让靠近网关的节点过早耗尽能量。
5. 水下传感器网络仿真避坑指南:现象、原因与解决
5.1 跑完统计全部为0:为什么你的业务包从未离开发射队列
现象:仿真结束,收到的分组数、吞吐量全是0,但终端没有任何报错。
原因:多数情况是MAC层排队机制的问题。水下MAC重传次数有限,如果信道一直忙或冲突持续发生,数据包会被丢弃在队列里,而你不一定能从默认日志看到。还有可能是应用层启动时间晚于仿真结束时间,或者发送速率设置过低导致整个仿真周期内只发出了几个包。
解决:先在物理层加追踪,确认节点是否尝试发送信道接入请求。如果根本没有发送动作,检查应用启动时间和发送速率;如果有发送动作但对方没收到,再查信道模型里的最大通信距离与发射功率是否匹配。务必先用两个节点近距离通信做冒烟测试,距离设成100米,确认链路通再放大距离。
5.2 端到端延迟比预期大一个量级:仿真时钟与声学延迟的计算
现象:1000米的节点间距,理论上声学传播延迟约0.7秒,但仿真算出来的端到端延迟常常达到3秒以上,并且随着节点数增加快速增长。
原因:延迟包含了排队延迟、信道接入延迟和重传延迟,有时协议握手带来的等待时间远超传播延迟。另一个隐蔽原因是仿真场景里节点的移动模型更新步长过大,导致数据包到达时节点坐标已经跳变,路由表失效,转发路径出现绕路。
解决:把端到端延迟拆成传播延迟与协议延迟两部分,先测单跳单包的传播延迟,验证与1500米每秒声速是否一致;再逐层叠加负载,观察排队延迟的增长曲线。如果协议延迟在空载时就异常,重点检查MAC层是否有不必要的退避计时,例如退避窗口的单位被误认为毫秒而实际是秒。
5.3 能量模型毫发无损:没有给物理层挂上调制解调器能耗
现象:仿真结束后所有节点剩余电量都是100%,或者只有应用层发包时电量在下降,而接收状态完全免费。
原因:能量模型没有与物理层状态绑定。水下节点的调制解调器能耗占大头在发送和接收状态,如果你用的是通用网络设备的能量模型,它可能只感知到数据包收发,而没有感知到物理层的状态切换。
解决:确认能量模型使用的是水下调制解调器专用的能耗模型,同时在物理层代码中添加状态切换的追踪。如果资料包里没有现成实现,可以写一个简单的能量包,根据物理层的发送、接收、空闲、睡眠四个状态分别记录功率。最后把节点剩余能量随时间的曲线画出来,与理论消耗做交叉验证。
5.4 结果永远不能复现:随机种子与移动模型的选择
现象:两次相同配置的仿真,结果差异很大,而且你找不到原因。
原因:仿真器里的随机变量来源很多,包括信道噪声、MAC退避时间、移动模型的随机游走。如果脚本里没有固定随机种子,每次跑的结果自然不同。还有人把移动模型的初始位置随机化又没设置种子,导致每次拓扑都不一样,结果当然不可比。
解决:在所有实验脚本开头固定随机种子,为不同的实验批次分配不同的种子编号并记录存档。对比实验时,不同协议应该使用相同的种子序列,才能把性能差异归因于协议本身。移动模型如果引入随机性,也要单独控制种子或直接关闭随机性做确定性对比。
5.5 节点位于岸基之外:部署坐标与信道范围边界
现象:在密集拓扑中,个别节点的统计结果异常低,甚至完全无通信。
原因:部署坐标边界超出信道模型的有效覆盖范围,或者节点被放置到陆地深度为0的区域,导致声学传播模型计算出异常高的损耗。常见于从资料包模板复制坐标,却未按你的场景调整水深。
解决:检查所有节点的坐标是否在设定的海域范围内,深度应大于几米且小于信道模型的支持范围。对边界节点单独做连通性测试,确保它们至少有一个邻居在有效通信半径内。批量部署时,建议在脚本里加入部署合法性校验,如果某个节点没有建立任何链路就输出警告。
6. 让仿真数据从“能看”到“能引用”:批量实验与统计口径
到这里,你已经能手动跑通水下传感器网络仿真,接下来要做的是把实验自动化,并且让数据经得起推敲。这里分享三个习惯,最后一个最容易被忽略但最影响结论。
第一个习惯:搭建批量实验外壳。把每次仿真参数通过命令行传入,用一段Shell脚本依次遍历参数组合,并自动汇总结果文件,这样你的注意力就从“重复敲命令”转移到“看数据趋势”。
#!/bin/bash # run-batch.sh 批量扫参:分别测试 3 个节点间距下的性能 for dist in 500 1000 2000; do for seed in 1 2 3 4 5; do ./build/mini-uan --simTime=200 --nodeSpacing=$dist --seed=$seed \ --resultFile=result_dist${dist}_seed${seed}.txt done done # 汇总:把每个结果文件的吞吐量字段拼接成一张表 python3 aggregate.py result_*.txt > summary.csv第二个习惯:统计指标不能只看平均值。水下网络受信道噪声和MAC随机退避影响,单次仿真结果方差很大。我在实际项目里至少跑5个不同随机种子,报告平均值的同时给出标准差或置信区间。对比两个路由协议时,先检查它们的性能分布是否重叠,如果重叠明显,就不能轻易得出某协议更优的结论。
第三个习惯:验证链路层判断。仿真数据在进入论文或方案报告之前,先做一次单跳单包实测:一个节点发送、一个节点接收,看收到的包数是否为发送数,端到端延迟是否在声学理论延迟附近。这一步能筛掉大多数模型配置错误。我个人的教训是,有一次批量实验跑完,吞吐量曲线很漂亮,后来发现信道模块的传播速度被误设为光速,所有延迟数据都不能用。从那以后,每次搭建新场景我都先做单跳校验再放大规模。
希望这组方法和踩坑记录能帮你在水下传感器网络仿真里少走几段弯路,把精力放在真正值得研究的协议设计上。
本文还有配套的精品资源,点击获取