tts - Cómo leer y acumular valores de sensores a alta frecuencia, sin escribir constantemente en el disco(RPi 2 b+, MCP3304, Python)
tts en python (1)
Estoy intentando usar un Raspberry Pi 2 Modelo B + para leer datos analógicos sobre la intensidad IR de un fotodiodo a través de un MCP3304 (5v VDD) a una velocidad de ~ 10kHz tres veces seguidas (0.1 ms entre lecturas, o una tasa de 10 ksps) una vez por segundo basado en un estímulo externo, promedie esos valores, luego escríbalos en un archivo de texto junto con la fecha actual. El fotodiodo simplemente lee los datos en un amplificador, que luego alimenta al MCP3304, que, a través de SPI, alimenta datos al RPi. (En esencia: RPi recibe una entrada digital, activando tres muestras consecutivas del fotodiodo a través de un MCP3304 y un amplificador de señal en línea. Estas tres muestras se almacenan en memoria, se promedian y luego se escriben en el disco junto con un sello de fecha y hora en un CSV existente archivo de texto.) Esto es todo en Python 2.7.
Tal como está, recibo una muestra de <1kHz con el siguiente código (SensorRead ()). Soy muy nuevo en Python (¡y juego con sensores y RPis, para el caso!), Y creo que la forma en que configuré mi clase para tomar tres muestras consecutivas de ADC consecutivas y posiblemente mi configuración para escribir en un disco puede estar ralentizándome abajo. Sin embargo, no puedo encontrar una mejor manera. Edit1: He realizado una buena investigación sobre las tasas máximas de muestreo a través de Python desde el RPi GPIO, y parece estar muy por encima de la restricción del ADC a ~ 1MHz o ~ 1000 ksps (por ejemplo, 1 , 2 ). Edit2: ¿ Quizás la tasa máxima de muestreo de ADC de 100 ksps realmente se refiere a cuántos bits se pueden leer en lugar de cuántas muestras completas de 12 bits se pueden tomar por segundo?
Sip. Esto fue. El MCP3304 puede hacer 100ksps, pero la velocidad de lectura de Python está más cerca de los 30ksps, que, cuando se divide entre los 24 bits leídos por el MCP3304 por iteración, está más cerca de 1ksps
Mis dos preguntas:
1) ¿Hay alguna forma mejor de acercarse a los 100 ksps completos anunciados en la hoja de especificaciones de MCP3304? Este enlace sugiere que llamar a WiringPi cada vez que deseo tomar una sola muestra puede causar una latencia considerable.
y
2) ¿es posible, con un nivel principiante / moderado de habilidad de Python para mí hacer todo este muestreo y promediado por segundo en la memoria, y solo escribir en el disco, por ejemplo, una vez por minuto? Editar: ¿podría indicarme algunos enlaces / recursos relacionados?
¡Gracias!
Nota 1: el código es "Enhebrado" porque hay otras funciones ejecutándose simultáneamente. Nota 2: También estoy, al mismo tiempo, leyendo un canal diferencial en el ADC, de ahí el "diferencial = True" en el comando MCP3304
''''''
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()
Editar: Terminé obteniendo excelentes resultados con Cython, como se demuestra en este código de prueba que escribí para cuantificar qué tan rápido podía leer mi ADC . También terminé escribiendo mi propia función para leer desde el MCP3304, que voy a vincular una vez que está todo limpio, que pude optimizar aún más en Cython.
Un punto sobre tu pregunta. Tres muestras en un segundo es una tasa de 3 Hz y no de 100 kHz. Me parece que lo que quieres son tres muestras a 10us de distancia.
1) 10us período de muestra en MCP3304 usando Pi corriendo Linux? Muy posiblemente no. Hacer un poco de búsqueda. Ver por ejemplo https://raspberrypi.stackexchange.com/questions/45386/microphone-via-spi donde una respuesta dice que lograron 33us (33ksps) usando el código C y evitando el controlador SPI. También sospecho que encontrará el cambio de proceso de Linux y otros hilos en el camino y que afectan la velocidad de muestreo, especialmente si también están leyendo el ADC. Esto puede ser más probable si tiene un procesador dedicado no Linux para leer el ADC, programado en C o lenguaje ensamblador, alimentando las tres muestras al Pi. Es más fácil si usa un ADC paralelo, es decir, no utiliza comunicaciones seriales SPI. Ver también http://www.hertaville.com/interfacing-an-spi-adc-mcp3008-chip-to-the-raspberry-pi-using-c.html y https://www.raspberrypi.org/forums/ viewtopic.php? f = 93 & t = 83069
2) Mientras que el muestreo en el período 10us usando el MCP3304 es difícil o imposible en Pi, el promediado y la escritura son definitivamente posibles.
Sin embargo, tengo una solución para su problema: si, literalmente, todo lo que va a hacer con las tres muestras es promediarlas, ¿por qué no agregar un filtro analógico de paso bajo pasado de moda antes del pin de entrada y luego simplemente tomar una muestra? . ¡Oye, presto, no hay necesidad de ADC duradero en tiempo real, no hay necesidad de preocuparse por otros procesos o interrupciones del kernel!