资讯动态

SCMA稀疏码多址ML检测工程实战:从码本到球形译码

发布时间:2026/10/30 3:50:55 来源:尧图企业网站定制
简介SCMA-ML-master 是一份面向无线通信与机器学习交叉方向研究者的开源代码资源聚焦稀疏码多址SCMA技术与机器学习方法的结合。SCMA 作为码域非正交多用户接入方案可提升频谱效率与抗干扰能力而该资源尝试用机器学习模型优化其编码、检测与解码流程适合具备通信原理与 Python 编程基础的学习者研读。压缩包共 9 个文件以 .m 脚本为主体辅以 LICENSE、README.md 及一个 .s 文件整体约 6KB体量轻巧但结构完整涵盖码本生成、SCMA 编解码、ML 检测及四进制与二进制映射转换等核心模块。目前已有 227 人学习下载。读者可从中获取一套可运行的 SCMA 仿真链路理解码本设计、信号检测与机器学习融合的实现思路并借助 README 快速上手为后续模型调优与性能评估提供参考。1. 从一份 SCMA 稀疏码多址的 ML 检测工程说起如果你手上正好有一份名为SCMA-ML-master的工程目录里面散落着SCMA、ML、morehsq这类命名第一反应大概率是这是一套用最大似然Maximum Likelihood, ML做 SCMA 多用户检测的 MATLAB 实现。SCMA 是稀疏码多址Sparse Code Multiple Access5G 非正交多址里被讨论得最多的一支ML 检测则是它性能上界的那把尺子。问题在于ML 检测的复杂度随用户数和码本维度指数爆炸工程里真正能跑起来的往往是加了球形译码、码本剪枝或者近似消息传递的变体。这份工程的价值不在于它多完美而在于它把「码本 → 信道 → 接收信号 → ML 判决」这条链路完整摆出来了你能顺着它把 SCMA 的检测逻辑从公式落到矩阵。这篇笔记就按这个思路走先讲清 SCMA 和 ML 检测到底在算什么再拆工程里最可能的结构和关键参数最后把踩过的坑和验证方法交给你。适合正在做多址接入仿真、想复现 SCMA 链路、或者被 ML 复杂度卡住的人。2. SCMA 码本与 ML 检测先把数学对象对齐2.1 SCMA 到底稀疏在哪码本怎么定义SCMA 的核心是把比特流映射到一个多维稀疏码字上。假设系统有K个资源单元RE比如子载波和J个用户层每个用户占用N个非零资源通常N K这就是「稀疏」的来源。过载因子λ J / K典型配置是K 4, J 6, N 2过载 150%。每个用户有一本码本CB_j大小是K × MM是码本码字数等于2^{log2(M)}常见M 4或8。码本不是随便填的它由母星座图经过相位旋转、资源映射和功率分配生成。工程里你看到的SCMA_codebook或类似变量本质就是一个三维数组[K, M, J]。理解这一点很关键后面 ML 检测的搜索空间就是所有用户码字组合的笛卡尔积规模是M^J。M 4, J 6时是 4096 种组合还能暴力搜M 8, J 6就是 262144 种单次符号检测就够呛这就是为什么工程里必须做剪枝或近似。2.2 ML 检测的目标函数与复杂度来源接收信号模型写成y Σ_j diag(h_j) x_j nh_j是用户j的信道向量x_j是从码本里选出的码字n是噪声。ML 检测就是在所有可能的码字组合上最小化欧氏距离[x_1*, ..., x_J*] argmin || y - Σ_j diag(h_j) x_j ||^2这个式子看着简单复杂度却是O(M^J)。工程里SCMA-ML这个名字直接点明了它用的是穷举 ML所以它的定位是「性能基准」而不是「可部署接收机」。你在读这份工程时要带着这个判断它的检测结果可以用来验证 MP消息传递或 MPA消息传递算法近似得够不够好而不是拿去对标实时吞吐。2.3 为什么工程里常见 ML 而不是直接上 MPAMPA 是 SCMA 的标准低复杂度检测基于因子图做迭代消息传递复杂度随J和M线性增长。但 MPA 有几个工程上绕不开的问题迭代次数、阻尼因子、消息初始化都会影响收敛调不好就出现误码平台。ML 没有这些玄学参数它给的是硬下界。所以一份叫SCMA-ML的工程通常承担两个角色一是给 MPA 提供 BER 对比曲线二是用来验证码本设计本身好不好——如果 ML 都跑不出低误码那码本或信道模型一定有问题。这也是我建议你先跑通 ML 再碰 MPA 的原因先有一个可信的参照系。2.4 从目录名推断工程结构SCMA-ML-master这种命名大概率是 GitHub 上直接下载的 zip 解压结果morehsq可能是作者 ID 或分支标记。常见目录会包含码本生成脚本、主仿真脚本、ML 检测函数、BER 统计脚本、可能的 MPA 对比脚本。你拿到手第一件事不是跑主脚本而是先确认码本维度和用户数配置因为后面所有矩阵索引都依赖它。如果码本是硬编码的.mat文件先load出来看size确认是[K, M, J]还是[J, M, K]这个顺序搞反了检测结果会全错但又不报错属于最隐蔽的翻车点。3. 把 SCMA-ML 工程在本地跑通的最小路径3.1 环境准备与目录确认MATLAB 版本建议 R2018b 以上因为工程里可能用到argmin类的隐式扩展或者pagemtimes。不需要额外工具箱SCMA 仿真基本是纯矩阵运算。如果你只有 Python也能复现后面会给 NumPy 版本。先确认目录结构# 进入工程根目录列出所有 .m 和 .mat 文件 cd SCMA-ML-master find . -maxdepth 2 -name *.m -o -name *.mat | sort这一步的目的是找到主入口和码本文件。常见主脚本叫main.m、SCMA_sim.m或test_ML.m。如果目录里同时有ML和MPA两个文件夹先只跑 ML别混着调。3.2 码本加载与维度校验% 加载码本假设文件名为 codebook.mat load(codebook.mat); % 变量名可能是 CB 或 codebook K 4; J 6; M 4; % 典型配置按实际改 % 校验维度兼容两种常见存储顺序 if size(CB,1) K size(CB,2) M size(CB,3) J fprintf(码本维度 [K,M,J] [%d,%d,%d] 正确\n, K, M, J); elseif size(CB,1) J size(CB,2) M size(CB,3) K CB permute(CB, [3 2 1]); % 转成 [K,M,J] fprintf(已从 [J,M,K] 转为 [K,M,J]\n); else error(码本维度不符合预期检查 size(CB)); end维度校验是整条链路的地基。K是资源数M是每用户码字数J是用户层数。permute那行是后悔药很多下载来的工程存储顺序和你的索引习惯不一致不转就等着 BER 恒为 0.5。3.3 生成接收信号与穷举 ML 检测N 2; % 每用户非零资源数 numSym 1000; % 仿真符号数 bitsPerSym log2(M); % 每用户每符号比特数 BER zeros(1, length(1:2:16)); snrIdx 1; for EbN0 1:2:16 errCount 0; totalBits 0; for s 1:numSym % 随机比特 - 每用户码字索引 txIdx randi(M, 1, J); % 叠加所有用户码字h 为信道此处设全 1可换成瑞利 y zeros(K, 1); for j 1:J y y CB(:, txIdx(j), j); end % 加高斯噪声 noiseVar 10^(-EbN0/10); y y sqrt(noiseVar/2) * (randn(K,1) 1i*randn(K,1)); % 穷举 ML遍历 M^J 种组合 bestDist inf; bestIdx zeros(1, J); for c 0:M^J-1 idx dec2base(c, M, J) - 0 1; % 组合索引 yhat zeros(K, 1); for j 1:J yhat yhat CB(:, idx(j), j); end d norm(y - yhat)^2; if d bestDist bestDist d; bestIdx idx; end end % 统计误比特 errCount errCount sum(txIdx ~ bestIdx); totalBits totalBits J * bitsPerSym; end BER(snrIdx) errCount / totalBits; snrIdx snrIdx 1; end semilogy(1:2:16, BER, o-); grid on; xlabel(Eb/N0 (dB)); ylabel(BER); title(SCMA ML 检测 BER);这段代码是整个工程的心脏。dec2base把十进制组合号转成M进制索引向量实现M^J穷举。noiseVar按Eb/N0换算注意这里用的是符号级噪声方差如果你要精确到比特级Eb/N0还要除以bitsPerSym和码率。bestDist用平方欧氏距离避免开根号。参数上numSym至少 1000 才能让高 SNR 点的 BER 有统计意义低 SNR 点 500 就够。J和M一改内层循环次数指数变化M8, J6时单符号 26 万次距离计算MATLAB 里跑 1000 符号会非常慢这时候要么减numSym要么上向量化。3.4 向量化加速把 M^J 循环压成矩阵% 预生成所有组合索引矩阵大小 M^J x J allIdx zeros(M^J, J); for c 0:M^J-1 allIdx(c1, :) dec2base(c, M, J) - 0 1; end % 预生成所有组合的叠加码字K x M^J allCodewords zeros(K, M^J); for c 1:M^J for j 1:J allCodewords(:, c) allCodewords(:, c) CB(:, allIdx(c,j), j); end end % 检测时只需一次矩阵运算 dist sum(abs(y - allCodewords).^2, 1); [~, bestC] min(dist); bestIdx allIdx(bestC, :);预生成allCodewords是拿空间换时间K × M^J的复数矩阵K4, M^J4096时约 256KB完全可接受。M8, J6时是 4 × 262144约 16MB 复数也能扛。这样每个符号的检测从M^J次循环降到一次sum速度提升几十倍。注意allCodewords只和码本有关和y无关所以放在 SNR 循环外面生成一次即可。3.5 Python 复现的关键差异import numpy as np K, J, M 4, 6, 4 # CB 形状 [K, M, J] CB np.load(codebook.npy) # 或从 mat 转 all_idx np.array(np.unravel_index(np.arange(M**J), [M]*J)).T # M^J x J all_cw np.zeros((K, M**J), dtypecomplex) for c in range(M**J): for j in range(J): all_cw[:, c] CB[:, all_idx[c, j], j] # 检测 dist np.sum(np.abs(y[:, None] - all_cw)**2, axis0) best np.argmin(dist)Python 里np.unravel_index直接生成所有组合比 MATLAB 的dec2base更干净。注意y[:, None]做广播all_cw是K × M^J相减后是K × M^J沿 axis0 求和得到每个组合的距离。MATLAB 用户转 Python 最容易错的是索引从 0 开始all_idx直接就是 0-based喂给CB时不用加 1。4. 参数怎么设码本、信道与 SNR 的实操边界4.1 码本维度与过载因子的取舍参数典型值影响建议K4资源单元数固定 4便于对比文献J6用户层数过载 150%ML 复杂度 4^6M4 或 8码字数先 4 跑通再 8 看复杂度N2非零资源稀疏度影响 MPA 收敛λ1.5J/K越高越非正交干扰越大M4时M^J4096MATLAB 单符号检测约几毫秒M8时M^J262144单符号可能上百毫秒。如果你只是验证码本M4足够如果要发论文对比M8才有说服力但必须向量化。N2是 SCMA 稀疏性的体现N越大码字越密MPA 的因子图越复杂但 ML 不受N影响只受M^J影响。4.2 信道模型AWGN 与瑞利的切换上面代码用的是全 1 信道等价 AWGN。要切瑞利把y y CB(:, txIdx(j), j)改成y y h(j) * CB(:, txIdx(j), j)h是每用户独立的复高斯。注意 ML 检测端必须知道h否则距离计算里的yhat也要乘h。工程里常见错误是发端加了h收端yhat没乘结果 BER 曲线在高 SNR 下不降查半天以为是码本问题。瑞利信道下 ML 检测的allCodewords不能预生成因为每个符号的h不同yhat要实时算向量化时把h广播进去。4.3 SNR 范围与统计样本数Eb/N0从 1 到 16 dB步进 2是 SCMA 文献的常见画法。低 SNR 段 BER 接近 0.5样本可以少高 SNR 段 BER 到 1e-4 以下需要至少 1e6 比特才能看到稳定点。numSym1000, J6, bitsPerSym2时总比特 12000高 SNR 下可能一个错误都没有BER 显示为 0semilogy会断线。解决办法是设一个错误数下限比如累计 100 个错误就停或者直接加大numSym到 10000。我一般先小样本跑通流程再大样本出曲线别一上来就 10000 符号干等。4.4 码本功率归一化码本每个码字的功率应该归一化到 1否则不同用户的发射功率不一致ML 距离会被功率大的用户主导。检查方法mean(sum(abs(CB(:,:,j)).^2, 1))应该接近 1。如果工程里码本没归一化检测前先除。这个坑很隐蔽因为 BER 曲线形状可能看起来正常只是整体偏移你不对比就发现不了。5. 避坑与排查SCMA-ML 工程里最容易翻车的五件事5.1 现象BER 恒为 0.5完全不随 SNR 下降原因码本维度顺序错了[J,M,K]当成[K,M,J]用导致CB(:, idx, j)取出的码字完全错位。或者dec2base的索引起点搞错MATLAB 里dec2base(0,M,J)返回全 0加 1 后才是 1-based漏加就全取到第 0 列报错或取错。解决先跑size(CB)校验再用固定txIdx手动算一次y和yhat确认距离最小的是真实索引。5.2 现象低 SNR 正常高 SNR 出现误码平台原因噪声方差换算错了。Eb/N0转噪声方差时如果忘了除以bitsPerSym或码率高 SNR 段实际噪声比设定值大BER 降不下去。另一个可能是码本未归一化用户间功率不平衡。解决打印实际noiseVar和理论值对比检查码本功率。5.3 现象MATLAB 报「索引超出矩阵维度」原因M^J组合遍历时dec2base返回的是字符数组M8时字符7减0得 7加 1 得 8没问题但M10时dec2base会用字母减0就错了。SCMA 里M一般不超过 8但如果你改到 16必须换base2dec或手动进制转换。解决用ndgrid或allcomb生成索引别依赖dec2base。5.4 现象向量化后结果和循环版不一致原因allCodewords预生成时用了旧的CB后面改了码本没重新生成。或者allIdx的行列顺序和allCodewords的列顺序没对齐。解决把预生成封装成函数每次改码本强制重算用一个小规模M2, J2手动验证两种实现结果一致。5.5 现象跑M8, J6时内存爆掉或卡死原因allCodewords是K × M^JM8, J6时 4 × 262144 复数约 16MB不该爆但如果K8或J8就是 8 × 16M直接上 GB。解决分块计算距离每次只生成一部分组合或者改用球形译码剪枝。ML 穷举本来就不适合大J工程里如果硬要跑先降numSym到 100 看趋势。6. 进阶用球形译码把 ML 复杂度压下来穷举 ML 的M^J在J6, M8时已经吃力再大就没法用。球形译码Sphere Decoding, SD的思路是只搜索落在以接收信号y为球心、半径r的球内的码字组合把复杂度从指数降到接近多项式。在 SCMA 里做 SD关键是把叠加码字写成树结构第j层对应第j个用户的码字选择每层累加部分欧氏距离一旦部分距离超过r^2就剪枝。实现上先设一个初始半径比如用迫零或最小均方误差检测的结果距离作为r。然后按用户逐层展开每层遍历M个码字累加||y_partial - sum||^2超半径就回退。半径可以动态收紧找到一个可行解后把r更新为当前最小距离后续剪枝更狠。这个策略在M8, J6时通常能把搜索节点数从 26 万降到几千速度提升两个数量级。验证 SD 是否正确最直接的方法是和穷举 ML 对比 BER在相同码本、相同信道下SD 的 BER 应该和 ML 几乎重合如果有偏差说明剪枝把最优解剪掉了半径设太小。我一般先用M4, J4这种小规模穷举和 SD 各跑一遍确认曲线重合再上大配置。另一个技巧是记录平均搜索节点数如果它随 SNR 升高而下降说明剪枝在起作用如果一直等于M^J说明半径没收紧SD 退化成穷举。最后说个习惯每次改码本或信道模型先跑M2, J2的最小用例手动算一遍期望结果确认链路无误再放大。SCMA 的矩阵索引多错一处不报错但结果全错这个最小用例就是你的后悔药。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑