LoginSignup
21
27

More than 5 years have passed since last update.

ラズパイで動体検知をして撮影した画像をSlackに通知する

Last updated at Posted at 2019-01-23

やりたいこと

ラズパイ・Webカメラ・hubot・Slackを組み合わせることで、Slackからの指示でラズパイに写真を撮らせてSlackに投稿することまではできた。詳細は前回までのQiitaを参照

今回はこの仕組みに動体検知を加えることで、Slackに写真撮影の指示を出さなくても、ラズパイ側で動体検知をトリガーにして写真を撮影し、撮影した写真をSlackに通知させたい。
また、ラズパイからSlackへの動体検知の通知はhubotから行わせたいという点もポイントである。

WS000074.JPG

ハードウェア

  • Raspberry Pi 2 Model B
  • Logicool Webcam C270

ソフトウェア

  • hubot
  • motion

前提

  • ラズパイにWebカメラを接続して写真を撮影できること
  • ラズパイにhubotをインストールしてセットアップが済んでいること
  • Slackにラズパイのhubotを連携させていること
  • Slackをトリガーにしてラズパイで写真を撮影するところまで実現できていること(前回までの流れを参照)

ラズパイの環境構築

motionのインストール

以下のコマンドでラズパイにmotionというソフトウェアをインストールする。

About Motion

sudo apt-get install -y motion

motionの設定

motionのインストールが終わったら、motionの設定ファイル(/etc/motion/motion.conf)を書き換える。
※管理者ユーザでないと上書きできないのでsudoで開く

$ sudo nano /etc/motion/motion.conf

motionの自動起動OFF

デフォルトでは自動起動がONになっている場合もあるので、OFFになっているか確認。

motion.conf
daemon off

動画撮影機能OFF

動画の撮影はしないので、動画出力はOFFにしておく。

motion.conf
ffmpeg_output_movies off

ファイル書き出し先の指定

motionで撮影した写真の保存先を指定する。
※環境によって異なると思うので好きな場所を指定

target_dir /home/pi/Pictures/motion

動作確認

motionの起動

motionを起動して写真が撮影されるか確認する。

$ sudo motion -c /etc/motion/motion.conf

撮影された写真

カメラに手をかざしただけでめっちゃ撮影されている。。。
WS000086.JPG

カメラが監視してる画像に変化が起きる度に撮影しているので間違った動作ではないが、これでは人が通りがかるだけで何枚も写真が撮られて、Slackに激しく通知されそう。。。

motionの停止

Ctrl+Cで停止する。

改善その1:撮影する写真の数を抑えたい

上記の動作確認を踏まえて、motion.confの設定を改善してみる。
まずは動体検知をしたときに大量の写真が撮影されてしまうのをどうにかする。

ファイル書き出し先の変更

motionの設定によってはものすごい数の画像ファイルが作成されるので、画像の出力先がSDカードのままだとSDカードが劣化してしまう恐れがあるらしい。
なのでラズパイのtmpメモリ領域に出力するように変更する。

motion.conf
target_dir /tmp/motion

写真撮影の設定を変更

写真撮影はデフォルトではonになっているが、これでは動体検知をした全フレームで写真を保存されてしまう。
この設定をbestに変更すると最も大きく変化したフレームのみ画像ファイルとして保存してくれるようになる。

motion.conf
output_pictures best

ただし、この設定を変えただけだと画像は大量に保存されなくなるが、画像の保存まで妙に時間がかかるようになる。
そのため、event_gapでイベントの間隔を狭める必要がある。

イベントの間隔を設定

動体検知が起きてから、次の動体検知をするまでの間隔を設定する。
デフォルトでは60になっているが、このままだと60秒に1枚しか写真が撮影されない。
(動体を検知してから、60秒間で一番大きく変化したフレームを画像ファイルとして保存するので、動体検知から画像保存まで60秒は時間がかかる)

それほどリアルタイムでなくてもいいのであれば60のままでもいいし、もう少し早く画像を保存してほしいのであればもう少し時間を縮めてもよいと思う。
今回は10秒に設定してみた。

motion.conf
event_gap 10

改善その2:写真を撮影したらhubotに通知してほしい

動体検知をしたら写真を撮影するところまでできたが、これだけだと画像が保存されるだけでSlackには通知できない。
なので、次は写真撮影が済んだらラズパイに常駐させているhubotに通知するようにする。
ラズパイにhubotを常駐させる方法は過去の投稿を参照。

motionには動体検知で画像を保存したときに任意のシェルスクリプトを実行できるオプションon_picture_saveが用意されているので、これを利用する。

hubotでPOST-APIを提供するスクリプトを作成

hubotにはHTTPリクエストを受け付けてチャットに発言する機能があるので、この機能を利用して動体検知で撮影した写真をSlackにアップロードしてもらう。
hubotを通さず、シェルスクリプトを利用してSlack APIで直接画像をアップロードする方法もあるが、せっかくラズパイにhubotを常駐させているのならhubotから通知してもらう方がスマートだと思う。

hubotはrobot.router.postを使用することでPOSTリクエストを受け付けることができるのでこれを利用する。

api_upload_photo.coffee
request = require "request";
fs = require "fs";
path = require "path";

module.exports = (robot) ->
    robot.router.post "/api/v1/upload-photo", (req, res) ->
        # リクエストが空なら何もせずにレスポンスを返す        
        if !req.body
            res.status(500).send("エラーが発生しました")
        else    
            roomName = req.body.room_name
            filePath = req.body.file_path

            # リクエストで指定された写真をSlackへアップロードする
            api_url = "https://slack.com/api/"
            options = {
                token: process.env.HUBOT_SLACK_TOKEN,
                filename: path.basename("#{filePath}"),
                file: fs.createReadStream("#{filePath}"),
                channels: roomName
            }
            request.post {url:api_url + 'files.upload', formData: options}, (error, response, body) ->
                if !error && response.statusCode == 200
                    res.status(200).send("写真投稿完了")
                else
                    res.status(500).send("エラーが発生しました")

hubotに向けてPOSTリクエストを実行するシェルスクリプトの作成

motionから直接POSTリクエストを出すことはできないので、hubotにPOSTリクエストを送るシェルスクリプトを作成する。
curlコマンドでPOSTリクエストを送るにはいろいろな書き方があるようだが、以下のページを参考にさせていただいた。

curlコマンドによるデータ送信あれこれ

以下の情報は再利用性を高めるためにシェルスクリプトには書かず、引数で渡すようにしている。

  • Slackのチャンネル名(ルーム名)
  • Slackにアップロードするファイルのフルパス
  • hubotのPOST-APIのURL
post_photo.sh
#!/bin/bash

room_name_val=${1}
file_path_val=${2}
url=${3}

echo "room_name: "${room_name_val}
echo "file_path: "${file_path_val}
echo "url: "${url}

curl --data-urlencode "room_name=${room_name_val}" --data-urlencode "file_path=${file_path_val}" $url

motion.confの設定を変更

on_picture_saveに画像保存時に実行するシェルスクリプトの名前を指定する。
このとき、%fには保存した画像ファイルのフルパス名が入っているので、シェルスクリプトに引数として渡す。
シェルスクリプトは以下の3つを引き数として受け取るようにしていたので、ここで実際の値を指定しておく。

  • Slackのチャンネル名(ルーム名)
  • Slackにアップロードするファイルのフルパス(%fを指定)
  • hubotのPOST-APIのURL
motion.conf
# Command to be executed when a picture (.ppm|.jpg) is saved (default: none)
# To give the filename as an argument to a command append it with %f
; on_picture_save value
on_picture_save sh /home/pi/hubot/pibot/sh/post_photo.sh [Slackのチャンネル名] %f [hubotのPOST-APIのURL]

動作確認

ラズパイでmotionを起動し、カメラの前に手をかざすなどして動体を検知させる。
動体検知に合わせて、シェルスクリプトが実行されること(シェルスクリプト内にechoを仕込んでおけばわかりやすい)、さらにhubotからSlackに画像の投稿が行われることを確認する。

WS000091.JPG

WS000090.JPG

21
27
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
27