サーモセンサをラズパイ に繋いで検温してみた（Python）


## 用途

とある制作で非接触検温をシステムに組み込むことになったので、ラズパイ＋サーモセンサを使い実装しました。手持ち形式ではなく、据え置き型のイメージです。

※先に言っておくと、そこまで精度は高くなく、誤差も場合によっては1度くらい生じる場合があります。ですが、そもそも非接触型の検温機ってそこまで精度高くない印象ですし、まあこんなものなのかなと思って使っております。（誤差に関しては、自分は複数回検温して、中央値を取って緩和してます）

## 使用した機材

PCのOS: macOS Catalina v10.15
サーモセンサ: https://www.switch-science.com/catalog/3395/
ラズパイ3B+: https://www.switch-science.com/catalog/3920/

サーモセンサの概要

> 各素子ごとの温度測定範囲は0℃～80℃です。測定エリアはセンサ正面（上下左右約60度）の四角錘で、このエリアを8x8ピクセルに分割した2次元画像が得られます。

## 準備

### ラズパイの環境構築

ラズパイのセットアップと接続の解説は省略します。SSHやディスプレイ接続等でターミナルが使える状態であることを前提にします。

今だと、ラズパイにOSをインストールするためのツールがあるみたいなので、下記の記事あたりを参考にするといいと思います。

https://www.itmedia.co.jp/news/articles/2006/05/news031.html

### I2C 有効化

```sh:terminal
sudo raspi-config
```

CUIの選択画面が出るので、
I2C -> enabled で有効化できます。

## 配線

ブレッドボートを使うといいです。[参考](https://dev.classmethod.jp/articles/raspberry-pi-led-lighting-cm-osaka/)

![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/2bdc38bd-cae5-6a4f-7261-e2ea4d8e7069.png)

配線が終わったら、接続できているか確認するコマンドを実行します。

```sh:terminal
i2cdetect -y 1
```

68があれば正しく認識されています。

```
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
```

## Pythonで温度取得

必要モジュールのインストール

```
sudo pip3 install adafruit-circuitpython-amg88xx
```

検温プログラム

```python
import time
import busio
import board
import adafruit_amg88xx

i2c_bus = busio.I2C(board.SCL, board.SDA)
sensor = adafruit_amg88xx.AMG88XX(i2c_bus, addr=0x68)
time.sleep(0.5) #スリープを挟まないとsensor.pixelsが取得できない
print(sensor.pixels)
```

実行すると、二次元配列が返ります。

```sh:terminal
sudo python3 test.py
[[23.5, 23.5, 24.25, 23.75, 23.25, 24.5, 24.25, 23.25], [23.0, 24.0, 23.5, 24.0, 23.75, 24.25, 24.5, 24.25], [23.75, 23.25, 23.75, 24.0, 23.75, 24.5, 24.5, 24.75], [23.25, 23.25, 23.25, 24.0, 23.5, 24.25, 24.5, 25.75], [23.5, 23.75, 23.75, 24.0, 23.5, 24.25, 24.0, 24.0], [23.75, 23.25, 24.25, 23.5, 23.75, 23.25, 23.75, 24.0], [23.25, 23.75, 23.5, 24.25, 23.75, 23.5, 24.0, 24.0], [23.5, 23.0, 24.0, 23.75, 23.25, 23.25, 24.75, 23.75]]
```

わかりやすくプロットしてみると...

```python
import time
import busio
import board
import adafruit_amg88xx
import matplotlib.pyplot as plt

i2c_bus = busio.I2C(board.SCL, board.SDA)
sensor = adafruit_amg88xx.AMG88XX(i2c_bus, addr=0x68)
time.sleep(0.5)
fig = plt.imshow(sensor.pixels, cmap="inferno")
plt.colorbar()
plt.savefig("plot.png")
```

顔です。それっぽく取得できてます。

![asddwq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/0a4155e8-f547-6611-f11a-c14259c9b7f3.png)

## 検温の実装

額周辺を検温する必要があるため、据え置き状態で検温するとなると、利用者に顔の位置を固定してもらう必要があります。さらに、理想は顔を所定の位置に合わせたら検温が始まるとUX的によいです。

これらを実現するために自分はカメラモジュールを別途用意し、所定の位置に顔があることを検知したことをトリガーにし検温を行いました。利用者にはディスプレイに表示されているカメラと枠を頼りに顔を合わせてもらいます。もちろんこの実装を行う場合は、カメラの位置とサーモセンサの相対位置を固定する必要があります。

以下が、サンプルプログラムです。顔検知はOpenCVを利用しました。（実際のコードをサンプル用に改変したもので、動作確認はできていません）

```python
import picamera
import picamera.array
import time
import cv2
import busio
import board
import adafruit_amg88xx

#カスケードファイルは配布されている物を使ってます
CascadeFile = "./haarcascade_frontalface_default.xml"

with picamera.PiCamera() as camera:
    #カメラの設定
    camera.resolution = (400, 400)
    camera.rotation = 180
    camera.start_preview()
    time.sleep(2)

    with picamera.array.PiRGBArray(camera) as stream:
        while True:
            #カメラの(50,50)から(350,350)の範囲を顔認識の対象かつ顔判定される最小サイズが250x250なので、認識する位置と大きさが絞られます
            camera.capture(stream, 'bgr', use_video_port=True)
            gray = cv2.cvtColor(stream.array[50:350, 50:350], cv2.COLOR_BGR2GRAY)
            cascade = cv2.CascadeClassifier(CascadeFile)
            face_list = cascade.detectMultiScale(gray, minSize=(250, 250), minNeighbors=3)

            if len(face_list) > 0:
                x, y, w, h = face_list[0]:
                temps = []
                i2c_bus = busio.I2C(board.SCL, board.SDA)
                sensor = adafruit_amg88xx.AMG88XX(i2c_bus, addr=0x68)

                #5回検温
                for i in range(5):
                    time.sleep(0.3)
                    _max = 0
                    for j in sensor.pixels[2:6]:
                        for k in j[3:5]:
                            _max = max(_max, k) #範囲内の最大値を扱う
                    temps.append(_max + 5) #表面温度と体温のギャップ埋め
                print(round(sum(sorted(temps)[1:4]) / 3, 1)) #検温結果をソートし中3つの平均を出力
                break

            stream.seek(0)
            stream.truncate()
        camera.stop_preview()
```

この部分は、精度向上の余地があるかもしれません。（気温を元にこの値を決めるなど）

```
temps.append(_max + 5) #表面温度と体温のギャップ埋め
```

このコードについて細かく説明するのは大変なので、詳しくは`OpenCV` `PiCamera`あたりで調べてみてください。

## 最後に

あくまで学生が制作でざっくりと実装した物なので、確実性が求められる場合はあまり参考にしすぎないようお願いします。

ありがとうございました。