Tengo unos 32 segundos de datos de acelerómetro de un escenario de conducción básico de 25 MPH en carreteras normales junto con unos 7 baches y un tramo de carretera irregular. El acelerómetro está montado en el tablero de instrumentos de mi automóvil con cinta adhesiva de doble cara.
Problema: Tengo todos los datos que son ruidosos del acelerómetro, y necesito hacer una manera simple de detectar que ha ocurrido un evento de baches. A continuación hay varios gráficos de datos en el dominio del tiempo y FFT. El acelerómetro mide en GForce
Básicamente, quiero que mi arduino sepa que ha ocurrido un bache con bastante precisión y que no usa matemáticas y técnicas de nivel de posgrado.
El acelerómetro muestreado a 100 Hz tiene un simple FILTRO DE PASO BAJO RC DE 50 HZ EN EL EJE Z
Here is the CSV data for the 32 seconds of accelerometer readings TIME, GFORCE format:
http://hamiltoncomputer.us/50HZLPFDATA.CSV
ACTUALIZACIÓN: Este es el ancho de banda completo RAW del acelerómetro 1000HZ muestreado a la velocidad de muestreo más alta que pude obtener en Arduino. Descarga directa de archivos CSV: aproximadamente 112 segundos de datos
http://hamiltoncomputer.us/RAWUNFILteredFULLBANDWIDTH500HZ.csv
La traza negra es RAW sin filtrar datos del acelerómetro: la traza azul es filtrada por un filtro de detención de banda basado en las frecuencias extremas encontradas en FFT, Dominate 2HZ y 12HZ.
El evento de baches se ve así en el dominio del tiempo:
no estoy seguro de cuál es el componente de 10 a 15 HZ en el FFT, ¿es ese el bache real, o es el salto de las ruedas contra la carretera, o es la frecuencia de resonancia del automóvil?
FFT:
parece que son los eventos reales de baches, aquí hay un HPF @ 13HZ Las características dominantes de los baches parecen mejoradas
Quiero poder detectar y contar los baches en tiempo real
Parece ser contrario a la intuición, la suspensión debería moverse mucho más lento que un HZ de 10 a 13, lo que podría causar mareo.
ACTUALIZAR:
Según las sugerencias de AngryEE, utilicé el ancho de banda completo del acelerómetro 1000HZ y la velocidad de muestreo máxima que pude obtener en el arduino.
FFT:
Aquí hay una muestra de datos del evento de baches y algunos baches y ruido de la carretera a su alrededor:
Se agregó el circuito del detector de envoltura de diodo, la salida se ve igual ... El acelerómetro siempre emite 0 a 3.3Voltios no negativo ...
ACTUALIZAR:
De muchas pruebas en carretera, nunca excedí 1.6G de hasta 45 MPH en mi automóvil en el eje Z, usé rand () para generar una aceleración pseudoaleatoria de Gforce.
Mi idea es que si puedo ver ventanas de datos de 1 a 3 segundos, puedo calcular el desplazamiento del eje Z, pero estaba preocupado por la deriva del acelerómetro y los errores en la integración. No necesito ni siquiera un 90% de precisión aquí,> 70% sería bueno, pero si estoy viendo el desplazamiento de uno a tres segundos a la vez, ¿sería posible hacerlo en tiempo real? De esta manera puedo ver si el desplazamiento es mayor que como 1 pulgada, 2 pulgadas, 5 pulgadas. Cuanto mayor era el desplazamiento, más rugosa era la protuberancia o el bache:
¿Puede verificar si estoy haciendo esto correctamente? Básicamente lo configuré en mi escritorio, usando rand () para generar una aceleración aleatoria de -1.6 a 1.6 G, capturando 3 segundos de datos a una velocidad de muestreo simulada de 50 HZ
Si, como si ejecutaras * nix, estoy usando Sleep () desde Windows.h para hacer que la demora de 20 ms, la frecuencia de muestreo de 50 Hz
Solo quería ver si el código le parece correcto, todavía no hice el búfer cicular, estoy un poco confundido sobre cómo implementarlo: el código comentado es de la clase en la que estoy trabajando para ello , pero aún no lo entiendo al 100%. Un búfer circular permitiría mover contiguamente ventanas de datos, ¿verdad?
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <ctime> // USED BY RAND
#include <windows.h> // Used for delay
using namespace std;
#define SAMPLE_RATE 0.020 // Sample rate in Milliseconds
#define GRAVITYFT_SEC 32 // Gravity velocity 32 feet/sec
#define INCH_FOOT 12 // 12 inches in foot, from velocity to inch displacement calculation
int main(int argc, char *argv[])
{
srand((unsigned)time(0)); // SEED RAND() for simulation of Geforce Readings
// SIMULATING ACCELERATION READINGS INTO A CIRCULAR BUFFER
// circular_buffer Acceleration; // Create a new Circular buffer for Acceleration
// cb_init(&Acceleration, 150, 4); // Sampling @ 50HZ, 3 seconds of data = 150, size is float data of 4 bytes
//Simulate a sample run of Acceleration data using Rand()
// WE WILL BE SIMULATING "RANDOM" GEFORCE RATINGS using the rand() function constraining to -1.6 to 1.6 GFORCE
// These ratings are consistent with our road tests of apparently random vibration and Geforce readings not exceeding about 1.6 G's
float Gforce[150]; // Random Geforce for 3 second window of data
float velocity[150]; // Hold velocity information
float displacement[150]; // Hold Displacement information
float LO = -1.6; // Low GForce limit recorded from 6 road tests at different speeds
float HI = 1.6; // High GForce limit recorded from 6 road tests at different speeds
for(int i = 0; i < 150; i++) // 3 Second iwndow of random acceleration data
{
Gforce[i] = LO + (float)rand()/((float)RAND_MAX/(HI-LO)); // Borrowed from Stackexchange : http://stackoverflow.com/questions/686353/c-random-float
if( i == 0) // Initial values @ first Acceleration
{
velocity[i] = Gforce[i] * SAMPLE_RATE * GRAVITYFT_SEC; // Initial velocity
displacement[i] = velocity[i] * SAMPLE_RATE * INCH_FOOT; // Initial Displacement
}
else
{
velocity[i] = velocity[i-1] + (Gforce[i] * SAMPLE_RATE * GRAVITYFT_SEC); // Calculate running velocity into buffer
displacement[i] = displacement[i-1] +(velocity[i] * SAMPLE_RATE * INCH_FOOT); // Calculate running displacement into buffer
}
//cout << endl << Gforce[i]; // Debugging
//cb_push_back(&Acceleration, &Gforce[i]); // Push the GeForce into the circular buffer
Sleep(SAMPLE_RATE*1000); // 20mS delay simulates 50HZ sampling rate Sleep() expects number in mS already so * 1000
}
// PRINT RESULTS
for (int j = 0; j < 150; j++)
{
cout << setprecision (3) << Gforce[j] << "\t\t" << velocity[j] << "\t\t" << displacement[j] << endl;
}
// READ THE BUFFER
//cb_free(&Acceleration); // Pervent Memory leaks
system("PAUSE");
return EXIT_SUCCESS;
}
Ejecución de muestra:
GFORCE FT/SEC Inch Displacement Z axis
-0.882 -0.565 -0.136
0.199 -0.437 -0.24
-1.32 -1.29 -0.549
0.928 -0.691 -0.715
0.6 -0.307 -0.788
1.47 0.635 -0.636
0.849 1.18 -0.353
-0.247 1.02 -0.108
1.29 1.85 0.335
0.298 2.04 0.824
-1.04 1.37 1.15
1.1 2.08 1.65
1.52 3.05 2.38
0.078 3.1 3.12
-0.0125 3.09 3.87
1.24 3.88 4.8
0.845 4.42 5.86
0.25 4.58 6.96
0.0463 4.61 8.06
1.37 5.49 9.38
-0.15 5.39 10.7
0.947 6 12.1
1.18 6.75 13.7
-0.791 6.25 15.2
-1.43 5.33 16.5
-1.58 4.32 17.5
1.52 5.29 18.8
-0.208 5.16 20.1
1.36 6.03 21.5
-0.294 5.84 22.9
1.22 6.62 24.5
1.14 7.35 26.3
1.01 8 28.2
0.284 8.18 30.1
1.18 8.93 32.3
-1.43 8.02 34.2
-0.167 7.91 36.1
1.14 8.64 38.2
-1.4 7.74 40
-1.49 6.79 41.7
-0.926 6.2 43.2
-0.575 5.83 44.6
0.978 6.46 46.1
-0.909 5.87 47.5
1.46 6.81 49.2
0.353 7.04 50.8
-1.12 6.32 52.4
-1.12 5.6 53.7
-0.141 5.51 55
0.463 5.8 56.4
-1.1 5.1 57.6
0.591 5.48 59
0.0912 5.54 60.3
-0.47 5.23 61.5
-0.437 4.96 62.7
0.734 5.42 64
-0.343 5.21 65.3
0.836 5.74 66.7
-1.11 5.03 67.9
-0.771 4.54 69
-0.783 4.04 69.9
-0.501 3.72 70.8
-0.569 3.35 71.6
0.765 3.84 72.5
0.568 4.21 73.5
-1.45 3.28 74.3
0.391 3.53 75.2
0.339 3.75 76.1
0.797 4.26 77.1
1.3 5.09 78.3
0.237 5.24 79.6
1.52 6.21 81.1
0.314 6.41 82.6
0.369 6.65 84.2
-0.598 6.26 85.7
-0.905 5.68 87.1
-0.732 5.22 88.3
-1.47 4.27 89.4
0.828 4.8 90.5
0.261 4.97 91.7
0.0473 5 92.9
1.53 5.98 94.3
1.24 6.77 96
-0.0228 6.76 97.6
-0.0453 6.73 99.2
-1.07 6.04 101
-0.345 5.82 102
0.652 6.24 104
1.37 7.12 105
1.15 7.85 107
0.0238 7.87 109
1.43 8.79 111
1.08 9.48 113
1.53 10.5 116
-0.709 10 118
-0.811 9.48 121
-1.06 8.8 123
-1.22 8.02 125
-1.4 7.13 126
0.129 7.21 128
0.199 7.34 130
-0.182 7.22 132
0.135 7.31 133
0.885 7.87 135
0.678 8.31 137
0.922 8.9 139
-1.54 7.91 141
-1.16 7.16 143
-0.632 6.76 145
1.3 7.59 146
-0.67 7.16 148
0.124 7.24 150
-1.19 6.48 151
-0.728 6.01 153
1.22 6.79 154
-1.33 5.94 156
-0.402 5.69 157
-0.532 5.35 159
1.27 6.16 160
0.323 6.37 162
0.428 6.64 163
0.414 6.91 165
-0.614 6.51 166
1.37 7.39 168
0.449 7.68 170
0.55 8.03 172
1.33 8.88 174
-1.2 8.11 176
-0.641 7.7 178
-1.59 6.69 179
1.02 7.34 181
-0.86 6.79 183
-1.55 5.79 184
-0.515 5.46 186
0.352 5.69 187
0.824 6.22 188
1.14 6.94 190
-1.03 6.29 192
-1.13 5.56 193
0.139 5.65 194
0.293 5.84 196
1.08 6.53 197
-1.23 5.75 199
-1.1 5.04 200
-1.17 4.29 201
-0.8 3.78 202
-0.905 3.2 203
-0.0769 3.15 203
-0.323 2.95 204
-0.0186 2.93 205
Press any key to continue . . .
Respuestas:
Parece que se puede resolver mediante un filtrado bastante sencillo. Aquí están sus datos originales:
Eso es demasiado para ver lo que sucede en un evento individual con el nivel de detalle apropiado aquí. Aquí están solo los datos del segundo 26 al 28:
Originalmente pensé en filtrar esto, pero eso no funciona porque no hay una señal de baja frecuencia allí. La amplitud de la señal de alta frecuencia aumenta en su lugar. Aquí hay un paso bajo superpuesto al original:
Observe que esto sigue el "promedio" de la señal bastante bien, no durante el evento de baches. Si restamos este promedio de la señal original, nos quedamos con excursiones mucho más altas de este promedio durante el evento que de otra manera. Dicho de otra manera, lo que realmente queremos es un filtro de paso alto. Lo haremos restando el paso bajo del original, ya que así es como llegamos aquí, pero en un sistema de producción lo haría mediante un filtrado de paso alto explícito. De todos modos, aquí está el original de paso alto filtrado:
Esto ahora señala un enfoque obvio para detectar el evento. Hay mucha más amplitud de señal durante el evento que de otra manera. Podemos detectar esto calculando el RMS y aplicando un filtro de paso bajo:
Acercándonos a todos los datos, vemos:
Esto identifica claramente cinco eventos en los datos, aunque no sé si eso es lo que se supone que muestran estos datos. Al observar los eventos más de cerca, notará que cada uno de ellos tiene bajas bajas aproximadamente 1 segundo antes y después de los picos. Esto significa que se puede hacer más si simplemente limitar la señal RMS como está ahora no es lo suficientemente bueno. Por ejemplo, un algoritmo simple que buscaba la altura de un punto en relación con el más bajo en 1 segundo de cualquier manera debería reducir aún más el ruido de fondo. Otra forma de decir lo mismo es diferenciar esta señal buscando el aumento en un período de 1 segundo. Luego, un doblete detectaría un evento de baches, lo que significa un pico alto seguido de un pico bajo.
Otra forma de ver esto es pasar en banda la señal RMS. Ya está filtrado de paso bajo, pero dado que está buscando eventos repentinos con fuertes pendientes, cortar algunas de las frecuencias bajas también debería funcionar para reducir el ruido de fondo.
Hay muchas formas de refinar la señal desde aquí, pero espero haber mostrado cómo obtener al menos un resultado útil de primer paso.
Adicional:
Tenía curiosidad por saber si funcionaría buscar salsas a ambos lados de un pico, así que lo probé. Usé un filtro no lineal que comienza con el RMS de la gráfica anterior. El valor de cada punto es el mínimo de cuánto está por encima del punto más bajo en el segundo anterior y el punto más bajo en el segundo siguiente. El resultado se ve bastante bien:
El más bajo de los 5 picos es más de 3 veces más alto que el ruido de fondo más alto. Por supuesto, esto supone que estos 5 golpes representan eventos que desea detectar y el resto no.
Agregado en respuesta a los comentarios:
Hice los filtros en el dominio del tiempo, por lo que no sé la respuesta de frecuencia directamente. Para el filtro de paso bajo convolví la señal de entrada con un núcleo de filtro COS ^ 2. Si no recuerdo mal, el radio (distancia del centro al borde) del núcleo es de unos 100 ms. Experimenté con el valor hasta que la trama se veía bien. Para filtrar paso bajo el RMS, utilicé el mismo núcleo de filtro pero esta vez con un radio de aproximadamente un segundo. No me acuerdo exactamente. Experimente hasta obtener buenos resultados.
El filtro no lineal no detectó dobletes. Como dije, encontré la diferencia entre el punto actual y el más bajo de todos los puntos dentro de 1 segundo antes, y también la diferencia entre el punto actual y el más bajo de todos los puntos dentro de 1 segundo después. Luego tomé el mínimo de esos dos.
El software que utilicé fue un programa que pirateé para este propósito. Ya tenía varias rutinas para leer y escribir archivos CSV, por lo que todo lo que tenía que escribir era el código de filtrado, que es muy simple. El resto se hizo con programas preexistentes que tengo para manipular y trazar archivos CSV.
fuente
La detección de baches puede estar causando problemas. La envolvente de vibraciones del automóvil es donde radica la respuesta, ya que las vibraciones reales vistas por el sensor están en frecuencias mucho más altas. Me gustaría ir con RMS a DC, que responde a unos 15Hz o más alto y baja la cosa.
fuente
En lugar de buscar un filtro de dominio de frecuencia o un umbral, recomiendo tratar de encontrar un núcleo para un bache "típico", y hacer una correlación en ejecución con él. Se consideraría una técnica de coincidencia de plantillas y parecería prestarse a una plataforma de microcontrolador.
Ver http://scribblethink.org/Work/nvisionInterface/vi95_lewis.pdf para una revisión rápida, y tal vez DOBBS, STEVEN E., NEIL M. SCHMITT y HALUK S. OZEMEK. "Detección de QRS mediante coincidencia de plantillas mediante correlación en tiempo real en un microordenador". Revista de ingeniería clínica 9.3 (1984): 197-212.
Si estuvieras en una plataforma más robusta, recomendaría darle una vuelta a las wavelets.
fuente
Otro enfoque sería calcular una variación en movimiento de su señal para ver si los baches realmente sobresalen. Aquí hay una función matlab para un filtro de varianza en movimiento, N puntos de ancho - inteligentemente (si debo decirlo yo mismo) usando una convolución para el cálculo
fuente
Mi pensamiento inicial es que un filtro de paso bajo podría ser el tipo de filtro incorrecto para usar. El bache es esencialmente un evento de alta frecuencia, como una función escalonada u onda cuadrada. Solo mirar los datos filtrados de 50Hz me hace pensar que está perdiendo la información sobre el bache: todo parece ser el mismo garabato sin distinción significativa para el evento del bache. Primero usaría un filtro de paso alto, luego un filtro de paso bajo con una frecuencia mucho más alta. Puede evitar el filtro de paso bajo por completo si su acelerómetro ya está filtrado de paso bajo.
Una vez que tenga los datos filtrados de paso alto, creo que un simple comparador con un umbral establecido adecuadamente seleccionará los picos en los datos de aceleración causados por los baches y le permitirá contarlos.
fuente