Python控制任意波形信号发生器AWG:pyvisa与SCPI实战指南
2026/9/18 13:54:41 网站建设 项目流程

1. 从零上手任意波形信号发生器:为什么值得花时间搞明白

任意波形信号发生器(Arbitrary Waveform Generator,简称AWG)在电子测试测量领域算是个“万能选手”。它能产生正弦波、方波、三角波这些常规波形,也能根据你自定义的数据点输出任意形状的信号——比如模拟传感器输出、复现某个电路故障时的异常波形、生成通信调制信号等等。如果你在做电路调试、器件测试、自动化验证,手里有一台AWG却只会按面板上的几个预设按钮,那基本等于买了一台跑车只在小区里遛弯。

我最初接触AWG是在一个电源纹波测试项目里,需要给被测板卡注入一个带特定谐波成分的干扰信号。手动在面板上拧旋钮根本没法精确控制每个周期的数据点,后来改用Python通过pyvisa库走SCPI命令远程控制,效率直接翻了好几倍。这篇内容就是把我从零摸索AWG编程的过程整理出来,包括环境搭建、SCPI命令基础、波形数据下发、常见坑的排查。不管你是刚入行的测试工程师,还是做嵌入式开发需要信号注入的软件人员,只要有一点Python基础,都能跟着走一遍。

核心关键词先摆出来:任意波形信号发生器AWGPythonpyvisaSCPI。这几个词贯穿全文,后面每个环节都会围绕它们展开。你不需要事先了解SCPI协议,也不需要买昂贵的商业软件,一台支持LAN或USB接口的AWG、一台电脑、Python环境,就够了。

2. 动手前的整体设计:为什么选Python加pyvisa这条路

2.1 控制AWG的几种方式对比

在决定用Python之前,我对比过几种常见的AWG控制方案,这里直接给个表格,方便你根据自己情况选:

控制方式典型工具适合场景主要限制
面板手动操作设备前面板按键/旋钮简单波形、临时调试无法批量、无法精确控制数据点
厂商上位机软件各品牌自带GUI常规测试、快速出结果跨品牌不通用、自动化能力弱
脚本编程控制Python + pyvisa自动化测试、自定义波形需要写代码、需要理解SCPI
嵌入式控制MCU/FPGA发命令产线集成、实时性要求高开发周期长、调试成本高

选Python + pyvisa的理由很直接:跨平台、跨品牌、生态好。pyvisa是Python对VISA(Virtual Instrument Software Architecture)库的封装,而VISA是测试测量行业事实上的标准I/O接口。只要你的AWG支持USBTMC、TCP/IP或GPIB中的任意一种,pyvisa基本都能通吃。换一台不同品牌的设备,底层SCPI命令可能有差异,但Python代码框架几乎不用大改。

2.2 SCPI命令的基本逻辑

SCPI(Standard Commands for Programmable Instruments)是一套标准命令语言,核心思想是用树状层级结构描述仪器功能。比如设置通道1的输出频率,命令是:

SOURce1:FREQuency 1000

拆开看:SOURce是根节点,1是通道号,FREQuency是子节点,后面跟参数值。查询当前频率则用:

SOURce1:FREQuency?

注意问号,这是SCPI的查询语法。命令不区分大小写,但习惯上大写字母表示缩写形式,小写字母表示可省略部分。比如SOURce可以写成SOUR,也可以写全SOURCE

提示:不同厂商对SCPI命令的扩展程度不同。Keysight、Tektronix、RIGOL、Siglent这些主流品牌的基础命令高度一致,但高级功能(如序列模式、调制深度)的命令格式会有差异。写代码前务必查对应型号的编程手册。

2.3 整体流程设计

我习惯把整个控制流程拆成四步:

  1. 环境准备:安装Python、pyvisa、VISA后端驱动,确认设备能通
  2. 连接与识别:用pyvisa枚举资源、建立连接、读取设备ID
  3. 基础控制:设置波形类型、频率、幅度、偏置、输出开关
  4. 任意波形下发:构造数据点数组、转换成设备要求的格式、通过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-VISA:National Instruments出品,功能最全,支持USB、LAN、GPIB。缺点是安装包大(几百MB),启动稍慢。
  • Keysight IO Libraries Suite:Keysight(原Agilent)的VISA实现,对自家设备支持最好。
  • RIGOL VISA:RIGOL设备用户可以用这个,体积小。
  • 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还是LAN

AWG通常支持USB和LAN两种连接方式。USB即插即用,适合桌面调试;LAN适合远程控制或多设备组网。从编程角度看,两者在pyvisa里的用法几乎一样,只是资源地址不同:

  • USB资源地址格式:USB0::0xVENDOOR_ID::0xPRODUCT_ID::SERIAL::INSTR
  • LAN资源地址格式:TCPIP0::192.168.1.100::INSTR

USB的优点是稳定、延迟低,缺点是线缆长度受限。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:频率1000Hz
  • SOURce1:VOLTage:AMPLitude 2:幅度2Vpp
  • SOURce1:VOLTage:OFFSet 0:偏置0V
  • OUTPut1:STATe ON:打开通道1输出

设置完可以用查询命令确认:

print(awg.query('SOURce1:FREQuency?')) print(awg.query('SOURce1:VOLTage:AMPLitude?'))

返回的字符串可能需要转成浮点数再比较,因为仪器返回的格式可能是+1.00000000E+03这种科学计数法。

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, datatype='f', is_big_endian=False)

write_binary_values是pyvisa提供的二进制数据传输方法,比逐点写ASCII字符串快几个数量级。datatype='f'表示单精度浮点,is_big_endian=False表示小端序。具体参数要看设备手册。

下发完成后,设置采样率:

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()返回空

排查顺序:

  1. 设备是否开机?USB线是否插紧?
  2. 设备管理器里能否看到仪器?Windows下看“通用串行总线控制器”或“其他设备”
  3. VISA后端是否安装?装完后是否重启过?
  4. 换一个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 ON

5.4 独家避坑经验

经验一:先读后写。在修改任何参数之前,先用查询命令读当前值。这样出问题时能快速定位是哪个参数设错了。

经验二:分步验证。不要一次性写完所有代码再跑。先连设备,再设基础波形,再下发任意波形,每一步都验证输出。这样出问题容易定位。

经验三:保存和复用配置。SCPI支持*SAV*RCL命令保存/调用仪器状态。调试好的配置存下来,下次直接调用,省去重复设置。

经验四:注意命令终止符。pyvisa的write默认会加换行符,但有些设备要求\n\r\n。如果命令不生效,检查awg.write_terminationawg.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类,和万用表、示波器、电源的控制代码整合到一个测试脚本里。典型流程:

  1. 电源上电
  2. AWG输出激励信号
  3. 示波器采集响应
  4. 万用表测量关键点电压
  5. 判断结果、记录数据
  6. 断电、进入下一个被测件

这种自动化测试的效率是手动操作的几十倍,而且一致性更好。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, datatype='f')

另外,把多条SCPI命令合并成一条发送也能减少通信开销。SCPI支持用分号分隔多条命令:

awg.write('SOURce1:FUNCtion SINusoid;FREQuency 1000;VOLTage:AMPLitude 2')

但注意,不是所有设备都支持这种写法,而且合并后出错时不好定位是哪条命令的问题。调试阶段还是分开写,稳定后再合并。

7. 一些个人体会

AWG编程这件事,入门门槛其实不高,但坑不少。我最初卡在VISA驱动上折腾了一下午,后来发现是USB线的问题。又有一次下发波形死活不对,查了半天才发现是字节序搞反了。这些经验在官方手册里不会写,只有实际踩过才知道。

另一个体会是:不要怕查手册。SCPI命令那么多,没人能全记住。遇到不认识的命令,直接翻编程手册的SCPI命令章节,通常都有详细说明和示例。厂商的示例代码也是很好的参考,虽然风格可能比较老旧,但命令格式是准确的。

最后说一个实用技巧:如果你手头没有实体AWG,可以用一些厂商提供的仿真软件或者SCPI模拟器来练手。虽然不能完全替代真实设备,但至少能把代码框架跑通,等有设备了直接改资源地址就能用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询