☰
RSMA速率拆分仿真:从code.zip到MMSE预编码与和速率优化
2026/10/12 21:52:39 网站建设 项目流程

简介:这份资源围绕RSMA(速率拆分多址)与MMSE基预编码在有限反馈通信系统中的结合展开,面向通信工程、无线通信方向的研究生与工程师,帮助理解速率拆分策略如何在信道状态信息受限条件下提升频谱效率与鲁棒性。压缩包共14个文件,全部为m脚本文件,整体约7KB,涵盖信道模型建立、有限反馈机制、MMSE预编码矩阵计算、速率拆分、信号收发与性能评估等仿真环节,可对照代码复现多用户MIMO场景下的完整流程。资源已有1129人学习下载,适合作为学术复现与工程验证的实践素材。读者可从中获得RSMA公共与私有信息划分的实现思路、MMSE预编码在有限反馈下的设计方法,以及吞吐量、误码率等指标的评估框架,为5G及未来网络中的干扰管理与预编码优化提供参考。

1. 从 code.zip 说起:RSMA 速率拆分仿真到底在算什么

如果你手头正好有一个叫code.zip的压缩包,解压后是一堆.m或.py文件,文件名里带着 RSMA、Rate Splitting、MMSE 这些词,那你大概率拿到的是一个多天线多用户通信系统的物理层仿真工程。RSMA(Rate-Splitting Multiple Access,速率拆分多址)这几年在通信圈热度一直不低,核心思路是把用户消息拆成公共部分和私有部分:公共部分所有用户共享,私有部分各自独立,接收端先解公共流再解私有流。这套机制的好处是它不像 SDMA 那样把干扰当敌人硬消,也不像 NOMA 那样完全靠功率域硬扛,而是在两者之间找了个更灵活的平衡点。

这个仿真工程要回答的问题很具体:在给定天线数、用户数、信道条件和功率约束下,RSMA 的和速率、公平性、误码表现到底比 SDMA 和 NOMA 好多少,以及预编码矩阵和速率拆分比例怎么设才能把性能拉满。适合谁看?通信专业做物理层仿真的研究生、基站算法工程师、以及想从 MATLAB 仿真切入 RSMA 的开发者。下面我按实际跑这套代码的顺序,把选型理由、参数设置、踩坑记录和验证方法一层层拆开讲。

2. RSMA 速率拆分的数学骨架与仿真选型

2.1 速率拆分到底拆的是什么

RSMA 的核心操作是把第 $k$ 个用户的原始消息 $W_k$ 拆成两部分:公共消息 $W_{c,k}$ 和私有消息 $W_{p,k}$。所有用户的公共消息合并成一个公共流 $s_c$,私有消息各自成流 $s_{p,k}$。发射信号写成:

$$ \mathbf{x} = \mathbf{p}c s_c + \sum{k=1}^{K} \mathbf{p}k s{p,k} $$

其中 $\mathbf{p}_c$ 是公共流的预编码向量,$\mathbf{p}_k$ 是第 $k$ 个用户私有流的预编码向量。接收端第 $k$ 个用户的信号是:

$$ y_k = \mathbf{h}_k^H \mathbf{x} + n_k $$

关键点在于接收顺序:先把所有私有流当干扰,解出公共流 $s_c$,然后用 SIC(串行干扰消除)把公共流减掉,再解自己的私有流。这个顺序决定了公共流的速率必须对所有用户都可达,也就是受最差信道用户约束。这就是速率拆分里“公共速率受限于最差用户”这个结论的来源。

仿真里要算的量包括:公共速率 $R_c$、各用户私有速率 $R_{p,k}$、总和速率 $R_{sum} = R_c + \sum R_{p,k}$,以及在不同拆分比例下的可达速率区域。拆分比例通常用一个向量 $\mathbf{c} = [c_1, c_2, \ldots, c_K]$ 表示,$c_k$ 是第 $k$ 个用户消息中分给公共部分的比例,满足 $0 \le c_k \le 1$。

2.2 为什么仿真里预编码选 MMSE 而不是 ZF

预编码的选择直接决定仿真结果好不好看。常见做法有三种:ZF(迫零)、MMSE(最小均方误差)、以及 MRT(最大比传输)。ZF 把用户间干扰完全消掉,但代价是噪声放大,在信道条件差或用户数接近天线数时性能急剧恶化。MRT 反过来,信号增益最大但干扰完全不管。MMSE 在两者之间做权衡,最小化接收端均方误差,公式是:

$$ \mathbf{P} = \mathbf{H}^H (\mathbf{H}\mathbf{H}^H + \alpha \mathbf{I})^{-1} $$

其中 $\alpha$ 是正则化因子,通常取噪声功率与信号功率之比。在 RSMA 仿真里,公共流和私有流可以用不同的预编码策略:公共流常用 MRT 或 MMSE 以覆盖所有用户,私有流常用 MMSE 或 ZF 以控制用户间干扰。我一般会在仿真里把三种预编码都跑一遍做对比,这样论文或报告里能说清楚选型理由。

提示:如果仿真里只跑了一种预编码就下结论说 RSMA 比 SDMA 好,审稿人或评审大概率会追问预编码是否公平对比。建议至少跑 MMSE 和 ZF 两组。

2.3 仿真工程的最小文件结构

拿到code.zip后,先别急着跑主脚本。我一般会先看目录结构,确认这几个核心文件在不在:

文件名(常见命名)作用缺失后果
main.m/main.py主入口,设参数、调子函数、画图无法整体运行
channel_gen.m信道生成,通常是瑞利或莱斯衰落结果不可复现
precoding.m预编码矩阵计算核心逻辑缺失
rate_split.m速率拆分与可达速率计算无法算和速率
sim_ber.m误码率仿真(可选)只有速率没有误码
plot_result.m结果可视化需要自己补画图

如果压缩包里缺了信道生成或预编码文件,那这个工程大概率是半成品,需要自己补。补的时候注意信道模型要和原代码里的接收端假设一致,否则速率算出来对不上。

3. 把 code.zip 跑起来:环境、参数与最小可复现流程

3.1 环境准备与依赖检查

MATLAB 路线的话,确认版本在 R2018b 以上,因为用到了pageinv或批量矩阵运算。Python 路线的话,核心依赖是numpy、scipy、matplotlib,如果用了 CVX 做凸优化还需要cvxpy。先跑一遍依赖检查:

# Python 路线:检查核心依赖版本 python -c "import numpy, scipy, matplotlib; print(numpy.__version__, scipy.__version__, matplotlib.__version__)"
% MATLAB 路线:检查工具箱 ver('comm') % 通信工具箱 ver('optim') % 优化工具箱,CVX 或 fmincon 需要

逻辑说明:先确认依赖齐全再跑主脚本,否则报错信息会混在业务逻辑错误里,排查起来很痛苦。参数说明:numpy版本建议 1.20 以上,scipy建议 1.6 以上,低版本在矩阵分解和优化接口上有差异。

3.2 核心参数怎么设:天线数、用户数、SNR 范围

仿真里最影响结果的几个参数:

参数符号常用取值影响
发射天线数$N_t$4 / 8 / 16越大空间自由度越高,但计算量涨
用户数$K$2 / 4 / 6接近 $N_t$ 时预编码性能下降明显
SNR 范围$\rho$0:5:30 dB覆盖低信噪比和高信噪比两段
公共速率拆分比例$c_k$0.1~0.9 扫描决定公共流和私有流的功率分配
正则化因子$\alpha$$1/\rho$ 或固定 0.01MMSE 预编码的关键参数
蒙特卡洛次数nIter1000 / 5000太少结果抖动大

我一般先设 $N_t=4$、$K=2$、SNR 从 0 到 30 dB、nIter=1000跑一个最小版本,确认曲线趋势对了再加大规模。拆分比例 $c_k$ 先固定 0.5,等主流程通了再做扫描。

3.3 最小可复现脚本:从信道生成到和速率计算

下面是一个精简版的 RSMA 速率计算流程,可以直接嵌到主脚本里:

import numpy as np def generate_channel(Nt, K, nIter): """生成瑞利衰落信道,返回 nIter 组 H,每组 shape (K, Nt)""" H = (np.random.randn(nIter, K, Nt) + 1j * np.random.randn(nIter, K, Nt)) / np.sqrt(2) return H def mmse_precoding(H, alpha): """MMSE 预编码,H shape (K, Nt),返回 P shape (Nt, K)""" K, Nt = H.shape HH = H.conj().T @ H # (Nt, Nt) P = H.conj().T @ np.linalg.inv(HH + alpha * np.eye(Nt)) # 列归一化,保证每列功率为 1 P = P / np.linalg.norm(P, axis=0, keepdims=True) return P def sum_rate_rsma(H, P, rho, c): """计算 RSMA 和速率,c 为拆分比例向量 shape (K,)""" K, Nt = H.shape # 公共流用所有私有流预编码的叠加近似 p_c = P.sum(axis=1, keepdims=True) p_c = p_c / np.linalg.norm(p_c) # 公共流可达速率:受最差用户约束 sinr_c = np.zeros(K) for k in range(K): hk = H[k, :].conj() sig = np.abs(hk @ p_c) ** 2 interf = sum(np.abs(hk @ P[:, j]) ** 2 for j in range(K)) sinr_c[k] = sig / (interf + 1 / rho) R_c = np.log2(1 + sinr_c.min()) # 私有流速率:SIC 后只剩其他私有流干扰 R_p = np.zeros(K) for k in range(K): hk = H[k, :].conj() sig = np.abs(hk @ P[:, k]) ** 2 interf = sum(np.abs(hk @ P[:, j]) ** 2 for j in range(K) if j != k) sinr_p = sig / (interf + 1 / rho) R_p[k] = np.log2(1 + sinr_p) return R_c + R_p.sum(), R_c, R_p # 最小跑通示例 Nt, K, nIter, rho = 4, 2, 1000, 10 ** (10 / 10) H_all = generate_channel(Nt, K, nIter) rates = [] for i in range(nIter): P = mmse_precoding(H_all[i], alpha=1/rho) r, _, _ = sum_rate_rsma(H_all[i], P, rho, c=np.array([0.5, 0.5])) rates.append(r) print(f"平均和速率: {np.mean(rates):.3f} bps/Hz")

逻辑说明:generate_channel生成复高斯信道,mmse_precoding算预编码矩阵并做列归一化,sum_rate_rsma先算公共流速率(取所有用户中最小的 SINR),再算私有流速率(SIC 后只留其他私有流干扰)。参数说明:alpha是 MMSE 正则化因子,通常取 $1/\rho$;rho是线性信噪比,不是 dB;c是拆分比例,这里固定 0.5,实际仿真里要扫描。跑通后你会看到一个平均和速率数值,接下来就是把它和 SDMA、NOMA 的基线对比。

注意:上面公共流预编码用的是私有流预编码叠加的近似,严格做法是单独优化公共流预编码。如果仿真里对精度要求高,需要把公共流预编码作为独立变量用凸优化求解。

4. 避坑与排查:RSMA 仿真里最容易翻车的五个地方

4.1 和速率曲线在高 SNR 段不涨反降

现象:SNR 超过 20 dB 后,和速率曲线变平甚至往下掉。原因:MMSE 正则化因子 $\alpha$ 固定没随 SNR 调整,高 SNR 时正则化过强,预编码退化成 MRT,干扰没压住。解决:把 $\alpha$ 设成 $1/\rho$ 或 $K/\rho$,让正则化随 SNR 自适应。如果代码里 $\alpha$ 是硬编码的常数,这是第一个要改的地方。

4.2 公共流速率算出来是负数

现象:R_c出现负值或 NaN。原因:公共流 SINR 计算时分母里的干扰功率算重了,或者预编码矩阵没做归一化导致功率爆炸。解决:检查p_c是否归一化,检查干扰项是否把公共流自身的功率也算进去了。公共流的 SINR 分母应该是所有私有流功率加噪声,不含公共流自己。

4.3 拆分比例扫描结果和理论不符

现象:$c_k$ 从 0 扫到 1,和速率单调变化,没有出现理论上的最优中间点。原因:公共流和私有流的功率分配没有跟着拆分比例联动。拆分比例变了,公共流和私有流的功率应该重新分配,如果代码里功率固定,那扫描 $c_k$ 只改了速率分配没改功率分配,结果自然不对。解决:把功率分配向量和拆分比例绑定,或者用联合优化。

4.4 蒙特卡洛次数太少导致曲线抖动

现象:每次跑出来的曲线不一样,差距能到 0.5 bps/Hz 以上。原因:nIter设了 100 或 200,统计平均不够。解决:至少 1000 次,论文级别建议 5000 次以上。如果计算太慢,先把天线数和用户数降下来跑通逻辑,再加大规模。

4.5 MATLAB 和 Python 结果对不上

现象:同一组参数,MATLAB 和 Python 跑出来的和速率差 0.2 以上。原因:随机数生成器不同、矩阵求逆的数值精度不同、或者预编码归一化方式不一致。解决:固定随机种子,两边用相同的信道矩阵(存成.mat或.npz文件互读),先确认预编码矩阵一致再比速率。数值精度问题一般差在 1e-6 量级,差 0.2 肯定是逻辑不一致。

5. 进阶技巧:用速率拆分比例扫描定位最优工作点

跑通基础流程后,最有价值的进阶操作是拆分比例扫描。具体做法:把 $c_k$ 从 0.1 到 0.9 按 0.1 步长扫描,每个点算一次和速率,画出和速率随 $c_k$ 变化的曲线。你会看到一条先升后降的曲线,峰值对应的 $c_k$ 就是当前信道条件下的最优拆分比例。这个峰值位置随 SNR 和用户数变化:低 SNR 时最优 $c_k$ 偏大(公共流更重要,因为公共流更鲁棒),高 SNR 时最优 $c_k$ 偏小(私有流能扛更多速率)。

我一般会做一张二维热力图:横轴 SNR,纵轴 $c_k$,颜色是和速率。这样一眼能看出不同 SNR 下最优拆分比例怎么漂移。代码上就是在原来循环外面再套一层 $c_k$ 的循环:

c_range = np.arange(0.1, 1.0, 0.1) snr_range = np.arange(0, 31, 5) heatmap = np.zeros((len(c_range), len(snr_range))) for i, c_val in enumerate(c_range): for j, snr_db in enumerate(snr_range): rho = 10 ** (snr_db / 10) rates = [] for k in range(nIter): P = mmse_precoding(H_all[k], alpha=1/rho) r, _, _ = sum_rate_rsma(H_all[k], P, rho, c=np.full(K, c_val)) rates.append(r) heatmap[i, j] = np.mean(rates) # heatmap 每列的最大值位置就是该 SNR 下的最优 c best_c = c_range[np.argmax(heatmap, axis=0)] print("各 SNR 下最优拆分比例:", best_c)

逻辑说明:外层扫 $c_k$,内层扫 SNR,每个格点做蒙特卡洛平均。参数说明:c_range步长 0.1 够用,要更精细可以 0.05;snr_range步长 5 dB 适合看趋势,精细分析可以 2 dB。跑完这张热力图,你对 RSMA 速率拆分的理解就从“知道公式”变成“知道什么时候该拆多少”。

验证方法上,我习惯做两件事:一是把 RSMA 的和速率和 SDMA、NOMA 画在同一张图上,确认 RSMA 在大多数 SNR 区间确实占优;二是检查 $c_k=0$ 时 RSMA 是否退化成 SDMA,$c_k=1$ 时是否退化成 NOMA,如果退化关系对不上,说明代码里的速率计算逻辑有问题。这两个边界检查是最便宜的后悔药,能在早期发现大部分实现错误。

最后说个血泪经验:RSMA 仿真里最容易被忽视的不是预编码也不是速率公式,而是功率归一化和噪声功率的一致性。我见过太多代码在预编码那一步忘了归一化,结果和速率虚高 3 dB 以上,曲线好看但全是假的。每次改完参数,先跑一遍 $K=1$ 的单用户场景,理论上这时候 RSMA 应该退化成点对点 MIMO 容量,对上了再跑多用户。这个习惯帮我省了至少两周的排查时间。希望帮到你。

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

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

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

立即咨询