300节点无线Mesh网络协议设计 (CMP-v4)
核心约束: 纯广播信道,无ACK确认,无重传机制。所有设计必须拥抱不确定性——用概率、冗余和最终一致性代替确定性保证。
一、数量级跃迁带来的根本性变化
| 指标 | 30节点 | 300节点 |
|---|---|---|
| TDMA时隙数 | ~40/superframe | ~400/superframe → 超帧长达4秒+ |
| 集群数 | 3-6个 | 如果保持每簇10节点 → 30个Head |
| Head路由表 | H² = 9~36 | H² = 900 → Head间控制信道饱和 |
| 泛洪放大(TTL=2) | ~10x | ~100x → 广播风暴摧毁信道 |
| 发现/收敛时间 | <15秒 | 扁平方案: >2分钟(不可接受) |
| 单跳覆盖节点数 | 全部可达 | 物理上只能覆盖局部邻居 |
核心结论:
300节点必须放弃”全网泛洪”思维,转向:
- 多级层次结构(至少3层)
- 地理/拓扑分区(只和局部通信)
- MPR多跳中继 + RPF反向路径转发(选代表性节点转发,抑制泛洪)
- 拥抱不确定性 — 用概率冗余代替精确状态同步
二、整体架构 — 三级层次 Mesh
1 | Level-2: Domain (域) |
层级参数
| Level | 名称 | 数量 | 每单元大小 | 代表角色 |
|---|---|---|---|---|
| L2 | Domain | ~6个 | ~50节点 | Backbone Node (骨干) |
| L1 | Cluster | ~50个 | ~10节点 | Head (集群头) |
| L0 | Node | 300个 | - | Leaf (叶子节点) |
为什么选这个比例?
- L0→L1: 每簇10节点 → 集群内TTL=2泛洪可控(~10x放大)
- L1→L2: 每域5个Cluster → Backbone路由表仅5项
- L2: 6个Domain → Domain间路由表仅6项,Backbone之间可直接广播通信
总控制开销对比
1 | 扁平泛洪: O(n²) = 90,000 |
三、各层级详细设计
Level-0: 叶子节点层 — 微型TDMA集群
每个Cluster内部的设计与30节点方案类似,但更紧凑。
Superframe结构 (500ms周期)
1 | ┌──────┬─────────────────────────────┬──────┐ |
关键变化 vs 30节点方案
- Superframe缩短到500ms → 降低延迟
- 时隙压缩到5ms/节点 → 需要更好的时钟同步(±1ms)
- 增加休眠机制 → 叶子节点87%时间可以sleep
叶子节点状态机
1 | ┌──────────┐ ┌─────────────┐ ┌──────────┐ |
ATTACHED节点行为:
- 在自己的TDMA时隙发送数据/HELLO
- 监听Head的广播(路由更新、同步信标)
- 在非活跃时段进入低功耗模式
- 每30秒报告一次链路质量给Head
- 新增: 相邻节点间交换时间戳做Peer-to-Peer时钟校正
Level-1: Cluster Head层 — RPF + 概率冗余转发网络
这是300节点方案最核心的设计。Head之间不能泛洪——需要用 RPF (Reverse Path Forwarding) + 概率冗余 来抑制广播风暴,同时避免MPR精确状态同步在无ACK环境下的静默丢包问题。
问题: 50个Head如果全部互相广播,控制信道饱和。
RPF转发规则(核心改进)
不再依赖”谁选了我做MPR”的全局状态——这种状态本身通过不可靠的广播维护,丢失即导致静默丢包。
改为 反向路径转发 (RPF) + 概率冗余 混合模式:
1 | 收到跨集群数据包 packet 时: |
效果:
- RPF保证”正确方向”的包一定转发 — 不依赖MPR_ASSIGNMENT的全局状态
- 概率冗余提供容错 — 即使RPF判断有误(因为拓扑信息不完整),仍有~10-20%的概率被其他节点转发
- 转发量从50x降到 ~8-12x,比纯MPR略高但鲁棒性大幅提升
Head间通信的时隙方案 — 粗粒度TDMA子槽
Head不使用精确TDMA(太复杂),也不使用纯CSMA(碰撞率被低估)。改用 粗粒度TDMA子槽 + ALOHA:
1 | Head通信窗口 (每个Superframe的Ctrl时段): |
为什么用ALOHA代替CSMA?
在广播无ACK环境下,CSMA的LBT(先听后说)实际上帮不上忙:
- LBT只能检测”现在信道忙不忙”
- 但不能防止两个节点同时开始发送(隐藏终端问题)
- 对于<64B的小控制包,ALOHA + RS(4,2) FEC的端到端可靠性可能优于CSMA——因为省去了LBT延迟和退避逻辑
为什么Head可以用ALOHA而叶子节点不行?
- Head只有50个,分到4个子槽 → 每子槽~12.5个竞争者
- 控制包小(<64B),RS(4,2)编码后96B,占用信道时间极短
- 控制消息本身有时间分集保护(连续3轮重复发送)
Level-2: Backbone层 — Domain间路由
Backbone Node (BN) 是每个Domain的”网关”:
职责
- 代表整个Domain对外通信
- 维护Domain间路由表
- 聚合Cluster Head的路由信息
- 在Domain间转发数据
BN选举(每个Domain内)— Bully算法 + 全局Epoch
改进: 使用基于同步时钟的全局epoch,避免两个Head各自独立递增导致的分裂脑。
1 | # epoch基于全网同步后的时间计算——所有节点用同一公式算出相同值 |
条件优先级:
HAS_BACKBONEflag(有线/高速连接的设备优先)- 连接到最多Cluster Head的节点
- Node_ID最小者(打破平局)
BN间通信
6个BN可以直接互相广播(数量少,CSMA完全够用)。
BN路由表 (极简):
| Domain_ID | Next_Hop_BN | Hop_Count | AoI_ms |
|---|---|---|---|
| (1 byte) | (1 byte) | (1 byte) | (2 bytes) |
最多5条路由项(6个Domain减自己)。新增 AoI_ms 字段记录信息年龄。
BN路由发现
每个BN每秒广播 DOMAIN_ADVERT:
| BN_ID | Domain_Route_Summary | Election_Epoch |
|---|---|---|
| (1 byte) | [{domain_id, hops}..] | (2 bytes) |
收到后更新路由表(标准距离向量)。新增: 用AoI代替LQI做路由选择——不依赖有偏的链路质量估计。
完整的数据转发路径示例
Node A (Cluster A1, Domain A) → Node Z (Cluster B3, Domain B):
1 | ┌─ L0: 集群内 ────────────────────────────────┐ |
四、路由协议 — 分层距离向量 + AoI信息年龄度量
路由信息聚合
关键设计: 每一层只关心本层的路由,不感知下层细节。
L0→L1 聚合 (Head维护)
- Head_A1知道:
- 集群内所有节点ID和时隙分配
- 每个节点的单向链路质量(LQI_in)
- Head_A1对外暴露:
- “我能到达Cluster A1的所有节点”
- 不暴露内部拓扑
L1→L2 聚合 (Backbone维护)
- BN_A知道:
- Domain A内所有Cluster的Head ID
- Head间的RPF转发关系
- BN_A对外暴露:
- “我能到达Domain A的所有Cluster”
L2 全局视图 (每个BN)
- BN知道:
- 其他Domain的BN ID
- Domain间跳数 + AoI(信息年龄)
路由查询(下行分解)
1 | dst=Node_Z → |
链路质量度量(LQI)— 无ACK环境下的单向估计
问题: 没有ACK,怎么知道链路质量好坏?而且无线链路经常不对称。
方案: 去掉对称性假设,只用单向指标 + 间接确认
LQI数据结构
每个节点维护:
1 | LQI_Entry[neighbor_id] = { |
LQI更新逻辑(每5秒窗口)
1 | # 入向质量 — 只依赖我收到的数据,不假设对称性 |
路由选择 — AoI优先,LQI辅助
核心改进: 用信息年龄(AoI)代替纯LQI做路由决策。AoI不依赖链路质量估计——只看”这条路由信息有多新鲜”。
1 | Route_Entry[dest] = { |
AoI的优势:
- 不依赖链路质量估计——单向广播就能更新AoI
- 天然处理拓扑变化——旧路由自动被淘汰(aoi_ms增大 → 优先级降低)
- 不需要双向测量——解决了无ACK环境下LQ_out无法准确获取的问题
路由环检测 — 增强版
300节点时路由环风险大增,需要多层防护。
机制1: Path_Trail(已有)
- 包携带已访问的Head/BN ID列表
- 最大长度: L0(0) + L1(8) + L2(4) = 12字节
机制2: Sequence Number + Generation Counter
每个路由条目携带 generation:
1 | Route_Entry[dest] = { |
机制3: Split Horizon + Poison Reverse(适配广播)
发送路由通告时:
- 从Head X学到的路由,不向X反向通告
- 如果某路由失效:
- 主动广播
INVALIDATION(dest, hop_count=255)— “到dest的路径已断”
- 主动广播
机制4: TTL硬性限制
| 层级 | TTL限制 |
|---|---|
| L0内 | TTL ≤ 2 |
| L1间 | RPF_Hops ≤ 8 |
| L2间 | BN_Hops ≤ 4 |
超限直接丢弃,不进入路由表。
五、时钟同步 — 三级分层 + Peer-to-Peer校正
同步层级
1 | Stratum 0: Global Reference (GR) |
同步误差累积分析
| 层级 | 单级误差 | 累积误差 | 是否满足需求 |
|---|---|---|---|
| GR→BN | ±0.5ms | ±0.5ms | BN间通信: ✓ (需±2ms) |
| BN→Head | ±0.5ms | ±1.0ms | Head间CSMA: ✓ (需±2ms) |
| Head→Node | ±0.5ms | ±1.5ms | TDMA 5ms时隙: ⚠ (需±1ms,临界) |
改进: Guard Interval从200μs增加到 500μs → 容忍偏差从±1ms提升到±2.5ms。代价:时隙利用率从92%降到82%,但容错提升2.5倍。
Peer-to-Peer时钟校正(新增)
不仅Head→Node单向同步,相邻叶子节点也交换时间戳:
1 | # 每个Superframe中,节点在自己的时隙末尾附加自己的timestamp |
Head→Node的主同步机制
CLUSTER_SYNC 帧格式:
| Magic | Epoch | Frame_Counter | Timestamp | FEC_Parity |
|---|---|---|---|---|
| (2 bytes) | (2 bytes) | (2 bytes) | (4 bytes) | (2 bytes) |
新增: FEC_Parity — CLUSTER_SYNC本身用RS(4,2)编码。节点收到任意2份(共3轮重复)即可解码。
成员节点的处理:
1 | 收到 CLUSTER_SYNC 时记录本地时间 T_rx_local |
同步收敛分析
初始状态: 成员时钟随机偏移,最大偏差 ±2.5ms(半个时隙)
每轮修正10%:
| Round | 偏差 |
|---|---|
| 0 | ±2500μs |
| 1 | ±2250μs |
| 2 | ±2025μs |
| … | … |
| 10 | ±984μs ← 已达标 |
| 20 | ±387μs |
| 50 | ±24μs ← 非常精确 |
收敛时间: 10 × 500ms = 5秒 → 完全可接受
六、发现与自组织 — 300节点的启动流程
冷启动(所有节点同时上线)
Phase 1: 静默监听 (0~5秒)
所有节点只收不发,收集环境信息。记录: 听到的Node_ID、RSSI分布、信号特征。
Phase 2: 分级宣告 (5~15秒)
Step 2a: BN候选者自举 — Bully算法 + 全局Epoch
1 | # epoch基于同步后的时间计算——所有节点用同一公式算出相同值 |
Step 2b: BN间建立L2路由
- 6个BN互相发现,交换
DOMAIN_ADVERT - 收敛: ~2秒
Step 2c: Head候选者自举(每个区域内)
CAN_BE_HEAD的节点广播HEAD_CANDIDATE- 受限于本Domain的BN协调
- 每域选5个Head → 全网~50个Head
- 收敛: ~3秒
Step 2d: Head间建立RPF转发表
- 交换
TWO_HOP_NEIGHBORS - 构建Forwarding_Graph(基于AoI和跳数)
- 收敛: ~2秒
Phase 3: 叶子节点加入 (15~30秒)
普通节点在Resv窗口发送 JOIN_REQUEST。Head分配时隙 → SLOT_ASSIGNMENT。批量处理: 每个Head每轮处理最多3个新成员。
Phase 4: 稳定运行 (>30秒)
- TDMA调度生效
- RPF转发链建立
- 路由表收敛
总启动时间: ~30秒(vs 3节点的2秒,30节点的15秒)
热加入(网络已运行,新节点上线)
- 监听5秒 → 发现最近的Head
- Resv窗口发
JOIN_REQUEST(head_id=最近Head) - Head分配时隙 →
SLOT_ASSIGNMENT - 下一Superframe开始工作
热加入时间: <8秒
七、可靠性 — 五层防御体系(升级版)
1 | ┌──────────────────────────────────────────────────────┐ |
综合可靠性估算
场景A: 集群内通信 (L0)
- TDMA消除碰撞 → p=0.03
- FEC(4/7) → P_fail ≈ 0.001
- NC增强 → P_fail ≈ 0.0003
- 时间分集x2 → P_fail ≈ 1e-7
- → 可靠性: 99.99999%
场景B: 跨Domain通信 (L0→L1→L2→L1→L0)
- 每跳 p=0.03, ~5跳
- FEC逐层应用
- RPF + 概率冗余备份
- → P_fail ≈ 0.004 → 可靠性: 99.6%
注意: 跨域通信可靠性较低是因为跳数多,每层都有损耗。解法: 关键数据走”慢但可靠”模式(更多FEC冗余+重复)
八、完整协议帧格式 v4
CMP-v4 (Custom Mesh Protocol v4)
1 | ┌──────────┬──────┬──────┬───────┐ |
Type codes v4 (按层级分组)
L0 - 集群内:
| Code | 名称 | 说明 |
|---|---|---|
| 0x01 | HELLO | 节点心跳 + 携带compact_missing_bitmap |
| 0x02 | JOIN_REQUEST | 加入集群 |
| 0x03 | SLOT_ASSIGNMENT | TDMA时隙分配 |
| 0x04 | CLUSTER_SYNC | 时钟同步(FEC保护) |
| 0x08 | DATA | 用户数据 |
| 0x09 | FEC_BLOCK | FEC块 |
| 0x0A | NC_BLOCK | 网络编码块 |
L1 - Head间:
| Code | 名称 | 说明 |
|---|---|---|
| 0x10 | HEAD_ANNOUNCE | Head宣告 |
| 0x11 | TWO_HOP_NEIGHBORS | RPF选举输入 |
| 0x13 | ROUTE_ADVERT_L1 | L1路由通告(含AoI) |
| 0x14 | ROUTE_INVALIDATE | 路由失效通知 |
删除:
MPR_ASSIGNMENT(0x12) — v4不再使用精确MPR状态同步
L2 - Backbone间:
| Code | 名称 | 说明 |
|---|---|---|
| 0x20 | BACKBONE_CANDIDATE | BN候选宣告(含election_epoch) |
| 0x21 | DOMAIN_ADVERT | Domain间路由(含AoI) |
| 0x22 | GLOBAL_SYNC | 全局时钟同步(FEC保护) |
| 0x23 | DOMAIN_SYNC | Domain内同步(FEC保护) |
跨层:
| Code | 名称 | 说明 |
|---|---|---|
| 0x30 | DATA_RELAY | 跨层数据转发 |
| 0x32 | COLLISION_REPORT | 冲突报告 |
| 0x33 | CLUSTER_MERGE | 集群合并/分裂 |
删除:
NACK(0x31) — v4用盲重复+聚合式慢速反馈代替即时NACK
九、性能指标汇总
| 3节点 | 30节点 | 300节点(v4) | |
|---|---|---|---|
| 架构 | 扁平 | 两层集群 | 三层层次(Domain/Cluster/Node) |
| MAC协议 | CSMA | TDMA | TDMA(L0) + RPF-ALOHA(L1/L2) |
| 路由协议 | TTL泛洪 | 受限泛洪+DV | RPF中继+分层DV+AoI选路 |
| 路由表大小 | 2项 | ~8项 | L0: |
| 控制节点数 | 0(全平等) | ~4个Head | ~50个Head + ~6个BN |
| FEC策略 | k=3,n=5 | k=4,n=7 | 自适应FEC(L0) + 分层(L1/L2) |
| 发现时间 | <2秒 | <8秒 | <30秒(冷启动) |
| 热加入时间 | <1秒 | <3秒 | <8秒 |
| 收敛时间 | <1秒 | <15秒 | <45秒 |
| 集群内延迟 | ~5ms | ~30ms | ~20ms |
| 跨域延迟 | N/A | ~100ms | ~200-500ms |
| 单包可靠性 | >95% | >99.99% | L0: 99.99999% / 跨域: 99.6% |
| 控制带宽开销 | ~10% | ~20% | ~28%(RPF冗余略高) |
| 节点休眠率 | 0% | 0% | ~87%(叶子节点) |
| 最大可扩展节点 | ~10 | ~100 | ~1000(加第四层Region) |
十、如果继续扩展到1000+节点
300→1000的演进路径
添加 Level-3: Region (区域)
1 | Region (~200节点, ~4个Domain) |
4层结构: Region → Domain → Cluster → Node
SBN间数量: ~5个(1000节点)→ 可以直接广播通信
关键变化
- SBN代替BN做跨Region路由
- BN只关心本Domain,不感知其他Region
- Path_Trail扩展到16字节
- 启动时间增加到 ~60秒
十一、总结 — 三个规模的核心设计哲学差异
3节点: “大家都是邻居,广播什么都行” → 暴力泛洪 + FEC + 重复发送
30节点: “需要分组管理,避免互相干扰” → TDMA集群 + Head路由 + MPR雏形
300节点(v4): “必须分层治理,拥抱不确定性” → 三层层次 + RPF概率转发 + AoI选路 + 自适应FEC
核心原则(适用于任何规模)
- 广播信道不可靠 → FEC是必须的,不是可选的
- 无ACK无重传 → 冗余要在发送端做,不能依赖反馈
- 节点越多 → 层次越深,每层管理半径越小
- 控制面和数据面分离 → Head/BN处理路由,叶子只管收发
- 信息聚合 → 上层不感知下层细节,路由表保持O(常数)大小
- v4新增: 拥抱不确定性 → 用概率和冗余代替精确状态同步。任何依赖”全局一致视图”的机制在广播无ACK环境下都是脆弱的
十二、RPF转发与概率冗余详解(替代原MPR章节)
1.1 为什么放弃纯MPR?
v3使用OLSR风格的MPR——维护精确的二跳拓扑,只有被选中的MPR节点才转发。在无ACK环境下有致命缺陷:
MPR_ASSIGNMENT本身通过不可靠广播发送 → 可能丢失- Head Y不知道自己被X选为MPR → Y会DROP本应转发的包
- X认为Y在MPR集中 → X以为包会被转发
- 结果:静默丢包,发送方完全不知道
v4改为 RPF (Reverse Path Forwarding) + 概率冗余 混合模式——不依赖全局MPR状态。
1.2 RPF转发规则
1 | 收到跨集群数据包 packet 时: |
1.3 Forwarding_Graph的构建
不再从MPR关系推导——直接用AoI和跳数构建转发表:
1 | # 每个Head维护到所有可达Cluster的转发表 |
1.4 概率冗余的效果分析
| 场景 | 纯MPR转发 | RPF+概率冗余(v4) |
|---|---|---|
| MPR_ASSIGNMENT丢失 | 静默丢包(0%到达) | RPF仍然工作(~95%到达) |
| 正常情况 | ~15个节点转发(30%) | ~20个节点转发(40%) + 概率冗余 |
| 链路质量差(p=0.1) | MPR选错→丢包 | 概率冗余自动提高→补偿 |
| 控制开销 | MPR_ASSIGNMENT广播 | 无额外MPR状态同步 |
结论: v4的转发量比v3多~30%,但鲁棒性大幅提升——不再依赖脆弱的精确MPR状态。
1.5 可选: Gossip协议(低频场景)
对于传感器网络/物联网Mesh,数据本身是低频的,收敛时间不是瓶颈。可以考虑用Gossip/Epidemic协议代替RPF路由:
1 | # 每个节点周期性选择k个随机邻居交换状态 |
十三、TDMA实现细节
2.1 Superframe结构设计
1 | ┌──────────────────────────────────────────────────────────────┐ |
各区域功能
Sync Region (5ms):
- Head广播
CLUSTER_SYNC信标(RS(4,2) FEC保护,连续3轮重复) - 包含: epoch, superframe_counter, timestamp, slot_map_update_flag
- 所有成员监听,调整本地时钟相位
Data Region (50ms):
- Head Slot (5ms): Head发送聚合数据/控制消息
- Node Slots (45ms = 9个 × 5ms): 叶子节点轮流发送
Ctrl Region (10ms):
- ALOHA模式(不是CSMA!)
- 用于: 紧急消息、时隙请求、碰撞报告
- 为什么用ALOHA? 见Level-1设计说明
Reserve Region (5ms):
- 新节点加入请求
- 时隙重新协商
- Head离线时的选举通信
2.2 时隙内部结构(改进版)
单个5ms时隙的微秒级分解:
1 | ┌──────────┬─────────────────────┬──────────┐ |
Guard Interval (GI, 500μs):
- 吸收时钟偏差(±2.5ms同步误差 → 500μs GI足够应对相邻时隙泄漏)
- 吸收前一时隙的尾部干扰
- 节点在GI开始时开启发射器,做频率/功率稳定
Payload (4100μs):
- 协议帧头: ~20 bytes → ~160μs @ 1Mbps WiFi
- 最大数据载荷:
5100 bytes @ 1Mbps(实际受MTU限制2000 bytes) - 如果数据不足,填充到最小帧长(确保可检测)
Tail Interval (TA, 500μs):
- 发射器关闭的缓冲时间
- 防止信号拖尾侵入下一时隙
有效数据传输率: 4100/5000 = 82% 时隙利用率,考虑协议头开销: ~76% 净数据率
2.3 时钟同步 — TDMA正常工作的先决条件
三级主同步 + Peer-to-Peer辅助校正
Stratum 2 → Stratum 3 (Head → Members):
CLUSTER_SYNC 帧格式(含FEC保护):
| Magic | Epoch | Frame_Counter | Timestamp | FEC_Parity |
|---|---|---|---|---|
| (2 bytes) | (2 bytes) | (2 bytes) | (4 bytes) | (2 bytes) |
Timestamp: Head的硬件定时器值(微秒精度)
成员节点的处理:
1 | 收到 CLUSTER_SYNC 时记录本地时间 T_rx_local |
Peer-to-Peer辅助校正:
1 | # 每个Superframe中,节点在自己的时隙末尾附加自己的timestamp |
2.4 时隙分配算法
Head维护的 Slot_Allocator 数据结构:
1 | Slot_Map = { |
固定规则:
slot_0永远分配给Head自己slot_1~slot_9分配给活跃成员(最多9个)slot_10+动态扩展(如果集群超过10节点,延长Data Region)
新节点加入的时隙分配流程
Step 1: 新节点在Reserve窗口广播 SLOT_REQUEST
| Node_ID | Priority | Data_Rate |
|---|---|---|
| (1 byte) | (1 byte) | (1 byte) |
Priority: HIGH(0)/NORMAL(1)/LOW(2)
Data_Rate: 期望的吞吐量等级(0=最低, 7=最高)
Step 2: Head收到后运行分配算法
1 | # 策略A: 有空闲时隙 → 直接分配 |
Step 3: Head广播 SLOT_ASSIGNMENT(连续3轮重复)
| Node_ID | Slot_Num | Effective | Duration |
|---|---|---|---|
| (1 byte) | (1 byte) | @ Frame N | (1 byte) |
Effective: 从第N个Superframe开始生效(给节点准备时间)
动态时隙调整(每30秒评估一次)
Head监控每个节点的时隙利用率:
1 | Utilization[node_id] = { |
调整规则:
1 | for each node in Slot_Map: |
2.5 空时隙处理与节能
空脉冲 (Empty Pulse)
当节点在分配的时隙中没有数据发送:
- 不发完整帧
- 只发一个20字节的
EMPTY_PULSE(含timestamp trailer用于P2P同步)
| Node_ID | Type | Checksum | Timestamp |
|---|---|---|---|
| (1 byte) | 0x00 | (2 bytes) | (4 bytes) |
Head检测到连续N个EMPTY_PULSE:
- N=3 → 标记节点为
IDLE - N=10 → 暂时释放时隙(发送
SLOT_SUSPENDED)
SLOT_SUSPENDED
| Node_ID | Resume_Condition |
|---|---|
| (1 byte) | (1 byte) |
Resume_Condition:
0x01= 发送SLOT_RESUME请求即可恢复0x02= 下一个Superframe自动恢复(临时释放)
节能模式 (Power Save)
叶子节点的时间线:
1 | ┌─────┬─────┬─────┬───────────┬─────┬──────────────┐ |
节能等级:
| 模式 | 行为 | 休眠率 |
|---|---|---|
| PSM_0 (全活跃) | 监听所有时隙 | 0%休眠,最大吞吐量 |
| PSM_1 (标准) | 只监听Sync+MySlot+Ctrl | ~86%休眠 |
| PSM_2 (深度) | Sync+MySlot,跳过Ctrl | ~90%休眠(紧急消息由Head在MySlot中代理通知) |
2.6 Head故障与TDMA恢复 — Bully算法选举
场景: Cluster Head离线,整个集群的TDMA调度失效。
检测机制
成员节点维护 head_alive_counter:
1 | 收到 CLUSTER_SYNC → head_alive_counter = 0 |
恢复流程
Phase 1: 静默 (1个Superframe)
- 所有节点停止发送数据
- 只监听Reserve窗口
Phase 2: Head选举 — Bully算法 + 全局Epoch
1 | # epoch基于同步后的时间计算——同一epoch只选一次 |
Phase 3: 时隙重分配 (2~3个Superframes)
新Head不知道旧Head的Slot_Map,需要重建:
- 广播
SLOT_REENUMERATION— “所有节点在Reserve窗口报告自己” - 节点依次在Reserve窗口发
NODE_PRESENT - Head收集完所有节点后构建新Slot_Map
- 广播完整 Slot_Map(压缩格式,连续3轮重复)
Phase 4: 恢复正常TDMA操作
总恢复时间: 58秒(10~16个Superframes)
旧Head回归处理
如果原Head在选举完成后重新上线:
- 它听到新Head的
CLUSTER_SYNC - 意识到自己不再是Head(head_authority中的epoch更高或Node_ID更大)
- 作为普通成员加入,发送
JOIN_REQUEST - 被分配一个叶子节点时隙
十四、自适应FEC编解码实现细节
3.1 Reed-Solomon编码基础(适配嵌入式环境)
RS码的核心数学: 在有限域 GF(2^m) 上的多项式运算。
为什么选GF(2^8)?
- 符号大小 = 1字节 → 与网络包边界对齐
- 硬件友好(XOR运算代替乘法/除法)
- 300节点Mesh中单块64~256字节,GF(2^8)足够
RS(n, k)码的参数含义
1 | n = 总符号数(编码后) |
纠错能力: 最多纠正 t = (n-k)/2 个符号错误或恢复 erasure(已知位置丢失)最多 n-k 个
我们的场景是 ERASURE模式(知道哪些块丢了,不是错了)→ 可以恢复最多 n-k 个丢失的块
3.2 自适应FEC参数配置
v4改进: FEC参数动态调整
不再固定使用RS(7,4)——Head根据观测到的解码成功率动态调整:
1 | # Head监控每个节点的FEC解码成功率 |
三层FEC的参数配置(默认值)
| 层级 | n | k | 块大小 | 冗余率 | 纠错能力 | 自适应范围 |
|---|---|---|---|---|---|---|
| L0 (集群内) | 7 | 4 | 64 bytes | 75% | 丢3/7块 | n=5~9, k=4 |
| L1 (Head间) | 6 | 3 | 128 bytes | 50% | 丢3/6块 | 固定 |
| L2 (BN间) | 4 | 2 | 256 bytes | 50% | 丢2/4块 | 固定 |
为什么只有L0自适应? L1/L2的Head和BN是固定设备,链路质量相对稳定。L0的叶子节点可能移动、遮挡,需要动态调整。
可选: Raptor/Fountain码(替代方案)
RS码的问题:接收方必须收到特定数量的块(≥k个)。Fountain码的优势:生成无限编码符号,接收方收集任意足够数量即可。
1 | 在广播环境下特别有用: |
3.3 FEC块的传输与重组协议
L0层FEC传输的完整流程
发送端 (叶子节点 Node A):
应用数据: 200 bytes
Step 1: 填充到k×block_size的整数倍
1 | k=4, block_size=64 → 需要 256 bytes |
Step 2: RS(n,k)编码(n,k由自适应参数决定)
1 | data_blocks = [ |
Step 3: 生成FEC组,分配Group_ID
1 | group_id = next_group_counter() # 全网唯一(src_id + seq) |
Step 4: 在TDMA时隙中发送 — 盲重复策略(替代NACK)
每个FEC块 = 1个协议包。节点A的5ms时隙只能发 ~1个包 → 需要多个Superframe才能发完n个块。
v4改进: 盲重复代替即时NACK
1 | # v3方案: 发送方等NACK再重传 — 但NACK本身可能丢失,违反无ACK原则 |
FEC包头格式
| Src_ID | Group_ID | Index | Total | Type | Data |
|---|---|---|---|---|---|
| (1 byte) | (2 bytes) | (1b) | (1 byte) | (1b) | (64 bytes) |
Type: DATA(0x00) / PARITY(0x01)。解码时不需要区分——RS解码器对所有符号一视同仁。
接收端 (同集群的 Node D)
Step 1: 监听并收集FEC块
1 | fec_cache[group_id] = { |
Step 2: 检查是否可以解码
1 | if len(cache.blocks) >= k: |
Step 3: 解码(高斯消元法)
1 | received_indices = sorted(cache.blocks.keys()) # e.g., [0, 2, 4, 5] |
Step 4: 去除padding,交付应用层
1 | actual_data = original_data[:original_length] |
Step 5: 超时清理 + 聚合式慢速反馈(替代即时NACK)
1 | if now() - cache.received_at[0] > FEC_TIMEOUT (30秒): |
3.4 网络编码 (Network Coding) 增强
传统FEC的问题: 每个接收方需要独立收集足够的FEC块。中间节点只是被动转发,没有利用已收到的信息。
网络编码的核心思想: 中间节点将收到的多个包做线性组合后广播,接收方用任意k个线性无关的组合包就能解码。
编码规则
中间节点B收到了来自A的FEC块: b[2] 和 b[5]
B在自己的TDMA时隙广播:
| Src_ID | Group_ID | Codec | Coeffs | Encoded_Data |
|---|---|---|---|---|
| (1 byte) | (2 bytes) | (1 byte) | (k bytes) | (64 bytes) |
Codec: 标识这是网络编码包(vs 原始FEC块)
Coeffs: [c_0, c_1, …, c_(k-1)] ∈ GF(2^8),表示 Encoded_Data = Σ c_i × original_block[i]
示例:
1 | B收到 b[2] 和 b[5] |
接收方处理NC_BLOCK
维护接收矩阵 R (m×k,m是已收到的包数):
1 | R = [row_1] D = [data_1] |
解码条件: rank(R) >= k
收益: 在链路质量差的场景下,NC可以将有效接收率提升30-50%。
十五、应用层设计建议
核心观点: 应用层对丢包的容忍度比网络层优化更重要
在无ACK广播环境下,网络层能做的是”尽力而为地提高交付概率”——但真正的可靠性来自应用层的设计。
推荐的应用层模式
1. CRDT (Conflict-free Replicated Data Type)
1 | - 每个节点维护本地状态副本 |
2. 事件溯源 + 序列号去重
1 | - 每条消息带全局递增的sequence number |
3. 批量聚合发送
1 | - 不逐条发消息——攒够N条或T秒后打包发送 |
十六、架构级优化建议
多信道/频分复用(如果硬件支持)
方案A: 控制面和数据面分离到不同信道
1 | Channel 1 (固定): 所有控制消息(HELLO, SYNC, ROUTE_ADVERT) |
方案B: 集群间用不同信道减少干扰
1 | Cluster A1 → Channel 2 |
