概要
赤外線の反射を利用した非接触空間センサーで、手の動き、方向、高さを検出しテープ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/)を使っています。
ソフトウェア
センサーの距離データ最大値が基準値を超えると、その最大値とそのインデックス(センサー番号)を配列に保存していきます。最大値が基準値以下になると配列の情報から速度、長さ、色を計算し、テープ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
さんが
2022/07/19
に
編集
をしました。
(メッセージ: 初版)
-
3duilab
さんが
2022/07/19
に
編集
をしました。
-
3duilab
さんが
2022/07/19
に
編集
をしました。
-
3duilab
さんが
2022/07/20
に
編集
をしました。
-
3duilab
さんが
2022/07/20
に
編集
をしました。
-
3duilab
さんが
2022/07/20
に
編集
をしました。
ログインしてコメントを投稿する