Raspberry Pi で近寄る何かを撮影

# はじめに

Raspberry Pi と USB カメラを使った工作です。

仕事で、コンベアを流れてきたものを自動で撮影することになりました。場所は屋内ですが、PC を置ける場所がありません。そんな予算もありません。
そこで、Raspberry Piを使うことにしました。これなら 1 万円程度で収まります。
シャッターの代わりは、当初はボタンとか機械的なスイッチを考えていました。しかし、コンベアの周囲にスイッチを固定できそうな場所がありません。そこで、距離センサを使うことにしました。
画像は1日の終わりに取り出せれば良いので、撮影したらとりあえず micro SD に保存することにしました。

探せば、似たような（あるいはもっと高度な）システムを作っている方がたくさんいます。
それらの二番煎じですが、自分の備忘録として残します。


# 用意するもの

- Raspberry Pi ... 1 式
    - Raspberry Pi 3 model B+ を使用したが、他のモデルでも大丈夫なはず。
    - OS は、Rasbian Stretch。
    - Python 3.5.3。
- USB カメラまたはカメラモジュール ... 1 個
    - 手元にあった USB カメラ (Buffalo BSW13K05H) を使用したが、他の製品でも大丈夫なはず。
    - カメラモジュールは v1 互換品を使用したが、v2 互換品でも大丈夫なはず。
- タクトスイッチ ... 2 個
    - シャッター用とシャットダウン用。
    - PC ケースから剥がしたものを使用したので、GPIO ピンに挿せるようになっていた。
    - 部品単品を使うなら別途ジャンパワイヤ(オス-メス) が 4 本必要。
- 超音波距離センサ (HC-SR04) ... 1 個
    - 距離の測定に使用。
- LED ... 緑と赤 各 1 個
    - 緑は待機中表示に、赤は処理中表示に使用。
- 抵抗 ... 4 個
    - 330Ω と 470Ω ... HC-SR04 用に各 1 個
    - 330Ω ... LED用に 2 個
- ブレッドボード ... 小 1 個
- ジャンパワイヤ(オス-メス) ... 10 本程度
    - 電源ラインがないブレッドボードを使用したので、ちょっと多め。


# ハードウェアの接続

1. Raspberry Pi の電源を切る。
1. カメラを接続する。
    - USB カメラなら、USB ポートに接続する。
    - カメラモジュールなら、リボンケーブルを CPI に接続する。
1. タクトスイッチを接続する。
    - シャッタースイッチを 13 番ピン (GPIO27) と 14 番ピン (GND) に接続する。
    - シャットダウンスイッチを 39 番ピン (GND) と 40 番ピン (GPIO21) に接続する。
1. 超音波センサ HC-SR04 を接続する。
    - HC-SR04 の Gnd を 9 番ピン (GND) に接続する。
    - HC-SR04 の Gnd に 470Ω を接続する。
    - HC-SR04 の Echo に 330Ω を接続する。
    - 2つの抵抗の反対側を 11 番ピン (GPIO17) に接続する。
    - HC-SR04 の Trig を 12 番ピン (GPIO18) に接続する。
    - HC-SR04 の Vcc を 2 番ピン (5V) に接続する。
1. LEDを接続する。
    - 緑色と 330Ω を直列にして 29 番ピン (GPIO5) と 30 番ピン (GND) に接続する。
    - 赤色と 330Ω を直列にして 33 番ピン (GPIO13) と 34 番ピン (GND) に接続する。

部品をブレッドボードに刺したもの。
- 銀色の筒がついた青い基板が超音波センサ HC-SR04。端子は左から、Gnd、Echo、Trig、Vcc。
- 470Ω の代わりに、1kΩ を 2 個並列にしている。
![ブレッドボード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176126/f77a8c97-c971-28ca-29d2-a95c94d09fdf.jpeg)

Raspberry Pi に接続したもの。
左下でとぐろを巻いているのは、PC ケースから剥がしたタクトスイッチ。
![Raspberry Pi に接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176126/11bf1dbf-291c-8ea5-2a2a-90f887950ee1.jpeg)

# システムの構築

## 準備

1. カメラを使えるように、設定する。
    - USB カメラであれば、`sudo apt-get install fswebcam` として fswebcam をインストールしておく。
    - カメラモジュールであれば、`sudo raspi-config` などでカメラインターフェイスを有効化して、再起動する。
1. 下の Python スクリプト `auto_camera.py` を Raspberry Pi に転送する。

```python:auto_camera.py
# auto_camera.py
# for python 3.5 and more

import os
import subprocess
import sys
from datetime import datetime
from time import sleep
from gpiozero import LED, Button, DistanceSensor
from picamera import PiCamera

## Configurations
#CAMERA_MODULE = 'PiCamera'  # 'USB' or 'PiCamera'
CAMERA_MODULE = 'USB'  # 'USB' or 'PiCamera'
SHUTTER = 'BUTTON'  # 'BUTTON' or 'DISTANCE'
#SHUTTER = 'DISTANCE'  # 'BUTTON' or 'DISTANCE'
SHUTTER_DISTANCE = 1.0  # Shutter when something will get close then 1.0m
USE_LED = True  # 
STOP_BY_SHUTDOWN = True  # True for shutdown, False for exit()

stop_auto_camera = lambda _=None: subprocess.run(('sudo', 'shutdown', '-h', 'now')) if STOP_BY_SHUTDOWN else sys.exit()
dummy_led = type('', (), {'on': lambda: None, 'off': lambda: None, 'blink': lambda **kw: None})

## Set up LEDs, super sonic sensor and buttons on GPIO
led_wait = LED(pin=5) if USE_LED else dummy_led  # HIGH = pin 29, GND = pin 30
led_busy = LED(pin=13) if USE_LED else dummy_led  # HIGH = pin 33, GND = pin 34
if SHUTTER == 'BUTTON':
    shutter = Button(pin=27)  # HIGH = pin 13, GND = pin 14
    wait_shutter = shutter.wait_for_press
else:
    shutter = DistanceSensor(echo=17, trigger=18, max_distance=3.0, threshold_distance=SHUTTER_DISTANCE)  # ECHO = pin 11, TRIG = pin 12, 5V = pin 2, GND = pin 9
    wait_shutter = shutter.wait_for_in_range
exit_sw = Button(pin=21)  # HIGH = pin 40, GND = pin 39
exit_sw.when_pressed = stop_auto_camera

## Guard from no camera
if not os.path.exists('/dev/video0'):
    # shutdown withoutcamera
    led_busy.blink(on_time=1, off_time=1, n=5)
    stop_auto_camera()

## Set camera control object
if CAMERA_MODULE == 'PiCamera':
    camera = PiCamera()
    camera.resolution = (1024, 768)
else:
    camera = type('', (), {})
    camera.capture = lambda imgpath: subprocess.run(('fswebcam', '--quiet', '--resolution', '1024x768', '--no-banner', imgpath))
    camera.start_preview = lambda: None
    camera.stop_preview = lambda: None

## take a photo for each shutter
camera.start_preview()
while True:
    led_wait.off()
    led_busy.on()
    cur = datetime.now()
    imgdir = cur.strftime('%Y%m%d')
    os.makedirs(imgdir, exist_ok=True)
    imgpath = cur.strftime('%Y%m%d/%Y%m%d-%H%M%S.jpg')
    camera.capture(imgpath)
    led_busy.off()
    led_wait.on()
    wait_shutter()
camera.stop_preview()
```

## ボタンで撮影

1. `auto_camera.py` の定数の値を修正する。
    - カメラに合わせて、`CAMERA_MODULE` の値を `'USB'` (USB カメラ)または `'PiCamera'` (カメラモジュール) から選択する。
    - LED を使わないなら、`USE_LED = False` にする。
1. `python3 auto_camera.py` を実行する。
    - 赤色 LED が点灯して、しばらくすると赤色 LED が消灯 LED して緑色が点灯する。
    - 実行したディレクトリに、日付のサブディレクトリができている。書式は `yyyymmdd`。
    - `yyyymmdd` の下に、日時を名前にした画像ファイルが保存されている。書式は `yyyymmdd-HHMMSS.jpg`。
1. シャッターボタンを押す。
    - ボタンを押すと、緑色 LED が消灯して赤色 LED が点灯する。しばらくすると赤色 LED が消灯 LED して緑色が点灯する。
    - `yyyymmdd` の下に、画像ファイルが追加されている。
1. Ctrl+C を押して `auto_camera.py` を終了する。

## 超音波センサーで撮影

1. `auto_camera.py` の定数の値を修正する。
    - `SHUTTER` の値を `'DISTANCE'` (超音波センサー)に変更する。
    - X m 以内に近づいたらシャッターを切るように、`SHUTTER_DISTANCE` の値を変更する。X は 0.5～4.0 くらいの間。
1. 超音波センサーの前を空ける。
    - 少なくとも `SHUTTER_DISTANCE` 以内に、物がないようにする。
1. `python3 auto_camera.py` を実行する。
    - ボタンの時と同様に動作する。
1. 超音波センサーでシャッターを切る。
    - 超音波センサーの前に何かを置いたり手をかざしたりすると、緑色 LED が消灯して赤色 LED が点灯する。しばらくすると赤色 LED が消灯 LED して緑色が点灯する。
    - `yyyymmdd` の下に、画像ファイルが追加されている。
1. Ctrl+C を押して `auto_camera.py` を終了する。

## ボタンでシャットダウン

1. `python3 auto_camera.py` を実行する。
1. シャットダウンボタンを押す。
    - シャットダウン処理が始まる。
    - Raspberry Pi の電源付近にある緑色 LED が点滅して消灯したら、シャットダウン完了。

## 起動時に自動実行

1. `crontab -e` を実行して `cron` の設定ファイルを開く(`sudo` はつけない)。
1. 設定ファイルの末尾に `@reboot /usr/bin/python3 /home/pi/auto_camera.py &` を追加する。
1. Raspberry Pi を再起動して撮影できることを確認する。


# おわりに

とりあえず、何かが近づいたら撮影するカメラが出来上がりました。この後も、

- シャッターに連動してフラッシュを焚く。
- ブラウザ経由で画像を見られるようにする。
- 撮影したことを Twitter などで自分に通知する。

など、いろいろ発展させていきたいです。

----
- 2019/7/3: auto_camera.py 中の `dummy_LED` を修正