Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AP_InertialSensor: ensure that notches get updated while converging #26958

Merged
merged 3 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 79 additions & 23 deletions Tools/autotest/arducopter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3178,9 +3178,10 @@ def MotorVibration(self):

psd = self.mavfft_fttd(1, 0, tstart * 1.0e6, tend * 1.0e6)
# ignore the first 20Hz and look for a peak at -15dB or more
ignore_bins = 20
# it should be at about 190Hz, each bin is 1000/1024Hz wide
ignore_bins = int(100 * 1.024) # start at 100Hz to be safe
freq = psd["F"][numpy.argmax(psd["X"][ignore_bins:]) + ignore_bins]
if numpy.amax(psd["X"][ignore_bins:]) < -15 or freq < 180 or freq > 300:
if numpy.amax(psd["X"][ignore_bins:]) < -15 or freq < 100 or freq > 300:
raise NotAchievedException(
"Did not detect a motor peak, found %f at %f dB" %
(freq, numpy.amax(psd["X"][ignore_bins:])))
Expand Down Expand Up @@ -5898,16 +5899,16 @@ def ThrowMode(self):
(tdelta, max_good_tdelta))
self.progress("Vehicle returned")

def hover_and_check_matched_frequency_with_fft(self, dblevel=-15, minhz=200, maxhz=300, peakhz=None,
reverse=None, takeoff=True):
def hover_and_check_matched_frequency_with_fft_and_psd(self, dblevel=-15, minhz=200, maxhz=300, peakhz=None,
reverse=None, takeoff=True, instance=0):
# find a motor peak
if takeoff:
self.takeoff(10, mode="ALT_HOLD")

tstart, tend, hover_throttle = self.hover_for_interval(15)
self.do_RTL()

psd = self.mavfft_fttd(1, 0, tstart * 1.0e6, tend * 1.0e6)
psd = self.mavfft_fttd(1, instance, tstart * 1.0e6, tend * 1.0e6)

# batch sampler defaults give 1024 fft and sample rate of 1kz so roughly 1hz/bin
freq = psd["F"][numpy.argmax(psd["X"][minhz:maxhz]) + minhz] * (1000. / 1024.)
Expand All @@ -5926,8 +5927,34 @@ def hover_and_check_matched_frequency_with_fft(self, dblevel=-15, minhz=200, max
self.progress("Detected motor peak at %fHz, throttle %f%%, %fdB" %
(freq, hover_throttle, peakdb))

return freq, hover_throttle, peakdb, psd

def hover_and_check_matched_frequency_with_fft(self, dblevel=-15, minhz=200, maxhz=300, peakhz=None,
reverse=None, takeoff=True, instance=0):
freq, hover_throttle, peakdb, psd = \
self.hover_and_check_matched_frequency_with_fft_and_psd(dblevel, minhz,
maxhz, peakhz, reverse, takeoff, instance)
return freq, hover_throttle, peakdb

def get_average_esc_frequency(self):
mlog = self.dfreader_for_current_onboard_log()
rpm_total = 0
rpm_count = 0
tho = 0
while True:
m = mlog.recv_match()
if m is None:
break
msg_type = m.get_type()
if msg_type == "CTUN":
tho = m.ThO
elif msg_type == "ESC" and tho > 0.1:
rpm_total += m.RPM
rpm_count += 1

esc_hz = rpm_total / (rpm_count * 60)
return esc_hz

def DynamicNotches(self):
"""Use dynamic harmonic notch to control motor noise."""
self.progress("Flying with dynamic notches")
Expand Down Expand Up @@ -5964,13 +5991,15 @@ def DynamicNotches(self):
})
self.reboot_sitl()

freq, hover_throttle, peakdb1 = self.hover_and_check_matched_frequency_with_fft(-10, 20, 350, reverse=True)
freq, hover_throttle, peakdb1 = \
self.hover_and_check_matched_frequency_with_fft(-10, 20, 350, reverse=True)

# now add double dynamic notches and check that the peak is squashed
self.set_parameter("INS_HNTCH_OPTS", 1)
self.reboot_sitl()

freq, hover_throttle, peakdb2 = self.hover_and_check_matched_frequency_with_fft(-15, 20, 350, reverse=True)
freq, hover_throttle, peakdb2 = \
self.hover_and_check_matched_frequency_with_fft(-15, 20, 350, reverse=True)

# double-notch should do better, but check for within 5%
if peakdb2 * 1.05 > peakdb1:
Expand All @@ -5982,7 +6011,8 @@ def DynamicNotches(self):
self.set_parameter("INS_HNTCH_OPTS", 16)
self.reboot_sitl()

freq, hover_throttle, peakdb2 = self.hover_and_check_matched_frequency_with_fft(-15, 20, 350, reverse=True)
freq, hover_throttle, peakdb2 = \
self.hover_and_check_matched_frequency_with_fft(-15, 20, 350, reverse=True)

# triple-notch should do better, but check for within 5%
if peakdb2 * 1.05 > peakdb1:
Expand All @@ -6008,7 +6038,7 @@ def DynamicRpmNotches(self):
"AHRS_EKF_TYPE": 10,
"INS_LOG_BAT_MASK": 3,
"INS_LOG_BAT_OPT": 0,
"INS_GYRO_FILTER": 100, # set gyro filter high so we can observe behaviour
"INS_GYRO_FILTER": 300, # set gyro filter high so we can observe behaviour
"LOG_BITMASK": 958,
"LOG_DISARMED": 0,
"SIM_VIB_MOT_MAX": 350,
Expand All @@ -6019,12 +6049,14 @@ def DynamicRpmNotches(self):

self.takeoff(10, mode="ALT_HOLD")

# find a motor peak
freq, hover_throttle, peakdb = self.hover_and_check_matched_frequency_with_fft(-15, 200, 300)
# find a motor peak, the peak is at about 190Hz, so checking between 50 and 320Hz should be safe.
# there is a second harmonic at 380Hz which should be avoided to make the test reliable
# detect at -5dB so we don't pick some random noise as the peak. The actual peak is about +15dB
freq, hover_throttle, peakdb = self.hover_and_check_matched_frequency_with_fft(-5, 50, 320)

# now add a dynamic notch and check that the peak is squashed
self.set_parameters({
"INS_LOG_BAT_OPT": 2,
"INS_LOG_BAT_OPT": 4,
"INS_HNTCH_ENABLE": 1,
"INS_HNTCH_FREQ": 80,
"INS_HNTCH_REF": 1.0,
Expand All @@ -6035,19 +6067,34 @@ def DynamicRpmNotches(self):
})
self.reboot_sitl()

freq, hover_throttle, peakdb1 = self.hover_and_check_matched_frequency_with_fft(-10, 20, 350, reverse=True)
# -10dB is pretty conservative - actual is about -25dB
freq, hover_throttle, peakdb1, psd = \
self.hover_and_check_matched_frequency_with_fft_and_psd(-10, 50, 320, reverse=True, instance=2)
# find the noise at the motor frequency
esc_hz = self.get_average_esc_frequency()
esc_peakdb1 = psd["X"][int(esc_hz)]

# now add notch-per motor and check that the peak is squashed
self.set_parameter("INS_HNTCH_OPTS", 2)
self.reboot_sitl()

freq, hover_throttle, peakdb2 = self.hover_and_check_matched_frequency_with_fft(-15, 20, 350, reverse=True)
freq, hover_throttle, peakdb2, psd = \
self.hover_and_check_matched_frequency_with_fft_and_psd(-10, 50, 320, reverse=True, instance=2)
# find the noise at the motor frequency
esc_hz = self.get_average_esc_frequency()
esc_peakdb2 = psd["X"][int(esc_hz)]

# notch-per-motor should do better, but check for within 10%. ( its mostly within 5%, but does vary a bit)
if peakdb2 * 1.10 > peakdb1:
# notch-per-motor will be better at the average ESC frequency
if esc_peakdb2 > esc_peakdb1:
raise NotAchievedException(
"Notch-per-motor peak was higher than single-notch peak %fdB > %fdB" %
(peakdb2, peakdb1))
(esc_peakdb2, esc_peakdb1))

# check that the noise is being squashed at all. this needs to be an aggresive check so that failure happens easily
# testing shows this to be -58dB on average
if esc_peakdb2 > -25:
raise NotAchievedException(
"Notch-per-motor had a peak of %fdB there should be none" % esc_peakdb2)

# Now do it again for an octacopter
self.context_push()
Expand All @@ -6060,20 +6107,29 @@ def DynamicRpmNotches(self):
defaults_filepath=','.join(self.model_defaults_filepath("octa")),
model="octa"
)
freq, hover_throttle, peakdb1 = self.hover_and_check_matched_frequency_with_fft(-10, 20, 350, reverse=True)
freq, hover_throttle, peakdb1, psd = \
self.hover_and_check_matched_frequency_with_fft_and_psd(-10, 50, 320, reverse=True, instance=2)
# find the noise at the motor frequency
esc_hz = self.get_average_esc_frequency()
esc_peakdb1 = psd["X"][int(esc_hz)]

# now add notch-per motor and check that the peak is squashed
self.set_parameter("INS_HNTCH_HMNCS", 1)
self.set_parameter("INS_HNTCH_OPTS", 2)
self.reboot_sitl()

freq, hover_throttle, peakdb2 = self.hover_and_check_matched_frequency_with_fft(-15, 20, 350, reverse=True)
freq, hover_throttle, peakdb2, psd = \
self.hover_and_check_matched_frequency_with_fft_and_psd(-15, 50, 320, reverse=True, instance=2)
# find the noise at the motor frequency
esc_hz = self.get_average_esc_frequency()
esc_peakdb2 = psd["X"][int(esc_hz)]

# notch-per-motor should do better, but check for within 5%
if peakdb2 * 1.05 > peakdb1:
# notch-per-motor will be better at the average ESC frequency
if esc_peakdb2 > esc_peakdb1:
raise NotAchievedException(
"Notch-per-motor peak was higher than single-notch peak %fdB > %fdB" %
(peakdb2, peakdb1))
(esc_peakdb2, esc_peakdb1))

except Exception as e:
self.print_exception_caught(e)
ex = e
Expand Down Expand Up @@ -11253,7 +11309,7 @@ def tests2b(self): # this block currently around 9.5mins here
self.MotorVibration,
Test(self.DynamicNotches, attempts=4),
self.PositionWhenGPSIsZero,
Test(self.DynamicRpmNotches, attempts=4),
self.DynamicRpmNotches, # Do not add attempts to this - failure is sign of a bug
self.PIDNotches,
self.RefindGPS,
Test(self.GyroFFT, attempts=1, speedup=8),
Expand Down
2 changes: 2 additions & 0 deletions Tools/autotest/vehicle_test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -14208,6 +14208,8 @@ def add_fftd(self, fftd):
sample_rate = 0
counts = 0
window = numpy.hanning(fft_len)
# The returned float array f contains the frequency bin centers in cycles per unit of the
# sample spacing (with zero at the start).
freqmap = numpy.fft.rfftfreq(fft_len, 1.0 / messages[0].sample_rate_hz)

# calculate NEBW constant
Expand Down
4 changes: 3 additions & 1 deletion libraries/AP_InertialSensor/AP_InertialSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,9 @@ void AP_InertialSensor::HarmonicNotch::update_params(uint8_t instance, bool conv
last_center_freq_hz[instance] = params.center_freq_hz();
last_bandwidth_hz[instance] = params.bandwidth_hz();
last_attenuation_dB[instance] = params.attenuation_dB();
} else if (params.tracking_mode() != HarmonicNotchDynamicMode::Fixed) {
}

if (params.tracking_mode() != HarmonicNotchDynamicMode::Fixed) {
if (num_calculated_notch_frequencies > 1) {
filter[instance].update(num_calculated_notch_frequencies, calculated_notch_freq_hz);
} else {
Expand Down
6 changes: 0 additions & 6 deletions libraries/AP_InertialSensor/AP_InertialSensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,6 @@ class AP_InertialSensor : AP_AccelCal_Client
float calculated_notch_freq_hz[INS_MAX_NOTCHES];
uint8_t num_calculated_notch_frequencies;

// Update the harmonic notch frequency
void update_notch_freq_hz(float scaled_freq);

// Update the harmonic notch frequencies
void update_notch_frequencies_hz(uint8_t num_freqs, const float scaled_freq[]);

// runtime update of notch parameters
void update_params(uint8_t instance, bool converging, float gyro_rate);

Expand Down
Loading