Python控制任意波形信号发生器AWG:pyvisa与SCPI实战指南

发布时间:2026/9/18 13:54:45
Python控制任意波形信号发生器AWG:pyvisa与SCPI实战指南
1. 从零上手任意波形信号发生器为什么值得花时间搞明白任意波形信号发生器Arbitrary Waveform Generator简称AWG在电子测试测量领域算是个“万能选手”。它能产生正弦波、方波、三角波这些常规波形也能根据你自定义的数据点输出任意形状的信号——比如模拟传感器输出、复现某个电路故障时的异常波形、生成通信调制信号等等。如果你在做电路调试、器件测试、自动化验证手里有一台AWG却只会按面板上的几个预设按钮那基本等于买了一台跑车只在小区里遛弯。我最初接触AWG是在一个电源纹波测试项目里需要给被测板卡注入一个带特定谐波成分的干扰信号。手动在面板上拧旋钮根本没法精确控制每个周期的数据点后来改用Python通过pyvisa库走SCPI命令远程控制效率直接翻了好几倍。这篇内容就是把我从零摸索AWG编程的过程整理出来包括环境搭建、SCPI命令基础、波形数据下发、常见坑的排查。不管你是刚入行的测试工程师还是做嵌入式开发需要信号注入的软件人员只要有一点Python基础都能跟着走一遍。核心关键词先摆出来任意波形信号发生器、AWG、Python、pyvisa、SCPI。这几个词贯穿全文后面每个环节都会围绕它们展开。你不需要事先了解SCPI协议也不需要买昂贵的商业软件一台支持LAN或USB接口的AWG、一台电脑、Python环境就够了。2. 动手前的整体设计为什么选Python加pyvisa这条路2.1 控制AWG的几种方式对比在决定用Python之前我对比过几种常见的AWG控制方案这里直接给个表格方便你根据自己情况选控制方式典型工具适合场景主要限制面板手动操作设备前面板按键/旋钮简单波形、临时调试无法批量、无法精确控制数据点厂商上位机软件各品牌自带GUI常规测试、快速出结果跨品牌不通用、自动化能力弱脚本编程控制Python pyvisa自动化测试、自定义波形需要写代码、需要理解SCPI嵌入式控制MCU/FPGA发命令产线集成、实时性要求高开发周期长、调试成本高选Python pyvisa的理由很直接跨平台、跨品牌、生态好。pyvisa是Python对VISAVirtual Instrument Software Architecture库的封装而VISA是测试测量行业事实上的标准I/O接口。只要你的AWG支持USBTMC、TCP/IP或GPIB中的任意一种pyvisa基本都能通吃。换一台不同品牌的设备底层SCPI命令可能有差异但Python代码框架几乎不用大改。2.2 SCPI命令的基本逻辑SCPIStandard Commands for Programmable Instruments是一套标准命令语言核心思想是用树状层级结构描述仪器功能。比如设置通道1的输出频率命令是SOURce1:FREQuency 1000拆开看SOURce是根节点1是通道号FREQuency是子节点后面跟参数值。查询当前频率则用SOURce1:FREQuency?注意问号这是SCPI的查询语法。命令不区分大小写但习惯上大写字母表示缩写形式小写字母表示可省略部分。比如SOURce可以写成SOUR也可以写全SOURCE。提示不同厂商对SCPI命令的扩展程度不同。Keysight、Tektronix、RIGOL、Siglent这些主流品牌的基础命令高度一致但高级功能如序列模式、调制深度的命令格式会有差异。写代码前务必查对应型号的编程手册。2.3 整体流程设计我习惯把整个控制流程拆成四步环境准备安装Python、pyvisa、VISA后端驱动确认设备能通连接与识别用pyvisa枚举资源、建立连接、读取设备ID基础控制设置波形类型、频率、幅度、偏置、输出开关任意波形下发构造数据点数组、转换成设备要求的格式、通过SCPI下发这个顺序的好处是每一步都有明确的验证点。连接不通就别急着写波形数据基础参数没设对就别碰任意波形模式。很多新手一上来就想直接下发自定义波形结果连设备都没连上排查起来一头雾水。3. 环境搭建Python、pyvisa和VISA后端一个都不能少3.1 Python安装与版本选择Python安装本身没什么好说的官网下载安装包一路下一步就行。但有几个细节值得注意版本选3.8以上。pyvisa从1.11版本开始对Python 3.6的支持就逐渐弱化了现在主流是3.9到3.12。我目前在用3.11稳定性和库兼容性都很好。安装时勾选“Add Python to PATH”。这个选项如果不勾后面在命令行里敲python会提示找不到命令还得手动配环境变量。建议用虚拟环境。python -m venv awg_env然后激活避免和系统里其他项目的库版本冲突。尤其是你如果同时在做数据分析、爬虫之类的项目numpy版本冲突是常有的事。如果你之前完全没碰过Python建议先花半小时过一遍基础语法——变量、列表、循环、函数。不需要学到面向对象能看懂for循环和列表操作就够了。网上有很多免费的Python入门教程搜“Python基础语法”就能找到。3.2 pyvisa安装与VISA后端配置pyvisa本身只是一个Python库它需要调用底层的VISA库才能和仪器通信。安装分两步pip install pyvisa然后安装VISA后端。常见的选择有NI-VISANational Instruments出品功能最全支持USB、LAN、GPIB。缺点是安装包大几百MB启动稍慢。Keysight IO Libraries SuiteKeysight原Agilent的VISA实现对自家设备支持最好。RIGOL VISARIGOL设备用户可以用这个体积小。pyvisa-py纯Python实现的VISA后端不需要装厂商驱动但功能有限部分设备可能不支持。我个人的建议是如果你用的是NI或Keysight的设备直接装对应厂商的VISA。如果是RIGOL、Siglent等品牌装NI-VISA最省事兼容性最好。pyvisa-py可以作为备选但遇到USB连接问题时优先换厂商VISA。安装完VISA后在Python里验证import pyvisa rm pyvisa.ResourceManager() print(rm.list_resources())如果输出类似(USB0::0x2A8D::0x1102::MY12345678::INSTR,)的元组说明VISA后端正常工作设备也被识别到了。如果输出空元组说明要么设备没连好要么VISA后端没装对。3.3 连接方式选择USB还是LANAWG通常支持USB和LAN两种连接方式。USB即插即用适合桌面调试LAN适合远程控制或多设备组网。从编程角度看两者在pyvisa里的用法几乎一样只是资源地址不同USB资源地址格式USB0::0xVENDOOR_ID::0xPRODUCT_ID::SERIAL::INSTRLAN资源地址格式TCPIP0::192.168.1.100::INSTRUSB的优点是稳定、延迟低缺点是线缆长度受限。LAN的优点是灵活缺点是网络配置可能出问题IP冲突、防火墙拦截等。我一般桌面调试用USB机架测试用LAN。注意用LAN连接时建议给AWG分配固定IP或者在路由器里做DHCP绑定。否则设备重启后IP变了代码里的地址就得跟着改很烦。4. 核心实操从连接设备到下发任意波形4.1 建立连接与设备识别连接设备的标准写法import pyvisa rm pyvisa.ResourceManager() # 替换成你实际枚举到的地址 awg rm.open_resource(USB0::0x2A8D::0x1102::MY12345678::INSTR) awg.timeout 5000 # 超时5秒 print(awg.query(*IDN?))*IDN?是SCPI的通用识别命令几乎所有仪器都支持。返回内容通常包含厂商、型号、序列号、固件版本比如Keysight Technologies,33522B,MY12345678,3.02这一步能通说明物理连接和基本通信没问题。如果报错VisaIOError先检查设备是否开机、线缆是否插紧、VISA地址是否正确。4.2 基础波形参数设置在玩任意波形之前先把基础波形控制跑通。以设置通道1输出1kHz正弦波、幅度2Vpp、偏置0V为例awg.write(SOURce1:FUNCtion SINusoid) awg.write(SOURce1:FREQuency 1000) awg.write(SOURce1:VOLTage:AMPLitude 2) awg.write(SOURce1:VOLTage:OFFSet 0) awg.write(OUTPut1:STATe ON)每条命令的含义SOURce1:FUNCtion SINusoid设置通道1波形为正弦SOURce1:FREQuency 1000频率1000HzSOURce1:VOLTage:AMPLitude 2幅度2VppSOURce1:VOLTage:OFFSet 0偏置0VOUTPut1:STATe ON打开通道1输出设置完可以用查询命令确认print(awg.query(SOURce1:FREQuency?)) print(awg.query(SOURce1:VOLTage:AMPLitude?))返回的字符串可能需要转成浮点数再比较因为仪器返回的格式可能是1.00000000E03这种科学计数法。4.3 任意波形数据的构造与下发这是整篇内容的核心。任意波形的本质是一串按时间顺序排列的电压采样点AWG按照设定的采样率依次输出这些点经过DAC和滤波后形成模拟波形。假设我要生成一个自定义的“阶梯波”每个周期10个点幅度从0到1V递增。步骤如下第一步确定采样率和点数采样率决定了波形的时间分辨率。如果我要输出一个1kHz的阶梯波每个周期10个点那采样率至少是10kHz。但实际AWG的采样率通常远高于此比如100MSa/s所以需要把波形数据“填充”到更高的点数。这里有个关键概念AWG的波形内存深度。比如设备支持16384个点你下发10个点设备可能会自动插值或者循环播放。不同设备的处理方式不同查手册确认。第二步构造数据数组用numpy构造数据最方便import numpy as np points 1000 # 总点数 cycles 10 # 包含10个周期 samples_per_cycle points // cycles waveform [] for i in range(cycles): for j in range(samples_per_cycle): waveform.append(j / samples_per_cycle) waveform np.array(waveform)第三步归一化到设备要求的范围大多数AWG要求波形数据是-1到1之间的浮点数或者-32768到32767之间的整数。以浮点为例waveform_normalized waveform * 2 - 1 # 映射到-1到1第四步通过SCPI下发数据不同厂商的下发命令格式不同。以Keysight 33500系列为例# 设置任意波形模式 awg.write(SOURce1:FUNCtion ARB) # 下发数据使用二进制块传输 data_bytes waveform_normalized.astype(f4).tobytes() awg.write_binary_values(SOURce1:DATA:ARBitrary , waveform_normalized, datatypef, is_big_endianFalse)write_binary_values是pyvisa提供的二进制数据传输方法比逐点写ASCII字符串快几个数量级。datatypef表示单精度浮点is_big_endianFalse表示小端序。具体参数要看设备手册。下发完成后设置采样率awg.write(SOURce1:FUNCtion:ARBitrary:SRATe 1000000) # 1MSa/s awg.write(OUTPut1:STATe ON)4.4 参数计算采样率、频率和点数的关系这三者的关系是新手最容易搞混的地方。公式很简单输出频率 采样率 / 每周期点数或者反过来每周期点数 采样率 / 输出频率举个例子采样率设为1MSa/s我想要输出1kHz的波形那每周期需要1000个点。如果波形内存深度只有16384点那最多能存16个周期。实际设置时我习惯先确定输出频率和波形细节要求再反推采样率。比如要输出一个含高次谐波的复杂波形最高谐波频率是100kHz根据奈奎斯特采样定理采样率至少要是200kSa/s实际取5到10倍余量设1MSa/s比较稳妥。注意采样率不是越高越好。高采样率意味着波形内存被快速消耗能存储的波形周期数减少。而且部分AWG在高采样率下会有更大的量化噪声。根据实际需求平衡。5. 常见问题与排查技巧实录5.1 连接类问题问题一list_resources()返回空排查顺序设备是否开机USB线是否插紧设备管理器里能否看到仪器Windows下看“通用串行总线控制器”或“其他设备”VISA后端是否安装装完后是否重启过换一个USB口试试前置面板的USB口供电可能不足问题二连接超时awg.timeout 5000 # 默认2000ms可能不够有些设备响应慢尤其是刚开机或正在执行长命令时。把超时设大一点5000到10000ms。问题三LAN连接不上先ping设备IP通了再检查防火墙是否拦截了VISA的端口设备是否设置了固定IP和电脑是否在同一网段资源地址里的IP是否正确5.2 波形下发类问题问题一下发的波形输出不对最常见的原因是数据格式不匹配。检查数据类型是float32还是int16字节序是大端还是小端数据范围是-1到1还是0到1我踩过一次坑设备要求int16我传了float32结果输出完全乱套。后来用awg.query(SOURce1:DATA:ARBitrary?)读回数据对比才发现。问题二波形有台阶感这是点数不够导致的。增加每周期点数或者开启设备的插值/滤波功能。部分AWG支持SOURce1:FUNCtion:ARBitrary:FILTer命令设置输出滤波器。问题三输出幅度不对任意波形的幅度受两个因素影响波形数据的归一化范围以及VOLTage:AMPLitude的设置。如果数据范围是-1到1幅度设为2Vpp那输出就是2Vpp。如果数据范围是-0.5到0.5输出就只有1Vpp。5.3 常见问题速查表现象可能原因解决方法枚举不到设备驱动未装/线缆问题重装VISA、换线换口连接超时超时设置太短增大timeout值波形输出乱数据格式不匹配查手册确认数据类型和字节序波形有台阶点数不足增加点数或开启插值幅度不对归一化范围不对检查数据范围与幅度设置频率不对采样率与点数不匹配按公式重新计算输出无信号输出未开启发送OUTPut1:STATe ON5.4 独家避坑经验经验一先读后写。在修改任何参数之前先用查询命令读当前值。这样出问题时能快速定位是哪个参数设错了。经验二分步验证。不要一次性写完所有代码再跑。先连设备再设基础波形再下发任意波形每一步都验证输出。这样出问题容易定位。经验三保存和复用配置。SCPI支持*SAV和*RCL命令保存/调用仪器状态。调试好的配置存下来下次直接调用省去重复设置。经验四注意命令终止符。pyvisa的write默认会加换行符但有些设备要求\n或\r\n。如果命令不生效检查awg.write_termination和awg.read_termination设置。经验五用*OPC?同步。下发长命令后用awg.query(*OPC?)等待操作完成。直接发下一条命令可能导致设备还没处理完就收到新命令出现不可预期的行为。6. 进阶方向从单次下发到自动化测试6.1 波形序列与循环播放基础玩法是下发一个波形然后循环输出。进阶玩法是利用AWG的序列模式Sequence Mode把多个波形片段按顺序播放每个片段可以设置不同的循环次数和跳转条件。这在模拟复杂信号场景时非常有用比如模拟一串通信数据包。以Keysight为例序列模式的命令大致是awg.write(SOURce1:FUNCtion ARB) awg.write(SOURce1:FUNCtion:ARBitrary:SEQuence:NEW) awg.write(SOURce1:FUNCtion:ARBitrary:SEQuence:SEGMent1:...)具体命令格式各厂商差异较大需要查对应手册。但核心思路是一样的定义片段、设置播放顺序、启动序列。6.2 与自动化测试框架集成如果你在做产线测试或批量验证可以把AWG控制封装成Python类和万用表、示波器、电源的控制代码整合到一个测试脚本里。典型流程电源上电AWG输出激励信号示波器采集响应万用表测量关键点电压判断结果、记录数据断电、进入下一个被测件这种自动化测试的效率是手动操作的几十倍而且一致性更好。pyvisa的跨品牌特性让这套框架可以适配不同厂商的设备换设备时只需要改资源地址和少量SCPI命令。6.3 用Python做波形可视化调试阶段把要下发的波形先画出来看看能避免很多低级错误import matplotlib.pyplot as plt plt.plot(waveform_normalized[:200]) plt.title(Waveform Preview) plt.xlabel(Sample Index) plt.ylabel(Amplitude) plt.grid(True) plt.show()matplotlib是Python最常用的绘图库安装pip install matplotlib即可。画出来的波形和预期不符就别急着下发先改数据。6.4 性能优化二进制传输与批量操作逐点用ASCII字符串下发波形数据非常慢。1000个点可能要好几秒。用二进制块传输能快几十倍awg.write_binary_values(SOURce1:DATA:ARBitrary , waveform_normalized, datatypef)另外把多条SCPI命令合并成一条发送也能减少通信开销。SCPI支持用分号分隔多条命令awg.write(SOURce1:FUNCtion SINusoid;FREQuency 1000;VOLTage:AMPLitude 2)但注意不是所有设备都支持这种写法而且合并后出错时不好定位是哪条命令的问题。调试阶段还是分开写稳定后再合并。7. 一些个人体会AWG编程这件事入门门槛其实不高但坑不少。我最初卡在VISA驱动上折腾了一下午后来发现是USB线的问题。又有一次下发波形死活不对查了半天才发现是字节序搞反了。这些经验在官方手册里不会写只有实际踩过才知道。另一个体会是不要怕查手册。SCPI命令那么多没人能全记住。遇到不认识的命令直接翻编程手册的SCPI命令章节通常都有详细说明和示例。厂商的示例代码也是很好的参考虽然风格可能比较老旧但命令格式是准确的。最后说一个实用技巧如果你手头没有实体AWG可以用一些厂商提供的仿真软件或者SCPI模拟器来练手。虽然不能完全替代真实设备但至少能把代码框架跑通等有设备了直接改资源地址就能用。