超音波距離センサーは安いし、音速を扱うという何とも理系的な響きが好きなセンサーの1つです。
ただ、距離を測るのは簡単ですが、人数をカウントするとなると、ちょっと大変です。
センサーから得られるデータを読み取りながら、どうすれば測定できるか考えて試行錯誤するところが勉強になるかと思います。
まず、距離を取得するプログラムをArduinoで作ったら、まず、シリアルモニターで値を見てみましょう。超音波距離センサーの前を何回か行ったり来たりしてみました。
多分、見にくいですが、グラフにしてみると以下のような感じになります。
青線が生データ、オレンジが3項移動平均、グレーが5項移動平均です。
移動平均なにそれ?という方はWikipediaでもどうぞ。要はデータの凸凹を少なくするためのフィルター的な役割になります。
最初、このデータを見るまでは、もっと綺麗な状態になっていると思いました。ですので、距離値の前回値と今回値から傾きを求め、傾きの符号が変化したところで1人とカウントできるかな。。。と思ったんですね。
で、実際に実装して動かしてみましたが、うまくいきませんでした。
これは色々な対処方法が考えられますが、どんなに複雑なパターンでも「入ったら必ず出ていく」わけです。
ということで、「入」を識別するため、傾きが負 でかつ 距離が 1200mm以下 であれば人が近づいたことを表すフラグをオンにします。ここで人数カウントを1増やします。
そのフラグをオフにするのは、距離が1200mm以上となったとき とします。
上の説明をコードに落とし込むと以下のようになります。
void setup() {
pinMode(2, OUTPUT); //TRIG
pinMode(3, INPUT); //ECHO
Serial.begin(115200);
}
//人の外形:50cm/人 ÷ 歩行速度:1.3m/s = 0.38s/人
//1人当たり5個測距を目指すとサンプリング間隔は76ms(>60ms)
const int MA_LEN = 5;
int l[MA_LEN];
int t = 0;
int pre_l = 0;
//int pre_dl = 0;
boolean approaching = false;
void loop() {
//測距
digitalWrite(2, HIGH); //TRIGを10μsHIGHにする
delayMicroseconds(10);
digitalWrite(2, LOW);
int Thl = pulseIn(3, HIGH); //ECHOの立上り時間を測定
int L = Thl / 2 * 340.0 / 1000; //時間Thlを距離に変換
// Serial.println(L);
//移動平均
l[t] = L;
t = ++t % MA_LEN;
int sum = 0;
for (int i = 0; i < MA_LEN; i++) {
sum += l[t];
}
int l_ma = sum / MA_LEN;
//変化検知
int dl = pre_l - l_ma;
if (l_ma < 1200) {
if (millis() > 1000) { //Arduino起動直後は人検知は無効とするため
if (dl < 0 && approaching == false) {
approaching = true;
tone(5, 880, 50); //圧電ブザーを鳴らす
}
}
} else {
approaching = false;
}
//前回値保存
pre_l = l_ma;
delay(76);
}
コード中に、パラメータを決定した根拠を残しておきました。ご参考まで。なお、1人あたりサンプリング数を5個にしたのは、傾きを算出するために必要な個数でもありますが、HC-SR04のサンプリング間隔が60ms以上でなければならないところからも来ています。
まぁ、もっとアカデミックっぽい数理的な方法があると思いますが、自分で考えたベタなアルゴリズムをまず実装できるようになること、も大事かと思いますので、このような方法をとっています。
センサーを配置する場所の工夫や、上記のコードですと、近づいたことを表す1200mmという数値や、移動平均の個数などは用途や状況に合わせて調整する必要があるパラメータになると思います。
以下、評価した様子です。オクルージョンは想定していません。人が一人だけ通れるような通路に配置することを想定しています。人が通過したときに圧電スピーカーが「ピッ」と鳴らすようにしていますが、鳴らないときもあるし、2度なるときもあります。
アルゴリズムはまだまだ改善の余地があります。
難しいですが、面白いですね。
でわっ!