ラズベリーパイを買って、家の中でIoTの雰囲気を味わってみたので、共有する記事です。
ラズベリーパイをセットアップして、dht11と接続し、温湿度をパソコンと共有する方法を解説します。
この記事の中で、以下の事が分かります。
- ラズベリーパイを使う時に必要なモノ
- ラズベリーパイのセッティング
- dht11の使い方
- MQTTで通信する方法
- ホテリングのT2理論
この記事と同じことがしたい時は、githubにコードを置いているので、参考にしてみてください。
はじめに
何をしたのか、何がしたいのか書いておきます。
何がしたいのか
リアルなビッグデータを触ってみたいと何となく思っていました。
python + djangoでアプリを作れるようになったので、自分でデータを取得する事に抵抗がなくなり、IOTでも齧ってみるかとなったのでした。
何をしたのか
ラズベリーパイ+センサーで冷蔵庫の中の温度(ついでに湿度)を10秒毎に記録しました。そのデータをpcに保存し続け、ホテリングのT2理論で異常検知を行いました。
冷蔵庫の中はある温度を基準に上がり下がりしているという仮説の元、異常値として冷蔵庫の開閉を検知できるかなと思っていました。
ホテリングのT2理論は、一つの正規分布があって、そこからの外れ値をカイ二乗検定で検知するという手法なので、今回の分析には最適かなと思い、選択しました。
実際、GMMや、k-meansでは、温度の上がり下がりそれぞれにクラスタを割り当ててしまい、上手くいきませんでした。
そして、データ取得、解析は共に上手くいきました。
どうやったのかを共有したいと思います。
使ったものたち
使ったものを紹介します。
使った言語はpythonです。linuxのシステムも少し使いました。
ラズベリーパイは冷蔵庫に入れようと思ってたので、3A以上出力できるモバイルバッテリーを買いました。6時間くらい連続で動きました。
ラズベリーパイ3 は2,5Aで動くのですが、ラズベリーパイ4は3A以上必要らしいので、バッテリーは3A以上のモノを選びました。
本は、6章の温湿度測定のコードを殆どパクりました。
ラズベリーパイ 3 Model B+
dht11
バッテリー
本
マイクロSD 64GB
ラズベリーパイとdht11
ラズベリーパイとdht11 について書きます。
ラズベリーパイの準備
ラズベリーパイは、小さいコンピューターです。とはいっても、モニターやOSが無くて、usbポートやcpuがむき出しになっています。
ラズベリーパイに電源を繋いでも、OSがインストールされていないので、動きません。
マイクロSDにOSをインストールしておく必要があります。
公式サイトに従ってインストールします。
拘りが無ければ、ラズビアンをインストールすればいいと思います。
https://www.raspberrypi.org/software/
ラズベリーパイとdht11の物理的な接続
物理的に接続した写真です。
買ったdht11 は、抵抗と端子が付いていたので、直接差し込むだけで動きました。
違うものを買うと、フレットボードや抵抗が必要だったり、自分で抵抗値と温度を換算したりしないといけないので、ラッキーでした。
端子の接続先などは、以下のサイトや、上にあげた本にも書いています。
https://raspi.taneyats.com/entry/home-electronics-2
ラズベリーパイとdht11の通信
dht11 用のpython ライブラリがあります。
それと、ラズベリーパイのピンの情報を扱うライブラリを使用して通信する事が出来ます。
例えば、次のようなコードでとりあえずはデータを取得できます。
import RPi.GPIO as GPIO
import dht11
#ピン番号を指定
tempGpio = 4
#pinの番号付けの方法の指定と取得する場所の指定
GPIO.setmode(GPIO.BCM)
dhtStat = dht11.DHT11(pin = tempGpio)
#dht11からデータを取得し、温度と湿度を表示
stat = dhtStat.read()
print(stat.temperature)
print(stat.humidity)
GPIO.cleanup()
crontab を使って、適当な感覚で上のようなファイルを実行させると、恒常的にデータを取得する事が出来ます。
データを取得しても、ラズベリーパイの中に貯めておいてはしょうがありません。
別の機器と通信する規格にMQTTがあります。
次は、MQTTを使ってパソコンにデータを送信し、csvに記録していきます。
MQTT
mqtt とは、データ配信プロトコルの一種で、データの送受信に、publisher, topic, broker, subscriber を使うものです。1
pubは、topicを決めてデータを送信する人(機器)です。subは、topicを決めてデータを受信する人(機器)です。
broker はpubからsubにデータを渡す役割を担います。この時、sub側は、適切にtopicを選ばないとデータを貰う事が出来ません。
実際に使うには、ラズベリーパイと、ラズベリーパイと通信したい機器にmosqitto をインストールします。
また、python でmosquittoを扱う為に、paho-mqtt をインストールします。これはpip install出来ます。
また、MQTTを動かすコードは、MQTTブローカーのセットアップ , pythonでMQTT送受信 が役に立ちました。
PCとラズベリーパイで通信する為のコード
今回は、ラズベリーパイがpub, 自分のpcがsubという状態です。
ラズベリーパイ側からは10秒毎にpublishされてくるので、pcを動かし続けて、sub用のコードを動かし続けるという感じになります。
初めに、ラズベリーパイ側、pc側で
mosquitto -v
としてmosquittoを起動しておきます。pc側では、以下のようなコードを作って、動かしておきます。2
import paho.mqtt.client as mqtt
import json
import os
#hostは自分自身なので、localhost に指定する.port は1883 がよく使われるらしい。
host = '127.0.0.1'
port = 1883
#keepalive は、処理のtimeoutまでの時間
keepalive = 60
#topicはpub側と合わせる必要がある
topic = 'mqtt/test'
#ファイルを保存したいpathをいれる
fileName = "path_to_csv_file"
#ブローカーに接続した時の挙動を決める
def on_connect(client, userdata, flags, rc):
print('Connected with result code ' + str(rc))
client.subscribe(topic)
#publish された時の挙動を決める。message はjson型で送られてくる
ので、json.loadsして辞書にしてから扱う
def on_message(client, userdata, msg):
sub_msg = json.loads(msg.payload)
print("Subscribed! "+ msg.topic + ' ' + str(msg.payload))
if not os.path.exists(fileName):
Data = open(fileName, "w")
Data.write("date_time,hmdt,temp\n")
Data.close()
print("csv file is created to " + fileName)
#データの保存
data = sub_msg.values()
Data = open(fileName,"a")
Data.write(",".join(map(str, data))+"\n")
Data.close()
print("data is saved!")
#mosquitto がブローカーに接続した時や、pubされたときの状態などを指定する
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(host, port, keepalive)
#一生subしてほしいので、最後の一行が必要
client.loop_forever()
上手く動いて、データを受信できると、sub側のコマンドに以下のようなメッセージが出力されます。
ラズベリーパイ側では、以下のようなコードを動かします。
#! /usr/bin/python3
import json
import os
import time
import datetime
import paho.mqtt.client as mqtt
import RPi.GPIO as GPIO
import dht11
#MQTT Functions
def on_connect(client, userdata, flag, rc):
print("Connected with result code " + str(rc))
def on_disconnect(client, userdata, flag, rc):
if rc != 0:
print("Unexpected disconnection.")
def on_publish(client, userdata, mid):
print("publish: {0}".format(mid))
#get datas from dht11
tempGpio = 4
GPIO.setmode(GPIO.BCM)
dhtstat = dht11.DHT11(pin = tempGpio)
while True:
stat = dhtstat.read()
#now = datetime.datetime.strptime(str(datetime.datetime.now())[0:16], '%Y-%m-%d %H:%M')
now = str(datetime.datetime.now())[0:19]
data = {"date_time":now, "temp":str(stat.temperature), "hdmt":str(stat.humidity)}
if stat.temperature==0 and stat.humidity == 0:
continue
data = json.dumps(data)
print("data is created"+ data )
break
GPIO.cleanup()
#publish datas to host
host = 'ip_adress_of_PC'
port = 1883
keepalive = 60
topic = 'mqtt/test'
client = mqtt.Client()
client.connect(host, port, keepalive)
#dataのpublish
client.publish(topic, data)
print("data is published!")
time.sleep(3)
client.disconnect()
while True の中身で、データの整形などを行っています。3
このコードは、ラズベリーパイ側で、crontabなどで適当な時間毎に動くようにしておきます。
準備が出来たら、ラズベリーパイをバッテリーに繋いで冷蔵庫に入れます。
湿気とかで壊れたら怖いなと思って、料理で使うパックに入れて封をして投入しました。
全く冷蔵庫に触れない深夜帯と、必ず冷蔵庫に触る朝方、2回に分けて冷蔵庫に入れました。
10秒毎にデータを取得し、大体12時間分のデータを集めました。
異常検知の手法
データが集まったので、ホテリングのT2理論を使って解析します。湿度はあまり当てにならない感じだったので、温度だけを使います。
ホテリングのT2理論については、解説の記事があるのでそちらをご覧ください。
初めに、得られた温度のグラフを見ます。
初めの方は、冷蔵庫の開け閉めが無かった気がするデータです。(深夜に起きて冷蔵庫を開けたかもしれませんが。)
後半は、何回か冷蔵庫を開け示したデータになっています。
ホテリングのT2理論で解析してみます。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
#おまじない
plt.tight_layout()
sns.set()
#データの読出しとグラフで良い感じにするためにindexの変更
X= pd.read_csv("temp_hmdt.csv", index_col="date_time")
X.index = pd.to_datetime(X.index)
def hoteling_1dim(x):
"""ホテリング統計量a を計算する関数
与えるデータx,平均μ, 分散σとして、
a= ((x-μ)/σ)^2で計算出来る
Args:
x : (N,1)型のnp.array
Returns:
a : データ毎のホテリング統計量
(N,-)型のnp.array
"""
mu = np.mean(x)
N=len(x)
sig =(np.sum( (x-mu)**2 ))/N
sig = np.sqrt(sig)
a = ( (x- mu)/sig )**2
return a
#異常値かどうか見極めるための限界値と、各データの異常度を計算する
a_th =stats.chi2.ppf(q=0.95, df=1)
a = hoteling_1dim(np.array(X.temp).reshape(-1,1))
#異常度の高い順にデータを並べて見る
X["anomaly"]=a
X["is_anomaly"] = X.anomaly >a_th
X.sort_values(by="anomaly", ascending=False).head(20)
以下にも、という時間に異常値が検出されています。多分朝7時ころに朝ごはんを食べたんでしょう。
もう少し分かりやすいように、結果で色分けしたグラフを描いてみます。
import datetime
start_time = datetime.datetime.strptime("2020-12-06 06:00:00","%Y-%m-%d %H:%M:%S" )
new_data = X.index > start_time
#全てのデータに印をつける
fig, ax = plt.subplots()
sns.scatterplot(data=X[new_data], x="date_time",y="temp", hue="is_anomaly")
locator = mdates.AutoDateLocator(minticks=4, maxticks=10)
formatter = mdates.ConciseDateFormatter(locator=locator)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
plt.show()
#明らかに開け閉めがあったデータに印をつける。
from matplotlib.dates import DateFormatter
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
sns.scatterplot(data=X[new_data], x="date_time",y="temp", hue="is_anomaly")
plt.show()
データ全ての結果。前半でも開け閉めがあったかもしれない 良い感じに検出できている
結果を眺めると、次の事が分かります。
- 基本的に温度は1℃~5℃を振動している
- 前半、後半に異常値がある
- 温度が低い部分と高い部分が異常値
温度が低すぎる部分と高すぎる部分を異常値として検知している事が分かります。(手法の特性上そうなるのは当たり前ですが。)
低い部分は冷蔵庫の開け閉めに関係ないので、高くなっている部分にだけ注目するスクリプトを書き足す事で、冷蔵庫の開け閉めを検知できそうです。
まとめ
- ラズベリーパイを買ってラズビアンをインストールした
- dht11と接続、通信した
- MQTTを使ってpc,ラズベリーパイ間でデータの送受信をした
- ラズベリーパイ+dht11 を冷蔵庫に投入して、大きめのデータを作った
- ホテリングのT2理論を使って異常検知(冷蔵庫の開け閉めの検知)を行った
やってみた感想
IOTの片鱗を垣間見た。サーバーの安定性などの問題が解決できるなら、あまりITに強くない中小企業とかでヒーローに慣れそうだなと思った。ビッグデータってどうやって作るんだろうと思っていたけど、今回みたいに、数秒ごとに何個かのデータを取って1か月もすればビッグデータが出来るなあと分かった。
少しビッグデータが身近に感じた。
次にやる(かもしれない)こと
GCPのサービスにMQTTのブローカーがあるので、電源をどうにかして、1週間くらいデータを取って同じことをしてみる。