GPIO Raspberry Pi: обнаружение движения
В предыдущих уроках мы рассмотрели основы физических вычислений и Raspberry Pi, активируя светодиоды и скрипты с помощью простой схемы с одной кнопкой. Если вы не читали предыдущие уроки, пожалуйста, сделайте это, так как они включают несколько важных моментов (таких как базовое программирование на Python и нумерация GPIO Board/BCM), которые будут пропущены в этом уроке.
В этом уроке мы расширим предыдущие уроки, заменив кнопку на PIR-датчик (для обнаружения движения). Этот урок объяснит, как собрать схему с PIR-датчиком и написать простой скрипт для вывода сообщения, когда датчик обнаруживает движение. В следующем уроке мы добавим зуммер и светодиоды к нашей схеме обнаружения, чтобы поднять тревогу при обнаружении нарушителя!
PIR-датчики
PIR-датчики, часто называемые «пассивными инфракрасными» (Passive Infrared) или «ИК-датчиками движения» (IR motion sensors), позволяют обнаруживать движение. Все объекты излучают небольшое количество инфракрасного излучения, и чем горячее объект, тем больше излучения он испускает. PIR-датчики способны обнаружить изменение уровня ИК-излучения в своей зоне обнаружения (например, когда человек входит в комнату) и таким образом фиксировать движение.
PIR-датчики, которые мы будем использовать в этом уроке, имеют три вывода: земля (ground), цифровой выход (digital out) и питание 3-5 В постоянного тока (3-5VDC in). В состоянии покоя, когда движение не обнаружено, цифровой выход остается на низком уровне, однако при обнаружении движения цифровой выход переходит на высокий уровень (3,3 В), и мы используем наш Raspberry Pi для его считывания! PIR-датчики, которые мы будем использовать в этом уроке, имеют дальность обнаружения примерно 7 метров и угол обзора 110° x 70°, поэтому они отлично подходят для наблюдения за дверью или углом комнаты.
Для этого упражнения вам понадобятся:
Макетная плата (breadboard)
6 x перемычек «папа-мама» (Male to Female Jumper Wires)
Сборка схемы
Примечание
Мы могли бы подключить PIR напрямую к GPIO-выводам Pi (и это бы отлично работало!), однако, поскольку мы собираемся добавить дополнительные функции позже, мы соберем схему на макетной плате.
Подключите три перемычки «папа-мама» к трем выводам PIR-датчика. Три вывода обозначены следующим образом: Красный — PIR-VCC (питание 3-5 В постоянного тока), Коричневый — PIR-OUT (цифровой выход) и Черный — PIR-GND (земля).
Подключите PIR-VCC к положительной шине макетной платы, подключите PIR-GND к отрицательной шине, а PIR-OUT подключите к любой другой свободной шине.
Используйте черную перемычку для подключения GPIO GND [Pin 6] на Pi к отрицательной шине макетной платы. Это та же шина, к которой мы подключили провод PIR-GND.
Используйте красную перемычку для подключения GPIO 5V [Pin 2] на Pi к положительной шине макетной платы. Это та же шина, к которой мы подключили PIR-VCC, и она будет питать наш PIR-датчик.
Мы будем использовать GPIO 7 [Pin 26] в качестве входа для определения момента, когда PIR обнаружит движение. Поэтому последний шаг — подключить GPIO 7 [Pin 26] к той же шине, что и PIR-OUT.
Считывание с помощью Python
Теперь, когда мы подключили PIR к нашему Pi, нам нужно написать скрипт на Python, чтобы регистрировать момент, когда PIR обнаруживает движение!
В нашем уроке с переключателем мы должны были подтянуть вход к высокому уровню, а затем с помощью программы на Python определить разницу между тем, находится ли вход на высоком или низком уровне. PIR же, напротив, всегда выдает низкий уровень (0 В), если движение не обнаружено, а в случае обнаружения движения — высокий уровень (3,3 В). Поэтому мы можем просто назначить вывод PIR-OUT GPIO как вход и использовать Python для обнаружения любого изменения напряжения.
Сначала импортируем библиотеку Python GPIO, импортируем библиотеку time (чтобы наш Pi мог делать паузы между шагами) и настроим нумерацию GPIO-выводов.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
Далее нам нужно дать имя нашему входному выводу, чтобы мы могли ссылаться на него позже в нашем коде на Python. Присвоение имени PIR_PIN позволит нам считывать, выдает ли PIR сигнал (то есть обнаружено ли движение). Вы можете назвать переменную вывода практически как угодно, так что попробуйте поэкспериментировать и посмотрите, что получится.
PIR_PIN = 7
Затем нам нужно определить наш GPIO-вывод, который мы только что назвали PIR_PIN, как вход.
GPIO.setup(PIR_PIN, GPIO.IN)
Следующий шаг — добавить некоторый информативный текст, чтобы мы знали, что наш PIR, Pi и программа на Python готовы к обнаружению движения. Вы можете пропустить это или изменить текст на любой другой! Следующий код выведет две строки текста с двухсекундной паузой между ними.
print "PIR Module Test (CTRL+C to exit)"
time.sleep(2)
print "Ready"
Для проверки состояния входа PIR_PIN мы используем конструкцию while True, работающую в бесконечном цикле.
while True:
if GPIO.input(PIR_PIN):
print "Motion Detected!"
time.sleep(1)
Код будет непрерывно проверять вход PIR_PIN и выводить строку текста, если вход перейдет в высокий уровень. Временная задержка между каждой итерацией цикла означает, что при обнаружении движения вы получите только один вывод, так как PIR_PIN перейдет на низкий уровень, как только движение стабилизируется.
Вот и всё! Мы можем обернуть нашу программу в небольшой код очистки, чтобы обеспечить корректное завершение. Это не обязательно, но считается хорошей практикой.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
PIR_PIN = 7
GPIO.setup(PIR_PIN, GPIO.IN)
try:
print "PIR Module Test (CTRL+C to exit)"
time.sleep(2)
print "Ready"
while True:
if GPIO.input(PIR_PIN):
print "Motion Detected!"
time.sleep(1)
except KeyboardInterrupt:
print " Quit"
GPIO.cleanup()
Вот и всё — очень простой способ подключения и программирования недорогого датчика движения с Raspberry Pi.
Если ваши сообщения просто продолжают бесконечно повторяться, возможно, чувствительность вашего PIR-датчика установлена слишком высоко. Наши PIR-датчики имеют встроенные потенциометры, которые позволяют регулировать чувствительность. Вращение по часовой стрелке увеличивает чувствительность, поэтому вращайте в обратном направлении, пока не добьетесь нужного уровня чувствительности!
Считывание на основе прерываний
Код выше использует «опрос» (polling) GPIO для определения состояния вывода. Опрос состоит из бесконечного цикла, который непрерывно проверяет состояние вывода, пока мы не увидим высокий сигнал. К сожалению, опрос потребляет много ресурсов процессора, и вы, скорее всего, заметите некоторое замедление системы при выполнении других задач.
Добавление строки time.sleep в наш код означает, что Pi проверяет вывод реже, что должно уменьшить проблемы с производительностью. Проблема с использованием временной задержки в коде заключается в том, что вы можете пропустить событие на выводе (во время паузы), поэтому это не идеальное решение, и мы по-прежнему быстро опрашиваем функцию! Есть другой способ…
Прерывания GPIO позволяют программе ожидать события GPIO. Вместо многократной проверки вывода код ждет, пока вывод не будет активирован, по сути не используя ресурсов процессора. Прерывания известны как «обнаружение фронта» (edge detection); фронт определяет переход от высокого к низкому уровню — «спадающий фронт» (falling edge) — или от низкого к высокому — «нарастающий фронт» (rising edge). Изменение состояния, или переход между низким и высоким уровнем, называется «событием» (event).
Прерывания — это функции одного события, поэтому нам нужен какой-то способ зациклить их. Очевидно, что зацикливание кода прерывания лишило бы смысла его использование — мы бы «опрашивали» функцию прерывания вместо вывода GPIO. Поэтому вместо этого мы зациклим некоторый ресурсоэкономный код и свяжем наше прерывание с функцией «обратного вызова» (call-back). Обратные вызовы — это функции, которые запускаются только при наступлении события.
Если представить ожидание письма: опрос — это когда вы весь день сидите дома, держите открытым почтовый ящик и выглядываете наружу, ожидая прихода почтальона. Прерывание в этом сценарии — это камера, которая наблюдает за улицей в ожидании почтальона. Когда она замечает почтальона, она звонит на ваш мобильный телефон (обратный вызов), чтобы сообщить, что почтальон будет у вашей двери через 10 минут. Это освободило бы вас, позволив заняться другими делами и даже выйти из дома!
Примечание
Для работы этого кода требуется RPi.GPIO версии 0.5.1 (или выше). Команды sudo apt-get update и sudo apt-get dist-upgrade обновят всё!
Первый шаг — определить (def) нашу функцию обратного вызова. Мы назовем наш обратный вызов MOTION и свяжем его с нашим PIR_PIN. Как и раньше, мы хотим, чтобы эта функция выводила текст при обнаружении движения.
def MOTION(PIR_PIN):
print "Motion Detected!"
Добавление следующей строки включает прерывания на выводе и сообщает коду ждать, пока не произойдет событие — например, когда наш PIR_PIN обнаружит сигнал. Вывод может быть настроен на срабатывание при переходе от низкого к высокому (GPIO.RISING), от высокого к низкому (GPIO.FALLING) или при любом из них (GPIO.BOTH). Затем мы указываем, какой обратный вызов использовать!
GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION)
Как упоминалось ранее, нам нужно добавить простую и ненагруженную функцию цикла, чтобы наше обнаружение событий оставалось активным. Поэтому мы можем зациклить функцию sleep! Мы могли бы использовать здесь функцию while True, однако из-за возможности переопределить True на другое значение это немного увеличивает нагрузку на код (при поиске переменной). Использование целого числа while 1 пропускает эту проверку переменной.
while 1:
time.sleep(100)
Вот и всё! Мы написали ресурсоэкономную программу со встроенным прерыванием и функцией обратного вызова, которая считывает сигнал от датчика движения!
Итоговый код
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
PIR_PIN = 7
GPIO.setup(PIR_PIN, GPIO.IN)
def MOTION(PIR_PIN):
print "Motion Detected!"
print "PIR Module Test (CTRL+C to exit)"
time.sleep(2)
print "Ready"
try:
GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION)
while 1:
time.sleep(100)
except KeyboardInterrupt:
print " Quit"
GPIO.cleanup()