raspberry piとサーモグラフィを使って体温測定デバイスを作成します。
サーマルカメラはamg8833モジュールをアマゾンで購入。

今回作成するデバイスの使用は以下の通り。

  • パイカメラとサーマルカメラの映像は常時表示
  • パイカメラで顔を認識したらサーマルカメラの最高温度によって赤黄青のLEDのいずれかを点灯
  • raspberry pi起動と同時にプログラムを起動
  • プッシュボタンでOSシャットダウン

性能テストを含めてraspberry pi3を使用します。

raspberry piのインストール

OSは最新のBusterを使用します。
ダウンロードとセットアップで数時間かかってしまいました。
セットパップが終わったらパイカメラを有効化、SSHやVNCもついでに有効化します。
Python3はデフォルトで入っているので、必要なライブラリをインストールします。

インストールが必要ライブラリは以下の通り

  • opencv
  • numpy
  • matplotlib

以前、opencvのインストールしたときは1日かかったけど、今回は数時間で終わりました。
ただ、opencvだけインストールするとエラーになるのでいろいろと事前にインストールしておかないとダメ見たい
試行錯誤の結果以下のような感じでセットアップ

sudo apt-get install -y libhdf5-dev libhdf5-serial-dev libhdf5-103
sudo apt-get install -y libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5
sudo apt-get install -y libatlas-base-dev
sudo apt-get install -y libjasper1
sudo apt-get install -y libjasper-dev
sudo pip3 install pillow
sudo pip3 install matplotlib
sudo pip3 install opencv-python==4.1.0.25
nano .bashrc
export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1 # 追加
source .bashrc

確認

python3
>>import cv2
>>cv2.__version__
'4.1.0'
>>quit()

“libqt4-test”をインストールしておかないと以下のエラーが出ます。

ImportError: libQtTest.so.4: cannot open shared object file: No such file or directory

“LD_PRELOAD”の設定をしておかないと以下のエラーが出ます。

ImportError: /usr/local/lib/python3.7/dist-packages/cv2/cv2.cpython-37m-arm-linux-gnueabihf.so: undefined symbol: __atomic_fetch_add_8

amg8833のライブラリも正規サイトにある通りインストール

https://cdn-learn.adafruit.com/downloads/pdf/adafruit-amg8833-8×8-thermal-camera-sensor.pdf

sudo pip3 install adafruit-circuitpython-amg88xx

基盤作成

raspberry pi、サーマルカメラ、LEDを以下のように結線する基盤をユニバーサル基盤で作成。

プログラム作成

opencv、サーマルカメラのロジックは以下のサイトを参考にさせていただきました。

「Raspberry PiとOpenCVによる画像認識で人の顔を判別する」

「サーマルセンサー「AMG8833」とRaspberry Piで非接触体温計っぽいものを作ってみた- EeePCの軌跡」

githubからopencvのソースを取得し、カスケード分類器を使うようにします。

wget https://github.com/opencv/opencv/archive/master.zip
unzip master.zip
cp -r opencv-master/data/haarcascades/ haarcascades/

以下のようにプログラムを作成

Thermometer.py

# -*- coding: utf-8 -*-
import picamera
import picamera.array
import cv2 as cv
import time
import busio
import board
import adafruit_amg88xx
import matplotlib.pyplot as plt
from gpiozero import LED
import RPi.GPIO as GPIO
import os
import sys

POFF_BUTTON = 18
GPIO.setmode(GPIO.BCM)
LED_B = LED(17)
LED_Y = LED(27)
LED_R = LED(22)

try:
	GPIO.setup(POFF_BUTTON, GPIO.IN, pull_up_down = GPIO.PUD_UP)
except Exception:
	sys.exit()

# I2Cバス初期化
i2c_bus = busio.I2C(board.SCL, board.SDA)

# サーマルカメラ初期化
sensor = adafruit_amg88xx.AMG88XX(i2c_bus, addr=0x69)

# 初期化待ち
time.sleep(.1)

plt.ion()
# ウィンドウ最大化のための設定
plt.switch_backend('QT4Agg')

plt.subplots(figsize=(8, 4))

# 顔検出のための学習元データを読み込む
face_cascade = cv.CascadeClassifier('haarcascades/haarcascade_frontalface_default.xml')

with picamera.PiCamera() as camera:
	with picamera.array.PiRGBArray(camera) as stream:
		# パイカメラ解像度の設定
		camera.resolution = (512, 384)
		face_on = False

		while True:
			# パイカメラの映像取得
			camera.capture(stream, 'bgr', use_video_port=True)
			# パイカメラ映像をモノクロに変換
			grayimg = cv.cvtColor(stream.array, cv.COLOR_BGR2GRAY)

			# 顔検出
			facerect = face_cascade.detectMultiScale(grayimg, scaleFactor=1.2, minNeighbors=2, minSize=(100, 100))
			face_on = len(facerect) > 0
			if face_on:
				# 検出箇所赤枠表示
				for rect in facerect:
					cv.rectangle(stream.array, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0, 255), thickness=3)

			# パイカメラ映像と顔検出結果を画面1フレーム目に表示
			plt.subplot(1,2,1)
			plt.imshow(stream.array)

			# 配列クリア
			stream.seek(0)
			stream.truncate()
			
			# サーマルカメラデータを画面2フレーム目に表示
			plt.subplot(1,2,2)
			fig = plt.imshow(sensor.pixels, cmap="inferno", interpolation="bicubic",vmin=27,vmax=40)
			# 凡例表示
			plt.colorbar()

			# ウィンドウ最大化
			figManager = plt.get_current_fig_manager()
			figManager.window.showMaximized()

			# 画面表示
			plt.draw()

			plt.pause(0.01)
			plt.clf()
			
			# LED初期化
			LED_R.off()
			LED_Y.off()
			LED_B.off()
			# サーマルカメラの最高温度によりLED点灯
			if face_on:
				max_value = max(max(sensor.pixels))
				if max_value > 35:
					LED_R.on()
				elif max_value > 33:
					LED_Y.on()
				else:
					LED_B.on()

			# キー入力で停止
			if cv.waitKey(1) > 0:
				break
			
			# プッシュボタンが押されたらシャットダウン
			sw_status = GPIO.input(POFF_BUTTON)
			if sw_status == 0:
				os.system("sudo shutdown -h now")
				print('shutdown called')
				break

		# 画面表示終了
		cv.destroyAllWindows()

opencvにも画像表示機能はあるけど、サーマルカメラの画像と同じウィンドウに表示したかったのでmatplotlibのpyplotにまとめるようにしています。

サーマルカメラの性能上、距離があると若干低めの温度で測定されるため低い温度でLEDを点灯させるようにしました。

利便性を高めるためにプッシュボタンでシャットダウンするように作成

自動起動

OS起動と同時にプログラムが起動するように設定をします。
いろいろやり方がありますが、今回はautostartを使いました。
設定内容は以下の通り

mkdir -p ~/.config/lxsession/LXDE-pi
cp /etc/xdg/lxsession/LXDE-pi/autostart ~/.config/lxsession/LXDE-pi/
nano ~/.config/lxsession/LXDE-pi/autostart

# 最終行に以下を追加
python3 Thermometer.py

筐体作成

基盤ですべて固定できないので筐体を作成しました。
2mm厚のプラ板を使用しています。
設計、加工、塗装で丸2日かかりました。

まとめ

トータル工数5人日ぐらいです。
動作はそれなりに動きました。

今後の課題は以下の通り

  • 正確な温度がはかれていない気がする(高価なセンサーに変えるしかない)
  • マスクしてると顔認識しない
  • やっぱり動きが重い(raspberry pi4に変えてみるか)
  • サーマルカメラの向きが上下逆だった!(筐体から作り直し T_T)
  • インターネットを使って何かしたい
  • 味気ないのでキャラクタ化したい(プラモデル+サーボモータで)

頑張れたら、いろいろやってみようと思います。