Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

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

Компоненты и расходные материалы

Arduino Nano R3
× 1
Стандартный ЖК-экран Adafruit - 16x2, белый на синем
× 1
ИС операционного усилителя TL081
× 1
Динамик:0,25 Вт, 8 Ом
× 1
NPN-транзистор общего назначения
× 1
Поисковые катушки
× 2
Резисторы, конденсаторы
× 1

Приложения и онлайн-сервисы

IDE Arduino

Об этом проекте

На этот раз я покажу вам, как сделать чувствительный металлоискатель, способный различать черные и цветные металлы. Чувствительность удовлетворительная, учитывая, что это относительно простое устройство.

Этот проект спонсируется PCBgogo:

https://www.pcbgogo.com/promo/from_MirkoPavleskiMK

Это продолжение проекта Дэвида Крокера, представленного на форуме Arduino CC в 2013 году. Я решил протестировать его код, потому что не нашел никаких доказательств (фото или видео) того, что этот металлоискатель был кем-то сделан и хорошо работает.

Сначала я сделал базовую версию с кодом, представленным на GitHub, чтобы убедиться в функциональности устройства, а затем обновил код, чтобы он имел звуковой сигнал и визуальную информацию о типе устройства на ЖК-дисплее 16 на 2. обнаруженный объект (черные или цветные металлы) и гистограмма на ЖК-дисплее для определения близости обнаруженных объектов.

Устройство очень простое в сборке и состоит всего из нескольких компонентов:

- микроконтроллер Arduino Nano

- Операционный усилитель (в моем случае LT1677, но можно использовать TL081 или 741)

- Мало резисторов и конденсаторов

- Маленький транзистор и динамик

- ЖК-дисплей

- 3 переключателя

- Потенциометр

- Аккумулятор

- И поисковые катушки

Это технология детектора индукционного баланса VLF (очень низкой частоты), которая содержит две идентичные катушки:катушку передатчика и катушку приемника. Как и во всех индукционных датчиках баланса, балансировка катушек очень важна. Потенциометр используется для обнуления небольшой составляющей сигнала, сдвинутой по фазе на 90 градусов. (синфазная составляющая обнуляется регулировкой относительного размещения катушек в типичном стиле IB-детектора). Каждая из катушек намотана на корпусе 11 см с использованием 64 витков эмалированного медного провода 0,5 мм ^ 2 в форме D, обернута вокруг них лентой, экранирована алюминиевой фольгой, связанной с луженой медной проволокой (стараясь оставить небольшой зазор, чтобы экран не ведет себя как закороченный виток), и привязал их к пластиковой пластине.

Сначала нам нужно определить параллельную резонансную частоту первичной цепи катушка-конденсатор с помощью одного из многих онлайн-калькуляторов. Я измерил его с помощью осциллографа, но если вы будете придерживаться размеров, указанных выше, это будет ровно 7,64 кГц, поэтому вы можете напрямую ввести значение, указанное в коде. В случае другого значения резонансной частоты нам необходимо внести соответствующее изменение в код в очереди:

#define TIMER1_TOP (249) // точная настройка частоты

Как видно на видео, результаты на удивление хорошие. Без наличия металла устройство отлично работает. Дальность действия относительно велика и, например, металлическая крышка диаметром 15 см обнаруживается на расстоянии более 30 см. Более крупные металлические предметы обнаруживаются на расстоянии более 40-50 см. Мы можем обнаружить небольшую монету на расстоянии 15 см в воздухе. Я использую две литиевые батареи для источника питания, которые соединены последовательно (7,4 В), и это напряжение подключается к входу Vin Arduino. Потребление не превышает 20 мА, поэтому батарейки работают очень долго. В видео подробно описывается конструкция всего устройства.

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

Код

  • Код Arduino
  • Библиотека LcdBarGraph.
Код Arduino Arduino
 // Металлоискатель с индукционным балансом // Мы запускаем ЦП на 16 МГц, а тактовую частоту АЦП на 1 МГц. На этой скорости разрешение АЦП снижается до 8 бит. // Таймер 1 используется для деления системной тактовой частоты примерно на 256 для получения прямоугольной волны 62,5 кГц. // Это используется для управления таймером 0, а также для запуска преобразования АЦП. // Таймер 0 используется для деления выхода таймера 1 на 8, что дает сигнал 7,8125 кГц для управления передающей катушкой. // Это дает нам 16 АЦП. тактов для каждого преобразования АЦП (на самом деле это занимает 13,5 циклов), и мы берем 8 выборок за цикл напряжения возбуждения катушки. // АЦП реализует четыре фазочувствительных детектора с интервалом в 45 градусов. Использование 4 вместо 2 позволяет нам отменить третью гармонику // частоты катушки. // Таймер 2 будет использоваться для генерации тона для наушника или гарнитуры. 235 вверх. // Подключение:// Подключите цифровой контакт 4 (псевдоним T0) к цифровому контакту 9 // Подключите цифровой контакт 5 через резистор к первичной катушке и настроечному конденсатору // Подключите выход усилителя приема к аналоговому контакту 0. Выход приема усилитель должен быть смещен примерно на половину аналогового опорного сигнала. // При использовании питания USB измените аналоговое опорное напряжение на вывод 3,3 В, потому что на шине + 5 В слишком много шума, чтобы получить хорошую чувствительность. # include  #include  #define max_ampAverage 200LiquidCrystal lcd (6, 7, 10, 11, 12, 13); LcdBarGraph lbg (&lcd, 16, 0, 1); #define TIMER1_TOP (259) // можно настроить это для точной настройки частоты для настройки катушки (см. выше) #define USE_3V3_AREF (1) // установить 1 для работы на Arduino с питанием от USB, 0 для встроенного atmega28p без источника питания 3,3 В // Определение цифровых выводов // Цифровой вывод 0 не используется, однако, если мы используем последовательный порт для отладки, то это последовательный inputconst int debugTxPin =1; // вывод передачи зарезервирован для отладкиconst int encoderButtonPin =2; // кнопка кодировщика, также IN0 для выхода из режима сна const int earpiecePin =3; // динамик, также известный как OCR2B для генерации тонаconst int T0InputPin =4; const int coilDrivePin =5; const int LcdRsPin =6; const int LcdEnPin =7; const int LcdPowerPin =8; // включение питания и подсветки ЖК-дисплеяconst int T0OutputPin =9; const int lcdD4Pin =10; const int lcdD5Pin =11; // выводы 11-13 также используются для ICSPconst int LcdD6Pin =12; const int LcdD7Pin =13; // Определения аналоговых выводовconst int ReceiverInputPin =0; const int encoderAPin =A1; const int encoderBpin =A2; // Аналоговые выводы 3-5 не используется // Переменные используются только бункерами ISRint16_t [4]; // бины, используемые для накопления показаний АЦП, по одному для каждой из 4 фаз uint16_t numSamples =0; const uint16_t numSamplesToAverage =1024; // Переменные, используемые ISR и за его пределами volatile int16_t усреднения [4]; // когда мы накопили достаточно показаний в ячейках, ISR копирует их сюда и запускает сноваvolatile uint32_t ticks =0; // системный счетчик тиков для хронометражаvolatile bool sampleReady =false; // указывает, что массив средних значений был обновлен // Переменные используются только вне ISRint16_t calib [4]; // значения (установленные во время калибровки), которые мы вычитаем из средних значенийvolatile uint8_t lastctr; volatile uint16_t misses =0; // подсчитывается, сколько раз ISR выполнялась слишком поздно. Должен оставаться на нуле, если все работает правильно. Const double halfRoot2 =sqrt (0.5); const double четвертьPi =3.1415927 / 4.0; const double radiansToDegrees =180.0 / 3.1415927; // Выборка и удержание АЦП происходит 2 такта АЦП (=32 система clocks) после установки флага переполнения таймера 1. // Это вносит небольшую фазовую ошибку, которую мы корректируем в вычислениях. const float phaseAdjust =(45.0 * 32.0) / (float) (TIMER1_TOP + 1); float threshold =5.0; // ниже =большая чувствительность. 10 можно использовать с хорошо сбалансированной катушкой. // Пользователь сможет настроить это с помощью потенциометра или поворотного энкодера .void setup () {lcd.begin (16, 2); // LCD 16X2 pinMode (encoderButtonPin, INPUT_PULLUP); digitalWrite (T0OutputPin, LOW); pinMode (T0OutputPin, ВЫХОД); // импульсный вывод от таймера 1 используется для питания таймера 0 digitalWrite (coilDrivePin, LOW); pinMode (coilDrivePin, ВЫХОД); // выход таймера 0, прямоугольная волна для управления передающей катушкой cli (); // Остановить таймер 0, установленный ядром Arduino TCCR0B =0; // останавливаем таймер TIMSK0 =0; // отключить прерывание TIFR0 =0x07; // очищаем все ожидающие прерывания // Настраиваем АЦП на запуск и чтение канала 0 при переполнении таймера 1 # if USE_3V3_AREF ADMUX =(1 <> 8); OCR1AL =(TIMER1_TOP / 2 &0xFF); ICR1H =(TIMER1_TOP>> 8); ICR1L =(TIMER1_TOP &0xFF); TCNT1H =0; TCNT1L =0; TIFR1 =0x07; // очищаем все ожидающие прерывания TIMSK1 =(1 <
 15000) * p =15000; } else {* p - =val; если (* p <-15000) * p =-15000; } если (ctr ==7) {++ numSamples; если (numSamples ==numSamplesToAverage) {numSamples =0; if (! sampleReady) // если предыдущая выборка была использована {memcpy ((void *) усреднения, бины, sizeof (средние)); sampleReady =true; } memset (бункеры, 0, sizeof (бункеры)); }}} void loop () {while (! sampleReady) {} uint32_t oldTicks =тики; if (digitalRead (encoderButtonPin) ==LOW) {// Нажата кнопка калибровки. Мы сохраняем текущие выходы фазового детектора и вычитаем их из будущих результатов. // Это позволяет нам использовать детектор, если катушка немного разбалансирована. // Было бы лучше использовать несколько выборок вместо одной. for (int i =0; i <4; ++ i) {calib [i] =средние [i]; } sampleReady =false; Serial.print ("Откалибровано:"); lcd.setCursor (0,0); lcd.print («Калибровка ...»); для (int я =0; я <4; ++ я) {Serial.write (''); Serial.print (calib [i]); lcd.setCursor (0,1); ЖК-печать (''); lcd.print (калибр [4]); lcd.print (""); } Serial.println (); } else {for (int i =0; i <4; ++ i) {средние [i] - =calib [i]; } const double f =200.0; // Массаж результатов, чтобы исключить чувствительность к 3-ей гармонике, и разделить на 200 double bin0 =(усредненные [0] + halfRoot2 * (средние [1] - средние [3])) / f; двойной bin1 =(средние [1] + halfRoot2 * (средние [0] + средние [2])) / f; двойной bin2 =(средние [2] + halfRoot2 * (средние [1] + средние [3])) / f; двойной bin3 =(средние [3] + halfRoot2 * (средние [2] - средние [0])) / f; sampleReady =false; // мы закончили считывание средних значений, поэтому ISR может перезаписать их снова double amp1 =sqrt ((bin0 * bin0) + (bin2 * bin2)); двойной amp2 =sqrt ((bin1 * bin1) + (bin3 * bin3)); двойной ampAverage =(amp1 + amp2) /2.0; // Выборка / удержание АЦП происходит через 2 такта после переполнения таймера double phase1 =atan2 (bin0, bin2) * radiansToDegrees + 45.0; двойная фаза2 =atan2 (bin1, bin3) * radiansToDegrees; если (фаза1> фаза2) {двойная температура =фаза1; фаза1 =фаза2; фаза2 =темп; } double phaseAverage =((phase1 + phase2) /2.0) - phaseAdjust; ((фаза1 + фаза2) /2.0) - настройка фазы; if (фаза2 - фаза1> 180,0) {if (среднее значение фазы <0,0) {среднее значение фазы + =180,0; } else {phaseAverage - =180.0; }} // Для диагностических целей распечатайте индивидуальные счетчики бункеров и 2 независимо вычисленных усиления и фазы Serial.print (misses); Serial.write (''); если (bin0> =0.0) Serial.write (''); Serial.print (bin0, 2); Serial.write (''); если (bin1> =0.0) Serial.write (''); Serial.print (bin1, 2); Serial.write (''); если (bin2> =0.0) Serial.write (''); Serial.print (bin2, 2); Serial.write (''); если (bin3> =0.0) Serial.write (''); Serial.print (bin3, 2); Serial.print (""); Serial.print (amp1, 2); Serial.write (''); Serial.print (amp2, 2); Serial.write (''); если (фаза1> =0,0) Serial.write (''); Serial.print (этап 1, 2); Serial.write (''); если (фаза2> =0,0) Serial.write (''); Serial.print (этап 2, 2); Serial.print (""); // Распечатываем окончательные амплитуду и фазу, которые мы используем, чтобы решить, что (если что) мы нашли) if (ampAverage> =0.0) Serial.write (''); Serial.print (ampAverage, 1); Serial.write (''); lcd.setCursor (0,0); lcd.print (""); lcd.print (ampAverage); lcd.setCursor (0,1); lbg.drawValue (ampAverage, max_ampAverage); если (phaseAverage> =0.0) Serial.write (''); Serial.print ((int) phaseAverage); // Решаем, что мы нашли, и сообщаем пользователю if (ampAverage> =threshold) {// Когда они удерживаются на одной линии с центром катушки:// - цветные металлы дают отрицательный фазовый сдвиг, например -90 ° для толстой меди или алюминия, -30 ° для тонкого алюминия. // Черные металлы дают нулевой фазовый сдвиг или небольшой положительный фазовый сдвиг. // Итак, мы скажем, что все, что имеет фазовый сдвиг ниже -20 градусов, является цветным. if (phaseAverage <-20.0) {Serial.print ("Цветные металлы"); lcd.setCursor (0,0); lcd.print («NonFerous»); } else {Serial.print ("Черный"); lcd.setCursor (0,0); lcd.print («Черный»); } float temp =ampAverage; int thisPitch =карта (темп, 10, 200, 100, 1500); тон (3, thisPitch, 120); в то время как (темп> порог) {Serial.write ('!'); темп - =(порог / 2); }} Serial.println (); } while (тики - oldTicks <8000) {}} 
Библиотека LcdBarGraph. C / C ++
 Нет предварительного просмотра (только загрузка). 

Схема


Производственный процесс

  1. Простой детектор детского плача своими руками с Raspberry Pi
  2. Самодельные простейшие часы Numitron IV9 с Arduino
  3. Игра с гироскопом Arduino с MPU-6050
  4. Чувствительный датчик землетрясений ADXL335 для самостоятельного изготовления
  5. DIY вольтметр с Arduino и дисплеем Nokia 5110
  6. MobBob:самостоятельный робот Arduino, управляемый смартфоном Android
  7. Простое измерительное колесо с поворотным энкодером, сделанное своими руками
  8. Датчик Интернета вещей с Arduino, Yaler и IFTTT
  9. Монитор качества воздуха своими руками с датчиком Sharp GP2Y1010AU0F
  10. Портативный счетчик Гейгера с Arduino Nano