ARTICLE DETAIL

资讯详情

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

蜂窝调度算法 MATLAB 实现:比例公平、轮询与最大 C/I 对比

蜂窝调度算法 MATLAB 实现:比例公平、轮询与最大 C/I 对比 简介面向无线通信与Matlab仿真学习者的蜂窝系统小区用户通信调度程序完整实现了比例调度、最大速率调度、最小延迟调度三种经典算法。程序以小区用户资源分配为核心通过模拟信道状态信息与调度器决策可直观对比不同策略下系统吞吐量、用户公平性与传输延迟的平衡关系。比例调度按信道质量比例分配资源高信噪比用户获得更多传输机会同时兼顾长期公平最大速率调度侧重系统总吞吐量最小延迟调度则适合实时业务场景。压缩包内仅含1个m文件体积约1KB代码简洁紧凑便于直接运行与二次修改适合通信工程专业学生、算法研究与系统仿真人员使用。目前已有225人学习下载。通过该程序可快速搭建蜂窝网络仿真环境观察多用户场景下各算法的平均速率、公平性指标与时延表现理解不同调度策略的实际效果为课程设计、毕业设计或论文仿真提供可直接复用的Matlab实现参考。1. 蜂窝系统调度为什么 schedule052001.m 值得拆开一个单小区 20 用户的场景中心用户和边缘用户的信噪比可能差出 20 dB。基站如果只按“谁信道好谁上”来分配资源系统总吞吐量不难看但边缘用户的请求可能几十个时隙得不到响应反过来做固定轮询公平性上去了频谱利用率和用户速率又明显浪费。蜂窝系统中的小区用户通信调度算法本质上就是在这个矛盾里选一个可调的位置。这个 matlab 资源包把轮询、最大载干比、比例公平三种调度算法放进同一个脚本schedule052001.m跑一遍即能看到三种策略的行为差异。下面按调度指标来历、核心代码、仿真评估和参数边界来拆解方便改成自己的小区级仿真环境。2. 比例公平调度从调度因子落到 .m 代码2.1 调度因子为什么是瞬时速率除以历史平均比例公平调度Proportional Fair, PF是 LTE/NR 小区均衡调度中常见的默认候选方案。它用到的输入只有两样用户当前时隙的瞬时速率以及过去一段时间的平均速率。设用户 i 在时隙 t 的瞬时速率为 r_i(t)历史平均速率为 R_i(t)PF 指标写成metric_i r_i(t) / R_i(t)调度器选择指标最大的用户。如果只按瞬时速率排序边缘用户几乎没有翻身机会。PF 的分母记录的是“用户过去被服务得怎么样”信道一直差的用户 R 会被压得很低当他某次信道条件稍有回升时分子增加得不算多但除以很小的分母之后指标仍然可能超过其他用户。因此 PF 能在不显著牺牲系统吞吐量的前提下给弱信道用户保留调度机会。从数学上看这种指标是最大化全体用户对数平均速率之和的近似解。在代码里实现这个指标之前还要定义“历史平均”怎么算。工程上常用指数加权平均R(t1) (1-alpha) R(t) alpha * r(t)alpha 是遗忘因子通常取1/avg_win。avg_win 越大历史占比越高调度结果越偏向公平avg_win 越小调度器越贪婪。avg_winalpha行为偏向50.2接近最大 C/I500.02折中2000.005接近轮询这里有个容易搞错的细节未调度用户的 R 也要按R (1-alpha) * R衰减而不是原地不动。如果不衰减一个从未被服务过的用户会一直保留初始值 1边缘用户很难追上那些已被“喂大”的用户长期公平性会被破坏。2.2 schedule052001.m 中 PF 分支的通常写法解开这个资源包后核心是单文件脚本schedule052001.m。整个脚本一般分为三段参数初始化、逐时隙调度循环、统计绘图。PF 分支的核心代码很短我习惯先把它抽成一个独立函数来验证逻辑如下function [sel_user, R] pfSelect(csi_dB, R, alpha) % csi_dB: 1 x N_user 的当前时隙各用户 SNR单位 dB % R: 1 x N_user 的历史平均速率进入本函数前的值 % alpha: 指数平均遗忘因子一般等于 1/avg_win speed log2(1 10 .^ (csi_dB ./ 10)); % 信道质量转瞬时速率 metric speed ./ max(R, 1e-3); % 比例公平因子 [~, sel_user] max(metric); % 选指标最大的用户 R (1 - alpha) * R; % 未调度用户也做时间衰减 R(sel_user) R(sel_user) alpha * speed(sel_user); % 被调度用户加速率增量 end代码的逻辑很直接先通过香农公式log2(1SNR)把 SNR 换算成当前时隙的可支撑速率再除以历史平均速率得到 PF 因子最后取最大因子对应的用户。max(R, 1e-3)是为了防止历史平均速率衰减到 0 之后出现除零异常这种防御在长仿真中非常重要。参数说明里最关键的是 R 的更新位置。R (1-alpha) * R这一步作用于全部用户表示时间在流逝所有用户的信息速率权重都按比例下降只有被选中的用户能拿到alpha * speed的增量。这套更新方式与教科书定义一致。如果把更新写进 if 里只对选中用户做仿真前几十个时隙可能看不出问题但长跑之后公平性指标会被高估。提示香农容量近似算出的速率会比真实调制编码表偏高几个 dB但三种调度算法使用的是同一套近似相对顺序不受影响。做算法对比时这种简化是安全的做链路级验证就必须换成有限 MCS 查表。2.3 平均窗口与调度周期的换算PF 的窗口不能随便照搬。脚本里avg_win 50指 50 个调度时隙如果把一个时隙理解成 1ms50 就代表 50ms。实际蜂窝系统中PF 平均窗口常常设置在 100ms 到秒级以适配业务时延要求。仿真中为了观察三种算法的差异把窗口缩短到几十个时隙是合理的但结论不能直接外推到真实基站。要调窗口时先看当前信道的时间相关性。信道衰落相关时间由多普勒频移决定如果用户移动速度低相干时间较长窗口太短会让调度结果频繁抖动影响收敛指标如果用户移动快窗口长会导致调度器使用早已过期的历史信息边缘用户反而被误伤。建议先用avg_win50跑出基线再按仿真目标向两个方向各调一组对比实验。3. 轮询与最大 C/I另外两种调度算法的实现思路3.1 轮询的索引与队列轮询调度器结构最简单但有一个容易忽略的前提用户列表必须连续且固定。静态仿真里一行代码就够% 静态仿真阶段按时间序号依次选择用户 sel_user mod(t - 1, N_user) 1; % 动态场景用队列索引先取队首再放回队尾 user_idx queue(1); queue(1) []; queue(end 1) user_idx;第一行的行为是第 1 个时隙选用户 1第 2 个时隙选用户 2到第 N 个时隙后又回到用户 1。它的优点是没有任何状态保存不依赖 CSI 估计也不会因为信道反馈错误而选错人。缺点也明显完全不看信道质量中心用户和边缘用户被分配到的资源块数量相同但由于边缘用户的频谱效率低其获得的实际速率远低于中心用户只能保证“机会公平”不能保证“速率公平”。动态场景中用户可能随时切换或掉线固定mod索引会指向已不在小区里的用户。这时需要维护一个循环队列把新用户压入队尾掉线用户从队列中移除每次从队头取一个用户调度再回插到队尾。这个约束在算法代码里很容易被漏掉但一旦接入实际业务模型就会出现计时器空转和资源浪费。3.2 最大 C/I 调度的贪心排序最大 C/ICarrier to Interference Ratio调度也常写作最大速率调度策略是每个时隙选信道质量最好的用户。核心代码更少[~, sel_user] max(csi_dB); % 选 SNR 最高的用户由于速率是 SNR 的单调递增函数直接选最大 SNR 与选最大瞬时速率等价。这个调度器是典型贪心策略能够给出系统总吞吐量的上界。问题是它完全没有公平约束当用户信道存在长期差异时边缘用户会被连续跳过。比如某个用户在 1000 个时隙里信道都不如别人那么他可能一个数据包都发不出去。在工程中我通常只把这个算法当作性能上界参考不会直接配置到实际小区里。调度器如果支持多资源块还需要在每一块上重复做一次排序复杂度从 O(N) 变成 O(RB * N)但对现代处理器依然可以接受。真正的坑是平局处理如果两个用户的 SNR 完全相同max函数永远返回所有最大值中的靠前一个这会产生偏置导致某个用户被额外优待。常见的修正方式是列出所有最大值候选再随机挑一个cand find(abs(metric - max(metric)) 1e-12); sel_user cand(randi(numel(cand)));3.3 三种调度器的适用边界和代价调度算法决策依据总吞吐量公平性单时隙复杂度轮询 RR时隙序号最低最好O(1)最大 C/I瞬时信道质量最高最差O(N)比例公平 PF瞬时速率/历史平均速率接近最大中间O(N)这张表的读法不是“选一个最好的”而是先确定仿真目标。如果目标是研究负载均衡或验证业务队列模型RR 的简洁性很有价值如果目标是寻找系统的速率上界用最大 C/I只有当你希望模拟真实移动宽带用户混合场景时PF 才是最接近现网行为的基线。还有一个实际工程点PF 和最大 C/I 的复杂度都是 O(N)但 PF 需要维护每个用户的历史平均速率这部分内存和更新计算在用户数超过上万时会开始占资源。蜂窝小区级仿真一般还不到这个规模所以调度器本身不会成为瓶颈。若对运行时间敏感可以把每时隙的speed计算从调度循环内提到循环外利用矩阵一次性算好只让调度循环做索引选择。4. 用单小区仿真循环对照三种调度器的性能4.1 先把仿真场景参数固定下来调度算法对比最容易出现的问题是三种算法不在同一个信道上比较。如果每次循环都重新生成随机信道那么吞吐量差异可能来自信道本身的涨落而不是调度器。因此先把随机源和仿真参数固定。参数数值说明N_user20小区内活跃用户数T_slot1000调度时隙数avg_win50PF 平均窗口baseSNR10 dB用户 SNR 中心值shadowStd4 dB对数阴衰标准差信道建模采用10 4 * randn(T, N)生成每时隙每个用户的 SNR单位 dB。这个模型避免了路径损耗和多普勒过程的复杂度足够暴露三种调度器的差异。如果要把结论扩展到真实小区则需要把baseSNR换成与用户距离相关的路径损耗表达式例如128.1 37.6 * log10(d)然后再叠加上面这层小尺度波动。4.2 三种调度器在同一个循环里跑下面是基于 schedule052001.m 的常见写法规整出的可运行骨架。rng(2026); % 固定随机种子保证三种算法共用同一信道 N 20; T 1000; avg_win 50; alpha 1 / avg_win; csi_dB 10 4 * randn(T, N); % 所有时隙的信道采样 rate_RR zeros(T, N); % 轮询实际分配的速率 rate_MC zeros(T, N); % 最大 C/I 实际分配的速率 rate_PF zeros(T, N); % 比例公平实际分配的速率 R_pf ones(1, N); % PF 历史平均队列 for t 1:T speed log2(1 10 .^ (csi_dB(t, :) ./ 10)); % 当前时隙各用户瞬时速率 % 轮询 sel mod(t - 1, N) 1; rate_RR(t, sel) speed(sel); % 最大 C/I [~, sel] max(speed); rate_MC(t, sel) speed(sel); % 比例公平 metric speed ./ max(R_pf, 1e-3); [~, sel] max(metric); R_pf (1 - alpha) * R_pf; % 未调度用户也随时间衰减 R_pf(sel) R_pf(sel) alpha * speed(sel); % 被调度用户增加历史速率 rate_PF(t, sel) speed(sel); end这里给每个时隙只分配一个用户模拟的是整个载波资源块只给一个用户使用的简化条件。每一行速率矩阵记录的是该时隙实际被选中的用户速率其余位置保持 0。主循环里的三种分支相互独立所以信道序列csi_dB一旦固定后续比较就是标准的控制变量实验。仔细看 PF 分支的顺序先算指标再更新 R最后把速率写入rate_PF。如果把 R 更新放在选择用户之前等于当前时隙的调度决策使用了上一时隙的历史信息这在移动性较强的场景中会产生半个调度周期的相位偏差仿真结果会略偏向保守。4.3 用平均吞吐量和 Jain 公平指数做评估完成循环后统计工作分两部分。第一部分是平均吞吐量直接对列求平均就能得到每个用户在整个仿真期间的吞吐第二部分是 Jain 公平指数公式为J (sum(x)^2) / (N * sum(x.^2))其中 x 是各用户的平均吞吐量向量。J 取值范围从 1/N 到 1完全均等时接近 1。start 51; % 丢弃前 50 个时隙瞬态 avgRate(1, :) mean(rate_RR(start:end, :), 1); avgRate(2, :) mean(rate_MC(start:end, :), 1); avgRate(3, :) mean(rate_PF(start:end, :), 1); totalThroughput sum(avgRate, 2); Jain sum(avgRate, 2).^2 ./ (N * sum(avgRate.^2, 2)); disp(table(totalThroughput, Jain, ... RowNames, {RR, MaxC/I, PF}));丢弃前 50 个时隙很重要尤其对 PF。仿真开始前 R_pf 被统一初始化为 1前几十个时隙PF 的指标和最大 C/I 几乎一致历史平均还没有积累起区分度这段数据混入统计会人为提高 PF 的总吞吐量或降低公平性。剔除瞬态后结果更接近稳态行为。一个典型运行下三种算法的数量级大致如下算法总吞吐量bps/HzJain 指数RR8.30.97Max C/I13.20.35PF11.60.66这个表只是示意数据真实数值随随机种子变化但相对顺序一般不会改变最大 C/I 吞吐最高但对用户极不公平RR 公平但浪费容量PF 落在两者之间并且通过调整avg_win可以向左或向右移动。若看到 PF 的总吞吐量低于 RR大概率是 R 更新逻辑写错了例如只更新被调度用户而未对未调度用户衰减导致调度指标里保留了大量“废弃历史”。5. 参数微调、验证与三个容易忽略的坑5.1 把脚本封成函数再批量验证schedule052001.m是脚本形式对学习方便但对参数扫描不方便。我一般会先在外面包一层函数输入用户数、时隙数、窗口和随机种子输出统计量。function out runScheduler(N, T, avg_win, seed) rng(seed); % 这里放第 4 章的调度循环和统计代码 out.totalThroughput totalThroughput; out.jain Jain; end做完封装后用多个种子做交叉验证。单次仿真只能描述某一组信道样本下的行为尤其是当用户数只有 20 时个别用户的信道波动会明显影响公平指数。批量执行是最快排除运气因素的方法seeds 1:20; res arrayfun((s) runScheduler(20, 1000, 50, s), seeds, UniformOutput, false); jains cellfun((x) x.jain, res); fprintf(PF Jain: %.3f /- %.3f\n, mean(jains), std(jains));这段代码在 MATLAB 2026b 或更早版本中都不需要额外工具箱只需要基础函数。即使换到 R2026b 也保留了原来的接口可以直接运行。另一个常用技巧是调整avg_win扫描窗口。建议用[5, 50, 200]三组窗口对比同一随机种子观察 PF 如何从接近最大 C/I 逐渐移向轮询。这样得到的不只是一张表而是调度器在整个“公平-吞吐”轨迹上的行为边界。最后有三个高频出现的坑未调度用户的 R 不衰减。这会造成长期公平性被高估仿真里要写成R (1-alpha) * R后再叠加增量。信道单位混用。csi_dB是分贝值speed必须在线性域计算不能把 dB 直接代入香农公式反之对速率求平均也不能在 dB 域进行。统计窗口内混入瞬态。至少丢弃前 10 个时隙稳定的做法是丢弃avg_win长度的时隙这样调度器进入稳态后才开始记录。如果 PF 的 Jain 指数稳定落在 0.6-0.8 区间而最大 C/I 降到 0.3 以下说明三种算法的边界被正确呈现此时再调avg_win才有实际意义否则先回头检查 R 的更新顺序。本文还有配套的精品资源点击获取
返回列表