Go Down

Topic: Aumentar la frecuencia de muestreo [SECUESTRADO] (Read 663 times) previous topic - next topic

difigueroa

Sep 27, 2018, 12:01 am Last Edit: Sep 27, 2018, 01:40 am by surbyte Reason: Agregado de etiquetas de código/errores
Hola a todos, he leído completamente la interesante discusión Aumentar la frecuencia de muestreo (Original) que se ha formado acá, y la verdad es que he intentado aplicarla a lo que requiero, hasta ahora sin resultados, estudio ingenieria mecánica y mi trabajo trata acerca del análisis de vibraciones en máquinas rotatorias, para lo cual necesito muestrear con un acelerómetro las vibraciones en 3 ejes, a una frecuencia de muestreo óptima de 4 KHz por eje, si ustedes me pudiesen guiar con la conformación del sketch seria de inmensa ayuda, les adjunto el sketch que tengo hasta ahora, con el cual alcanzo solo 1,6 KHz de velocidad.

Code: [Select]
void setup(){
 // Se inicia la comunicación serial
 Serial.begin(115200);
 analogReference(EXTERNAL);
}
void loop() {

 const uint8_t analog_pins[] = {A0,A1,A2};
while (millis () <= 1000)
// Se ingresan pines analógicos para leer
for (int i = 0; i < 3; i++) { //pines del 0 al 2
 Serial.print (analogRead(analog_pins[i]));
 Serial.print("\t");
 if (i == 2)
 Serial.println();
}  
}




Con este sketch lo que obtengo son 3 columnas, con las aceleraciones equivalentes a los ejes x,y,z.


PeterKantTropus

La estructura for , consume pasos, conviene hacer tres lecturas consecutivas y eliminar el For
Saludos
"Si no entra como tornillo, entra como clavo"

difigueroa

PeterKantTropus gracias por la respuesta, el problema es que también tengo un código que no utiliza el ciclo for, pero con este obtengo lecturas de 800 Hz aproximadamente, no se que herramientas me pueden servir para optimizar el código y hacer las lecturas mas rápidas.

Code: [Select]

const int xpin = A3;                  // eje x
const int ypin = A2;                  // eje y
const int zpin = A1;                  // eje z


void setup() {
 
  Serial.begin(115200);
  analogReference(EXTERNAL);
}

void loop() {
  // Espacio entre lecturas:
  Serial.print(analogRead(xpin));
  // Espacio entre lecturas:
  Serial.print("\t");
    Serial.print(analogRead(ypin));
  // Espacio entre lecturas
  Serial.print("\t");
  Serial.print(analogRead(zpin));
   Serial.println();
}




Lucario448

Asumiendo que es alguno de los Arduinos más populares, con esta línea acelerarás bastante las lecturas analógicas:
Code: [Select]
ADCSRA &= B11111000;

Si esto todavía no es suficiente, prueba lo que dice PeterKantTropus; pero no como lo tienes ahora.
Si aún no es suficiente, ya habría que pensar en interrupciones temporizadas.

difigueroa

Lucario448 estoy usando una Arduino Nano, con el procesador ATmega 328. Con respecto a tus consejos me surgen algunas dudas:

1- En que parte del código debo poner la instrucción que me sugeriste?

2- Con respecto a lo que me aconsejó PeterKantTropus, cual crees tú que debe ser la forma en la
    que debo plantear el código?.

Gracias  :)

difigueroa

Por otra parte, he estado intentando obtener mayor velocidad de otras formas:

1- He descargado la librería AnalogReadFast, sin embargo creo que le estoy dando un mal uso, pues no
    aprecio diferencia alguna en cuanto a velocidad de sampleo.

2- He obtenido el código que se ve abajo, con el cual logro una velocidad de 2,6 KHz, sin embargo esto
    es midiendo cada eje por separado, es decir, no simultáneo, lo cual constituye mi plan B puesto que si
    mido cada eje por separado no podre apreciar los desfases que existen entre las vibraciones de cada eje,
    lo que mecánicamente es muy interesante.

 
Code: [Select]

#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
  /*int start ;
  int i ; */

#if FASTADC
  // set prescale to 16
  cbi(ADCSRA, ADPS2) ;
  sbi(ADCSRA, ADPS1) ;
  sbi(ADCSRA, ADPS0) ;
#endif
  Serial.begin(115200) ;
  analogReference(EXTERNAL);

}

void loop() {
 
if (micros () <= 1000000)   
    Serial.println((analogRead(A0)));
if (micros() > 1000000 && micros () <= 2000000)
Serial.println((analogRead(A1)));
if (micros () > 2000000 && micros () <= 3000000)
Serial.println((analogRead(A2)));
   
}

PeterKantTropus

¿Como estas midiendo la frecuencia? No puedes utilizar la salida serial para ello, porque en ese caso el limitante seria la comunicación serial. En el ultimo ejemplo estas obteniendo mayor velocidad simplemente porque utilizas un tercio de la salida serial.
"Si no entra como tornillo, entra como clavo"

difigueroa

PeterKantTropus la frecuencia hasta ahora la estoy midiendo de la forma más básica que conozco, es decir, mido durante un tiempo fijo, con Realterm, y guardo los archivos en bloc de notas, los copio a Excel y luego divido el tiempo de medición entre el N° de datos obtenidos.

Gracias.

Mi plan es luego pasar esos datos a Matlab para hacer una FFT y obtener el espectro, pero mi espectro debe abarcar frecuencias entre [0 - 1500] Hz, por eso me propongo tener una frecuencia de muestreo de 3750 Hz óptimo (Nyquist es el caso limite, el factor que considero es 2,5 y no dos como lo recomienda el caballero)

surbyte

Primero para lograr lo máximo de un Arduino tienes que usar buffer, un buffer ciclico, guardar ahi las muestras y transferir en ráfagas si eso quieres.

Pero no puedes tomar datos y transferir porque es ineficiente.
Luego la mejor forma de tomar datos es poniendo el Ad en free wheeling o sea se pone a tomar datos a su máxima velocidad sin frenarse por nada.
Puedes incluso decirle que quieres hacerlo en varios pines A0...A5 pero esos datos deben ir a un buffer.


Lucario448

1- En que parte del código debo poner la instrucción que me sugeriste?
En algún punto del setup(), ya que es una configuración que se realiza una sola vez


2- Con respecto a lo que me aconsejó PeterKantTropus, cual crees tú que debe ser la forma en la
    que debo plantear el código?.
Hacer las lecturas primero, luego imprimir. Si intercalas ambas acciones, obviamente el muestreo se iba a ralentizar. Y ejecutar las tres lecturas consecutivamente; utilizar un ciclo for equivale a intercalar más instrucciones (que al final se vuelven retrasos) entre las lecturas.


Si eso no es suficiente, presta atención a surbyte. Aunque creo que al final sería necesario una interrupción temporizada, para que el muestreo no se vea retrasado por el proceso de impresión.

difigueroa

Gracias por los consejos, surbyte y Lucario448, buscaré la forma de hacer lo que me aconsejan en el sketch, tengo claro lo que quiero que el programa haga, otra cosa es que lo sepa escribir  :) .Leeré acerca de como implementar algún buffer que almacene las lecturas, si ustedes conocen alguna fuente de información que me permita aprender como usarlos se los agradeceré muchísimo.

ArduMyth

#11
Sep 27, 2018, 11:47 pm Last Edit: Sep 27, 2018, 11:50 pm by ArduMyth
Lo que te comentó lucario lo tienes explicado en algunas páginas en castellano. Esta no esta mal: https://aprendiendoarduino.wordpress.com/tag/adc/ Es como el port manipulation (no sé si has mirado algo de esto), por lo que a falta de práctica la lectura del código puede ser algo más compleja. Si bien es más eficiente es bajar un poco en el nivel de programación (que no indica que sea más fácil a sino que nos alejamos de la programación orientada a objetos)

Y respecto al buffer no sé si te vendrá bien la librería que aporta al final del post luis llamas en el buffer circular: https://www.luisllamas.es/buffer-circular-arduino/ pero es bastante útil en temas de memoria hacer el buffer circular.

En cualquier caso recuerda que cualquier Serial.println() afecta en los tiempos por insignificante que sea el retardo (esto puede verse usando micros())

Espero que te sirva de algo. Sé que es materia algo pesada de leer, pero repito porque estás buscando eficiencia por encima de "legibilidad" en el código.

PeterKantTropus

Arduino utiliza la configuración serial 8n1, 8 bits de datos mas uno de stop  (9 bits), ahora bien análogRead devuelve un valor de 0 a 1023 es decir  4 caracteres ( máximo) y utilizas un caracter de salto de página.
Por cada valor de lectura analógica utilizas 45 bits. A la velocidad de 115200 bits por segundos puedes transferir 2500 valores analógicos por segundo, es decir 2,5 KHz. Tu limitante no es la lectura, es la transferencia.
"Si no entra como tornillo, entra como clavo"

difigueroa

PeterKantTropus he aumentado el baud rate hasta el máximo y si bien me aumenta la frecuencia de muestreo, sigue siendo muy baja.

Entiendo lo que me dices, mi frecuencia estará limitada por los 115200 bits que puedo enviar, es decir, estoy leyendo a 2,5 KHz, y peor aún, como mido los 3 ejes simultáneos, cosa que requiero, en realidad estoy muestreando cada eje a 833 Hz. Al aumentar el baud rate a 2 millones debiese ser más que suficiente para lograr la velocidad que quiero, sin embargo, logro medir a 1,8 KHz por eje, es decir a 5,4 KHz.

Para solucionar la ineficiencia que genera el medir y enviar datos, pretendo hacer que el programa lea, almacene y luego envíe datos, como me lo sugirió Surbyte, con la ayuda de ArduMyth. Sin embargo quedo estancado en la parte del almacenaje, pues según lo que he leído, el buffer circular va eliminando datos a medida que se llena, y por otra parte, no se si la memoria del Arduino Nano pueda almacenar todos estos datos para enviarlos.

¿Conocen alguna solución que permita almacenar la cantidad de datos que requiero? (3 ejes a 3700 Hz cada uno)

difigueroa

Una solución aplicable, y la que estoy intentando implementar es la modificación del prescaler del ADC para dejarlo en 64, sin embargo, no sé si esto es válido para varios pines.

Luego de lograr la aceleración de la ADC y aumentando el baud rate hasta un número mayor a 506250 (suponiendo 45 bits por muestra, a una velocidad requerida por eje de 3750 Hz [45*3750*3 = 506250]) debiese poder medir a la velocidad que quiero. La piedra de tope es que no sé si puedo variar el prescaler para varios pines.

 

Go Up