игнорирование изменений значения потенциометра

У меня есть потенциометр 50k, подключенный к Arduino Uno. Пользователь должен настроить оттенок Adafruit NeoPixelStrip.

У меня есть следующий код внутри функции цикла:

int hue = analogRead(poti1);
int mappedHue = map(hue, 0, 1023, 0, 65535);

neoPixelStrip.fill(neoPixelStrip.gamma32(neoPixelStrip.ColorHSV(mappedHue, 255, 255)));
neoPixelStrip.show();

Если я поверну ручку потенциометра, цвета обновятся правильно и отобразят правильный оттенок. Но если я отпущу ручку, светодиоды немного мерцают (слегка меняя значения своего оттенка). Если я выведу значения потенциометра на консоль, они немного изменятся. Если я поверну потенциометр до упора влево, значения будут в диапазоне от 0 до 13 или около того. Это изменение влияет на оттенок пикселей.

Как игнорировать эти варианты значений? Я хочу, чтобы пользователь постоянно менял цвет, поэтому мне нужно постоянно читать значение.

Есть идеи?

, 👍2

Обсуждение

У вас включен выход AREF и потенциометр подключен между AREF и землей, а не VCC и землей. Это поможет с шумом. Кроме того, в качестве другой альтернативы посмотрите на поворотные энкодеры., @Rich

Не совсем уверен, что такое AREF., @WalterBeiter


2 ответа


2

Существует множество способов удаления шума, ниже приведены некоторые из них:

  • Изменяйте значение только тогда, когда оно достаточно отличается. Предположим, что диапазон составляет 0-1023. Изменяйте значение только в том случае, если входное значение отличается более чем на постоянное значение (например, 5 или 10, поэкспериментируйте с этим, чтобы найти подходящее значение для вашего потенциометра).
  • Еще один способ — использовать среднее значение последних x показаний. Вы также можете комбинировать его с предыдущим подходом. Возможна небольшая задержка из-за усреднения.

Существует множество других решений, от достаточно простых до очень сложных, поиска по удалению шума в данных датчика.

,

3

У вас есть две проблемы: шумы и потенциометр с неточными упорами на одном или обоих концах.

Горшок починить проще всего: какие самые низкие и самые высокие показания вы можете надежно получить, повернув его до упора? Используйте их вместо «0» и «1023» в вашей функции map().

Улучшение шума требует лишь немного больше усилий. Экспоненциальное среднее — это простой в написании фильтр, который весьма эффективен для подавления зашумленных данных, если отношение сигнал/шум достаточно низкое. Основная идея заключается в том, что вы добавляете только часть каждого нового значения и сохраняете (1 - эта часть) старое среднее значение; поэтому, если выбранная вами дробь равна 0,1, вы учитываете только 10% каждого нового значения, сохраняя 90% текущего среднего значения.

среднее = f*среднее + (1-f)*новое_значение

Вывод этого усредняющего фильтра может медленно реагировать на изменения, если ваша фракция слишком мала, но он менее эффективен для подавления шума, если ваша фракция слишком велика.

Обычно я использую 0,25, или 25 % от каждой новой точки и 75 % от текущего среднего значения, когда просто пытаюсь убрать незначительный шум, что, вероятно, и происходит у вас.

Вот мой фильтр усреднения 25%:

int16_t xpavg(int16_t newdat, int16_t avg){

   // (3 * avg + 1 * newdat) / 4
   return( (((avg<<2) - avg + newdat) + 2) >> 2 );
}

Как видите, я выполняю арифметические операции в фиксированной точке — так же, как и целочисленные арифметические операции, но с предполагаемой двоичной точкой на 2 разряда влево во время вычислений. И я выбираю дробь, которая представляет "хорошо" в двоичном формате - в данном случае 1/4 - для ускорения арифметики (поскольку умножение и деление можно заменить сдвигом).

Функция просто умножает текущее среднее значение на 4 и вычитает старое среднее значение (фактически умножая на 3), добавляет новое значение и делит сумму на 4. (Этот дополнительный "+2" округляет дробные биты непосредственно перед тем, как мы отодвиньте их). Конечный результат:
(3*среднее + новые данные)/4 (== 3/4*среднее + 1/4*новые данные).

Если бы вы отбирали пробы из горшка только каждую секунду или две, вы, вероятно, заметили бы задержку, поскольку фильтр отстает, когда вы поворачиваете сосуд, и догоняет, когда вы останавливаетесь. Если вы сэмплируете со скоростью 10x/сек или меньше, вы не должны этого заметить.

Обновление:

Вам нужно добавить в свой код функцию xpavg() и вызывать ее каждый раз, когда вы читаете новое значение горшка (то есть "newdat"), передавая также старое среднее значение. Он возвращает новое среднее значение горшка, теперь менее подверженное влиянию шума. Вы используете это новое среднее значение вместо необработанного числа, которое вы только что прочитали из горшка, при расчете оттенка. Как предложил Дункан, вы можете — и я это делаю — инициализировать среднее значение необработанным значением банка. Вы также можете начать с нуля, но потребуется несколько выборок, чтобы приблизиться к текущим фактическим показаниям банка. Недостаток использования одного необработанного образца для начала заключается в том, что если это одно значение ужасно зашумлено, то есть сильно отличается от показаний горшка, для сходимости все равно потребуется несколько образцов.

unsigned int AvgPot = analogRead(poti1);        // инициализируем среднее значение банка

// Функции экспоненциального усреднения
int16_t xpavg(int16_t newdat, int16_t avg)
{
   // (3 * среднее + 1 * новое значение) / 4
   return( (((avg<<2) - avg + newdat) + 2) >> 2 );
}

void setup()
{
   ;    // здесь идут инициализации
}

void loop()
{
   int hue = analogRead(poti1);
   hue = xpavg(poti1, PotAvg);
   int mappedHue = map(hue, 0, 1023, 0, 65535);

   neoPixelStrip.fill(neoPixelStrip.gamma32(neoPixelStrip.ColorHSV(mappedHue, 255, 255)));
   neoPixelStrip.show();
}
,

Извините, но я не понимаю весь ваш пост. Это слишком продвинуто для меня. Я не знаю, как я собираюсь реализовать это в своем коде. Откуда эта переменная new_value? Или что он представляет? А ф? И что именно я должен передать в качестве параметра newdat? Я не знаю, как использовать вашу функцию в моем коде..., @WalterBeiter

Идея состоит в том, что вы хотите избежать «дрожания» показаний вашего потенциометра. Алгоритм, подобный экспоненциальному среднему из этого ответа, сглаживает входные значения с течением времени. Вы должны создать глобальную переменную int16_t (назовем ее average), чтобы хранить значение "скользящего среднего" для вашего банка., @Duncan C

Каждый раз через вашу функцию loop() вы читали новое "сырое" значение из банка, а затем вызывали функцию xpavg(), передавая новое значение банка и старое среднее значение. Затем вы должны сохранить возвращенное среднее значение в своей средней переменной и использовать его в качестве значения горшка. По мере изменения значения горшка среднее значение будет меняться медленнее и будет игнорировать быстрые «нервные» изменения ввода., @Duncan C

Но каково начальное значение «среднего»?, @WalterBeiter

Начальное значение не имеет большого значения. По мере поступления новых значений среднее значение будет совпадать с фактическим средним значением. Я предлагаю установить его на значение посередине между вашим максимальным и вашим минимальным значением для начала. Или вы можете инициализировать его своим самым первым аналоговым чтением., @Duncan C

Я предполагаю, что в строке hue = xpavg(poti1, PotAvg); вам нужно передать hue в качестве первого параметра, а не poti1. poti1 — это просто объявление пин-кода. Мерцание пропало, но я не могу получить весь диапазон значений оттенков. Я думаю, это потому, что мне понадобится потенциометр выше 1023 или 0, чтобы получить среднее значение 1023. Кажется, я не могу достичь начальной и конечной точек., @WalterBeiter

а если добавить конденсатор?, @WalterBeiter

Да, это еще один очень разумный способ фильтрации шума. Что там про мужика с молотком говорят, что все похоже на гвоздь? Я инженер-программист! :), @JRobert