座席の利用状況を可視化するシステムを作った話

弊社では、よく出社する一部の社員を除き、座席を固定していません。
2023年6月のオフィス移転時に、「フリーアドレスな座席の利用状況を見て、使用頻度の低い座席を把握したい」という要望が寄せられました。
そこで、座席にセンサーを設置し、利用状況を集計。最終的に、どの座席がどれくらい使われているかを視覚化する画面を作成しました。
現在、このシステムでは以下の 2 つをヒートマップで表示しています。
- 座席ごとの利用状況
- 全座席の利用頻度

ただし、検証段階のため、センサーが設置されているのは 3 席のみです。
ヒートマップでは、未計測の日付はグレー、使用頻度が高まるにつれて色が濃くなります。
例えば、C-7 は最もよく使われており、C-5 と C-6 も最近頻繁に利用されるようになってきました。
もっとセンサーが増えてデータが充実すれば、「季節や気温による座席使用状況の違い」が見えるようになるかもしれません。そうすると、寒すぎる席や暑すぎる席への対策も可能になります。
以下は全デバイスのところを拡大した画像です。

01 AWSを活用した構成
このシステムは、AWS上で構築されています。全体の構成は以下の通りです。

- データ収集
センサーからの計測データを AWS IoT Core で受信し、加工した後に DynamoDB へ保存します。 - データの集計
DynamoDB に保存されたデータは、1日1度、Lambda 関数によって集計されます。その結果は JSON 形式のファイルとして S3 に保存され、フロントエンドで利用されます。 - フロントエンド
ヒートマップを表示する画面は、S3 に保存された JSON データを利用しています。
このようなセンサーからのデータを集計して視覚化する場合、様々な実現方法がありますが、今回は社内での検証ということもあり「コストをかけず、できるだけ安く!」という方針で構成を決定しました。
データの集計処理は以下の2種類を実行しています。
- 1 日単位でファイルを作成し、その日のデバイスごとの集計値を保存する
- デバイス単位でファイルを作成し、1 ヶ月分の集計値を保存する
これらを一つの Lambda 関数でまとめる方法もありましたが、柔軟性を考慮し、それぞれの集計処理を別の Lambda 関数に担当させました。これにより、将来的にどちらか一方が不要になったり修正が必要になった場合でも、簡単に対処できる構成になっています。
1 の集計処理が終了したら 2 の集計処理を開始するよう、Step Functions でステートマシンを設定しています。
また、これらの処理が毎日決まった時間に実行されるよう、EventBridge にルールを作成しました。
このルールにより、毎日指定した時間に Step Functions が起動し、設定された Lambda 関数が処理を実行。最終的に S3 へ集計ファイルが保存される仕組みになっています。
AWS のリソース設定は、AWS Management Console で手動設定することも可能ですが、今回は AWS CDK を使用しました。これにより、設定内容をコードで管理でき、サービス構築から時間が経っても容易に内容を振り返ることが可能です。CDK を使ったおかげで、こうしてブログを書く際にも思い出しやすくなっています。
02 センサー部分
最終的に全座席へのセンサー設置を目指し、コストを抑えつつ、AWS IoT Core を試す目的で連携部分を自作できる「赤外線センサー」と「Raspberry Pi Pico W」を採用。
以前にも、Raspberry Pi を利用した DIY センシング行ったことがあったので慣れているつもりでしたが、Pico W は従来の Raspberry Pi とは仕様が異なり、購入後に事前調査の不足を痛感しました。それでも、MicroPython を使用すれば問題を解決できると判断し、以下の処理を実装。
- WiFi に接続
- センサーの値を ADC(アナログ-デジタル変換)で読み取り
- 一定の閾値を超える変化があれば、MQTTClient を利用して IoT Core にデータを送信
- 1 秒スリープ後、再び計測
IoT Core へデータ送信するには、デバイスに証明書をインストールする必要があります。この手順は AWS 公式チュートリアルに従い、ポリシー設定や証明書の作成を進めました。
この証明書をRaspberry Pi Pico に書き込み、ファイルに普通にアクセスできる形で保存してあります。
例えば、/certs/private.der
として参照し、接続時にオプションで指定することで、IoT Core にスムーズにメッセージを送信できるようにしました。

03 IoT Core設定
IoT Core の「メッセージのルーティング」機能を利用し、センサーから取得した値を DynamoDB に保存する仕組みを構築しました。これを実現するために、まずどのような形式でデータを保持するのが良いかを検討し、DynamoDBのスキーマを以下のように決めました。
- パーティションキー: センサーを特定する情報(部屋/デバイス番号)
- ソートキー: タイムスタンプ
今回は、集計処理が「センサーごと」で、単位は「1日分」です。そのため、集計処理では「センサー」をキーに検索し、タイムスタンプで絞り込む形が適していると判断しました。
IoT Coreのルーティングルールでは、以下の項目を設定しました。
- どの DynamoDB テーブルに保存するか
- パーティションキーにどのデータを入れるか
- ソートキーにどのデータを入れるか
- センサーから取得したデータを入れる列名

これらの設定に基づき、ルールクエリでフィルタリングされたセンサーのデータが、DynamoDB の value カラムに自動的に保存されるようになりました。
04 集計
センサーのデータは、DynamoDB に保存された後、毎日決まった時間に集計処理が行われます。
この処理は Step Functions を利用して 2 つの Lambda 関数で実行され、結果は S3 に保存されます。
- 1 日単位の集計
- その日のデバイスごとの利用状況を集計し、日付をファイル名として保存。
- 保存されるデータ形式は、
{device_id: string, time: long}
- デバイス単位の集計
- 1ヶ月分のデバイスごとの利用状況を集計し、デバイス名をファイル名として保存。フロントエンドのヒートマップでは、このデータを基に利用状況を可視化しています。
- 保存されるデータ形式は、
{date: string, time: long}
1 つ目の Lambda で保存される日単位のデータは、現時点では使用されていませんが、他の表示形式で活用できる可能性があります。
今後も利用の見込みがない場合は、この処理を変更または停止する予定です。
Lambda関数を分割しているため、不要な処理の停止や変更が柔軟に対応できる構成になっています。
05 フロントエンド
フロントエンドは React で作成し、S3 でホスティングしています(構成図には含まれていません)。
S3 に保存された集計ファイルを取得し、計測開始から現在までのデータを表示する仕組みになっています。
ヒートマップ表示には、react-calendar-heatmap
を採用しました。
開発当時「使ってみた」系の記事をよく見かけたというのが採用理由の一つですが、普段バックエンドばかりでフロントエンドは滅多に触る機会がない私でも容易に導入できました。
現在、計測期間が長くなってきたため、ヒートマップで表示する期間を指定する機能を追加する必要が出てきています。
06 終わりに
普段から AWS を活用してシステムを構築していますが、まだ触れていないサービスも多くあります。
今回は、社内で生まれた課題に対して IoT Core を起点に EventBridge や Step Functions を組み合わせたシステムを構築することで、課題解決とともに新たな知見を得ることができました。
特に、デバイスから取得したセンシングデータを弊社の主戦場である AWS に統合するノウハウを得たことで、IoT 関連のシステム開発においても守備範囲を広げられたと感じています。
もし、以下のようなお悩みがあれば、ぜひご相談ください。
- こんなデータを取得して可視化できないか?
- センサーデータを活用しきれずに困っている
- そもそも何ができるの?
具体的な実現イメージがある場合はもちろん、初期段階のご相談からでもお力になります。
私たちは、お客様の課題を解決し、新たな可能性を見つけるお手伝いをさせていただきます。
お気軽にお問い合わせください。

S2ファクトリー株式会社
様々な分野のスペシャリストが集まり、Webサイトやスマートフォンアプリの企画・設計から制作、システム開発、インフラ構築・運用などの業務を行っているウェブ制作会社です。
実績
案件のご依頼、ご相談、その他ご質問はこちらからお問い合わせください。