<think>我们根据用户需求,使用Python控制Rigol示波器(特别是DS1000系列)并保存单通道电压数据到CSV文件。
步骤包括:连接示波器、设置通道参数、获取波形数据、转换数据、保存为CSV。
注意:代码中需要根据实际示波器型号调整资源字符串(VISA地址)和命令。
我们将按照以下步骤进行:
1. 使用pyvisa建立与示波器的连接
2. 配置示波器通道参数(通道1)
3. 设置触发模式为单次触发,并启动采集
4. 获取波形数据(原始字节数据)
5. 将原始数据转换为实际电压值(需要查询示波器的垂直设置)
6. 生成对应的时间轴数据
7. 将时间和电压数据写入CSV文件
注意:数据转换公式参考Rigol示波器的编程手册,对于DS1000系列,电压转换公式通常为:
电压值 = [(原始数据值 - 垂直偏移值) * 垂直刻度系数] - 垂直偏移
但实际编程手册中给出的公式可能是:电压 = (data - (YORigin - YREF)) * YINCrement
然而,根据经验,对于Rigol DS1000系列,我们通常使用以下步骤:
1. 读取垂直刻度(:CHAN1:SCAL?)、垂直偏移(:CHAN1:OFFS?)和探头比(:CHAN1:PROB?)
2. 原始数据是8位(0-255),其中128代表零电平
3. 转换公式:voltage = (data_value - 128) * (volt_scale * probe_ratio) / 25 - volt_offset
但是,请注意:不同的示波器型号可能有不同的转换公式,请以实际手册为准。
另一种更通用的方法是使用示波器自带的波形参数查询命令:
YINCrement = float(scope.query(':WAV:YINC?')) # 每个垂直单位的Y增量
YORigin = float(scope.query(':WAV:YOR?')) # 垂直原点
YREFerence = float(scope.query(':WAV:YREF?')) # 垂直参考值(通常是128)
则电压值 = (data_value - YREFerence - YORigin) * YINCrement
但为了简单和兼容性,我们采用第一种方法(也是Rigol常见应用笔记中的方法)。
时间轴数据:
总时间 = 时基(秒/格) * 12格(因为水平方向有14格,但通常数据点对应12格?实际上,我们需要查询采样率和点数)
实际上,我们可以通过以下命令获取时间增量:
XINCrement = float(scope.query(':WAV:XINC?'))
然后生成时间轴:time_axis = [i * XINCrement for i in range(len(data))]
然而,我们也可以手动计算:
时基 = time_scale = float(scope.query(':TIM:SCAL?')) # 秒/格
水平方向格数一般为12(注意:示波器屏幕水平方向是14格,但波形数据通常对应12格?)
采样点数 = len(data)
时间步长 = (time_scale * 12) / (采样点数) # 因为12格是时间窗口
但是,更准确的做法是使用示波器返回的X增量(:WAV:XINC?)。
因此,我们采用查询XINC的方法。
步骤总结:
1. 连接设备
2. 停止示波器(:STOP)以确保设置
3. 设置通道参数(耦合、探头、垂直刻度等)
4. 设置时基
5. 设置触发为单次(:SINGLE)并等待采集完成
6. 设置波形源为通道1
7. 设置波形模式为RAW(原始数据),格式为BYTE(字节)
8. 查询垂直参数(SCAL, OFFS, PROB)和水平参数(时基)或者直接查询波形参数(:WAV:YINC?, :WAV:YREF?, :WAV:YOR? 以及 :WAV:XINC?)
9. 读取波形数据(二进制)
10. 转换数据:电压值
11. 生成时间轴
12. 写入CSV
我们选择使用查询波形参数的方法(步骤8中的第二种),因为这样更直接。
具体代码实现如下:
</think>
# Python控制Rigol示波器保存单通道电压测量结果到CSV
以下是使用Python控制Rigol示波器(如DS1000系列)并将单通道电压测量结果保存为CSV文件的完整方法:
## 完整代码实现
```python
import pyvisa as visa
import csv
import time
import numpy as np
# 1. 连接示波器
def connect_oscilloscope():
rm = visa.ResourceManager('@py') # 使用pyvisa-py驱动
try:
# 查找连接的Rigol设备 - 实际ID可能不同
resources = rm.list_resources()
rigol_resources = [r for r in resources if 'Rigol' in r or 'DS' in r]
if not rigol_resources:
raise Exception("未检测到Rigol设备")
# 连接第一个检测到的Rigol设备
scope = rm.open_resource(rigol_resources[0])
scope.timeout = 10000 # 设置超时(ms)
print(f"已连接: {scope.query('*IDN?')}")
return scope
except visa.VisaIOError as e:
print(f"连接错误: {e}")
return None
# 2. 配置示波器参数
def configure_oscilloscope(scope, channel=1):
scope.write('*RST') # 重置示波器
time.sleep(1)
# 基本通道配置
scope.write(f':CHAN{channel}:DISP ON') # 开启通道
scope.write(f':CHAN{channel}:COUP DC') # 直流耦合
scope.write(f':CHAN{channel}:PROB 10') # 探头衰减比(10X)
# 自动设置垂直范围(可选)
scope.write(':AUT')
time.sleep(2) # 等待自动设置完成
# 设置单次触发模式
scope.write(':TRIG:MODE EDGE')
scope.write(':TRIG:EDG:SOUR CHAN1')
scope.write(':SINGLE') # 单次触发模式
# 等待采集完成
while int(scope.query(':TRIG:STAT?')) != 1:
time.sleep(0.1)
# 3. 获取波形数据
def get_waveform_data(scope, channel=1):
# 设置波形参数
scope.write(f':WAV:SOUR CHAN{channel}')
scope.write(':WAV:MODE RAW') # 原始数据模式
scope.write(':WAV:FORM BYTE') # 二进制格式
# 获取波形参数
volt_scale = float(scope.query(f':CHAN{channel}:SCAL?')) # 垂直刻度(V/div)
volt_offset = float(scope.query(f':CHAN{channel}:OFFS?')) # 垂直偏移(V)
probe_ratio = float(scope.query(f':CHAN{channel}:PROB?')) # 探头衰减比
sample_rate = float(scope.query(':ACQ:SRAT?')) # 采样率
# 获取原始数据
raw_data = scope.query_binary_values(':WAV:DATA?', datatype='B', container=np.array)
# 电压转换公式: (原始值 - 128) × 垂直刻度 × 探头比 / 25 - 垂直偏移
voltage = (raw_data - 128) * volt_scale * probe_ratio / 25 - volt_offset
# 生成时间轴
time_axis = np.arange(len(voltage)) / sample_rate
return time_axis, voltage
# 4. 保存为CSV文件
def save_to_csv(time_axis, voltage, filename=None):
if not filename:
filename = f"oscilloscope_data_{time.strftime('%Y%m%d_%H%M%S')}.csv"
with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['Time(s)', 'Voltage(V)']) # 表头
writer.writerows(zip(time_axis, voltage)) # 写入数据
print(f"成功保存 {len(voltage)} 个数据点到 {filename}")
return filename
# 主程序
if __name__ == "__main__":
# 连接示波器
scope = connect_oscilloscope()
if not scope:
print("无法连接示波器,请检查连接")
exit(1)
try:
# 配置示波器
configure_oscilloscope(scope, channel=1)
# 获取波形数据
time_axis, voltage = get_waveform_data(scope, channel=1)
# 保存为CSV
csv_file = save_to_csv(time_axis, voltage)
print(f"采集完成! 文件已保存为: {csv_file}")
finally:
# 清理连接
scope.close()
```
## 关键技术说明
### 1. 电压值转换原理
Rigol示波器的原始数据需要转换为实际电压值,转换公式为:
$$V = (D - 128) \times \frac{Scale \times Probe}{25} - Offset$$
其中:
- $D$ 是原始数据值(0-255)
- $Scale$ 是垂直刻度(V/div)
- $Probe$ 是探头衰减比
- $Offset$ 是垂直偏移(V)
### 2. 时间轴生成
时间轴基于采样率计算:
$$t_n = \frac{n}{SR}$$
其中:
- $n$ 是采样点序号(0,1,2,...)
- $SR$ 是采样率(samples/s)
### 3. 自动优化功能
代码包含自动设置功能(`:AUT`命令),示波器会自动调整垂直和水平设置以适应信号特征[^1]。
## CSV文件格式示例
```
Time(s),Voltage(V)
0.000000,0.012
0.000001,-0.008
0.000002,0.025
0.000003,0.038
...
0.010000,-0.215
```
## 使用说明
1. 安装依赖库:
```bash
pip install pyvisa pyvisa-py numpy
```
2. 连接Rigol示波器到计算机(USB或LAN)
3. 运行脚本
4. 查看生成的CSV文件(默认名称包含时间戳)
## 高级功能扩展
### 1. 添加元数据到CSV头部
```python
def save_to_csv(time_axis, voltage, scope, filename=None):
# ...其他代码...
with open(filename, 'w', newline='') as csvfile:
# 写入元数据
csvfile.write(f"# 设备: {scope.query('*IDN?')}\n")
csvfile.write(f"# 采样率: {scope.query(':ACQ:SRAT?')} Sa/s\n")
csvfile.write(f"# 垂直刻度: {scope.query(':CHAN1:SCAL?')} V/div\n")
csvfile.write(f"# 探头衰减比: {scope.query(':CHAN1:PROB?')}X\n")
csvfile.write(f"# 触发源: {scope.query(':TRIG:EDG:SOUR?')}\n")
csvfile.write(f"# 采集时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
# 写入数据
writer = csv.writer(csvfile)
# ...
```
### 2. 自动量程优化
```python
def auto_range_optimization(scope, channel=1):
# 先采集一次数据
_, voltage = get_waveform_data(scope, channel)
# 计算峰峰值
vpp = np.max(voltage) - np.min(voltage)
# 计算理想垂直刻度(80%屏幕高度)
ideal_scale = vpp / 6.4 # 6.4 div = 80% of 8 div
# 设置最接近的标准量程
standard_scales = [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5]
best_scale = min(standard_scales, key=lambda x: abs(x - ideal_scale))
scope.write(f':CHAN{channel}:SCAL {best_scale}')
print(f"自动优化垂直量程至 {best_scale} V/div")
```
### 3. 批量采集多组数据
```python
def batch_capture(scope, num_captures=5):
for i in range(num_captures):
configure_oscilloscope(scope)
time_axis, voltage = get_waveform_data(scope)
save_to_csv(time_axis, voltage, filename=f"capture_{i+1}.csv")
print(f"完成采集 #{i+1}")
time.sleep(1) # 等待间隔
```
## 注意事项
1. 确保已安装Rigol USB驱动(如需要)
2. 不同型号示波器可能需要调整命令语法
3. 对于大容量数据采集,增加超时时间(scope.timeout)
4. CSV文件可直接导入Excel、MATLAB或Python进行数据分析[^1]