Как добавить гистерезис к пороговым значениям?

Этот код сравнивает аналоговое входное значение с двумя порогами, имеющими три области напряжения. Затем он включит светодиод в соответствии с областью, в которой находится считанное напряжение.

Проблема в том, что когда измеренное напряжение очень близко к пороговому значению, шум будет мешать выходу, что приведет к неконтролируемому переключению между светодиодами.

Мне нужно было бы добавить что-то вроде ГИСТЕРЕЗИСА вокруг моих порогов, чтобы избежать неконтролируемого переключения, но я понятия не имею, как это записать на языке кода. Может ли кто-нибудь помочь мне написать бит порога?

Вот полный код:

// эти константы не изменятся:
const int BatteryVoltagePin = 14;   // контакт, к которому подключено напряжение батареи
const int GreenLedPin = 3;           // контакт, к которому подключен зеленый светодиод (на зарядке)
const int OrangeLedPin = 4;          // контакт, к которому подключен оранжевый светодиод (без риска)
const int RedLedPin = 5;             // контакт, к которому подключен красный светодиод (низкий заряд батареи)

// Переменные изменятся:
int currentVoltage ;         // текущее значение напряжения
int previousVoltage ;     // предыдущее значение напряжения

void setup() {
  // инициализируем вывод напряжения батареи как вход:
  pinMode(BatteryVoltagePin, INPUT);
  // инициализируем зеленый светодиод как выход:
  pinMode(GreenLedPin, OUTPUT);
  // инициализируем оранжевый светодиод как выход:
  pinMode(OrangeLedPin, OUTPUT);
  // инициализируем красный светодиод как выход:
  pinMode(RedLedPin, OUTPUT);
}


void loop() {
  // считываем напряжение на выводе напряжения батареи:
  currentVoltage = digitalRead(BatteryVoltagePin);

  // сравнить текущее значение напряжения с предыдущим значением
 if (currentVoltage != previousVoltage) 

 delay(10);

    // значение выше максимального порога
    {
     if ((( ( analogRead(BatteryVoltagePin) ) > ( 699 ) ))   
        {
        digitalWrite(GreenLedPin, HIGH);
        }
        else
        {
          digitalWrite(GreenLedPin, LOW );
        }
    }

    //значение между двумя порогами (средняя область)
    {
     if (( ( ( analogRead(BatteryVoltagePin) ) <= ( 699 ) ) && ( ( analogRead(BatteryVoltagePin) ) >= ( 628 ) ) ))          
        {       
        digitalWrite(OrangeLedPin, HIGH );
        }
        else
        {
          digitalWrite(OrangeLedPin, LOW );
        }
    }

    // значение ниже минимального порога
    {
     if (( ( analogRead(BatteryVoltagePin) ) < ( 628 ) ))   
        {
        digitalWrite(RedLedPin, HIGH);
        }
        else
        {
          digitalWrite(RedLedPin, LOW );
        }
     }


  // сохранить текущее состояние как последнее состояние,
  //для следующего раза в цикле
  previousVoltage = currentVoltage;
 }

, 👍7


4 ответа


7

Добавить поведение гистерезиса в ваш код несложно. Вам просто нужно сохранить состояние, в котором вы находитесь, и сделать пороги перехода в другое состояние зависимыми от этого.

Вы можете использовать перечисление для хранения состояния:

enum BatteryStates {
  Red, Orange, Green
};

Затем вместо определения и проверки одного порогового значения определите два пороговых значения для каждого перехода, разнесенных на такое расстояние, чтобы не наблюдать нежелательного поведения переключения.

Например, при переходе от зеленого к оранжевому вы можете сделать что-то вроде этого:

if ((state == Green) && (battery < 690))
  state = Orange;

А в другом направлении будет использоваться немного более высокий порог

if ((state == Orange) && (battery > 700))
  state = Green;

чтобы небольшие колебания напряжения не вызывали переворотов.

(Обратите внимание, что мой код предполагает

int battery = analogRead(BatteryVoltagePin);

Переменная state должна быть объявлена и инициализирована следующим образом:

enum BatteryStates state = Green;

Вместо enum вы можете использовать простое int и сопоставить каждое состояние с числом (например, красный = 1, оранжевый = 2, зеленый = 3), но использование enum упрощает чтение и написание кода. (На самом деле enum использует целые числа.)

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

,

2

Это включает гистерезис и использует некоторые циклы и массивы для упрощения и сжатия кода, поскольку логика для каждого диапазона по сути та же самая. Больше диапазонов легко включить, расширив массивы Cutoffs и LedPins (на один светодиод больше, чем cutoff).

const int VoltagePin = 14;
const int Cutoffs[] = { 628 , 699 };
const int NumCutoffs = sizeof(Cutoffs)/sizeof(int);
const int Hysteresis = 5;

const int LedPins[NumCutoffs+1] = {5,4,3};

int Level = 0; 
int Voltage = 0;

void setup()
{
  for (int i=0; i<=NumCutoffs; ++i) {
    pinMode(LedPins[i],OUTPUT);
  }
}

void loop()
{
  Voltage = analogRead(VoltagePin);

  while (Level < NumCutoffs && Voltage >= Cutoffs[Level]) {
    ++Level;
  }

  while (Level > 0 && Voltage < Cutoffs[Level-1] - Hysteresis) {
    --Level;
  }

  for (int i=0; i<=NumCutoffs; ++i) {
    digitalWrite(LedPins[i], i == Level);
  }

  delay(20);
}
,

2

Вот что я делаю. Я определяю, растет или падает напряжение, и на основе этого регулирую порог.

int ledPin = 13;   //контакт 13 будет подключен к реле MOSFET SS

void setup() {
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT);
}

void loop() {
    int sensorValue = analogRead(A0); // Это датчик напряжения основного аккумулятора
    int RLA1;   //это будет подключено к реле MOSFET SS
    float threshold; // Чтобы включить некоторый гистерезис и предотвратить дребезжание реле, мы перемещаем порог в зависимости от того, идем ли мы вверх или вниз

    float Vmainold;  // Используется для определения того, идем ли мы вверх или вниз
    float  Vmain;   // Напряжение основного аккумулятора.
    float Vaux;     // Напряжение вспомогательной батареи

    Vmain = sensorValue / 204.6 + 9.8;   // Добавьте обратно ~10 В, которые сдвигает стабилитрон для более высокого разрешения

   if (Vmain > Vmainold) threshold = 13.2;  // Если напряжение растет, устанавливаем порог 13.2
   else
    if (Vmain < Vmainold) && (Vmainold > 12.8) threshold = 12.8 // Если напряжение падает и предыдущее напряжение выше TH, установить theshhold на 12.8
    else
        if (Vmain = Vmainold) threshold = 13.2;  // Если напряжение стабильно, устанавливаем порог 13.2

   if (Vmain >= threshold) RLA1 = 1; // Зарядка

   else
    if (Vmain < threshold) { RLA1 = 0;  Vmainold = Vmain; }  // Не подключено

   delay(1000);
   Serial.print(Vmain);
   Serial.print("  Main  ");
   Serial.print(Vmainold);
   Serial.print("  old  ");
   Serial.println();
   Vmainold = Vmain;
   digitalWrite(ledPin, RLA1);
}

`

,

2

Государственная машина — это надежный подход.

Для этого простого случая вы также можете решить эту проблему, реализовав небольшие мертвые зоны вокруг уровней изменения. Когда значение находится в диапазоне около порога перехода, ничего не делайте. Только когда значение определенно выше или ниже перехода, вносите изменения. Ваш код будет таким:

// Переменные будут изменены:
int currentVoltage = 0;         // текущее значение напряжения
int previousVoltage = 0;     // предыдущее значение напряжения

void loop() {
  int deadband = 5;
  // в самом первом цикле, начать без мертвой зоны
  // это необходимо в случае, если пусковое напряжение находится в
  // середина диапазона перехода
  if (0 == previousVoltage) {
    deadband = 0;
  }

  // считываем напряжение на выводе напряжения батареи:
  currentVoltage = analogRead(BatteryVoltagePin);

  // проверить напряжение по трем диапазонам
  // значение выше максимального порога
  if ((699+deadband) < currentVoltage) {   
    digitalWrite(GreenLedPin, HIGH);
    digitalWrite(OrangeLedPin, LOW );
    digitalWrite(RedLedPin, LOW );
  }

  //значение между двумя порогами (средняя область)
  if (((628+deadband) < currentVoltage) && (currentVoltage <= (699-deadband))) {          
    digitalWrite(GreenLedPin, LOW);
    digitalWrite(OrangeLedPin, HIGH);
    digitalWrite(RedLedPin, LOW);
  }

  // значение ниже минимального порога
  if (currentVoltage < (628-deadband)) {
    digitalWrite(GreenLedPin, LOW);
    digitalWrite(OrangeLedPin, LOW );
    digitalWrite(RedLedPin, HIGH);
  }

  // Обратите внимание, что на данный момент ни один из случаев if, описанных выше, не выполняется.
  // может быть правдой. Когда напряжение находится прямо около
  // пороги, ничего не делать. Только когда напряжение окончательно
  // в этом диапазоне изменяется состояние светодиода.

  // сохранить текущее состояние как последнее состояние,
  //для следующего раза в цикле
  previousVoltage = currentVoltage;
  delay(10);

  return;
}

Функционально это даст тот же результат, что и гистерезис.

,