mirror of
https://gitee.com/xiaohuolufeihua/bizhang_-obav.git
synced 2026-05-21 01:12:11 +00:00
gyro_fft: add simple SNR requirement and reduce number of peaks
This commit is contained in:
@@ -6,10 +6,12 @@ uint32 device_id # unique device ID for the sensor that does not change
|
|||||||
float32 sensor_sample_rate_hz
|
float32 sensor_sample_rate_hz
|
||||||
float32 resolution_hz
|
float32 resolution_hz
|
||||||
|
|
||||||
float32[6] peak_frequencies_x # x axis peak frequencies
|
float32[4] peak_frequencies_x # x axis peak frequencies
|
||||||
float32[6] peak_frequencies_y # y axis peak frequencies
|
float32[4] peak_frequencies_y # y axis peak frequencies
|
||||||
float32[6] peak_frequencies_z # z axis peak frequencies
|
float32[4] peak_frequencies_z # z axis peak frequencies
|
||||||
|
|
||||||
uint32[6] peak_magnitude_x # x axis peak frequencies magnitude
|
float32[4] peak_magnitude_x # x axis peak frequencies magnitude
|
||||||
uint32[6] peak_magnitude_y # y axis peak frequencies magnitude
|
float32[4] peak_magnitude_y # y axis peak frequencies magnitude
|
||||||
uint32[6] peak_magnitude_z # z axis peak frequencies magnitude
|
float32[4] peak_magnitude_z # z axis peak frequencies magnitude
|
||||||
|
|
||||||
|
float32[3] total_energy
|
||||||
|
|||||||
@@ -13,15 +13,21 @@ tone_alarm start
|
|||||||
|
|
||||||
ver all
|
ver all
|
||||||
|
|
||||||
param set IMU_GYRO_RATEMAX 2000
|
param set IMU_GYRO_RATEMAX 1000
|
||||||
|
|
||||||
param set IMU_GYRO_FFT_EN 1
|
param set IMU_GYRO_FFT_EN 1
|
||||||
param set IMU_GYRO_FFT_MIN 1
|
param set IMU_GYRO_FFT_MIN 10
|
||||||
param set IMU_GYRO_FFT_MAX 1000
|
param set IMU_GYRO_FFT_MAX 1000
|
||||||
|
param set IMU_GYRO_FFT_LEN 1024
|
||||||
|
|
||||||
|
# dynamic notches ESC/FFT/both
|
||||||
|
#param set IMU_GYRO_DYN_NF 1
|
||||||
|
#param set IMU_GYRO_DYN_NF 2
|
||||||
|
#param set IMU_GYRO_DYN_NF 3
|
||||||
|
|
||||||
# test values
|
# test values
|
||||||
param set IMU_GYRO_CUTOFF 40
|
param set IMU_GYRO_CUTOFF 60
|
||||||
param set IMU_DGYRO_CUTOFF 30
|
param set IMU_DGYRO_CUTOFF 40
|
||||||
#param set IMU_GYRO_NF_FREQ 60
|
#param set IMU_GYRO_NF_FREQ 60
|
||||||
#param set IMU_GYRO_NF_BW 5
|
#param set IMU_GYRO_NF_BW 5
|
||||||
|
|
||||||
@@ -42,9 +48,9 @@ sleep 10
|
|||||||
logger off
|
logger off
|
||||||
|
|
||||||
sensors status
|
sensors status
|
||||||
uorb top -a -1
|
|
||||||
listener sensor_gyro
|
listener sensor_gyro
|
||||||
listener sensor_gyro_fifo
|
listener sensor_gyro_fifo
|
||||||
|
perf
|
||||||
|
|
||||||
echo "ALL TESTS PASSED"
|
echo "ALL TESTS PASSED"
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ GyroFFT::GyroFFT() :
|
|||||||
ModuleParams(nullptr),
|
ModuleParams(nullptr),
|
||||||
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default)
|
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default)
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < MAX_NUM_PEAKS; i++) {
|
||||||
|
_sensor_gyro_fft.peak_frequencies_x[i] = NAN;
|
||||||
|
_sensor_gyro_fft.peak_frequencies_y[i] = NAN;
|
||||||
|
_sensor_gyro_fft.peak_frequencies_z[i] = NAN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GyroFFT::~GyroFFT()
|
GyroFFT::~GyroFFT()
|
||||||
@@ -123,7 +128,7 @@ bool GyroFFT::init()
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// otherwise default to 256
|
// otherwise default to 256
|
||||||
PX4_ERR("Invalid IMU_GYRO_FFT_LEN=%.3f, resetting", (double)_param_imu_gyro_fft_len.get());
|
PX4_ERR("Invalid IMU_GYRO_FFT_LEN=%d, resetting", _param_imu_gyro_fft_len.get());
|
||||||
AllocateBuffers<256>();
|
AllocateBuffers<256>();
|
||||||
_param_imu_gyro_fft_len.set(256);
|
_param_imu_gyro_fft_len.set(256);
|
||||||
_param_imu_gyro_fft_len.commit();
|
_param_imu_gyro_fft_len.commit();
|
||||||
@@ -240,37 +245,48 @@ void GyroFFT::VehicleIMUStatusUpdate(bool force)
|
|||||||
// helper function used for frequency estimation
|
// helper function used for frequency estimation
|
||||||
static float tau(float x)
|
static float tau(float x)
|
||||||
{
|
{
|
||||||
float p1 = logf(3.f * powf(x, 2.f) + 6 * x + 1);
|
// tau(x) = 1/4 * log(3x^2 + 6x + 1) – sqrt(6)/24 * log((x + 1 – sqrt(2/3)) / (x + 1 + sqrt(2/3)))
|
||||||
float part1 = x + 1 - sqrtf(2.f / 3.f);
|
float p1 = logf(3.f * powf(x, 2.f) + 6.f * x + 1.f);
|
||||||
float part2 = x + 1 + sqrtf(2.f / 3.f);
|
float part1 = x + 1.f - sqrtf(2.f / 3.f);
|
||||||
|
float part2 = x + 1.f + sqrtf(2.f / 3.f);
|
||||||
float p2 = logf(part1 / part2);
|
float p2 = logf(part1 / part2);
|
||||||
return (1.f / 4.f * p1 - sqrtf(6) / 24 * p2);
|
return (0.25f * p1 - sqrtf(6.f) / 24.f * p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GyroFFT::EstimatePeakFrequency(q15_t fft[], uint8_t peak_index)
|
float GyroFFT::EstimatePeakFrequency(q15_t fft[], int peak_index)
|
||||||
{
|
{
|
||||||
// find peak location using Quinn's Second Estimator (2020-06-14: http://dspguru.com/dsp/howtos/how-to-interpolate-fft-peak/)
|
if (peak_index > 2) {
|
||||||
int16_t real[3] { fft[peak_index - 2], fft[peak_index], fft[peak_index + 2] };
|
// find peak location using Quinn's Second Estimator (2020-06-14: http://dspguru.com/dsp/howtos/how-to-interpolate-fft-peak/)
|
||||||
int16_t imag[3] { fft[peak_index - 2 + 1], fft[peak_index + 1], fft[peak_index + 2 + 1] };
|
float real[3] { (float)fft[peak_index - 2], (float)fft[peak_index], (float)fft[peak_index + 2] };
|
||||||
|
float imag[3] { (float)fft[peak_index - 2 + 1], (float)fft[peak_index + 1], (float)fft[peak_index + 2 + 1] };
|
||||||
|
|
||||||
const int k = 1;
|
static constexpr int k = 1;
|
||||||
|
|
||||||
float divider = (real[k] * real[k] + imag[k] * imag[k]);
|
const float divider = (real[k] * real[k] + imag[k] * imag[k]);
|
||||||
|
|
||||||
// ap = (X[k + 1].r * X[k].r + X[k+1].i * X[k].i) / (X[k].r * X[k].r + X[k].i * X[k].i)
|
// ap = (X[k + 1].r * X[k].r + X[k+1].i * X[k].i) / (X[k].r * X[k].r + X[k].i * X[k].i)
|
||||||
float ap = (real[k + 1] * real[k] + imag[k + 1] * imag[k]) / divider;
|
float ap = (real[k + 1] * real[k] + imag[k + 1] * imag[k]) / divider;
|
||||||
|
|
||||||
// am = (X[k – 1].r * X[k].r + X[k – 1].i * X[k].i) / (X[k].r * X[k].r + X[k].i * X[k].i)
|
// dp = -ap / (1 – ap)
|
||||||
float am = (real[k - 1] * real[k] + imag[k - 1] * imag[k]) / divider;
|
float dp = -ap / (1.f - ap);
|
||||||
|
|
||||||
float dp = -ap / (1.f - ap);
|
// am = (X[k - 1].r * X[k].r + X[k – 1].i * X[k].i) / (X[k].r * X[k].r + X[k].i * X[k].i)
|
||||||
float dm = am / (1.f - am);
|
float am = (real[k - 1] * real[k] + imag[k - 1] * imag[k]) / divider;
|
||||||
float d = (dp + dm) / 2 + tau(dp * dp) - tau(dm * dm);
|
|
||||||
|
|
||||||
float adjusted_bin = peak_index + d;
|
// dm = am / (1 – am)
|
||||||
float peak_freq_adjusted = (_gyro_sample_rate_hz * adjusted_bin / (_imu_gyro_fft_len * 2.f));
|
float dm = am / (1.f - am);
|
||||||
|
|
||||||
return peak_freq_adjusted;
|
// d = (dp + dm) / 2 + tau(dp * dp) – tau(dm * dm)
|
||||||
|
float d = (dp + dm) / 2.f + tau(dp * dp) - tau(dm * dm);
|
||||||
|
|
||||||
|
// k’ = k + d
|
||||||
|
float adjusted_bin = peak_index + d;
|
||||||
|
float peak_freq_adjusted = (_gyro_sample_rate_hz * adjusted_bin / (_imu_gyro_fft_len * 2.f));
|
||||||
|
|
||||||
|
return peak_freq_adjusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GyroFFT::Run()
|
void GyroFFT::Run()
|
||||||
@@ -377,42 +393,63 @@ void GyroFFT::Update(const hrt_abstime ×tamp_sample, int16_t *input[], uint
|
|||||||
|
|
||||||
// if we have enough samples begin processing, but only one FFT per cycle
|
// if we have enough samples begin processing, but only one FFT per cycle
|
||||||
if ((buffer_index >= _imu_gyro_fft_len) && !fft_updated) {
|
if ((buffer_index >= _imu_gyro_fft_len) && !fft_updated) {
|
||||||
arm_mult_q15(gyro_data_buffer[axis], _hanning_window, _fft_input_buffer, _imu_gyro_fft_len);
|
|
||||||
|
|
||||||
perf_begin(_fft_perf);
|
perf_begin(_fft_perf);
|
||||||
|
arm_mult_q15(gyro_data_buffer[axis], _hanning_window, _fft_input_buffer, _imu_gyro_fft_len);
|
||||||
arm_rfft_q15(&_rfft_q15, _fft_input_buffer, _fft_outupt_buffer);
|
arm_rfft_q15(&_rfft_q15, _fft_input_buffer, _fft_outupt_buffer);
|
||||||
perf_end(_fft_perf);
|
perf_end(_fft_perf);
|
||||||
|
|
||||||
fft_updated = true;
|
fft_updated = true;
|
||||||
|
|
||||||
static constexpr uint16_t MIN_SNR = 10; // TODO:
|
// sum total energy across all used buckets for SNR
|
||||||
|
float bin_mag_sum = 0;
|
||||||
|
|
||||||
|
for (uint16_t bucket_index = 2; bucket_index < (_imu_gyro_fft_len / 2); bucket_index = bucket_index + 2) {
|
||||||
|
const float freq_hz = (bucket_index / 2) * resolution_hz;
|
||||||
|
|
||||||
|
if (freq_hz >= _param_imu_gyro_fft_max.get()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float real = _fft_outupt_buffer[bucket_index];
|
||||||
|
const float imag = _fft_outupt_buffer[bucket_index + 1];
|
||||||
|
|
||||||
|
const float fft_magnitude_squared = real * real + imag * imag;
|
||||||
|
|
||||||
|
bin_mag_sum += fft_magnitude_squared;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sensor_gyro_fft.total_energy[axis] = bin_mag_sum;
|
||||||
|
|
||||||
|
|
||||||
bool peaks_detected = false;
|
bool peaks_detected = false;
|
||||||
uint32_t peaks_magnitude[MAX_NUM_PEAKS] {};
|
float peaks_magnitude[MAX_NUM_PEAKS] {};
|
||||||
uint8_t peak_index[MAX_NUM_PEAKS] {};
|
int peak_index[MAX_NUM_PEAKS] {};
|
||||||
|
|
||||||
// start at 2 to skip DC
|
// start at 2 to skip DC
|
||||||
// output is ordered [real[0], imag[0], real[1], imag[1], real[2], imag[2] ... real[(N/2)-1], imag[(N/2)-1]
|
// output is ordered [real[0], imag[0], real[1], imag[1], real[2], imag[2] ... real[(N/2)-1], imag[(N/2)-1]
|
||||||
for (uint16_t bucket_index = 2; bucket_index < (_imu_gyro_fft_len / 2); bucket_index = bucket_index + 2) {
|
for (uint16_t bucket_index = 2; bucket_index < (_imu_gyro_fft_len / 2); bucket_index = bucket_index + 2) {
|
||||||
const float freq_hz = (bucket_index / 2) * resolution_hz;
|
const float freq_hz = (bucket_index / 2) * resolution_hz;
|
||||||
|
|
||||||
if (freq_hz > _param_imu_gyro_fft_max.get()) {
|
if (freq_hz >= _param_imu_gyro_fft_max.get()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freq_hz >= _param_imu_gyro_fft_min.get()) {
|
const float real = _fft_outupt_buffer[bucket_index];
|
||||||
const int16_t real = _fft_outupt_buffer[bucket_index];
|
const float imag = _fft_outupt_buffer[bucket_index + 1];
|
||||||
const int16_t complex = _fft_outupt_buffer[bucket_index + 1];
|
|
||||||
|
|
||||||
const uint32_t fft_magnitude_squared = real * real + complex * complex;
|
const float fft_magnitude_squared = real * real + imag * imag;
|
||||||
|
|
||||||
if (fft_magnitude_squared > MIN_SNR) {
|
float snr = 10.f * log10f((_imu_gyro_fft_len - 1) * fft_magnitude_squared / (bin_mag_sum - fft_magnitude_squared));
|
||||||
for (int i = 0; i < MAX_NUM_PEAKS; i++) {
|
|
||||||
if (fft_magnitude_squared > peaks_magnitude[i]) {
|
static constexpr float MIN_SNR = 10.f; // TODO: configurable?
|
||||||
peaks_magnitude[i] = fft_magnitude_squared;
|
|
||||||
peak_index[i] = bucket_index;
|
if (snr > MIN_SNR) {
|
||||||
peaks_detected = true;
|
for (int i = 0; i < MAX_NUM_PEAKS; i++) {
|
||||||
break;
|
if (fft_magnitude_squared > peaks_magnitude[i]) {
|
||||||
}
|
peaks_magnitude[i] = fft_magnitude_squared;
|
||||||
|
peak_index[i] = bucket_index;
|
||||||
|
peaks_detected = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -420,7 +457,7 @@ void GyroFFT::Update(const hrt_abstime ×tamp_sample, int16_t *input[], uint
|
|||||||
|
|
||||||
if (peaks_detected) {
|
if (peaks_detected) {
|
||||||
float *peak_frequencies[] {_sensor_gyro_fft.peak_frequencies_x, _sensor_gyro_fft.peak_frequencies_y, _sensor_gyro_fft.peak_frequencies_z};
|
float *peak_frequencies[] {_sensor_gyro_fft.peak_frequencies_x, _sensor_gyro_fft.peak_frequencies_y, _sensor_gyro_fft.peak_frequencies_z};
|
||||||
uint32_t *peak_magnitude[] {_sensor_gyro_fft.peak_magnitude_x, _sensor_gyro_fft.peak_magnitude_y, _sensor_gyro_fft.peak_magnitude_z};
|
float *peak_magnitude[] {_sensor_gyro_fft.peak_magnitude_x, _sensor_gyro_fft.peak_magnitude_y, _sensor_gyro_fft.peak_magnitude_z};
|
||||||
|
|
||||||
int num_peaks_found = 0;
|
int num_peaks_found = 0;
|
||||||
|
|
||||||
@@ -428,9 +465,11 @@ void GyroFFT::Update(const hrt_abstime ×tamp_sample, int16_t *input[], uint
|
|||||||
if ((peak_index[i] > 0) && (peak_index[i] < _imu_gyro_fft_len) && (peaks_magnitude[i] > 0)) {
|
if ((peak_index[i] > 0) && (peak_index[i] < _imu_gyro_fft_len) && (peaks_magnitude[i] > 0)) {
|
||||||
const float freq = EstimatePeakFrequency(_fft_outupt_buffer, peak_index[i]);
|
const float freq = EstimatePeakFrequency(_fft_outupt_buffer, peak_index[i]);
|
||||||
|
|
||||||
if (freq >= _param_imu_gyro_fft_min.get() && freq <= _param_imu_gyro_fft_max.get()) {
|
if (PX4_ISFINITE(freq) && freq >= _param_imu_gyro_fft_min.get() && freq <= _param_imu_gyro_fft_max.get()) {
|
||||||
|
|
||||||
|
if (!PX4_ISFINITE(peak_frequencies[axis][num_peaks_found])
|
||||||
|
|| (fabsf(peak_frequencies[axis][num_peaks_found] - freq) > 0.01f)) {
|
||||||
|
|
||||||
if (fabsf(peak_frequencies[axis][num_peaks_found] - freq) > 0.1f) {
|
|
||||||
publish = true;
|
publish = true;
|
||||||
_sensor_gyro_fft.timestamp_sample = timestamp_sample;
|
_sensor_gyro_fft.timestamp_sample = timestamp_sample;
|
||||||
}
|
}
|
||||||
@@ -446,7 +485,7 @@ void GyroFFT::Update(const hrt_abstime ×tamp_sample, int16_t *input[], uint
|
|||||||
// mark remaining slots empty
|
// mark remaining slots empty
|
||||||
for (int i = num_peaks_found; i < MAX_NUM_PEAKS; i++) {
|
for (int i = num_peaks_found; i < MAX_NUM_PEAKS; i++) {
|
||||||
peak_frequencies[axis][i] = NAN;
|
peak_frequencies[axis][i] = NAN;
|
||||||
peak_magnitude[axis][i] = 0;
|
peak_magnitude[axis][i] = NAN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
bool init();
|
bool init();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float EstimatePeakFrequency(q15_t fft[], uint8_t peak_index);
|
float EstimatePeakFrequency(q15_t fft[], int peak_index);
|
||||||
void Run() override;
|
void Run() override;
|
||||||
bool SensorSelectionUpdate(bool force = false);
|
bool SensorSelectionUpdate(bool force = false);
|
||||||
void Update(const hrt_abstime ×tamp_sample, int16_t *input[], uint8_t N);
|
void Update(const hrt_abstime ×tamp_sample, int16_t *input[], uint8_t N);
|
||||||
|
|||||||
Reference in New Issue
Block a user