告别手动调节!用Python+PyQt5打造你的程控电源自动化测试平台(以IT6322B为例)

张开发
2026/4/13 4:59:41 15 分钟阅读

分享文章

告别手动调节!用Python+PyQt5打造你的程控电源自动化测试平台(以IT6322B为例)
用PythonPyQt5构建程控电源自动化测试平台实战指南在电子产品研发和测试环节中电源参数的精确控制和数据采集是确保产品质量的关键步骤。传统手动调节方式不仅效率低下还容易因人为因素导致测试结果不一致。本文将带您从零开始使用Python生态中的PyQt5和pyserial库打造一个功能完备的程控电源自动化测试平台。1. 环境准备与硬件连接1.1 所需硬件与软件构建自动化测试平台前需要准备以下基础环境硬件设备IT6322B程控电源或其他支持SCPI协议的电源设备USB转串口线或直连串口线待测设备及测试负载软件环境Python 3.7PyQt5 5.15pyserial 3.5pyvisa可选用于其他接口类型pandas用于数据导出安装依赖库只需执行以下命令pip install pyqt5 pyserial pandas pyvisa1.2 设备通信基础IT6322B支持多种通信方式本方案采用最通用的RS232串口通信。设备默认参数如下参数项默认值波特率9600数据位8停止位1校验位None流控无连接设备后可通过简单的Python代码测试通信是否正常import serial def test_connection(port): try: with serial.Serial(port, baudrate9600, timeout1) as ser: ser.write(b*IDN?\n) response ser.readline().decode().strip() return response if response else 无响应 except Exception as e: return f连接失败: {str(e)} print(test_connection(COM3)) # 替换为实际端口2. 核心通信模块设计2.1 SCPI指令封装程控电源通常采用SCPI(Standard Commands for Programmable Instruments)标准指令集。我们需要封装常用指令class SCPICommands: staticmethod def set_voltage(channel, value): return f:INST:NSEL {channel};:VOLT {value} staticmethod def set_current(channel, value): return f:INST:NSEL {channel};:CURR {value} staticmethod def measure_voltage(channel): return f:MEAS:VOLT? CH{channel} staticmethod def measure_current(channel): return f:MEAS:CURR? CH{channel} staticmethod def output_control(state): return f:OUTP:STAT {1 if state else 0}2.2 通信层实现创建稳定的通信基础类处理所有底层串口操作from serial import Serial, SerialException from threading import Lock class PowerSupplyController: def __init__(self, port, baudrate9600, timeout1): self.port port self.baudrate baudrate self.timeout timeout self.lock Lock() self.connection None def connect(self): try: self.connection Serial( portself.port, baudrateself.baudrate, timeoutself.timeout ) # 切换到远程控制模式 self._send_command(:SYST:REM) return True except SerialException as e: print(f连接失败: {e}) return False def disconnect(self): if self.connection and self.connection.is_open: self._send_command(:SYST:LOC) # 返回本地控制 self.connection.close() def _send_command(self, command): with self.lock: try: self.connection.write(f{command}\n.encode()) return True except Exception as e: print(f指令发送失败: {e}) return False def query(self, command): with self.lock: try: self.connection.reset_input_buffer() self._send_command(command) return self.connection.readline().decode().strip() except Exception as e: print(f查询失败: {e}) return None注意实际应用中应考虑添加重试机制和更完善的错误处理特别是在长时间运行的测试中。3. PyQt5界面设计与实现3.1 主界面架构使用PyQt5构建现代化测试界面主要包含以下功能区域from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QPushButton, QLabel, QLineEdit, QComboBox, QCheckBox, QTabWidget, QTextEdit) class PowerSupplyGUI(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(程控电源自动化测试平台) self.setGeometry(100, 100, 800, 600) # 主控件 self.connection_group self._create_connection_group() self.control_group self._create_control_group() self.test_group self._create_test_sequence_group() self.log_output QTextEdit() # 布局 main_widget QWidget() layout QVBoxLayout() layout.addWidget(self.connection_group) layout.addWidget(self.control_group) layout.addWidget(self.test_group) layout.addWidget(self.log_output) main_widget.setLayout(layout) self.setCentralWidget(main_widget) def _create_connection_group(self): group QGroupBox(设备连接) layout QHBoxLayout() self.port_combo QComboBox() self.baudrate_combo QComboBox() self.baudrate_combo.addItems([9600, 19200, 38400, 57600, 115200]) self.connect_btn QPushButton(连接) layout.addWidget(QLabel(端口:)) layout.addWidget(self.port_combo) layout.addWidget(QLabel(波特率:)) layout.addWidget(self.baudrate_combo) layout.addWidget(self.connect_btn) group.setLayout(layout) return group def _create_control_group(self): # 实现类似的控件创建逻辑 pass def _create_test_sequence_group(self): # 实现测试序列相关控件 pass3.2 实时数据监控添加实时数据图表显示功能使用PyQtGraph库import pyqtgraph as pg from PyQt5.QtCore import QTimer class RealTimePlotWidget(pg.PlotWidget): def __init__(self, parentNone): super().__init__(parent) self.setBackground(w) self.showGrid(xTrue, yTrue) self.setLabel(left, Value) self.setLabel(bottom, Time (s)) self.voltage_curve self.plot(penpg.mkPen(b, width2)) self.current_curve self.plot(penpg.mkPen(r, width2)) self.data_buffer {time: [], voltage: [], current: []} self.max_points 500 def update_plot(self, voltage, current): current_time len(self.data_buffer[time]) * 0.1 # 假设0.1秒采样一次 self.data_buffer[time].append(current_time) self.data_buffer[voltage].append(voltage) self.data_buffer[current].append(current) # 保持数据量在合理范围 if len(self.data_buffer[time]) self.max_points: for key in self.data_buffer: self.data_buffer[key] self.data_buffer[key][-self.max_points:] self.voltage_curve.setData(self.data_buffer[time], self.data_buffer[voltage]) self.current_curve.setData(self.data_buffer[time], self.data_buffer[current])4. 自动化测试框架实现4.1 测试序列设计创建灵活的测试序列编排系统支持多种测试场景import json import time from dataclasses import dataclass from typing import List, Dict dataclass class TestStep: name: str duration: float # 秒 parameters: Dict[str, float] measurements: List[str] class TestSequence: def __init__(self): self.steps: List[TestStep] [] def add_step(self, step: TestStep): self.steps.append(step) def run(self, controller, callbackNone): results [] for i, step in enumerate(self.steps): if callback: callback(f正在执行步骤 {i1}/{len(self.steps)}: {step.name}) # 设置电源参数 for param, value in step.parameters.items(): if param.startswith(CH): channel int(param[2]) if V in param: controller.set_voltage(channel, value) elif I in param: controller.set_current(channel, value) # 等待稳定 time.sleep(0.5) # 执行测量 step_result {step: step.name, time: time.time()} for measurement in step.measurements: if measurement voltage: step_result[voltage] controller.measure_voltage() elif measurement current: step_result[current] controller.measure_current() results.append(step_result) time.sleep(step.duration) return results def save_to_file(self, filename): with open(filename, w) as f: json.dump([step.__dict__ for step in self.steps], f, indent4) classmethod def load_from_file(cls, filename): sequence cls() with open(filename) as f: data json.load(f) for item in data: sequence.add_step(TestStep(**item)) return sequence4.2 数据记录与分析实现专业级数据记录功能支持多种导出格式import pandas as pd from datetime import datetime class DataLogger: def __init__(self): self.data [] self.columns [timestamp, step, channel, voltage, current, temperature] def add_record(self, record): self.data.append(record) def to_dataframe(self): return pd.DataFrame(self.data, columnsself.columns) def export_to_csv(self, filename): df self.to_dataframe() df.to_csv(filename, indexFalse) def export_to_excel(self, filename): df self.to_dataframe() with pd.ExcelWriter(filename) as writer: df.to_excel(writer, sheet_nameTestData, indexFalse) def generate_report(self, filename): df self.to_dataframe() report f 测试报告 生成时间: {datetime.now()} 总记录数: {len(df)} 测试时长: {df[timestamp].max() - df[timestamp].min()}秒 电压统计: {df[voltage].describe()} 电流统计: {df[current].describe()} with open(filename, w) as f: f.write(report)5. 系统集成与高级功能5.1 多线程处理为避免界面冻结将耗时操作放入工作线程from PyQt5.QtCore import QThread, pyqtSignal class WorkerThread(QThread): update_signal pyqtSignal(str) result_signal pyqtSignal(dict) def __init__(self, controller, sequence): super().__init__() self.controller controller self.sequence sequence def run(self): try: results self.sequence.run( self.controller, callbacklambda msg: self.update_signal.emit(msg) ) self.result_signal.emit({success: True, data: results}) except Exception as e: self.result_signal.emit({success: False, error: str(e)})5.2 异常处理与恢复增强系统鲁棒性的关键措施通信超时处理自动重试机制数据校验检查返回值合理性状态恢复意外中断后能安全恢复日志记录详细记录所有操作和异常import logging class RobustController: def __init__(self, base_controller): self.controller base_controller self.logger logging.getLogger(PowerSupply) def safe_execute(self, command, max_retries3): for attempt in range(max_retries): try: result self.controller.query(command) if not result: raise ValueError(空响应) return result except Exception as e: self.logger.warning(f尝试 {attempt1} 失败: {e}) time.sleep(0.5 * (attempt 1)) raise Exception(f执行命令 {command} 失败已达最大重试次数) def recover_from_error(self): self.logger.info(尝试恢复系统状态) try: # 发送复位命令 self.safe_execute(*RST) # 确认设备状态 idn self.safe_execute(*IDN?) self.logger.info(f设备恢复成功: {idn}) return True except Exception as e: self.logger.error(f恢复失败: {e}) return False5.3 插件系统设计为支持不同型号电源设备设计可扩展的插件架构from abc import ABC, abstractmethod class PowerSupplyPlugin(ABC): abstractmethod def connect(self, **kwargs): pass abstractmethod def disconnect(self): pass abstractmethod def set_voltage(self, channel, value): pass abstractmethod def set_current(self, channel, value): pass abstractmethod def measure_voltage(self, channel): pass abstractmethod def measure_current(self, channel): pass class IT6322BPlugin(PowerSupplyPlugin): def __init__(self): self.serial None def connect(self, port, baudrate9600): self.serial serial.Serial(port, baudratebaudrate) return self.query(*IDN?) is not None def query(self, command): self.serial.write(f{command}\n.encode()) return self.serial.readline().decode().strip() # 实现其他抽象方法... class PluginManager: def __init__(self): self.plugins {} def register_plugin(self, name, plugin_class): self.plugins[name] plugin_class def get_plugin(self, name, **kwargs): plugin_class self.plugins.get(name) if plugin_class: return plugin_class(**kwargs) raise ValueError(f未找到插件: {name}) # 使用示例 manager PluginManager() manager.register_plugin(IT6322B, IT6322BPlugin) power_supply manager.get_plugin(IT6322B, portCOM3)

更多文章