
简介面向车载自组织网络VANET环境下GPSR协议验证的MATLAB仿真程序专为路由协议研究者、通信专业学生与相关课题开发者设计。程序构建了城市双向6车道十字路口仿真环境支持手动输入车辆密度并自由选择源节点与目的节点的初始位置坐标来源左侧坐标系实时动态呈现路由建立与数据转发结果便于直观理解贪婪周边路由策略的选路与转发逻辑。压缩包共14个文件核心为13个MATLAB函数脚本.m与1个交互界面工程文件.mlapp分别承担节点初始化、邻居表构建、位置更新、轨迹坐标获取及界面调度等任务整体仅87KB结构紧凑、模块划分清晰适合按需调用和二次开发。目前已有1036人浏览学习。该版本需在MATLAB2019b及以上环境运行适合用作课程设计、毕业仿真、协议对比分析或作为学习GPSR实现细节与排错思路的参考范例。1. GPSR 仿真包怎么用一张路由表都换不来的位置驱动转发拿到一个标注着“贪婪周边无状态路由协议(GPSR)路由仿真”的压缩包第一件事不该是解压跑例程而是先问自己一个问题这片自组网里有没有路由空洞GPSR 的学名是 Greedy Perimeter Stateless Routing它在无线自组网里靠节点坐标做转发决策不维护端到端路由表协议开销低在无线传感器网络、车载自组网方向尤其常见路由仿真则是把协议放进可重复的随机拓扑里用投递率、延迟、路由开销这些指标验证它到底行不行。下面按“协议在做什么 → 仿真怎么搭 → 参数怎么调 → 常见坑在哪 → 结果怎么验证”的顺序展开适合正在做课设、毕设或论文验证的从业者。读完你能判断一个 GPSR 仿真包能不能用能复现最小组网也能处理几类最常见的仿真翻车问题。2. 先把 GPSR 的转发逻辑看完贪婪转发、周边转发与路由空洞GPSR 最核心的洞察是节点只要知道自己的位置、邻居的位置和目标节点的位置就可以做转发决策不需要知道完整网络拓扑。在自组网里跑 GPSR 仿真本质上是在模拟两件事——贪婪转发能走多远周边转发什么时候被激活以及激活后能不能走出来。这两个模式构成 GPSR 全部行为的主干仿真里的几乎所有参数都围绕它们设置。先把这个逻辑理清楚后面调参数时才不会瞎试。2.1 贪婪转发每个节点都把包交给“离目的地更近”的邻居贪婪转发的思想非常直接源节点要发数据给目标节点 D它把自己的位置和 D 的位置写进分组头然后从邻居表里找出一个“到 D 的距离比自己更近”的邻居把包转发过去。这里的距离通常用欧氏距离也就是两点之间的直线距离。每个中间节点重复同样操作数据包就逐步逼近目的地。这个策略的本质是局部最优因为节点只看得到局部拓扑不知道空洞之外还有没有更好的路径它只会选那个“当前离 D 最近的邻居”。邻居表从哪来靠周期性信标。每个节点定期广播自己的位置收到信标的节点把对方 ID、坐标、更新时间记进邻居表。信标间隔太长邻居表跟不上移动速度间隔太短信道开销又上去了。实现贪婪转发时通常要遍历一次邻居表记录下最小距离对应的邻居代码逻辑大致是这样GpsrNeighbor* GpsrRouting::selectGreedyNext(Coord myPos, Coord destPos) { GpsrNeighbor* best nullptr; // 用“当前节点到目标距离”作为初始阈值 double bestDist myPos.distance(destPos); for (auto nb : neighborTable) { // 只考虑仍在超时有效期内的邻居 if (nb.isAlive) { double nbDist nb.pos.distance(destPos); if (nbDist bestDist) { bestDist nbDist; best nb; } } } return best; }这段代码的逻辑是先以“当前节点到目标节点的距离”作为初始阈值再遍历邻居表每当发现某个邻居到目标的距离更近就更新bestDist和best。遍历结束后如果best为空说明贪婪转发失败需要切换周边转发。参数说明里最值得关注的是myPos和destPos的来源——myPos来自定位模块在仿真里通常是全局坐标直接赋值destPos来自位置服务GPSR 本身不负责维护目标位置这也是“无状态”的含义之一它没有路由表不记录完整路径只有一张邻居表和一个目标坐标。这里要特别提醒贪婪转发的失效不是概率事件是拓扑结构导致的必然事件。当一个节点周围没有任何邻居比它离目标更近时就出现了“路由空洞”。仿真里最常见的空洞形状是 U 形凹槽节点传输范围覆盖不到凹槽底部所有进入这个区域的包都会卡在空洞边缘的节点上。注意如果目标是做“GPSR 失效场景分析”构造 U 形空洞是最容易复现的实验不用刻意调坏参数只用在场地中央放一个挡板状障碍或调整节点分布即可。2.2 周边转发与右手法则怎么走出路由空洞贪婪失效以后GPSR 切换到一个叫周边转发Perimeter Forwarding的模式。这里的“周边”指的是路由空洞的边界。GPSR 有两种让邻居表变成平面常见做法一是 GG 图Gabriel Graph二是 RNG 图Relative Neighborhood Graph。它们的共同点是删掉那些可以被判定为“不是必须”的边使邻居关系构成没有交叉边的平面图这样才能保证右手法则一定能绕出一个闭合区域。右手法则可以这样理解当前节点把“带包到达自己的那个邻居”定义为前一个节点然后在平面图里从“前一个节点”链路的方向开始按一个固定方向寻找最近的邻边把包交给那条边对应的节点。数据包沿着空洞边界一圈圈绕行直到抵达一个比触发周边转发时那个节点离目标更近的位置再切回贪婪转发。这个机制确保只要平面图构造正确包不会永远困在同一个节点上。仿真工程里和周边转发直接相关的往往不是某个数值参数而是平面图筛选开关。需要实现的判定函数大致是bool GpsrRouting::isGGEdge(NodePos u, NodePos v, NodePos w) { // GG 图判定以 uv 为直径的圆内不允许出现第三个节点 w double cx (u.x v.x) / 2.0; double cy (u.y v.y) / 2.0; double r2 u.distance(v) * u.distance(v) / 4.0; double d2 (w.x - cx) * (w.x - cx) (w.y - cy) * (w.y - cy); return d2 r2; }这段代码的逻辑是对任意两个节点 u 和 v以线段 uv 为直径画一个圆如果圆内有第三个节点 w就认为边 uv 在 GG 图的定义下是被遮挡住的边构造平面邻居集合时把它丢掉。参数说明里要留心距离计算精度仿真坐标是浮点数毫米级误差可能导致平面图构造不稳定进而让周边转发出现死循环。如果发现仿真长时间停在某个节点附近刷日志优先排查这里。2.3 为什么 GPSR 必须靠仿真验证而不是直接上硬件GPSR 的设计前提是每个节点都知道自己的地理位置这决定了它的验证分两层。第一层是协议逻辑层要确认贪婪转发和周边转发的切换规则正确第二层是网络性能层要回答 50 个节点随机撒在 1000 米见方的区域里包能投递多少、延迟多大。第一层可以用单元测试解决第二层几乎只能靠仿真因为现实部署一个 50 节点的移动自组网成本高、环境不可控、实验不可复现。仿真还有一个硬件测试给不了的优势可以把拓扑保存下来反复加载精确控制哪条链路断、哪个节点移动验证一次路由空洞的产生和恢复。这也是路由仿真在无线自组网研究里一直是标配方法的原因。做仿真前建议把目标写清楚是为了证明 GPSR 比 AODV 好还是为了分析某类拓扑下 GPSR 的失效概率这两种目标对应的脚本差别很大。3. 用 OMNeT 搭建 GPSR 路由仿真最小工程与核心代码确定用仿真验证以后接下来是选平台和搭工程。打开一个“GPSR 路由仿真.zip”时最可能是这几种情况完整的 OMNeT 工程、NS2 的 Tcl 脚本加 C 补丁或者某个课程设计的简化版。无论哪种第一步都是确认它能在本机跑通再谈改参数。如果压缩包里只有文档没有完整工程那就照着最小工程自己搭一个。3.1 仿真平台选型NS2、OMNeT 和 NS3 怎么选平台决定了你后续要改什么语言、怎么组织代码选错平台往往意味着整个包不能用。三个平台的对比如下平台脚本/语言无线模块成熟度GPSR 代码常见形态适合场景NS2Tcl C偏旧配置繁琐Tcl 脚本 协议补丁复现老论文、比对历史数据OMNeTNED CINET 框架较完整独立路由模块工程课设、毕设、协议二次开发NS3C模块化现代Application Routing 类大规模仿真、性能调优我的习惯是优先看压缩包里的工程类型。如果是.cc/.h加.ned文件就是 OMNeT 工程装好 OMNeT 并配好 INET 框架就能编译如果是一堆.tcl和.cc混合多半是 NS2要注意它在较新的 Linux 和 macOS 上编译容易碰壁。选平台不是选最好的是选包里已经配套的不然光改环境就够折腾一周。如果是自己新建工程我建议用 OMNeT因为 NED 语言描述网络拓扑直观调试端口友好GPSR 这种带明显状态切换的协议特别适合用它来观察每一步转发。3.2 最小仿真目录NED 文件、omnetpp.ini 与 GPSR 模块骨架一个能跑的 GPSR 仿真工程最少要有三个东西网络拓扑描述文件、仿真配置文件、GPSR 路由模块实现。OMNeT 里用 NED 语言描述节点和连接关系GPSR 场景里的“连接关系”不是有线链路而是节点独立的无线网卡和移动模块节点之间靠无线信道互相发现。先看网络描述文件network GpsrNetwork { parameters: int numHosts 50; double playgroundSizeX 1000m; double playgroundSizeY 1000m; submodules: // 无线信道负责节点间信号传播 channel: WirelessChannel { parameters: display(p0,0); } host[numHosts]: GpsrHost { parameters: display(puniform(0,1000,0,1000)); } }这个 NED 文件的逻辑是定义一个名字叫GpsrNetwork的仿真网络声明了 50 个节点和一条无线信道。参数说明里要重点关住numHosts它直接决定网络密度固定场地里节点越多、邻居表平均长度越大、贪婪转发成功率越高节点太少路由空洞出现的概率会大幅上升。playgroundSizeX和playgroundSizeY是仿真场地的边长单位必须和后面配置文件里一致如果不一致节点坐标会直接错乱。然后是仿真配置文件它决定仿真时长、随机种子、移动模型和路由参数。[General] network GpsrNetwork sim-time-limit 100s **.host[*].mobility.type RandomWaypointMobility **.host[*].mobility.speed uniform(0, 10m/s) **.host[*].mobility.pauseTime 2s **.host[*].gpsr.beaconInterval 1s **.host[*].gpsr.neighborTimeout 3s **.host[*].gpsr.faceRouting true这个配置的逻辑是先指定仿真网络名和运行时长然后给每个节点配置随机路点移动模型速度 0 到 10 米每秒移动到位后暂停 2 秒最后是 GPSR 的两项关键参数——信标间隔和邻居超时时间。参数说明里beaconInterval控制每个节点广播位置信息的频率1 秒意味着每个节点每秒发现一次邻居neighborTimeout是邻居表里一条记录多久没更新视为失效。如果移动速度快而信标间隔长邻居表永远跟不上真实拓扑发送时选中的下一跳可能已经跑到远处这也是很多 GPSR 仿真“看起来能跑但投递率极低”的直接原因。3.3 核心代码信标处理、贪婪转发与周边转发的 C 实现OMNeT 的 GPSR 模块本质是一个继承自cSimpleModule的类处理两类消息上层传来的数据包和邻居节点广播的信标包。数据包走路由决策逻辑信标包只用来更新邻居表。下面这段是消息入口和信标处理的骨架void GpsrRouting::handleMessage(cMessage* msg) { if (msg-arrivedOn(upperIn)) { // 上层应用来的数据包进入转发决策 processDataPacket(check_and_castGpsrPacket*(msg)); } else if (msg-arrivedOn(radioIn)) { // 无线信道收到的信标或数据包先判断类型 if (dynamic_castGpsrBeacon*(msg)) { updateNeighborTable(check_and_castGpsrBeacon*(msg)); delete msg; } else { // 数据包可能是转发包或目标包 handleDataFromRadio(check_and_castGpsrPacket*(msg)); } } }这段代码的逻辑是GPSR 模块只有一个入口handleMessage按消息到达的门判断来源。upperIn连接上层应用radioIn连接无线网卡数据包走processDataPacket信标走updateNeighborTable。参数说明里需要注意的不是门名而是信标和数据包的优先级——仿真里如果信道容量不够信标和数据会互相挤占。更精细的工程会从schedulingPriority上压低信标优先级让数据包优先转发。再来看转发决策的核心函数。它处理三种可能目标节点就在当前节点一跳范围内时直接单播目标不在一步内但存在更近的邻居时走贪婪转发否则切周边转发。转发函数的返回值是下一跳的 MAC 地址不是完整路径这也是“无状态”的具体体现const L3Address GpsrRouting::forwardDecision(const GpsrPacket* pkt) { Coord destPos pkt-getDestPos(); // 情况一目的节点在自己的邻居范围内直接单播 GpsrNeighbor* destNb neighborTable.lookupByPos(destPos); if (destNb ! nullptr) { return destNb-macAddress; } // 情况二贪婪转发 GpsrNeighbor* next selectGreedyNext(myPos, destPos); if (next ! nullptr) { return next-macAddress; } // 情况三切换周边转发先根据平面图筛选邻居 if (faceRoutingEnabled) { return selectPerimeterNext(pkt, myPos, destPos); } // 三种情况都不满足就丢包 return L3Address::UNSPECIFIED_ADDRESS; }逻辑说明转发决策分三步走。先查目的节点是否在邻居表里这对应真实场景的最后一跳再查贪婪转发候选最后才用周边转发兜底。参数说明要留意faceRoutingEnabled这个开关——它在代码里是布尔值而不是数值参数但它决定周边转发代码路径是否执行。如果仿真包里这个开关默认是 false所有贪婪转发失败的包都会走进UNSPECIFIED_ADDRESS分支在统计结果里表现为“无原因丢包”查错时容易被忽略。3.4 采集指标投递率、端到端延迟和路由开销怎么统计跑通一个 GPSR 仿真以后要让它产出一组能说明问题的数字。最低限度统计三个指标分组投递率、端到端平均延迟、路由开销。分组投递率等于目的节点收到的数据包数除以源节点发出的数据包数延迟等于数据包从应用层发出到被目标应用层接收的时间差路由开销等于全网络信标和数据包控制头占据的字节数除以交付数据包字节数。这三个指标里路由开销是 GPSR 最能体现优势的地方。GPSR 不维护路由表但仍有开销它来自周期性信标不像 AODV 要按需洪泛控制报文。如果信标间隔 1 秒、每个信标约 24 字节、50 个节点跑 100 秒信标总字节数是可计算的固定开销。OMNeT 里统计指标常用信号槽emit(packetDeliverySignal, delivered ? 1 : 0); emit(endToEndDelaySignal, simTime().dbl() - pkt-getCreateTime().dbl()); emit(routingOverheadSignal, beaconSize * beaconCount);逻辑说明和参数说明三行代码分别在数据包到达时发出投递信号、延迟信号和路由开销信号。投递指标用布尔值累加每个交付成功的包发一个 1仿真结束后对所有信号求均值就是投递率。延迟信号的关键是pkt-getCreateTime()它记录包在源应用层的创建时间必须在发送时把这个时间写进包头否则统计出来的延迟恒为 0。路由开销里beaconSize不要只算 GPSR 信标内容要把 MAC 头和 IP 头的固定字节也算进去不然和 AODV 对比时口径不一致。4. 影响 GPSR 仿真结果的核心参数信道、移动模型与节点密度GPSR 仿真里代码正确只占一半另一半是参数。同样的协议实现换个信道模型或移动速度投递率可能从 90% 掉到 30%。参数大体可以分成四类物理层、传播信道、移动模型、拓扑密度。每一类变化都会传导到邻居表质量、平面图构造和转发决策上所以调参之前先想清楚你在调哪一层。4.1 物理与接入层参数发射功率、接收灵敏度与干扰门限物理层参数决定一个节点的传输范围而传输范围是 GPSR 邻居关系的基础。常见做法是在无线网卡配置里设置发射功率、接收灵敏度、路径损耗指数和背景噪声功率。发射功率从 1mW 提到 100mW开阔场景里传输范围可能从几十米涨到几百米邻居表平均长度变大贪婪转发成功率提高但信道竞争加剧重传概率上升端到端延迟变高。这四组参数里最影响邻居表数量的是接收灵敏度。灵敏度越低能听到的弱信号越多邻居数越多。对 GPSR 来说邻居数不是越多越好邻居太多平面图构造成本上升信标交互也频繁再加上 MAC 层竞争吞吐量反而下降。建议的基准组合是发射功率 20mW、接收灵敏度 -85dBm、路径损耗指数 2.4、背景噪声 -95dBm在这个基础上做单参数扫描。注意单参数扫描指的是只改一个变量其余保持基准值不变。先跑基准组再分别扫发射功率和灵敏度这样才知道哪个参数真正影响投递率。4.2 信道与传播模型载波频率、路径损耗与随机衰落信道模型直接决定“这个节点是不是我的邻居”。仿真里的信道不是天然的电磁环境是用公式算接收功率。通用做法是选择简单路径损耗模型再加瑞利或阴影衰落。GPSR 的贪婪转发对随机衰落很敏感因为阴影衰落会随机缩短某些节点的实际接收范围让节点在统计意义上“看不见”本可以更接近目标的邻居转发路径因此发生偏移。载波频率选 2.4GHz 还是 5.9GHz主要影响路径损耗基准值和传输距离的整体衰减对协议决策逻辑影响不大但会改变节点的有效邻居数量。如果跑步的是车联网场景5.9GHz 更贴近真实如果只是验证 GPSR 协议本身2.4GHz 更容易得到平滑的网络密度曲线。信道模型从“无衰落”换成“阴影衰落”后投递率一般会下降 10% 到 20%属于正常现象不要急着改协议代码。4.3 移动模型随机路点、暂停时间与最大速度GPSR 对移动性特别敏感原因在于邻居表永远是过期的快照。节点每秒广播一次位置但真实网络里节点可能每秒跑 10 米上一秒看起来合适的下一跳邻居发出去时可能已经移出传输范围。移动模型三个核心参数是最大速度、暂停时间和目标点选择方式。随机路点最常用节点随机选一个目标位置匀速移动到达后暂停再选下一个目标。推荐的初值是最大速度 10m/s、暂停 2s、仿真 100s。把目标从验证协议改为评估“高速车联网里的 GPSR”最大速度提到 30m/s同时把信标间隔缩到 0.2s 左右否则邻居表过期率高到无法接受。具体联动关系是移动速度每提高一倍信标间隔大致要缩小到原来的一半才能维持相近的邻居表精度。4.4 节点密度与拓扑形状贪婪转发成功率的底层变量节点密度对 GPSR 的影响是决定性的与具体信道数值无关。1000 米边长场地里 50 个节点的平均邻居数远少于 200 个节点时的邻居数。贪婪转发能成功的条件是每个中间节点周围至少存在一个更接近目标的邻居而这个条件依赖邻居数量服从网络的随机几何分布。密度低到某个阈值以下路由空洞就会成为常态。实操中我一般先把节点数量固定在 50 到 100 之间跑一遍基础仿真用平均邻居数评估密度合理性平均邻居数少于 5说明场地太大或者节点太少贪婪转发模式几乎瘫痪平均邻居数超过 20需要考虑 MAC 层冲突对投递率的影响。这个判断比直接看投递率更早暴露拓扑问题也更方便对不同仿真包做横向对比。5. GPSR 仿真常见问题与避坑抓不到包、路由空表、性能崩溃到了调试阶段问题往往不是“协议怎么实现”而是“为什么仿真跑出来一团糟”。这一章整理几类最高频的问题按“现象 → 原因 → 解决”来写每一类都是常见工程里真正踩过的坑。5.1 现象抓包工具看不到任何数据包仿真里一片静默仿真跑完后输出文件里只有初始化信息没有数据包记录连信标都看不到。原因通常是 OMNeT 的模块连接断了或者节点根本没有安装 GPSR 路由模块。排查路径是先看 omnetpp.ini 里的网络名和节点数量再回 NED 文件确认host[*].gpsr模块被实例化最后在initialize()里加一行EV getFullPath() endl确认模块被构建。如果路由模块根本没创建后续所有分析都没有意义。5.2 现象邻居表始终为空贪婪转发永远失败邻居表为空意味着数据包永远不会进入贪婪转发分支全被丢进“无可用下一跳”逻辑。原因基本是信标发送周期和邻居表超时周期不匹配比如beaconInterval设成 10 秒、neighborTimeout设成 3 秒每个信标到达 3 秒后就删除邻居表自然永远为空。解决方法是把超时时间设为信标间隔的 3 倍以上或者先临时设成 0 表示永不过期以此排除超时干扰。5.3 现象投递率接近 0%但邻居表非空且日志无报错邻居表有内容数据包也发了但投递率是 0 且没有任何报错。这种情况最常见的原因是目的节点位置信息为空或坐标全为零。GPSR 依赖目的节点位置如果应用层初始化时没设置目标坐标分组头里的destPos可能一直是 (0,0)节点会一直朝场地角落转发永远到不了目标。解决方法是先打印每个数据包的destPos确认位置向量有没有被初始化这一步往往比调参数更快定位问题。5.4 现象节点进入周边转发后一直死循环仿真时间推进极慢排除数据包内容问题后周边转发可能陷入死循环同一个节点反复转发同一个数据包仿真时间推进慢到怀疑人生。原因一般是平面图构造没生效或者周边转发模式下“下一个节点”没有排除上一个已经来过的邻居。解决方法是给每个数据包加一条“访问节点记录”单包最多累计 20 跳超过就丢弃并记录日志先在数据层面打破死循环再回头查 GG/RNG 图的边过滤逻辑。5.5 现象仿真结果不可复现每次跑出来的投递率差 20% 以上不可复现的根源几乎都是随机种子没有固定。OMNeT 默认情况下每次运行生成不同的随机数种子导致节点初始位置、移动轨迹都不同统计结果自然剧烈波动。解决办法是在 omnetpp.ini 里显式指定随机种子并按节点分配不同种子值把种子和网络配置一起保存。这样论文里的图和表才能对得上“复现”要求。[General] seed-set 42 **.host[*].mobility.seed 42这段配置的逻辑是给全局仿真和每个节点的移动模型都指定同一个随机数源。参数说明里seed-set控制整个仿真的随机流mobility.seed控制移动模块的随机流两者分开设置可以保证“拓扑变了但移动轨迹固定”的可控实验条件。6. 让 GPSR 仿真结果有说服力对比实验与转发链路验证仿真跑通、参数调完、投递率看着也不错但距离“能写进论文或报告”还有一步验证结果经得起追问。这一步分两块一是和别的路由协议做对比二是检查转发链路本身是否符合 GPSR 的转发语义。6.1 和 AODV 做对比实验控制变量与指标对齐GPSR 最常见的对照组是 AODVAd hoc On-Demand Distance Vector Routing两者目标都是移动自组网但机制完全不同GPSR 靠位置做本地转发AODV 靠按需路由发现维护端到端路径。对比实验的控制变量很关键相同的场景、相同的移动轨迹、相同的节点密度、相同的数据流。最容易做到的方法是保存一份仿真场景描述文件把节点初始位置、移动轨迹数据写死然后在 GPSR 和 AODV 两个模块上加载同一份描述文件。指标对齐同样重要。GPSR 的路由开销是周期性信标的字节数AODV 的开销是 RREQ/RREP/RERR 控制报文总和两者必须按“控制字节数 / 数据字节数”的同一口径比较。我一般三个指标都统计投递率、端到端延迟、归一化路由开销。GPSR 在静态和低移动性场景下通常赢在开销在中高移动性下可能输在投递率因为邻居表过期率上升这个趋势本身就是论文里值得写的结论。6.2 用轨迹日志验证一条路径而不是只看统计数字统计数字只能告诉你好不好不能告诉你为什么。要说服自己或审稿人最好取一个数据包把它的完整转发路径打出来逐跳确认是否符合 GPSR 语义贪婪模式每一跳都向目标推进周边模式沿空洞边界有序绕行且最终切回贪婪。我的做法是在每个节点转发时追加一条记录char modeTag (pkt-isForwardedInGreedyMode() ? G : P); pathStream getParentModule()-getIndex() modeTag ; // 仿真结束前把路径写入独立文件 ofstream pathLog(path_pkt_ to_string(pkt-getId()) .log); pathLog pathStream.str() endl;这段代码的逻辑是数据包每经过一个节点就在路径流里追加“节点编号 转发模式”仿真结束时写入独立日志文件。参数说明里modeTag是贪婪还是周边标记getParentModule()-getIndex()是当前节点编号pkt-getId()是包的唯一编号。验证时看两条规则第一所有标记为 G 的跳都必须满足“下一跳比当前节点离目标更近”第二标记为 P 的序列不能有重复节点且最终回到 G。如果这两条都满足投递率再难看也是参数问题如果不满足统计数字再好看也不能用。我第一次跑 GPSR 仿真的时候投递率只有 3%第一反应是调大发射功率结果怎么调都还是低。后来把转发路径一条条打出来才发现是邻居表超时参数比信标间隔还短所有邻居都是“见过即忘”。改完后投递率一下子到了 80% 以上。从那以后我养成了习惯每次改参数先打印一组转发路径再去看统计曲线路径对了曲线才有意义。希望这个验证习惯也能帮到你。本文还有配套的精品资源点击获取