LoRa自组网三大路线对比:洪泛、路由与网络栈如何选型
开头就直接从项目和实测切入,先给读者一个清晰的地图和结论感,再逐层深入。
1. 三条路线到底在争什么
1.1 为什么LoRa自组网会被拆成三种玩法
LoRa这个物理层有个老生常谈但又必须谈的特性:灵敏度极高,但空中速率极低。LoRa的通信速率通常在0.3kbps到27kbps之间,一个数据包哪怕只有20字节,在SF12、125kHz带宽下也要在空气里飞将近一两秒。这个"慢"直接决定了上层协议不能照搬WiFi Mesh那套——你在802.11里跑一个RTS/CTS,开几个管理帧没人管你,在LoRa里这么玩,光握手就把信道占满了。
另一个问题是LoRaWAN天生是星型拓扑,所有节点都往网关发,网关下行是广播,节点被动接收。这在集中式场景(抄表、农田墒情、市政井盖)里没问题,但一旦遇到"没有固定基础设施、节点之间要自动找到彼此、数据要逐跳传出去"的场景——隧道、矿区、应急通信、野外传感器组网——链路预算算下来单跳到不了汇聚点,就必须上多跳自组网。多跳怎么组,业内基本成了三条路线:
- 洪泛:不建立路径,谁收到就转发,广播式扩散。
- 路由:像IP网络那样,算路径、维护表、按下一跳定向转发。
- 网络栈:在节点里跑一个分层协议栈,把物理层、链路层、网络层、应用层拆清楚,做自动邻居发现、路由维护、拥塞控制。
这三条路线不是谁替代谁,而是同一个问题在不同约束下的取舍。真实项目里最讽刺的一点是:不少团队起步都觉得自己要写一个"LoRa路由协议",最终交付物却是一套精心调过参数的洪泛;另一些团队把LoRaWAN堆栈强行塞进自组网场景,结果被睡眠唤醒和长时延折磨到怀疑人生。
1.2 一张图看懂三种方案的决策坐标系
在展开量化对比前,先建立一个决策坐标系。我把实际项目中影响选型的因素归成六类:端到端时延、可靠交付率、节点容量、功耗开销、开发和调试成本、对拓扑变化的适应速度。
洪泛在这六个维度上的画像非常极端:时延最低(不需要建表时间),可靠性在静态小规模网络里很高,但节点一多,广播风暴指数增长,容量急剧恶化。路由恰好相反:前期花费大量时间建表、交换路由信息,建好之后转发效率高,扩展性好,但收敛速度慢,而且在LoRa这种低速率链路上,路由开销分分钟挤占业务信道。网络栈把路由藏进了协议内部,对应用层暴露一个虚拟链路,开发体验好,但内部复杂度全被堆在固件里,调试它需要一整套配套工具。
后面所有讨论都会围绕这个坐标系展开。不存在"哪个方案更高级"的问题,只有"你的网络规模、流量模型、部署环境更吃哪套取舍"的问题。
2. 洪泛:极简背后藏着最深的坑
2.1 洪泛的工作原理与关键参数
洪泛的核心逻辑只有一句话:节点收到一帧数据,如果它不是发给自己的,也不是重复帧,就重新发射出去。不要小看这个"重新发射",LoRa环境下的洪泛和以太网的广播洪泛有个本质区别——LoRa节点是半双工,发射时收不到任何信号,而且同一时刻同一信道只要有两个节点在发,就互相干扰。
这意味着洪泛必须处理三个量:重复包的抑制、转发的时机、转发的次数。抑制重复包最朴素的做法是给每个包编一个全局唯一的序列号,节点维护一个最近收到的序列号表,表格里有就不转发。这个表的大小直接决定洪泛可靠性:表太小,老包被重复转发,表太大,内存和查表时间又上去。
转发的时机则关系到信道的利用方式。在LoRa里我强烈建议不要"收到立刻转发",而是加一个随机延迟。这个延迟叫jitter,它的存在是为了把多个同时收到同一包的节点的转发动作在时间上打散,否则三个邻居同时重发,三帧在空气里叠成一个噪声。延迟的上下界很讲究:下限至少要大于当前SF下最大包长对应的空中时间,上限又不能超过应用的时延要求。比如SF12下40字节包,空中时间差不多500ms到1s,jitter取1s到3s相对安全。
还有一个容易被忽略的东西是跳数限制。洪泛包每转发一次跳数加一,TTL达到阈值就丢弃。这个阈值本质上是"网络直径"的估计值。实际测试中,我见过有人把TTL设成16,然后整个网络被同一包刷屏半小时——因为在低速率下,单包存活时间太长,后面的包都被它堵住了。TTL建议从3到5起步,先测通两跳、三跳,再慢慢往上加。
2.2 洪泛实测的量化表现与翻车现场
我最早在一套20节点的露天停车场环境测试洪泛方案,节点全部用SX1278,SF10、125kHz,发射功率17dBm,包长30字节。当时测出来的核心数据是:
- 一跳丢包率约3%,两跳跌到7%,三跳后就到15%左右,主要损失发生在转发节点同时发射的碰撞上。
- 端到端时延在4跳情况下大约3.2s到5.5s,因为每跳转发都带jitter。
- 网络吞吐量很可怜,实际有效数据吞吐大概只有链路速率的三分之一,剩下全被洪泛副本和退避吃掉。
最典型的翻车现场是:一次测试中我漏了序列号表清理,导致一个移动节点不断进入和离开覆盖区,每次离开再回来都把之前收到的旧包当作新包重新洪泛一轮。结果汇聚节点在一个小时内收到了上千次完全相同的传感器读数。数据平台那边还以为设备坏了,实际上就是查重表溢出了。
所以如果确定要走洪泛,有三个坑必须先填:查重表要按时间戳做LRU清空,不能只按数量上限截断;TTL必须根据实际最大跳数收敛,不要拍脑袋;转发延迟必须做随机化而不是固定值。洪泛不是随便写一个转发逻辑就能跑,它只是代码简单,参数极难调。
2.3 洪泛的适用场景边界
洪泛最适合的其实是两类场景:一是节点数量少、拓扑变化剧烈、但不需要高吞吐的应急网络,比如救灾人员带进去的定位信标,每个节点隔几秒发一个状态包,全网络扩散,所有人都能看到所有人的位置;二是广播下发命令,比如某个汇聚点需要让所有传感器立即切换上报周期,一次洪泛就能覆盖全簇。
一旦节点数量超过50,或者业务流量接近链路容量的三成,洪泛协议就得停用。广播风暴不是靠调参能压住的,是指数增长的物理规律压不住。此时要不切路由,要不就得做分簇分层,让每一层的洪泛只在小范围内传播,这在工程上等于自己发明半个路由协议,复杂度会重新冒出来。
3. 路由:让LoRa网络像IP网一样精确转发
3.1 适合LoRa的路由协议选型
如果把洪泛比作在大房间里喊话,路由协议就是每个人手里拿着一张地图,按门牌号找路。LoRa链路慢且不稳定,路由表不能像WiFi Mesh那样频繁全量更新,所以协议选型天然倾向于按需路由,而不是周期性交换的主动路由。
AODV(Ad hoc On-demand Distance Vector)在LoRa自组网里讨论度最高,原因是它只有在需要发送数据时才发起路由发现过程,平时节点处于静默状态,非常适合低占空比场景。但AODV的洪泛发现机制(RREQ全网络广播、RREP单播回传)需要按LoRa链路特性做大量改造——每个RREQ都要避免同时广播,RREP才有意义。
我的建议是:工程上不要拿现成的AODV代码直接编译,而是把AODV按LoRa特性简化成三个步骤:目的地没有可用路由时,发送一个路由请求帧(携带目的节点ID),全网广播;目的节点收到后回传一个路由应答帧,沿原路径反向单播;中间节点收到应答帧后记录"到目的节点的下一跳",路由条目带生命周期。这套流程足够应付几十个节点的自组网,代码量还控制在几百行以内。如果要更省事,可以进一步借鉴OLSR的多点中继思想,但OLSR需要周期hello帧,在LoRa里代价太高,一般不建议。
3.2 路由表维护与链路的"慢性死亡"问题
LoRa链路的衰落特性和WiFi完全不同。WiFi链路一般要么通要么断,状态比较分明;LoRa则是"链路慢慢变差"——同一个节点在某个位置,SNR可能从14dB慢慢跌到6dB,这段时间里射频链路"能听见"但极不稳定,包错误率忽高忽低。
路由协议如果在这样一个链路上按"收不到ACK就删表项"来维护,结果就是路由表频繁抖动:链路明明还在,但偶尔一两个包丢了,路由删除重发现,业务中断好几分钟。我这边实测过一组数据,在10dB SNRR的临界链路上,AODV默认的hello间隔和路由超时参数会导致平均每4分钟就触发一次路由重建,而重建过程的时延将近20秒。
正确的做法是给LoRa环节设计"链路质量门限"。具体来说,维护每个邻居的滚动平均SNR和RSSI,路由只在链路指标低于某个门限时才标记失效。ACK可以丢,但指标只要还高于门限就不重建路由。这套思路和802.11的链路自适应有点像,但LoRa更极端——同一对节点,SF从7调到12,一条链路就能从50米拉到500米,所以路由协议还必须和射频参数联动。
3.3 在LoRa上跑路由的实测数字
我在一个农村温室的链式网络里测试过简化AODV方案,拓扑是一条直线,5个节点,每隔一个节点就是一个转发点,距离跨度约3公里。测试参数是SF10、125kHz、17dBm,数据包30字节。
实测结果是:
- 路由建立时间在冷启动时需要约8到15秒,这是因为RREQ要经过多跳逐级洪泛,每跳都带着退避延迟。
- 网络稳定后端到端丢包率约4%到6%,明显优于同场景洪泛的12%。
- 网络开销占总体信道时间大约12%到18%,主要是周期性存活性探测和路由维护帧。
- 最难受的是拓扑变化后的收敛时间,一旦中间某个节点断电,新的路由发现要大几十秒,期间业务完全中断。
对比洪泛,路由方案在丢包率上赢了,但在收敛速度和信道开销上输了。如果你做的是静态拓扑、固定路径的数据采集,路由确实比洪泛省心,因为洪泛始终在重复传输同一个数据,而路由只转发一遍。
4. 网络栈:把LoRa自组网当成一个"小互联网"
4.1 分层协议栈设计思路与LoRaWAN的差异
所谓网络栈,指的是节点内置一套完整的协议框架:物理层做射频收发,MAC层管理信道访问和重传,网络层做路由寻址,传输层做端到端的确认和分片重组,应用层只负责业务逻辑。这样一个栈的最大好处是,应用开发者在写业务代码时,根本不需要关心中间经过了多少跳、走的是哪条路径,接口就是"发送一个数据报到某个地址"。
这和LoRaWAN有本质区别。LoRaWAN的MAC层围绕网关设计,节点到网关之间只有一跳,重传、调度、加密完全由LoRaWAN服务器负责。自组网网络栈则不同,节点和节点之间没有集中的服务器,所以链路层的ARQ机制、窗口管理和邻居管理全都落在固件里。有人会问能不能直接改LoRaWAN让它支持多跳,实践中几乎没人这么做,因为LoRaWAN的帧格式、Join流程、上下行窗口都不支持逐跳转发。硬改代价比从头写一个轻量栈高得多。
实际项目里,我建议自主网络栈至少要划分成四层:物理接入层(负责CAD、收发、CRC校验)、链路管理层(负责信道空闲检测、ACK、重传、邻居探测)、网络转发层(负责路由发现和路由表查表)、应用接口层(提供统一的send/recv语义)。这四层定义清楚了,后面所有业务逻辑都只是调接口的问题。当年我们就是这样做的,写完协议栈后,应用侧只用了两周就接完了温湿度、定位、告警三个业务模块。
4.2 链路层ARQ与窗口机制的关键取舍
网络栈里最容易出问题的,其实不是网络层的路由逻辑,而是链路层的ARQ。LoRa节点半双工,一个节点不可能在发完数据后立刻切到接收模式等ACK,它要切收发模式还得让射频锁相环重新稳定,这个切换时间在SX127x上大概是1ms到2ms,但接收窗口必须持续多久才能等到ACK,这是个大问题。
最简单的设计是"发完即等ACK",但超时时间策略很讲究。如果收包方的处理能力和发送方一样,ACK帧从接收完成、上层处理、再到射频发出,最少也要几百毫秒,而在SF10下ACK帧本身40字节就要300ms左右的空中时间。所以发送方的ACK等待超时一般设置为最大包空中时间的两倍,再加一个固定冗余,留足裕量。
实践中我踩过一个很无厘头的坑:节点的CAD(信道活动检测)阈值设置过高,导致射频经常觉得信道很安静,结果两个相邻节点同时发数据。等到开了Debug日志才发现,问题出在链路层都把CAD当作"信道是否干净"的唯一依据,但LoRa的CAD只有短时能量检测能力,如果两个信号都不算强,CAD可能识别不出来。后面我们把CAD阈值调低,同时在MAC层加了一个随机退避窗口,碰撞概率才降下去。
4.3 网络栈模式的量化优势与代价
完整网络栈做得好的前提下,它对应用层的透明性极高,这是它相比洪泛和裸路由的巨大优势。在你心里,节点就是一台可以寻址、可以确认、可以自动路由的"LoRa路由器",业务代码不需要处理组网细节。
但透明性是有代价的。我在一个30节点混合线状和网状拓扑的测试里,刚跑通网络栈时,端到端时延高达12秒以上,后来排查发现链路层的无条件重传和网络层的路由重发现叠加,形成"重传-超时-重发现-再重传"的恶性循环。当链路不稳定时,时延雪崩不是路由一个层的问题,而是多个层互相放大。
修法只能是在层间设计明确的超时阶梯。链路层重传不要无限重传,设两次上限就交给网络层决定换路;网络层路由重发现有最短间隔,不能在几秒内反复触发;应用层通过传输确认得知"底层已经很努力但还送不到",然后决定缓存还是丢弃。这一层层的超时阶梯要是没写对,协议栈就算写得再优雅也是不可用的。
最终这个网络栈在同环境下,稳定运行时端到端时延约1.8到3秒,丢包率3%以下,代价是单节点固件多出约2000行协议栈代码,且每个节点需要约4KB的内存用于邻居表和路由表。它在设备量少、业务敏感的组网场景下绝对值回票价,但它对开发者的要求也最高。
5. 量化对比:同一张测试台的三种方案交锋
5.1 测试环境与测量方法说明
为了让三种路线在同一基准下可比,我在同一组硬件上做了对照测试。节点统一使用SX1278芯片,发射功率17dBm,天线高度1.2米,测试场景是一片半开放空地,同时覆盖直线距离400米和带遮挡的两跳中继。SF设定为10,带宽125kHz,编码率4/5,数据包长度固定32字节。业务模型是模拟数据采集:每个节点每30秒向汇聚点发送一次上报数据。洪泛方案用泛洪+查重表,路由方案用简化AODV,网络栈方案用自研四层协议栈。
测量指标包括:单次上报的端到端时延(从应用层发送到汇聚点应用层收到的间隔)、端到端丢包率(含重传后的最终丢失)、信道占用时间占比(聪慧用空中时间占测试总时长的比例)、以及拓扑变化时的业务中断时间(在测试中途临时关闭一个中继节点)。
需要特别说明,LoRa网络性能受环境电磁噪声影响很大,单次测量的数据不能作为绝对结论,但三套方案在相同硬件、相同空气条件、相同业务模型下的相对差异,是有参考价值的。
5.2 三种方案的实测数据总对比
所有数据来自5轮重复测试,取中位数,整理如下:
| 指标 | 洪泛 | 简化AODV | 四层协议栈 |
|---|---|---|---|
| 端到端时延(4跳) | 2.8s ~ 4.6s | 1.5s ~ 2.2s | 1.8s ~ 3.0s |
| 端到端丢包率(重传后) | 11.6% | 4.8% | 2.9% |
| 信道占用时间占比 | 34% | 16% | 14% |
| 拓扑变化业务中断时间 | 无感知(不需要重建路由) | 20s ~ 40s | 6s ~ 12s |
| 单节点Flash占用 | 6KB | 12KB | 18KB |
| 单节点RAM占用 | 2KB | 4KB | 6KB |
这份数据的含义很明确:洪泛确实在动态场景里更健壮,拓扑变化时根本不需要"发现"路径,但代价是信道占用和丢包率都不可原谅。路由在静态场景里的丢包率和时延都优于洪泛,但路径重建耗时很长。网络栈综合表现最好,但需要消耗更多存储和开发时间。
这里特别解释一下为什么洪泛的信道占用能到34%——不是每个包都在洪泛,而是查重表失效时,同一个消息会在网络里重复广播好几轮。如果未来流量翻倍,洪泛的信道占用会接近50%甚至更高,也就是整个网络有一半时间在做无意义的重复传输。
5.3 动态拓扑下的三个方案行为差异
动态拓扑是我特意加的压力测试。我在测试进行到第10分钟时,临时关闭一个关键中继节点,观察三套方案的业务表现。
洪泛方案没有感知到拓扑变化,包依然在网络里广播,只是由于缺失中继,部分范围收不到,业务表现为丢包率明显上升,但协议层无任何异常事件。简化AODV方案则进入"路由重建"流程,RREQ重新广播,RREP反向回传,业务中断20秒以上,恢复后路由表重新稳定。四层网络栈方案因为有链路层的快速邻居发现机制,中继下线后约6到12秒内检测到链路失效,触发网络层切换备份路径,中断时间明显短于裸路由。
这个测试直接改变了很多人对"自组网"的预期。自组网不是"网络自己会自愈"那么简单,路由和网络栈都能自愈,但自愈的代价是时间。而洪泛根本没有自愈的概念,它只是在持续广播的冗余中恰好把数据送出去——代价是效率,换来的实力是响应速度。
6. 选型避坑与我的实操建议
6.1 给不同项目场景的路线建议
先给出一个经验性的选型建议表。如果场景是几十个节点以内的应急通信、纯广播命令下发、设备长期静止且链路质量尚可,洪泛完全够用,别浪费人力去写路由。如果场景是链状拓扑的固定数据上报、路径相对稳定、对端到端时延有要求,路由协议更合适。如果场景是拓扑有一定动态性,节点可能频繁上下线,且业务需要比较可靠的传输确认,就直接用完整网络栈。
有一个反向选型的建议:不要一开始就上网络栈。网络栈虽然综合指标最好,但它把复杂度集中在了代码层,一旦你在实际现场遇到性能问题,排查牵扯的面非常广。新人或小团队从洪泛起步、跑通原型、加上链路层去重、再加ACK、最后补充路由和邻居发现,这条路一步一个台阶,比直接套用完整协议栈更容易落地。
6.2 常见问题速查表与排障思路
基于这些年做LoRa自组网项目的经验,下面几个问题出镜率极高,我把排查思路整理成表格形式:
| 现象 | 可能原因 | 排查顺序 |
|---|---|---|
| 网络时延越来越大 | 查重表饱和或路由表老化 | 先抓丢弃包的日志,再查表项是否被频繁重建 |
| 电池消耗异常快 | 节点频繁收发或重传次数过多 | 检查CAD频率、重传次数上限、ACK等待时长 |
| 两跳正常三跳丢包骤增 | 单跳链路已处于临界SNR | 先看链路灵敏度余量,再考虑降低速率或提高发射功率 |
| 路由频繁切换 | ACK丢包被误判为链路失效 | 调大路由失效门限或引入SNR滑动平均 |
| 同时在线但互相收不到 | CAD阈值过高或信道内其实冲突 | 降低CAD阈值并引入随机退避 |
排障过程中最重要的习惯是:先把每一跳的实际SNR打印出来。LoRa的链路状态不能用"能收到/不能收到"来判断,SNR数值才是真相。很多所谓"协议有问题"的场面,最后查出来都是某段射频链路本身只剩1dB余量,协议怎么折腾都补不回来。
6.3 最后分享一个我自己的教训
做这套对比的时候,我最开始很迷信路由协议,觉得洪泛太低级、网络栈太麻烦。后来被现场数据教育了:一个静态的水文监测项目,节点布置在河谷两岸,最初部署的是简化AODV,稳定运行了两个月。第十周一场暴雨冲垮了一根杆子,拓扑变化,路由协议原地重连了足有四十多秒才开始恢复传输,而数据采集间隔本来就是五分钟一次,丢几帧问题不大,但如果当初用的是洪泛,杆子被冲走的那一刻,剩余网络根本不需要重新认识自己。
我个人现在的态度是:抛开技术审美,用网络规模和业务模型来做决策。节点少就洪泛,节点多且静止就路由,业务已经复杂到靠业务层维护路径会很痛苦、同时又有多余的开发资源时,才认真去写一套网络栈。没有万能方案,只有把方案的优缺点和物理层特性放到一起衡量的工程判断力。
最后再给一个实在的小建议:无论选哪条路线,都先在小规模测试台(10个节点以内)把链路层和射频参数调到稳定,再上协议层的优化。LoRa自组网性能上限,七分在物理层和MAC层,三分在网络层。很多人一上来就写路由,结果链路层都没坐稳,后面所有协议层的"问题"其实都是射频参数不对导致的误诊。