公的空間のイベント混雑状況をレーザー距離計で可視化する

## 概要　
* 公的空間で行われるマルシェの混雑状況を2日間に渡りカウントし可視化する実験を試みた
* 通行量、滞在人数、滞在時間を計測するには入り口、出口を絞る必要がある。今回は会場のあるポイントの人の通過量を混雑度と見なす
* イベントでの入場者数は貴重なデータである。無人でカウントがどこまでできるのかを試してみる
* 広島駅北口エリアのエリアマネジメントを行う、エキキタまちづくり会議 (https://ekikita.jp) にご協力頂いた。

【参考】
　エリアマネジメントについて（国土交通省）
　https://www.mlit.go.jp/common/001059393.pdf
　地方創生 まちづくり - エリアマネジメント - (内閣官房まち・ひと・しごと創生本部事務局)
　~~https://www.kantei.go.jp/jp/singi/sousei/about/areamanagement/areamanagement_panf.pdf~~
※地方創生ホームページのリニューアルに伴い資料ページのURLも下記に変更になりました。
　（2021年4月～）
https://www.chisou.go.jp/sousei/about/areamanagement/areamanagement_panf.pdf

## 用意するもの
* Raspberry Pi3 Model B (4でもおそらく大丈夫)
* Raspberry Pi用のケース
* Raspberry Pi用のヒートシンク、ファン
* Raspberry Pi3 Model B B+ 対応 電源セット(5V 3.0A)
* vl53l1x レーザー測距センサー (最大計測範囲 4m)
* LED,330Ωの抵抗
* 100円ショップの三脚
* 100円ショップで購入したAirPodsのケース (センサーのカバーとして使用)

![IMG_0131.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/bab2a52c-6ae6-a697-f28d-bca4465fad9c.jpeg)
![IMG_0197.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/97632d3f-ec10-cf4b-df9d-3ee4cf961927.jpeg)

## 準備

* RaspberryPiのセットアップをしWifi接続できるようにしておく。  
  https://www.raspberrypi.org/downloads/
* Ambient(IoTの可視化サービス)のライブラリが使用できるようにしておく。  
* Ambientにてダッシュボードを作成し、チャネルIDとライトキーを取得しておく。  
  https://ambidata.io/refs/python/
* vl53l1xのPython用ライブラリを使用できるようにしておく  
  https://github.com/pimoroni/vl53l1x-python

## 組み立て

* Raspberry Piとレーザー距離センサー、LEDを以下のように接続する
![スクリーンショット 2020-11-17 9.39.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/6e055620-2105-42de-9ec3-a8831b75e851.png)


## プログラミング
* 汎用のライブラリを使うためPython3を使いプログラミング
* git cloneしたライブラリのexamplesフォルダにあるdistance.pyを改変して使用
* レーザー距離センサーの値をloopして読み取り、しきい値を下回った数を通過数としてカウント
* カウントする毎にGPIOピンに接続したLEDを点灯/消灯させる
* 5分毎のカウント数をAmbientに送信し可視化する

```python:distance.py
#!/usr/bin/env python

import time
import sys
import signal

import VL53L1X

import ambient
import datetime
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.OUT)　#LED点滅用
from time import sleep


print("""distance.py

Display the distance read from the sensor.

Uses the "Short Range" timing budget by default.

Press Ctrl+C to exit.

""")

ambi = ambient.Ambient(xxxxx, 'xxxxxxxxxxxxxxxx')　# ←ambientのチャネルIDとライトキー
count = 0
sndcnt = 0

# Open and start the VL53L1X sensor.
# If you've previously used change-address.py then you
# should use the new i2c address here.
# If you're using a software i2c bus (ie: HyperPixel4) then
# you should `ls /dev/i2c-*` and use the relevant bus number.
tof = VL53L1X.VL53L1X(i2c_bus=1, i2c_address=0x29)
tof.open()

# Optionally set an explicit timing budget
# These values are measurement time in microseconds,
# and inter-measurement time in milliseconds.
# If you uncomment the line below to set a budget you
# should use `tof.start_ranging(0)`
tof.set_timing(66000, 70)

tof.start_ranging(0)  # Start ranging
                      # 0 = Unchanged
                      # 1 = Short Range
                      # 2 = Medium Range
                      # 3 = Long Range

running = True


def exit_handler(signal, frame):
    global running
    running = False
    tof.stop_ranging()
    print()
    sys.exit(0)


# Attach a signal handler to catch SIGINT (Ctrl+C) and exit gracefully
signal.signal(signal.SIGINT, exit_handler)

while running:
    distance_in_mm = tof.get_distance()
    print("Distance: {}mm".format(distance_in_mm))

    now = datetime.datetime.now()
    minute = '{0:%M}'.format(now)
    second = '{0:%S}'.format(now)

    print(minute)
    print(second)

    if distance_in_mm < 1300:　　# カウントするか否かのしきい値(mm)

        count += 1
        GPIO.output(25, GPIO.HIGH)　　# LED点滅
        sleep(0.5)
        GPIO.output(25, GPIO.LOW)
        sleep(0.5)

    if int(minute) % 5 == 0 and int(second) == 0:

        if sndcnt < 1:

            r = ambi.send({'d1': count})
            if r.status_code != 200:
                continue

            print(count)
            print("***sended***\n")

            count = 0

        sndcnt += 1

    else:

        sndcnt = 0

    time.sleep(0.1)
```

## 現地に設置した様子

![IMG_0138.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/2ed490f8-828c-4e65-128e-4174dd9aece2.jpeg)
![IMG_0139.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/c2de39ed-12d0-7574-34fa-6e5395146344.jpeg)
![IMG_0140.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/509ad2f9-7973-af78-1b46-051e383fea05.jpeg)

## 結果
* イベントの開催時間を通して正常なカウントをすることはできなかった。
* 調整設置場所、しきい値の変更など現地で調整したが、安定したカウントはできなかった。  
　赤背景：データ取得されていた部分  
　青背景：調整中  
　グレー背景：イベント開催時間外  
![スクリーンショット 2020-11-17 9.46.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/65d61159-89f7-31b1-1920-cb9eaebce652.png)
![スクリーンショット 2020-11-17 9.46.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701445/7ffe5242-c5a3-58c8-c6d3-ee3b8c887e8b.png)

## 不具合内容
* レーザー距離計の値が安定しない。対象物の素材（石、木、紙）や表面の凸凹により安定度が変わってしまう。
* 1人の通過で2〜3人分とカウントされてしまう。（検知した場合のwaitを入れてある程度改善した）
* 明るさ、日光が当たらない場合と当たらない場合での安定度の違いが大きい模様。
* 数時間に一度、ambientのapi URLに対して、HTTP MaxRetryErrorが出る。  
　自宅Wifiでは10時間以上稼働していたため、現地でお借りしたWifi設備による可能性が高い。

## 今後に向けて
* コロナ渦で屋内イベントの開催が難しい状況であるが、まずは屋内で確実にカウントできるように調整を続ける
* 屋外でカウントが難しい問題は、他の種類のセンサーを検討することも考える
* ToFタイプの距離計を使う場合は安定して反射できる対象物を用意する
* 現地のWifi環境で予め長時間接続に問題がないかテストが必要