【手作業】の検査はお任せあれ！

[前々回](https://qiita.com/shinmura0/items/41b15b308e9ba54a90d0)に示したとおり、三菱電機から素晴らしいAIが登場しました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">教師データ不要でAIが工場での作業者の手順の違いや無駄な動作を検出 <a href="https://t.co/rKYmyzqNxn">https://t.co/rKYmyzqNxn</a> <a href="https://t.co/8DlgZRro05">pic.twitter.com/8DlgZRro05</a></p>&mdash; MarvinNewsJP (@MarvinNewsJP) <a href="https://twitter.com/MarvinNewsJP/status/1097360275236257792?ref_src=twsrc%5Etfw">2019年2月18日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


そして、[前々回](https://qiita.com/shinmura0/items/41b15b308e9ba54a90d0)は三菱電機の特許技術の中身を推理しました。
今回は、これを実際に使ってみます。


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">異常作業その１（Qiita用）。MobileNet V2 SSDlite + BayesianOptimization + KalmanFilter <a href="https://t.co/MSXQzANybK">pic.twitter.com/MSXQzANybK</a></p>&mdash; shinmura0 (@shinmura0) <a href="https://twitter.com/shinmura0/status/1144088836110053376?ref_src=twsrc%5Etfw">2019年6月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#概要
コード全体は[Github](https://github.com/shinmura0/BayesAssemble)に置きました。

本技術を一言でいうと、「手の動きを見て、いつもと違う動きなら警告する」という
ことになります。

基本的には、元となった三菱電機のシステムと同じアルゴリズムです。
ただし、ハード的な面で相違点があります。

||三菱電機|本技術|
|---|---|---|
|カメラ|ステレオカメラ|**単眼カメラ**|
|手の座標|XYZ|XY|
|作業員の装備|リストバンド|**無**|
|アドバイスの提示|有|無|

単眼カメラで、かつ作業員の装備が不要なところが本技術のウリです。
ただ、課題も色々ありまして、そこは後述します。

#技術的な内容
詳細な処理の流れは以下のとおりです。

+ ```MobileNet V2 SSDlite```で左手と右手の座標を取得する。
+ 取得した座標に対し、カルマンフィルタを適用する。
+ ベイズ最適化を適用して最適な分割点を探索する。


このような処理の流れになっている理由は、[前々回](https://qiita.com/shinmura0/items/41b15b308e9ba54a90d0)の記事で書いています。

##MobileNet v2 SSDlite　で手の座標を取得

MobileNet v2 SSDliteは、以前記事にした[handtracking](https://qiita.com/shinmura0/items/07d976fc13ce774036b8)を使っています。

ここでいう座標とは検出枠の中心を意味します。従って、左右の手があるので

+ 右手のx座標
+ 右手のy座標
+ 左手のx座標
+ 左手のy座標

の４つの値を取得しています。

ちなみに、handtrackingはPCのCPUでも10FPSくらい出るので、GPUを使わなくても
本技術は構築できると思います。ただ、今回の実装はNCS2（もしくはMovidius）の
GPUを使った仕様になっています。

##カルマンフィルタ

このままだとノイズがひどいため、カルマンフィルタを適用します。

ここでいうノイズとは、SSDが誤検知して検出枠が変な場所に飛ぶ現象をいいます。
こんな感じです。

![iswyn-cync0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/674b11e6-91ff-837f-809c-441f46541209.gif)

あとは、当然、検出枠のゆらぎもノイズとなっています。

カルマンフィルタは状態空間モデルを定義することにより、明示的に
ノイズを除去することが可能です。さらに、t+1秒後の位置も予測する
ことができるため、非常に汎用性が高いものとなっています。

物体検出とカルマンフィルタは、かなり相性が良いと感じています。
それは物体検出は以下の性質があるためです。

+ 検出枠にノイズが混入しやすい
+ 物体の位置を予測、あるいは同一のものか同定するタスクが多い

交通量を調べた以下の記事でも、カルマンフィルタが大活躍しています。
https://qiita.com/ComputerVision/items/d865f084c98a59349910

話しを元に戻して、今回のカルマンフィルタの適用はpykalmanを使用しました。
そして、本稿では（恐らく）カルマンスムーザーの値を取得しています。

```py
kf = KalmanFilter(transition_matrices=np.array([[1, 1], [0, 1]]),
                              transition_covariance=0.01*np.eye(2))

result.append(kf.em(x[i]).smooth(x[i])[0][:, 0])
```

カルマンスムーザーは以下の記事が参考になります。
https://qiita.com/acela86/items/fdafb10ce5e2e0d73c9e

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/b9f4c46d-be98-4f1a-ea22-d91c31d23244.png)

上の図はカルマンフィルタ**適用前**、下の図はカルマンフィルタ**適用後**です。
ノイズが消え、人らしい手の動きが再現されています。

##ベイズ最適化

[前々回](https://qiita.com/shinmura0/items/41b15b308e9ba54a90d0)の記事では、分割点の決め方としてcos類似度が一番大きくなる点を選びました。
つまり、波形が似ているペアを作り出していたわけです。

ところが、今回それを適用してもうまく差が出ず、代わりに時系列データの類似度として
よく使われる動的時間収縮法（DTW）を使いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/9b36eb89-28e6-6489-bbe0-6f06e17147f1.png)

DTWの算出はdtwライブラリを使用しました。

```py
d, _, _, _ = dtw(x, y, dist=euclidean_norm)
result.append(d)
```

ただし、今回の題材では前々回の記事と異なり、四つの波形が同時に取得されます。
本稿では、四つの波形でそれぞれDTWを取得し平均値を算出しました。そして、
その平均値が一番小さくなるように（波形が似ているとDTWは小さくなる）分割点を
探索させています。

そして、探索手法はブラックボックスを良い感じに探索してくれるベイズ最適化を
使っています。

##結果
今回は、こんな感じで箱に付箋を貼る作業を題材にします。

![js765-m3lzf.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/15f44bf4-246c-75aa-130b-560808dc58ab.gif)

箱の四隅に付箋を貼るのが正解です。

+ 異常その1
最後に**付箋を余分に貼っている**ため、異常な作業となっています。
異常を検知すると枠が赤くなります。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">異常作業その１（Qiita用）。MobileNet V2 SSDlite + BayesianOptimization + KalmanFilter <a href="https://t.co/MSXQzANybK">pic.twitter.com/MSXQzANybK</a></p>&mdash; shinmura0 (@shinmura0) <a href="https://twitter.com/shinmura0/status/1144088836110053376?ref_src=twsrc%5Etfw">2019年6月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
異常の閾値は「8」に設定しています。

+ 異常その2
最後の**付箋を貼るのを忘れている**ため、異常な作業となっています。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">異常作業その２（Qiita用）。MobileNet V2 SSDlite + BayesianOptimization + KalmanFilter <a href="https://t.co/04HUFTM8cT">pic.twitter.com/04HUFTM8cT</a></p>&mdash; shinmura0 (@shinmura0) <a href="https://twitter.com/shinmura0/status/1144089121700233216?ref_src=twsrc%5Etfw">2019年6月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

+ 正常その1
最後の付箋は長い時間がかかっていますが、手順は正しく正常な作業をしています。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">正常作業その１（Qiita用）。MobileNet V2 SSDlite + BayesianOptimization + KalmanFilter <a href="https://t.co/sgRaAV2P33">pic.twitter.com/sgRaAV2P33</a></p>&mdash; shinmura0 (@shinmura0) <a href="https://twitter.com/shinmura0/status/1144088948064473090?ref_src=twsrc%5Etfw">2019年6月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

+ 正常その2
最初の付箋は長い時間をかけていますが、手順は正しく正常な作業をしています。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">正常作業その２（Qiita用）。MobileNet V2 SSDlite + BayesianOptimization + KalmanFilter <a href="https://t.co/RMBqUaQD7L">pic.twitter.com/RMBqUaQD7L</a></p>&mdash; shinmura0 (@shinmura0) <a href="https://twitter.com/shinmura0/status/1144089460562223105?ref_src=twsrc%5Etfw">2019年6月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#使い方

##環境
+ Raspberry Pi 3 model B (PCも可)
+ NCS2
+ USBカメラ

##必要なライブラリ
+ [OpenVINO](https://qiita.com/shinmura0/items/318c775544fae64ae0db)
+ [dtw](https://github.com/pierre-rouanet/dtw)
+ [pykalman](https://github.com/pykalman/pykalman)
+ [GPy](https://github.com/SheffieldML/GPy)
+ [GPyOpt](https://github.com/SheffieldML/GPyOpt)

##手順
+ NCS2もしくはMovidiusを装着
+ [Github](https://github.com/shinmura0/BayesAssemble)からダウンロード
+ 以下のコマンドで実行

```
python3 main.py -wd 640 -ht 480 -vidfps 15
```

+ カメラの画像が出力され、右上に文字が出る。
+ 「m」キーを押すと、トレインデータ（お手本）の収録開始。
+ 「e」キーで収録終了。
+ 「s」キーでテストデータの収録開始。
+ 「e」キーで収録終了。

その後、1分くらいフリーズします・・・

+ その後、コンソール画面でスコアが出てくる。これも終了するまで１分くらいかかる。終了すると「finish」と出る。
+ フォルダ内に、result_1.aviが保存され、動画内でスコアが表示されている。
+ 続いて、テストする場合、「s」キーでテストデータの収録開始。「e」キーで収録終了。

#課題
+ 特許でカバーされる可能性がある。
前々回も示したとおり、三菱電機から特許が申請されています。特許公開はいつになるのか分かりませんが、本技術が特許の範囲内である可能性も考えられます。その場合、研究用途以外の使用は制限される可能性があります。

+ 手袋をしているとうまく認識できない。
本技術は、「素手」を検出できるディープラーニングを使っていることから、「手袋をした手」は検出できません。手袋をした手を検出させるには、新たに手袋をした手の画像を集める必要があります。

+ 組立品の位置ずれに頑強ではない。
本技術は、「組立品は決まった位置におかれている」という前提で成り立っています。従って、組立品の置かれる位置にバラツキがあると、誤動作する可能性があります。ただ、位置ずれに対し頑強にする方法はいくつか思いつきます。

+ 手順前後に対応できない。
これは例えば、A→**B→C**という手順がお手本だったとして、A→**C→B**でも正常な場合、本技術をそのまま使うとNGと判定されてしまいます。ただ、これを回避する方法はいくつか思いつきます。

上記の課題は、特許以外は全て克服できると考えています。

製造業の方でご興味ある方は、是非ご連絡いただけると幸いです。
共同研究などで対応できるかもしれません。
shinmura_0@yahoo.co.jp

#まとめ

+ 単眼カメラを使っても、手作業（組立工程）の検査が可能であることが分かった。
+ 課題もあるが、解決可能であると思われる。
+ 三菱電機のアイデアは素晴らしい。