简介:可重构智能表面辅助多用户多输入多输出系统的无源波束成形与信息传输设计,是一套基于MATLAB的完整仿真工程,面向无线通信与信号处理方向,尤其适合电子信息工程、计算机科学与技术、数学与应用数学等专业学生开展课程设计、期末大作业或毕业设计。工程采用参数化编程,关键参数均便于修改,代码注释详细,包含信道估计、稀疏贝叶斯学习、协同波束成形等核心模块,同时附有案例数据可直接运行,能帮助使用者快速掌握智能反射面辅助多天线系统的建模与优化流程。资源包共29个文件,主体为23个m脚本,并配有自动保存备份、程序输出文本、说明文档、许可证和数据集,压缩包约47KB,结构紧凑、分类清晰。目前已有50人学习下载,所附数据与脚本可在多个MATLAB版本中稳定运行,对于初学者而言是一份兼具教学意义与工程参考价值的实践资料。
1. 重构智能表面:为什么“被动反射”能让多用户MIMO凭空多出一维自由度
第一次在 matlab 里对比 RIS 辅助多用户 MIMO 的有源链路和无源链路时,我盯着仿真结果愣了很久:发射功率没加、天线没加,只是把一组可调反射单元从随机相位换成优化相位,用户 SINR 却能明显回升。反直觉的地方在于,反射面没有放大器、没有射频链路,只靠相位对齐就“凭空”改写了信道。原因在于传统 MIMO 把多径当作统计损耗,而 RIS 把反射路径变成可编程变量:整体信道从给定的 H 变成 H(θ)。搞懂这个设计动机,才能真正理解无源波束成形为什么能 work,也是后面读懂代码、修改参数、做课程设计或毕业设计时最值得花时间的底层门槛。
2. 从信道到目标函数:无源波束成形到底在优化什么
2.1 复合信道:一根天线传出去,却被“编辑”了两段路径
RIS 辅助系统里,信号从基站发出后至少走了两条路:一条是基站到用户的直射路径,另一条是基站到 RIS、再由 RIS 反射到用户的级联路径。RIS 本身不放大信号,它只调整每个反射单元的相位,让反射波在用户端尽量同相叠加。用下行链路的复基带模型来写,第 k 个用户的接收信号可以表示为:
y_k = (h_d,k + h_r,k^H · diag(φ) · G) · x + n_k
其中 G 是基站到 RIS 的 M×Nt 维信道矩阵,h_r,k 是 RIS 到第 k 个用户的 M 维信道向量,h_d,k 是基站到该用户的直射信道向量,φ 是 M 个反射单元的相移向量,且每个元素满足 |φ_i| = 1。这个式子里的 diag(φ) 是整个设计的核心:它把“信道”从固定参数变成了可优化变量。
在这个项目的 Generate_H.m 里,这三段信道通常分别生成再组合。常见的做法是让直射路径和 RIS 级联路径都走瑞利或莱斯衰落模型,再按路径损耗折算功率。示意代码大概长这样:
% Generate_H.m 中的信道生成思路(示意) Nt = 4; M = 64; K = 2; % 发射天线、RIS单元数、用户数 H_br = sqrt(0.5) * (randn(M, Nt) + 1i*randn(M, Nt)); % 基站->RIS H_ru = sqrt(0.5) * (randn(K, M) + 1i*randn(K, M)); % RIS->用户 H_d = sqrt(0.5) * (randn(K, Nt) + 1i*randn(K, Nt)); % 直射路径这里的 randn 是标准复高斯随机数,sqrt(0.5) 是为了让实部虚部各占一半功率,信道的总功率被归一化,后续所有 SNR 仿真都在这组归一化信道上进行。你可能会问:M 取 64,但用户只有 2 个,反射单元是不是浪费?实际上 M 越大,可调的相位自由度越多,多用户之间的干扰就越有可能被“错开”到不同的空间方向上,这正是 RIS 与普通天线阵列的差别:它不新增收发链路,却新增了几十个可编程相位自由度。
2.2 恒模约束是第一个真正的难点
无源波束成形的设计变量 φ 不是任意复向量,它必须满足恒模约束:每个反射单元只能改变相位,不能改变幅度,所以 |φ_i|² = 1。这个约束看起来简单,却是整个优化问题变难的根本原因。常见的目标函数有这么几类:
- 最大化加权和速率:max Σ w_k log2(1 + SINR_k)
- 最小化用户间均方误差:max 或 min 用户 MSE 的最大值
- 保证最差用户 SINR 不低于某个阈值
无论选哪一种,目标里都会出现 φ 的二次型,比如 |h_r,k^H diag(φ) G w_k|² 这一项。把它展开后,φ 是以 φφ^H 的形式出现在目标里的,而且所有用户通过同一个 φ 耦合在一起。因此你没法像单用户场景那样逐个相位贪心对齐,必须把所有用户的信道状态信息拿到一起做联合优化。
恒模约束还带来一个直接后果:单位模约束的可行集是非凸的。如果直接把 φ 丢给 CVX 求解,求解器根本不知道“每个元素模长为 1”该怎么处理。这也是为什么后面要做半定松弛:先把非凸约束“放松”成凸约束,再把丢掉的秩一约束用随机化方法补回来。
2.3 为什么简单相位对齐在多用户场景失效
单用户场景下,RIS 的最优相移几乎可以闭式得到:只要让每个反射单元的相位等于级联信道相位的负值,让所有反射信号在用户端同相叠加即可。但多用户场景不是“多个单用户问题的叠加”,因为同一个反射单元对用户 A 友好,可能正对用户 B 制造干扰。
举个例子,用户 A 处在某个反射波的增强方向,用户 B 恰好也在这个方向附近,如果你想同时增强 A、削弱 B,就需要在两个目标之间找平衡。这种平衡不是靠启发式能简单试出来的,所以才需要把“所有用户的等效信道矩阵”拼在一起,统一写成优化问题,再用 SDR、SAA、EM 这些工具去逼近最优解。理解这一点,再看项目里的 CVX_Optimal_SAA.m 和 CVX_Optimal_Simplify.m,就会明白为什么它们都在围绕一个大的复矩阵做处理,而不是直接调整某个相移值。
3. 算法骨架:半定松弛、EM 与贝叶斯估计各自管哪一段
3.1 半定松弛:把恒模约束拆开再补上秩一约束
半定松弛(SDR)是处理 RIS 相位优化最经典的思路。原始问题里,φ 以 φφ^H 的形式出现,所以可以先构造一个矩阵 W = φφ^H,把问题重写成关于 W 的线性目标。但 W 必须满足两个条件:一是对角元全为 1,对应每个反射单元的恒模约束;二是秩必须为 1,对应 W 确实能分解成单个向量乘自身共轭转置。
问题在于,秩一约束也是非凸的。SDR 的做法是把 rank(W) = 1 直接丢掉,只保留“W 是半正定矩阵、对角元为 1”这两个约束。丢掉的约束虽然让问题变松弛了,但换来的是一个凸的半定规划,可以用 CVX 高效求解。求解完再通过高斯随机化从 W 里提取候选的 φ 向量。核心建模片段如下:
% CVX_Optimal_Simplify.m / CVX_Optimal_SAA.m 的相似建模 cvx_begin sdp variable W(M+1, M+1) hermitian semidefinite maximize(real(trace(C * W))) subject to W(M+1, M+1) == 1; % 辅助变量块 diag(W(1:M, 1:M)) == 1; % 恒模约束 W >= 0; % 半正定 cvx_end phi = randomize_rank1(W, 500); % 高斯随机化取最优代码里 W 取 M+1 维,是因为目标中往往还有一个与发射功率或辅助变量相关的常数项,需要把它也放进矩阵里统一求解。diag(W(1:M,1:M)) == 1 对应的是“每个反射单元只转相位、不放大信号”。求解完成后,randomize_rank1 会生成大量随机候选向量,再从这些向量里挑出使目标函数最大且满足恒模约束的一个。随机化次数直接决定效果:次数太少,可能找不到足够好的秩一近似;次数太多,仿真时间明显变长,这是 SDR 方法绕不开的代价。
3.2 GAMP:把估计问题拆成“输入模型+输出模型”
前面说的是波束成形设计,而 RIS 系统里还有一个更实际的问题:接收端怎么在不知道完整信道和符号的情况下恢复数据。这就要用到广义近似消息传递(GAMP)。GAMP 把观测模型 y = A x + w 中的估计问题拆成两个部分:输入估计器和输出估计器。
输入估计器根据 x 的先验分布,比如星座点等概率分布,给出当前均值估计;输出估计器根据噪声模型,把残差转换成下一次迭代需要的伪观测。项目里的 EstimIn.m、EstimOut.m 是这两个接口的定义,AwgnEstimIn.m、CAwgnEstimIn.m、CAwgnEstimOut.m 是复高斯噪声假设下的具体实现。CA 开头的文件专门处理复数域,因为无线通信的基带信号天然是复信号,不能简单地把实部虚部分开处理。
迭代结构可以抽象成下面这段伪代码:
% GAMP 主循环结构(示意) for iter = 1 : maxIter r = y - A * xhat + corr; % 伪观测:残差加修正项 tau = tau_obs + tau_prior; % 噪声方差和后验方差之和 xhat = EstIn(r, tau, prior); % 输入估计器:从星座先验得到后验均值 corr = EstOut(r, xhat, tau); % 输出估计器:给出下一步修正 end这里 tau 的作用非常关键。tau 太小,估计器会把噪声误当成信号,结果符号估计剧烈抖动;tau 太大,估计器又偏向先验均值,无法分辨星座点。GAMP 的优势在于,它把高维线性估计的问题分解成多个标量估计的循环,计算量远小于直接做矩阵求逆,而且对大规模 M 天线和 RIS 单元数更友好。
3.3 EM 在什么时候真正介入
GAMP 本身假设噪声方差已知,但实际系统里这个参数也是未知的。EM 算法在这里的角色,就是在外层循环里不断更新这些未知超参数。典型做法是:每轮 GAMP 迭代结束后,根据当前的符号估计残差重新估计噪声方差;M 步的更新公式本质上是在做最大似然估计。
项目里的 MIMO_EM.m、MIMO_EMX.m、MIMO_EMS.m 是 EM 与不同变量组合的版本:有的重点估计符号,有的重点估计信道,有的同时估计信道和噪声参数。SparseScaEstim.m 处理稀疏信号的收缩估计,SBL_GAMP_S.m 和 SBL_GAMP_X.m 则是把稀疏贝叶斯学习与 GAMP 结合,用来估计稀疏信道或者稀疏的发射符号。它们之间的关系可以这样理解:SBL 负责“信道的结构”,GAMP 负责“信号恢复的迭代”,EM 负责“未知参数的自动更新”,三者共同组成一条完整的估计链路。
4. 常见问题与排查:CVX 求解失败、恒模约束失效、低信噪比翻车
4.1 CVX 报错,或者显示 Solved 之后结果比 MMSE 还差
现象:cvx_status 显示 Solved,但最终 BER 或 MSE 比传统 MMSE 还差,看起来“优化了个寂寞”。
原因:SDR 丢弃秩一约束后,松弛出的上界在 RIS 单元数较少或者用户空间相关较强时可能过松。另外,高斯随机化样本数不足也会导致无法找到足够好的可实现向量。
解决:先把随机化次数从 100 提到 500 到 1000,观察目标值是否还在上升。如果上升明显,说明是随机化样本不够;如果样本翻倍后目标值纹丝不动,再检查 CVX 建模里是否把恒模约束写成了连续约束,比如误用了 norm(W) 之类的写法。
4.2 恒模约束悄悄失效
现象:仿真没有报错,也能跑完,但最后输出的 φ 向量里某些元素模长明显不是 1,比如 0.87 或 1.13。
原因:很多代码在得到 SDR 解后,会对 φ 做归一化处理,但在计算目标函数时却沿用了未归一化的向量,导致“约束只在形式上成立,数值上不成立”。
解决:在每次迭代或随机化结束后强制加一行 phi = phi ./ abs(phi);,然后再代入目标函数。要注意,这种后处理只能修复数值误差,不能替代建模阶段对恒模约束的显式表达,两者必须同时做。
4.3 低信噪比下 EM 收敛到星座之外
现象:在 SNR 接近 0 dB 时,星座估计结果既不落在 QPSK 的四个点上,也不是合理的软概率分布,而是偏向某个非星座点。
原因:很可能是接收端的输入估计器用了高斯先验而不是离散星座先验。高斯先验会让估计值落在连续复平面上,EM 更新噪声方差后也不会自己修正到星座点,因为先验根本没有提供“信号来自有限星座”的信息。
解决:检查估计链路里是否真的调用了 Constell_Modulate.m 或 Constell_Mapping.m 来生成离散先验,并在输入端使用对应的估算器。如果只用了 AwgnEstimIn.m,那默认假设就是高斯输入,低信噪比下翻车几乎是必然的。
4.4 matlab 版本兼容性问题
现象:在 matlab 2014a 里运行正常,换到 matlab 2024b 后提示“未定义函数或变量”,或者 CVX 布局报错。
原因:CVX 本身对 matlab 版本敏感,旧版 CVX 的某些语法在新版 matlab 里已经被调整;另外,项目里的 .asv 文件是编辑器自动备份,如果误把它当成 .m 文件运行,也会出现奇怪的作用域问题。
解决:不要直接双击 .asv 文件,以 .m 脚本为入口;运行前先用 addpath 把项目目录和 CVX 目录加进搜索路径。如果条件允许,用 matlab 2019b 作为折中版本,它既能兼容大多数旧脚本,又不会太老。
4.5 随机种子不一致导致结果不可复现
现象:同一份代码、同一组参数,跑两次得到的 MIMO_result.txt 内容差别很大,却找不出代码改动。
原因:Generate_H.m 里用了 randn 生成信道,main.m 没有在开头固定随机种子。RIS 信道矩阵只要变一点点,后续的波束成形和估计结果就会完全不同,这不是 bug,而是随机种子的问题。
解决:在 main.m 的第一行加 rng(2024, 'twister');,并把生成的信道矩阵保存到 matlab.mat。之后每次对比实验结果,都从 matlab.mat 重新加载同一组信道,而不是重新生成。
5. 代码地图:六个核心脚本分别对应哪条设计链路
5.1 从 main.m 到 Generate_H.m:先造一个可复现信道
main.m 是整个项目的入口,它的任务不是实现算法,而是把所有模块串起来。通常它会先定义系统参数,比如发射天线数 Nt、RIS 单元数 M、用户数 K、调制阶数、SNR、信道衰落类型,然后调用 Generate_H.m 生成三段信道,再依次进入波束成形优化和信号估计流程。
参数化编程在这里的价值体现得最明显:你要做的不是改代码逻辑,而是改参数。比如想从 4 天线变 8 天线,只需要修改 Nt,后面的矩阵维度会自动适应;想看 RIS 单元数从 32 到 128 带来的增益变化,也只需要循环修改 M。Generate_H.m 的返回值决定了后续所有仿真结果,所以每次实验前先确认信道是否归一化、随机种子是否固定,否则后面的一切都是玄学。
5.2 发射端与接收端:Constell_Modulate 与星座映射
Constell_Modulate.m 负责把二进制比特流映射到复平面星座点,常见的是 QPSK 或 16QAM。Constell_Mapping.m 则承担反向任务:把我们常见的软信息映射回星座概率。这两者是估计链路中最重要的“先验来源”。GAMP 的输入估计器必须知道发送符号来自哪个星座、每种符号概率多大,否则只能把信号当连续高斯变量处理,性能会损失一大截。
5.3 优化链路:CVX_Text.m 与 CVX_Optimal_* 脚本
CVX_Optimal_Simplify.m 和 CVX_Optimal_SAA.m 都是 CVX 建模脚本。从命名方式看,前者倾向于直接求解单次信道实现下的最优相移,后者用样本均值近似(SAA)处理随机信道实现下的期望性能。两者的共同点是都围绕半定松弛建模,区别在于对“信道随机性”的处理粒度。
CVX_Text.m 更像是中间输出脚本,用于打印或保存 CVX 的状态信息和中间矩阵。调参时建议同时打开 CVX 的 log 输出和 MIMO_result.txt,确认每一次优化是“真收敛”还是“被 CVX 判定为不可行”。很多看起来像算法问题的情况,实际是建模约束前后不一致。
5.4 估计链路:SBL_GAMP_S、SBL_GAMP_X 与 MIMO_EM
SBL_GAMP_S.m 和 SBL_GAMP_X.m 负责稀疏贝叶斯学习和 GAMP 的结合,一个侧重信道相关的稀疏估计,一个侧重符号或状态变量的稀疏恢复。MIMO_EM.m、MIMO_EMX.m、MIMO_EMS.m 是 EM 框架下的不同变体,它们的共同点是都依赖 EstimIn.m、EstimOut.m 以及 CAwgnEstimIn.m 这类基础估计器。
如果只是想跑通流程,主线路径并不需要全部调用这些脚本。你可以先跑一段不经过 EM 的 GAMP,得到一个基线,再打开 EM 更新噪声方差,观察性能提升幅度。这样能清楚看到每一段代码到底贡献了多少增益,而不是把它们绑在一起然后只看最后的 BER 曲线。
5.5 结果回读:MIMO_result.txt 与 matlab.mat
MIMO_result.txt 保存的是历次实验的数值结果,比如不同 SNR 下的 MSE、BER、目标值或运行时间。matlab.mat 保存的是信道矩阵和中间变量,方便在多次实验之间复用同一组信道。下面这张表只列最核心的文件角色,避免一开始就在十几个文件里迷路:
| 文件 | 主要角色 |
|---|---|
| main.m | 参数定义与模块调度入口 |
| Generate_H.m | 生成直射和 RIS 级联信道 |
| CVX_Optimal_SAA.m | 样本平均近似下的相移优化 |
| SBL_GAMP_S.m | 稀疏贝叶斯 + GAMP 的信道/信号估计 |
| MIMO_EM.m | EM 超参数更新与符号恢复 |
| MIMO_result.txt | 实验结果的持久化输出 |
实际下载后你会发现,项目里另外还有 Xestim.m、SparseScaEstim.m、CAwgnEstimOut.m 等文件。它们不是装饰,而是上述主流程的“零件”。比如 Xestim.m 负责对某种未知量做整体估计,SparseScaEstim.m 为稀疏信号提供收缩型的估计器。把这个零件表放进 main.m 的调用顺序里,整条链路就清晰了。
6. 收敛与取舍:为什么设计动机比迭代次数更影响最终增益
看到 SDR 能给出稳定的上界后,很多人会以为“只要迭代次数足够多,最终性能就会逼近上界”。实际情况并不是这样,影响最终增益的主要是设计取舍。
第一个取舍是松弛带来的损失。半定松弛丢掉了秩一约束,高斯随机化能弥补一部分,但补不回来的那部分 gap 与信道结构强相关。RIS 单元数很小时,可用的相位自由度本来就少,松弛边界和可实现边界之间的差距会被放大;单元数增大后,随机化更容易找到近似秩一的解,gap 反而缩小。所以“单元数越多越好”不只是在信道增益层面成立,在算法可解性层面同样成立。
第二个取舍是 EM 的复杂度。EM 每轮都要更新噪声方差和其他超参数,但如果初始值离真实值太远,EM 会收敛到很差的局部极值。实际调试时,先固定一组合理初值,比如把噪声方差设成接收信号的 10%,能显著提高稳定性。这与迭代次数无关,而是初值设计的问题。
第三个取舍是离散相位带来的工程代价。仿真里可以把 φ 当作连续变量优化,但真实 RIS 硬件通常只能提供 1 位或 2 位的离散相移。把连续解量化到离散集后,性能会有明显下降。因此在做课程设计或毕设时,我会额外关心“连续设计到离散量化”之间的差距,而不是只看理想曲线。
从那以后,我每次跑 RIS 相关的 matlab 仿真,都会强制走一遍固定随机种子、确认恒模约束、查看 CVX 收敛状态、再用离散相位做一次量化对比,这样调试出来的结果才真正能说明算法本身的优劣,而不是噪声和偶然性的产物。希望帮到你。
本文还有配套的精品资源,点击获取