2023-03-13

IoTハッカソンに参加しました

kawakami

IoTハッカソンに参加しました

Web×IoT メイカーズチャレンジ PLUSというハッカソンイベントに参加してきたので、その内容と作った作品について書き残したいと思います。

自己紹介

 フロントエンド・エンジニアをしている川上と申します。会社では、React/Next、Vue/Nuxtで開発を行っております。フロントエンドが盛んになる前は、現在のバックエンド・エンジニアの範囲の開発も行っていたので、ある程度は幅広く開発が行えます。また、最近、弊社はアジャイル開発のスタイルをとっており、フロントエンド/バックエンド関係なく開発をおこなっていたりします(これを真に実現できているところが弊社の良さだったりします) そんな私が、新年のチャレンジとして選んだのが今回のハッカソンでした。

ハッカソン概要

Web×IoT メイカーズチャレンジ PLUSは総務省が主催されているハッカソンになりまして、毎年全国各地で開催されています。総務省が主催されていることもあり、イベントはワークショップから、アイディアソン、ハッカソンと充実した内容なので十分にテクノロジーを楽しめるイベントとなっております。

私が参加した、東京会場の日程は1月9日にワークショップ、1月14日にハンズオン・アイディアソン、2月4日と2月5日にハッカソンという1ヶ月にわたる期間で開催されました

IoTハッカソンに参加しました

ワークショップについて

ワークショップは、IoTやプログラミングに経験の浅い人向けの勉強会という形式で開催されました。内容もハンズオンなので置いていかれることなく進めることが出来ました。ハンズオンでは、今回使用するデバイスRaspberry Pie Zeroと、各種センサー使い方、Raspberry Pi上でJavascriptを使ってセンサーを扱うための開発環境、CHIRIMENの学習も行われました。

アイディアソン

ワークショップから約1週間後の1月14日にアイディアソンが行われました。  アイディアソンは、ハッカソンの作品を決定するために行われるアイディアワークショップです。今回のテーマが「203X 年に役立つであろう IoT デバイス」ということで、アイディアソンでは、世の中を分野に分けて、それぞれの分野で問題になっている点を洗い出し、203X年にそれがどう解決されているかを付箋に書き起こし、最終的に集まった付箋からチームのアイディアへの偏りから作る作品を決定するという流れで進行しました。  チームは事務局がチームを作ってくれます。もし一緒のチームになりたい人がいる場合は、参加申込時にその人の名前を書いていれば、一緒のチームになれます。ちなみに私は一人で参加したため、初めまして同士のチームとなりました。 ハッカソンにおいてチーム作りはとても重要です。チームメンバーと合わないと楽しくないハッカソンになりますし、意見がぶつかることも頻繁に起きます。そういった時に、チームを変えたり、自分は意見を出さないこともひとつの解決策と思いますが、チームメンバーの良い部分を探したり、提案を持った上で意見してみたりコストの高いことにチャレンジしてみることをお勧めします。

何を作ったのか

FIFA ワールドカップ カタール 2022でボールにセンサーが搭載されて、リアルタイムにプレイヤーのキックポイントを正確に検出できるようになり、それによりVARが強化されさまざまな感動が生まれたことは記憶に新しいと思います。  私は203X年には、ボールにカメラが搭載されボール視点の映像を見れたらもっとサッカーを楽しめると考えました。フリーキックで、サッカーボールを蹴られボールが加速し、人の壁をすり抜け、弧を描いた軌道がゴールポストをかすめながら、キーパーの手を横にゴールに吸い込まれる。そんな映像が見れたら誰でも興奮するはず。と思いました。ということでボールにカメラを付けてみることにしました

課題

 しかしボールにカメラをつけれらとしても、ボールは常に回転しているので、とても見れる映像は撮れないです。また、ボールには向きがないので、前や後ろなど進行方向がわからないためカメラをつける位置もわからないという課題がありました。 この課題を解決するのがこのハッカソンのゴールと考えました

解決策

 まず、映像ですがカメラを複数台搭載して360度の映像を撮影することにしました。映像が回転してしまう問題に関しては、ジャイロセンサーでボールの回転を検知して映像を切り替えるようにして、ボールの進行方向は加速度センサー、または地磁気センサー(コンパス)で検知するようにしました。

 カメラの台数は、多ければ多いほど良いです。10°毎に設置したいとすれば36台×36台の1,296台くらいつければ理想なのかなと思いますが、今の人類が持っているテクノロジーでは設置は難しいだろうと思います、しかし203X年なら出来るかもしれないと思います。将来の技術進歩に期待しつつ、今回は90°単位で6台のカメラを設置することにしました。

 進行方向に関しては、加速度センサーを使えば加速度から進行方向がわかるだろうと考え、実際に取得されるデータから割り出そうと頑張りましたが、今の僕の持っているテクノロジーでは扱うのは無理でした。(203X年の僕なら出来るかもしれない)ということで、今回は進行方向を方角で決めようと切り替えて地磁気センサーを使うことにしました(ハッカソンは時間が限られているので、どんどん考えを切り替えていくのが大事だと思います)

制作

 解決策から必要ものは、カメラとジャイロセンサー、加速度センサー、地磁気センサーを用意できれば良いということがわかりました。

MPU-9250とは

センサーには1つの機能だけを持つものだけではなく、複数の機能を1つのセンサーでまかなうものがあり、MPU-9250というセンサーは、ジャイロ、加速度、地磁気、温度、湿度を測れる機能を持つ、まさに今回にぴったりなセンサーです。 MPU-9250とRaspberry Piとの接続も簡単でI2Cで接続可能です。MPU-9250から取得できる情報を扱うサンプルコードはCHIRIMENで用意されています。サンプルコードを見ると、new mpu6500new AK8963を使っていることがわかります。mpu6500はジャイロと加速度を扱うセンサーでAK8963はコンパスを扱うセンサーです。MPU-9250はこの二つのセンサーが入っていることがソースコードからもわかります。 mpu6500.getGyro()でジャイロのデータ、mpu6500.getAcceleration()で加速度のデータ、ak8963.readDataでコンパスのデータを取得可能で、それぞれのデータはx,y,zの3つの値を持っています。

async function main() {
  const i2cAccess = await requestI2CAccess();
  const port = i2cAccess.ports.get(1);
  const mpu6500 = new MPU6500(port, 0x68);
  const ak8963 = new AK8963(port, 0x0c);
  await mpu6500.init();
  await ak8963.init();

  while (true) {
    const g = await mpu6500.getGyro();
    const r = await mpu6500.getAcceleration();
    const h = await ak8963.readData();

    console.log(
      [
        `Gx: ${g.x}, Gy: ${g.y}, Gz: ${g.z}`,
        `Rx: ${r.x}, Ry: ${r.y}, Rz: ${r.z}`,
        `Hx: ${h.x}, Hy: ${h.y}, Hz: ${h.z}`
      ].join("\n")
    );

    await sleep(500);
  }
}

このサンプルコードを以下のコマンドで実行すると、取得できたデータを確認することができます shell $ node ./main-mpu9250.js

MPU-9250で取得したデータからセンサーの傾きを把握する

 制作はジャイロセンサーで取得できる値から調査を始めました。センサーの値はXYZ軸がFloat型で取得できます。それぞれの値は1から、-1までの間の数値で取得でき、1が90°、-1が-90°を表しています。これは、実際にセンサーを傾けたりして、最大値と最小値をみながら確かめました。 90°から-90°だと180度しか計測できないので、残りの180°はZ軸の値に関連させると計測することができます。Z軸が正の場合は、センサーは正転しており、負の場合は、センサーが反転している状態です。これを組み合わせることで360°検知することがでました。

    const roundX = Math.round(gyro.x * 100) / 1000

    let degreeX = Math.floor(roundX * 90)

    if (roundZ < 0.3) {
      if (roundX >= 0) {
        degreeX = (Math.floor(roundX * 90) -180) * -1
      } else {
        degreeX = (Math.floor(roundX * 90) +180) * -1
      }
    }

取得したデータをブラウザに送信する

 取得したデータはブラウザ上で確認したいため、WebSocketを使ってデータを送信します。その際に中継するサーバーが必要なのですが、今回はCHIRIMENで用意してくれる中継サーバーと、relayServer.jsを利用しました。

import nodeWebSocketLib from "websocket";
import {RelayServer} from "./RelayServer.js";

var channel;

async function connect(){
  // webSocketリレーの初期化
  var relay = RelayServer("chirimentest", "chirimenSocket" , nodeWebSocketLib, "https://chirimen.org");
  channel = await relay.subscribe("chirimen");
  console.log("web socketリレーサービスに接続しました");
}

await connect()

main();

async function main() {
  ...

  while (true) {
    ...
    channel.send(data)
  }
}

複数台設置のカメラからアクティブにするカメラを決定する

 次に、どのカメラが進行方向(静止している時に決めている)にあるかを検知することにとりかかりました。X軸回転上には複数台のカメラを設置しているので、360°をカメラの台数で割り、1台のカメラが受け持つ角度を決めました。カメラ4台設置の場合は、90°変化したらカメラを切り替えるようにしました。 下記のコードでは、次の範囲で設定しています。 カメラ1: -45°〜45° カメラ2: 135°〜180°と、-180°〜-135° カメラ5: -45°〜-135° カメラ6: 45°〜135°

    // カメラのActiveを算出する
    if (degreeX >= 45 && degreeX < 135) {
      setActiveCamera(6)
    } else if (degreeX >= 135 || degreeX < -135) {
      setActiveCamera(2)
    } else if (degreeX <= -45 && degreeX > -135 ) {
      setActiveCamera(5)
    } else {
      setActiveCamera(1)
    }

センサーの傾きを視覚的に表現させる

 センサーの情報は数値なので状況が視覚的には分かりづらいです。そこで画面上に3Dオブジェクトを表示させてその動きと、センサーの動きを同期させることにしました。はじめは、3Dオブジェクトなので、WebGLで書こうと思いましたが、CSSでも立方体であれば書くことができたのでCSSで3Dオブジェクトを書くことにしました。 詳細は実際に使ったコードをCodePenに移植しましたので、そちらからご覧ください。 https://codepen.io/angularF/pen/OJozvmY

複数台のカメラ映像を切り替える

 カメラをRaspberryPiで認識させて写真や映像を撮ることは比較的簡単なのですが、映像をストリーミングでPCに送るとなると難易度は上がります。代表的な案としては、WebRTCを使うことです。しかし、WebRTCを1から設定するのは大変なので、Skywayなどのライブラリを使うが良いと思います。今回は、時雨堂さんのOSSであるmomoを使いました。momoのテストモードで起動すると、テストサーバーが立ち上がりそこで映像を確認することができます。  映像の切り替えを考えた時に、複数のカメラ映像を1台のPCで集めることが時間的な理由から実現することができなかったため、momoのテストサーバーで表示されている画面を利用することにしました。つまり、momoのテストサーバーの画面をPC側の画面から、iframeで読み込む形にしました。 映像の切り替えは、カメラの台数分のiframeをCSSで重ねて、重ね順(zindex)を変化させることで、アクティブなiframeが一番上になるようにして実現させました。

{/* CAMERA 01 */}
<iframe src="http://192.168.128.111:8080/html/test.html"
        width="100%" height="480"
        style={active === 1 ? {zIndex: 10} : {zIndex: 1}}
></iframe>
{/* CAMERA 02 */}
<iframe src="http://192.168.128.110:8080/html/test.html"
        width="100%" height="480"
        style={active === 2 ? {zIndex: 10} : {zIndex: 1}}
></iframe>
{/* CAMERA 05 */}
<iframe src="http://192.168.128.116:8080/html/test.html"
        width="100%" height="480"
        style={active === 5 ? {zIndex: 10} : {zIndex: 1}}
></iframe>
{/* CAMERA 06 */}
<iframe src="http://192.168.128.117:8080/html/test.html"
        width="100%" height="480"
        style={active === 6 ? {zIndex: 10} : {zIndex: 1}}
></iframe>

できなかったこと

 制作を進めるなかで、できなかったことも多くありました。 - ジャイロセンサーのX軸回転、Y軸回転は取得される値を紐解いて再現できたのですが、Z軸回転に関しては再現させることができませんでした。 - すべての回転軸が再現できなかったため、カメラを6つ搭載するまでは実現できませんでした。 - 加速度センサー、地磁気センサーにで進行方向を取得したかったのですが、得られる値から進行方向を割り出すまでは出来ませんでした。そのため進行方向をX軸の0°と固定としました。 - カメラ映像については、momoのテストモードで起動していたためか、30秒くらいすると映像が止まってしまい、連続した映像を流すことができませんでした。

結果

 ハッカソンの結果は、参加チームは6チーム、賞は3つ用意されたのですが、どの賞も取ることが出来ませんでした。悔しい思いをしましたが、仕事もある中での3週間で、やれることは全てやれたので走り切れた達成感があります。 賞は取れませんでしたが、終わった後に他のチームメンバーから声をかけてもらえたりして、満足度は高かったです。チームでの反省会のビールは大変美味しかったのは言うまでもありません。  満足ではあるのですが、「なぜ賞を取れなかったか」を分析すると、ネタは誰でも想像しやすくわかりやすかったのですが、その反面インパクトには欠けていたのかなと思っております。確かに審査員から見るとボールにカメラを設置しただけと捉えられても仕方ないと思います。 このようにインパクトが足りない場合は、クオリティが高くないといけないと思うのですが、そのクオリティが出せなかったと思っています。せめて映像が30秒で止まることなく見れて、カメラ6つの映像が切り替わって見れることが実現出来ていたら、このコンセプトの本当の良さを伝えられたのではと思っています。 この悔しさや後悔はハッカソンでしか晴らすことが出来ないと思っているので、また挑戦したいなと思っています。

pic03

最新の記事