☰
5G-NOMA功率分配MATLAB仿真:从SIC解码到算法实现全解析
2026/10/18 13:41:32 网站建设 项目流程

简介:提供一套完整的MATLAB 5G-NOMA系统功率分配仿真环境,适配MATLAB 2022a版本,面向通信专业学生与工程师,用于快速复现并评估单小区多用户非正交多址系统的功率域资源分配性能。资源包共8个文件、约8.87MB,包含带逐行中文注释的MATLAB主程序、演示完整操作流程的AVI操作录像、辅助验证结果的仿真图片以及txt/py等配置文件。核心计算逻辑基于标准香农公式,实现用户容量Cf、Cnf、Cn及累积容量Ca等指标,可清晰看出功率分配策略对用户速率与容量的影响,并输出SINR、用户可达速率对比及功率系数影响分析。操作录像还专门强调了工作目录的正确设置,避免路径错误导致变量未定义或数据加载失败,适合直接运行并开展二次开发。压缩包内文件类型覆盖主程序、操作录屏、结果图片与说明文档,结构清楚便于按需取用,目前已有156人学习下载。

1. 5G-NOMA功率分配仿真:一个MATLAB资源包能帮你省下什么

有段时间我在做5G-NOMA的链路级仿真,项目X卡在功率分配模块上整整两周。当时手头有一堆参考论文,但论文里那几行优化公式落到MATLAB代码里,各种对不上号——信道增益排序方向错了、SIC解调顺序反了、功率归一化漏了,跑出来的误码率曲线简直没法看。后来拿到一份带中文注释和操作录像的MATLAB仿真资源包,半天时间就把系统级平台跑通了,并且功率分配算法部分可以直接改参数做对照实验。这份资源的核心价值是:它把NOMA系统建模、用户配对、SIC解码和功率分配算法的完整链路打包成可运行的MATLAB工程,每行关键代码都有中文注释,还附带操作录像帮你对步骤。适合正在做5G通信方向毕业设计、课程设计,或者想快速搭一个NOMA仿真平台验证算法效果的人——你不用从零啃通信原理和MATLAB语法,直接改参数、跑结果、看曲线就行。

2. 功率域NOMA的工作机制:叠加编码、SIC解码与功率分配的角色

2.1 为什么NOMA能提升频谱效率:从正交到非正交的演进逻辑

传统正交多址(OMA)系统里,不同用户占用不同的时频资源块,互相不干扰,但频谱利用率天花板明显——5G的eMBB场景要支撑海量连接和高吞吐,正交资源划分太奢侈了。NOMA的思路是让多个用户共享同一时频资源块,在功率域或码域上做区分,接收端用干扰消除算法把信号一层层解出来。

功率域NOMA是最主流、也最容易用MATLAB仿真验证的一种。它不需要复杂的码本设计,核心就两件事:发送端对不同用户信号分配不同功率并叠加发射;接收端按信道增益从强到弱依次做串行干扰消除(SIC)。由于信道好的用户先解码并消除其他用户干扰,信道差的用户反而因为分配功率高而更容易保底,系统总吞吐量能比OMA提升30%~50%(具体数值取决于信道条件和功率分配策略)。

在MATLAB里建模这个链路不算难,但有两个前提必须理清:第一,功率分配算法决定每个用户的发射功率,直接影响SIC的解码门限;第二,用户配对方式决定哪些用户共享同一资源块——通常是一个信道好的近用户配一个信道差的远用户,这样功率差大、SIC增益明显。这两点在仿真代码里对应两个独立的子函数,后面第4章会具体展开。

2.2 发送端功率叠加与接收端SIC解码

功率域NOMA的发送端,假设同一子信道上两个用户u1和u2,总发射功率为P,功率分配系数分别为α1和α2(α1+α2=1),那么发送信号为:

s = sqrt(α1·P)·s1 + sqrt(α2·P)·s2

这里有个关键约定:信道增益低的用户分配更大的功率系数。原因是信道差的用户信号弱,如果功率也低,SIC解码时它的信号会被信道好的用户信号完全淹没。所以一般约定:|h1|² > |h2|² 时,α1 < α2。也就是说,信道好的用户拿小功率,信道差的用户拿大功率。

接收端SIC解码顺序:信道好的用户u1先解码,把u2的信号当作干扰,直接解出自己的信号(因为自己的信号功率虽小,但信道条件好,等效信噪比能扛得住);然后u1从接收信号中减去自己的信号分量,剩下的就是u2的信号,由于u2功率大,u2在u1解码完后的残余信号里信噪比也很高,能顺利解出。MATLAB实现这个过程的伪代码逻辑是:

% SIC解码顺序:信道增益降序排列 [~, idx] = sort(channel_gain, 'descend'); % idx(1)是信道最好的用户,先解码 for k = 1:num_users % 计算当前用户信号在接收信号中的功率占比 % 解码当前用户信号 % 减去已解码信号分量,更新残余接收信号 end

逻辑说明:这段代码的核心是按信道增益排序后依次解码。每个用户解码时,把尚未解码的用户信号视为噪声,所以排序方向错了,整个SIC链路就全乱了。参数说明里,channel_gain是各用户的信道增益向量,num_users是共享同一资源块的用户数,一般取2或3,超过3个用户时SIC的误差传播会急剧放大,仿真结果很难看。

2.3 功率分配在NOMA中的核心地位

功率分配策略直接决定NOMA系统的性能上限。固定功率分配(FTPA)实现简单,但公平性差——信道好的用户速率极高、信道差的用户可能连最低速率都保不住。分数阶功率分配(FTPA)引入一个公平性因子,在吞吐量和公平性之间折中。注水算法则是在总功率约束下最大化系统总速率,但需要知道所有用户的信道状态信息,计算复杂度高,不适合实时场景。

在MATLAB仿真里,这三种策略对应三条不同的功率分配曲线,直接影响后面误码率、吞吐量、用户速率均衡性三个指标。很多初学者在跑仿真时,为了省事直接用等功率分配(α1=α2=0.5),结果SIC解码出来的误码率跟OMA差不多,然后得出“NOMA也没什么优势”的错误结论。实际上等功率分配在NOMA里是最差的选择——功率差没拉开,SIC增益几乎为零。

所以功率分配这块,既不能为了追求吞吐量把功率全给信道差的用户,也不能为了公平性把功率平均分。我一般会在仿真参数表里同时定义总功率P、功率分配策略类型、以及策略对应的关键参数(比如FTPA的衰减因子),这样跑一个对照实验只需要改一行配置。

3. MATLAB仿真框架搭建:从系统参数到信道模型的分层设计

3.1 顶层系统参数配置:载波频率、带宽、用户数与调制阶数

拿到这份资源包后,第一步不是看代码,而是先改仿真参数。这套MATLAB工程的顶层参数集中在一个初始化脚本里,包含载波频率、系统带宽、子载波数、用户数、用户配对方式、调制方式、信道类型、目标误码率等。参数直接决定仿真运行时间——我之前有次把子载波数设成2048、蒙特卡洛次数设成10000,结果脚本跑了一整晚还没结束。

% 仿真系统参数配置 fc = 2e9; % 载波频率 2GHz,5G FR1典型频段 bw = 20e6; % 系统带宽 20MHz num_subcarriers = 128; % 子载波数(资源块粒度) num_users_per_sub = 2; % 每个子信道复用的用户数(NOMA配对) total_power = 46; % 基站总发射功率 dBm,约40W noise_power = -174 + 10*log10(bw) + noise_figure; % 底噪功率 dBm mod_order = 4; % QPSK调制,高阶调试验时可改16QAM num_mc_trials = 1000; % 蒙特卡洛仿真次数,调试时先设100

逻辑说明:这段参数配置是整个仿真平台的“总闸门”。总功率和噪声功率都用了dBm单位,计算信噪比时直接相减即可,不用来回转换线性功率。注意noise_figure是接收机噪声系数,资源包里默认设置为5dB,实际仿真时可以按场景调整。参数说明:num_subcarriers决定频域粒度,资源包默认128个子载波,对应一个20MHz带宽下的基本资源块配置;num_users_per_sub设成2是最经典的NOMA场景,改成3或4时需要同步修改SIC解码部分的循环逻辑,资源包代码已经支持,但复杂度会明显上升。

3.2 信道模型与用户配对:频率选择性信道与归一化增益

NOMA仿真里信道模型的选择比OMA更敏感。因为SIC解码顺序完全依赖信道增益排序,如果信道模型太理想(比如纯AWGN),所有用户的信道增益接近相等,功率分配拉开差距的效果就体现不出来,仿真结果跟OMA几乎没区别。资源包默认使用瑞利衰落信道模型,并考虑路径损耗——近用户和远用户的信道增益差异通过路径损耗体现。

% 瑞利衰落信道生成 + 路径损耗 distance_near = 100; % 近用户距离基站 100m distance_far = 500; % 远用户距离基站 500m path_loss_exp = 3.5; % 城区典型路径损耗指数 % 生成信道系数:h = 小尺度衰落 * sqrt(路径损耗) h_near = (randn(1,num_subcarriers) + 1j*randn(1,num_subcarriers)) / sqrt(2); h_far = (randn(1,num_subcarriers) + 1j*randn(1,num_subcarriers)) / sqrt(2); pl_near = 10^(-28/10) * distance_near^(-path_loss_exp); pl_far = 10^(-28/10) * distance_far^(-path_loss_exp); h_near = h_near .* sqrt(pl_near); h_far = h_far .* sqrt(pl_far); % 计算归一化信道增益,用于用户配对和SIC排序 gain_near = abs(h_near).^2; gain_far = abs(h_far).^2;

逻辑说明:信道系数由单位方差复高斯随机变量乘上路径损耗的平方根得到,这是OFDM系统仿真的标准做法。randn生成实部和虚部都服从标准正态分布,除以sqrt(2)保证幅度期望为1。路径损耗用对数距离模型:L(dB) = 28 + 10·n·log10(d),这里的28dB是1米参考距离处的路径损耗,n是路径损耗指数。参数说明:distance_near和distance_far决定了两个用户之间的信道增益差,这个差值直接影响NOMA功率分配的收益——差值太小(比如100m和150m),SIC增益有限;差值太大(比如100m和1000m),远用户即使分到最多功率也达不到最低速率门限。实际仿真中我会把远近用户距离比控制在3~5倍,效果最典型。

3.3 代码结构设计:主函数、算法子函数、统计输出三层划分

这份资源包的工程结构分三层,跟我个人做仿真项目的习惯很一致。第一层是主脚本,负责参数初始化、循环调度、汇总结果;第二层是核心算法子函数,包括用户配对、功率分配、SIC解码、误码率统计;第三层是结果可视化脚本,负责画星座图、误码率曲线、吞吐量柱状图。这样分层的最大好处是:换功率分配算法时只改第二层对应的子函数,其他代码完全不动。

% 主脚本调用结构 % 1. 初始化参数 config.m % 2. 生成信道并配对用户 [user_pairs, channel_gain] = pairing(channel_params) % 3. 功率分配 alpha = power_allocation(strategy, channel_gain, power_params) % 4. 发送端叠加编码 tx_signal = superpose_signals(signal1, signal2, alpha) % 5. 经过信道 rx_signal = channel_transmit(tx_signal, h) % 6. 接收端SIC解码 [rx_bit1, rx_bit2] = sic_decode(rx_signal, h, alpha) % 7. 统计误码率和吞吐量 ber = bit_error_rate(tx_bits, rx_bits)

逻辑说明:这种调用链把NOMA收发流程拆成了7个步骤,每步对应一个独立子函数。如果只想看功率分配的作用,可以直接在步骤3打断点,检查alpha(功率分配系数)在不同策略下的变化。参数说明:strategy参数支持'equal'(等功率)、'ftp'(固定功率分配)、'frac'(分数阶功率分配)、'water'(注水算法),在config.m里修改这个字符串变量就能切换算法,不需要改其他代码。这个设计对于做对照实验特别友好——同一套信道、同一组比特,只换功率分配策略,跑出来的性能差就是算法的真实差距。

4. 功率分配算法实战:固定分配、分数阶分配与注水算法的代码实现

4.1 等功率分配与固定功率分配:基准线和最简单的分配方式

等功率分配作为NOMA的“反面教材”基准线,在对照实验中必不可少。它的代码最简单,但跑出来的曲线能让你直观看到功率差对SIC的重要性。固定功率分配(FTPA)在此基础上引入一个固定比例——信道好的用户分小功率、信道差的用户分大功率,比例预先设定,不随信道变化。

function alpha = power_allocation_equal(P, num_users) % 等功率分配:每个用户分到相同功率 alpha = ones(1, num_users) / num_users; end function alpha = power_allocation_fixed(P, channel_gain, ratio) % 固定功率分配:按预设比例分配,信道差用户拿更多功率 num_users = length(channel_gain); alpha = zeros(1, num_users); % 按信道增益降序排序(增益从小到大对应功率从大到小) [~, idx] = sort(channel_gain, 'ascend'); % 给信道最差的用户分配 ratio 比例的功率 alpha(idx(1)) = ratio; alpha(idx(2)) = 1 - ratio; end

逻辑说明:等功率分配里alpha全是0.5,此时SIC解码时用户间干扰完全无法消除,仿真出来的速率曲线和OMA几乎重合。固定功率分配里ratio是信道差用户的功率占比,一般设0.7~0.9,ratio越大,信道差用户的保护越强,但信道好用户的速率会明显下降。参数说明:注意sort用'ascend'排序,目的是让idx(1)对应信道增益最小的用户(即信道最差的用户)。很多初学者在这里会用错排序方向,导致功率分配给了信道最好的人,仿真结果直接反直觉。

4.2 分数阶功率分配FTPA:公平性因子的作用与代码实现

分数阶功率分配是目前论文和课题里最常用的策略,它用一个公平性因子α_ftp控制功率分配的不均匀程度。当α_ftp=0时退化为等功率分配;α_ftp=1时功率与信道增益成反比,极端不公平。通常取0.2~0.6之间,兼顾系统吞吐量和用户公平性。

function alpha = power_allocation_frac(P, channel_gain, alpha_ftp) % 分数阶功率分配:alpha_i = P * (1/gain_i)^alpha_ftp / sum(...) num_users = length(channel_gain); numerator = (1 ./ channel_gain).^alpha_ftp; denominator = sum(numerator); alpha = P * numerator / denominator; % 归一化保证功率系数之和为1 alpha = alpha / sum(alpha); end

逻辑说明:核心公式是功率系数与信道增益的-α_ftp次幂成正比。信道增益小(信道差)的用户,其倒数的α_ftp次幂大,分到的功率就多。α_ftp越大,功率分配越倾斜。参数说明:alpha_ftp这个参数需要手动调节,在不同信道条件下最优值不同。我一般会写一个外层循环扫alpha_ftp从0.1到0.8,画出吞吐量和公平性随alpha_ftp变化的曲线,然后选取拐点处的值。这是资源包操作录像里重点演示的操作——因为alpha_ftp不是越大越好,超过0.6后信道好用户的速率会急剧恶化,系统总吞吐量反而下降。

4.3 迭代注水算法:总功率约束下最大化系统吞吐量

注水算法是功率分配的性能上界,它的思想是:把更多的功率分配给信道条件好的子信道,因为那里“性价比”最高。但在NOMA场景下,注水算法要同时考虑SIC解码的功率差约束,不能完全按OMA的思路来做。

function alpha = power_allocation_water(P, channel_gain, noise, max_iter) % 迭代注水算法:最大化系统总速率 % 初始化功率分配 num_users = length(channel_gain); alpha = P / num_users * ones(1, num_users); tolerance = 1e-3; for iter = 1:max_iter % 计算每个用户的等效信噪比 sinr = alpha .* P .* channel_gain / noise; % 注水水位:1/lambda = mean(1./sinr) water_level = mean(1 ./ sinr); % 更新功率分配:alpha_new = max(0, water_level - 1./channel_gain*...) alpha_new = max(0, water_level - noise ./ (P .* channel_gain)); % 归一化 alpha_new = P * alpha_new / sum(alpha_new); % 判断收敛 if norm(alpha_new - alpha) < tolerance break; end alpha = alpha_new; end alpha = alpha / sum(alpha); end

逻辑说明:这是一个简化版的迭代注水实现。每次迭代先算当前分配下的SINR,再根据注水公式更新功率分配。max(0, ...)确保功率不为负,归一化保证总功率恒定。收敛判据是前后两次迭代的功率分配向量差值的范数小于容差。参数说明:max_iter一般设50就到头了,实际仿真中10~20次迭代就能收敛。tolerance设1e-3够用,设太小(1e-6)会显著增加迭代次数,对结果精度几乎没帮助。另一个坑是噪声归一化——noise必须是线性功率值,不能直接用dBm数值参与计算,否则注水公式完全失效。

值得注意的一点:注水算法虽然总速率最高,但它的功率分配结果可能不满足SIC解码的门限约束——尤其是信道好的用户可能分配到极小的功率,导致它第一层解码就失败。所以在工程实用中,注水算法的结果通常只作为性能上界参考,实际系统还是用FTPA多一些。这个资源包的对照实验里,注水算法的总速率通常比FTPA高10%~15%,但信道好用户的误码率也可能比FTPA高一个量级。

5. 调参与避坑指南:初学者最常见的五个翻车现场

5.1 翻车现场一:SIC解码顺序用错方向,信号全解错

现象:仿真出来的误码率接近0.5,跟随机猜差不多,而且两个用户的误码率都很高。

原因:SIC解码顺序应该按信道增益从高到低排列——信道好的先解码。但很多初学者在排序时用了'ascend',导致信道差的用户先去解码,它的信号功率大但信道增益差,解出来全是错的,错误信号被减掉后反而把信道好用户的信号也破坏了。

解决:在排序前加一行显示当前排序结果,确认是降序排列。代码里统一用sort(channel_gain, 'descend'),并且排序索引要同步传递给功率分配函数和SIC解码函数,保证两边用的是同一套顺序。我曾经在调试时发现功率分配用的是降序索引、SIC解码用的是升序索引,两个函数各排各的,结果恶劣到完全不能看。从那以后我每次都会在配对和SIC两个函数入口各加一个assert,检查排序方向一致。

5.2 翻车现场二:功率归一化漏做,SIC解码门限全乱

现象:仿真曲线整体偏移,吞吐量看起来比理论值高出一大截——高得离谱,明显不合理。

原因:功率分配函数返回的α系数没有除以总功率和,导致叠加信号的总功率超过设定值P。在接收端,SIC解码门限是基于归一化功率计算的,实际功率偏大时等效信噪比被抬高,误码率虚低、吞吐量虚高。

解决:在功率分配函数末尾统一加alpha = alpha / sum(alpha),确保所有用户的功率系数之和严格等于1。资源包代码里每个功率分配函数都有这一行,但如果你自己新写了一个功率分配算法,很容易忘记加。我一般会在主脚本里加一行assert(abs(sum(alpha) - 1) < 1e-6),程序一运行就自动检查,省得事后对着错误曲线瞎猜。

5.3 翻车现场三:注水算法不收敛,迭代次数耗尽

现象:运行注水算法时,10次仿真里有几次结果明显异常——某次吞吐量特别低,功率分配系数出现负值或者大于1的情况。

原因:注水算法对初始值敏感,尤其是信道增益动态范围很大时,初始等功率分配可能让某些用户的SINR特别低,导致注水水位计算偏离,功率更新后出现震荡不收敛。另外噪声参数如果用了dBm值而不是线性值,整个公式就错了。

解决:首先确认noise参数是线性功率(瓦特),不是dBm。然后给注水算法加一个“稳定化”处理——每次迭代的步长乘以一个0.8的衰减因子,防止功率更新在临界点附近震荡。最后把收敛容差适当放大到1e-2,因为注水算法对精度要求不高——功率系数差0.01对最终速率的贡献可以忽略。如果还是不收敛,检查初值:把等功率分配改成按信道增益反比分配,能加速收敛。

5.4 翻车现场四:随机信道不固定种子,蒙特卡洛对比失真

现象:两次运行同一个仿真脚本,结果差异巨大;或者用两套功率分配算法做对比,一个比另一个高20%,换一次运行时差距变成5%,结论完全不稳定。

原因:MATLAB随机数生成器没固定种子。每次运行生成不同的信道系数,蒙特卡洛统计结果没有可比性。更隐蔽的是,不同功率分配算法内部可能也调用了随机数函数(比如迭代算法里的随机初始化),导致算法间的对比混入了随机因素。

解决:在主脚本最前面加rng(2024)固定随机种子,确保每次运行的信道完全一致。如果要比较不同功率分配算法,必须在同一批信道下逐个运行算法,然后对比结果。资源包的脚本设计已经保证了这一点——信道生成在功率分配之前,所以固定种子后,所有算法跑的是同一批信道。我个人的习惯是固定三组不同种子分别跑,看结论是否在三种信道条件下都成立,这样论文里的对比曲线更有说服力。

5.5 翻车现场五:误以为NOMA总优于OMA,不设公平性约束

现象:仿真发现NOMA总速率比OMA高,但其中一个用户的速率还不如OMA,甚至低了一半以上。如果只看总速率指标,这个结果看似不错,但实际系统里这个用户可能直接掉线。

原因:NOMA牺牲信道好用户的速率来换取信道差用户的接入和总吞吐量提升。如果不加任何公平性约束,功率分配算法会把大部分功率给信道差的用户,信道好的用户速率被压得很低。在总速率最大化的目标下,极端情况是信道差的用户占用几乎全部功率,信道好的用户速率接近零——这在工程上不可接受。

解决:在功率分配算法中加一个最低速率约束或最低功率占比约束。比如在FTPA中,把α_ftp最大值限制在0.5以内,确保信道好用户至少保留30%的功率。另外,我也养成了一个习惯:每次跑完仿真,不止看总吞吐量曲线,还要单独画两个用户的速率对比柱状图,用肉眼看公平性。如果发现信道好用户的速率低于OMA方案,就说明功率分配倾斜过度了,需要回调α_ftp或增加公平性约束项。

6. 仿真验证与进阶:蒙特卡洛统计、收敛性判别与可视化输出技巧

完成基础仿真后,下一步是验证算法的可信度。单个信道的误码率曲线意义有限,因为瑞利信道是随机的,一次信道的仿真结果可能偏差很大。我一般会跑蒙特卡洛统计:固定信道参数配置,随机生成1000次信道实现,每次记录系统吞吐量和各用户速率,最后统计均值和方差。这样得到的结果在论文或者课程设计里才有说服力。资源包的操作录像里演示了怎么用parfor并行计算加速——把for循环改成parfor,配合Parallel Computing Toolbox,1000次仿真从半小时压缩到几分钟,前提是每次循环调用独立、不共享内存变量。

收敛性判别方面,注水算法和任何迭代类算法都要检查收敛行为。我习惯在算法里记录每次迭代后的目标函数值(总速率),画成一条收敛曲线。正常情况是一条快速下降然后平稳的曲线,如果出现锯齿状震荡,说明步长太大或容差太紧。判断收敛的标准不是迭代次数达到上限,而是目标函数值的变化量连续几次小于容差。有一次我发现注水算法的收敛曲线在平稳后又突然跳变,追查后发现是某次迭代里信道增益向量被意外修改了——这个排查过程让我养成了一个习惯:迭代算法里所有不变的输入都加constant标志或复制存档,防止被意外覆盖。

可视化输出这块,除了常规的误码率曲线和吞吐量柱状图,我最推荐的是“用户-功率-速率”三维关系图:X轴是信道好用户的功率分配系数(0到1),Y轴是信道差用户的速率,Z轴是系统总吞吐量。这张图能直观显示功率分配策略的最优工作点——曲面的最高点就是最优功率分配,但往往不是总吞吐量的最大值,而是在公平性约束下的合理折中。用MATLAB的surf函数画这个曲面,每个点对应一组功率分配系数,做一次网格扫描就能覆盖整个参数空间。这个技巧是我在某次课题评审时被问“你怎么确定你的功率分配是最优的”之后才补上的——从那以后我每次做功率分配仿真都强制自己画一张三维功率-速率曲面图,既验证了算法的正确性,也方便看出参数边界在哪里。希望这个验证路径能帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询