映画ハリーポッターに足跡が歩く"忍びの地図(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のシステムは今回初めてでしたがデータ保存や複数連携など拡張の余地がありまた使ってみようと思います。日本の電子機器産業が残念なのはイノベーションのジレンマで新しいものが作れないから、なのでこれからも攻めた感じの斬新なシステムを創って行きたいと思います。
ご覧いただきありがとうございました。
投稿者の人気記事
-
3duilab
さんが
2021/05/11
に
編集
をしました。
(メッセージ: 初版)
Opening
3duilab
2021/05/31 -
3duilab
さんが
2022/01/18
に
編集
をしました。
ログインしてコメントを投稿するobniz IoTコンテスト電子工作部門 最優秀賞ありがとうございます。今回は応募が多く最優秀賞は無理だと思ってたのでとても嬉しいです。
独創性や創造力を発揮してオリジナルな製品で勝負すれば日本の電子機器産業もまた世界一になれると思います。これからもユニークな開発を続けます。