news 2026/9/15 18:13:22

MATLAB实现蜂窝小区用户调度:RR、Max C/I与比例公平算法详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MATLAB实现蜂窝小区用户调度:RR、Max C/I与比例公平算法详解

简介:一套面向蜂窝系统小区用户通信调度研究的Matlab程序包,适合通信工程专业学生、无线网络研究人员及算法初学者,用来理解小区中多用户资源分配的核心逻辑。程序包含三种经典调度算法,比例调度算法依据用户信道质量按比例分配时频资源,兼顾系统吞吐量与长期公平性;同时通过调度器、CSI反馈与质量度量等模块,涉及信噪比、误码率等指标,配合另外两种经典策略,使学习者可以直观对比不同算法的效率与适用场景。压缩包仅含1个m脚本文件,整体约1KB,轻量精简,无需复杂配置即可运行,可作为基础模板进行二次开发与参数调整。已有225人学习参考,适合在课程设计或项目入门阶段阅读源码,快速掌握从蜂窝网络建模、信道模拟到算法仿真评估的完整流程,为实际系统设计提供有益参考。

1. 蜂窝系统小区用户调度:为什么比例公平算法是MATLAB仿真的默认起点

在LTE/NR蜂窝系统里,基站每个时隙都要面对一屋子排队等资源的用户。调度算法决定谁用这个时隙的资源块,直接决定用户感知速率、小区边缘吞吐量和系统公平性。比例公平(PF)之所以成为学术和工程里最常被实现的经典算法,是因为它既不追求每时每刻的系统容量极限,也不是简单的轮流分配,而是用"瞬时速率除以历史平均吞吐量"这个比值做排序,让信道条件好的用户多传数据,同时给信道差的用户保留翻身机会。这篇博文用MATLAB从零实现轮询(RR)、最大载干比(Max C/I)和比例公平(PF)三种经典调度算法,给出一套可运行、可扩展的小区用户通信调度程序框架。适合刚接触无线资源管理的研究生,也适合想快速验证调度算法想法的工程师。

2. 三种经典调度算法的模型与MATLAB实现基础

2.1 调度问题建模:用户集合、时隙与信道质量矩阵

蜂窝系统的资源调度问题在仿真里被离散成时隙序列,每个时隙通常对应一个TTI(传输时间间隔)。假设小区内有N个激活用户,每个用户在每个时隙有一个瞬时可支持速率r_i(t),这个速率一般由用户上报的CQI(信道质量指示)映射得到,在系统级仿真里常近似为香农容量。另外,每个用户需要一个长期平均吞吐量R_i(t),它随着每个时隙被调度或不被调度而更新。调度算法的任务就是根据r_i(t)R_i(t),从N个用户中选出在这个时隙获得资源的用户下标i*(t)

在实际MATLAB代码中,我习惯把整个仿真里的瞬时速率组织成一个N x T的矩阵,每一列是一个时隙所有用户的速率,每一行代表一个用户的时变信道。“信道质量矩阵”这个词指的就是这个矩阵,它可以直接从信噪比矩阵换算而来。三种算法的核心差异在于选用户的决策规则:轮询只依赖时隙序号和用户数;最大载干比只看当前时隙瞬时速率最大值;比例公平同时看瞬时速率和历史平均吞吐量。下表列出这三种算法在设计动机上的不同:

算法选择度量公平性定位系统吞吐量预期计算开销
RR(轮询)时隙序号取模严格等概率较低O(1)
Max C/I(最大载干比)当前瞬时速率最大完全不考虑公平O(N)
PF(比例公平)瞬时速率/历史平均速率最大兼顾吞吐量与公平接近最优但低于Max C/IO(N)

这个表格在仿真开始时用于提醒自己:如果只追求系统容量曲线,用Max C/I就够了;如果要说明算法在实际系统中的可用性,PF和RR往往是必须一起展示的对比基线。

2.2 轮询(RR)算法:公平性基准的代码骨架

轮询算法是最容易实现、也最容易被低估的调度策略。它不读取任何信道反馈信息,只维护一个转弯指针,每个时隙把调制解调资源轮流分给下一个用户。在MATLAB中,一个完整的调度决策函数只需要一行核心代码:

function idx = rr_schedule(slot, N) % slot : 当前时隙序号,从1开始 % N : 小区激活用户数 idx = mod(slot - 1, N) + 1; end

逻辑说明:mod(slot-1, N)的结果范围是0到N-1,加一后映射到1到N之间的用户编号。假设N=10,slot=1选择用户1,slot=12选择用户2。该函数完全不依赖信道状态,因此复杂度是O(1)。由于每个用户被选中的概率完全相等,RR的用户平均吞吐量长期会趋于一致。但这意味着信道条件好的用户不能多传,而信道条件差的用户也占着同样多的时隙,导致整个小区的频谱效率很低。在仿真中,RR通常用来作为下界基线:如果新的算法连RR的公平性都达不到,那在实际系统中就没有宣传价值。

2.3 最大载干比(Max C/I)算法:系统吞吐量上限

Max C/I调度,也叫最大信干比调度,是另一种极端。它的目标很简单:每个时隙都把资源分配给当前瞬时信干噪比最高的用户,从而让系统的瞬时吞吐量最大化。用MATLAB实现时,max函数一步就能算出结果:

function idx = max_ci_schedule(r_inst) % r_inst: 1xN或Nx1的瞬时速率向量 [~, idx] = max(r_inst); end

逻辑说明:max返回速率的极大值和其位置。由于瞬时速率与信干噪比是单调递增关系,找出速率最大值等价于找出信干噪比最大用户。这里的idx默认是max沿第一个维度的位置,不管r_inst是行向量还是列向量都不会影响结果。

但Max C/I的最大问题在于公平性趋近于零。如果用户离基站的远近差异明显,那么靠近基站、处于良好信道状态的用户会连续被调度,而小区边缘用户的吞吐量可能长期为零,这在容灾和业务保障上是不可接受的。在代码里观察这个问题最直接的方式是看每个用户累积服务次数:Max C/I算法下,某些用户的sum(served > 0)会非常小,甚至接近于0。

2.4 比例公平(PF)算法:吞吐量与公平性的折中

比例公平算法的思想来自经济学中的比例公平性定义,后来被引入无线调度。它需要维护每个用户的历史平均吞吐量R_i(t),然后在每个时隙选择能最大化度量值r_i(t) / R_i(t)的用户。这个比值意味着:如果某个用户历史平均吞吐量很低,即使它当前信道不是最好的,也有机会被调度;反之,如果某个用户刚刚被连续调度了很多次,它的R_i(t)被抬高了,竞争下一个时隙的能力就会下降,从而把机会让给其他用户。MATLAB代码可以写成:

function idx = pf_schedule(r_inst, avg_rate, alpha) % r_inst : 当前时隙的瞬时速率向量 % avg_rate: 当前时隙开始前的历史平均吞吐量向量 % alpha : 公平性调节因子,经典PF取1 metric = r_inst ./ max(avg_rate, eps); [~, idx] = max(metric); end

逻辑说明:./是逐元素右除,r_instavg_rate必须是同型向量。max(avg_rate, eps)把可能为0的平均吞吐量替换成比零稍大的极小值,避免除零警告。alpha出现的位置在广义PF公式里是对比值的指数,这里为了展示简洁直接省略了metric = (r_inst ./ avg_rate).^alpha,实际上当alpha=1时两者完全一样。alpha=0时所有用户的度量值恒为1,算法退化成轮询;alpha趋于无穷大时,算法越来越偏向瞬时速率最大的用户,逼近Max C/I。在设计了pf_schedule后,还需要同时实现平均吞吐量的更新,否则avg_rate永远是初始值,PF就会退化成Max C/I。更新公式通常写作:被调度的用户R_i(t) = (1 - 1/Tc)*R_i(t-1) + (1/Tc)*r_i(t);未被调度的用户R_i(t) = (1 - 1/Tc)*R_i(t-1)。这个公式会在下一章的完整程序里体现,它也是学习这个算法时最容易漏掉的一段。

3. 用MATLAB编写可复现的调度仿真程序

3.1 仿真主循环:从信道生成到调度决策

在有了三个调度决策函数之后,剩下的工作是把它们整合进一个可复现的仿真主循环。下面给出一个在MATLAB R2023b上可以直接运行的版本。程序包含信道生成、调度决策、平均吞吐量更新和结果记录四个基本步骤,没有使用任何通信工具箱,纯手写矩阵操作。

clear; clc; rng(42); N = 10; % 激活用户数 T = 1000; % 仿真时隙数 Tc = 100; % 平均吞吐量平滑窗口 alpha = 1; % PF调节因子 alg_list = {'RR', 'MAXCIR', 'PF'}; % 生成瞬时信噪比矩阵:瑞利信道下,线性域SNR服从均值1的指数分布 snr = exprnd(1, N, T); avg_rate_all = zeros(N, length(alg_list)); total_throughput_all = zeros(T, length(alg_list)); for k = 1:length(alg_list) alg = alg_list{k}; avg_rate = zeros(N, 1); % 列向量,避免维度问题 for t = 1:T r_inst = log2(1 + snr(:, t)); % 香农速率,单位bps/Hz switch alg case 'RR' idx = mod(t - 1, N) + 1; case 'MAXCIR' [~, idx] = max(r_inst); case 'PF' metric = r_inst ./ max(avg_rate, eps); [~, idx] = max(metric); end served = zeros(N, 1); served(idx) = r_inst(idx); % 更新所有用户的历史平均吞吐量 avg_rate = (1 - 1/Tc) * avg_rate + (1/Tc) * served; total_throughput_all(t, k) = sum(served); end avg_rate_all(:, k) = avg_rate; end

逻辑说明:exprnd(1, N, T)生成均值1的指数分布矩阵,这对应单径瑞利衰落下的瞬时SNR分布。r_inst使用连续香农容量近似,实际系统中要按CQI量化成离散速率,但三种算法在这里的对比不受影响。主循环里每一个时隙先读取该时隙所有用户的瞬时速率,然后按算法选择用户,随后构造served向量,只有被选中的用户位置有速率。平均吞吐量更新时,所有用户都乘以(1 - 1/Tc),被调度用户再加上(1/Tc)*r_inst(idx),这正是前面说过的指数平均。Tc=100意味着历史平均吞吐量约在100个时隙内完成主要更新,这是一个比较常见的经验取值。

3.2 用户吞吐量记录与统计口径

上面代码里avg_rate_all保存的是仿真结束时每个用户的平均吞吐量指数平滑值,这个值可以作为最终统计口径吗?严格来说,指数平滑值不等同于真正的长期时间平均,尤其当T不够大时,最后一个时隙的avg_rate还带有历史记忆。更稳妥的统计口径是另外维护一个累计服务速率计数器,在每个时隙把served累加进一个变量,仿真结束后除以T得到每个用户的平均吞吐量。修改方式如下:

cum_rate = zeros(N, 1); % 在时隙循环内部,调度完成之后: cum_rate = cum_rate + served; % 循环结束后: user_mean_rate = cum_rate / T;

逻辑说明:cum_rate每个时隙累加的是被服务用户实际获得的瞬时速率,未调度用户累加零。所以user_mean_rate是真正的按时间平均的用户吞吐量。它与指数平滑值之间的误差会随着T增大而缩小。建议在报告中同时输出这两套数值:一套用于公平性指数计算(用user_mean_rate),另一套用于展示典型用户的吞吐量收敛轨迹(用avg_rate随时间的变化)。如果需要看收敛轨迹,可以在每个时隙把avg_rate存进一个T x N矩阵,代价是内存占用随T线性增加。

3.3 参数设置:用户数、时隙数、遗忘因子与反馈粒度

仿真结果经常随参数不同而出现截然不同的结论,所以参数设置必须有所依据。下表给出这套程序中最关键的四个参数以及它们的参考范围:

参数参考值对结果的影响
N用户数5~30用户数越多,PF相对RR的吞吐量提升越明显,但计算时间线性增加
T时隙数500~5000太短则统计不收敛,太长则白白消耗内存和CPU时间
Tc平滑窗口10~1000窗口越小,平均吞吐量跟随瞬时速率变化越快,公平性越好,但吞吐量损失更大
alphaPF指数0~20退化为RR,1为经典PF,2更偏向容量最优,边缘用户速率会下降

除此之外,snr矩阵的生成方式也属于隐式参数。用exprnd(1,N,T)表示所有用户平均SNR相同,这适合展示算法本身的行为。如果要模拟单小区用户分布,可以把每个用户设置成不同的平均SNR。常见做法是先定义用户到基站的距离向量d,路径损耗指数pl_exp,然后把平均SNR算成snr_mean_db = 10 - 10*pl_exp*log10(d + eps),再用10.^(snr_mean_db/10)转换成线性域均值。信道矩阵变成:snr = 10.^(snr_mean_db/10) .* exprnd(1,N,T)。这样改写后,Max C/I的边缘用户饿死现象会比原来更明显,PF的公平性提升也会更直观。

3.4 扩展场景:不同算法在自定义SNR矩阵上的对比

exprnd生成的对称信道下,Max C/I也会让每个用户轮流获得最高速率,因为各用户统计同分布,不会出现某个用户永远最差的情况。因此,如果想看到Max C/I的病态行为,必须让用户平均信道质量拉开差距。比如设置用户1的平均SNR比其他用户高10dB,然后对比三种算法的用户平均吞吐量。可以这样修改:

snr_mean_db = linspace(10, 0, N); % 用户1最好,用户N最差 snr_mean_lin = 10 .^ (snr_mean_db / 10); snr = snr_mean_lin' * ones(1, T) .* exprnd(1, N, T);

逻辑说明:snr_mean_lin' * ones(1,T)生成N x T的均值矩阵,再与同样尺寸的exprnd(1,N,T)逐元素相乘,得到的每个用户时变SNR的均值各不相同。这样设置之后,RR的用户平均吞吐量会按SNR均值排列,Max C/I几乎只调度用户1,PF介于两者之间。这张对比图能直观地说明为什么真实系统中不会使用纯Max C/I调度。

4. 调度结果分析与排错

4.1 吞吐量与公平性指标的计算

仿真得到每个用户的平均吞吐量后,第一个要计算的指标是系统平均吞吐量,即所有用户平均吞吐量之和;第二个是公平性指数。Jain公平性指数是最常用的标准,定义是所有用户吞吐量之和的平方除以用户数与平方和。MATLAB代码:

R = user_mean_rate; % 某一算法的Nx1平均吞吐量向量 sys_throughput = sum(R); fairness = sys_throughput^2 / (N * sum(R.^2));

逻辑说明:fairness取值范围是1/N到1。如果所有用户吞吐量相等,fairness等于1;如果只有一个用户获得全部资源,fairness等于1/N。可以用这个指数评价三种算法。在典型单小区同分布信道下,RR的公平性接近0.95以上,Max C/I的公平性可能在0.5左右,PF通常能到0.8左右。如果加入用户间SNR差距,Max C/I的公平性会直线下降,低到0.1也不奇怪。

4.2 典型曲线:累积分布函数与时间平均吞吐量

除了数值指标,画累积分布函数是展示用户间速率差异最好的方式。代码片段如下:

figure; for k = 1:length(alg_list) [f, x] = ecdf(user_rate_all(:, k)); % user_rate_all各列为不同算法 plot(x, f, 'LineWidth', 1.5); hold on; end hold off; xlabel('用户平均吞吐量 (bps/Hz)'); ylabel('CDF'); legend('RR', 'Max C/I', 'PF', 'Location', 'southeast');

逻辑说明:ecdf是MATLAB自带函数,会返回经验CDF的横纵坐标。横轴是用户平均吞吐量,纵轴是累积概率。CDF曲线越靠右,说明整体吞吐量越高;曲线越陡峭,说明用户间差异越小。Max C/I的曲线往往会有一段很长的水平段,表示大量用户吞吐量接近0;RR的曲线几乎是一条垂直上升的直线;PF的曲线位于两者之间,既不太陡也不太平。这张图在写报告时比表格更有说服力。

4.3 常见陷阱:对数域计算、边缘用户饿死与矩阵维度不匹配

调度仿真代码报错最多的不是算法本身,而是MATLAB矩阵操作细节。第一个陷阱是信噪比单位混用。如果snr是dB值,那么香农公式不能写成log2(1 + snr),因为dB是功率的10倍对数,必须先把dB转回线性域。正确的写法是:

snr_lin = 10 .^ (snr_db / 10); r_inst = log2(1 + snr_lin);

第二个陷阱是avg_rate初始值为零时,PF的r_inst ./ avg_rate会得到无限大或NaN。使用max(avg_rate, eps)可以解决,但eps并不是一个合适的平均吞吐量下限,因为它会导致第一次调度时所有有非零速率的用户度量值都是超大数,只有刚好瞬时速率也为0的用户被排除。在实际代码里,更好的处理是让初始avg_rate等于一个小的正数,比如1e-6,同时用eps保护,双保险。

第三个陷阱是矩阵维度不匹配。avg_rate初始化为zeros(N,1),而served不小心写成了zeros(1,N),那么avg_rate = (1-1/Tc)*avg_rate + (1/Tc)*served在MATLAB中会尝试隐式扩展,最终得到一个N x N矩阵,不仅不报错,还会在后续计算中产生莫名其妙的维度错误。这是最隐蔽的坑。建议在代码开头强制把所有相关向量都转成列向量:

avg_rate = avg_rate(:); served = served(:); r_inst = r_inst(:);

这样即使中间某个变量是行向量,也会被纠正。另一个值得注意的问题是边缘用户饿死。如果仿真结束后发现某个用户累计速率严格为0,可以先检查Tc是否过大导致历史平均吞吐量下降太慢,再检查alpha是否过大导致PF偏向Max C/I。在真实场景中,解决饿死问题的常用手段是加最低资源保障,例如每隔N个时隙强制调度一次边缘用户,但这属于调度算法扩展,不在三种经典算法范围内。

5. 场景扩展:从单小区到多小区与移动性

5.1 从单小区到多小区:把干扰折算进瞬时速率

单小区调度跑通后,最常见的延伸方向是把你关注的调度算法放到多小区干扰场景下验证。此时不能再用独立的snr矩阵,而是要为每个用户生成到所有小区的信道增益,再把其他小区的信号作为干扰叠加。在主循环中,可以先把邻区干扰折算成对每个用户的SINR,再转换成瞬时速率。比如用户i在时隙t的SINR可以写成:

sinr_i = P_own * G_i_own / (sum(P_other .* G_i_other) + noise); r_inst_i = log2(1 + sinr_i);

这样做的好处是,调度器本身不需要改动,它依然只接收一个r_inst向量。三种算法在干扰变化下的行为差异就能被直接观察到:干扰较强时,Max C/I会更明显地偏向干扰小的用户,而RR和PF的公平性优势则会更加突出。

5.2 验证调度器正确性的手工矩阵技巧

扩展代码之前,有一个必须做的小验证:用手工构造的极端SNR矩阵检查调度输出是否符合直觉。例如设置N=2、T=4,用户两时瞬态速率交替为高和低:

时隙用户1速率用户2速率
1101
2110
3101
4110

Tc设成极大值,比如1e9,此时PF的历史平均吞吐量更新非常慢,效果接近Max C/I,调度序列应当是1、2、1、2。再把Tc设成1,此时历史平均吞吐量完全跟随上一时隙的服务结果,PF的度量值会经常变化,但长期统计下会接近RR。用这两组参数运行程序,逐一检查时隙调度索引是否和手推一致,能快速定位调度器代码里的取模错误、索引偏移或者更新公式写反的问题。这种验证方法比直接看吞吐量曲线可靠得多,因为曲线只会告诉你整体对错,手工序列能告诉你具体哪个时隙出错。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 18:12:10

移动端点餐H5 DEMO详解:触摸滑动、购物车与订单流程实现

简介:一款基于HTML5、JavaScript与CSS构建的移动端点餐系统DEMO,聚焦餐饮App的菜单浏览、菜品分类、购物车、订单评价等核心场景,适合Web前端初学者、移动端开发入门者,也适合需要课程设计或毕业设计原型的高校学生,代…

作者头像 李华
网站建设 2026/9/15 18:10:46

stc89c52驱动的小型清扫机器人系统设计|毕业设计项目|毕设项目|单片机项目|物联网专业|毕业设计

一、项目介绍 摘 要 一台能自主规避障碍、边行进边吸尘的小型机器人,设计并实现了一款基于STC89C52单片机的小型清扫机器人系统。系统以STC89C52单片机作为核心控制器,将E18-D80NK红外避障传感器、L298N电机驱动模块、BD681风扇驱动电路与按键输入电路有…

作者头像 李华