公開日:

ラズパイ(Raspberry Pi)で複数のUSBカメラと水温計などのセンサを用いてSlack経由で水槽を監視する

先日の動物学会で、ラズパイでカメラを制御して云々という発表を見た。そこに”ラズパイおじさん”たちが募っていたのが面白かった。かくいう私も突っ込んだりとかしていたのだが、その中に「1台のラズパイで複数のカメラを制御できないか」という質問があった。

それに対して「USBカメラならできる」と回答したので、それについてここで少しだけ書き留めておこうと思う。

ついでにこれまでのラズパイで水槽監視装置よりだいぶアップグレードされているので、それについても説明しておく。

環境はラズパイ2B、Raspbian

以前の記事

ラズパイとSlackで自宅水槽監視 – 初期設定
 ラズパイとSlackで自宅水槽監視 – Pythonで自動投稿

できたもの

こんな感じで水槽部屋の状況が写真付きで流れてくる

複数のUSBカメラで写真を撮ってSlackに投稿

写真を撮って保存する方法

fswebcam というソフトウエアが必要になるのでまずはそれをインストール。

$ sudo apt-get install fswebcam

基本的にはこれで準備完了

今回はUSBカメラ3台で行うこととする。単純に4つのポートのうち1つをWifiに当てているから3台なのであって、1台でも4台でもいけるはずである。

また私は定期的に実行したいので camera.sh というシェルスクリプト内に

Datetime=`date +%Y%m%d%H%M%S`
fswebcam -r 1280x720 --no-banner --device /dev/video0 -D 2 /tmp/Suisou/$Datetime.jpg
python3 /home/pi/SlackPochi/main.py /tmp/Suisou/$Datetime.jpg

Datetime2=`date +%Y%m%d%H%M%S`
fswebcam -r 1280x720 --no-banner --device /dev/video1 -D 2 /tmp/Suisou/$Datetime2.jpg
python3 /home/pi/SlackPochi/main.py /tmp/Suisou/$Datetime2.jpg

Datetime3=`date +%Y%m%d%H%M%S`
fswebcam -r 1280x720 --no-banner --device /dev/video2 -D 2 /tmp/Suisou/$Datetime3.jpg
python3 /home/pi/SlackPochi/main.py /tmp/Suisou/$Datetime3.jpg

というような記述をした。

かんたんに説明すると、保存するjpgの名前が重複しないようにそれぞれのカメラごとにDatetimeをその都度取得し、ファイル名に設定。

fswebcamの行は解像度の指定、バナー(時刻などのバナーを入れることができるが別途その情報は入れるので)、そして --device でカメラの指定を行っている。こいつは0始まりでそれぞれのカメラを単体で認識できる。ちなみにカメラは超広角にするためにすべて同じ以下のものを使用している。

廃盤になっていたので後継種(同じ画角)のものを右に載せた。

-D はディレイ、つまり遅延の長さ。ある程度の長さを挟んでおいたほうが動作が安定した。

最後のpythonから始まる行は後述するセンサから温度などを取得するプログラムの動作だ。

これでひとまず「写真撮影とストレージ内への保存」ができる。

保存した写真をSlackに投稿

これはもうかんたんにサクッといける

import requests
import sys


TOKEN = "xoxb-1234567890-1234567890-ZZZZXXXXXXjiafs9821rjAD"
CHANNEL = "#" + "ぽち監視サーバー"

files = {'file': open(sys.argv[1], 'rb')}
param = {
    'token':TOKEN, 
    'channels':CHANNEL,
    'title': "Pochi Camera"
}
requests.post(url="https://slack.com/api/files.upload",params=param, files=files)

これでおしまい。

tmpというところに保存しているのは、そこを仮置の場所にしてcronで溜まった写真を定期的に削除するためである。このスクリプト自体もcronで定期的に実行してやっている。詳しくは最後に。

センサ類からの水温・湿度・室温・気圧の取得

今回は水槽部屋の室温・湿度・気圧をBME280から、水温の取得を防水仕様のDS18B20から行った。

いずれも手頃な値段で入手可能でソースコードなどもよく公開されている。5個セットを購入したが、ラズパイ1つしか持ってないし壊れてもいなかったので完全に道具箱の肥やしになってしまった……

さて、それらセンサー2つから温度などを取得して一度CSVに格納しておく。のちのち研究などで必要となってくる場合があるかもしれないからだ。Slackで遡れるには無料アカウントだと上限があるので。

なお、Slack投稿周りの話は以前の記事を参照してほしい。こちらでは触れない。

以前の記事

ラズパイとSlackで自宅水槽監視 – 初期設定
 ラズパイとSlackで自宅水槽監視 – Pythonで自動投稿

#coding: utf-8
 
import bme280_custom
import datetime
import os
import requests
import sys
import glob


os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'

# 生の温度データを取得する関数
def read_temp_raw():
    f = open(device_file, 'r')
    lines = f.readlines()
    f.close()
    return lines

# 温度データのみを取り出して返す関数
def read_temp():
    lines = read_temp_raw()
    while lines[0].strip()[-3:] != 'YES':
        sleep(0.2)
        lines = read_temp_raw()
    equals_pos = lines[1].find('t=')
    if equals_pos != -1:
        temp_string = lines[1][equals_pos + 2:]
        temp_c = float(temp_string) / 1000.0
        return temp_c


TOKEN = "xoxb-1234567890-1234567890-AFHue21rjiafs9821rjADdaf"
CHANNEL = "#" + "ぽち監視サーバー"
 
dir_path = '/home/pi/bme280-data'
 
now = datetime.datetime.now()
filename = now.strftime('%Y%m%d')
label = now.strftime('%H:%M')
csv = bme280_custom.readData()
csv = csv + "," + str(read_temp())
notcsv = csv.split(",")

url = 'https://slack.com/api/chat.postMessage'
token = "xoxb-1234567890-1234567890-AFHue21rjiafs9821rjAD" # APIのトークンを入力
headers = {"Authorization" : "Bearer "+ token}
channel = "#" + "ぽち監視サーバー"
if float(notcsv[2]) > 90:
	payload = { 
        	'channel': channel,
        	'text': "日付: " + filename + "\n現在時刻: " + label + "\n気圧: " + notcsv[0] + "\n気温: " + notcsv[1] + "\n湿度: " + notcsv[2] + "\n水温: " + notcsv[3]
	}
else:
        payload = {
                'channel': channel,
                'text': "------現在の水槽部屋の状況------\n" + "日付: " + filename + " " + label + "\n気圧: " + notcsv[0] + "\n室温: " + notcsv[1] + "\n湿度: " + notcsv[2] + "\n水温: " + notcsv[3]
        }
requests.post(url ,headers = headers, data=payload)

if not os.path.exists('/home/pi/bme280-data'):
    os.makedirs('/home/pi/bme280-data')
f = open('/home/pi/bme280-data/'+filename+'.csv','a')
f.write("'"+label+"',"+csv+"\n")
f.close()

f = open("/home/pi/bme280-data/tograph.csv", "a")
f.write("'" + label + "'," + csv + "\n")
f.close() 

なんかややこしいけど増設に増設を加えたザココードなので勘弁してほしい。

ちなみに回路っていうか、実際にどんな感じでセンサを配置しているかというと、こんな感じ。

ラズパイ センサ2つ

ブレッドボードでやってたものをそのまま移植したら楽じゃねえかなって思ってそういうタイプのユニバーサル基板を買ってきた。秋月電子で。

もっと小さなユニバーサル基板でもうまく配線はできると思うが、単純に今回そこまでこだわらなかったのでこんな具合に収まった。

cronで定期的にスクリプトを実行

さて、これまでのプログラムは単体で実行してやればそれだけでもきちんとSlackに投稿できるようになっている。

しかしこれを完全に自動化してやりたい。

そこでcronというものを使う。cronは基本的に最初からLinux系には入っているはずである。

$ sudo crontab -u root -e

でエディタが起動する。間違って-r にすると最悪な結果(消える)になるので打ち間違いには細心の注意を払おう。

この辺の話は過去の記事に書いたので参照してほしい。

さいごに(生物学業界におけるラズパイ等使用の動向)

「ラズパイなどを使って水槽監視や自動化をしている人は極々僅か」と思っていたし、実際に目にしたことがあるのは東大の某研究室くらいであった。

生物系、特にウェットの人間でパソコンやプログラミングに長けている人間は多くない印象であったが、学会で思っていたより多くの人間がこのような自動化に興味を抱いていることを知り、刺激を受けた。

ラボでも特別この装置が興味を集めるようなこともなかったので、工業大学の学生といえどもおもしろくねーのなって不貞腐れていたが、実際に生物の研究者でラズパイとかいじっている人間がいて、なんていうか、嬉しかった。

某パンデミックもあり、ラボでの実験の時間の削減を余儀なくされている状況だとは思うので、それをいかに効率化させるかが今後の課題となってくるであろう。そのときにこうした「ラズパイ」であったり、最近だと「Jetson nano」などの少し高性能なディープラーニング向けの小型組み込みコンピュータなどが現場で役に立っていくだろう。

自身の研究に対してコンピュータを最適化できるのは、その研究をデザインした自分自身であるので、色々と作ったり、試行錯誤していきたい。

おわり。