この記事は IoTLT Advent Calendar 2019 - Qiita の18日の記事です。
17日はmoomooyaさんによるの電動健康器具の無改造スマート化プラグを試作してみたでした。コリ具合とかで調整できるの良いですね(とぼけ
18日は、超音波センサーで見守りシステム的なものを作ろうとした作業の様子です。できたものはこちらになります。
自己紹介をしておきます。静岡東部の富士市で、家業の自動車製造機械の設計事務所にいます、IT系なんでも屋の佐野と言います。
IoTLTは今年の4月頃に「お風呂IoT」のネタを発表しました。これを風呂場に入れるってなかなかのことをやっていると自負してます。(残念ながらこのバージョンは未完なので入れてません)
最近は本業含めて色々あり(記事の最後を要チェック)めっきりIoTから離れていたので、そろそろ開発再開したいなと思ってます。
お風呂IoTはお風呂が溜まったことを検知するセンサーと、お風呂の蛇口を止める機能を備えたものです。現在は複数に分かれて互いを無線通信で連携させてます(しかも未完)が、それぞれ作るのも大変なのでやはりひとつにまとめるつもりです。
お風呂検知のセンサーはフロートスイッチとケーブルを垂らしてお風呂の浴室に固定してたのですが、超音波センサーが使えないかなと思いつきました。
というのも、防水の超音波センサーがあるそうです。しかも安い(万単位かと思ってた)
WINGONEER DC 5V防水超音波距離センサー測定距離トランスデューサーモジュール2.5 Mケーブル付きArduino | |
ASIN : B07PRF9DTD こちらはAmazonアソシエイトプログラム参加リンクです |
これを使えるようにしたら、蛇口操作の部分からセンサー部分をちょっと浴槽に伸ばすだけで水位検知もできるのではないかと思いついたわけです。
超音波センサーとは
超音波の反射で距離を計測するセンサーです。距離センサーとも言われてます。超音波は音波なので音波を遮蔽する物体に当てることで反射します。その反射の時間で距離を図るそうです。
IoT界隈だと👀に似ているような部分が特徴的なセンサーをよく見ます。
HC-SR04 超音波距離センサーモジュール For Arduino | |
ASIN : B004U8TOE6 こちらはAmazonアソシエイトプログラム参加リンクです |
超音波センサーの使い勝手を見たい
別業として関わってる大学研究室の学生さんが、見守り系のネタに困ってたらしいので、ちょっとだけ手伝うことにしたのですが、見守りするアイディアに使う手段を上げてみました。
超音波センサーを部屋のいくつかの箇所に平行に並べて、各センサーの距離が変わるときに人がいたとして、センサーの反応パターンで人の識別ができるかを見てみたらおもしろいのではないかと。
(設計事務所の人間なのに絵がひどい
格子状か並行にセンサーを並べて、その部屋でセンサーのマッピングするとおもしろいかなと考えました。
その時に、超音波センサーがある程度移動しやすく(電源あることは条件)小型な機器にできれば良いなと思います。
実装する
ということで実装しようと思います。まずは超音波センサーはさきほどの👀ことHC−SR04です。
利用したマイコン(?)はラズパイです。今回は研究室にたくさん転がってるRaspberry Pi 3を使いました。OSはRaspbian Buster 2019-09-26を使って、デスクトップモードで起動するようにしてます。(そうするとGPIO周りがすぐ使えるので)
I-O DATA Raspberry Pi メインボード Bluetooth(R) Wi-Fi対応モデル Raspberry Pi 3 model B 安心の1年間ハードウェア保証 UD-RP3 | |
ASIN : B01NHEBAN5 こちらはAmazonアソシエイトプログラム参加リンクです |
超音波センサーは温度計測をすることで精度が上がるらしいので、温度センサーも利用します。これも安価なもので、研究室に転がっていたので使いました。
Rasbee 5個 DHT11 温度センサー モジュール 国内配送 湿度センサーモジュール DHT-11 デュポンラインと付属 Raspberry pi Arduinoと互換 デジタル 相対 温湿度測定 DIY | |
ASIN : B07PTR1X9H こちらはAmazonアソシエイトプログラム参加リンクです |
ほしい要件はこんなかんじ
- Wifiがつながることを前提
- 電源を入れたら自動でセンサーの計測が開始
- 自動的に日付, 距離がCSV形式で記録される
ラズパイはPython+RPI.GPIOを経由して、超音波センサー, 温度センサーを初期化しつつ大体1秒単にで計測するようにします。
Wifiがつながること前提にする理由は、時間を正しく記録する必要があったためです。複数のセンサーがどのように検知したかを時系列で分析できないと人の動きのマッピングは難しいです。
ラズパイには通常のPCみたいにボタン電池による時刻の保持ができず、起動時にNTPサーバーより時刻を補正しています(GUIのデスクトップモード動作だと確か)。そのためにWifiが必要になります。
Wifiはwpa_supplicant.confを書いてRaspbianを焼いたSDカードの/boot領域にコピペすれば良いので割愛です。ググれば詳しい方法がたくさんあります。
利用したコード
やっつけレベルです。以下のサイトを参考にしました。~インスパイヤ~ほぼまるパクリレベルです申し訳ない。。
- 超音波センサー:メモ帳という名の備忘録: 測距センサー(HC-SR04)とRaspberry Piをつないでみる
- 温度センサー: szazo/DHT11_Python: Pure Python library for reading DHT11 sensor on Raspberry Pi
import RPi.GPIO as GPIO
import dht11
import time
import datetime
import os
# 超音波センサーのコード参考(というか拝借しました。感謝):https://umiushizn.blogspot.com/2017/10/hc-sr04raspberry-pi.html
# 温度センサーのライブラリ:https://github.com/szazo/DHT11_Python
def (pin, value=GPIO.HIGH, timeout=1.0):
start_time = time.time()
not_value = (not value)
# 前のパルスが終了するのを待つ
while GPIO.input(pin) == value:
if time.time() - start_time > timeout:
return 0
# パルスが始まるのを待つ
while GPIO.input(pin) == not_value:
if time.time() - start_time > timeout:
return 0
# パルス開始時刻を記録
start = time.time()
# パルスが終了するのを待つ
while GPIO.input(pin) == value:
if time.time() - start_time > timeout:
return 0
# パルス終了時刻を記録
end = time.time()
return end - start
def init_sensors(trig, echo, mode=GPIO.BCM):
GPIO.cleanup()
GPIO.setmode(mode)
GPIO.setup(trig, GPIO.OUT)
GPIO.setup(echo, GPIO.IN)
def get_distance(trig, echo, temp=15):
# 出力を初期化
GPIO.output(trig, GPIO.LOW)
time.sleep(0.3)
# 出力(10us以上待つ)
GPIO.output(trig, GPIO.HIGH)
time.sleep(0.000011)
# 出力停止
GPIO.output(trig, GPIO.LOW)
# echo からパルスを取得
dur = pulse_in(echo, GPIO.HIGH, 1.0)
# ( パルス時間 x 331.50 + 0.61 * 温度 ) x (単位をcmに変換) x 往復
# return dur * (331.50 + 0.61 * temp) * 100 / 2
return dur * (331.50 + 0.61 * temp) * 50
if __name__ == "__main__":
# 超音波センサーの情報用ピン
GPIO_TRIG = 26
GPIO_ECHO = 19
# 温度センサー初期化
# initialize GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()
# read data using pin 14
instance = dht11.DHT11(pin = 14)
# 超音波センサー初期化
init_sensors(GPIO_TRIG, GPIO_ECHO)
# 温度湿度の初期化
result_temp = 0
result_humi = 0
# 温度センサーを読みながら超音波センサーで距離を測る
while True:
result = instance.read()
# 距離は毎回計測するのでループ中で初期化
result_distance = 0
# エラーの番号的なものを記録
result_error = 0
if result.is_valid():
result_temp = result.temperature
result_humi = result.humidity
else:
result_error = result.error_code
# print("Error not temp: %d" % result.error_code)
result_distance = get_distance(GPIO_TRIG, GPIO_ECHO,result.temperature)
print("計測時間:{}, 温度:{} 湿度:{}, 超音波距離:{}, エラー番号:{}".format(
datetime.datetime.now().isoformat(),
result_temp,
result_humi,
result_distance,
result_error
))
with open("./result_{}.csv".format(os.uname()[1]), "a") as result_file:
result_line = "{},{},{},{},{}\n".format(
datetime.datetime.now().isoformat(),
result_temp,
result_humi,
result_distance,
result_error
)
# 結果を追記する
result_file.write(result_line)
time.sleep(0.5)
# next.Google スプレッドシートへ書き出す?
回路についてはFritzingで画像を用意したかったのですが、作ってる暇がないので完成後の写真ベースでご勘弁ください。
HC-SR04は5Vでセンサーの情報を出すので、抵抗で3.3Vに落としてます。その編も参考にしたサイトで確認できます。電源も温度センサーと同居させてます。アンペアあんまり考えていないのが素人
電源を入れたら自動的に動く方法
この辺をどうしようか考えてました。piユーザーへ自動的にログインする機能を利用して、.bashrcなどのシェル起動時の設定ファイルに実行させる方法もありますが、cronを使うことにしました。
cronは(再)起動時に一度だけコマンドを実行する@rebootという時間設定機能があります。これを使って、ラズパイを起動させたタイミングでセンサーの計測をするPythonスクリプトが動きます。
## crontab -l
@reboot /usr/bin/env python3 [スクリプトの絶対パス]
ちなみに、起動したと同時にpython3プロセスが動きます。なので止めるためにはプロセスをkillすればいいです killall python3
とかで良いと思います(他にPython3を使ってなにかされていた場合はPID指定で止めたほうが良いです)
完成したもの
並行で試すために2つ作りました。
実験
まだ終わってない!のでアイディアまでにしつつ😓
今の所の問題点は、距離情報がセンサーで検知できる以上に出てしまうときがあります。同じコードとOS, デバイスも同じなのに起きてしまうので個体差(ラズパイ側です)かも知れなくて、解決できてないです。。ぎぶみーへるぷ。。
終わりに
結論がかけないので以上。。というのは半分冗談です。。
ラズパイ周りとPythonの扱いに慣れていると、高機能である程度簡易にセンシングできるなと感じます。ちなみに開発はVS Code + Remote SSHで行いました。こちらも便利だったのでIoTな文脈で解説できたらしようかと。
お風呂IoTで超音波センサーを扱うかはわかりませんが、来年はまたIoTLTで成果を報告できたら良いなあと。
告知です
(11月から告知行脚してますが、例に習ってここでも告知です)
静岡ではじめて地域版Pythonカンファレンスをすることになりました🎉
PyCon JP Blog: PyCon mini Shizuoka 開催決定のお知らせ
Pycon mini Shizuokaが2020/2/29に開催されます。只今スタッフ総出の準備中です。~まだそれほど詳しくないけど~詳しくは公式サイトをチェックで!