如何在不不断写入磁盘的情况下高频读取和累积传感器值(RPi 2 b+、MCP3304、Python)

How to read & accumulate sensor values at high frequency, without constantly writing to disk (RPi 2 b+, MCP3304, Python)

我正在尝试以约 10kHz 的速率连续三次使用 Raspberry Pi 2 Model B+ to read analog data on IR intensity from a photodiode via an MCP3304 (5v VDD)(读数之间 0.1 毫秒,或 10 ksps 的速率)每秒一次,基于外部刺激,平均这些值,然后将它们与当前日期时间一起写入文本文件。光电二极管只是将数据读出到放大器,然后将其馈送到 MCP3304,后者通过 SPI 将数据馈送到 RPi。 (本质上:RPi 接收数字输入,通过 MCP3304 和在线信号放大器从光电二极管触发三个连续样本。这三个样本存储在内存中,取平均值,然后与日期时间戳一起写入磁盘到现有 CSV文本文件。)这一切都在 Python 2.7.

就目前而言,我使用以下代码 ( SensorRead() ) 获得 < 1kHz 的采样。我是 Python 的新手(并且使用传感器和 RPis,就此而言!),想想我设置我的 class 以获取三个独立的连续 ADC 样本的方式,可能还有我的设置因为写入磁盘可能会减慢我的速度。但是,我似乎找不到更好的方法。 Edit1: 我通过 RPi GPIO 的 Python 对最大采样率做了很多研究,它似乎远高于 ADC 在 ~ 1MHz 或~ 1000 ksps(例如 1,2)。 Edit2: 也许 100 ksps 的 ADC 最大采样率实际上是指可以读取多少位而不是每秒可以获取多少个完整的 12 位样本?

是的。就是这样。 MCP3304 可以达到 100ksps,但 Python 读取速率接近 30ksps,当在 MCP3304 每次迭代读取的 24 位之间拆分时,接近 1ksps



我的两个问题:
1) 是否有更好的方法来接近 MCP3304 规范中宣传的完整 100 ksps sheet? This link 建议每次我想获取单个样本时调用 WiringPi 可能会导致相当大的延迟。

2) 是否有可能,以 beginner/moderate 水平的 Python 技能让我在内存中完成所有这些采样和每秒平均,并且仅写入磁盘,例如,每分钟一次? 编辑:能否请您指出一些相关的方向 links/resources?

谢谢!

注1:代码是"Threaded"因为同时还有一些其他的功能运行。 注 2:我也在同时读取 ADC 上的差分通道,因此 MCP3304 命令中的 "differential = True"

'''
FILENAME = "~/output_file_location/and/name.txt"
adc_channel_pd = pin of the ADC from which analog signal is taken
stimulus_in_pin = the the pin that receives the alert to begin sampling
stimulus_LED_alert_pin = pin that goes "high" to illuminate an LED every time the stimulus_in_pin is triggered
Vref = the reference voltage for the ADC (3.3v; VDD = 5V)

'''

# import packages
import wiringpi2 as wiringpi
import time
from gpiozero import MCP3304
import threading
import datetime

# Define important objects
Vref = 3.3
adc_channel_pd = 7
stimulus_in_pin = 32
stimulus_LED_alert_pin = 16

# establish GPIO reading structure
wiringpi.wiringPiSetupPhys()

# set appropriate pin inputs and outputs (0 = input, 1 = output)
wiringpi.pinMode(stimulus_in_pin, 0)
wiringpi.pinMode(stimulus_LED_alert_pin, 1)

# create a class to take 3 PD readings, then average them, immediately upon stimulus
class SensorRead(threading.Thread):
    def __init__(self):
        super(SensorRead, self).__init__()
        self.daemon = True
        self.start()
    def run(self):
        for i in itertools.count():
            if (wiringpi.digitalRead(stimulus_in_pin) == True):
                val_ir_1 = MCP3304(adc_channel_pd, True).value * Vref)
                val_ir_2 = MCP3304(adc_channel_pd, True).value * Vref)
                val_ir_3 = MCP3304(adc_channel_pd, True).value * Vref)
                voltage_ir = round(  (float( (sum([val_ir_1,val_ir_2,val_ir_3])) / 3)) , 9)
                dt_ir = '%s' % datetime.datetime.now()
                f = open(FILENAME, "a")
                f.write("IR Sensor," + dt_ir + "," + str(voltage_ir) + "\n")    
                f.close()
                # print to terminal so I can verify output in real time
                print "IR Sensor:", dt_ir,",",voltage_ir
                # blink ir light on board for visual verification of stimulus in real time
                wiringpi.digitalWrite(stimulus_LED_alert_pin, 1)
                time.sleep(0.5)
                wiringpi.digitalWrite(stimulus_LED_alert_pin, 0)
                # sleep to avoid noise post LED firings
                time.sleep(0.5)

# run class
SensorRead()

编辑: 我最终使用 Cython 获得了一些不错的结果,如 所示。我还最终编写了自己的函数来从 MCP3304 中读取——我将 link 到它全部干净后——我能够在 Cython 中进一步优化。

关于你的问题的一点。每秒三个样本是 3Hz 的速率,而不是 100kHz。在我看来,您想要的是相隔 10us 的三个样本。

1) 使用 Pi 运行 Linux 在 MCP3304 上的 10us 采样周期?很可能不是。做一些搜索。参见示例 https://raspberrypi.stackexchange.com/questions/45386/microphone-via-spi where one answer says they achieved 33us (33ksps) using C code and avoiding the SPI driver. Also I suspect you will find Linux process switching and other threads getting in the way and affecting the sample rate particularly if they are also reading the ADC. This may be more likely if you have a dedicated non-Linux processor to read the ADC, programmed in C or assembly language, feeding the three samples to the Pi. Easier if you use a parallel ADC, i.e. not using SPI-like serial comms. See also http://www.hertaville.com/interfacing-an-spi-adc-mcp3008-chip-to-the-raspberry-pi-using-c.html and https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=83069

2) 虽然使用MCP3304在10us周期采样在Pi上很难做到,但平均和写入绝对是可能的。

不过,我有一个解决你的问题的方法:如果你对这三个样本所做的一切实际上就是对它们进行平均,为什么不在输入引脚之前添加一个老式的低通模拟滤波器和然后只取一个样本。嘿 presto,无需硬实时 ADCing,无需担心其他进程或内核中断!

我们最近针对不同频率下读取输入的准确性对 PIGPIO 和 RPi.GPIO 进行了基准测试。测试是在 Raspberry Pi 3b.

上进行的

我建议使用 PIGPIO 以获得更好的结果。在我们的测试中,读取准确率高于 99% 的库的最大读取频率为 20 KHz,而 Rpi.GPIO 库

的最大读取频率为 5KHz

您可以在此 post 上找到准确的测试设置和完整的结果:https://atman-iot.com/blog/raspberry-pi-benchmark/