Tengo aproximadamente 32 segundos de datos del acelerómetro de un escenario de conducción básico 25 MPH en carreteras normales junto con 7 baches y un tramo irregular de la carretera. El acelerómetro está montado en el tablero de mi auto con cinta adhesiva de doble cara.
Problema: tengo todos los datos que son ruidosos del acelerómetro y necesito crear una forma sencilla de detectar que se ha producido un evento de bache. A continuación se muestran varios gráficos de datos en el dominio del tiempo y FFT. El acelerómetro está midiendo en GForce
Básicamente, quiero que mi arduino sepa que se ha producido un bache con una precisión bastante grande y sin usar técnicas y matemáticas de nivel de posgrado.
El acelerómetro muestreado a 100 hz tiene un FILTRO PASO BAJO RC simple 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 tasa de muestreo más alta que pude obtener en Arduino. Descarga directa de archivos CSV: alrededor de 112 segundos de datos
http://hamiltoncomputer.us/RAWUNFILTEREDFULLBANDWIDTH500HZ.csv
El rastro negro son datos del acelerómetro RAW sin filtrar: el rastro azul se filtra mediante un filtro de parada de banda basado en las frecuencias extremas que se encuentran en FFT, Dominate 2HZ y 12HZ.
El evento de bache se ve así en el dominio del tiempo:
No estoy seguro de cuál es el componente de 10 a 15 HZ en la 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 de baches reales, 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 10 a 13 HZ que causaría mareos, creo.
ACTUALIZAR:
Según las sugerencias de AngryEE, utilicé el ancho de banda completo del acelerómetro 1000HZ y la tasa de muestreo máxima que pude obtener en el arduino.
FFT:
aquí hay una muestra de datos del evento de bache y algunos baches y ruido de la carretera a su alrededor:
Se agregó el circuito detector de envolvente de diodo, la salida se ve igual... El acelerómetro siempre emite de 0 a 3,3 voltios, no es negativo...
ACTUALIZAR:
De muchas pruebas en carretera, nunca excedí 1.6G hasta 45 MPH en mi automóvil en el eje Z, usé rand () para generar una aceleración Gforce pseudoaleatoria.
Mi idea es que si puedo ver ventanas de datos de 1 a 3 segundos, puedo calcular el desplazamiento del eje Z, pero me preocupaba la deriva del acelerómetro y los errores en la integración. No necesito tener ni un 90 % de precisión aquí, >70 % estaría bien, pero si miro 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 1 pulgada, 2 pulgadas, 5 pulgadas. Cuanto mayor era el desplazamiento, más áspero era el bache o bache:
¿Puede verificar si estoy haciendo esto bien? Básicamente 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 frecuencia de muestreo simulada de 50 HZ
Si como usted ejecuta *nix, estoy usando Sleep() de Windows.h para hacer el retraso de 20mS, frecuencia de muestreo de 50HZ
Solo quería ver si el código te parece correcto, aún no hice el búfer circular, estoy un poco confundido sobre cómo implementarlo: el código comentado es de la clase en la que estoy trabajando. , pero todavía no lo entiendo al 100%. Un búfer circular permitiría mover ventanas de datos de forma contigua, ¿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;
}
Ejemplo de ejecución:
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 . . .
Esto parece que se puede resolver mediante un filtrado bastante directo. Aquí están sus datos originales:
Eso es demasiado para ver lo que sucede en un evento individual al nivel de detalle apropiado para este caso. Aquí están solo los datos del segundo 26 al 28:
Originalmente había pensado en filtrar esto en paso bajo, pero eso no funciona porque no hay una señal de baja frecuencia allí. En cambio, la amplitud de la señal de alta frecuencia aumenta. Aquí hay un pase bajo superpuesto al original:
Tenga en cuenta que esto sigue bastante bien el "promedio" de la señal, no durante el evento de bache. Si restamos este promedio de la señal original, nos quedan 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ías mediante un filtrado de paso alto explícito. De todos modos, aquí está el original filtrado de paso alto:
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:
Haciendo zoom en 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. Mirando los eventos más de cerca, se da cuenta de que cada uno de ellos tiene caídas bajas alrededor de 1 segundo antes y después de los picos. Esto significa que se puede hacer más si el simple umbral de la señal RMS tal como está ahora no es lo suficientemente bueno. Por ejemplo, un algoritmo simple que buscara la altura de un punto en relación con el más bajo dentro de 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. Entonces, un evento de bache sería detectado por un doblete, lo que significa un pico alto seguido de un pico bajo.
Otra forma de ver esto es pasar banda a la señal RMS. Ya tiene filtrado de paso bajo, pero dado que está buscando eventos repentinos con fuertes pendientes, la eliminación de algunas de las frecuencias bajas también debería funcionar para reducir el ruido de fondo.
Hay muchas maneras de refinar la señal desde aquí, pero espero haber mostrado cómo llegar al menos a un primer resultado útil.
Tenía curiosidad por lo bien que funcionaría buscar depresiones a ambos lados de un pico, así que lo probé. Usé un filtro no lineal comenzando con el RMS del gráfico 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.
Hice los filtros en el dominio del tiempo, por lo que no conozco la respuesta de frecuencia directamente. Para el filtro de paso bajo, convolucioné la señal de entrada con un núcleo de filtro COS^2. Si no recuerdo mal, el radio (distancia desde el centro hasta el borde) del núcleo es de unos 100 ms. Experimenté con el valor hasta que la trama se veía bien. Para el filtro de paso bajo del RMS, utilicé el mismo kernel de filtro pero esta vez con un radio de aproximadamente un segundo. no recuerdo exactamente Experimenta hasta que obtengas 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 min 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, así 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.
Los baches de detección de bordes pueden estar buscando problemas. La envolvente de vibración de los automóviles es donde se encuentra la respuesta, ya que las vibraciones reales vistas por el sensor tienen frecuencias mucho más altas. Iría con RMS a DC, que responde a aproximadamente 15 Hz o más y pasa bajo.
En lugar de buscar un filtro de dominio de frecuencia o un umbral, recomiendo tratar de encontrar un kernel para un bache "típico" y hacer una correlación con él. Se consideraría una técnica de coincidencia de plantillas y parecería prestarse a una plataforma de microcontrolador.
Consulte http://scribblethink.org/Work/nvisionInterface/vi95_lewis.pdf para obtener una revisión rápida, y tal vez DOBBS, STEVEN E., NEIL M. SCHMITT y HALUK S. OZEMEK. "Detección de QRS por comparación de plantillas usando correlación en tiempo real en una microcomputadora". Revista de ingeniería clínica 9.3 (1984): 197-212.
Si estuvieras en una plataforma más robusta, te recomendaría darle una vuelta a las wavelets.
Otro enfoque sería calcular una variación móvil de su señal para ver si los baches realmente sobresalen. Aquí hay una función de matlab para un filtro de varianza móvil, N puntos de ancho, hábilmente (si debo decirlo yo mismo) usando una convolución para el cálculo
function y=movingvar(X,N)
% y=movingvar(X,N)
% Calculates N-point moving variance of Vector X
% Highly recommend that N be odd (no error checking)
% Note: first and last N/2 points will be unreliable.
% Output will be a column vector.
X=X(:);
XSQR=X.*X;
convsig=ones(1,N);
y=(conv(convsig,XSQR)-(conv(convsig,X).^2)/N)/(N-1);
y=y(ceil(N/2):length(X)+floor(N/2));
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 de paso o una onda cuadrada. Solo mirar los datos filtrados de 50 Hz me hace pensar que está perdiendo la información sobre el bache: todo parece los mismos garabatos sin una 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 tiene un filtro de paso bajo.
Una vez que tenga los datos filtrados de paso alto, creo que un comparador simple con un umbral establecido adecuadamente detectará los picos en los datos de aceleración causados por los baches y le permitirá contarlos.
Anindo Ghosh
zacharon16
Shashank de Chintalagiri
zacharon16
olin lathrop
olin lathrop
olin lathrop
zacharon16
phil escarcha