Устройство на Ardiuno для контроля пересечения барьера.

Ардуинщики всех стран - объединяйтесь! В этом форуме, конечно.
Ответить
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

jcxz писал(а): Чт июн 18, 2026 11:19:06 или скажем - человек несёт большое полотенце (или ещё какую вещь), габаритную. Которую оставляет затем в ванной. Которая даст ложную отметку. И получается двойная отметка.
Сработает правильно. Могу продемонстрировать.
jcxz писал(а): Или даже муха пролетела в ванную на уровне датчиков и привет. :)))
Не среагирует.
jcxz писал(а): Да даже элементарно: у человека на халате пояс развязался и при входе в ванную мотанулся вперёд, на обратном качании прилипнув к телу. Уже получается как будто зашли двое.
Нет, посчитает, как одного.
........
Я вижу, тут описание алгоритма мало кто читает. И отвечают собственным представлениям.
jcxz писал(а): Это уж не говоря про всякие случаи прохода двух человек вместе, вплотную,
Это да. Но обмануть можно любую систему.
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

Закончил тестирование, всё работает. Для отладки – зелёный и красный светодиоды показывают перекрытие 1 и 2 каналов, соответственно. Жёлтый – имитация включения нагрузки. Индикатор – количество незавершённых пересечений.
https://cloud.mail.ru/public/EBK5/6aH1aWLep
Платы теперь только три вместо пяти.
https://cloud.mail.ru/public/xPNU/TAw6CASJk

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

Код: Выделить всё

//
// Shirokov V.V. aka Massaraksh7, 18.06.2026
//
#define IN1   2
#define IN2   3
#define FIRE  4
#define FB1   5
#define FB2   6

#define R_WAIT 0
#define R_DIR  1
#define R_BACK 2
#define R_DO_DIR  3
#define R_DO_BACK 4
#define MAX_VIS  9      //---Максимальное число посетителей 

int count,rate;
int m1[3],m2[3];

int adata[10][8]={ 0, 0, 0, 0, 0, 0, 0, 0,  // 0 - 7, 8, 9,10,11,12, 0, 0,
                   8, 9, 0, 0, 0, 0, 0, 0,  // 1
                   7, 8,11,10,13, 0, 0, 0,  // 2 
                   7, 8, 9,10,13, 0, 0, 0,  // 3
                   8, 9,12,13, 0, 0, 0, 0,  // 4
                   7, 9,10,12,13, 0, 0, 0,  // 5
                   7, 9,10,11,12,13, 0, 0,  // 6
                   7, 8, 9, 0, 0, 0, 0, 0,  // 7
                   7, 8, 9,10,11,12,13, 0,  // 8   
                   7, 8, 9,10,12,13, 0, 0}; // 9

volatile int sign1,sign2; //---Сигнал 0 или 1
volatile int tim0; //---счётчик основного цикла

//---Записать цифру в индикатор
void setDig(int n) {
int i,j;  
for (j=7;j<=13;j++)digitalWrite(j,LOW);
for (i=0;i<8;i++)
   {
   if (adata[n][i]==0)break;
   digitalWrite(adata[n][i],HIGH);
   }
}

//---Прочитать состояние входов и записать в буфер
void ReadAndShift() {
m1[2]=m1[1];m1[1]=m1[0];m1[0]=sign1;
m2[2]=m2[1];m2[1]=m2[0];m2[0]=sign2;
if (sign1==0) digitalWrite(FB1,LOW); else digitalWrite(FB1,HIGH);
if (sign2==0) digitalWrite(FB2,LOW); else digitalWrite(FB2,HIGH);
}

//---Очистка буфера
void clearBuf() {
int i;  
for(i=0;i<3;i++){m1[i]=0;m2[i]=0;}  
}

//---Подключить нагрузку
void fireON() {
digitalWrite(FIRE,HIGH);  
}

//---Отключить нагрузку
void fireOFF() {
digitalWrite(FIRE,LOW);  
}

//---Прерывание таймера 1 канала
ISR(TIMER1_COMPA_vect) {
sign1=1;
}

//---Прерывание таймера 2 канала
ISR(TIMER2_COMPA_vect) {
sign2=1;
}

//---Прерывание по переднему фронту 1 канала
void cannal_1() {
sign1=0;
TCNT1=0;
}

//---Прерывание по переднему фронту 2 канала
void cannal_2() {
sign2=0;  
TCNT2=0;
}

//---300 мкс - основной цикл
void startTimer0() {
cli();
TCCR0A |= (1 << WGM01);
TCCR0B |= (1 << CS01) | (1 << CS00);
OCR0A = 74;
TIMSK0 |= (1 << OCIE0A);
sei();
}

//---90 мкс - 1 канал
void startTimer1() {
cli();
TCCR1A = 0;
TCCR1B = 0;
TCCR1B |= (1 << WGM12);
OCR1A = 1439;
TIMSK1 |= (1 << OCIE1A);
TCCR1B |= (1 << CS10);
sei();
}

//---90 мкс - 2 канал
void startTimer2() {
cli();
TCCR2A = 0;
TCCR2B = 0;
TCCR2A |= (1 << WGM21);
TCCR2B |= (1 << CS21);
OCR2A = 179;
TIMSK2 |= (1 << OCIE2A);
sei();
}

//---Инициализация
void setup() {
  int j;
  for (j=7;j<=13;j++)pinMode(j,OUTPUT);    
  pinMode(IN1,INPUT);pinMode(IN2,INPUT);
  pinMode(FIRE,OUTPUT);pinMode(FB1,OUTPUT);pinMode(FB2,OUTPUT);
  digitalWrite(FIRE,LOW);digitalWrite(FB1,LOW);digitalWrite(FB2,LOW);
  count=0;rate=R_WAIT;
  clearBuf();
  sign1=1;sign2=1;
//---Запустить таймеры  
  tim0=1;
  startTimer0();
  startTimer1();startTimer2();
//---Запустить внешние прерывания
  attachInterrupt(digitalPinToInterrupt(IN1),cannal_1,RISING);  
  attachInterrupt(digitalPinToInterrupt(IN2),cannal_2,RISING);    
}

//---Основной цикл по таймеру 0
ISR(TIMER0_COMPA_vect) {
tim0--;if (tim0!=0) return;
tim0=1;
  if (rate==R_WAIT) //---Режим ожидания основной
     {
     ReadAndShift();
     if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_BACK;return;}
     if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_DIR;return;}
     return;
     }
  if (rate==R_DIR) //---Режим возможного пересечения вперёд
     {
     ReadAndShift();
     if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_WAIT;return;}
     if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_DO_DIR;return;}      
     return;
     }
  if (rate==R_BACK) //---Режим возможного пересечения назад
     {
     ReadAndShift(); 
     if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_WAIT;return;}         
     if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_DO_BACK;return;}           
     return;
     }     
  if (rate==R_DO_DIR) //---Точно пересечение вперёд
     {
     if (count<MAX_VIS) count++;
     setDig(count);
     if (count==1) fireON();
     tim0=2000;clearBuf();
     rate=R_WAIT;
     return;
     }
  if (rate==R_DO_BACK) //---Точно пересечение назад
     {
     if (count>0) count--;
     setDig(count);
     if (count==0) fireOFF();
     tim0=2000;clearBuf();
     rate=R_WAIT;
     return;
     }    
}

void loop() {
while (0==0) {};
}

И, да, для тех, кто пропустил предыдущую потерянную ветку, подобное устройство уже работает у меня дней 10, без единого сбоя.
Реклама
Мучитель микросхем
Сообщения: 486
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Ну, три платы конечно уже лучше, чем пять.
Код конечно же по-прежнему ужасен. И по-прежнему, "rate" переводится как "скорость, темп", а не как "режим". Режим - это mode.
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

А я до сих пор жду Вашего кода, который обещали ещё неделю назад. Но знаю, что не дождусь. )))
Реклама
Эиком - электронные компоненты и радиодетали
Мучитель микросхем
Сообщения: 486
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

А смысл? Вы его всё равно не поймете, поскольку я не пишу для Ардуины на AVR. И во-вторых, я ранее, в пропавшей теме, уже объяснял основные моменты.
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

Я так и думал )))
Реклама
Это не хвост, это антенна
Сообщения: 1330
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

Massaraksh7 писал(а):Закончил тестирование, всё работает.
Н-да...

Код: Выделить всё

m1[0]==0 && m1[1]==0 && m1[2]==0
Видна попытка попытка давить дребезг. А если выясниться, сто надо не 3, а 5 считываний состояний датчика для подавления? 3 считывания через 90мкс выглядят сомнительно мало.

Код: Выделить всё

  if (rate==R_DIR) //---Режим возможного пересечения вперёд
     {
     ReadAndShift();
     if (m1[0]==0 && m1[1]==0 && m1[2]==0 && m2[0]==0 && m2[1]==0 && m2[2]==0){rate=R_WAIT;return;}
     if (m1[0]==1 && m1[1]==1 && m1[2]==1 && m2[0]==1 && m2[1]==1 && m2[2]==1){rate=R_DO_DIR;return;}      
     return;
     }
     
....

 if (rate==R_DO_DIR) //---Точно пересечение вперёд
     {
     ....
     
Явно не все _реальные_ ситуации (то есть комбинации сигналов) рассмотрены. Подумайте, какие ещё случаются в жизни. Кстати, касательно критериев входа и выхода та же самая картина, рассмотрены не_все комбинации сигналов, указывающие на вход или отказ от него. Попробуйте представить пересечение барьера человеком в халате, пОлы которого после прохода барьера могут кратковременно попасть в барьер после прохода.
Мучитель микросхем
Сообщения: 486
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

А что, халат отдельно от человека может проходить? Или в халате есть дырки и он развивается за человеком, аки королевская мантия? :)) :))
Источником второго сигнала могут быть только руки. Если конечно датчик не ставить на уровне ног.

Вообще, в прошлой теме я уже ж объяснял, как измерить габарит проходящего объекта.
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

tonyk писал(а): Чт июн 18, 2026 16:03:32 Видна попытка попытка давить дребезг. А если выясниться, сто надо не 3, а 5 считываний состояний датчика для подавления?
Тогда сделаю цикл. Пока три - было лень.
tonyk писал(а): 3 считывания через 90мкс выглядят сомнительно мало.
Через 300мкс
Очередной просмотр кода по диагонали...
tonyk писал(а): Попробуйте представить пересечение барьера человеком в халате, пОлы которого после прохода барьера могут кратковременно попасть в барьер после прохода.
Представил. Устройство сработает правильно.
Нет, ну я уже устал. Вы алгоритм читали? Код смотрели внимательно? Почему тогда неверные выводы?
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

Rapra писал(а): Чт июн 18, 2026 16:10:11 Источником второго сигнала могут быть только руки.
Всё это я уже пробовал. Проходил, выставив вперед полусогнутую руку. Сработало правильно. Даже видео записал.
Это не хвост, это антенна
Сообщения: 1330
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

Massaraksh7 писал(а):Почему тогда неверные выводы?
Выводы верные. Во-первых, я нарисовал граф состояний конечного автомата (КА), поэтому мне видно, что вы рассматриваете не_все возможные комбинации сигналов, которые возникают в реальной жизни. Во-вторых, более сложный КА появился после анализа статистики, набранной в процессе испытаний. Плюс я изначально хотел сделать алгоритм, настраиваемый на разное расстояние между лучами (использую два датчика), чтобы упростить их вписывание в интерьер квартиры. Более того, мой алгоритм учитывает, что датчики могут быть размещены не только на уровне плеч, но и на уровне ног.
Друг Кота
Сообщения: 9188
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

Ну вот... Таймеры и прерывания пошли. Сразу же предлагали избавит'ся от триггеров.
Осталось сделать усилитель с автосмещением))
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

roman.com писал(а): Чт июн 18, 2026 23:01:20 Ну вот... Таймеры и прерывания пошли. Сразу же предлагали избавит'ся от триггеров.
Осталось сделать усилитель с автосмещением))
Мне интересен не только результат, но и процесс.
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

tonyk писал(а): Чт июн 18, 2026 20:21:41 Более того, мой алгоритм учитывает, что датчики могут быть размещены не только на уровне плеч, но и на уровне ног.
Мой тоже. Вы просто поленились посмотреть код.
Мучитель микросхем
Сообщения: 486
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

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

Здесь в коде можно увидеть следующие последовательности:
0 0 0 1 1 1 _ _ _/- - -
1 1 1 0 0 0 - - -\_ _ _

0 0 0 0 0 0 _ _ _ _ _ _
1 1 1 1 1 1 - - - - - - -

А дальше начинается какая-то путаница с условиями - R_DIR (????) и R_BACK.
Я же еще в прошлой теме давал правильные названия. DIR - это от слова direction - направление. Просто направление. А не конкретное направление.

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

Код: Выделить всё

if(inputA== true)
{
    if(inputB == false)
    {
        //
     }else
     {
        //
     }
 }else
 {
     if(inputB == true)
     {
         //
     }else
     {
         //
     }
 } 
Это не хвост, это антенна
Сообщения: 1330
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

Rapra писал(а): в нем реализуется тот самый алгоритм квадратурного энкодера
Если сравнить конечные автоматы для квадратурного энкодера и счётчика вошедших-вышедших, то первый это 1/3 от второго по числу состояний. Второй легко заменяет первый, а вот первый не во всех случаях распознаёт направление движения и количество вошедших-вышедших.

Судя по видео с тестами, ТС при тестировании не учёл того, о чём упоминал jcxz. Будь у его девайса способность логгирования, он бы увидел ряд забавных вещей в поведении его КА. Думаю, что как только его девайс будет установлен на входах во все помещения, он столкнётся со сбоями в распознавании входа-выхода.
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

Rapra писал(а): Пт июн 19, 2026 06:16:31 К сожалению, такой код довольно тяжело воспринимать из-за его отвратительного стиля.
Человек, который на форуме не показал ни одного серьёзного блока своего кода, не является для меня авторитетом в экспертизе кода.
Я тоже мог бы зайти на форум, скажем, балерин, и высказывать там своё экспертное мнение. Но я этого не делаю. )))
А это

Код: Выделить всё

if(inputA== true)
{
    if(inputB == false)
    {
        //
     }else
     {
        //
     }
 }else
 {
     if(inputB == true)
     {
         //
     }else
     {
         //
     }
 } 
просто кошмар.
И на этом я по поводу кода с Вами препираться заканчиваю. Мнение любителя мне не интересно.
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

roman.com писал(а): Чт июн 18, 2026 23:01:20 Осталось сделать усилитель с автосмещением))
Почитал я про это автосмещение, и так понял, что основные его преимущества - это стабилизация по температуре и напряжению. Учитывая, что такая схема предназначена для использования в домашних условиях, не очень понятно, так ли оно нужно? Так-то, ничего сложного, перекинуть контакт резистора с питания на коллектор, но оно так уж необходимо в данном случае?
Друг Кота
Сообщения: 9188
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

1.
Усилитель с автосмещением не требует настройки. Спаял и все работает.))
В Вашей схеме требуется подбирать резистор в цепи базы транзистора для получения на коллекторе транзистора половины напряжения питания.

2.
При увеличении сопротивления в коллекторе транзистора растёт коэффициент усиления.
Однако с ростом коэффициента усиления растёт и коэффициент температурной нестабильности.

Итого:
Высокая температурная стабильность и отсутствие настройки увеличивает повторяемость и снижает трудозатраты ))

Поэтому все нормальные люди делают усилители с автосмещением.
Открыл глаза
Аватара пользователя
Сообщения: 74
Зарегистрирован: Ср июн 03, 2026 18:51:39
Откуда: Воронеж

Сообщение Massaraksh7 »

Ну, в общем, понятно. Попробую перепаять.
Ответить

Вернуться в «Arduino»