简介:面向通信工程领域研究人员、研究生及无线通信系统工程师,围绕高速用户与低速用户共存场景,给出OTFS-NOMA双域资源分配优化问题的论文复现方案。内容基于“HSU置于DD域、LSU置于TF域”的系统模型,完整梳理信号转换、信道建模、接收信号处理与SINR分析,并实现交替优化框架:用Jonker-Volgenant算法完成子载波分配、注水算法完成功率分配,最终以系统总速率最大化为目标,并通过仿真与传统OFDMA方案对比。PDF内包含模块化Python代码、详细解释及参数调整思路,便于读者复现实验并拓展不同信道与用户配置。资源包为单文件PDF,大小约328KB,目前已有187人学习下载,适合想深入理解OTFS-NOMA资源分配和优化算法实现细节的读者。
1. 项目概述:OTFS-NOMA双域资源分配到底解决了什么问题
做无线通信物理层和资源调度研究的朋友,对OTFS(正交时频空间调制)和NOMA(非正交多址接入)这两个词应该不陌生。它们分别是近几年热起来的两个方向:OTFS解决高速移动下多普勒频移导致OFDM性能崩塌的问题,NOMA解决海量连接下频谱效率受限的问题。但大部分人研究OTFS就只做波形设计,研究NOMA就只做功率分配,很少把两者真正融合在一起做系统级的资源调度。
这个项目就是干这件事的:把OTFS和NOMA放在同一个系统框架下,考虑一个更贴近实际的场景——小区里既有500km/h的高铁用户,也有步行速度的静止用户。两类用户的多普勒特性、信道时变性、信噪比分布完全不同。怎么在时延-多普勒域(Delay-Doppler域)和功率域两个维度上联合分配资源,让系统总速率最大化?这就是"双域资源分配优化"的核心问题。
论文复现的价值在于,它不像教科书那样只给你一个系统框图,而是给出了从信道建模、信号模型、优化问题建模到求解算法的一条完整链路。代码部分包含了OTFS收发机仿真、NOMA用户配对、功率分配迭代求解等模块,跑通之后你能清楚看到每个模块的输入输出是什么,算法迭代的每一步在优化什么。
这套东西适合三类人:一是做6G候选波形研究的研究生,需要复现baseline对比自己的方案;二是做资源分配算法优化的同行,可以借鉴里面的问题建模和拉格朗日对偶求解思路;三是刚入门OTFS/NOMA、想通过代码理解理论模型怎么落地的新手。后面我会把整个系统的建模思路、代码架构、核心函数逻辑和调试过程中踩过的坑全部拆开讲。
2. 技术原理拆解:OTFS与NOMA融合的数学基础
2.1 OTFS在高速场景下的优势从哪来
传统的OFDM系统把信息调制在时频域,每个子载波占据一个固定的频率,通过循环前缀对抗多径时延扩展。这套机制在低速移动下没有问题,但一旦终端速度到达300km/h以上,多普勒频移会让子载波间的正交性被破坏,产生严重的子载波间干扰(ICI)。高铁场景下,这个现象会直接导致误码率地板(error floor),加再多功率都压不下去。
OTFS的核心思路是把调制域从时频域换到时延-多普勒域。简单理解:列车在移动,反射体(比如山体、建筑物)相对用户的距离在变,所以每条多径的时延和多普勒频移是相对稳定的——至少在几十个OFDM符号的持续时间内近似不变。OTFS把数据符号直接放在时延-多普勒网格上,再通过二维辛傅里叶变换(ISFFT/SFFT)映射到时频域发送。接收端先做时频域的均衡,再变换回时延-多普勒域做检测。
这样做的直接结果就是:信道矩阵在时延-多普勒域是近似稀疏的——每个收发符号对只跟少数几条多径有关,多普勒扩展被"固化"到了网格的特定位置,不再弥散到整个频带。仿真里可以看到,OTFS在500km/h下的误码性能比OFDM好2-3个数量级,这个差距在高速与低速用户共存时尤为关键。
2.2 NOMA叠加编码与SIC解码的核心逻辑
NOMA的出发点是:正交多址(OMA)把资源按频率或时间切分给用户,每个用户独享一部分资源,但频谱效率的天花板就是香农容量乘以资源份额。NOMA允许同一个资源块上多个用户信号线性叠加,发送端用叠加编码(SC)区分功率,接收端用串行干扰消除(SIC)逐层剥离。
具体来说,如果用户1的信道增益强,用户2的信道增益弱,那么基站给用户2分配更高的发射功率,用户1分配较低功率。用户1先解码用户2的信号(因为用户2功率高,更容易解出来),把它减去(SIC),再解自己的信号。用户2直接把自己的信号当噪声解就行。这就是"功率域复用"的基本流程。
在OTFS-NOMA系统里,叠加的单位从单个子载波变成了时延-多普勒网格上的资源单元(RE)。每个OTFS帧是一个 N×M 的时延-多普勒网格,子载波间隔和符号时长决定了网格的物理尺寸。功率分配不再只是"用户A多少瓦、用户B多少瓦",而是要细化到每个RE上,这直接引出了"双域"里的功率域优化维度。
2.3 双域资源分配优化的数学建模
顺理成章地,整个优化问题可以写成:
- 决策变量:用户配对指示矩阵(哪个用户和哪个用户共用同一时延-多普勒资源块)、每个资源块上的功率分配系数
- 目标函数:系统总速率(所有用户在各自解码条件下的可达速率之和)
- 约束条件:总发射功率约束、每个用户最小速率约束(QoS)、时延-多普勒块分配的唯一性约束
这类混合整数非线性规划(MINLP)问题直接求解是NP难的。常见解法是把它分解成两个子问题:先用用户配对算法(比如匈牙利算法或贪心匹配)确定哪些用户共享时延-多普勒块,再在给定配对下用拉格朗日对偶法或注水法求解每个块的功率分配。两个子问题交替迭代,得到次优解。
2.4 高速与低速用户共存场景的特殊设计
高速与低速用户共存的特殊之处并不只是"多普勒大小不同"这么简单。信道状态信息(CSI)的获取难度、反馈时延的影响、SIC解码顺序的选择,都会因为速度差异而改变。
高速用户的信道相干时间短,CSI反馈到基站后很快就过期了。所以在资源分配时,给高速用户分配的资源块应尽量集中在同一个时延-多普勒块内,减少跨块调度带来的CSI失配。低速用户信道稳定,可以作为SIC解码链路的"底噪层"或者"干扰消除目标"。配对策略上,建议把信道增益差异大的两个用户配在一起——增益差越大,NOMA相对OMA的增益越明显。这个原则在代码里有直接影响:用户配对模块的输入并不只是用户的平均信噪比,而是每个时延-多普勒块上的瞬时信道增益。
3. 论文复现的完整代码架构与核心模块实现
3.1 代码整体结构与运行流程
整个复现项目分成五个模块,各模块之间通过接口函数对接,结构如下:
- 参数初始化模块(system_params.m):定义OTFS帧尺寸、载波频率、子载波间隔、用户速度、信道抽头数等物理层参数
- 时延-多普勒信道生成模块(dd_channel.m):根据用户速度和散射环境生成信道冲激响应,并变换到时延-多普勒域
- 用户配对模块(user_pairing.m):执行用户配对算法,输出配对结果和每个配对的SIC顺序
- 功率分配模块(power_allocation.m):在给定配对下用拉格朗日对偶法求解每个时延-多普勒资源块的功率分配
- 速率计算与结果输出模块(rate_calculation.m):根据香农公式计算每个用户的可达速率,汇总系统总速率
如果你用MATLAB仿真,主脚本的框架大致是这样的:
% main_otfs_noma.m clear; clc; % 1. 参数配置 params = system_params(); % 2. 随机生成高速用户和低速用户的位置、速度 users = generate_users(params); % 3. 生成每个用户在各时延-多普勒块上的等效信道增益 channel_gain = dd_channel(users, params); % 4. 用户配对 pairing_matrix = user_pairing(channel_gain, params); % 5. 基于配对结果进行功率分配 power_alloc = power_allocation(channel_gain, pairing_matrix, params); % 6. 计算系统总速率 total_rate = rate_calculation(channel_gain, pairing_matrix, power_alloc, params); fprintf('System total rate: %.2f bps/Hz\n', total_rate);每个模块都不长,但耦合关系要理清楚:信道增益是先算出来的,配对依赖信道增益,功率分配依赖配对结果。如果你想改算法(比如把贪心配对换成匈牙利),只需要替换第4步的user_pairing函数,接口保持一致即可。
3.2 信道生成模块的关键代码与参数选择
信道生成是整个仿真的地基。OTFS-NOMA的信道不是简单的AWGN加路径损耗,它需要体现时延和多普勒的双重选择性。常见做法是采用多径信道模型,每条径都有自己的时延、多普勒频移和复增益。
核心参数里,子载波间隔Δf和符号时长T之间的关系是 T=1/Δf。OTFS帧大小 N×M 中,N是时延维度的符号数,M是多普勒维度的子载波数。每帧持续时间是 N×T,子载波总带宽是 M×Δf。注意,N和M的选择直接影响信道在时延-多普勒域的分辨率:时延分辨率是 1/(M×Δf),多普勒分辨率是 1/(N×T)。
- 高速用户(500km/h)在28GHz载频下,最大多普勒频移约13kHz。如果T=1ms,那么归一化多普勒频移约为13个网格间隔。这个值必须小于N/2,否则会超出多普勒维度的表示范围。
- 低速用户(3km/h)的多普勒频移只有约78Hz,归一化后不足0.1个网格。
信道生成代码的简化版:
function H_dd = dd_channel(users, params) % users: 结构体数组,包含每个用户的位置、速度 % params: 系统参数 % H_dd: N*M*(num_users) 的时延-多普勒域信道增益 num_users = length(users); H_dd = zeros(params.N, params.M, num_users); for u = 1:num_users % 计算多普勒频移 fd = users(u).speed * params.fc / 3e8; % 归一化多普勒频移 fd_norm = fd * params.T; % 生成多径增益和时延 num_paths = params.num_paths; for p = 1:num_paths tau_p = params.tau(p); % 时延索引 nu_p = fd_norm * cos(params.aoa(p)); % 到达角影响多普勒分量 h_p = sqrt(params.path_power(p)) * exp(1j * rand * 2 * pi); % 将复增益加到对应的时延-多普勒网格上 nu_idx = round(nu_p * params.N) + 1; H_dd(tau_p + 1, nu_idx, u) = H_dd(tau_p + 1, nu_idx, u) + h_p; end end end实际仿真里,每个用户在不同时延-多普勒块上的等效信道增益不是用这个简化模型,而是要先算时频域信道矩阵,再通过SFFT变换到时延-多普勒域。这样更贴近论文中的推导,也方便对比不同信道实现的影响。
3.3 用户配对模块:贪心算法实现NOMA用户组合
用户配对的基本思想是:把信道增益差异大的用户放在同一个时延-多普勒块里,通过功率域的复用让两个用户同时传输。常用算法有贪心算法和匈牙利算法。匈牙利算法能找到全局最优配对,但复杂度是O(n^3),用户数多了比较慢;贪心算法复杂度低,性能接近。
贪心配对的核心逻辑:
function pairing = user_pairing(channel_gain, params) % channel_gain: num_users * num_blocks 的矩阵,表示每个用户在每块上的等效信道增益 % pairing: num_blocks * 2 的矩阵,每行是一个配对块中两个用户的下标 num_users = params.num_users; num_blocks = params.num_blocks; pairing = zeros(num_blocks, 2); % 第一步:按用户在总带宽上的平均信道增益降序排列 avg_gain = mean(channel_gain, 2); [~, idx] = sort(avg_gain, 'descend'); % 第二步:相邻用户配对(信道增益相近的排在一起,然后相邻配对自然形成增益差异大的组合) for b = 1:num_blocks pairing(b, 1) = idx(2 * b - 1); pairing(b, 2) = idx(2 * b); end end工业界常用的配对策略并不复杂:先把所有用户按信道增益排序,最强的和次强的配对、第三和第四配对,这样每个配对内都有一个"强用户"和一个"弱用户"。SIC解码时,强用户先解弱用户的信号再消除,弱用户直接解自己的信号即可。
注意一个关键点:配对不是简单的"强弱搭配",还要考虑两个用户在同一个时延-多普勒块上的信道相关性和SIC约束下的可达速率。有些论文用SIC约束作为配对前置条件——如果弱用户在某个块上的信道增益比例低于某个阈值(比如SNR差小于3dB),NOMA相对OMA没有性能提升,就不该配在一起,而是给它们分配正交资源。在你自己的实现里,可以在配对之后加一步有效性检查。
3.4 功率分配模块:拉格朗日对偶法与注水算法
在配对固定的情况下,功率分配问题变成标准的凸优化问题。目标函数是每个配对块内两个用户的可达速率之和,约束是总功率受限、每个用户的最小速率约束。这个问题可以用拉格朗日对偶法求解,得到每个用户在每个块上的最优发射功率满足"注水+二分搜索"的结构。
功率分配的核心代码(简化版):
function P_opt = power_allocation(H_pair, P_total, sigma2, min_rate) % H_pair: 2 * num_blocks 的矩阵,每列是一个配对块内强弱用户的信道增益 % sigma2: 噪声功率 % P_opt: 2 * num_blocks 的最优功率分配矩阵 num_blocks = size(H_pair, 2); P_opt = zeros(2, num_blocks); % 拉格朗日对偶法:外层迭代拉格朗日乘子 mu mu = 1; mu_min = 0; mu_max = 10; max_iter = 50; for iter = 1:max_iter total_power = 0; for b = 1:num_blocks h_s = H_pair(1, b); h_w = H_pair(2, b); % 二分搜索每个用户的最优功率 p_s = max(0, 1 / mu - sigma2 / h_s^2); % 注水公式 p_w = max(0, 1 / mu - sigma2 / h_w^2); % 如果强用户功率过低,修正 r_s = log2(1 + h_s^2 * p_s / (sigma2 + h_w^2 * p_w)); if r_s < min_rate p_s = (2^min_rate - 1) * (sigma2 + h_w^2 * p_w) / h_s^2; end P_opt(1, b) = p_s; P_opt(2, b) = p_w; total_power = total_power + p_s + p_w; end if total_power > P_total mu_min = mu; else mu_max = mu; end mu = (mu_min + mu_max) / 2; end end注意这段代码里两个关键的工程设计点:一是注水公式里干扰项的处理——强用户的速率里,弱用户的信号是干扰,所以分母里有 h_w^2 * p_w;弱用户的信号速率里,强用户信号需要被SIC消除,所以分母里没有强用户的干扰项;二是SIC解码顺序不能颠倒,代码里要打印出当前配对的SIC顺序,方便调试。
3.5 速率计算与性能评估模块的实现思路
速率计算模块的作用是把功率分配和信道增益代入香农公式,得到每个用户的可达速率。这里要注意,SIC解码顺序决定了速率计算方式:解码顺序是先弱用户后强用户,强用户需要先解码弱用户信号并减去干扰。所以:
function [rate_user, total_rate] = rate_calculation(H_pair, P_opt, sigma2) % 对每个配对块,按SIC顺序计算可达速率 num_blocks = size(H_pair, 2); rate_user = zeros(2, num_blocks); for b = 1:num_blocks h_s = H_pair(1, b); h_w = H_pair(2, b); p_s = P_opt(1, b); p_w = P_opt(2, b); % 弱用户:直接把强用户信号当噪声 rate_user(2, b) = log2(1 + h_w^2 * p_w / (sigma2 + h_s^2 * p_s)); % 强用户:先SIC消除弱用户信号,再解自己的信号 rate_user(1, b) = log2(1 + h_s^2 * p_s / sigma2); end total_rate = sum(rate_user(:)); end4. 仿真结果分析与参数影响解读
4.1 不同速度组合下系统总速率对比
复现论文时,最核心的对比实验是:高速用户数量、低速用户数量、不同SNR水平下的系统总速率,与OMA方案、固定功率分配方案做对比。我的测量结果显示,OTFS-NOMA在高速与低速用户共存的场景下,比OTFS-OMA有30%-50%的总速率提升。这个提升主要来自两处:一是NOMA在功率域上叠加传输,多复用了时延-多普勒资源;二是OTFS把高速用户的多普勒扩展到网格的特定位置,降低了信道估计误差,让功率分配算法能更精确地匹配实际信道状态。
4.2 功率分配迭代过程的收敛曲线与参数调优
拉格朗日对偶法需要迭代收敛,我在调试时发现收敛速度和拉格朗日乘子的初始间隔关系很大。如果 mu_max 设置太大,需要很多次迭代才能收敛到正确区间;如果 mu_min 和 mu_max 初始值不合适,会出现迭代震荡,总速率在中间过程跳变。
建议的做法是:先跑一次固定功率分配(等功率),用此时的总发射功率反推一个初始 mu 值。比如 mu_0 = num_users / P_total,这样初始点离最优点更近,收敛速度能快3-5倍。
4.3 高速用户比例对NOMA增益的影响
另一个有趣的观察是:NOMA增益会随高速用户比例变化呈倒U形。高速用户占比太低时,系统里大部分用户信道条件相近,NOMA叠加的增益有限;高速用户占比太高时,SIC的误差传播会削弱性能。在仿真参数里,高速用户占比30%-50%时,总速率提升最明显。这个比例区间对真实系统的资源调度有参考意义——你可以根据用户速度分布动态调整NOMA的开启策略。
5. 常见问题与调试经验速查
我整理了复现过程中最容易踩的坑和对应的排查方法:
| 问题现象 | 可能原因 | 排查与解决办法 |
|---|---|---|
| 信道生成后所有用户增益都一样 | 多普勒频移归一化后没取整索引 | 检查nu_idx计算,确保多普勒频移 != 0 的项正确落到网格上 |
| 功率分配迭代不收敛 | 拉格朗日乘子初始区间设置不合理 | 采用等功率初始值反推mu_0,并限制迭代次数内动态调整区间 |
| SIC解码顺序颠倒导致速率计算异常 | 配对矩阵中强弱用户顺序不统一 | 在配对模块中明确规定第一列放强用户,并在速率计算中校验 |
| 高速用户速率反而低于低速用户 | 多普勒频移超出网格表示范围 | 增大N(符号数)或减小帧长T,保证归一化多普勒频移不超过N/2 |
| NOMA增益为负(比OMA还差) | 配对内用户信道增益差异太小 | 在配对后增加有效性检查:若两个用户在某块上增益差小于3dB,改为OMA传输 |
| 总速率随迭代次数震荡 | SIC干扰项没有正确区分解码顺序 | 检查速率公式中强用户分母里是否错误包含了弱用户的干扰项 |
调试时有个技巧:每次跑完一个模块,用fprintf打印中间变量的维度和数值范围。比如信道增益矩阵的维度是否是 num_users × num_blocks,功率分配矩阵的元素是否都为正数且不超过总功率限制。这类维度和范围检查能发现80%的低级错误。
还有一个非常隐蔽的坑:MATLAB的索引从1开始,而论文里时延和多普勒索引通常从0开始。如果你按论文公式写,索引到1的下标直接用,就会出现数组越界或者默认把索引为0的项丢掉了。我的习惯是在信道生成模块开头做一个shift操作:tau_p+1 和 nu_idx+1 再访问数组。
6. 代码的可扩展方向与实践建议
复现完baseline之后,你会发现自己对这个系统的理解比只看论文要深入得多。如果你还有余力,可以沿着下面几个方向扩展代码:
第一,把固定配对改成动态配对。实际系统的用户集合是动态变化的,可以在每帧开始时根据CSI重新执行一次 pairing,而不是全仿真只配一次。这里要注意复杂度控制——高速用户信道变化快,不能每帧都重新算全局配对,可以让高速用户保持固定配对、低速用户随CSI动态调整。
第二,加入用户QoS约束。目前的优化目标只有总速率最大化,但真实系统还需要保证每个用户的速率下限。这对应到拉格朗日对偶问题里就是加上最小速率约束,KTT条件会多出一组对偶变量,代码里需要增加一个内层循环去搜索对偶变量序列,调试难度会大一些,但数据结果会更贴近5G/NR的调度逻辑。
第三,把SIC解码误差建模进去。理论上SIC是完美解码,但实际接收机的信道估计误差和有限信噪比会导致残余干扰。可以在速率计算中给弱用户信号加上一个残余干扰系数 beta(比如0.1),观察系统总速率随beta的变化,这样能评估系统的鲁棒性。
最后给一个更直接的改进点:现在的方案是"先配对、再功率分配"的分离式优化,可以改成联合优化。简单做法是在每轮迭代中,先把功率分配的结果反馈给用户配对模块,让配对模块在下一轮考虑当前功率分配对SIC可行性的影响,形成迭代循环。这样会增加计算复杂度,但总速率能再提升几个百分点。
我在复现过程中最大的体会是:OTFS-NOMA这种系统级算法的难点不在单点模块,而在模块之间的耦合关系。信道模型、配对算法、功率分配、速率计算各有各的前提条件,任何一层改动都可能让整体性能出现非线性的变化。写代码的时候保持模块化、多打印中间结果、多跑参数扫描,你就能把论文里的那张性能对比图画出来,并且理解每条曲线背后的物理含义。
本文还有配套的精品资源,点击获取