Adquisición de datos con Arduino I: Tiempo de muestreo y Resolución

En la mayoría de proyectos que realizamos con Arduino necesitamos que la placa conozca magnitudes del mundo real, como pueden ser de luz o temperatura, para que esta active una serie de actuadores, como pueden ser motores o leds. Para ello las placas de Arduino cuentan con entradas analógicas con las que, a través, de sensores, medimos dichas magnitudes.

Las dudas vienen, cuando nos planteamos que un ordenador o microcontrolador en este caso, es un sistema digital y las magnitudes que deseamos medir son analógicas, por ello necesitamos un sistema que pase de analógico a digital, el cual llamaremos ADC (Analog digital coverter). Como es lógico Arduino cuenta con un ADC.

Este conversor ADC va conectado a un multiplexor, para que así, con uno solo de estos podamos tener varías entradas, como es en el caso de las placas Arduino  que normalmente cuenta con 6 de estas. La Arduino MEGA en cambio cuenta con 16 entradas analógicas.

El tiempo de muestreo, es el tiempo que transcurres entre dos mediciones consecutivas, es fundamental para la adquisición de datos y se suele expresar en frecuencia. Siempre que midamos un sistema como puede ser el sonido, necesitamos que nuestra frecuencia de muestro sea superior a la frecuencia del sistema, de no ser así, la representación que obtengamos con nuestro muestreo, del sistema, no será correcta.

A continuación, haremos un programa con Arduino para calcular el tiempo de muestreo que tiene nuestra placa. Para este ejemplo solo requerimos de una placa Arduino y el código será el siguiente:

void setup(){

  Serial.begin(9600);
  muestreo();

}

void loop(){
}

void muestreo(){

  unsigned long time1=0;
  unsigned long time=0;
  Serial.println("*************************");
  Serial.println("ENSAYO TIEMPO DE MUESTRO:");
  Serial.println("*************************");
  for(byte i =0; i<4; i++){
    time1=micros();
    int A=analogRead(A0);
    time=micros()-time1;
    Serial.print(" Muestra: ");
    Serial.print(i+1);
    Serial.print(" Tiempo: ");
    Serial.println(time);
  }

}

Activando el serial monitor podremos ver los resultados de nuestro experimento:

captura

Excepto en la primera medida, las demás tiene un tiempo de muestreo de 112 micro segundos, lo que equivale a una frecuencia de muestreo de 8.928 kHz, podemos tomar por valido ya que estoy utilizando una placa Arduino UNO. A demás debemos tener en cuenta una pequeña perdida de tiempo que tenemos al guardar y calcular las variables time1 y time.




Otro factor a tener en cuenta del ADC es su resolución, para ello hablamos de los bits de resolución que tiene, los bits no permiten hacer combinaciones, cuantos mas tengamos mayor numero de combinaciones podremos hacer, cada una de estas combinaciones muestra una medida del sistema analógico, la cantidad de medidas que tenemos serán 2 elevado al numero de bits de resolución. Puesto que Arduino UNO, en mi caso, tiene un ADC de 10 bits de resolución tendrá 1024 combinaciones. La placa Arduino DUE por ejemplo cuenta con un ADC de 12 bits (4096  combinaciones).

Pero hay un segundo factor que interviene en la resolución, es el rango de medida, El rango de Voltae de las placas Arduino por defecto es de 5v o 3.3v. Con esto sabemos que en voltios tenemos una resolución igual a;

captura

 A continuación haremos un ejemplo con Arduino de la resolución midiendo el voltaje en un potenciómetro, iremos girando las manecilla del potenciómetro y veremos que conversión da el ADC y  a que corresponde en voltaje. Comprobaremos a demás que el paso calculado antes de 4.88mV es correcto.

captura

El código que utilizaremos es el siguiente:

int contador;

void setup(){
  
  contador=1;
  Serial.begin(9600);
  Serial.println("*****************");
  Serial.println("ENSAYO RESOLUCION");
  Serial.println("*****************");  
}

void loop(){

  muestreo();
  contador++;
}

void muestreo(){

    int A=analogRead(A0);
    float B= float(A)*5/1023;
    Serial.print(" Muestra: ");
    Serial.print(contador);
    Serial.print(" Medida Digital: ");
    Serial.print(A);
    Serial.print(" Medida Analogica: ");
    Serial.println(B, 5);

delay(1000);
  }

Una vez compilado, abrimos el serial monitor y comenzamos a rotar el potenciómetro para ver que sucede, si ha sido montado correctamente, os saldrá lo siguiente:

captura

A partir de estos datos reales podemos conocer el resolución tenemos  cuando trabajamos en el rango de voltaje de 0 a 5v, para ello cogemos dos muestras cuales quieras, por ejemplo la 21 y la 22 y hacemos lo siguiente:

captura

 

La resolución medida ha cuadrado con la teórica.

//

//

En ocasiones, utilizaremos sensores cuyo rango de voltaje no será igual a [0, 5v]. En primer lugar, tenemos que tener en cuenta, que no debemos introducir en la placa Arduino voltajes mayores de 5v o 3.3v, partiendo de esto podemos cambiar el límite superior del rango de voltaje. De manera interna o externa:

La manera interna de cambiarlo, es utilizando sowftware únicamente, para ello utilizaremos la función analogReference(). Esta función nos permitirá cambiar el límite superior del rango de voltaje, en la placa Arduino UNO por ejemplo, si introducimos como parámetro INTERNAL el límite de voltaje superior será de 1.1v. En la placa Arduino mega podemos introducir como parámetro INTERNAL1V1 y INTERNAL2V56 siendo el nuevo límite 1.1v y 2.56v respectivamente.

A continuación haremos un nuevo ejemplo análogo al anterior, con una placa Arduino utilizando la función interna analogReference para que su voltaje de referencia sea 1.1v. Para alimentar el potenciómetro requeriremos una fuente de 1.1v, para crearla utilizaremos un divisor de tensión y un seguidos de tensión:

captura

Una vez montado cargamos el siguiente programa, para verificar que funciona correctamente:

int contador;

void setup(){
  analogReference(INTERNAL);
  contador=1;
  Serial.begin(9600);
  Serial.println("******************");
  Serial.println("ENSAYO RESOLUCION");
  Serial.println("REFERENCIA INTERNA");
  Serial.println("******************");  
}

void loop(){

  muestreo();
  contador++;
}

void muestreo(){

    int A=analogRead(A0);
    float B= float(A)*1.1/1023;
    Serial.print(" Muestra: ");
    Serial.print(contador);
    Serial.print(" Medida Digital: ");
    Serial.print(A);
    Serial.print(" Medida Analogica: ");
    Serial.println(B, 5);

delay(1000);
  }

captura
Como hicimos antes podemos tomar dos muestras, la 19 y 20, por ejemplo y calcular la nueva resolución que tenemos:

captura

 

Conseguimos pues una resolución casi 5 veces mayor que la anterior.

Finalmente, queda la manera externa para cambiar el voltaje de referencia, para ello utilizaremos el pin AREF. El funcionamiento de este pin es sencillo el voltaje que introduzcamos por el, será la nueva referencia, pero cuidado, dicho voltaje debe estar entre entre 1.1v y 5v.

Para el siguiente ensayo utilizaremos la configuración anterior,  generaremos con el divisor de tensión y el seguidor de tensión 2.5v, y los llevaremos al pin AREF, el resto lo dejaremos igual. Respecto al código utilizaremos el siguientes:

int contador;

void setup(){
  analogReference(EXTERNAL);
  contador=1;
  Serial.begin(9600);
  Serial.println("******************");
  Serial.println("ENSAYO RESOLUCION");
  Serial.println("REFERENCIA EXTERNA");
  Serial.println("******************");  
}

void loop(){

  muestreo();
  contador++;
}

void muestreo(){

    int A=analogRead(A0);
    float B= float(A)*2.5/1023;
    Serial.print(" Muestra: ");
    Serial.print(contador);
    Serial.print(" Medida Digital: ");
    Serial.print(A);
    Serial.print(" Medida Analogica: ");
    Serial.println(B, 5);

delay(1000);
  }

Activamos el Serial monitor y vemos los resultados:

captura

 

Con esto damos por terminado el primer tutorial de adquisión de datos con Arduino. Tened siempre en cuenta el tiempo de muestreo que tenéis y tratad siempre una buena resolución, ajustada al rango de voltaje que trabaja vuestro sensor. Hasta otra.

24 Comments on "Adquisición de datos con Arduino I: Tiempo de muestreo y Resolución"

  1. Cordial saludo
    quisiera saber cómo tomo el tiempo exacto para gratificar lo con la distancia que me arroja el sensor ultrasónico HC-SR04 estoy utilizando una arduino nano

    • Hola; en principio podrías comprobar cuanto tiempo tarda en recibir la señal de retorno cuando mandas la señal para mandar un pulso del sensor. Luego compararlo con lo que el propio sensor te da. Esperemos que esto resuelva tu duda.

      • Hola administración gracias por responder, éste es el código

        int Trig=A0;
        int Echo=A1;
        void setup() {
        Serial.begin(9600);
        pinMode(Trig,OUTPUT);
        pinMode(Echo,INPUT);

        }

        void loop() {
        float duracion;
        float distancia;

        digitalWrite(Trig,LOW);
        delayMicroseconds(4);
        digitalWrite(Trig,HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig,LOW);
        duracion=pulseIn(Echo,HIGH);
        duracion=duracion/2;
        distancia=duracion/29.2;
        Serial.println(distancia);
        delay(50);
        }

        pero mi pregunta es como saco el tiempo real para gratificarlo con la distancia…

        • Hola de nuevo. Tu código parece correcto pero si quieres obtener el real te recomiendo utilizar una librería como Ultrasonic. En esta librería puedes ver que posee una función llamada timig. Esta función permitiría obtener el tiempo en milisegundos que tarda el pulso en ir y volver.

          Si quieres saber como usarla tenemos un tutorial en BooleanBite usandola.

  2. Probé el primer codigo para medir el tiempo entre muestra y muestra, sin embargo, me arroja un error en el primer ciclo: for(byte i =0; i<4; i++) justo en (i<4;). ¿Podrías explicarme cual es la bronca?
    -De antemano muchas gracias a todos. 🙂

    • Ahora que lo pegué aquí me corrigió, creo que era cosa de que la pagina o mi compu en lugar del simbolo (<) me arrojaba caracteres raros.

    • Mira que en tu código en vez de poner el símbolo de i<4 y no “i&lt” que salió por defecto al actualizar el artículo jajaja perdona las molestias

  3. Hola! Genial el artículo, muy didáctico e ilutrativo. Gracias. Es posible que haya un errata en el primer ejemplo en la fórmula de “resolución real”? a la hora de coger los valores pones “321 – 343” Un saludo!!!

  4. Hola, un muy buen tutorial que me ha servido para tener algo con que comenzar con Arduino.
    Ahora estoy tratando de muestrear una señal sinusoidal de 1khz (y luego tendré que hacer lo mismo con otra de 2Khz) la cual genero con un Generador de funciones y entra a través de uno de los pines analógicos. Necesito adquirir varias muestras de la señal (había pensado en 100 muestras por periodo) pero no consigo obtener muchas (aplicando el código que pones, sin delay, obtengo unas 10 ó 11). Me podrías ayudar con esto? supongo que podré lograrlo cambiando la frecuencia de muestreo o algo, pero no tengo idea de como hacerlo.

    Gracias!.

    PD-La placa que utilizo es una Arduino DUE.

    • Lo que te sucede es que el tiempo de muestreo de tu placa es de casi 10khz, que es diez veces mas rápido que la señal que estas muestreando, de modo que por cada ciclo de tu señal, tu ADC puede hacer solo 10 lecturas.

      Para hacer 100 lecturas necesitas de una frecuencia de muestreo 100 veces mayor que tu señal, osea unos 100kHz, por desgracia muy lejos de la frecuencia de muestreo de Arduino.

      Saludos

    • entonces a que velocidad envia un pulso arduino??
      GRACIAS

  5. Muchas gracias por estos tutoriales tan bien explicados y tan extensos en ejemplos. ¡Se agradece!

    Sólo quería hacer una pregunta…¿existe alguna manera de aumentar la frecuencia de muestro de la placa UNO -que en teoría es de aprox. 9600KHz aunque a ti te sale algo menor- ? Me vale cualquier método, ya sea hardware o software.

    ¡Gracias!

    • A nivel placa arduino no, porque es una imposición del diseño, necesitarías una velocidad de reloj mayor, para que el micro-controlador trabajara mas rápido. Por otro lado supongo que existirá alguna shield para grabar audio y cosas así que tengan una velocidad de muestreo mas alta y vaya enviando la información al Arduino.

  6. Otra preguntilla que no me ha quedado clara:

    Tiempo de muestreo – Siempre que midamos un sistema como puede ser el sonido, necesitamos que nuestra frecuencia de muestro sea superior a la frecuencia del sistema.

    frecuencia de muestro > frecuencia de sistema

    Bien, el resultado obtenido en tu ejemplo es:
    Tiempo de muestreo = 112 micro segundos
    Frecuencia de muestreo = 1/112 = 0,00892 = 8,92 kHz

    Bien y ahora tu conclusión; podemos tomar por valido ya que estoy utilizando una placa Arduino UNO!

    frecuencia de muestro > ?????

    Mi pregunta, cómo sabemos la frecuencia de sistema del Arduino UNO (o cualquiera otro). Guarda alguna relación con la frecuencia de lectura del puerto serie? ( Serial.begin(9600); )

    En el caso de que la frecuencia de muestreo fuera menor, como solventariamos el problema? Necesitariamos un hardware más potente en al que la frecuencia de sistema es mayor o se puede ajustar de alguna manera?

    Gracias por adelantado.

    • Efectivamente la frecuencia de muestreo analógica no es suficiente para audio, aunque si se puede utilizar para medir nivel de volumen, si se esta hablando en voz alta o susurrando… en internet utilizan otros sistemas secundarios como puede ser un móvil o algún shield específico que habrá ya.

      Por lo de la transmisión del puerto serie no te preocupes no altera la del Arduino, el micro-controlador 328p, que es el grande que puedes quitar por lo general en formato dip-28, envía esta información / datos, a otro superficial que se ve cerca del usb, que es el que se encarga de la comunicación serie y ya va haciendo la transmisión por su cuenta.

      • Gracias por tu contestación Juan Carlos, de lujo. Pero aún hay algo que no se me queda claro del todo.

        Si me remito a tu tutorial tú dices: <>

        Hacer mención que es “válido” ya que utilizamos un Arduino UNO pero a qué frecuencia de sistema funciona el UNO ? (He leído por ahí q funciona a 16Mhz) Si esto es así, no se cumple la regla de que
        8,928kHz > 16MHz por lo que arduino, como me has comentado en tu respuesta, no es válido para muestrear sonido, correcto? (Perdona que pregunte tanto pero trato de entender esto xq me parece muy interesante) Gracias

        • Ahí te has confundido no se bien en que punto pero te contesto globalmente, has puesto que 8,928 KHz es mayor que 16MHz (“8,928kHz > 16MHz”):

          Si realmente querías expresar eso, tu confusión está en el significado de kilo(K), que significa que el número al que a compaña se multiplica por 10 elevado a 3, y mega(M), que significa que el número al que acompaña se multiplica por 10 elevado a 6, como puedes comprobar entonces 8,928kHz < 16MHz sería lo correcto.

          O realmente querías poner 8,928kHz < 16MHz, y lo que no comprendres muy bien es, porque teniendo una frecuencia tan superior en el reloj del micro-controlador la de muestro no es cercana o semejante. Eso es totalmente normal, el micro-controlador funciona por instrucciones, dichas instrucciones requieren de varios ciclos de reloj para llevarse acabo y a su vez la tarea de tomar un valor, conlleva al uso de varias instrucciones por eso y algunas otras cosas más…(xD), la frecuencia de muestreo es muy inferior a la del reloj del micro-controlador.

          Saludos

          • Hola Juan Carlos, joe, vaya debate nos estamos echando. Mira al final después de darle tanta vuelta se me ha quedado clarísimo, jaja. Pero comentarte, que lo que he tratado de explicarte (quizás no de la forma más clara) es que en tu tutorial hay una incongruencia. Voy a tratar de explicarlo de nuevo:

            Al principio del artículo, se afirma que siempre que midamos un sistema como puede ser el sonido, necesitamos que nuestra frecuencia de muestro sea superior a la frecuencia del sistema. Lo que viene a ser: Fmuestreo > Fsistema
            Luego los resultados obtenidos para cada una de estas variables es: 8,928kHz > 16MHz
            Conclusión: evidentemente la Fmuestreo obtenida no es mayor que la Fsistema, por lo tanto no se cumple la teoría inicial de que siempre debe ser mayor la Fmuestreo que la Fsistema.
            Tu conclusión: <<“válido” ya que utilizamos un Arduino UNO>> . Esto es lo q no veo, cómo que válido, si ateniendonos a los resultados, la fórmula no se cumple.
            Mi conclusión: creo que hay un error en la afirmación de que la frecuencia de muestreo deber ser “”menor”” que la del sistema.

            Solo eso, pero vamos, Juan Carlos, que te estoy dando el coñazo que no veas. Bueno, sin más, un saludo y gracias por los tutos. Me gusta un huevo tu blog.

          • Aunque vaya tarde, corregidme si me equivoco, creo que lo que el autor quería decir con la afirmación de que la frecuencia de muestreo tiene que ser mayor a la del sistema para que sea válida esque el “sistema” no es el “Arduino” sino el medio a medir, ejemplo: si es temperatura y se producen cambios de temperatura cada segundo, la frecuencia de muestreo tiene que ser mayor a un segundo.

  7. Lo mismo que el comentario anterior. Muy buen tutorial. Pero tengo una pregunta como novato que soy, entonces siempre que tengamos un sensor que nos de valores analógicos, debemos siempre ajustar los valores obtenidos al rango de voltaje con los que trabaja el sensor? (En qué casos se debe aplicar estos ajustes de resolución) Podría alguien explicar un caso práctico?

    • Buenas, el ajuste del rango de voltaje en el muestreo sirve para ganar resolución, la resolución que necesites viene ligada al tipo de proyecto que quieres hacer. Si necesitas que tenga una precisión de +-10mv, para empezar necesitas una resolución menor a este valor y luego medir correctamente ya que precisión y resolución no es lo mismo xD. Normalmente cuanta mas resolución tengas, mayores ventajas tendrás.

      Un ejemplo seria con el típico sensor lm35 de temperatura, este integrado cuenta con un pin en el que su voltaje varia 10mv por cada grado centigrado, así por ejemplo, si estas a 20 grados, en su salida tendrás 0,2v… La máxima temperatura que puede medir este dispositivo en dicha configuración es de 150 ºC que en voltaje sería un voltio y medio, como puedes comprobar se estaría desperdiciando mucho rango de voltaje, en este caso sería interesante cambiar a analogReference( INTERNAL ) y tener un rango que llegue solo hasta 1.1v.
      De este modo solo podrías medir hasta 110ºC pero sería los mas rápido, si quisieras poner otro rango ya deberías hacerlo de manera externa.

      Saludos

  8. Victor Alfonso Arias | Septiembre 8, 2015 en 11:53 pm | Responder

    Tu trabajo es excelente, muy informativo, tanto que vale la pena comentarte, muchas gracias por la info.

Deja un comentario.

Tu dirección de correo no será publicada.


*


*