紫外線と戦う技術

## はじめに
季節は冬ですが、夏っぽいテーマです。

女性を中心に「紫外線を避ける行動」をとっている人は少なくないのではないでしょうか。
主な目的は「日焼けの防止」かと思います。帽子、日傘、日焼け止めクリーム・・・といった対策を日々とられていることでしょう。
日焼けは「人」に限ったことではありません。例えば、洗濯物、書籍、畳など日焼けによって物の色合いが変色するということはよく知られています。人を紫外線から守る術は多くありますが、物を紫外線から守る技術というのは意外と少ないのではないかと思いました。
そこで、今回はIoTの技術を使って紫外線を観測し、物を紫外線の脅威から守る仕組みを作ってみました。
「物」といっても千差万別なので、紫外線に縁がありそうな「洗濯物」をテーマにします。守り方はシンプルに「アラートで人に通知」とします。イメージは「”洗濯物が強い紫外線を浴びてます”という通知を受けたら洗濯物を取り込む」という流れです。
![オペレーションイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43330/07dc1706-75e3-3787-327b-5fc3e3d3ac2e.png)

## 紫外線を知ろう
紫外線の情報を得るには「紫外線センサー」を使用します。ただ、「使用する」といっても紫外線とはどういう情報を得ることが可能なのかがわからなければ始まりません。ということで紫外線とは何かを調べてみると、環境省が公開している[紫外線環境保健マニュアル](https://www.env.go.jp/chemi/uv/uv_pdf/01.pdf)でまとめられていました。ここでは仕組み作りに必要な情報だけを取り上げます。

### 紫外線は3つの種類がある
紫外線は波長の長さによって、A領域、B領域、C領域の3つの種類があります。
このうちC領域の紫外線は、空気中の酸素分子とオゾン層によってさえぎられているため、地上にはA/B領域の2種類の紫外線が届いています。そのため、紫外線センサーはA/B領域の両方に対応しているものを選択するほうが良いでしょう。

### 紫外線の強さを測る指標「UVインデックス」
日焼けの度合いに影響するのは「紫外線の強さ」です。そして紫外線は「人体への影響度合い」を示す指標として「UVインデックス」というものが国際委員会で定められています。
いくつかの紫外線センサーを調べてみると、紫外線の値として取得できる情報にUVインデックスがあるため、これを使うのが良さそうです。

## 紫外線を検知して対策する仕組みを考える
### 仕組みの全体像
仕組みの大まかなイメージは下図のような感じです。
![全体像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43330/ec0a2afd-3d3a-b308-7b3f-f102cb3f0141.png)
① 洗濯物と同じ場所に紫外線センサーを設置して、UVインデックスを計測する
② UVインデックスの値（＝紫外線の強さ）が閾値に到達したか判定する
③ 閾値以上の場合、利用者のLINEにアラートを通知する

次に仕組みの構築に必要な機材（ハードウェア）、ソフトウェアを考えます。
まずは機材面です。
紫外線センサーは、紫外線の強さをUVインデックス値で取得できるものが望ましいです。冒頭でも述べましたが、市販の紫外線センサーはUVインデックス値で取得できるものが多数ありますので、それらから選べばよいでしょう。
センサーを制御するコンピュータですが、LINEへの通知にLINE APIと通信するといった、比較的に高度な処理を実行するため、Raspberry PIを使用します。
次にソフトウェア面です。
必須なのがプログラムを作成するプログラミング言語のソフトウェアですが、紫外線センサーを制御するモジュールをメーカーが提供しているので、提供モジュールが対応している言語を使用することにします。

### ハード構成
今回は、下表の機材を使用します。

|種類|機材名|
|---|---|
|コンピュータ|Raspberry PI 3|
|紫外線センサー|[VEML6075搭載UVセンサーモジュール](https://www.switch-science.com/catalog/4083/)|

紫外線センサーは、Vishay社のVEML6075を使用します。
選定の理由ですが、「紫外線のA領域、B領域に対応し、UVインデックスを求める機能が備わっていること」、「コンピュータがRaspberry PIだけではなく、（今回は使用しませんが）Arduinoでも使用できること」、「それでいて1つ900円程度と安価であること」の3点が購入の理由です。
　また厳密に比較したわけではありませんが、より高額な製品と比較してもUVセンサー（チップ）の質が良いというのもポイントが高いです。他製品は紫外線だけでなく、赤外線センサー、可視光の光量測定など多機能であるものもあります。今回は紫外線のみを取り扱うため、こちらを選択しました。

### ソフトウェア構成
開発言語ですが、VEML6075がPython用のモジュールを提供していることから、Python3を採用することにしました。VEML6075のPythonモジュールは[GitHub](https://github.com/adafruit/Adafruit_CircuitPython_VEML6075)で公開されているため、ドキュメントを参考にRaspberry PIにインストールします。

## 実際につくってみた
それでは構築から実行結果を確認するところまで解説します。
最終的なゴールは「洗濯物が強い紫外線を受けていることが分かる」ことです。
そのために、まずは紫外線センサーを動作させる「電子回路」を構築します。次にソフトウェアで「センサーから紫外線情報（＝UVインデックス）を取得する」「紫外線の強さが基準値を超えたか判定」「基準値を超えた場合にLINEでユーザに通知」の3つの機能を作ります。

### 電子回路の構築
まずはRaspberry PI、紫外線センサーをブレッドボードを使って配線し、電子回路を組んでいきます。
VEML6075の[データシート](https://cdn-learn.adafruit.com/downloads/pdf/adafruit-veml6075-uva-uvb-uv-index-sensor.pdf)を参考に、下図のように配線しました。
![veml6075_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43330/b2bc029b-b19f-6ff4-d1e8-5baf8f4ef46f.png)
VEML6075の電源は3.3V/5Vの両方に対応しています。大丈夫だと思いますが、万が一電力不足による動作不良が起きた場合を考えて、今回は5Vに接続しています。その他はデータシート通りです。

### ソフトウェアのインストール
続いてプログラムの実装です。
先に述べた通り、実装に必要なものは開発言語と開発言語に合ったVEML6075のモジュールです。
Raspberry PIのOSにRaspbianを使用している場合は、Python3が標準インストールされていますので、そちらを使用します。
※Python2/3が両方インストールされているのでコマンドは注意してください。Python3のコマンドは"python3"です。（"python"と実行すると2.7系が実行されます）

VEML6075のPythonモジュールは[GitHub](https://github.com/adafruit/Adafruit_CircuitPython_VEML6075)にインストール手順が記載されているのでそちらを参考にしてください。
モジュールのインストールが完了したら、Pythonのプログラム上からimportし、エラーにならないことを確認しましょう。

```python:veml6075_sample.py
import adafruit_veml6075
```

```shell:プログラムの実行（"python3コマンドを使用している点に注意"）
python3 veml6075_sample.py
```

### LINE 通知の準備
LINE通知の実現は、[LINE Notify](https://notify-bot.line.me/ja/)を使います。メッセージを送るだけなら非常に簡単に実装できます。
LINE Notifyの利用にはLINE IDでログイン後、トークンの発行が必要です。手順は下記サイトで丁寧に説明されているので、そちらをご覧ください。

* [[超簡単]LINE notify を使ってみる](https://qiita.com/iitenkida7/items/576a8226ba6584864d95)


### プログラムの実装
VEML6075からUVインデックスを取得するプログラムは、[GitHub](https://github.com/adafruit/Adafruit_CircuitPython_VEML6075)に下記のサンプル(/example)が提供されていますので、こちらをベースに実装します。まずはサンプルを実行して、どのように値が取得出来ているか確認します。

```python:veml6075_simpletest.py
import time
import board
import busio
import adafruit_veml6075

i2c = busio.I2C(board.SCL, board.SDA)

veml = adafruit_veml6075.VEML6075(i2c, integration_time=100)

print("Integration time: %d ms" % veml.integration_time)

while True:
    print(veml.uv_index)
    time.sleep(1)
```

モジュールのオブジェクトを生成し、そこからuv_indexという値を取得するだけ。とてもシンプルです。
Raspberry PIと紫外線センサーを窓際に配置してプログラムを実行します。
実行結果が次の画像です。UVインデックスの値が取得できているのがわかります。
![ログ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43330/1c090ac0-9340-7a5b-02e2-faa5d093ba45.png)

続いて、UVインデックスの値が閾値を超えたときにLINEの通知を送るように変更します。
閾値ですが、一旦0.3以上で通知を出すこととします。またwhileループ内のスリープを1->60（1分毎にチェック）に変更します。
※1秒毎に通知を飛ばしたら大変なことになりました…。

```python:veml6075_sample.py（LINENotify通知機能を追加のところのみ抜粋）
import requests

　・・・

while True:
    notifyLine()
    time.sleep(60)

def notifyLine():
    url = "https://notify-api.line.me/api/notify"   # APIのエンドポイント
    token = 'Line Notifyのサイトで発行したトークンの文字列'

    message = '紫外線が強いよ'
    payload = {"message" :  message}  # 送信するメッセージを設定

    r = requests.post(url,headers=headers,params=payload)  # APIにリクエストを送信
```

実行すると、LINEのトーク画面に「LINE Notify」が現れ、送信したメッセージが送られてきます。
シンプルですが、非常に簡単に実現できましたね。
![line_notify.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43330/e6a41a01-618a-8efc-b9a5-3c385d72f3f8.png)



### 紫外線の取得と通知ができた・・・が、十分ではない。
無事にRaspberry PIと紫外線センサーで「物に強い紫外線が当たったら知らせてくれる」仕組みが完成しました。仕組みとしては十分ですが、1つ課題があります。それは「閾値」です。

上記のプログラムでは閾値を「0.3」と定めました。これが適切な閾値かというと、特に根拠はありません。VEML6075のサンプルプログラムを実行した際に取得できた数値をみて決めただけの数値です。
実用的な仕組みに仕上げるには、閾値を適切に設定することは必要不可欠です。今回の「物」は洗濯物の設定なので、科学的／学術的根拠に基づいて「洗濯物はUVインデックスが〇〇以上なら日焼けする」という数値があるのがベターですが、残念ながらそのような数値を得ることはできませんでした。
そこでもう一つの手段として、数日間UVインデックスを観測し、その実測値から閾値を決定したいと思います。閾値の決定方法ですが、時間毎にUVインデックスの平均値を求め、値の推移を観察して決めることにします。

## 紫外線の観察
「数日間UVインデックスを観測する」ためには、取得したUVインデックスと取得した時刻を何らかのデータベースに記録する仕組みを作り、データ分析をすればよいでしょう。今回はデータベースにFirebaseのFirestoreを採用し、veml6075_simpletest.pyを修正しFirestoreにUVインデックスと取得日時を登録するプログラムを実装します。
Pythonには、Firestoreの機能を容易に使用できるモジュールが提供されているので、そちらを用いたプログラムを実装しましょう。ソースコードは下記の通りです。
※firebaseのモジュールは[公式サイト](https://firebase.google.com/docs/admin/setup/)の手順に従ってインストールしてください。


```python:uvsensing.py
import datetime
import time
import board
import busio
import adafruit_veml6075
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore

# Use a service account
cred = credentials.Certificate('firebase-config.json') 
firebase_admin.initialize_app(cred)

# connect firestore
db = firestore.client()

# setup VEML6075 
i2c = busio.I2C(board.SCL, board.SDA)
veml = adafruit_veml6075.VEML6075(i2c, integration_time=100)
print("Integration time: %d ms" % veml.integration_time)

# get UV Index
while True :
    uvindex = veml.uv_index

    if uvindex > 0:
        doc_ref = db.collection('uv').document()
        doc_ref.set({
            'datetime': firestore.SERVER_TIMESTAMP,
            'uvindex': uvindex,
        })
    time.sleep(60)
```

ソースコードについて2点補足します。
1点目はUVインデックスを登録する際に「uvindex > 0」という条件をつけました。これは夜などUVインデックスが取得できない場合に、0が返却されます。0をデータとして記録すると、閾値をUVインデックスの平均値から求める時にノイズになるため、記録しないようにしています。
2点目はスリープ時間を1秒から60秒に変更しました。1秒ずつでも特に問題はないのですが紫外線が秒単位で急激に変化するものではないと感じたためです。

### UVインデックスの時間推移
プログラムを9日間実行し、時間毎の推移をグラフ化したのが下図です。
![グラフ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43330/7fcc0220-9c34-9ccb-79a0-32c1151a476b.png)

縦軸がUVインデックスの値、横軸が時刻、各折れ線が測定日です（11月21日～29日）。
結果を考察してみます。
まず時刻ですが、6時台～16時台の範囲でデータが取得出来ており、それ以外の時刻では情報が収集できていません。これは計測場所での日照時間が現れています。紫外線センサーは紫外線が取得できなかった（あるいはUVインデックスに算出した結果極小値だった）場合に、0を返します。先のソードコード解説で述べた通りUVインデックスが0以下の場合はデータを記録していないため、このような結果になっています。この結果は実装の狙い通りになっています。

次にUVインデックスの値ですが日によって最大／最小にばらつきがあります。ピーク時刻も様々です。
※観測環境のネットワーク等の問題で一部の日付でデータが取得出来ていない時間帯があります。
これは天候などの外的要因による差と考えられます。例えば、最大値が最も大きいな11/29の東京の天気は終日晴れでした。それに比べて11/28は終日雨です。雲や大気中の水分に紫外線が拡散され、UVインデックスが低下したと考えられます。

### 紫外線が「多い」と感じる閾値を観測値から定義する
9日間の観測でしたが、得られたデータからUVインデックスは天候などの外部環境により最大／最小、ピーク時刻などに差があることが読み取れると思います。この結果があれば、いくつかの方法で閾値を決めることができます。
一番単純な方法は「全データの平均値」を閾値にすることでしょう。今回得られたデータを全ての平均をとると、約0.127です。ただ、この値は閾値としては難しいかもしれません。グラフをみると、0.127の値はかなり低い値です。11/29の晴れの日の場合、7時～14時台よりやや前の時間帯までUVインデックスが0.127を超えてくるため、晴れの日は洗濯物が干せなくなってしまいます。
次に単純な方法は「晴れの日の平均値」を閾値にすることでしょう。11/21～29の内晴れの日は11/21,25,29の3日間です。その3日間の平均は約0.32です。全ての平均より、こちらの方がより現実的に感じます。

このほかにも様々な定義方法が考えられます。複雑にはなりますが、晴れの日の平均値を更に昇華し、「晴れの日の上位5%以内の平均値」や、観測日数を増やして季節に合わせて一定の係数を掛け合わせることで季節によらない閾値を決めることができます。
また、少々脱線しますが、UVインデックスを閾値にするのではなく紫外線量（UVインデックス×時間）を指標にしたり、紫外線センサー以外の情報（例えば温湿度、光量）を掛け合わせることでより精度の高い指標と判断基準を作り出すことができそうです。さらに言えば、判断ロジックも現在の1回の測定で判断するのではなく、例えば過去10回の平均値で判断するという方法にすれば、センサーの誤検知（外れ値の発生等）を低減した仕組みにすることができます。
このようなデータの使い方、ロジックの組み方を実際にデータを観測しながらチューニングしていけば、より良いものになることでしょう。


## おわりに
紫外線と戦う技術と題して、紫外線センサーから得た情報で紫外線を感知する仕組みと、その精度を向上させるためにデータを観測するアプローチを紹介しました。
今回この仕組みを構築してみて一番の感じたことは、市販のセンサーが情報をかなり扱いやすい形にしてくれている点です。当初、紫外線について調べ始めた時に、A領域、B領域と波長が違うデータであること、観測した情報が光線で具体的な観測のイメージがつかなかった（何らかのアナログ値だろう程度）ために、データの取り扱いが想像できていませんでしたが、センサーを調べてみると「UVインデックス」というデジタル値として取得でき、簡単に取り扱うことができました。センサーメーカーの方には頭が上がりませんね。
もう1点、センサー値の生データを扱う際は、「観測→分析／評価」の活動が必要不可欠であることと、この活動にかなり労力がかかるという点です。今回は収集したデータ量が少ないために考察できていませんが、より大量のデータを分析すると、センサー誤差、外れ値などの考慮が必要になります。何をもって「誤差」なのか？許容範囲はどこまでか？などといった課題が待っていることでしょう。過去の経験上、このような課題は往々にして「正解」がないため、「どのように見極めるか」が腕の見せ所ではないでしょうか。