3duilabのアイコン画像
3duilab 2022年07月19日作成 (2022年07月20日更新)
製作品 製作品 閲覧数 988
3duilab 2022年07月19日作成 (2022年07月20日更新) 製作品 製作品 閲覧数 988

動作検出、方向、速度、高さ

動作検出、方向、速度、高さ

概要

赤外線の反射を利用した非接触空間センサーで、手の動き、方向、高さを検出しテープLED、NeoPixelで表示するシステムを創りました。AIカメラより速く3次元で検出する世界でも珍しい動作検出システムについて解説します。
website 動作検出システム Arduino Nano Every(https://interactive-hand-sensor.com/root/motion-detection-arduino-nano-every)

動画

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

双方向ハンドセンサーの原理

原理は赤外線の反射です。約1.2mS毎に出る8個の距離データを分析しています。赤外線LEDを瞬間的に強く発光させ反射光の強さをADコンバータで数値化しています。
参考記事:スイッチングフォトリフレクタ(非接触空間センサー)(https://elchika.com/article/b6785113-f812-4b39-bf32-891fa1cc0da1/)
原理

配線

双方向ハンドセンサーの接続はこちらArduino Nano Every Example(https://interactive-hand-sensor.com/root/arduino-nano-every-example)
カラーLEDはNeoPixel(https://www.switch-science.com/catalog/1861/)を使っています。
配線図 双方向ハンドセンサー
配線図 NeoPixel
完成図

ソフトウェア

フローチャート
センサーの距離データ最大値が基準値を超えると、その最大値そのインデックス(センサー番号)を配列に保存していきます。最大値が基準値以下になると配列の情報から速度、長さ、色を計算し、テープLED NeoPixelを回します。

  • 遅延時間(1/速度):配列長さ✕定数 /(インデックス最大値 ー インデックス最小値))
  • 長さ        :インデックス最大値 ー インデックス最小値
  •          :距離データ最大値の平均→6段階の色に変換

コード

IHS11_Nano_Every_Motion.ino

#include "CLED.h" #include "MotionNeoPixel.h" #include <SPI.h> Sensor sensor(0); // 40:smaller -> high sense CLED cled(0); // IHSxx MotionNeoPixel mt; NeoPixel np(1); // D1 or TX:NeoPixel // ************************** setup ******************************** void setup() { Serial.begin(57600); sensor.init(); cled.set(5); // init blink np.clr(); delay(2000); } // *************************** loop ******************************** void loop() { sensor.setAd(); if (mt.exec(sensor)) { // mt.pm.print(); np.drift(mt.pm); } }

MotionNeoPixel.cpp

#include "MotionNeoPixel.h" int getMaxIdx(int* ary) { int rev = 0; int Idx = 0; for (int i=0; i<COL_LEN; i++) { if (ary[i] > rev) { rev = ary[i]; // max Value Idx = i; // max Index } } return Idx; } int ad2val(int ad) { // div6 true:1~6 false:1~12 int cri[NEO_CRI_LEN] = {450,700,850,1200,1650,1950}; for (int i = 0; i < NEO_CRI_LEN; i++) if (ad < cri[i]) return i; return NEO_CRI_LEN; } template <class X> void Sprint(X out, int lf = 0) { Serial.print(out); Serial.print(", "); if (lf) Serial.println(""); } // ******************* class MotionNeoPixel ****************** MotionNeoPixel::MotionNeoPixel(){ init(); } void MotionNeoPixel::init() { isOn = false; idx = 0; } bool MotionNeoPixel::exec(Sensor& sen) { const int CRI = 300; // criteria sen.getColAd(adVal); // get sensing data int max_idx = getMaxIdx(adVal); bool isBig = (adVal[max_idx] >= CRI); // max Value >= CRI if (isBig) { // continue isOn = true; // starting copy2Ary(max_idx); } else if (isOn) { // stopping return result(); } return false; } void MotionNeoPixel::copy2Ary(int max_idx) { maxAd[idx] = adVal[max_idx]; // max Value maxIdx[idx] = max_idx; // max Index if (idx < ARY_SIZE) idx++; } bool MotionNeoPixel::result() { // direction length delay(ms) RGB int plus = 0; int minus = 0; for (int k=1;k<idx;k++) { int delt = maxIdx[k] - maxIdx[k - 1]; if (delt > 0) plus++; else if (delt < 0) minus++; } bool dir = (plus > minus); // direction true: Cw int deltIdx = maxIdx[idx - 1] - maxIdx[0]; if (deltIdx < 0) deltIdx *= (-1); bool isEdge = (deltIdx==0) && ((maxIdx[idx - 1] == 7) || (maxIdx[idx - 1] == 0)); if (isEdge) { deltIdx = 2; dir = (maxIdx[0] == 0); } if ((idx <= 2) || (deltIdx==0)) { init(); return false; } int len = deltIdx; const int RATIO = 2; int waitMs = idx * RATIO / deltIdx; int color = ad2val(getAvg(maxAd)); pm.set(dir, waitMs, len, color); init(); return true; } int MotionNeoPixel::getAvg(int* ary) { long addVal = 0; for (int i=0; i<idx; i++) { addVal += ary[i]; } return addVal / idx; }

MotionNeoPixel.h

#ifndef MOTION_NEOPIXEL_H #define MOTION_NEOPIXEL_H #include "Sensor.h" #include "NeoPixel.h" class MotionNeoPixel { public: MotionNeoPixel(); bool exec(Sensor& sen); Param pm; private: static const int ARY_SIZE = 1024; int maxAd[ARY_SIZE]; int maxIdx[ARY_SIZE]; int adVal[COL_LEN]; bool isOn; int idx; // 0 ~ ARY_SIZE void init(); void copy2Ary(int max_idx); bool result(); int getAvg(int* ary); }; #endif

NeoPixel.cpp

#include "NeoPixel.h" NeoPixel::NeoPixel(uint16_t pin) : pixels(NUMPIXELS, pin, NEO_GRB + NEO_KHZ800) { #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) } void NeoPixel::drift(int idx) { idx = idx % NUMPIXELS; pixels.clear(); pixels.setPixelColor(idx, pixels.Color(150, 150, 0)); pixels.show(); // Send the updated pixel colors to the hardware. } void NeoPixel::drift(Param& p) { int delta = 0; for (int i = 0; i < NUMPIXELS+p.len; i++) { pixels.clear(); int idx = (p.isCw) ? i : -i; if (i >= NUMPIXELS) delta++; for (int k = 0; k < p.len - delta; k++) { int m = (idx + NUMPIXELS +((p.isCw) ? -k : k)) % NUMPIXELS; pixels.setPixelColor(m, pixels.Color(p.r(), p.g(), p.b())); } pixels.show(); // Send the updated pixel colors to the hardware. delay(p.mSec); } } void NeoPixel::clr() { pixels.clear(); pixels.show(); }

NeoPixel.h

#ifndef NEO_PIXEL #define NEO_PIXEL #include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> // Required for 16 MHz Adafruit Trinket #endif const int NUMPIXELS = 60; // NeoPixel ring size const int NEO_CRI_LEN = 6; struct Param { bool isCw; // direction int mSec; // delay(mS) int len; // length int colIdx; const int ColorAry[NEO_CRI_LEN][3] = { {255,0,0}, // Red {100,100,0}, // Yellow {0,255,0}, // Green {0,100,100}, // light blue {0,0,255}, // blue {150,0,50} // purple }; uint8_t r() {return ColorAry[colIdx-1][0];} // Red uint8_t g() {return ColorAry[colIdx-1][1];} // Green uint8_t b() {return ColorAry[colIdx-1][2];} // Blue void set(bool cw, int ms, int le, int col) { isCw = cw; mSec = ms; len = le; colIdx = col; } void print() { int ary[4] = {isCw, mSec, len, colIdx}; for (int i=0; i<8; i++) { if (i % 2) Serial.print(", "); else { Serial.print(ary[i >> 1]); } } Serial.println(""); } }; class NeoPixel { public: NeoPixel(uint16_t pin); void drift(int idx); void drift(Param& p); void clr(); private: Adafruit_NeoPixel pixels; }; #endif

終わりに

双方向ハンドセンサーは約2年前に開発し発売しました。現在まであまり売れていませんが、電機産業が総崩れとなりつつある現状でこのような世界でも珍しいセンサーが突破口となる期待をこめて投稿しました。一人で開発しましたが今まで品質問題など致命的な問題がありません。特許は取らず、回路図、ソフトウェアはユーザーガイド(https://interactive-hand-sensor.com/root/user-guide)から公開しています。動作検出の他、非接触スイッチ、非接触タッチパネル、動的展示、侵入検知、マウスなど応用範囲が広く清潔です。世界に売れる現代的な製品を創りたい。みなさんも協力してもらえませんか。

リンク

双方向ハンドセンサーwebsite:https://interactive-hand-sensor.com/root/
双方向ハンドセンサー発売中:https://www.switch-science.com/catalog/6495/

download(双方向ハンドセンサーArduino Nano Every Example):https://drive.google.com/drive/folders/1apqLGDlBQQrGnEpgINgA0En2ZdTqqJnb
download(動作検出システム Arduino Nano Every):https://drive.google.com/drive/folders/1nEYjUnJgwbNl6JxprOL8BeTMk-m_75uz
Instagram:https://www.instagram.com/3duilab/?hl=ja
ツイッター:https://twitter.com/TakahiroMaeda3

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