Компьютерное зрение как датчик движения для SmartThings
Используя Raspberry Pi 3 и PiCam, этот датчик с компьютерным зрением обнаруживает лица и отправляет данные о присутствии через LAN - UPNP в SmartThings.
Я начну с предположения, что у вас есть Raspberry Pi 3 с работающей камерой и установленным Open CV. Если нет, я рекомендую это руководство 🙂
Создание собственного обработчика устройства SmartThings
В SmartThings IDE мы создаем новый обработчик устройства для нашего датчика движения Computer Vision.
Перейдите в раздел «My Device Handler», нажмите «+ Create New Device Handler» в правом верхнем углу.
В этом случае мы создадим его из кода. Нажмите на вторую вкладку «Из кода», вставьте прикрепленный код «Обработчик устройства» . и нажмите «Создать».
На следующей странице нажмите «Опубликовать», чтобы устройство стало вам доступно.
Написание приложения SmartThings
Как и в случае с Device Handler, мы перейдем в раздел «Мои SmartApps», нажмите «+ Create New SmartApps» в правом верхнем углу.
Мы также создадим его из кода. Щелкните вторую вкладку «Из кода» и вставьте прикрепленный код « SmartApp» . и нажмите «Создать».
На следующей странице нажмите «Опубликовать».
Подготовка Raspberry Pi
Теперь нам нужно добавить скрипт python, который будет получать изображения с камеры, определять лица и сообщать SmartThings.
Первый д ownload и установка imutils и twisted
Если у вас еще не установлен пакет imutils, вы можете загрузить его с GitHub или установить через:
pip install imutils
Для скрученных:
sudo apt-get install python-twisted-web
Теперь, когда все готово, перейдите в / home / pi . и создайте каталог для хранения скрипта
камера mkdir cameracd
Создайте файл сценария:
sudo nano ssdpcamera.py
Вставьте прикрепленный код “ Скрипт Python для камеры » и сохраните, нажав «control + x», затем «y» и введите.
Протестируйте скрипт, набрав python ssdpcamera.py, вы должны увидеть что-то вроде этого:
Обнаружение и соединение Raspberry Pi
Из мобильного приложения SmartThings мы должны перейти в Marketplace в правом нижнем углу, щелкнуть вкладку «SmartApps» и, наконец, найти «Компьютерное зрение (подключение)» в «+ Мои приложения»
Убедитесь, что Raspberry Pi включен и скрипт python запущен.
SmartApp запустит процесс обнаружения и после обнаружения щелкните диалоговое окно выбора, выберите устройство и нажмите «Готово».
Это создаст устройство в вашей учетной записи и начнет получать обновления.
Автозапуск
Наконец, если вы хотите запускать скрипт python автоматически при включении Raspberry Pi, вы можете отредактировать /etc/rc.local и добавить следующую строку.
(sleep 10; python /home/pi/camera/ssdpcamera.py)&
() Заставляет обе команды работать в фоновом режиме.
Код
#! / usr / bin / python2.7 "" "Камера компьютерного зрения для SmartThingsCopyright 2016 Хуан Пабло Риссо <[email protected]> Зависимости:python-twisted, cv2, pyimagesearch Лицензировано под лицензию Apache версии 2.0 («Лицензия»); вы не можете использовать этот файл, кроме как в соответствии с Лицензией. Вы можете получить копию Лицензии по адресу:http://www.apache.org/licenses/LICENSE-2.0 требуется применимым законодательством или согласовано в письменной форме, программное обеспечение, распространяемое по Лицензии, распространяется на УСЛОВИЯХ «КАК ЕСТЬ», БЕЗ ГАРАНТИЙ ИЛИ УСЛОВИЙ ЛЮБОГО РОДА, явных или подразумеваемых. См. Лицензию, чтобы узнать о конкретных языках, регулирующих разрешения и ограничения по Лицензии. "" "import argparseimport loggingimport cv2import urllib2import imutilsfrom time import timefrom picamera.array import PiRGBArrayfrom picamera import PiCamerafrom twisted.web import server, resourcefrom twisted.internet import responsefrom twisted.internet.defer import успешноfrom net.protocol import DatagramProtocolfrom twisted.web.client import Agentfrom twisted.web.http_headers import Headersfrom twisted.web.iweb import IBodyProducerfrom twisted.web._newclient import ResponseFailedfrom zope.interface import ImplementsSSDP_PORT.28 -9220-11e4-96fa-123b93f75cba'SEARCH_RESPONSE ='HTTP / 1.1 200 OK \ r \ nCACHE-CONTROL:max-age =30 \ r \ nEXT:\ r \ nLOCATION:% s \ r \ nSERVER:Linux, UPnP / 1.0, Pi_Garage / 1.0 \ r \ nST:% s \ r \ nUSN:uuid:% s ::% s '# инициализируем камеру и получаем ссылку на необработанную камеру # capturecamera =PiCamera () camera.resolution =(640 , 480) camera.framerate =32rawCapture =PiRGBArray (camera, size =(640, 480)) auxcount =0 # создать детектор лиц и дать камере прогреться fd =FaceDetector ("cascades / haarcascade_frontalface_default.xml") time.sleep (0.1) def define_ip_for_host (host):"" "Определить локальный IP-адрес, используемый для связи с конкретным хостом" "" test_sock =DatagramProtocol () test_sock_listener =Reaction.lis tenUDP (0, test_sock) # pylint:disable =no-membertest_sock.transport.connect (host, 1900) my_ip =test_sock.transport.getHost (). hosttest_sock_listener.stopListening () return my_ipclass StringProducer (object):"" "Записывает строка в памяти для Twisted запроса "" "реализует (IBodyProducer) def __init __ (self, body):self.body =bodyself.length =len (body) def startProroduction (self, consumer):# pylint:disable =invalid- name "" "Начать создание предоставленной строки для указанного потребителя" "" consumer.write (self.body) return success (None) def pauseProroduction (self):# pylint:disable =invalid-name "" "Приостановить создание - нет операции "" "passdef stopProroduction (self):# pylint:disable =invalid-name" "" Остановить производство - нет операции "" "passclass SSDPServer (DatagramProtocol):" "" Получение запросов на обнаружение M-SEARCH от концентратора SmartThings и ответ на них " "" def __init __ (self, interface ='', status_port =0, device_target =''):self.interface =interfaceself.device_target =device_targetself.status_port =status_portself.port =Reaction.listenMulticas t (SSDP_PORT, self, listenMultiple =True) # pylint:disable =no-memberself.port.joinGroup (SSDP_ADDR, interface =interface) diver.addSystemEventTrigger ('before', 'shutdown', self.stop) # pylint:disable =no-memberdef datagramReceived (self, data, (host, port)):try:header, _ =data.split (b '\ r \ n \ r \ n') [:2] кроме ValueError:returnlines =header.split ('\ r \ n') cmd =lines.pop (0) .split ('') lines =[x.replace (':', ':', 1) for x in lines] lines =[x for x в строках, если len (x)> 0] заголовки =[x.split (':', 1) для x в строках] заголовки =dict ([(x [0] .lower (), x [1]) для x в заголовках]) logging.debug ('Команда SSDP% s% s - от% s:% d с заголовками% s', cmd [0], cmd [1], хост, порт, заголовки) search_target ='' if ' st 'в заголовках:search_target =headers [' st '], если cmd [0] ==' M-SEARCH 'и cmd [1] ==' * 'и search_target в self.device_target:logging.info (' Получено% s % s для% s из% s:% d ', cmd [0], cmd [1], search_target, host, port) url =' http://% s:% d / status '% (define_ip_for_host (host) , self.status_port) response =SEARCH_R ESPONSE% (url, search_target, UUID, self.device_target) self.port.write (response, (host, port)) else:logging.debug ('Игнорируемая команда SSDP% s% s', cmd [0], cmd [ 1]) def stop (self):"" "Выйти из группы многоадресной рассылки и прекратить прослушивание" "" self.port.leaveGroup (SSDP_ADDR, interface =self.interface) self.port.stopListening () class StatusServer (resource.Resource):"" "HTTP-сервер, который передает статус камеры в хаб SmartThings" "" isLeaf =Truedef __init __ (self, device_target, subscription_list, garage_door_status):self.device_target =device_targetself.subscription_list =subscription_listself.garage_door_status =garage_door__ self) def render_SUBSCRIBE (self, request):# pylint:disable =invalid-name "" "Обработка запросов на подписку от концентратора ST - концентратор хочет получать уведомления об обновлениях статуса двери гаража" "" headers =request.getAllHeaders () logging.debug («ПОДПИСАТЬСЯ:% s», заголовки) если «обратный вызов» в заголовках:cb_url =headers ['callback'] [1:-1] если не cb_url в self.subscription_list:self.subs cription_list [cb_url] ={} # response.stop () logging.info ('Добавлена подписка% s', cb_url) self.subscription_list [cb_url] ['expiration'] =time () + 24 * 3600return "" def render_GET ( self, request):# pylint:disable =invalid-name "" "Обработка запросов на опрос от концентратора ST" "" if request.path =='/ status':if self.garage_door_status ['last_state'] =='inactive' :cmd ='status-inactive'else:cmd =' status-active'msg =''% (cmd, UUID, self.device_target) logging.info («Запрос на опрос от% s для% s - вернул% s», request.getClientIP (), request.path, cmd) return msgelse:logging.info ( "Получен поддельный запрос от% s для% s", request.getClientIP (), request.path) return "" class MonitorCamera (object):"" "Отслеживает состояние камеры, генерируя уведомления при изменении ее состояния" "" def __init __ ( self, device_target, subscription_list, camera_status):# pylint:disable =too-many-argumentsself.device_target =device_targetself.subscription_list =subscription_listself.camera_stat us =camera_statuscurrent_state ='inactive'reactor.callLater (0, self.check_garage_state, current_state, auxcount) # pylint:disable =no-memberdef check_garage_state (self, current_state, auxcount):self.countture_state =current_came =auxcount. rawCapture, format ="bgr", use_video_port =True) # взять необработанный массив NumPy, представляющий imageframe =rawCapture.array # изменить размер кадра и преобразовать его в оттенки серогоframe =imutils.resize (frame, width =640) gray =cv2.cvtColor (frame, cv2.COLOR_BGR2GRAY) # обнаружение лиц на изображении и затем клонирование кадра # чтобы мы могли рисовать на нем faceRects =fd.detect (gray, scaleFactor =1.1, minNeighbors =10, minSize =(30, 30)) frameClone =frame.copy () if faceRects! =():auxcount =0 if current_state =='inactive':current_state ='active' logging.info ('Состояние изменено с% s на% s', self.camera_status ['last_state '], current_state) self.camera_status [' last_state '] =current_stateself.notify_hubs () еще:auxcount =auxcount + 1if auxcount ==60:current_state ='inactive' logging.info ('Состояние изменено с% s на% s', self.camera_status ['last_state'], current_state) self.camera_status ['last_state'] =current_stateself.notify_hubs () # цикл завершен ограничивающие грани рамки и нарисуйте их для (fX, fY, fW, fH) в faceRects:cv2.rectangle (frameClone, (fX, fY), (fX + fW, fY + fH), (0, 255, 0), 2 ) # показать видеопоток в новом окне графического интерфейса # cv2.imshow ("Face", videorotate) rawCapture.truncate (0) # Запланировать следующий checkreactor.callLater (0, self.check_garage_state, current_state, auxcount) # pylint:disable =no-memberdef notify_hubs (self):"" "Уведомить подписанные хабы SmartThings о том, что произошло изменение состояния" "", если self.camera_status ['last_state'] =='inactive':cmd ='status-inactive'else:cmd ='status-active' для подписки в self.subscription_list:if self.subscription_list [subscription] ['expiration']> time ():logging.info («Notifying hub% s», подписка) msg =' % s uuid:% s ::% s '% (cmd, UUID, self.device_target) body =StringProducer (msg) agent =Agent (response) req =agent.request ('POST', подписка, заголовки ({'CONTENT-LENGTH':[len (msg)]}), тело) req.addCallback (self.handle_response ) req.addErrback (self.handle_error) def handle_response (self, response):# pylint:disable =no-self-use "" "Обработка хаба SmartThings, возвращающего код состояния в POST. Это на самом деле неожиданно - обычно он закрывается соединение для POST / PUT без указания кода ответа. "" "if response.code ==202:logging.info (" Обновление статуса принято ") else:logging.error (" Неожиданный код ответа:% s ", response.code ) def handle_error (self, response):# pylint:disable =no-self-use "" "Обработка ошибок, генерирующих выполнение NOTIFY. Кажется, не существует способа избежать ResponseFailed - SmartThings Hub не генерирует правильный код ответа для POST или PUT, а если используется NOIFY, он игнорирует тело. "" "If isinstance (response.value, ResponseFailed):logging.debug («Ответ не удался (ожидался)») else:logging.error («Неожиданный ответ:% s», ответ) def main ():«" «Основная функция для обработки использования из командной строки" "" arg_proc =argparse .ArgumentParser (description ='Предоставляет активный / неактивный статус камеры для концентратора SmartThings') arg_proc.add_argument ('- httpport', dest ='http_port', help ='Номер порта HTTP', по умолчанию =8080, type =int) arg_proc.add_argument ('- deviceindex', dest ='device_index', help ='Device index', default =1, type =int) arg_proc.add_argument ('- pollingfreq', dest ='polling_freq', help =' Количество секунд между опросом состояния камеры ', по умолчанию =5, type =int) arg_proc.add_argument (' - debug ', dest =' debug ', help =' Включить отладочные сообщения ', по умолчанию =False, action =' store_true ' ) options =arg_proc.parse_args () device_target ='urn:schema s-upnp-org:device:RPi_Computer_Vision:% d '% (options.device_index) log_level =logging.INFOif options.debug:log_level =logging.DEBUGlogging.basicConfig (format ='% (asctime) -15s% (levelname) - 8s% (message) s ', level =log_level) subscription_list ={} camera_status ={' last_state ':' unknown '} logging.info (' Initializing camera ') # Сервер SSDP для обработки обнаружения SSDPServer (status_port =options.http_port, device_target =device_target) # HTTP-сайт для обработки подписок / pollingstatus_site =server.Site (StatusServer (device_target, subscription_list, camera_status)) Reaction.listenTCP (options.http_port, status_site) # pylint:disable =no-memberlogging.info ('Инициализация завершена' ) # Мониторинг состояния камеры и отправка уведомлений об изменении состоянияMonitorCamera (device_target =device_target, subscription_list =subscription_list, camera_status =camera_status )actor.run () # pylint:disable =no-memberif __name__ =="__main __":main () Источник:Компьютер Зрение как датчик движения для SmartThings % s uuid:% s ::% s
Производственный процесс
- ST:датчик движения с машинным обучением для высокоточного отслеживания активности без использования аккумул…
- Датчик движения, тревога, запись видео в HA на Raspberry Pi
- Самодельная инфракрасная система датчиков движения для Raspberry Pi
- Датчик движения с использованием Raspberry Pi
- Счетчик Гейгера - Учебная плата датчика излучения для Raspberry Pi
- Raspberry Pi GPIO с датчиком движения PIR:Лучшее руководство
- Создание сенсорной сети для мельницы 18-го века
- Взаимодействие датчика движения HC-SR501 PIR с Raspberry Pi
- Сборка роботов Raspberry Pi:лучшее руководство для начинающих
- 7 приложений компьютерного зрения