Encontre a frequência dominante de dados sintéticos de vibração com FFT
Use a FFT do NumPy em um sinal sintético de vibração com um componente conhecido de 2 Hz, examine os bins de frequência e verifique se a frequência dominante detectada corresponde à entrada. O CSV original permanece inalterado e o espectro é gravado em uma nova pasta de saída.
Conteúdo verificado 2026.09.20Inclui arquivos de exemplo
Ver o sumário
A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English
Para quem éEste guia é voltado a pessoas que querem um exemplo pequeno e verificável antes de aplicar análise de frequência baseada em FFT a dados de vibração automotiva ou de sensores.
Preparação
Python 3.12 e um comando de terminal que inicie essa versão.
NumPy instalado com python -m pip install numpy.
Uma pasta de trabalho em que o script possa criar uma nova pasta dentro de outputs.
Compreensão básica de que um sinal amostrado possui uma frequência de amostragem e um intervalo de tempo discreto.
01Comece com uma frequência conhecida antecipadamente
O exercício inicial mais seguro com FFT é usar um sinal cuja frequência você já conhece. Este exemplo usa 8 samples por segundo e um sinal sintético de vibração puro de 2 Hz. Com 8 samples, o espaçamento de frequência da FFT é sampling_rate / sample_count = 8 / 8 = 1 Hz.
Para uma entrada de valores reais, rfft do NumPy retorna apenas os bins de frequência não negativos. Com 8 samples a 8 Hz, esses bins são 0, 1, 2, 3 e 4 Hz. O bin de 4 Hz é a frequência de Nyquist.
02Crie o CSV sintético de vibração
O conjunto de dados a seguir é sintético e foi criado especificamente para este artigo. Salve-o como vibration_data.csv. O intervalo de amostragem é 0.125 seconds, portanto a frequência de amostragem é 1 / 0.125 = 8 Hz.
Os valores se repetem a cada quatro samples: 0, 1, 0, -1. Com 8 samples por segundo, quatro samples correspondem a 0.5 seconds, e um período de 0.5 seconds corresponde a 2 Hz. Portanto, o sinal tem uma frequência dominante de entrada conhecida de 2 Hz.
Grandeza
Valor
Samples
8
Sample interval
0.125 s
Sampling frequency
8 Hz
FFT frequency spacing
1 Hz
Known signal frequency
2 Hz
Nyquist frequency
4 Hz
03Preveja o resultado da FFT antes de executar o código
Como o registro contém um número inteiro de ciclos e o sinal de 2 Hz cai exatamente em um bin da FFT, o exemplo sintético é deliberadamente limpo. A magnitude bruta da FFT deve ficar concentrada em 2 Hz.
Para a sequência 0, 1, 0, -1, 0, 1, 0, -1, o coefficient da FFT em 2 Hz tem magnitude 4. Os outros bins de frequência não negativa têm magnitude zero em aritmética exata. Pequenos erros numéricos de arredondamento podem aparecer em cálculos no computador, por isso o espectro exportado é arredondado para seis casas decimais.
Frequência
Magnitude bruta esperada da FFT
0 Hz
0
1 Hz
0
2 Hz
4
3 Hz
0
4 Hz
0
O bin de 0 Hz representa a média ou componente DC. Este sinal tem média 0. Mesmo assim, o script subtrai a média antes da FFT, o que é uma etapa comum de preparação quando o objetivo é examinar o conteúdo oscilatório em vez de um offset constante do sensor.
04Calcule o espectro e a frequência dominante
Salve o script a seguir como fft_dominant_frequency.py. Ele verifica o espaçamento dos timestamps, remove a média, calcula a FFT real, grava o espectro de frequência em um novo CSV e verifica se o bin de frequência diferente de zero mais forte é o esperado de 2 Hz.
python
import csv
from pathlib import Path
import numpy as np
SOURCE = Path("vibration_data.csv")
OUTPUT_DIR = Path("outputs") / "fft_result"
OUTPUT = OUTPUT_DIR / "spectrum.csv"
EXPECTED_DOMINANT_HZ = 2.0
DT_TOLERANCE = 1e-12
FREQUENCY_TOLERANCE = 1e-12
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
times = []
values = []
with SOURCE.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(stream)
required = {"time_s", "accel_m_s2"}
if reader.fieldnames is None or not required.issubset(reader.fieldnames):
raise ValueError("CSV is missing a required column.")
for row in reader:
times.append(float(row["time_s"]))
values.append(float(row["accel_m_s2"]))
if len(times) < 2:
raise ValueError("At least two samples are required.")
time_array = np.asarray(times, dtype=float)
signal = np.asarray(values, dtype=float)
intervals = np.diff(time_array)
dt = intervals[0]
if dt <= 0:
raise ValueError("Sample interval must be positive.")
if not np.allclose(intervals, dt, rtol=0.0, atol=DT_TOLERANCE):
raise ValueError("Sampling interval is not constant.")
sample_rate = 1.0 / dt
centered = signal - signal.mean()
spectrum = np.fft.rfft(centered)
frequencies = np.fft.rfftfreq(len(centered), d=dt)
magnitudes = np.abs(spectrum)
if len(frequencies) < 2:
raise ValueError("No nonzero frequency bin is available.")
dominant_index = 1 + int(np.argmax(magnitudes[1:]))
dominant_frequency = float(frequencies[dominant_index])
if abs(dominant_frequency - EXPECTED_DOMINANT_HZ) > FREQUENCY_TOLERANCE:
raise RuntimeError(
f"Expected {EXPECTED_DOMINANT_HZ} Hz, got {dominant_frequency} Hz."
)
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
with OUTPUT.open("x", encoding="utf-8", newline="") as stream:
writer = csv.writer(stream)
writer.writerow(["frequency_hz", "fft_magnitude"])
for frequency, magnitude in zip(frequencies, magnitudes):
writer.writerow([f"{frequency:.6f}", f"{magnitude:.6f}"])
resolution = sample_rate / len(centered)
print(f"Samples: {len(centered)}.")
print(f"Sampling frequency: {sample_rate:.1f} Hz.")
print(f"Frequency resolution: {resolution:.1f} Hz.")
print(f"Dominant nonzero frequency: {dominant_frequency:.1f} Hz.")
print(f"Output: {OUTPUT.as_posix()}")
if __name__ == "__main__":
main()
05Compare o espectro esperado
O CSV do espectro deve conter cinco linhas porque uma FFT real de 8-sample retorna bins de 0 até a frequência de Nyquist de 4 Hz. Arredondado para seis casas decimais, apenas o bin de 2 Hz deve ter magnitude diferente de zero.
A saída esperada do console abaixo foi derivada manualmente a partir do sinal sintético e do script. Não é um log capturado de execução.
text
Samples: 8.
Sampling frequency: 8.0 Hz.
Frequency resolution: 1.0 Hz.
Dominant nonzero frequency: 2.0 Hz.
Output: outputs/fft_result/spectrum.csv
06Verifique o resultado e erros comuns
Verifique manualmente o intervalo de amostragem: 0.125 seconds.
Calcule manualmente a frequência de amostragem: 1 / 0.125 = 8 Hz.
Calcule o espaçamento de frequência: 8 / 8 = 1 Hz.
Confirme que a maior magnitude de FFT diferente de zero ocorre em 2 Hz.
Confirme que o vibration_data.csv original permaneceu inalterado.
Execute o script novamente sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError em vez de sobrescrever o espectro.
Problema
O que verificar
A frequência dominante está errada por um fator de escala
Verifique a unidade de tempo e o intervalo de amostragem usados em rfftfreq.
0 Hz é o maior componente
Uma média diferente de zero ou um offset do sensor pode dominar; decida se a remoção da média é apropriada.
A energia aparece em bins vizinhos
A frequência do sinal pode não cair exatamente em um bin da FFT, ou o registro pode não conter um número inteiro de ciclos.
O resultado em alta frequência parece suspeito
Verifique o limite de Nyquist e se a frequência de amostragem é suficientemente alta para o sinal físico.
Execuções diferentes sobrescrevem os resultados
Use uma nova pasta de saída; este exemplo para deliberadamente quando o destino já existe.
07Entenda os limites antes de usar dados medidos de vibração
Este exemplo é intencionalmente ideal. Registros reais de vibração raramente se alinham perfeitamente com os bins da FFT, portanto o vazamento espectral pode espalhar energia para frequências vizinhas. Window functions são usadas com frequência para controlar o vazamento, mas também alteram a interpretação da amplitude e exigem suas próprias verificações.
A resolução em frequência depende do comprimento do registro, além da frequência de amostragem. Com 8 samples a 8 Hz, o espaçamento é 1 Hz, portanto esta análise não consegue distinguir frequências como 2.1 Hz e 2.4 Hz como bins exatos separados. Um registro mais longo fornece espaçamento de frequência mais fino quando a frequência de amostragem permanece inalterada.
Para dados automotivos medidos, considere também filtragem anti-alias, largura de banda do sensor, sincronização de amostragem, samples perdidos, detrending, escolha da window, escala de amplitude e se o sinal é estacionário o suficiente para que uma única FFT seja significativa. Preserve os dados brutos no domínio do tempo e documente cada etapa de pré-processamento antes de interpretar fisicamente os picos.
Foi verificado manualmente que os 8 timestamps estão separados por 0.125 seconds, resultando em uma frequência de amostragem de 8 Hz.
Foi calculado manualmente o espaçamento de frequência da FFT como 8 Hz / 8 samples = 1 Hz.
O padrão repetitivo de quatro samples 0, 1, 0, -1 foi identificado como um período de 0.5 seconds, correspondente a 2 Hz.
Foram determinadas manualmente as magnitudes esperadas da FFT não negativa como 0, 0, 4, 0, 0 em aritmética exata.
O script foi inspecionado quanto às verificações de amostragem constante, remoção da média, rfft, rfftfreq, exclusão do bin DC da seleção da frequência dominante, proteção contra colisão de saída e preservação do CSV original.
O CSV de espectro esperado e a saída do console foram derivados manualmente.
Limites da verificação
O código não foi executado pelo autor desta resposta; a saída da FFT do NumPy e as gravações no sistema de arquivos não foram testadas aqui.
Vazamento espectral, window functions, calibração de amplitude, amostragem irregular, aliasing, ruído de sensores e conjuntos de dados longos não foram testados.
O pico sintético em 2 Hz é uma entrada matemática conhecida e não é apresentado como uma ressonância real do veículo ou frequência de falha.
As URLs da documentação oficial foram fornecidas com base em locais de documentação conhecidos, mas não foram verificadas ao vivo.
Verifique unidades, valores ausentes, números finitos e timestamps duplicados ou invertidos em dados sintéticos de demonstração e depois salve os resultados da verificação e um gráfico.
Valide três arquivos CSV sintéticos de demonstração e depois sobreponha em um único gráfico as curvas que compartilham as mesmas amostras de tempo e unidades de aceleração.
Se você apenas colocar lado a lado os valores de tensão de pico, diferenças de malha, intervalo de saída e local de avaliação podem parecer mudanças de projeto. Monte uma tabela de condições de comparação a partir de casos sintéticos e registre a diferença numérica junto com o intervalo que realmente pode ser avaliado.
Aplique uma média móvel retrospectiva de três amostras a dados sintéticos de aceleração, verifique manualmente cada valor filtrado e deixe explícito o atraso de alinhamento de uma amostra introduzido pelo filtro. O CSV original permanece inalterado e os dados processados são gravados em uma nova pasta.