ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Matlab脚本驱动Simulink QPSK仿真:从模型搭建到星座图输出

Matlab脚本驱动Simulink QPSK仿真:从模型搭建到星座图输出 简介面向本硕博通信专业学生与科研人员的正交相移键控QPSK调制解调仿真学习资料重点解决如何在MATLAB/Simulink环境中搭建完整仿真链路输出基带数据并绘制星座图同时兼顾算法验证、课程设计与数字通信原理教学覆盖从本科高年级到研究生阶段的实践需求。压缩包共七个文件包含仿真模型、主程序脚本、数据结果文件、文本说明、操作视频以及FPGA与MATLAB联调参考整体约443KB以轻量方式呈现从信号产生到结果分析的完整流程文件间分工明确便于按需查阅。已有2905人学习下载工程在MATLAB 2021a及以上版本中测试通过运行时应执行主脚本而非子函数并确保当前文件夹指向工程路径这些细节可显著减少调试时间。配合操作视频读者可按步骤跟学逐步观察不同处理阶段基带数据的变化理解符号映射与解调判决的对应关系为后续算法优化或硬件实现打下基础。 做QPSK仿真的时候很多人习惯直接打开Simulink拖模块、点运行然后盯着Scope看波形。这当然能跑通但一旦需要批量测试不同信噪比、对比多种参数、或者要把仿真结果交给后端处理纯手工操作就显得很低效。我自己的做法是用Matlab脚本去驱动Simulink模型让仿真变成可复现、可批处理、能自动输出基带数据和星座图的一套流程。这篇文章就把这套流程完整拆开讲清楚包含从模型搭建到脚本调用的关键细节也会聊聊我录操作视频时踩过的坑适合正在做通信课程设计、无线通信算法验证或者刚接触Simulink仿真想少走弯路的朋友。1. 为什么要用Matlab脚本调用Simulink做QPSK仿真1.1 QPSK仿真场景拆解QPSKQuadrature Phase Shift Keying正交相移键控是数字通信里最基础的调制方式之一它把数据映射到四个相位点每个符号携带2比特信息。做仿真时最核心的诉求是验证发射端到接收端整个链路在加性高斯白噪声AWGN信道下的误码性能同时观察星座图的汇聚情况。如果只是单次验证直接拖模型就够了。但实际工作中我经常要做的是扫描不同Eb/N0每比特能量与噪声功率谱密度比画误码率曲线或者调整滚降系数、过采样倍数看星座图的变化。这时候用脚本循环调用Simulink模型每次修改参数、跑仿真、取数据效率能提升一个量级。而且脚本跑完还能直接把误码率曲线、星座图、眼图全部自动出图不需要一个个手动截屏。1.2 脚本驱动模型比纯手点Simulink的优势脚本驱动的最大优势是可重复性和可批量性。手点模型时参数改了之后容易忘记改回来下次运行结果就对不上了。但脚本里所有参数都用变量控制模型里的参数块直接引用工作区变量这样每次运行前脚本把变量赋值好模型就自动用新值跑整个过程不会有人为遗漏。另外Simulink的仿真结果默认存在仿真数据检查器或者输出端口里纯手工模式下想拿到精确的基带数据序列做离线分析比较麻烦。通过脚本调用sim函数可以在仿真结束后直接从输出对象中提取波形数据、离散状态量、以及模型中To Workspace模块保存的变量然后统一处理。这一步是后面输出基带数据和星座图的基础。2. 前期准备环境与模型搭建2.1 环境版本与工具包我用的环境是Matlab R2021b自带的Simulink需要安装Communications Toolbox通信工具箱和DSP System Toolbox如果要用到一些信号处理模块。版本不同模块库的位置略有差异但核心模块名称基本一致。如果电脑里还没有通信工具箱直接在App界面或者命令行用ver查看已装工具箱缺少的话去附加功能里补装。这里多说一句很多人喜欢用破解版但如果你涉及毕业设计或者公司项目建议还是用正版授权避免后续生成代码或者导出模型时出现授权问题。我自己的机器上装的是学校提供的正版许可用起来比较省心。2.2 在Simulink中搭建QPSK调制解调模型模型结构不复杂但有几个细节需要提前想清楚。我推荐把模型拆成三部分发射端、信道、接收端这样调试时定位问题更快。发射端包含Bernoulli Binary Generator伯努利二进制序列发生器或Random Integer Generator用于生成0/1比特流如果直接用QPSK调制模块可以用Rectangular QPSK Modulator Baseband矩形QPSK基带调制器它接收整数符号输出复数基带信号为了模拟带通信道通常要加一个脉冲成形滤波器如Raised Cosine Transmit Filter把基带信号扩展到一定带宽。信道部分最简单就是AWGN Channel模块设置信噪比参数。接收端包含匹配滤波器Raised Cosine Receive Filter与发射端成形滤波器匹配载波同步可选初学可以先不加QPSK解调器误码率统计模块Error Rate Calculation。模型搭建完成后要确保从发射端到接收端的采样率、帧长度完全一致。一个常见错误是发射端和接收端的滤波器参数不一致导致信号失配星座图收敛不到理想位置。2.3 模型参数设置关键点Simulink里的参数绝对不能全用默认值。以下几个点必须由脚本或工作区变量控制采样率我习惯定义Fs 100e3表示100kHz采样率符号速率Rs 10e3即每秒10000个符号这样每个符号对应10个采样点每符号采样数sps Fs / Rs 10滚降系数beta 0.35这是根升余弦滤波器的典型值帧长numBits 100000用于统计误码率太短的话高信噪比下误码率可能测不准。在发射端和接收端的滤波器中把“Filter span in symbols”滤波器符号跨度设置成10到12滚降系数设为beta采样倍数设为2*sps注意如果是根升余弦匹配滤波需在参数里指定“滤波器的采样倍数”等于接收端的采样率/符号率。把模型的步长设置为固定步长步长取1/Fs保证每个采样点都被计算到。在求解器设置中选“Fixed-step”然后“ode4”或“discrete”。如果不设置固定步长仿真步长可能会自适应变化导致To Workspace模块采到的数据不均匀星座图会变形。3. 用Matlab脚本调用Simulink模型3.1 用sim函数驱动模型仿真模型搭好之后脚本调用非常简单。假设模型文件名为qpsk_sim.slx在Matlab命令行或脚本中这样写% 设置参数 Fs 100e3; % 采样率 Rs 10e3; % 符号速率 sps Fs / Rs; % 每符号采样数 beta 0.35; % 滚降系数 EbNo 10; % Eb/N0单位dB numBits 100000; % 调用sim函数 simOut sim(qpsk_sim, StopTime, num2str(numBits / Rs));这里有两个关键点。第一个是sim函数第二个参数用了StopTime指定仿真时长。numBits / Rs是总符号时长例如100000 / 10000 10秒。第二个是模型内部的参数需要引用工作区变量也就是刚才说的模型中的滤波器的滚降系数、增益、信噪比等参数都要直接填变量名不要填数字。否则脚本改了工作区变量模型里面还是旧值。3.2 配置输入数据与工作空间变量有时你需要自己生成特定的发送序列而不是用随机数发生器。这时可以把伯努利发生器换成“From Workspace”模块数据由脚本生成。例如bits randi([0 1], numBits, 1); sendBits.time (0:numBits-1) / Rs; % 时间戳 sendBits.signals.values bits; sendBits.signals.dimensions 1;然后在模型中用From Workspace模块参数设为sendBits。注意数据的时间向量必须按采样间隔递增且最后一个时间点不小于仿真停止时间。如果用符号速率作为时间间隔则From Workspace模块需要设置采样时间确保每个符号时刻读一个值。我个人的习惯是直接让仿真生成随机比特同时用“To Workspace”模块把仿真后的接收比特和原始比特都导出再在脚本里对比计算误码率这样模型结构更简洁。3.3 采集基带数据与星座图数据要采集基带数据和星座图数据需要在Simulink模型里的关键节点放置To Workspace模块。至少需要采集三处信号发射端的调制输出复数基带信号即经过脉冲成形之前的符号序列还是叠加成形后的信号如果想看星座图通常看接收端的均衡/匹配滤波后、判决前的信号接收端的匹配滤波后采样信号即每个符号时间点上抽取的复数信号接收端解调后的比特序列。设三个To Workspace模块变量名分别设为txBaseband、rxSymbols、rxBits采样时间设置成-1表示继承信号的采样率或手动设为符号周期。保存格式选择“Timeseries”或“Array”我建议用“Array”因为后续画星座图直接用矩阵操作更方便。仿真结束后提取数据txIq simOut.txBaseband; % 发射端基带复数信号 rxSym simOut.rxSymbols; % 接收端匹配滤波后采样值 rxBitSeq simOut.rxBits; % 接收端解调比特需要注意simOut是一个Simulink.SimulationOutput对象可以直接用点号访问模型中的To Workspace变量。如果保存成Array则txIq是一个N维数组第一维是采样点第二维如果是复数Matlab会把实部和虚部合成复数。如果输出是二维实数矩阵两列需要转换成复数例如rxSymComplex rxSym(:,1) 1i*rxSym(:,2);4. 星座图绘制与基带数据输出4.1 从仿真结果中提取I/Q分量拿到复数符号序列后I/Q分量分别是实部和虚部。我习惯把抽取点对齐到每个符号的最佳采样点。这里的“最佳采样点”在QPSK中就是匹配滤波输出波形的符号周期整数倍处。如果你的接收端模型里匹配滤波器后面接了一个“Downsample”模块那么To Workspace保存下来的数据就已经是每个符号一个点直接取实部虚部就行。如果保存的是滤波后未抽取的波形那么要在脚本中做降采样。例如已知每个符号有sps个采样点那么抽取方式为idx 1:sps:length(rxBaseband); rxSym rxBaseband(idx);不过要注意延迟对齐。因为滤波器有群延迟直接按整数倍抽取可能会抽到符号边沿。更稳妥的做法是发射端数据经过整个链路后接收端接收信号相对发射端有固定延迟可以先观察时域幅度波形找到眼图张开最大的位置偏移量再修正。对于初学仿真也可以直接在模型里让发射端滤波器与接收端滤波器完全匹配并使用相同的群延迟然后在脚本里补偿。4.2 用matlab绘制星座图星座图最简单的画法就是复数散点图figure; plot(real(rxSym), imag(rxSym), .); xlabel(In-Phase); ylabel(Quadrature); title(QPSK Constellation); axis equal; grid on;如果安装了Phased Array System Toolbox或者Communications Toolbox也可以用scatterplot函数它会自动画出判决边界和标签scatterplot(rxSym);我个人更喜欢自定义plot因为可以灵活设置点的大小、颜色还可以叠加理论星座点。比如把理论点画成红色圆圈hold on; refPts [11i, -11i, 1-1i, -1-1i] / sqrt(2); plot(real(refPts), imag(refPts), ro, MarkerSize, 10);这样叠加之后判决质量一眼就能看出来。4.3 输出基带数据到文件基带数据输出听起来简单但格式和精度有讲究。我建议同时输出发射符号、接收符号和比特序列用writecell或writematrix保存为文本或二进制。保存到文本用writematrixwritematrix([real(rxSymComplex), imag(rxSymComplex)], rx_symbols.csv);如果后续要导入FPGA或C程序处理保存成二进制更合适fid fopen(rx_symbols.bin, w); fwrite(fid, [real(rxSymComplex); imag(rxSymComplex)], double); fclose(fid);二进制的格式需要注意字节序Matlab默认小端跟C默认一致一般没问题。另外我还会保存一份“仿真参数.json”或“.txt”记录信噪比、采样率、滚降系数等确保数据可追溯。别小看这一步回头你要写报告或者复现别人的实验时这个参数文件能帮你省无数时间。5. 常见问题与排查技巧5.1 仿真步长与采样不匹配导致星座图发散这是最常见的现象。星座图上每个符号点不是聚成四团而是拉成一条条弧线或者像乱麻一样。原因一般是仿真步长固定为符号周期但滤波器在符号周期内没有足够的点数导致信号重建丢失高频分量。解决办法是把仿真固定步长设成1/Fs也就是每个采样点都算一次。同时检查接收端的Downsample因子确保每个符号只抽一个点不然会过采样或欠采样。我用一个经验公式接收端匹配滤波后的信号长度除以sps就是符号数。如果长度不对就去检查模型里的buffer、frame size设置。5.2 变量覆盖与工作区冲突问题Simulink模型里的参数引用的是Matlab基础工作区变量。如果脚本里没有提前定义某个变量模型打开时会报错说找不到变量。这种情况通常出现在你刚启动Matlab、还没运行脚本就按了模型里的运行按钮。解决方法是把参数初始化写成一个函数或者在脚本开头一次性把用到的变量全部定义好。我习惯写一个init_qpsk_sim.m里面定义所有参数并在脚本开头调用它。这样不管是从脚本启动还是从模型手动运行只要先执行初始化就能避免变量缺失。另一个坑是变量名冲突。比如你定义了一个data变量模型里也有个To Workspace模块叫data那仿真输出会覆盖基础工作区的变量可能导致后续处理出错。建议工具变量和模型输出变量名分开比如模型输出统一加sim_前缀如sim_rxSym。5.3 模型回调函数使用技巧Simulink模型回调函数是一个容易被忽视但很实用的功能。在模型属性里可以配置InitFcn、StartFcn、StopFcn等回调。我通常把参数初始化直接配置到模型的InitFcn里这样不管是脚本还是点击运行模型都会自动调用一段脚本完成参数赋值。具体操作模型菜单“文件 - 模型属性 - 回调函数”选择InitFcn在编辑区写% 自动初始化参数 Fs 100e3; Rs 10e3; sps Fs / Rs; beta 0.35;回调函数会先于仿真执行这样再也不用担心忘记初始化变量。不过要注意如果脚本中也要修改某些参数比如EbNo那么脚本中要在sim之前重新赋值因为回调函数执行时会把EbNo重置为回调里写的值。解决方法是回调里不定义EbNo只定义常量和模型结构参数EbNo在脚本里单独设置。这样既保证模型能独立运行又方便脚本批量扫描。5.4 关于Matlab操作视频的录制与分享建议标题里提到“操作视频”这里单独聊聊录制操作视频这件事。我一开始用屏幕录制软件直接录后来发现有几个细节很重要。第一录制前先把Matlab字体调大代码区用默认深色或浅色都行但窗口不要有杂乱的信息。第二把模型窗口和脚本窗口分屏左侧脚本右侧模型或者先展示脚本再展示模型不要频繁切换窗口不然观众会头晕。第三关键参数修改时动作放慢鼠标点击后停顿一两秒。我通常会用Windows的“步骤记录器”截图但视频还是用OBS Studio录制免费且清晰度高。录制过程中涉及命令行时最好把命令行字体调大并且预先想好输入内容不要出现打错命令又删掉的情况。如果操作失误没关系可以直接说一句“这里输错了重来一下”比剪辑掉更真实。当然如果你是做正式教程可以用剪映或Premiere把多余片段剪掉保留关键步骤。还有一个实用窍门在脚本中适当加上pause(0.5)让程序每一步暂停半秒录视频时画面不会一闪而过。但要注意正式批量仿真时不要加pause会影响效率。我会单独做一个“录制版本”的脚本加上pause和分段打印方便录屏。6. 从仿真结果反推系统设计的个人体会QPSK仿真跑通并不难难的是从结果里找到改进方向。我最初做的时候星座图出来了但总有散点误码率怎么都降不下去。后来把接收端的信号和发射端的比特对齐才发现是滤波器延迟没补偿对齐偏了三个符号。用脚本调用模型的另一个优势就是这时候体现出来的我可以在脚本里写出相关对齐的自动算法每次运行后自动搜索时延然后补偿。这个在纯手动Simulink操作里很难实现。另外要强调一个容易被忽略的点星座图上每个簇的密度分布能反映很多信息。如果四个簇呈现出椭圆而不是正圆说明I/Q路的增益不平衡如果出现旋转那就说明有频率偏差如果整体是一个模糊的大圆那大概率是信噪比太低或者滤波器不匹配。学会看星座图的形态比单纯看误码率更快发现问题。最后再分享一个小技巧每次跑完仿真把工作区的变量用save(qpsk_result.mat)存下来。这样下次即使关闭Matlab也可以重新加载数据画图而不必重新跑一遍费时间的仿真。尤其是大帧长、高采样率的仿真重新跑一次动辄几分钟存档能让你省下很多重复劳动。这个习惯我保持了很久做算法对比时特别有用。如果有人问你某个点的结果怎么来的你直接加载mat文件当场复现画图那就是相当靠谱的工程师姿势了。本文还有配套的精品资源点击获取
返回列表