非接触空間センサー双方向ハンドセンサー(https://interactive-hand-sensor.com/root/)を使って人の動線を検出する-1です。
ドアの角度を検出できるセンサーで細かい動きを観測します。今部屋から出ようとしているとか一瞬ドアが開いてすぐ閉まったとかをウェブブラウザから遠隔操作で検出できます。
デモ動画
システム概要
- センサーコントローラ:ドアの位置をUARTでobnizに送信する
- obniz:UART受信の値からアニメのドア位置を描画する
- obniz:サーボボタンクリックでサーボを動作してドアを閉める
センサーの詳細はユーザーマニュアル(https://interactive-hand-sensor.com/root/user-manual)を参照して下さい。
接続図
システム詳細
ドア位置検出
- 写真のようにセンサーをケーブルで延長してドア蝶番周囲に両面テープで貼り付けます。
- センサー上にドアがある場合はその位置が現在位置です。
- センサー間にドアがある場合は前後のセンサーが反応するのでその大きい方の位置が現在位置です。
- ドアが開き、センサーを外れてしまうと外れた位置が現在位置です。
ドア位置アニメーション
- 前回のドア描画を背景で上書きし消します。
- 現在のドアを描画します。
- ドア周囲の壁などを描写します。
サーボモータの描画も同様です。
サーボモータ
- サーボモータのPWMは常に出力されています。
- ボタンクリックでPWMをドア角度相当のdutyに変更します。
- サーボモータはアクリル板に両面テープで接着しL字アングルで補強しています。
ソースコード
obniz_app.html
<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://cdnjs.cloudflare.com/ajax/libs/jcanvas/21.0.1/min/jcanvas.min.js"></script> <! *** jcanvas ***>
<script src="https://unpkg.com/obniz@3.x/obniz.js" crossorigin="anonymous" ></script>
</head>
<body>
<div id="obniz-debug"></div>
<!img src="https://obniz.com/ja/users/4978/repo/image.png" style="position:absolute; left: 5%; top: 30%">
<input type="text" value="140" id="in_angle" maxlength="3" style="position: absolute; left: 5%; top: 38%; width:100px"/>
<input type="text" value="degree" id="in_unit" maxlength="3" style="position: absolute; left: 12%; top: 38%; width:100px; border:0" readonly/>
<button class="btn btn-primary" id="set" style="position: absolute; left: 5%; top: 45%; width : 70px">Set</button> <! *** button ***>
<button class="btn btn-primary" id="reset" style="position: absolute; left: 9.5%; top: 45%; width : 70px">Reset</button> <! *** button ***>
<button class="btn btn-primary" id="m30" style="position: absolute; left: 5%; top: 50%; width : 70px">-30</button> <! *** button ***>
<button class="btn btn-primary" id="p30" style="position: absolute; left: 9.5%; top: 50%; width : 70px">+30</button> <! *** button ***>
<canvas width="700" height="300"></canvas> <! *** jcanvas ***>
<script>
const SANG = 140.0;
var obniz = new Obniz("OBNIZ_ID_HERE");
var ang = 90; // door angle
var sAng2 = 0; // servo angel for animation
var sAng = 0; // servo angle
var stt = 0; // start time for elapse
const position = '{"x":230, "y":50, "width":400, "height":20, "b_c":"white", "margin":10, "x2":150, "y2":120, "w2":120}';// b_c:back color
const pos = JSON.parse(position);
// called on online
obniz.onconnect = async function() { // *** connect ***
var uart = obniz.getFreeUart(); // ***uart ***
uart.start({ tx: 9, rx: 10, gnd:8, baud:57600});
var servo = obniz.wired("ServoMotor", {gnd:5,vcc:4,signal:3});
servo.range = { min: 0.9, max: 1.34 } // *** servo S125 1T/2BB***
// servo.range = { min: 0.8, max: 2.7 } // MG92B
servo.angle(sAng); // half position
servo.on;
// called while online. ************* loop ***************
obniz.onloop = async function() {
if(uart.isDataExists()){
lang = ang
lsAng = sAng2;
angStr = uart.readText(); // '0'~ '7' or '9'
var cNum = parseInt(angStr.charAt(0));// 7, 6, .., 1, 0, 9
aLst = [110,93,80,70,60, 35,20,0,-0,125];// angle list
ang = aLst[cNum];
console.log(sAng2);// ang, cNum
let delBar = (val, col) => val + ((col == pos.b_c) ? pos.margin : 0);
let dLst = [];
// *** door ***
dLst.push({fillStyle: pos.b_c, x: pos.x, y: pos.y, width: delBar(pos.width, pos.b_c), height: delBar(pos.height, pos.b_c), rotate: lang});// delete last door
dLst.push({fillStyle: '#d4d4d4', x: pos.x,y: pos.y, width: delBar(pos.width,'#d4d4d4'), height: delBar(pos.height,'#d4d4d4'), rotate: 0});// default door position (grey)
dLst.push({fillStyle: 'green', x: pos.x, y: pos.y, width: pos.width, height: pos.height, rotate: ang}); // current door
dLst.push({fillStyle: pos.b_c, x: 0, y: 0, width: (pos.x + pos.margin*2) << 1 , height: (pos.y + pos.margin) << 1}); // corner square to hide
dLst.push({fillStyle: pos.b_c, x: 0, y: 0, width: 600 , height: 80}); // corner square2 to hide
dLst.push({fillStyle: 'grey', x: 0, y: (pos.y), width: pos.x << 1 , height:pos.height}); // left wall
dLst.push({fillStyle: 'grey', x: pos.x + pos.width + pos.margin * 3, y: pos.y, width: (pos.x - pos.margin) << 1 , height:pos.height}); // right wall
// *** servo ***
dLst.push({fillStyle: 'yellow', x: pos.x2, y: pos.y2, width: 50, height: 50 });
sAng2 = getSAng2();
dLst.push({fillStyle: pos.b_c, x: pos.x2 + pos.w2 * Math.cos((SANG-lsAng) * Math.PI/180) * 0.5, y: pos.y2 + pos.w2 * Math.sin((SANG-lsAng) * Math.PI/180)* 0.5, width: delBar(pos.w2, pos.b_c), height:delBar(pos.height, pos.b_c), rotate: (SANG-lsAng)});// current servo
dLst.push({fillStyle: 'orange', x: pos.x2 + pos.w2 * Math.cos((SANG-sAng2) * Math.PI/180) * 0.5, y: pos.y2 + pos.w2 * Math.sin((SANG-sAng2) * Math.PI/180)* 0.5, width: pos.w2, height:pos.height, rotate: (SANG-sAng2)});// current servo
for (var rect of dLst)
$('canvas').drawRect(rect);
$('canvas').drawArc({fillStyle: 'purple', strokeWidth: 5, x: pos.x2, y: pos.y2, radius: pos.margin});
$('canvas').drawArc({strokeStyle: '#000', strokeWidth: 5, startArrow: true, arrowRadius: 15, arrowAngle: 90, x: pos.x2, y: pos.y2, radius: 50, start:160, end:220});
}
servo.angle(sAng); // half position
servo.on();
};
$("#set").on("click", function() { // jQuery id="close" on_method to click_event, handler_function
sAng = parseInt($("#in_angle").val());
stt = performance.now();
});
$("#reset").on("click", function() { // jQuery id="close" on_method to click_event, handler_function
sAng = 0;
stt = performance.now();
// $("#in_angle").val(sAng.toString()); // overwrite text box
});
$("#p30").on("click", function() { // jQuery id="close" on_method to click_event, handler_function
sAng += 30;
$("#in_angle").val(sAng.toString()); // overwrite text box
});
$("#m30").on("click", function() { // jQuery id="close" on_method to click_event, handler_function
sAng -= 30;
$("#in_angle").val(sAng.toString()); // overwrite text box
});
function getSAng2() {
if (stt) {
const elaEnd = 5000;
var elaMs = performance.now() - stt;
if (elaMs > elaEnd) {
elaMs = elaEnd;
stt = 0;
return sAng;
}
return sAng * (elaMs / elaEnd);
} else {
return sAng;
}
}
};
// called on offline
obniz.onclose = async function() {
uart.end();
for (i=0; i<10; i++) {
servo.angle(0.0); // half position
servo.on();
}
};
</script>
</body>
</html>
下のコード、ライブラリはこちら(https://os.mbed.com/users/maro/code/Nucleo_IHS11a/)
Sensor_mbed.cpp
// *** IHS ver1.1a ***
// *** Setting in Sensor.h: every/normal, single-board/cube/cube-dual(row-length) ***
#include "mbed.h"
#include "CLED.h"
#include "SerialTest.h"
// **************** global valiables *************************
extern const int COL_LEN;
// **************** global functions *************************
char getMaxIdx();
// **************** global objects ***************************
Sensor sensor(-70); // The smaller the value, the higher the height
CLED cled(11); // IHSxx
SerialTest st;
// ********************** main *******************************
int main() {
// ********************** get initial value ******************
cled.set(10);
wait(1);
sensor.set_adAryInit(); // set initial value
// st.intvExec('x');
// *************************** loop **************************
while(1) {
sensor.setAd();
st.intvExec(getMaxIdx());
}
}
char getMaxIdx() {
Sensor& sen = sensor;
const int MAX_AD = 100;
const uint8_t NG_IDX = 9;
uint8_t idx = NG_IDX;
int maxV = 0;
for (uint8_t i=0; i < COL_LEN; i++) {
int val = sen.getColAd(i);
if (val > maxV) {
idx = i;
maxV = val;
}
}
uint8_t rev = (maxV > MAX_AD) ? idx : NG_IDX;
return 0x30 + rev;
}
投稿者の人気記事
-
3duilab
さんが
2021/04/26
に
編集
をしました。
(メッセージ: 初版)
-
3duilab
さんが
2021/04/26
に
編集
をしました。
-
3duilab
さんが
2021/04/26
に
編集
をしました。
-
3duilab
さんが
2022/01/18
に
編集
をしました。
ログインしてコメントを投稿する