🧼 AIで判定し清掃した所をチェック!? Teachable Machine × Webカメラで衛生管理ツールを作ってみた

# 🧼 AIで判定し清掃した所をチェック!? Teachable Machine × Webカメラで衛生管理ツールを作ってみた

こんにちは！  
スーパーで副店長をしている**とうすけ**と申します。  
今回でQiita投稿は**4回目**になります。

---

## 🍱 衛生管理も副店長の大事な仕事

私の職場では、副店長が**衛生管理の責任者**を務めています。  
そのため、毎日「野菜」「肉」「魚」「惣菜」などの**生鮮部門の作業場を巡回し、清掃状況をチェックする**のが日課。

でも、こう思ったんです。

> これ、AIで自動化できたら楽じゃない？

そこで今回は、**Teachable Machine**を使って「**清掃の合否判定ツール**」を作ってみました！

---

## 🛠 どんなツールなの？

完成したのがこちら👇  

https://x.com/ryo_i79094/status/1929692101744418910

スマホのカメラで清掃対象を映すと、

- **合格**（きれい）  
- **不合格**（まだ汚れてる）

を自動で判定してくれます。  
不合格だったら掃除をやり直し、**合格後にチェックリストに記録する仕組み**です。

---

## 🧪 使用した技術

- [Teachable Machine](https://teachablemachine.withgoogle.com/)
- [CodePen](https://codepen.io/)

---

## 🔧 作成手順まとめ

### ステップ①：Teachable Machineで清掃状態を学習させる



1. [Teachable Machine](https://teachablemachine.withgoogle.com/)にアクセス  
2. 「Image Project」を選択  
3. 判定対象（例：電子レンジ・壁・換気扇・冷蔵庫）の  
   - **合格（清掃済）**  
   - **不合格（汚れてる）**  
   の写真をそれぞれ撮影・アップロード  
4. モデルをトレーニング  
5. 「エクスポート」→「アップロード」→「JavaScriptコード」をコピー  


<details>
<summary>スクリーンショットを表示</summary>


<p style="display: flex; gap: 10px;">
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049380/226e385e-ccfe-43af-b019-7c1942159011.png" style="height: 300px;" />
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049380/8da3dac6-db94-4270-a66f-37d4f99b2f34.png" style="height: 300px;" />
</p>

</details>

---

### ステップ②：Webカメラと連動するWebアプリを作る

1. [CodePen](https://codepen.io/)を開く  
2. HTMLにコピーしたコードを貼り付ける  
3. 「Start」ボタンでカメラが起動するようになります

<details>
<summary>スクリーンショットを表示</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049380/a95a4f70-db29-4a2d-a310-094075f77078.png)

</details>

---


### ステップ③：スマホで**アウトカメラ**が使えるようにする

通常は**インカメラ**が起動してしまうため、  
コード内の下記の部分を：

```js
await webcam.setup();
```

次のように変更します👇
```js
await webcam.setup({ facingMode: "environment" });
```

これで、**スマホのアウトカメラが起動する**ようになります！

---

### ステップ④：清掃チェックリストを追加

清掃結果を記録できるように、**チェックリスト機能**を追加します。

まず、HTMLに以下のコードを追加します。  
貼り付け位置は次のようなコードのすぐ下です👇

```html
<div>Teachable Machine Image Model</div>
<button type="button" onclick="init()">Start</button>
<div id="webcam-container"></div>
<div id="label-container"></div>
```

👇このコードをその下に貼り付けてください：

```html
<h3>✅ チェックリスト</h3>
<form id="checklist-form">
  <label><input type="checkbox" name="item" value="電子レンジ"> 電子レンジ</label><br>
  <label><input type="checkbox" name="item" value="壁"> 壁</label><br>
  <label><input type="checkbox" name="item" value="換気扇"> 換気扇</label><br>
  <label><input type="checkbox" name="item" value="冷蔵庫"> 冷蔵庫</label><br>
  <button type="button" onclick="submitChecklist()">チェック完了</button>
</form>

<div id="checklist-result" style="margin-top:1em;"></div>
```

続いて、JavaScriptに以下のコードを追加してください👇

```html
function submitChecklist() {
  const checkedItems = [];
  const checkboxes = document.querySelectorAll('input[name="item"]:checked');

  checkboxes.forEach((cb) => checkedItems.push(cb.value));

  const timestamp = new Date().toLocaleString();
  const resultDiv = document.getElementById('checklist-result');

  if (checkedItems.length === 0) {
    resultDiv.innerHTML = `<p style="color:red;">何もチェックされていません</p>`;
    return;
  }

  resultDiv.innerHTML = `
    <p>🧹 チェック完了！</p>
    <ul>
      ${checkedItems.map(item => `<li>${item}</li>`).join('')}
    </ul>
    <p><small>${timestamp} に記録されました</small></p>
  `;
}
```

これで、チェックリストに完了項目が表示され、
実行日時も記録として画面に残るようになります。


<details>
<summary>スクリーンショットを表示</summary>

![Screenshot_2025-05-31-16-17-38-966_com.android.chrome[1].jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049380/3c7453cf-10a2-4859-83c3-95bbc7d81baa.jpeg)

</details>
---

## 🚧 本当はスプレッドシートに記録したかった…

本当は、チェック結果を**Googleスプレッドシートに自動で記録したい**と考えていました。  
ですが、ここで大きな壁にぶつかります。

CodePenでアプリを実行していたところ、こんなエラーが表示されました👇

![エラー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049380/d66d103b-7403-4180-83f4-8e170da6b873.png)

ChatGPTさんに確認したところ、以下のような仕組みになっているとのこと。

> CodePen（`https://cdpn.io`）から、Google Apps Script（`https://script.google.com/...`）にデータを送信しようとした  
> ↓  
> ブラウザが「安全じゃない通信」と判断してブロック  
> ↓  
> 「Failed to load resource」という赤いエラーが出る

つまり、**CodePenから直接GASにデータ送信するのは難しい**ようです。

本当にそうなのでしょうか？
何かやりようはある気がします…

解決策としては、  
**「最初からGASでWebアプリを作る」** という方法があるそうなので、現在そちらも含めて並行して検討中です。

---
## 👨‍👩‍👧 家庭で試したら妻が激怒！？試作のリアルな感想

今回、現場の写真で学習モデルを作る前に、まずは**試作テスト**として自宅のもので作成してみました。  
そこで、実際に**妻に使ってもらうことに**しました。

- **妻**「掃除したのに“不合格”って出た。納得いかない」  
　「逆に、汚れてるのに“合格”になることもあるよ」  
　「記録が残せなきゃ意味ないんじゃない？」  
　「そもそも掃除に合否をつけられるなんて嫌！」

…という感じで、実は最初から機嫌が悪かったんです。  
というのも、**私が事前に"汚れていた場所"を撮影していたこと**がバレてしまって…。

でも正直なところ、**汚れている場所を探すのは本当に大変でした。**  
それだけ、日ごろからしっかり掃除が行き届いているということなんですよね。  
私は掃除をほとんどしていないので…妻には本当に感謝です 🙏

---

### 📌 反省点

- 学習データ（特に**不合格の写真**）が圧倒的に不足していた  
- **汚れている状態の写真を撮るのが難しい**
  - → 掃除してしまうから撮影タイミングが限られる  
  - → 衛生的にも、汚れを放置できない

---

### 💬 最後に

今回の取り組みは、**現場で本格的に使うには課題が山積み**でした。  
ただ、**実際に使ってもらって見えたことがあった**という点では、大きな一歩だったと思います。

今後は、

- ✅ スプレッドシートへの記録機能の実装  
- ✅ 判定モデルの精度向上

を目指して、さらにアップデートしていく予定です！

「ここはこうしたら？」というアドバイスやアイデアがあれば、  
ぜひコメントで教えていただけるとうれしいです😊



### 🔧追記

記事を投稿した後、いろんな方の記事をみたら、やっぱり連携することができそうです。
そこで、改めてもう一度ChatGPTさんに聞きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4049380/f8035ca4-dc09-42f7-be39-d8deb233b966.png)


すると、あっさり完成することができました。

<details>
<summary>作成方法はこちら</summary>


## ✅ ステップ①：スプレッドシートの準備

1. Googleドライブで「新規」→「Googleスプレッドシート」を作成  
2. スプレッドシートの名前をわかりやすく「清掃チェック記録」などに変更  
3. 1行目に以下のように入力します（見出し）

| チェック項目 | 日時 |
|--------------|------|

※「シート1」という名前のままでOKです

4. スプレッドシートのURLにある ID をコピーしておきます  
　例：https://docs.google.com/spreadsheets/d/**ここがID**/edit#gid=0


---

## ✅ ステップ②：Google Apps Script の設定

1. スプレッドシート画面で  
　上部メニュー「拡張機能」→「Apps Script」をクリック

2. 以下のコードを**まるごとコピペ**してください👇

```javascript
function doPost(e) {
  const sheet = SpreadsheetApp.openById("★あなたのスプレッドシートID★").getSheetByName("シート1");
  const data = JSON.parse(e.postData.contents);

  sheet.appendRow([
    data.checkedItems || '',
    new Date()
  ]);

  return ContentService.createTextOutput("記録しました！");
}
```

📝 ポイント

- ★あなたのスプレッドシートID★ の部分を、ステップ①でコピーした スプレッドシートID に書き換えてください

- getSheetByName("シート1") の部分は、シート名が違う場合はそれに合わせてください（通常そのままでOK）

---

## ✅ ステップ③：ウェブアプリとしてデプロイ

1. スクリプト画面の右上「デプロイ」→「新しいデプロイ」をクリック  
2. 「種類の選択」→「ウェブアプリ」を選択  
3. 下記のように設定します：

- **説明**：空欄でもOK  
- **次のユーザーとして実行**：自分（自分のGoogleアカウント名）  
- **アクセスできるユーザー**：「全員（匿名ユーザーを含む）」

4. 「デプロイ」を押すとURLが表示されるので、これをコピーしておきます

> 🔗 このURLが、アプリからスプレッドシートにデータを送る「送り先（API）」になります

---

## ✅ ステップ④：JavaScriptに送信関数を追加

CodePenにある JavaScript に、以下の関数を**追加**してください👇

```javascript
function sendToSheet(checkedItems) {
  const url = "★ここにGASでデプロイしたURLを貼り付けてください★";

  fetch(url, {
    method: "POST",
    mode: "no-cors",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      checkedItems: checkedItems
    }),
  })
  .then(() => {
    console.log("✅ スプレッドシートに送信しました！");
  })
  .catch((error) => {
    console.error("❌ エラーが発生しました:", error);
  });
}
```
⚠️ const url = "..." の部分は、ステップ③でコピーしたGASのURLを貼ってください

---

## ✅ ステップ⑤：チェックリスト送信処理に組み込む

以下のコードを、JavaScript にそのまま貼り付けてください👇  
`submitChecklist()` 関数の中に、スプレッドシート送信処理を追加しています。

```javascript
function submitChecklist() {
  const checkedItems = [];
  const checkboxes = document.querySelectorAll('input[name="item"]:checked');

  checkboxes.forEach((cb) => checkedItems.push(cb.value));

  // 個別送信（もともとの処理）
  checkedItems.forEach((item) => {
    sendCheckItem(item);
  });

  const timestamp = new Date().toLocaleString();
  const resultDiv = document.getElementById('checklist-result');

  if (checkedItems.length === 0) {
    resultDiv.innerHTML = `<p style="color:red;">何もチェックされていません</p>`;
    return;
  }

  resultDiv.innerHTML = `
    <p>🧹 チェック完了！</p>
    <ul>
      ${checkedItems.map(item => `<li>${item}</li>`).join('')}
    </ul>
    <p><small>${timestamp} に記録されました</small></p>
  `;

  // ✅ チェック項目をまとめてスプレッドシートに送信！
  sendToSheet(checkedItems.join(', '));
}
```
⚠️ この関数が正しく動作するためには、sendToSheet() 関数が別で定義されている必要があります（ステップ④参照）

</details>

---

## ✅ 完成品はこちら！

https://x.com/ryo_i79094/status/1931525144109093272

1. Teachable Machineで合否判定  
2. 合格になったらチェックを入れる  
3. チェック完了ボタンを押すと → 完了日時がスプレッドシートに記録！


---

## ✍️ おわりに

これでただの"キレイ汚いを判定するだけのマシーン"から"清掃チェックリストマシーン"に変わりました。
これならば妻も納得してくれるかもしれません。

<details>
<summary>…</summary>

でも、また機嫌が悪くなると面倒なんで使ってもらうのはやめておきます。

</details>
---

ここまで読んでいただき、ありがとうございました！