资讯详情

资讯详情

PyQt5+SCPI:泰克示波器自动化测试与实时刷新方案

简介基于PythonPyQt5开发的泰克示波器自动化测试资源面向需要将泰克MDO3014/3000系列示波器接入上位机进行实时控制与数据采集的测试工程师、自动化脚本开发人员及PyQt5学习者。软件可在界面实时显示示波器波形与图像并保存同时支持log实时读取、显示和界面日志窗口内容保存提供单次循环测试用例执行框架测试逻辑需根据项目自行编写。资源包为RAR格式共16个文件包含Python源码、QT Designer的ui界面文件、xml配置、png图片、txt日志等体积仅139KB结构精简适合快速参考与二次开发。资源中详细讲解了常用QT控件在Python中的调用方法并给出完整的工程目录组织方式可帮助读者理解从界面绘制到示波器通信、日志管理的整体流程。目前已有3657人学习下载适用于入门PyQt5界面编程或搭建轻量级仪器自动化测试原型。1. 这个项目解决什么把示波器上的肉眼看测试搬进 PyQt5 界面产线或者实验室里最常见的场景是被测板子上电测试员盯着泰克3000系列示波器屏幕手动按 Print 键截图再拿笔记下峰值电压、频率、上升沿时间最后填到 Excel 表格里。一台板子测下来少说十几分钟遇上批量测试一整天都在跟旋钮和按键较劲。这个项目就是把这一整套搬进 PyQt5 写的 Windows 桌面程序里示波器连上电脑后软件自动下发配置指令、实时刷新波形图片、实时刷新打印测量信息跑完自动生成测试记录。适合做硬件产线测试、实验室验证、以及想用自动化测试替代人工盯屏的开发者。核心价值不在“连上仪器”而在“图片和文本两条链路同时实时刷新测完不用人碰示波器”。2. 系统架构与技术选型VISA 通信层、界面线程与指令模型2.1 整体架构三层模型指令全走 SCPI这个项目本质上是一个仪器控制桌面应用主框架是 PyQt5 pyvisa。泰克 3000 系列示波器支持标准 SCPI 指令集通信方式有 USB、网口和 GPIB 三种程序里通常推荐用网口或者 USB因为 GPIB 需要额外转接卡。项目架构我拆成三层看界面层PyQt5 负责主窗口、波形显示区域、测量信息打印区域、测试控制按钮。所有用户操作都在这一层不能直接碰仪器。指令层基于 pyvisa 封装一个示波器驱动类把*IDN?、:MEASUREMENT:MEAS1:VALUE?、:SAVE:IMAGe这类 SCPI 指令包成 Python 方法。上层调用scope.get_voltage()不需要知道 VISA 指令长什么样。数据链路层波形图片和测量文本是两条独立链路。图片用二进制读取测量文本用 ASCII 解析两条链路各自跑在线程或定时器里。这个分层的好处是如果后面把泰克换成是德科技或者国产示波器只需要替换指令层界面和测试流程不用动。2.2 线程模型为什么图片和打印必须分开刷新实时刷新图片是典型的“读大块二进制数据”操作——示波器截图通常是几百 KB 到几 MB 的 PNGVISA 总线读一次要几百毫秒。实时刷新打印信息是“读短字节文本”操作一次就几百字节几十毫秒能跑完。如果把两条链路都丢在主线程里界面会周期性地卡顿按钮点一下要等一秒多才有反应。常见做法是开两个 QThread一个线程专门轮询波形图片另一个线程专门轮询测量值文本。线程拿到数据后通过pyqtSignal发回主线程主线程只负责往控件上刷新内容。线程里不碰任何界面对象这是写完不翻车的前提。2.3 指令与配置设计Tektronix3000 的常用指令集映射泰克 3000 系列选型时通常关注这几个指令指令功能返回格式*IDN?查询仪器型号逗号分隔的字符串:MEASUREMENT:MEAS1:SOURCE CH1指定测量通道无:MEASUREMENT:MEAS1:MEASurement PK2PK指定测量类型无:MEASUREMENT:MEAS1:VALUE?查询测量数值ASCII 浮点:SAVE:IMAGe:FILEFmt PNG设置截图格式无:SAVE:IMAGe:ACTUal触发截图无:FILESystem:READFile 路径读取示波器文件二进制块指令层的关键点在于测量前要先配置SOURCE和MEASUREMENT直接VALUE?会返回空值或者9.91e37表示无有效测量值。这个顺序写错程序不会报错但数据全是废的。3. 核心功能实现实时刷新图片与打印信息的完整逻辑3.1 仪器连接与指令封装第一步写示波器驱动类所有 SCPI 指令集中在里面。连接时需要指定 VISA 地址网口地址一般是TCPIP0::192.168.1.100::inst0::INSTRUSB 地址一般是USB0::0x0699::0x0368::C012345::INSTR。泰克官方驱动选 TekVISA也可以装 NI-VISApyvisa 会自动探测后端。import pyvisa class ScopeDriver: def __init__(self, visa_address: str): self.rm pyvisa.ResourceManager() self.instr self.rm.open_resource(visa_address) self.instr.timeout 5000 # 超时设为5秒避免卡死 self.instr.encoding latin-1 # 某些3000系列返回的字符编码 def check_idn(self) - str: # 连接后先确认型号避免误连到其他仪器 resp self.instr.query(*IDN?) return resp.strip() def set_measure(self, channel: str, meas_type: str): # 先用SOURCE指定测量通道再指定测量类型 self.instr.write(f:MEASUREMENT:MEAS1:SOURCE {channel}) self.instr.write(f:MEASUREMENT:MEAS1:MEASurement {meas_type}) def read_measure_value(self) - float: # 查询测量值注意返回的是ASCII字符串 value self.instr.query(:MEASUREMENT:MEAS1:VALUE?) return float(value.strip()) def save_screenshot(self, remote_path: str): # 设置PNG格式并触发截图截图会保存到示波器本地 self.instr.write(:SAVE:IMAGe:FILEFmt PNG) self.instr.write(f:SAVE:IMAGe:ACTUal {remote_path}) def read_file(self, remote_path: str) - bytes: # 示波器文件系统读取返回二进制数据 return self.instr.query_binary_values( f:FILESystem:READFile {remote_path}, datatypeB, is_binaryTrue, expect_dataTrue, )query_binary_values是这个项目里比较容易踩坑的地方泰克返回的是二进制块header 格式#N长度pyvisa 需要指定is_binaryTrue和expect_dataTrue才能正确解析。如果不加这两个参数返回的数据会是乱码或者直接报错。超时时间设置很重要读大文件时 5000ms 不够的话需要加大到 10000ms但注意超时时间过大会让界面假死时间变长通常配合线程使用。3.2 实时刷新波形图片QThread 轮询链路图片刷新线程的核心逻辑每隔一段时间截一张 PNG读取二进制发信号给主线程显示。线程里不能直接调用QLabel.setPixmap必须通过信号传QImage或者bytes。from PyQt5.QtCore import QThread, pyqtSignal import time class WaveformRefreshThread(QThread): # 信号参数是bytes类型主线程收到后转QImage image_ready pyqtSignal(bytes) def __init__(self, driver: ScopeDriver, remote_path: str, interval: float 0.5): super().__init__() self.driver driver self.remote_path remote_path self.interval interval self.running True def run(self): while self.running: try: # 触发截图并读取 self.driver.save_screenshot(self.remote_path) time.sleep(0.2) # 等示波器把文件写入文件系统 data self.driver.read_file(self.remote_path) self.image_ready.emit(data) # 发信号给主线程 except Exception as e: print(f截图异常: {e}) time.sleep(self.interval) def stop(self): self.running False self.wait()time.sleep(0.2)是必须的3000 系列在:SAVE:IMAGe:ACTUal之后文件写入需要时间立刻去读经常读到上一次的旧图或者空文件。轮询间隔建议设 0.3 到 0.8 秒太频繁会让示波器面板操作变得迟滞太慢则实时性太差。主线程这边的槽函数负责把二进制 PNG 转成QPixmapfrom PyQt5.QtGui import QPixmap, QImage import io # 在MainWindow中连接信号 self.waveform_thread.image_ready.connect(self.update_waveform_image) def update_waveform_image(self, data: bytes): # 从二进制数据直接构建图片对象不需要落到磁盘 pixmap QPixmap() pixmap.loadFromData(data, PNG) # 按控件宽度等比缩放避免图片过大 scaled pixmap.scaled( self.image_label.width(), self.image_label.height(), aspectRatioMode1 # 保持比例 ) self.image_label.setPixmap(scaled)注意aspectRatioMode1表示等比缩放如果省略这个参数图片会被强行拉伸变形波形图这种带网格的图片一变形幅度读数视觉上就是错的。3.3 实时刷新打印信息测量值文本链路文本刷新链路更轻量不需要线程用QTimer 普通方法轮询就可以。核心是把测量值格式化后追加到QPlainTextEdit同时保留清屏按钮。from PyQt5.QtCore import QTimer class MainWindow: def setup_measure_timer(self): self.measure_timer QTimer(self) self.measure_timer.timeout.connect(self.refresh_measure_info) self.measure_timer.start(300) # 300ms刷新一次 def refresh_measure_info(self): try: # 这里同时采集多个测量值频率和幅度 freq self.driver.read_measure_value() # 切换到第二个测量通道配置 self.driver.instr.write(:MEASUREMENT:MEAS1:MEASurement FREQUENCY) freq_val self.driver.read_measure_value() ts time.strftime(%H:%M:%S) line f[{ts}] 频率: {freq_val:.2f} Hz self.print_text.appendPlainText(line) # 只保留最近100行防止内存无限膨胀 if self.print_text.blockCount() 100: self.print_text.clear() except Exception as e: self.print_text.appendPlainText(f[采集异常] {e})这个定时器方案有个隐藏坑read_measure_value()里每次都切换测量类型如果示波器不是 3000 系列或者固件版本旧切换后立刻查询会返回 9.91e37。我一般会加一个防御性写法查询后判断数值是不是这个特定值是的话等 100ms 再查一次。3.4 测试流程编排一键跑一个完整用例自动化测试的核心是把测步骤串起来。这里用一个 JSON 配置定义测试项按顺序执行{ test_items: [ {name: 5V输出峰值电压, channel: CH1, meas: PK2PK, low: 4.8, high: 5.2}, {name: 时钟频率, channel: CH1, meas: FREQUENCY, low: 9900000, high: 10100000} ] }执行循环里每项先配置指令再取测量值与上下限比对把结果写进 CSV 文件同时把当前的波形图截图按测试项命名保存到report目录。这个流程可以在测试跑完后自然写成测试报告。4. 环境搭建与启动从接线到跑通一个完整测试流程4.1 依赖安装项目依赖不多核心是 pyvisa、PyQt5 和 pyserial如果用串口扩展的话。Python 版本建议 3.8 以上pyvisa 在 1.12 以上的版本对二进制块解析更稳。pip install pyvisa pyvisa-py PyQt5装完验证 VISA 后端python -c import pyvisa; rm pyvisa.ResourceManager(); print(rm.list_resources())如果输出空列表先确认示波器网络能 ping 通网口或者 USB 驱动装了 TekVISA。常见做法是网口优先——USB 在截图高频轮询时偶尔会掉线。4.2 连接配置与目录结构项目解压后的目录结构大致如下scope_auto_test/ ├── main.py # 程序入口 ├── config/ │ └── device.json # VISA地址、截图保存路径、采样间隔 ├── core/ │ ├── scope_driver.py # 示波器指令封装 │ └── test_engine.py # 测试流程编排 ├── ui/ │ ├── main_window.py # PyQt5 主窗口 │ └── widgets.py # 图片显示控件、打印控件 ├── report/ # 自动生成的测试报告目录 └── requirements.txt修改config/device.json里的visa_address为自己的示波器地址{ visa_address: TCPIP0::192.168.1.100::inst0::INSTR, screenshot_path: C:\\temp\\scope_screen.png, waveform_interval: 0.5, measure_interval: 0.3 }4.3 启动程序python main.py启动后界面分三个区域左边是波形图实时刷新区右边是测量信息打印区底部是测试按钮区。先点“连接仪器”打印区显示*IDN?返回的型号信息然后点“开始刷新”就能看到波形图动起来。测试按钮区的“一键测试”会执行 3.4 节那个 JSON 里的全部测试项。跑不起来时排查顺序先单独跑scope_driver.py下的连接方法确认 VISA 地址没写错再跑截图方法确认C:\temp在示波器上存在示波器侧也需要建同名目录最后才开界面。直接把这三步一次跑完出问题不好定位。5. 避坑记录实时刷新卡顿、乱码与兼容性排查5.1 界面假死截图时按钮点不动现象点击“开始刷新”后界面卡住鼠标转圈几秒后恢复。原因截图线程没启用save_screenshot和read_file直接跑在 UI 主线程里一个轮询周期内阻塞 1 秒多界面事件循环被卡死。解决把图片轮询放进 QThread参考 3.2 节主线程只处理信号槽。如果你在别的项目里用的是threading.Thread记得在run()里不要碰任何QWidget方法pyqtSignal 是 QThread 跨线程更新的唯一安全姿势。5.2 中文乱码测出来的频率显示成乱码现象打印区显示ç¹å¾之类的乱码或者测量值变成负数。原因泰克 3000 系列在某些固件版本返回的字符集不是 UTF-8pyvisa 默认按ascii或utf-8解码会导致字节错位。解决在open_resource后显式设置encodinglatin-1或者读取后手动重新解码。我一般会做双重保险字符串前后strip()再判断是否含nan或9.91e37是的话丢弃本次读数重查一次。这不是工具类代码这是拿真实示波器测过的血泪经验。5.3 图片刷新不及时一直是上一张现象波形图内容没变化但文件时间戳在变。原因:SAVE:IMAGe:ACTUal触发后立刻读文件示波器文件系统写入有延迟读到的是上一张图。另一种可能连了两台示波器read_file读的是另一台的路径。解决截图指令后至少等 200ms再把间隔调成 0.5 秒。另外每次read_file前先check_idn()一次确认连接的还是同一台设备。实测 3000 系列在 USB 链路下延迟最高能到 500ms建议把time.sleep放宽到 0.3 秒更稳。5.4 更换示波器型号后程序报错现象从 Tektronix3000 换成 2000 系列或者其它品牌提示:MEASUREMENT:MEAS1:VALUE? 语法错误。原因不同系列支持的 SCPI 子集不一样2000 系列只支持:MEASure:VPP?这种带问号的短指令不支持:MEASUREMENT长指令加VALUE?查询的组合。解决在驱动类里做一个指令适配层——启动时用*IDN?识别型号然后给指令模板字典切换。这个改造其实很轻量每条指令从字符串变成lambda channel: f:MEASUREMENT:MEAS1:SOURCE {channel}这样的模板型号变了换模板加载就行。避免“换仪器就改代码”的被动局面适配层值得一写。6. 二次开发实战把单机测试改成可配置的多场景测试台最后一个改造建议把测试流程从写死的循环改成配置驱动。我在 3.4 已经用了 JSON 定义测试项如果要扩展就再加一个“场景”字段每个场景是独立的测试项数组界面下拉框切换场景。这样产线换测试项目时不用改代码改 JSON 就够了。另外一个高频需求是阈值判定可视化。目前test_engine.py里测量值只在打印区显示文本我建议把测量值同时画成实时趋势曲线pyqtgraph库比matplotlib更适合高频刷新plot.setData()直接更新曲线数据不重绘坐标轴。这样波形图、趋势图、文本打印三路同时跑产线测试员一眼能看出信号漂移趋势比只看数字直观得多。还要加一个 QA 小技巧测试结束自动生成报告目录按时间戳建子目录里面放 PNG 截图、CSV 测量记录和一个summary.txt。CSV 用csv.writer写 UTF-8 带 BOM不然 Excel 打开中文列名会乱。截图命名带上测试项名称和数值方便后续追溯。从那以后我每次写这类仪器控制程序都强制先确认“线程边界、编码、超时”三件事再动手。线程边界决定了界面会不会卡死编码决定数据看着对不对超时决定了异常时程序会不会挂住。这三件事花十分钟想清楚后面能省下好几天的调试时间。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →