3duilabのアイコン画像

角度検出できるドアセンサーをサーボモータで閉じる

3duilab 2021年04月26日に作成  (2022年01月18日に更新) 製作品 製作品

角度検出できるドアセンサーをサーボモータで閉じる

非接触空間センサー双方向ハンドセンサー(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のアイコン画像
赤外線フォトリフレクタを利用した次世代の非接触空間センサー「双方向ハンドセンサー」を開発しています。電子回路と組込みソフトウェアのエンジニアです。事故で指先を失いました(冬山で凍傷になって)😁 動画まとめ https://imgur.com/user/3duilab/posts  website https://interactive-hand-sensor.com/root/
ログインしてコメントを投稿する