A continuación se presentan los conceptos básicos necesarios para poder realizar control remoto de un instrumento y adquisición programada.

Un instrumento de medición permite convertir una magnitud física que se desea medir en otra magnitud que pueda ser registrada o percibida por nuestros sentidos. Los instrumentos fueron variando a lo largo de la historia en la medida que avanza la tecnología.

  • Un ejemplo antiguo y analógico puede ser una balanza: transforma una fuerza (el peso) en un desplazamiento espacial que puede ser medido mediante una regla.
  • Con el advenimiento de la electrónica, se pasó a un esquema en el que la mayoría de los instrumentos transforman la magnitud medida a una señal eléctrica (una corriente o tensión), que luego es medida y registrada por otro instrumento (multímetro, osciloscopio, etc) y presentada en un pantalla.
    • magnitud a medirseñal de corriente / tensiónpantalla o registro en papel
  • En la actualidad, con la tecnología digital, cada vez es más habitual que los instrumentos entreguen directamente en forma digital (por un canal de comunicación de datos) el resultado de una medición.
    • magnitud a medirseñal de corriente / tensiónpantalla
    • magnitud a medirseñal de corriente / tensióncanal digital de datoscomputadora
    • magnitud a medircanal digital de datoscomputadora

Por ello, hoy en día se necesita conocer cómo es el proceso de digitalización y el de transmisión de datos para poder realizar una adquisición por computadora. La incorporación de la computadora al laboratorio permite entonces automatizar (programación mediante) el registro de datos o la realización de (algunas partes de) un experimento.

grafico

Instrumentación

El primer paso es conocer los instrumentos que se van a utilizar. Imaginemos que contamos con un osciloscopio y un generador de funciones. Debemos ir a buscar la información relevante de estos equipos.

En los manuales están los detalles técnicos de cada instrumento. Esto incluye detalles de la conversión Analógico/Digital de los equipos. Por ejemplo, en el manual del osciloscopio (pág 115):

grafico

Nos especifica:

  • Datos de la digitalización
    • Resolución “vertical” (de Voltaje): 8 bits → $2^8 = 256$ pasos de digitalización
    • Sample Rate: 1 GS/s, 1 Giga Sample son 1000 millones de datos por segundo (maximo)
    • Record Length: 2500 puntos, 2500 datos de 8 bits pueden ser registrados en el tiempo.
  • Datos de electrónica relevantes:
    • “Bandwidth” / Ancho de banda: 50 MHz , frecuencia de corte a partir de la cual se pierden armónicos. Es una limitación eléctrica (la limitación de digitalización es el SampleRate/2 , mucho mayor al BandWidth que reportan acá).
    • Impedancia de entrada: 1 MΩ

En el manual del generador de funciones (pag 11)

grafico

(pag 22)

grafico

Nos especifica:

  • Datos de la conversión A/D
    • “Waveform” / Forma de la Onda: hasta 8192 puntos (para definir la forma de onda) de 14 bits de resolución ($2^{14} = 16384$ pasos de digitalización)
    • Sample Rate: 125 MS/s, Hasta 125 Mega Samples por segundo son 125 millones de puntos por segundo (máximo)
    • Amplitud: los 14 bits se distribuyen en un rango desde 2 mVpp (mili Volts de pico a pico) hasta 20 Vpp para una carga “alta” (mucho mayor a 50 Ω ).
  • Datos de electrónica relevantes:
    • Impedancia de salida: 50 Ω

Luego, debemos conocer cuales son los canales de comunicación con ese instrumental. Existe una API estándar en la industria llamada VISA (Virtual instrument software architecture), que permite unificar en una sola interfaz las diferentes tecnologías de comunicación.

grafico

Cada sistema operativo y software o lenguaje de programación tiene alguna implementación propia de VISA (a veces debe ser instalada). En el caso de python hay que instalar pyvisa.

La mayoría de los instrumentos soporta comandos por SCPI, que es una sintaxis estándar para escribir instrucciones para instrumentos. Las instrucciones consisten en palabras y valores escritos en texto plano, cada una asociada a los diferentes valores que puede medir un instrumento o parámetros de configuración necesarios para su operación.

Por ejemplo, para adquirir el vector de números que representan el voltaje medido por el canal 1 de un osciloscopio se usan estos dos comandos:

Seleccionar el canal 1:

'DATA:SOURCE CH1'

leer los datos

CURV?

Para establecer la frecuencia de la función de onda de un generador de funciones en 50 Hz usamos este comando:

FREQ 50

Así, en nuestro lenguaje de programación, deberemos crear textos con estas instrucciones y enviarlas a cada instrumento mediante la API de VISA.

Control remoto en Python

Hay varios ejemplos de adquisición remota y control ya armados. Algunos están en repositorios compartidos, como los del profesor Hernan Grecco en GitHub.

Veamos dos ejemplos…

Osciloscopio

Importaremos la librería visa y usaremos el Resource Manager de la librería que nos permite conectarnos al equipo informándole la dirección. Luego, los métodos query() y write() nos permitirán enviar las instrucciones al equipo y traer la respuesta (en el caso de query()).

Vamos a usar las siguientes instrucciones extraídas del manual. Lo que está entre corchetes [] o en minúscula es opcional ponerlo.

Referencia Comando (ej) Función
DATa:SOUrce <wfm> DATA:SOU CH1 Selecciona el canal del osciloscopio
CURV? CURV? Pide los datos medidos del canal actual
HORizontal:MAIn:SCAle <escala> HOR:MAIN:SCA 5E-3 Fija la escala temporal del osciloscopio (en segundos)
WFMPRE:XZE?;XIN?;YZE?;YMU?;YOFF?; WFMPRE:XZE?;XIN?;YZE?;YMU?;YOFF?; Adquiere los datos de la escala de la pantalla del osciloscopio

El último comando son en realidad varios anidados. Permiten obtener la información necesaria para transformar los 2500 números enteros de 8 bits que adquirimos con CURV? en Volts con el espaciado en segundos que corresponda.

from matplotlib import pyplot as plt
from numpy import *
import numpy as np
import pyvisa as visa

print(__doc__)

# Cargamos el Resource Manager. El manejador de recursos VISA
rm = visa.ResourceManager()

# Informamos la dirección de acceso al osciloscopio (en este caso, por USB)
osci = rm.open_resource('USB0::0x0699::0x0363::C065089::INSTR')

# Con el método 'query()' podemos enviar instrucciones QUE TIENEN RESPUESTA
# Por ejemplo, la instrucción que nos informa el nombre del instrumento
# al que nos conectamos
respuesta = osci.query('*IDN?')

# La instruccion '*IDN?' nos permite conocer a que instrumento nos conectamos
print(respuesta)


# Le pido algunos parametros de la pantalla, para poder escalear adecuadamente
xze, xin, yze, ymu, yoff = osci.query_ascii_values('WFMPRE:XZE?;XIN?;YZE?;YMU?;YOFF?;', separator=';')

#############################################################################
# Nota: Si falla la línea anterior, corroborá que el canal del osciloscopio #
# esté encendido. A veces es necesario que ambos estén encendidos para que  #
# funciones correctamente                                                   #
#############################################################################

# Con el método 'write' enviamos instrucciones QUE NO TIENEN RESPUESTA
# Modo de transmisión: Binario
osci.write('DAT:ENC RPB')
osci.write('DAT:WID 1')

# Adquiere los datos del canal 1 y los devuelve en un array de numpy
data = osci.query_binary_values('CURV?', datatype='B', container=np.array)

voltaje =(data-yoff)*ymu+yze;
tiempo = xze + np.arange(len(data)) * xin

plt.plot(tiempo, voltaje )
plt.xlabel('Tiempo [s]')
plt.ylabel('Voltaje [V]')

Generador de funciones

Vamos a usar las siguientes instrucciones extraídas del manual. Lo que está entre corchetes [] o en minúscula es opcional ponerlo.

Referencia Comando (ej) Función
[SOURce[1|2]]:VOLTage <amplitude> VOLT 0.5 Cambiar la amplitud pico a pico (en Volts)
[SOURce[1|2]]:FREQuency <frequency> FREQ 2000 Cambiar la frecuencia (en Hz)
[SOURce[1|2]]:VOLTage[:LEVel][:IMMediate]:OFFSet <voltage> VOLT:OFFS 0.3 Cambiar la tensión del centro de la funcion de onda.

Vamos a realizar distintos tipos de barridos sobre parámetros del generador de funciones. Cuando no especificamos el canal, se asumo que es el SOURCE1.

import time
from numpy import *
import numpy as np
import pyvisa as visa

# Cargamos el Resource Manager. El manejador de recursos VISA
rm = visa.ResourceManager()

# Informamos la dirección de acceso al osciloscopio (en este caso, por USB)
fungen = rm.open_resource('USB0::0x0699::0x0346::C033250::INSTR')

# Con el método 'query()' podemos enviar instrucciones QUE TIENEN RESPUESTA
# Por ejemplo, la instrucción que nos informa el nombre del instrumento
# al que nos conectamos
print(fungen.query('*IDN?'))

# Para enviar instrucciones que no entregan una respuesta se usa
# el método 'write()'

# Vamos a generar valores de frecuencias con una separación logarítmica
# vamos de 10^1 a 10^3 , con 20 pasos
for freq in logspace(1, 3, 20):
    fungen.write('FREQ {:f}'.format(freq) )
    print('Comando enviado: ' + 'FREQ {:f}'.format(freq)  )
    time.sleep(0.1)  # tiempo de espera de 0.1 segundos

# Rampa lineal de amplitudes
# Vamos a tener 10 pasos que van de 0 V a 1 V
for amplitude in np.linspace(0, 1, 10):
    fungen.write('VOLT {:f}'.format(amplitude) )
    print('Comando enviado: ' + 'VOLT {:f}'.format(amplitude)  )
    time.sleep(0.1)  # tiempo de espera de 0.1 segundos

# Rampa lineal de offset
# Vamos a tener 10 pasos que van de 0 V a 1 V
for offset in np.linspace(0, 1, 10):
    fungen.write('VOLT:OFFS {:f}'.format(offset)  )
    print('Comando enviado: ' + 'VOLT:OFFS {:f}'.format(offset)   )
    time.sleep(0.1)  # tiempo de espera de 0.1 segundos

# Cuando dejamos de usar el generador de funciones, lo cerramos
fungen.close()