资讯动态

用Matlab和ADALM-Pluto搭建QPSK点对点通信链路实战

发布时间:2026/9/8 19:49:00 来源:尧图企业网站定制
简介基于Pluto软件无线电平台的点对点通信系统发送与接收端Matlab实现适用于西电通院/电院通信原理课程大作业也适合学习通信链路搭建的本科高年级学生。代码按发送端与接收端进行组织覆盖从信源编码到调制发射、从接收解调到信宿还原的完整流程涉及PCM、CAZAC序列、QPSK解调、载波同步等关键环节包含完整发送与接收主程序及若干辅助函数可清晰对照各部分算法实现并配有2个mat数据文件用于突发捕获与预处理校验便于实验验证。资源共12个文件以m脚本为主、mat数据文件为辅压缩包约786KB结构紧凑、打开即用可快速迁移到同类点对点通信仿真任务。已有3650人学习适合正在完成通信原理大作业或需要快速上手Pluto平台方向的研究者参考。 手头有两块 ADALM-Pluto想用它搭一套最简单的点对点无线通信链路验证发送接收端的 Matlab 代码。这块软件无线电板子只有 U 盘大小却能覆盖 325 MHz 到 3.8 GHz 频率范围配合 Matlab 的 Communications Toolbox 和 Pluto 硬件支持包基本是我见过最省心的 SDR 教学组合。这篇文章我就拿一个真实跑过的项目来拆怎么用 Matlab 控制两块 Pluto完成点对点通信包括发送端信号生成、接收端同步与解调、误码率统计以及我调试时踩过的一堆坑。如果你手上有 Pluto想快速搭一套能收发、能看星座图、能统计 BER 的链路或者你在纠结要不要用 Pluto 做课程设计、通信实验这篇文章可以直接当参考脚本用。全文不整玄乎的理论只讲实际跑通的做法代码尽量短但每段我都会说清楚“为什么这么配”。1. 项目整体思路与方案选型1.1 为什么选 Pluto Matlab 这套组合市面上能做点对点通信验证的硬件不少从 USRP、HackRF 到各种开发板但我个人最推荐 Pluto原因很直接价格相对友好收发一体AD9361 芯片自带 12 位 ADC/DAC基带采样率最高能到 20 MHz 左右做教学演示和算法验证完全够用。相比 USRP 动辄上万Pluto 对实验室预算或者个人自学习来说压力小得多。Matlab 这边的优势更明显。很多人一开始会用 Python GNU Radio但我想说的是如果你需要快速验证调制解调算法、看 BER 曲线、对比不同方案性能Matlab 通信工具箱里现成的函数实在太多了pskmod、pskdemod、rcosdesign、berawgn 这些一键调用省去大量底层实现时间。尤其是做课程设计或者论文仿真Matlab 跑通后再移植到嵌入式平台这套工作流非常成熟。还有一个容易被忽视的理由Matlab 自带的 Pluto 支持包封装了射频配置、数据搬移、DMA 传输这些底层细节。你只需要设置载波频率、采样率、增益然后收发复数基带波形就行可以真正把精力放在通信算法上而不是折腾驱动和缓冲。1.2 点对点通信的系统架构这个项目不是简单的“发一串正弦波然后收回来”而是完整走了一遍点对点数字通信链路信源比特 - QPSK 调制 - 组帧插入训练序列- 发送板上变频 - 无线信道 - 接收板下变频采样 - 同步捕获 - 解调 - 恢复比特 - 统计误码率。为什么一定要加训练序列因为无线信道是持续流动的接收端开始采样的时候并不知道发送端的数据从哪个时刻开始。训练序列本质上是一个接收端也知道的“锚点”接收端通过和本地序列做相关就能在采样数据流里找到帧起点。这个思路和 WiFi、LTE 里的前导码preamble是同一个逻辑只是我们这里用手工方式实现方便看到中间每个环节。在方案取舍上我特意选了 QPSK 而不是 16QAM 或者 OFDM原因很简单点对点通信项目第一步是把链路通起来QPSK 调制解调复杂度低、抗噪能力强、星座点只有 4 个出了问题一眼就能看出来。等 QPSK 链路稳定了再往 OFDM、更高阶调制去扩展思路会更清晰。2. 环境搭建与射频参数选型2.1 支持包安装与连接状态确认第一次接触 Pluto 的人最容易被环境配置卡住。我建议直接走 Matlab 附加功能管理器打开 Matlab 后在命令行输入supportPackageInstaller搜索 ADALM-Pluto Radio登录 MathWorks 账户后选择安装即可。安装过程中需要联网下载硬件支持包体积不算小耐心等一会儿。注意 Matlab 版本不要太旧我用的 R2021a 以上版本都支持太老的版本可能找不到对应的支持包入口。装完之后先别急着写代码先确认板子能被识别。把 Pluto 用 USB 线连到电脑在 Matlab 命令行运行sdrdev(Pluto)如果返回一个 pluto radio 设备对象说明连接正常。一次接两块板子时系统会按 USB 端口顺序分配usb:0、usb:1所以后面代码里如果只有一块板子可以不指定设备编号但多板子场景最好手动指定避免抢设备。2.2 核心射频参数怎么定这一步是整个项目最容易“玄学”的地方其实参数选择是有套路可循的。载波频率我推荐先试 2.4 GHz这是 ISM 频段不需要额外申请许可且 Pluto 在 2.4 GHz 附近性能稳定。需要注意的是2.4 GHz 也是 WiFi 频段实验室里 WiFi 信号很密集可能会造成干扰所以实操时可以稍微偏离一下 WiFi 常用信道比如选 2.44 GHz。基带采样率我选了 1 MHz。你可能想问为什么不用更高的 20 MHz因为采样率越高单次采样的数据量越大DMA 缓冲和 USB 传输的压力也越大对教学验证而言 1 MHz 完全够用。而且 1 MHz 采样率配合 1 Msps 符号速率每符号 1 个采样点逻辑最简单清楚。增益设置是另一门学问。发送增益我一般从 -10 dB 开始接收增益从 30~40 dB 开始。如果你是天线直连两米以内这个配置比较稳。要是发送增益调太高接收端很容易饱和星座图外圈会被“削平”调太低接收端噪声太大同步也会失败。后面第 4 节我会详细说增益怎么排查。2.3 收发端基础对象配置Matlab 的 Pluto 支持包有两种 API 风格一种是用sdrtx、sdrrx这种高层对象另一种是comm.SDRTxPluto、comm.SDRRxPluto封装类底层逻辑一样。我习惯用sdrtx和sdrrx代码更短。发送端基本配置tx sdrtx(Pluto); tx.CenterFrequency 2.44e9; tx.BasebandSampleRate 1e6; tx.Gain -10;接收端基本配置rx sdrrx(Pluto); rx.CenterFrequency 2.44e9; rx.BasebandSampleRate 1e6; rx.GainSource Manual; rx.Gain 40; rx.SamplesPerFrame 2 * 2112;这里有一个隐藏知识点收发两端所有影响载波频率和采样率的参数必须完全一致否则接收端解调出来的星座图一定是旋转的或者完全散开。后面调试常见问题时会再强调。3. 发送端、接收端代码实现与联调3.1 发送端从比特到发射波形发送端的完整流程是生成随机比特 - QPSK 符号映射 - 拼接训练序列 - 创建发射对象 - 循环发射。代码长这样% 发送端完整代码 fc 2.44e9; % 射频载波频率 fs 1e6; % 基带采样率 M 4; % QPSK % 生成 4000 个随机比特作为有效载荷 dataBits randi([0 1], 4000, 1); dataSym bi2de(reshape(dataBits, [], 2), left-msb); modSym pskmod(dataSym, M, pi/4); % 生成 128 个 QPSK 符号作为训练序列 trainBits randi([0 1], 128, 1); trainSym pskmod(bi2de(reshape(trainBits, [], 2), left-msb), M, pi/4); % 组帧训练序列在前数据在后 frame [trainSym; modSym]; % 配置发射对象并循环发射 tx sdrtx(Pluto); tx.CenterFrequency fc; tx.BasebandSampleRate fs; tx.Gain -10; tx.transmitRepeat(frame);这里我特别想解释两个点。第一pskmod(dataSym, M, pi/4)里的pi/4是相位偏移。QPSK 的星座点如果放在 0°、90°、180°、270°星座图看起来比较“方”但实际通信里很多系统会把星座旋转 45°这样符号跳变时的瞬时幅度波动更小。用pi/4偏移不是必须的但能让星座图更规范也方便接收端判断。第二训练序列我用的是随机 QPSK 符号不是一段连续正弦波或者全 1 序列。随机序列的自相关特性比较好接收端做滑动相关的时候相关峰更尖锐不容易误判。如果你用全 1 序列做训练相关峰会变得很平同步精度会明显下降。3.2 接收端捕获、同步与解调接收端的核心难点是帧同步。Pluto 一开机就在持续采样你的数据信号混在采集流里怎么知道从哪个点开始解调我的做法是用训练序列和一个本地模板做滑动相关相关峰出现的位置就是训练序列在采样流里的起点。接收端代码% 接收端完整代码需要与发送端使用相同的 fc、fs、M、trainSym rx sdrrx(Pluto); rx.CenterFrequency fc; rx.BasebandSampleRate fs; rx.GainSource Manual; rx.Gain 40; rx.SamplesPerFrame 2 * length(frame); % 多采一些保证能捕获完整帧 data rx(); % 滑动相关用卷积实现匹配滤波同步 corrOut conv(data, conj(flipud(trainSym))); [~, peakIdx] max(abs(corrOut)); startIdx peakIdx - length(trainSym) 1; % 检查边界 if startIdx 1 || startIdx length(frame) - 1 length(data) error(帧同步失败请检查发射端是否开启、接收增益是否合适); end % 截取完整帧去掉训练序列解调载荷 rxFrame data(startIdx : startIdx length(frame) - 1); rxPayload rxFrame(length(trainSym) 1 : end); rxSym pskdemod(rxPayload, M, pi/4); rxBits de2bi(rxSym, 2, left-msb); rxBits rxBits(:); % 计算误码率 ber sum(rxBits ~ dataBits) / numel(dataBits); fprintf(接收误码率 BER %.6f\n, ber);这段代码里最需要理解的是conv(data, conj(flipud(trainSym)))这一行。它等价于一个匹配滤波器把训练序列翻转取共轭再和接收数据卷积相当于把本地模板一个点一个点地滑过整个数据流输出值最大的位置就是模板和接收信号最相似的地方。peakIdx对应的是训练序列最后一个符号的位置所以真正帧起点要减掉length(trainSym) - 1。我第一次写同步代码时忘了减这个数结果解调出来的全是垃圾数据排查了很久才想到。还有一个容易被忽略的问题接收端脚本必须能访问到trainSym变量。实际使用时发送端和接收端通常不在同一个 Matlab 进程里所以要么把trainSym生成代码复制到接收端脚本里要么存成.mat文件共享。这一点本质上就是通信协议约定收发双方必须用同一个训练序列。3.3 完整点对点传输的调用流程说下实际操作流程。我一般开两个 Matlab 命令行窗口一个跑发送端脚本一个跑接收端脚本。跑发送端之前先把 Pluto 插好确认系统能识别跑发送端脚本后会看到命令窗口一直停留在transmitRepeat那行这代表板子正在循环发射不要以为卡死了。然后切到接收端窗口运行接收端脚本。这里有个小技巧SamplesPerFrame设置的是单次采集的样本数我习惯设为“帧长 训练序列长度”的两倍多采一点给同步留下余量。如果只设成和帧长一样接收窗口恰好会错过完整帧经常出现同步失败或者解调数据缺一段。两块板子如果都在同一台电脑上需要给收发对象指定硬件编号避免读写冲突。发送端用sdrtx(Pluto, RadioID, usb:0)接收端用sdrrx(Pluto, RadioID, usb:1)这样两个脚本不会同时抢占同一个设备。3.4 误码率统计与链路质量判断跑通之后代码最后会打印一个 BER 值。短距离一两米加上合适的增益QPSK 链路的 BER 应该稳定在 0 或者接近 0。如果 BER 在 1e-2 量级以上说明链路质量有问题但不要急着怀疑代码先按这个顺序排查收发频率是否一致 - 采样率是否一致 - 天线是否接好 - 接收增益是否太低或太高 - 是否有同频干扰。我还习惯顺手画一个接收星座图代码就三行scatterplot(rxPayload) title(接收端 QPSK 星座图)正常情况四个星座点应该清晰聚拢在圆上而且和发送端的相位偏移一致。如果星座点围成一个圈说明载波频率没对上如果聚成一坨说明增益设置有问题或者没同步成功。星座图比 BER 数字直观得多调试时一定要养成看星座图的习惯。4. 调试实录常见问题与排查技巧4.1 采样率与带宽失配我在测试中最常遇到的问题是“发送端明显在发接收端星座图却完全散开”十有八九是采样率不一致。Pluto 支持包的采样率看似设置的是浮点数值但实际硬件会做重采样如果你发送端写1e6接收端写1.2e6两边时钟根本对不上解调出来的符号就全是乱码。还有一个进阶问题基带采样率要大于信号带宽。我们这版代码没有加成型滤波QPSK 符号每样本一个信号带宽接近符号速率所以 1 MHz 采样率没问题。但如果你参考其他例程加入了根升余弦脉冲成形rcosdesign采样率就不是 1 Msps 了而是按滤波器的过采样倍率提升比如每个符号 4 个采样点采样率就要设成 4 MHz。这类问题不会有报错提示只能靠检查星座图和 BER 判断。4.2 增益、饱和与噪声的平衡增益是另一个调参重灾区。有一次我把发送增益调到 10 dB结果接收端星座图外圈被“压平”了四个点都胖乎乎的BER 高达 0.1。这是因为接收端 ADC 动态范围有限信号太强直接进入饱和区波形被削顶。增益调节口诀优先降低发送增益然后逐步加大接收增益。发送端目的是提供一个“够用且不过强”的信号接收端目的是让信号幅度在 ADC 的最佳工作区间。加上天线近距离直连-10 dB 到 -20 dB 的发送增益完全够用接收增益先从 30 dB 往上调到 40 dB如果星座图幅度偏大就往回调。4.3 数据帧抓取不到、同步失败同步失败的现象有两种一是代码报错“帧同步失败”二是同步没报错但 BER 极高。前者多半是startIdx计算出界原因是采样的数据窗口里根本没有完整训练序列后者多半是训练序列相关峰不够尖锐或者干扰太强导致同步位置偏了好几个符号。我的解决方法是用一个自相关性更好的序列例如 Zadoff-Chu 序列或者 m 序列替代随机 QPSK 序列。Matlab 里有现成的lteZadoffChuSeq函数可以生成 ZC 序列再映射成 QPSK 符号。如果实在不想改序列也可以把训练序列加长到 256 或 512 个符号相关峰更明显同步更稳。不过训练序列变长也有代价帧开销变大有效数据比例降低。点对点演示无所谓如果做实际系统就要权衡。4.4 双 Pluto 同时工作的防坑指南两块 Pluto 同时插在电脑上最常见的坑是供电不足。Pluto 用 USB 直接驱动有的电脑 USB 口供电能力弱插了两块板子之后一块或两块会随机掉线。我的做法是尽量用主机背板的 USB 接口不要用前置面板的口更不要用无供电的 USB HUB 串联多块板子。另一个坑是设备编号混乱。你以为是usb:0和usb:1实际可能因为插入顺序变成别的方式最好的办法是在代码里显式指定sdrdev(Pluto, RadioID, usb:0)运行后先查一下 deviceID 是否正确再跑收发脚本。还有一点收发两块板子如果在同一个 Matlab 进程里同时运行transmitRepeat会一直占用发送线程接收端建议放到另一个 Matlab 进程或者使用多线程并行池处理否则可能卡住。5. 扩展思路与个人心得5.1 从点对点向 OFDM、MIMO 扩展这套点对点链路跑通之后扩展方向其实非常清晰。如果要做 OFDM只需要在发送端把 QPSK 符号经过 IFFT 变成时域信号再加循环前缀接收端先做帧同步再 FFT 解调。Pluto 的带宽足够承载 64 个子载波的 OFDM 信号Matlab 的lteOFDMModulate函数可以直接复用。如果想做 MIMO两块板子可以做 2x2 空间复用但这会引入多天线同步问题难度上一个大台阶。我的建议是先做完 OFDM再考虑 MIMO循序渐进。5.2 我对这套方案的整体评价与建议跑完这个项目下来我的总体感受是Pluto Matlab 做点对点通信是性价比极高的组合它让你只关注通信算法本身不被硬件驱动和射频配置拖累。对于学生党它简直是移动的通信原理实验室对于做算法预研的工程师它也够用。最后分享一个我的小习惯把收发配置里的关键参数载波频率、采样率、增益、训练序列写成一个结构体保存到.mat文件每次测试前加载。这样无论怎么改代码只要加载同一份配置至少能排除“参数不一致”这个最常见变量。我后面做 OFDM 和自适应调制实验时一直延续这个习惯省掉了大量重复排查时间。如果你也要在这套链路上继续扩展建议一开始就养成记录参数组合的习惯。本文还有配套的精品资源点击获取

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

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

免费获取报价