Eu tenho cerca de 32 segundos no valor de dados do acelerômetro de um cenário básico de condução de 25 MPH em estradas normais, além de atingir cerca de 7 buracos e um trecho áspero da estrada. O acelerômetro é montado no painel de instrumentos do meu carro com fita dupla face.
Problema: tenho todos os dados barulhentos do acelerômetro e preciso criar uma maneira simples de detectar que um evento ocorreu. Abaixo estão vários gráficos de dados no domínio do tempo e FFT. O acelerômetro está medindo no GForce
Basicamente, quero que meu arduino saiba que ocorreu um buraco com uma precisão bastante grande e que não usa técnicas e matemática de nível de pós-graduação.
O acelerômetro amostrado a 100 hz possui um simples filtro de baixa frequência RC de 50 HZ no Eixo Z
Here is the CSV data for the 32 seconds of accelerometer readings TIME, GFORCE format:
http://hamiltoncomputer.us/50HZLPFDATA.CSV
ATUALIZAÇÃO: Esta é a largura de banda RAW completa do acelerômetro 1000HZ amostrada na taxa de amostragem mais alta que eu poderia obter no Arduino. Download direto do arquivo CSV: cerca de 112 segundos de dados
http://hamiltoncomputer.us/RAWUNFILTEREDFULLBANDWIDTH500HZ.csv
O traço preto é um dado RAW não filtrado do acelerômetro: o traço azul é filtrado por um filtro de parada de banda com base nas frequências extremas encontradas em FFT, Dominate 2HZ e 12HZ.
O evento Pothole se parece com isso no domínio do tempo:
Não tenho certeza qual é o componente de 10 a 15HZ na FFT, é o buraco real, ou é o salto das rodas contra a estrada ou a frequência de ressonância do carro?
FFT:
parece que são os eventos reais do buraco, aqui está um HPF @ 13HZ As características dominantes dos buracos parecem aprimoradas
Eu quero ser capaz de detectar e contar os buracos em tempo real
Parece ser contra-intuitivo que a suspensão deva se mover muito mais devagar que 10 a 13 HZ, o que causaria enjôo, acredito
ATUALIZAR:
De acordo com as sugestões do AngryEE, usei toda a largura de banda do acelerômetro 1000HZ e a taxa máxima de amostragem que pude obter no arduino.
FFT:
aqui está uma amostra de dados do evento buracos e alguns solavancos e ruídos da estrada ao seu redor:
Adicionado o circuito detector do envelope de diodo, a saída parece a mesma ... O acelerômetro sempre gera 0 a 3,3Volts não negativos ...
ATUALIZAR:
Em muitos testes de estrada, nunca ultrapassei os 1,6 G até 45 MPH no meu carro no eixo Z, usei rand () para gerar aceleração pseudo-aleatória do Gforce.
Minha idéia é que, se eu puder olhar para janelas de dados de 1 a 3 segundos, eu puder calcular o deslocamento do eixo Z, mas estava preocupado com a deriva do acelerômetro e com os erros na integração. Não preciso ser nem 90% preciso aqui,> 70% seria bom, mas se eu estiver olhando para o deslocamento de um a três segundos por vez, isso seria possível em tempo real? Dessa forma, posso ver se o deslocamento é maior do que 1 polegada, 2 polegadas, 5 polegadas. Quanto maior o deslocamento, maior o impacto ou o buraco:
Você pode verificar se estou fazendo isso direito? Basicamente, configurei na minha área de trabalho usando rand () para gerar aceleração aleatória de -1,6 a 1,6 G, capturando 3 segundos de dados em uma taxa de amostragem simulada de 50 Hz
Se você rodar * nix, estou usando o Sleep () do Windows.h para fazer o atraso de 20 ms, a taxa de amostragem de 50 Hz
Eu só queria ver se o código parece certo para você, ainda não fiz o buffer cicular, estou meio confuso sobre como implementá-lo: o código comentado é da classe em que estou trabalhando nele , mas ainda não o entendo 100%. Um buffer circular permitiria mover contiguamente janelas de dados, certo?
#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;
}
Exemplo de execução:
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 . . .
Respostas:
Parece que isso pode ser resolvido com uma filtragem bastante direta. Aqui estão seus dados originais:
É demais ver o que acontece em um evento individual no nível de detalhe apropriado aqui. Aqui estão apenas os dados do segundo 26 ao 28:
Eu pensava originalmente em filtrar passa-baixo isso, mas isso não funciona porque não há um sinal de baixa frequência lá. A amplitude do sinal de alta frequência aumenta em seu lugar. Aqui está um passe baixo sobreposto ao original:
Observe que isso segue muito bem a "média" do sinal, não durante o evento do buraco. Se subtrairmos essa média do sinal original, ficaremos com excursões muito mais altas dessa média durante o evento do que o contrário. Em outras palavras, o que realmente queremos é um filtro passa-alto. Para fazer isso, subtraímos o passe baixo do original, pois foi assim que chegamos aqui, mas em um sistema de produção, você faria isso por meio de filtragem explicitamente passa alta. De qualquer forma, aqui está o original com filtro passa-alto:
Isso agora aponta uma abordagem óbvia para a detecção do evento. Há muito mais amplitude de sinal durante o evento do que o contrário. Podemos detectar isso calculando o RMS e aplicando alguma filtragem passa-baixo:
Fazendo um zoom de volta para todos os dados, vemos:
Isso identifica claramente cinco eventos nos dados, embora eu não saiba se é isso que esses dados devem mostrar. Observando os eventos mais de perto, você percebe que cada um deles tem quedas baixas cerca de 1 segundo antes e depois dos picos. Isso significa que mais pode ser feito se o limiar do sinal RMS como está agora não for bom o suficiente. Por exemplo, um algoritmo simples que procurou a altura de um ponto em relação ao ponto mais baixo dentro de 1 segundo de qualquer maneira deve reduzir ainda mais o ruído de fundo. Outra maneira de dizer sobre a mesma coisa é diferenciar esse sinal procurando o aumento durante um período de 1 segundo. Um evento de buracos seria então detectado por um dupleto, significando um pico alto seguido por um pico baixo.
Outra maneira de ver isso é passar o sinal RMS em banda. Já é filtro passa-baixo, mas como você está procurando eventos repentinos com fortes inclinações, cortar algumas das baixas frequências também deve funcionar para reduzir o ruído de fundo.
Existem várias maneiras de refinar o sinal a partir daqui, mas espero ter mostrado como obter pelo menos um resultado útil de primeira passagem.
Adicionado:
Eu estava curioso para saber como a procura de mergulhos de ambos os lados de um pico funcionaria, então tentei. Usei um filtro não linear começando com o RMS do gráfico anterior. O valor de cada ponto é o mínimo de quanto está acima do ponto mais baixo no segundo anterior e do ponto mais baixo no segundo seguinte. O resultado parece muito bom:
O menor dos 5 picos é mais de 3 vezes maior que o maior ruído de fundo. É claro que isso pressupõe que esses 5 saliências representam eventos que você deseja detectar e o resto não.
Adicionado em resposta aos comentários:
Eu fiz os filtros no domínio do tempo, então não conheço a resposta de frequência diretamente. Para o filtro passa-baixo, convolvi o sinal de entrada com um núcleo de filtro COS ^ 2. Se bem me lembro, o raio (distância do centro à borda) do kernel é de apenas 100 ms. Eu experimentei o valor até o enredo parecer bom. Para filtro passa-baixo do RMS, usei o mesmo núcleo de filtro, mas desta vez com um raio de cerca de um segundo. Não me lembro exatamente. Experimente até obter bons resultados.
O filtro não linear não detectou dupletos. Como eu disse, encontrei a diferença entre o ponto atual e o menor de todos os pontos dentro de 1 segundo antes, e também a diferença entre o ponto atual e o menor de todos os pontos dentro de 1 segundo depois. Então eu peguei o min desses dois.
O software que usei foi um programa que hackeei para esse fim. Eu já tinha várias rotinas para ler e gravar arquivos CSV, então tudo o que eu precisava escrever era o código de filtragem, que é muito simples. O resto foi feito com programas pré-existentes que tenho para manipular e plotar arquivos CSV.
fonte
Os buracos na detecção de bordas podem estar causando problemas. O envelope de vibração dos carros é onde está a resposta, pois as vibrações reais vistas pelo sensor estão em frequências muito mais altas. Eu iria com RMS para DC, que responde a cerca de 15Hz ou superior e passa baixo a coisa.
fonte
Em vez de procurar um filtro no domínio da frequência ou um limite, recomendo tentar criar um kernel para um buraco "típico" e fazer uma correlação contínua com ele. Seria considerado uma técnica de correspondência de modelos e pareceria se prestar a uma plataforma de microcontrolador.
Veja http://scribblethink.org/Work/nvisionInterface/vi95_lewis.pdf para uma revisão rápida e talvez DOBBS, STEVEN E., NEIL M. SCHMITT e HALUK S. OZEMEK. "Detecção de QRS por correspondência de modelos usando correlação em tempo real em um microcomputador." Jornal de engenharia clínica 9.3 (1984): 197-212.
Se você estivesse em uma plataforma mais robusta, eu recomendaria dar uma volta nas wavelets.
fonte
Outra abordagem seria calcular uma variação móvel do seu sinal para ver se os buracos realmente se destacam. Aqui está uma função matlab para um filtro de variação móvel, N aponta largamente - de forma inteligente (se devo dizer assim) usando uma convolução para cálculo
fonte
Meu pensamento inicial é que um filtro passa-baixo pode ser o tipo errado de filtro a ser usado. O buraco é essencialmente um evento de alta frequência - como uma função de passo ou onda quadrada. Basta olhar para os dados filtrados de 50Hz e me fazer pensar que você está perdendo as informações sobre o buraco - tudo parece os mesmos rabiscos, sem distinção significativa para o evento do buraco. Eu usaria primeiro um filtro passa-alto, depois um filtro passa-baixo com uma frequência muito maior. Você pode evitar completamente o filtro passa-baixo se o seu acelerômetro já estiver filtrado.
Depois de ter os dados filtrados de alta passagem, acho que um comparador simples com um limite definido adequadamente selecionará os picos nos dados de aceleração causados pelos buracos e permitirá que você os conte.
fonte