ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

6G MAC层仿真核心解析:调度器、HARQ与波束管理的建模实践

6G MAC层仿真核心解析:调度器、HARQ与波束管理的建模实践 1. 为什么6G研究绕不开MAC层仿真前几篇我们把6G仿真的物理层、信道模型、天线阵列那些底子打完了到了这一篇终于要碰整个协议栈里最“磨人”的一层——MAC层。做无线网络仿真的都知道物理层再复杂本质上是算信道、算误码、算星座点数学功底到位结果就不会太离谱。真正让人血压升高的是MAC层这种“一堆实体互相抢资源、还要定时器驱动、状态机满地跑”的层。MAC层为什么在6G里变得特别关键我说点实在的。6G的研究方向里超密集组网、毫米波/太赫兹频段、大规模天线阵列、动态TDD、无小区架构、感知通信一体化每一项都对MAC层提出了新要求。比如太赫兹频段下信道变化极快传统的CSI反馈周期可能跟不上信道老化速度MAC层必须设计新的调度机制再比如感知通信一体化雷达感知和通信业务要共用一个资源池MAC层得同时保证感知的刷新率和通信的时延还有动态TDD上下行时隙比例可以灵活调整但调整的触发条件、保护间隔怎么预留、相邻小区怎么协调全是MAC层的活。物理层告诉你“调制编码方式能干到多快”MAC层决定“在这么多用户抢一个频谱池时谁先拿、拿多少、什么时候拿”。在6G这种动辄上百连接、业务类型五花八门的场景里MAC层设计得好不好直接决定系统容量是大路货还是黑科技。这篇是系列的第6篇我默认你已经对6G物理层仿真和信道建模有基本概念了没看过的往回翻一下。没有基础也没关系我会从MAC层仿真最核心的模块讲起把架构、参数、代码、坑全部串一遍。这篇内容适合三类人一是刚接触6G接入网研究、准备搭系统级仿真的研究生二是做无线网络产品预研、想评估MAC算法性能的工程师三是纯好奇6G协议栈到底怎么仿真、想建立全局认知的爱好者。我把话放这里读完这篇你不仅能看懂别人的MAC层仿真模型还能自己动手改调度器、调参数、跑仿真、看结果。2. MAC层仿真的整体设计与核心模块拆解2.1 仿真场景设计先选好业务模型再做MAC六个月的仿真经验告诉我一个道理MAC层仿真最忌讳“啥都想跑”。6G场景太多URLLC、eMBB、mMTC、感知通信一体化每种业务的QoS要求天差地别如果你在一个仿真里全塞进去结果就是调度器变成一团浆糊你根本分不清性能瓶颈在哪。我建议第一次跑MAC层仿真就选一个主场景加上一个对比场景比如“URLLC为主、eMBB做背景流量”后面所有结果分析都有清晰的参照系。具体到业务模型URLLC的数据包大小我一般设置为32字节到达间隔服从泊松分布平均到达率20包/秒对应单用户业务速率约5kbps看似不高但时延预算只有1ms——注意是空口时延1ms不是端到端。eMBB作为背景每用户目标速率10Mbps包大小1500字节TCP流量模型。这个配置我用了很多次跑出来的曲线既有区分度又不会因为某种业务太极端导致仿真没法收敛。场景拓扑上6G无小区架构是热点但如果你第一次做我劝你先从经典的小区架构开始一个gNB半径100m的覆盖范围30个UE均匀分布移动速度3km/h到60km/h混合。无小区架构留给后面熟悉了再扩展它涉及多gNB协作调度复杂度是单站的好几倍一上来就玩这个容易把自己劝退。2.2 理解MAC层里谁在干活调度器、HARQ、CSI反馈、波束管理MAC层不是一个大黑盒里面几个关键实体各有分工。我拆开讲你仿真的时候就知道该在哪里下功夫。第一个核心是调度器。它对上接收RRC层下发的QoS需求对下向物理层申请资源并分配。调度器做的事情简单讲就是“在共享的时频资源块里决定哪块给哪个用户”。6G里调度器还要考虑波束每个资源块不仅仅是一个时频格子还对应一个发送波束方向。这个耦合关系在物理层仿真里经常被忽略但在MAC层必须显式建模因为波束切换是需要时间的切换期间不能调度数据。第二个核心是HARQ。LRLLC和eMBB的HARQ参数不同URLLC通常使用更少的重传次数最大重传次数2次eMBB允许到4次甚至更多。我在仿真里处理HARQ最头疼的是ACK/NACK反馈延迟——这个延迟在系统级仿真里很容易被简化成“0”但实际MAC层里反馈要等固定的处理时间。这个时间如果设得太理想URLLC时延指标会好看得骗人。第三个是CSI反馈。调制编码方式要选择合适的MCS前提是基站知道信道质量。CSI反馈周期、测量间隔和反馈延迟这三个参数一起决定了基站眼里“信道的新鲜度”。6G高频场景下信道变化快反馈周期要更短但反馈本身也占用资源这是一个此消彼长的关系仿真里能清晰看到这个trade-off。第四个是波束管理。这是6G MAC层比4G/5G复杂很多的地方初始接入时的波束扫描、业务进行中的波束跟踪、波束失败后的恢复。我在仿真模型里单独实现了一个波束状态机模块状态包括INACTIVE、ACTIVE、TRACKING、FAILED、RESTORING。这个状态机跑起来你会发现物理层仿真完全看不出来的问题全都冒出来了波束切换失败会导致这一帧的调度直接作废如果模型里没留切换时间点仿真结果看起来会“过于流畅”。2.3 工具选型NS-3、OMNeT和MATLAB怎么选工具选择是个老生长谈但我还是想基于实操感受说几句。我三种都用过最后长期保留了NS-3。维度NS-3OMNeTMATLAB 5G Toolbox学习曲线陡峭C入门但文档好中等NED语言和C混合平缓但算法封装太重模块丰富度5G-LENA、NIST模块都是现成的有INET框架无线模块要自己补协议栈完整但偏PHYMAC层可改性好调度器、HARQ都能换中看模型设计差核心算法难深改大规模仿真支持并行效率还行慢几千个节点就开始卡底层的多用户场景很难扩展适合阶段系统级算法验证协议细节设计物理层与链路级验证如果你要深改MAC层算法——比如设计一个感知和通信联合调度器那只能选NS-3理由是它的模块结构清晰NR MAC层的代码路径相对独立改起来不牵连其他模块。OMNeT适合更上层的协议细节仿真特别是你要模拟完整协议栈的信令交互时它的状态机建模很顺手。MATLAB我建议只用来做链路级验证验证完赶紧切到系统级工具。3. 实操搭建6G MAC层仿真环境与关键参数配置3.1 环境准备NS-3 5G-LENA模块的搭建笔记我在Ubuntu 22.04上搭过很多次NS-3环境每次都踩一遍CMake的坑这次给你一个相对干净的安装流程。git clone https://gitlab.com/nsnam/ns-3-dev.git cd ns-3-dev git checkout ns-3.39 ./ns3 build # 先编译一个干净的基座然后安装5G-LENA模块这是目前开源社区里对NR MAC层支持最完整的扩展模块。安装方式是把模块目录链接到NS-3的contrib目录下。git clone https://gitlab.com/cttc-lena/nr.git contrib/nr ./ns3 configure --enable-tests --enable-examples ./ns3 build编译顺利的话你已经有一个支持NR物理层与MAC层协议的仿真器了。这里有个经验NS-3版本和5G-LENA版本要严格匹配我见过无数人死在这里。NR模块的README里会明确写支持的NS-3版本号几个月前的nr模块就不支持ns-3.39你是3.38就老老实实用3.38。仿真代码我习惯用C脚本写而不是Python绑定。原因是MAC层的回调机制、定时器这些Python绑定时有时会有隐藏的逻辑坑C版本虽然写起来啰嗦但调试的时候能直接用gdb打断点定位问题快得多。3.2 关键参数计算从6G指标反推仿真配置仿真参数不能凭空捏要从你关心的性能指标倒推。以URLLC空口1ms时延为例我们反推一下帧结构参数。6G的基础参数集还在讨论中但目前主流候选是子载波间隔120kHz起步可扩展到240kHz、480kHz。为什么这么大因为子载波间隔越大符号周期越短时延越低。以120kHz为例OFDM符号周期约8.33微秒一个时隙包含14个符号时隙长度约0.125毫秒。空口1ms的时延预算最多最多能容纳8个时隙的传输加反馈周期这意味着调度周期必须收敛在一个时隙以内。资源块的定义5G/6G里一个资源块是频域12个子载波时域1个时隙。系统带宽100MHz、子载波间隔120kHz时子载波总数是100e6/120e3约833个向下取整为792个能被12整除的最大值因此资源块总数为792/12等于66个资源块。这66个RB就是调度器每时隙要分配的总资源。我看过不少新手直接把子载波间隔用成15kHz——那是4G的数值跑出来的时延数据跟6G目标完全对不上还以为是自己调度算法有问题。做6G仿真第一步就是检查帧结构参数别用默认值。3.3 调度器实现比例公平算法的MAC层版本调度器我以比例公平算法为例子讲解它在“公平性”和“吞吐量”之间平衡得恰到好处是MAC层仿真最常用的基线。// 简化版的PF调度器核心逻辑 void PfScheduler::Schedule() { // 1. 获取每个用户当前的MCS等级和对应传输速率 // 2. 计算每个用户的长期平均吞吐量 T_avg[i] // 3. 计算每个用户在当前时隙的瞬时可达速率 T_inst[i] // 4. 调度优先级 P[i] T_inst[i] / T_avg[i] std::vectoruint32_t sortedUe; for (uint32_t i 0; i m_ueList.size(); i) { m_pfMetric[i] m_instRate[i] / (m_avgRate[i] 1e-6); sortedUe.push_back(i); } // 按P[i]降序排序依次分配RB std::sort(sortedUe.begin(), sortedUe.end(), [](uint32_t a, uint32_t b) { return m_pfMetric[a] m_pfMetric[b]; }); // 分配过程每个RB给当前优先级最高的用户但受限于其MCS for (uint32_t rb 0; rb m_totalRb; rb) { for (uint32_t ue : sortedUe) { if (m_spectrumManager-CanTransmit(ue, rb)) { m_assignment[rb] ue; break; } } } }我写的这个是精简示意版。真实环境里还要考虑LDPC码率、MCS表映射、功率分配、qos流到承载的映射。但“CanTransmit”这一步特别关键它做的是时域和频域资源检查比如这个UE在那个RB上是否做了波束对齐如果波束状态不是ACTIVE这个RB就算有资源也不能分配。这个逻辑在4G仿真里没有到了6G就变成必需品。URLLC流量的调度优先级处理是另一件事。URLLC的分组如果到达后发现所有RB已经指派给eMBB用户了那就是一个“in-band抢占”的场景。我的实现方式是eMBB的调度器先跑URLLC到达后用更高优先级抢占低MCS的eMBB用户的RB被抢占的eMBB数据包在下一个时隙重新调度。这个策略实现出来你能直观看到eMBB吞吐量在URLLC业务重载下的退化曲线这个曲线本身就是一篇论文的好素材。3.4 仿真脚本一个可运行的URLLCeMBB混合场景这里给出一个NS-3 5G-LENA的配置片段我跑过可以出结果。核心是场景搭建和模块参数设置。#include ns3/core-module.h #include ns3/nr-module.h #include ns3/nr-helper.h using namespace ns3; int main(int argc, char* argv[]) { // 1. 创建gNB和UE节点 NodeContainer gnbNodeContainer, ueNodeContainer; gnbNodeContainer.Create(1); ueNodeContainer.Create(30); // 2. 设置天线和无线参数 double frequency 28e9; // 28GHz毫米波频段 double bandwidth 100e6; // 100MHz带宽 double subcarrierSpacing 120e3; // 120kHz子载波间隔6G URLLC基础场景 // 3. 部署在任意位置 MobilityHelper mobility; mobility.SetMobilityModel(ns3::ConstantPositionMobilityModel); mobility.Install(gnbNodeContainer); mobility.Install(ueNodeContainer); // 4. MAC层关键参数配置 Config::SetDefault(ns3::NrMacSchedulerNs3::SchedType, StringValue(PF)); Config::SetDefault(ns3::NrMacSchedulerNs3::HarqEnabled, BooleanValue(true)); Config::SetDefault(ns3::NrMacSchedulerNs3::MaxHarqTx, UintegerValue(2)); // 5. 业务配置URLLC与eMBB的QoS承载 // 核心思路是给不同承载设置不同的LCG优先级和数据包大小 // 这步需要结合你自己的流量模型类来实例化代码略 Simulator::Stop(Seconds(10)); Simulator::Run(); Simulator::Destroy(); return 0; }不要指望直接复制粘贴就能跑。MAC层仿真脚本就像一道菜谱盐少许、糖适量才是关键跑不起来很正常下面的问题排查章节会帮到你。4. 仿真结果分析与关键性能指标解读4.1 指标不只是吞吐量MAC层仿真跑完之后输出了一堆数据文件然后呢很多新手只盯着吞吐量这一个指标这不够的。我建议至少看五个维度。指标计算方法6G下重点关注时延数据包到达MAC层到被成功接收的时间差URLLC场景需统计99.999%分位时延不只是均值吞吐量单位时间成功传输的数据量小区总吞吐量与边缘用户吞吐量要分开看丢包率重传耗尽后丢弃的分组比例看不同业务等级的丢包差异资源利用率已分配RB/总RB低于30%说明调度器过度保守公平性指标一般用Jain指数小于0.6说明调度严重偏斜6G通信时延极其讲究分位数。均值1ms和“99.999%的分位是1ms”是两个概念前者用户观感上完全不可用后者才是URLLC夸下的海口。NS-3本身不直接给你输出这些分位数但你可以用仿真记录的Packet Delay Histogram自己统计。我习惯在仿真代码里挂一个自定义Trace每个包完成传输时记录延迟最后拿到Matlab或者Python里算分位数这个流程比用仿真器内置的统计工具灵活得多。4.2 一次典型仿真结果长什么样我拿上次跑的混合场景给你演示一下“怎么看结果”。同样是30个用户纯eMBB场景下PF调度器的Jain公平指数是0.89小区吞吐量跑到了500Mbps。加入URLLC流量后URLLC用户的平均时延是0.8ms但eMBB的吞吐量掉到了420Mbps公平指数也降到0.74。看起来eMBB被牺牲了还挺合理对吧但关键问题在于eMBB掉量是必要的还是算法太笨造成的我调了MaxHarqTx参数把URLLC的最大重传次数从2改成1时URLLC丢包率上升了0.7%但eMBB吞吐量回升到460Mbps。这就是经典的trade-off题目你论文里的图就应该是这种“鱼与熊掌”的对比而不是只画一条仰望星空的好看曲线。再细看时延分布你会发现URLLC的平均时延和99.999%分位时延相差3倍以上。原因一是HARQ重传躲不掉二是调度器在每个时隙只能处理有限数量的高优先级分组突发流量一来必然排队。所以下一步优化方向就很明确排队时间的压缩或者给URLLC预留专用资源块池。“从数据发现问题从问题反推算法”这才是仿真的价值。5. 常见问题与排查技巧实录5.1 仿真跑不动/跑得慢怎么优化MAC层仿真的计算开销大头在调度算法和HARQ状态机。30个用户、66个RB每个时隙调度一次仿真10秒要调度8万个时隙CPU时间轻松飙到几十分钟。我的处理套路有三个级别。第一降低仿真时长先跑0.1秒验证逻辑正确性再加到1秒、10秒不要一上来就10秒。第二关掉不必要的trace像Packet::EnablePrint和RngRun这种日志在Debug阶段开跑真实数据时全关I/O时间大幅下降。第三最有效的手段——分析模式下先跑一个只有调度器、没有复杂物理层的场景把MAC代码和PHY解耦。这样可以确定是调度算法本身慢还是信道计算拖累了整体性能。如果调度算法本身跑10秒仿真需要20分钟那就要回头看看你的RB遍历方式有没有多重嵌套、排序算法是否用了O(n^2)的手写冒泡——这种低级问题我在审代码时见得太多了。5.2 仿真结果和理论完全不匹配先排查什么现象一仿真出的URLLC时延稳定在0.1ms远好于1ms理论值。恭喜你你的HARQ反馈延迟大概率被设成了0。反馈模型必须要有处理时间哪怕你模拟1个OFDM符号的处理延迟结果也会立刻正常起来。现象二小区总吞吐量高得离谱超过带宽上限。这是物理层简化的锅。一个RB在既定MCS下能传多少比特有个上限如果你没有接物理层的传输块大小映射表MAC层会以为任意MCS都能传任意大小。解决方法是引入“Transport Block Size”查表函数把MAC调度结果和真实物理层能力挂钩。现象三公平性指标Jain指数长期稳定在0.95以上完美得像化装舞会。这往往意味着你UE的业务模型是“无限背靠背流量”大家都在全速跑调度器怎么排都差不多。改成泊松到达混合业务模型差异性立刻出现。5.3 那些文档里不会写、但我踩过的坑第一个坑仿真随机性管理。NS-3如果不显式设置随机种子每次运行结果都不同你很难判断算法修改是有效果还是运气。我每次跑完一个版本会把当时的随机种子记录在一个文本文件里和一个基线配置做对照实验时严格只用同一套种子。第二个坑资源块编号和子载波偏移。NS-3里RB的索引从0开始物理层逻辑信道映射时如果偏移了哪怕1个子载波频率偏置会悄悄吃掉边界路段的吞吐但不会直接报错你看中间数据一切正常只有结果差一口气。遇到这种“怎么调都差一点”的情况拿频谱资源分配图出来晾一眼检查有没有奇怪的边缘碎片。第三个坑波束管理状态机没有退出条件。我第一版实现里波束失败恢复时状态机卡在RETRY循环UE的几个RB一直不能被调度整个系统掉了一半吞吐量我还以为是调度器优先级公式算错了。这个Bug靠读代码差点读崩溃最后是靠画状态转移图才定位的。用到状态机的地方一定要添加状态计数器和滞留时间监测。最后一个实打实的建议仿真日志要有条件编译级别的开关。你永远不知道哪些细节会在后面debug时用到我给每个关键事件都打上标记如“UE_3_BEAM_FAILED”和“RB_12_PREEMPTED”用条件编译控制开关。这比事后反复重跑仿真、重新加日志高效太多。做MAC层仿真debug的时间往往多于建模的时间这些准备工作能让你的心态平和不少。我自己现在跑6G MAC层仿真已经开始往AI原生调度方向延伸了——用深度强化学习动态调整资源块预留比例对比传统算法的机会很大。你要是有兴趣可以先把这篇里的基线场景跑通把调度器、HARQ、波束管理这几个核心模块的手感摸透再把你的创新算法挂上去。仿真这种东西真正的深度不在代码跑通的那一下而在于你看到每一个性能曲线背后协议栈里到底是哪个环节用怎样的机制写下了那一条线。
返回列表