Первый блин комом... (Ультразвуковой дальномер HC-SR04)

Тема в разделе "Глядите, что я сделал", создана пользователем Medium, 7 июн 2017.

  1. Medium

    Medium Нуб

    Здравствуйте уважаемые завсегдатые.
    Буквально месяц назад купил свой первый набор матрешка и куча прибамбасов к нему. Сейчас пробую создать свое устройство подключив к Ардуинке релешку и дальномер. Но возникла проблема...

    Нужно, чтобы релешка замыкалась и размыкалась в зависимости от показателя дальномера. Если расстояние менее 120 сантиметром и более 80, то реле замыкается, в остальных положениях размыкается. Но в реальности у меня получаются перещелкивания реле ВКЛ/ВЫКЛ по много раз даже когда дальномер ни кто не пересекает.
    Понимаю что проблема банально в коде, но что бы я не пытался придумать своими мозгами - не работает.

    Прошу пнуть меня в нужную сторону.
    Ниже код на данный момент:

    Код (C++):
    int Relay = 4; // Реле модуль подключен к цифровому выводу 4

    int Echo = 2; // дальномер
    int Trig = 5; // дальномер

    void setup()
    {              
      pinMode(Relay, OUTPUT);
      Serial.begin (9600);
      pinMode(Trig, OUTPUT);
      pinMode(Echo, INPUT);
    }
    unsigned int impulseTime=0;
    unsigned int distance_sm=0;

    void loop()
    {            
    digitalWrite(Trig, HIGH);
      /* Подаем импульс на вход trig дальномера */
      delayMicroseconds(10); // равный 10 микросекундам
      digitalWrite(Trig, LOW); // Отключаем
      impulseTime=pulseIn(Echo, HIGH); // Замеряем длину импульса
      int NEWdistance_sm=impulseTime/58; // Пересчитываем в сантиметры.
     
    if (NEWdistance_sm<300&&NEWdistance_sm>1) // отбрасываем бесконечные значения
    {  
    distance_sm = (distance_sm * 4 + NEWdistance_sm) / 5; // <новое среднее> = (<старое среднее>*4 + <текущее значение>) / 5
     
      Serial.println(distance_sm); // Выводим на порт
      if (distance_sm<120&&distance_sm>80) // Если расстояние менее 120 сантиметром и более 80
      {  
        digitalWrite(Relay, HIGH); // Свет горит
        delay(100);
      }
      else
      {  
        digitalWrite(Relay, LOW); // иначе не горит
      }
      delay(100);
      /* ждем 0.1 секунды, Следующий импульс может быть излучён, только после исчезновения эха от предыдущего.
       Это время называется периодом цикла (cycle period).
       Рекомендованный период между импульсами должен быть не менее 50 мс.  */

    }
    else
       {   }
    }
     
  2. akl

    akl Гуру

  3. rkit

    rkit Гуру

    С логикой в коде всё в порядке, если у вас рассчеты расстояния правильные. Не знаю формул для этого.
    Перепроверьте подключение, питание. Добавьте вывод ключевых переменных в Serial, и посмотрите, что там творится.
     
  4. Medium

    Medium Нуб

    Блин, на самом деле все оказалось намного банальнее! Датчик плохо отражает сигнал от ворсистых поверхностей. По простому он зависает в попытке измерить расстояние до нее.
    Придется искать обходной путь в коде для этого.
    Долбаные законы физики! )))
     
  5. akl

    akl Гуру

    в NewPing.h для этого предусмотрен предел дальности, после которого датчик выдает значение 0
     
  6. Medium

    Medium Нуб

    Даже с библиотекой NewPing.h те же проблемы. Уже установил sonar.ping_median(100). Реже, но все равно криво меряет расстояние до ворсистой поверхности.

    Код (C++):
    #include <NewPing.h>
    #define echo 2
    #define trig 5
    int Relay = 4; // Реле модуль подключен к цифровому выводу 4
    double distance=0;
    NewPing sonar(trig, echo, 300);
     
    void setup()
    {
      pinMode(Relay, OUTPUT);
      Serial.begin (9600);
      pinMode(trig, OUTPUT);
      pinMode(echo, INPUT);
    }
     
    void loop()
    {
      distance=sonar.convert_cm(sonar.ping_median(100));
      Serial.println(distance); // Выводим на порт
     
      if (distance<120&&distance>80) // Если расстояние менее 120 сантиметром и более 80
      {  
        digitalWrite(Relay, HIGH); // Свет горит
        delay(10);
      }
      else
      {  
        digitalWrite(Relay, LOW); // иначе не горит
      }
      delay(10);
      /* ждем 0.1 секунды, Следующий импульс может быть излучён, только после исчезновения эха от предыдущего.
       Это время называется периодом цикла (cycle period).
       Рекомендованный период между импульсами должен быть не менее 50 мс.  */

    }
     
  7. akl

    akl Гуру

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

    Medium Нуб

    Вот вот! Что в итоге и приводит к частым переключениями в релешке...
     
  9. akl

    akl Гуру

    не очень понимаю из описани что конкретно должен делать этот робот, почему релешка должна быть включена именно в присутствии объекта на расстоянии от 80 до 120 см и почему детектируемый объект ворсистый, но можно например поставить в программе таймер. Если проблема в том, что когда объект на правильном расстоянии приходят неправильные сигналы, а когда объекта нет, то ложные правильные сигналы не приходят, то можно сделать просто - вставить в программу таймер, который будет обнуляться при каждом правильном сигнале - то есть например при нахождении объекта датчик выдает хаотичные результаты, но среди них хотя бы раз в секунду приходит правильный, то просто ставим таймер чтобы релешка могла отключиться только через 2 секунды и тогда дребезга туда-сюда не будет. вот если есть ложные срабатывания при отсутствии объекта - тогда хз

    кстати, делей(10) это не 0,1 секунды, а 0,01. а датчику этому нужен дилей побольше, 50мс кажется мимнимум

    и еще, вроде эхо-пин нет надобности ставить в инпут.
     
    Последнее редактирование: 10 июн 2017
  10. Medium

    Medium Нуб

    Этот датчик расположен в туалете на бачке прямо напротив входной двери.
    Логика проста:
    1) Пока расстояние от 80 до 120 - это значит дверь в туалет закрыта и возле унитаза ни кого нет. Следовательно свет не нужен - выключаем.
    2) Если датчик измеряет расстояние более чем 120 см. значит дверь открыта, следовательно нужно включить свет, так как кто-то входит или специально открыл дверь.
    3) Если датчик измеряет расстояние меньше чем 80 см, значит кто-то находится в туалете возле унитаза - свет оставляем включенным.

    Проблема в том, что когда я зашел в туалет и нахожусь возле унитаза датчик плохо меряет расстояние и иногда выдает данные, которые заставляют релешку выключится и тут же включится.
    Я уже увеличил количество измерений, что бы нивелировать такие моменты с плохим измерением, но все равно бывают случаи когда релешка делает подряд переключение вкл-выкл.
    Естественно если я подключу к ней лампочку 220В, то такие переключения не очень благоприятно будут сказываться на сроке ее службы и вообще удобстве использования.
     
  11. rkit

    rkit Гуру

    Включение по срабатыванию, выключение через 30 секунд после последнего срабатывания.
     
  12. Medium

    Medium Нуб

    А если сидишь там минут 10? Опять переключения появятся.
     
  13. rkit

    rkit Гуру

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

    akl Гуру

    а в программе вроде наоборот.
    и зачем дистанция double, оно же в целых приходит. а, для усреднения, потому что время приходит. тогда нормально
    усреднять 100 результатов - не многовато?
     
  15. akl

    akl Гуру

    я бы вот так сделал - тупо и по быдляцки

    Код (C++):
    #include <NewPing.h>
    #define echo 2
    #define trig 5
    #define Relay 4
    int distance=0;
    unsigned long timer = millis();
    NewPing sonar(trig, echo, 300);
    void setup()
    {
      pinMode(Relay, OUTPUT);
      Serial.begin (9600);

    }
    void loop()
    {

      if (millis()-timer > 10000) { // если с момента последней засечки времени прошло больше 10 секунд
      digitalWrite(Relay, LOW);} // выключает свет

      distance=sonar.ping_cm();
      delay(100);
      Serial.println(distance); // Выводим на порт
      if (distance>120||distance<80) // Если расстояние более 120 сантиметром или менее 80 (дверь открыта или возле толчка кто-то тусит)
      {
        distance=sonar.ping_cm(); // измеряет второй раз (защита от случайных ложных включений)
        delay (100);
          if (distance>120||distance<80) {// если второй раз подряд дверь открыта или кто-то тусит
          digitalWrite(Relay, HIGH); // включает свет
          timer = millis(); //засекает время
          }
      }
    }
    то есть когда открывается дверь или кто-то заходит, приходит результат что расстояние больше 120 или меньше 80, тогда оно измеряется второй раз (это на случай если есть ложные включения при закрытой двери и пустом сортире), то есть свет будет включаться и таймер обнуляться только при двух подряд сигналах на включение. А выключаться свет будет автоматически через 10 секунд после того как перестали приходить по 2 подряд сигналы на включение. думаю должно работать.

    еще надо учитывать, что у этого датчика очень большой угол обзора, то есть маленькое разрешение, то есть если например дверь открыта, он может срабатывать на ближайший косяк например или другой какой-то неправильный предмет. тут больше подошло бы что-то лазерное, но нормальных лазерных дальномеров дешевых нету.
     
    Последнее редактирование: 11 июн 2017
  16. Troll

    Troll Гик

    Ну ведь дверь-то у вас не ворсистая? Меряйте расстояние от нее, а точнее засекайте ее отдаление(открытие).