3duilabのアイコン画像

非接触空間センサーとobnizで作る"忍びの地図"

3duilab 2021年05月11日に作成  (2021年05月11日に更新)

非接触空間センサーとobnizで作る"忍びの地図"

映画ハリーポッターに足跡が歩く"忍びの地図(Marauder's Map)"という魔法の地図が出てきます。これを※非接触空間センサー(https://interactive-hand-sensor.com/root/)
で作りました。やってみると、とても楽しいです😄これは試作ですがIoTで複数連動させるとセキュリティやテーマパーク等で使える斬新なシステムができます。
※赤外線反射光(フォトリフレクタ)を利用した簡易距離センサーです。約20cmの距離を高速ピンポイント検出(8個1ミリ秒)できます。

デモ動画

ここに動画が表示されます

システム

非接触空間センサーの3次元グラフアニメーション

足の動きをイメージしやすいようz軸を反転しています。ご覧のようにシステムは足の位置しか分からないので、左右と向きはタイミング、位置、動きから判断します。

ここに動画が表示されます

デバイス

  • センサー

    • 簀子:床に置くセンサーを保護します。
    • センサー:合計16個、10cmの間隔で配置します。センサーは0.1秒毎に距離データを取得します。
  • コントローラ(STM32F303K8)

    • シリアル通信:z方向約5センチ以下で足の位置(xy方向)を検出しUARTでobnizに送信します。2個以上反応した時は平均値とします。
  • obniz

    • 通路表示:最初に正方形を描き、足跡表示直前に中央の道を消して"表示のゴミ"が残らないようにしています。
    • 足跡表示:左右の足跡画像をobnizのリポジトリに保存し、UARTで受信した足の位置に表示します。

配線図

実験装置

キャプションを入力できます

プログラム

obniz

<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" /> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://unpkg.com/obniz@3.x/obniz.js" crossorigin="anonymous" ></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jcanvas/21.0.1/min/jcanvas.min.js"></script><! *** jcanvas ***> </head> <body> <!div id="obniz-debug"></div> <canvas width="900" height="850"></canvas> <script> let LINE = 400, DELTA = 50, SHIFT = 55, dir_inv = false; function clear() { $('canvas').clearCanvas({x: LINE + DELTA, y: LINE + SHIFT, width: (LINE + DELTA) * 2, height: 240});} function exec(text) { let lrIdx = [[-1, -1],[-1, -1]]; //** ary2 : [[lr,idx],[lr,idx]] ** for(let i in text){ let ch = text.charCodeAt(i); if ((ch < 65) || ((ch > 79) && (ch < 97)) || (ch > 111)) continue; lrIdx[i][0] = (ch >= 97) ? 1 : 0; // lr L:0, R:1 lrIdx[i][1] = ch - ((ch >= 97) ? 97 : 65); // 97:'a', 65:'A' } return lrIdx; } function getFtAng(isLeft, dirInv) { // [footprint, angle] let ftR = 'https://obniz.com/ja/users/4978/repo/fpL.png'; // foot left let ftL = 'https://obniz.com/ja/users/4978/repo/fpR.png'; // foot right return [((isLeft ^ dirInv) ? ftL : ftR), (dirInv ? 180 : 0)]; } function show(ary, dirInv) { // changed:true let LR_INV = false, sca = 0.92, chg = false; clear(); for (i=0;i<2;i++) { if (ary[i][0]>-1) { let isLeft = (ary[i][0] == (LR_INV ? 1 : 0));// left: true, right: false let ftAng = getFtAng(isLeft, dirInv); // foot angle 0 / 180 degree let pm = isLeft ? -1 : 1; // plus / minus $('canvas').drawImage({source: ftAng[0], x: LINE * 2 - DELTA * (ary[i][1]), y: LINE + SHIFT + pm * DELTA, rotate: ftAng[1] - 90, scale: sca}); chg = true; } } return chg; } $('canvas').css('background-color','#E2AE59'); $('canvas').drawText({fillStyle: '#461B7E', strokeStyle:'#E5E4E2',strokeWidth: 1, x: 450, y: 25, fontSize: 48, fontStyle: 'bold', text: '👣忍びの地図👣'}); $('canvas').drawRect({fillStyle: '#7A5E30', x: LINE + DELTA, y: LINE + SHIFT + DELTA, width: (LINE ) * 2, height: (LINE + DELTA) * 2,}); clear(); // ary2 = exec("oA"); // show(ary2); // ********************************************************** let wrtTime = 0, clrCnt = 0; // clrCnt: clear count let obniz = new Obniz("OBNIZ_ID_HERE");:sweat_smile: obniz.onconnect = async function() { // *** called on onLINE *** obniz.display.print("Marauder's Map"); let uart = obniz.getFreeUart(); uart.start({ tx: 9, rx: 10, gnd:8, baud:57600}); uart.send("Xyz\n"); uart.onreceive = function(data, text){ // 2 charcters like "Ac" // console.log(text, data); ary2 = exec(text); if ((clrCnt > 4) && (ary2[0][1] > -1)) { dir_inv = (ary2[0][1] > 3); } if (show(ary2, dir_inv)) { clrCnt = 0; wrtTime = new Date(); } } obniz.onloop = async function() { // delete left footprints // $('h1').text("**" + clrCnt.toString() + "**"); if (wrtTime == 0) { // not written clrCnt++; return; } nowTime = new Date(); if (nowTime - wrtTime > 100) { // ms clear(); wrtTime = 0; } }; }; // *** called on onLINE *** // called on offLINE obniz.onclose = async function() { uart.end(); }; </script> </body> </html>

STM32F303K8(mbed)

// *** IHS ver1.1a *** // *** Setting in Sensor.h: every/normal, single-board/cube/cube-dual(row-length) *** #include "mbed.h" #include "CLED.h" // **************** global valiables ************************* const float INTV_SEC = 0.1; //0.1 extern const int COL_LEN, ROW_LEN; // const int MAX_CNT = 400; // **************** global functions ************************* void tick_ISR(); // **************** global objects *************************** Serial ua(PA_9, PA_10); Ticker tick; Sensor sensor(-70); // The smaller the value, the higher the height CLED cled(11); // IHSxx // ********************** main ******************************* int main() { ua.baud(57600); ua.printf("Hello\n"); tick.attach(&tick_ISR, INTV_SEC); // ********************** get initial value ****************** wait(1); sensor.set_adAryInit(); // set initial value for (int i=0; i<100; i++)// 100:cled error measurement cled.set(10); wait(1); for (int i=0; i<100; i++)// 100:cled error measurement cled.set((byte)0); wait(0.2); // *************************** loop ************************** while(1) { } } // ************************ tick_ISR ************************* void tick_ISR() { const char conv[8] = {5,7,6,4,1,3,2,0}; // 1 3 5 7 // 0 2 4 6 const int CRI = 3000; uint16_t ad[2][8]; // ad[lr][col] char tx[8]; for (int r = 0; r < ROW_LEN; r++) { sensor.setAd(r); // get AD value for (int c = 0; c < COL_LEN; c++) { int lr = (c % 2) ? 1 : 0; int i = r * 4 + c / 2; ad[lr][i] = sensor.getColAd(conv[c]); } } int txc = 0; // tx count for (int r=0; r < ROW_LEN; r++) { // ** row 0,1 ** int ovc[8] = {0,0,0,0}; // over criteria int cnt = 0; for (int c = 0; c < COL_LEN; c++) { // check over criteria to col[0-7] if (ad[r][c] > CRI) ovc[cnt++] = c; } for (int k=0; k<cnt; k++) { char ch = ((r==1) ? 'a' : 'A') + ovc[k] * 2; int chc = 0; // check continuous if (k < cnt - 1) { // check continuous 1 or 2 if (ovc[k + 1] == ovc[k] + 1) { chc = 1; if (k < cnt - 2) { if (ovc[k + 2] == ovc[k] + 2) { chc = 2; } } } } tx[txc++] = ch + chc; k += chc; } } // ** row 0,1 ** if (txc > 0) { tx[txc] = '\0'; ua.printf(tx); } }

STM32F303K8(mbed)のライブラリはこちら(https://os.mbed.com/users/maro/code/Nucleo_IHS11a/)

回路図

双方向ハンドセンサー:本体基板
センサー基板のカラーLEDドライブとタイミング回路が中心です。
SPIでカラーLED制御とフォトトランジスタの出力読み取りをしています。
ユーザーマニュアルはこちら(https://interactive-hand-sensor.com/root/user-manual)
双方向ハンドセンサー:センサー基板
双方向ハンドセンサー:基板選択

感想

非接触空間センサーは新しいデバイスでこれを使うと魔法のようなシステムが出来ますが、obnizはさらに背中を押してくれるよう気がします。IoTのシステムは今回初めてでしたがデータ保存や複数連携など拡張の余地がありまた使ってみようと思います。日本の電子機器産業が残念なのはイノベーションのジレンマで新しいものが作れないから、なのでこれからも攻めた😅感じの斬新なシステムを創って行きたいと思います。
ご覧いただきありがとうございました。

3
3duilabのアイコン画像
赤外線フォトリフレクタを利用した次世代の非接触空間センサー「双方向ハンドセンサー」を開発しています。電子回路と組込みソフトウェアのエンジニアです。事故で指先を失いました(冬山で凍傷になって)😁 動画まとめ https://imgur.com/user/3duilab/posts  website https://interactive-hand-sensor.com/root/
  • 3duilab さんが 2021/05/11 に 編集 をしました。 (メッセージ: 初版)
  • Opening
    3duilabのアイコン画像 3duilab 2021/05/31

    obniz IoTコンテスト電子工作部門 最優秀賞ありがとうございます。今回は応募が多く最優秀賞は無理だと思ってたのでとても嬉しいです。

    独創性や創造力を発揮してオリジナルな製品で勝負すれば日本の電子機器産業もまた世界一になれると思います。これからもユニークな開発を続けます。

    0 件の返信が折りたたまれています
ログインしてコメントを投稿する