覚えた技術を全部使って顔認証スマートロックを作る！#Teachable Machine #JavaScript

## 日常生活での課題
私が日常生活を送るうえで困っている事があります。
それは **家の鍵を会社に忘れてきがち** という事です。これまで10回以上は忘れてきているし、終電に間に合わずホテルに宿泊したこともあるほどです・・・

そんな私が欲しいモノとして **『スマートロック』** があります。
ただし不注意を治せば良い話と思い、これまで購入するに至っておりませんでした。

そんな中、機械学習による画像認識の技術について学んでいる際に、 
**「あれ？画像認識とこれまでの技術を組み合わせればスマートロック作れるのでは？」** 
と思った事が、今回のモノづくりのきっかけとなります。

という事で今回は、下記の実装目標で自作でスマートロックを作成してみます！

:::note info
**【実装目標】
・ 顔認証によるスマートロックを作成する
・ これまでに学んだ技術を全て組み合わせてみる
・ 実用性を意識したモノを作成する**
:::

## 完成品！自作スマートロック『スマドア』のご紹介！！
まずは作成したスマートロック『スマドア』の紹介動画をご覧ください！
工作部分にまだ課題がありますが、スマートロックの元となる仕組みの実装はできました！


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これまでのプロトアウトで学んだ技術を全部使って、自作スマートロック『スマドア』を作成！<br>やれる事が増えてきた気がする！何作ろうを考えるのが楽しい！！<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/NodeRed?src=hash&amp;ref_src=twsrc%5Etfw">#NodeRed</a> <a href="https://twitter.com/hashtag/LINEBOT?src=hash&amp;ref_src=twsrc%5Etfw">#LINEBOT</a> <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> <a href="https://twitter.com/hashtag/javascript?src=hash&amp;ref_src=twsrc%5Etfw">#javascript</a> <a href="https://twitter.com/hashtag/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92?src=hash&amp;ref_src=twsrc%5Etfw">#機械学習</a> <a href="https://t.co/CeexFiwZam">pic.twitter.com/CeexFiwZam</a></p>&mdash; oooookawa (@ooooooooookawa) <a href="https://twitter.com/ooooooooookawa/status/1728391488714018961?ref_src=twsrc%5Etfw">November 25, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 環境準備
今回使用した環境は以下の通りです。
【環境】
・Node-Red（Codespaces）
・LINE BOT（Messaging API）
・CodePen（HTML/CSS/JavaScript）
・Teachable Machine

【ライブラリ】
・ml5.js

【モノ】
・obniz Board 1Y
・サーボモータ
・ジャンパーワイヤ（3本）
・100均グッズ（割りばし/木材/ガムテープ）

## 実装内容
### 構成イメージ図
作成した『スマドア』の構成イメージ図は以下の通りです。
一般的なスマートロックはスマホからの操作のみですが、今回作成したスマドアは、**画像認識による顔認証** と **スマホ紛失時の不正利用対策として開錠時にLINEでメッセージを送信機能を付けることで**、セキュリティ面での差別化を意識しております。

![スクリーンショット 2023-11-26 003502.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/4f849508-167e-eec8-1d1c-9f426be2f598.png)

### 実装内容の詳細
以降は実装の詳細となります。
長くなるため必要に応じて展開してご確認ください！

<details><summary>①Teachable Machineで画像認識のデータを作成</summary><div>

まずは[Teachable Machine](https://teachablemachine.withgoogle.com/train)の新しいプロジェクトから、「画像プロジェクト」を選択します。

![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/d75bbb54-1e4d-804c-4ebc-579298e523af.jpeg)

自分自身の写真と、その他の人の写真をアップロートして「トレーニング」させて、モデルを作成します。

![スクリーンショット 2023-11-26 010734.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/0d4a7f7e-6c4e-c739-5c70-ee6d51d2a7e0.png)

モデルをアップロードして、生成されたモデルのURLを取得します。

![スクリーンショット 2023-11-26 011331.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/07800b58-61f2-dddb-8e2a-202e25a9d9ea.png)

</div></details>


<details><summary>②CodePenでWebアプリケーションを作成</summary><div>

CodePenのソースは以下の通りとなります。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="abXKNVm" data-user="oooookawa" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/oooookawa/pen/abXKNVm">
  顔認証開錠施錠アプリ</a> by oooookawa (<a href="https://codepen.io/oooookawa">@oooookawa</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

**JavaScriptの以下2点は利用環境に合わせて記載を直してご使用ください。**
1.｛画像プロジェクトのモデルURL｝
2. {https://Node-RedのURL/receiver}

```JavaScript:script.js
// 作成したモデルのURL
const imageModelURL = '｛画像プロジェクトのモデルURL｝';

// メインの関数（ここでは定義しているだけでボタンクリックされたら実行）
async function start() {
  // カメラからの映像ストリーム取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true,
  });
  
  // 「id="webcam"」となっているパーツ（videoタグ）を取得
  const video = document.getElementById('webcam');
  
  // videoにカメラ映像ストリームをセット
  video.srcObject = stream;
  
  // Googleのサーバーにアップロードした自作モデルを読み込みにいきます
  classifier = ml5.imageClassifier(imageModelURL + 'model.json', video, () => {
  });
  
  // 認証
  function authentication() {
    // 顔認証を実行して結果をresultsに格納する

    classifier.classify(async (err, results) => {
    
    // 結果のresultsは配列ですが、先頭に中身があれば以下の処理を実行します
    if (results[0]) {
      // 自分の顔の画像を取得
      const myFace = results[0].label;
      
      // 判定結果を入れる変数を用意
      let check = '';
      const result = document.getElementById("result");
      // 判定
      if (myFace == '本人') {
      // 本人の場合
        result.style.color = 'green';
        check = '生体認証成功！開錠します！';
      } else {
        // 他人の場合
        result.style.color = 'red';
        check = '生体情報が一致しません';
      } 
      // HTML表示に反映 & NodeRed起動
      result.textContent = check;
      axios.post('{https://Node-RedのURL/receiver}', {check: check});
    }
    // 10秒後に自分の関数を実行(ループになる)
    //setTimeout(loop, 10000);
　　});
  }
  // 最初の繰り返し処理を実行
  authentication();
}
```

■WebアプリケーションURL
お試し版としてNetlifyに作成したWebアプリケーションを公開しております。
こちらでは顔認証の精度は低くしており、ご自身の顔とマスク付きの顔（または手で口元を覆うなど）で判別可能です。

https://sweet-bublanina-6f5cd4.netlify.app/

※QRコードを読み取ればスマホからも確認可能です！
![スクリーンショット 2023-11-26 020507.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/af4b237e-4d94-1ad2-26e6-1cae3c4bea78.png)


</div></details>

<details><summary>③Node-Redのフローを作成</summary><div>

次にNode-Redのフローを作成します。
Node-Redの画面とJSONは以下の通りとなります。

■Node-Red画面
![スクリーンショット 2023-11-26 114907.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/008f3d18-9b04-663e-da6a-db68dffe6e97.png)



```json:JSON
[{"id":"0edaecfbb3b69736","type":"tab","label":"フロー 1","disabled":false,"info":"","env":[]},{"id":"bafa908fb1c37191","type":"http in","z":"0edaecfbb3b69736","name":"","url":"/receiver","method":"post","upload":false,"swaggerDoc":"","x":320,"y":160,"wires":[["5dbf8177368f7210"]]},{"id":"5dbf8177368f7210","type":"function","z":"0edaecfbb3b69736","name":"function 1","func":"const message = msg.payload.check;\n\nmsg.flag = message\nmsg.url = \"https://api.line.me/v2/bot/message/push\";\nmsg.method = \"POST\";\nvar ACCESS_TOKEN = \"{LINEアクセストークンを記載}\";\nmsg.headers = {\n    \"Authorization\": `Bearer ${ACCESS_TOKEN}`,\n    \"Content-Type\": \"application/json\"\n};\nmsg.payload = {\n    \"to\": \"{LINEユーザーIDを記載}\", //誰に送るか指定\n    \"messages\": [　//メッセージ内容を指定\n        {\n            \"type\": \"text\",\n            \"text\": message\n        }]\n};\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":160,"wires":[["9f843962b5773536"]]},{"id":"d7ea0bdb6b10346a","type":"http request","z":"0edaecfbb3b69736","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":610,"y":240,"wires":[["b2c24ecc108a3df3"]]},{"id":"b2c24ecc108a3df3","type":"obniz-function","z":"0edaecfbb3b69736","obniz":"37ad24e55808fdb7","name":"","code":"let degrees = context.get('degrees') || 90; \r\ncontext.set('degrees', degrees);\r\ndegrees = 150.0;\r\n\r\n// サーボを指定の角度まで動かす（開錠）\r\nobnizParts.servo.angle(degrees);\r\n\r\nawait obniz.wait(30000); // 30秒後に自動施錠\r\n\r\ndegrees = 0.180;\r\n\r\n// サーボを指定の角度まで動かす（施錠）\r\nobnizParts.servo.angle(degrees);\r\n\r\n","x":800,"y":240,"wires":[[]]},{"id":"739193b4f372449d","type":"comment","z":"0edaecfbb3b69736","name":"LINEメッセージ送信 + サーボモータ起動","info":"","x":720,"y":200,"wires":[]},{"id":"fdb942e675949ae6","type":"debug","z":"0edaecfbb3b69736","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":650,"y":80,"wires":[]},{"id":"70379f262dc88076","type":"obniz-function","z":"0edaecfbb3b69736","obniz":"37ad24e55808fdb7","name":"","code":"msg.payload = \"finish\";\nawait obniz.wait(1000); \nobniz.close();\n\nreturn msg;","x":480,"y":80,"wires":[["fdb942e675949ae6"]]},{"id":"16dfbdba13d3daac","type":"inject","z":"0edaecfbb3b69736","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":300,"y":80,"wires":[["70379f262dc88076"]]},{"id":"9f843962b5773536","type":"switch","z":"0edaecfbb3b69736","name":"","property":"flag","propertyType":"msg","rules":[{"t":"eq","v":"生体認証成功！開錠します！","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":370,"y":260,"wires":[["d7ea0bdb6b10346a"],["a94e5b78b5e2212b"]]},{"id":"3e045cf873893e79","type":"PushMessage","z":"0edaecfbb3b69736","name":"","x":800,"y":300,"wires":[]},{"id":"a94e5b78b5e2212b","type":"template","z":"0edaecfbb3b69736","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"顔認証に失敗しました","output":"str","x":600,"y":300,"wires":[["3e045cf873893e79"]]},{"id":"37ad24e55808fdb7","type":"obniz","obnizId":"xxxx-xxxx","deviceType":"obnizboard","name":"","accessToken":"","code":"obniz.display.clear(); // 画面を消去\r\n\r\n//obnizParts.hcsr04 = obniz.wired(\"HC-SR04\",{ gnd:0, echo:1, trigger:2, vcc:3 });\r\n///obnizParts.Speaker = obniz.wired(\"Speaker\",{ signal:6, gnd:7 });\r\nobnizParts.servo = obniz.wired(\"ServoMotor\", { gnd: 9, vcc: 10, signal: 11 });"}]

```

**以下3点は利用環境に合わせてご使用ください。**
【functionノード】
・ ACCESS_TOKEN ⇒ **LINEのアクセストークン**　
・ TO           ⇒ **LINEのユーザーID**
![スクリーンショット 2023-11-26 013723.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/f8d677ab-91ac-a94a-ded8-165a8a482acb.png)


【obnizノード】
・ obnizId ⇒ **利用するobnizのID**

【LINE Pushノード】
・ Secret　        　　 ⇒　**LINEのシークレットキー**
・ AccessToken　        ⇒　**LINEのアクセストークン**
・ User Id or Group ID　⇒ **LINEのユーザーID**

</div></details>

<details><summary>④obnizとパーツの接続・工作</summary><div>

obnizとサーボモータを接続します。

![IMG_0706.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/b3cb611e-02a0-2ae7-6517-73a9ea155ab8.jpeg)

工作はこちらの記事を参考に作成してみました。
[帰宅したら自動で解錠するスマートロックを自作する①～オートロック編～](https://ppdr.softether.net/smartlock-1)

ただしの通りココが甘かったため、玄関の鍵を回す事ができませんでした・・・
**改善にはより工夫し強固な工作が必要です。**

![IMG_0762.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3567275/b4ddf5b6-1eb0-3cda-f0ac-90115f51538e.jpeg)


</div></details>

## あとがき
今回は機械学習という事で学習前までは難しそうだなと思ったのですが、例によって便利なツールが提供されているため簡単に扱う事ができました。**世の中便利なモノに溢れてますねー！！！**

なお今回作成した『スマドア』は工作部分に課題があり、満足のいく成果物ではなかった為、
**今後はよりクオリティを追求してモノづくりに取り組みたいなと思います！！**

最後までご覧頂きましてありがとうございました！