AND OR  

Internet of Things (IoT)に関するメモ。主にRaspberry Piと様々なモジュールを使って進めていく。

Raspberry Pi

基本・ツール等

Raspberry Pi 3のスペック
SoC/CPU: Quadcore 64-bit 1.2GHz ARM Cortex A53
GPU: Broadcom VideoCore IV @ 400 MHz
RAM: 1GB (LPDDR2-900 SDRAM)
Wi-Fi: 802.11n Wireless LAN
Bluetooth: Bluetooth 4.0

Raspbian OS

Debianベースの Raspberry Pi 用の小規模な開発者向けOS

  • コマンド等

GPIO

Raspberry Pi 3のGPIOピンレイアウトは以下のようになっている。

 500x535

環境一般

  • X11
    Xオプション付きでSSHログイン後(ssh -X ~) lxsession コマンドを実行する(MBAで動作確認)。

ツール

vcgencmd
Native Python binding for RaspberryPi vcgencmd command-line tool

多いし、見難い、。各コマンドの使い方は以下のページで概要は掴める。あとで整理しておく。

WiringPi
GPIO Interface library for the Raspberry Pi.
GPIOを操作するライブラリ+コマンドラインツール。Cで書かれているが他の言語のバインディングも多数公開されている。
本家のGitは重いのでこちらから取得した方が良いかもしれない。

ネットワーク

Raspberry Pi 3からはオンボードで802.11n Wi-fi機能が搭載されているので無線LANアダプタは不要。5GHz帯には対応していないので注意。Raspberry Pi 2用には無線LANアダプタはBUFFALO WLI-UC-GNMを用いた。こちらも残念ながら5GHz帯は使えない。あと、しばらく稼働させていると触るのをためらうくらい発熱があるので何か熱対策が必要だと思う。

任意のホスト名でアクセスするには avahi-daemon をインストールする。AvahiはZeroconf仕様のLinux向けの実装となっている。

接続元ではBonjourサービスを起動しておく。後は 任意のホスト名.local という名前でアクセスすれば良い。ここではraspiという短いホスト名にしておいた。

カメラ

SONY製イメージセンサーを採用したCamera Module V2.1を用いる。

cameramodulev2.jpg

イメージセンサー IMX219の製品情報

  • Sony 製品・技術紹介 IMX219PQ
    1/4.0型なのでスマートフォンに組み込まれているイメージセンサーよりも一回り小さいが、SONYお得意の裏面照射型なので高感度性能も高い。

Raspberry Pi側で raspi-config コマンドからカメラ機能を有効にしておく。

raspi-config1.png

Raspberry Pi 3との接続には15ピンMIPIカメラインタフェース(CSI)にカメラモジュールのフレキシブルケーブルを挿すだけ。ねじれに弱いケーブルなので慎重に扱おう。

こういうエラーが出る場合、エラーメッセージ中の OV5647 というのは以前のv1.xのセンサー OmniVision OV5647 を指している。ここで接続しているのはv2のカメラモジュールなので正常に認識しない。

mmal: Cannot read cameara info, keeping the defaults for OV5647
mmal: mmal_vc_component_create: failed to create component 'vc.ril.camera' (1:ENOMEM)
mmal: mmal_component_create_core: could not create component 'vc.ril.camera' (1)
mmal: Failed to create camera component
mmal: main: Failed to create camera component
mmal: Camera is not detected. Please check carefully the camera module is installed correctly

これを解決するにはファームウェアをアップデート後、再起動する必要がある。

カメラモジュールの接続確認は以下のコマンド。

raspistill

写真を撮るには raspistill コマンドを用いる。

  • RASPISTILL (公式のコマンドリファレンス)

カメラモジュールにアクセスするにはsudo権限が必要なので注意。raspistillコマンドはオプションはv2センサーでしか機能しないオプションもいくつかあるようだ。

一例としてタイムラプス画像を撮影するには以下のようなオプションを付ける。

v2カメラモジュールで撮影した写真のEXIFを見てみた。

  • モデル名: RP_imx219
  • 解像度(実効画素数): 3280x2464px (アスペクト比 4:3)
  • 焦点距離: 3mm
  • 開放F値: F2.0

35mm換算の画角は約29mmとなっている。スマートフォンのカメラだと大体20mm前後なので少しだけ望遠寄り。

raspivid

動画を撮影するには raspivid コマンドを利用する。

デフォルトだとFullHD(1920x1080px) 30fps 17Mbpsの動画となるようだ。動画コーデックはH.264、いくつかのエンコードパラメータはオプションで指定できる。ビットレートは最大25Mbpsまでのようだ。それ以上を指定すると以下のログが出力される。

Bitrate too high: Reducing to 25MBit/s
  • 測光(Metering -mm)と露出(Exposure -ex)を逆光用の設定にしてみたけどほとんど見た目には変わらなかった。
  • HD(1280x720px) 30fps 17Mbpsの約2分間の動画(空を定点観測)で約30MB程度の容量になった。同条件なら1時間で1GB前後になる計算。

raspivid コマンドで作成される動画はRAW H.264フォーマットなので、これを使いやすいMP4動画に変換するには例えば以下のような方法がある。MP4BoxコマンドはRaspberry Pi上でも特に問題なく実行できるオススメの方法。

Pythonから操作(picamera)

Pythonからカメラモジュールを操作するには picamera モジュールを利用する。サンプルコード類はGitHubに上げておく。

注意点等

  • シャッタースピード変更時の注意点

    In later firmwares, this attribute is limited by the value of the framerate attribute. For example, if framerate is set to 30fps, the shutter speed cannot be slower than 33,333µs (1/fps).

ISOの値も変更できるようになっているので、もし低感度+スローシャッターで撮影したい場合はフレームレートを遅くしてからシャッタースピードを変更する必要がある。単純にISO値を低く設定して残りを全てオート設定で撮影しても、露出不足で写真が暗くなってしまう。

  • メモリ不足のエラー
    picamera.exc.PiCameraValueError: too many macroblocks/s requested; reduce resolution or framerate
    このようなエラーが出たら、書いてある通り解像度やフレームレートを落とす。8MPをフルで使うことは少ないと思うので、Full HDかHDサイズくらいで撮ると良いかも。
  • 動体を撮影する場合
    このカメラモジュールはローリングシャッターであるため、動体を撮るときはシャッタスピードが遅いと歪むので注意。

LCD (キャラクタ液晶)

 600x400

1602-V2のLCDを300円くらいで購入。Raspberry Pi 3との結線はだいたい以下のサイトの通りでOK。GPIO7とGPIO8はT-Cobbler上の表記だとSPICSO1、SPICSO0になっているので注意。また、VOはGNDに繋いでしまうと液晶上の文字がほとんど見えなくなったので、可変抵抗(半固定ボリューム)を使って1.0-1.5Vくらいで調整するとちょうど良いコントラストになる。バックライトの明るさも適宜調整すると良い。

PythonでのサンプルコードはGitHubに。表示処理部分はAdafruitのモジュールを使うと簡単。

センサー

センサーモジュールの試用について。

DS18B20 (温度センサー)

1-Wire接続の温度センサー

RPI-DS18B20.png

/etc/modules に以下の2行を追加、その後再起動しておく。

センサーを結線後、/sys/bus/w1/devides 以下に 28 から始まるディレクトリが作成されていることを確認したら、そのディレクトリ内の w1_slave ファイルから現在の温度が取得できる。

計測精度は後述するBMP180センサーと実測で同等程度だった。コストパフォーマンスはなかなか良いのではないだろうか。

参考: DS18B20 Temperature Sensor with Raspberry Pi

Pythonからは W1ThermSensor モジュールを使って値を読み取ることもできる。

  • W1ThermSensor
    コマンドラインツールも付属しているので併せて試してみる。

複数のDS18B20センサーを接続してもIDで判別できるようになっている。

BMP180 (気圧/温度センサー)

I2C接続の気圧/温度センサー

RPI-BMP180.png

I2C (Inter-Integrated Circuit): フィリップス社が開発したデバイスとのシリアル通信方式のひとつ。接続認識確認には i2cdetect コマンドを利用できる。事前に i2c-tools パッケージを入れておく。

Pythonからデータ取得するにはAdafruitのモジュールが便利。Python3.x系でも動作することを確認。

コード中にはBMP085とあるがBMP180のセンサーでも動作する。

気圧測定の活用方法を考えねば。

DHT11 (温湿度センサー)

シリアル接続の温度/湿度センサー

RPI-DHT11.png

BMP180と同様に、AdafruitがPythonモジュールを公開している。こちらもPython3.x系でも動作することを確認。

安価なだけあって精度は低い。自宅にある精密機械用防湿庫備え付けの庫外湿度計と比較して20%ほど数値に開きがある。外れ値の頻度も多い。温度計測の精度も±2℃程度はあるようだ。

HC-SR501 Infrared PIR Motion Sensor (モーションセンサー)

焦電型赤外線センサー (焦電人感センサー)

HC-SR501.jpg

ドーム型のフレネルレンズは光を効率良く集めるためのもの。基板下に取り付けられている半固定抵抗で感度を調整することができる。

RPI-Digital-PIR-Motion-Sensor.png

モーションの検知はGPIOの値を読み取るだけ。RPi.GPIOモジュールのイベントハンドラを使い、エッジの立ち上がりを検知したらコールバック関数に処理を渡すと良い。

ADコンバータ

10bit 8ch ADコンバータ MCP3008

量子化は10bit、サンプリング周波数が最大200kHz。サンプリングレートを落とせば8chまで使えるのでコスパは高い。

 600x400

ADコンバータを上手く活用できるようになれば電子工作でできることの幅が一気に広がるとは思うけど、まだ知見が少なすぎるのでもっと勉強する必要がある。

その他部品メモ

可変抵抗 半固定抵抗 半固定ボリューム

 400x267

この部品の呼び方はいくつかあるようだ。抵抗値をどうやって読み取るのかわからないので、型番毎の抵抗値をメモしておく。写真の可変抵抗は103なので抵抗値は0 - 10kΩとなる。

メーカーEcowsera
ASINB01GNJQCE4
抵抗値100Ω (101), 200Ω (201), 500Ω (501), 1kΩ (102), 2kΩ (202), 5kΩ (502), 10kΩ (103) 20kΩ (203), 50kΩ (503), 100kΩ (104), 200kΩ (204), 500kΩ (504), 1MΩ (105)

性能検証

Raspberry Pi 3の主に性能面を検証する。安定してどのくらい負荷の高い処理が実行できるか。

機械学習

線形SVMによる文書分類モデルをscikit-learn実装を用いて作成してみる。データセットは news20.binary を利用するので二値分類モデルとなる。

学習自体はなんとか完走。マルチプロセス(最大2プロセスに制限)によるグリッドサーチ(5-fold cv)で約25分時間を要した。Raspberry Pi 3のカタログスペックだけで判断するともっと速く処理できても良いのではと思ったけれど、実際はこれだけの時間がかかってしまった。

プロセッサ(コア)数を自動検出させて実行みるとOSErrorが発生。Raspberry Pi 3は4コアなので4プロセス生成されるようだ。1プロセスでちょうどメモリを45-55%程度消費していたので妥当な結果。

$ sudo tailf /var/log/messages
Oct 11 23:59:12 raspi kernel: [ 3607.615942] [ 9846]  1001  9846    84171    38381     144       0    20853             0 clf.py
Oct 11 23:59:12 raspi kernel: [ 3607.615949] [ 9896]  1001  9896   144454    90366     247       0    20793             0 clf.py
Oct 11 23:59:12 raspi kernel: [ 3607.615957] [ 9897]  1001  9897   154324   112257     288       0    20755             0 clf.py
Oct 11 23:59:12 raspi kernel: [ 3607.615965] [ 9899]  1001  9899   145339    99528     267       0    20779             0 clf.py
# OSError例外発生
Exception in thread Thread-1:
Traceback (most recent call last):
 File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
   self.run()
 File "/usr/lib/python3.4/threading.py", line 868, in run
   self._target(*self._args, **self._kwargs)
 File "/usr/lib/python3.4/multiprocessing/pool.py", line 366, in _handle_workers
   pool._maintain_pool()
 File "/usr/lib/python3.4/multiprocessing/pool.py", line 240, in _maintain_pool
   self._repopulate_pool()
 File "/usr/lib/python3.4/multiprocessing/pool.py", line 233, in _repopulate_pool
   w.start()
 File "/usr/lib/python3.4/multiprocessing/process.py", line 105, in start
   self._popen = self._Popen(self)
 File "/usr/lib/python3.4/multiprocessing/context.py", line 267, in _Popen
   return Popen(process_obj)
 File "/usr/lib/python3.4/multiprocessing/popen_fork.py", line 21, in __init__
   self._launch(process_obj)
 File "/usr/lib/python3.4/multiprocessing/popen_fork.py", line 70, in _launch
   self.pid = os.fork()
OSError: [Errno 12] Cannot allocate memory

学習処理実行中のCPU温度の推移は以下の通り。Raspberry Pi用のケースを外して基板剥き出し状態で計測。

train_temp.png

ケースを付けるとさらに温度が上がる。70℃超えるとなかなか危険、この温度になるとCPUのヒートシンクを手で触り続けるのも難しいくらい。

train_temp_incase.png

やはり計算処理はサーバ側に任せて、末端のデバイス側はあくまでデータ収集/送信の役割に徹するのが安全なようだ。

その他機能/ツールメモなど

パワーマネージメント機能

Wi-Fi接続が頻繁に切断されてしまう場合はパワーマネージメント機能が働いている可能性がある。この機能をOFFにする方法はいくつかあるが、8192cu.conf ファイルを設置する方法が良さそう。

CPU温度

70℃を超えてくると、CPUやLANコントローラが手でずっと触れ続けるのは厳しいくらい熱くなる。初期設定段階でいろんなモジュールをコンパイル・インストールしている際に起こりがち。この温度だとSSHのコネクションが突然切れることも増えてくるので熱対策が必要。上述の機械学習検証の項目で示したようにRaspberry Piのケース有無でも10℃ほど温度が変わるようだ。

IoTクラウドサービス

Raspberry Piなどの機器と組み合わせてIoT開発をサポートするクラウドサービスを試用。

M2X

AT&Tが提供するIoTクラウドサービス

提供機能はデータの可視化程度なので導入のハードルは低い。初級者は後述のAWS IoTよりオススメできる。無料枠でもUp to 100,000(per device/month)ということなので、そこそこの量のデータを送ることができるようだ。

m2x_temp.png

GitHubにM2XをHTTPおよびMQTTプロトコルから利用するサンプルコードを上げておく。

AWS IoT

Amazonが提供するIoTクラウドサービス

他のIoTクラウドサービスと比較すると認証・認可周りがしっかりしているし、セキュリティ面での安心感は高い。他のAWSサービスとの連携をするなら連携するサービス分の学習コストを都度払う必要がある。まずはAmazon SNSと連携するサンプルを作るのが楽。マイクロサービスみたいな要領で開発する。

 600x309

Cayenne myDevices

フランスのスタートアップ企業が提供するIoTサービス。日本語/英語ではあまり情報が集まらない。フランス語を読む必要があるのか? Osoyoo社と提携してたりするので営業活動も力を入れているようだ。

接続されたセンサーモジュールが自動認識されるのは楽だが、起動するデーモンが認証無しのHTTPでひたすらデータを送ってるのでセキュリティ面は危ない。

MQTT (MQ Telemetry Transport)

MQTT is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. It is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium.

  • MQTT.ORG
    メッセージ指向ミドルウェアのアプリケーション層で使用される、TCP/IPによるPub/Sub型データ配信モデルの軽量なメッセージキュープロトコル。フットプリントが小さくM2M/IoT向けの通信プロトコルとして普及してきている。

クライアント

インターネット上にMQTTのオープンな検証環境として test.mosquitto.org が公開されている。mqtt://test.mosquitto.org:1883 へ接続してMQTTによるメッセージ送受信を試してみよう。

  • Pythonモジュール
    Pythonからは paho-mqtt モジュールが利用できる。
  • Rubyモジュール
    Rubyからは mqtt モジュールが利用できる。

上記モジュールを利用してMQTT通信するサンプルコードを上げておく。

また、上述で説明したM2XサービスもMQTTプロトコルに対応しているようだ。

MQTTブローカー

様々なMQTTブローカー実装が公開されているのでいくつか試しておきたい。test.mosquitto.org が重いことがあるので勉強用にMQTTブローカーサーバを1つくらい構築しておくと良いかも。

  • Mosquitto
    MQTTのリファレンス実装であるMosquittoサーバを構築する。バイナリインストーラやrpm/aptレポジトリが公式配布されているのでインストールは簡単。以下から対象OS用のレポジトリから取得する。
    Mosquitto Downloads Pus/Subの動作確認。 MQTTでSSL通信を手軽に試すには自己認証局とサーバー証明書を作成して、mosquittoの設定ファイルを一部変更して起動するだけ。証明書ファイル等を置くディレクトリはどこでもいいが、mosquittoをインストールすると /etc/mosquitto/ca_certificates と /etc/mosquitto/certs ディレクトリが作成されるようだ。

また、独自ドメインを持っているのならオレオレ証明書を使うのではなく、無償でSSL証明書を発行できる認証局(CA)を利用した方が良い。Mosquitto公式サイトではLet's Encryptサービスを利用する手順が説明されているのでこれに従って設定すればOK。

MosquittoはMQTTのリファレンス実装なのでMQTTの仕様自体は網羅されているけれど、冗長化のための機能も無いので実際のシステムに組み込むには難しいか。

  • VerneMQ
    冗長化にも対応しており実運用でも十分利用できるプロダクトようなのでこちらは入念に検証してみたい。
    vernemq_overview.png

現在検証中、あとでまとめる。

サンプルコード (GitHub)

IoT関連の勉強中に作成したサンプルコードをGitHubに置いておく。各センサーモジュール、LCD、IoTクラウドサービスなどを利用するためのサンプルコード等が含まれる。Pythonが多いが一部はRubyやJavaScriptも含まれる。

参考資料など

作業メモ (ペット観察システム)

Raspberry Pi 3 + Camera Module V2.1で安価に自宅内ペット観察システムを作るには。

  • QNAPのSurveillance Stationみたいなシステムが理想。
    QNAPのNASを持っているので既にこのシステムは利用していたのだが、とりあえず勉強のためにRaspberry Piを使って自作してみる。
  • ネックはストレージ、Raspberry PiのmicroSDカードにライブ映像を録画保存するのは現実的にムリなので工夫する必要がある。

raspistill/raspivid コマンドでそれぞれ静止画/動画を撮影することができる。シャッタースピードやISO感度の変更、タイムラプス撮影機能も備わっており意外と高機能。

検証: Camera Module V2.1による1時間のライブ映像撮影時のRaspberry PiのCPU温度推移。室温 26℃で測定。

raspi_camera.jpg
raspi_temp.png

(毎秒測定してみたが、横幅のスケールの都合上見難くなってしまった)
動画撮影時のCPU温度はだいたい65℃前後、GPUも併用しているためそれほど温度上昇はしていないが、無停止稼働は厳しそう。

デジタルカメラとの連携

Camera Module V2.1のレンズやセンサーだと画質はもちろん、高感度性能や逆光耐性が不十分。Raspberry Piからデジタルカメラを操作する方向でも検討してみる。

ILCE-7M2.jpg

SONY製デジタルカメラはWi-fi経由で接続して細かくリモート制御できる。Raspberry Pi用のSDKなどは提供されていないが、機能はHTTPのAPIから操作できるのでプラットフォームを選ばないのも良い。

The Camera Remote API uses JSON-RPC over HTTP. You can therefore use the Camera Remote APIs with any operating system, such as Android, IOS or Microsoft® Windows®.

camera_remote_overview.png

部屋全体をファインダーに収めるには出来る限り広角で撮影する必要がある。ここでは手持ちの16mmのレンズを使用する。

前述の通り、動画撮影はストレージ容量問題があるので、タイムラプス撮影を採用した方が良さそう。自分で撮影部分の処理を書くのは難しくはないけど公式有料アプリもあるのでこちらを使って楽しむのもアリかも。1,028円

playmemories_timelapse.png

動体検知センサーとの連携

上述で紹介した焦電型赤外線センサーと組み合わせる。

ストレージ容量の節約を目的として、動体検知をトリガーとして撮影する方式を検証する。HC-SR501センサーは半固定抵抗で検知感度を適宜調整していく。検知処理自体はGPIO出力のエッジが立つのでそこを読み取ればいいだけ。

異常検知 Anomaly Detection

動体検知センサーに限らず、センサーモジュールは常に正しい(もっともらしい)情報を検知できるわけではない。製品によっては検品が不十分だと異常値を検知する頻度も高くなる。ここではセンサーの異常値パターンをモデル化する方法を検討してみる。

まず簡単な事前検証としてBMP180センサーによる気温と気圧の測定値を見てみる。前述のIoTクラウドサービス M2Xを利用して可視化してみると外れ値が稀に検知されていることがわかる。電源OFFしていた日も多いので、もし無停止で定常運用する場合はもっと高頻度に発生すると考えられる。

m2x_temp_outlier.png
m2x_pressure_outlier.png

M2Xのグラフだと縦軸のスケールが外れ値に引っ張られてしまうようなので解消したい。ただし、気温や気圧など測定する値毎に外れ値を定義するようなアドホックな対応ではなく、まずはLocal Outlier Factor(LOF)を使って外れ値の検知を試みる。

TODO:
検証用ダミーデータの準備
アルゴリズム実装(まずはナイーブな実装で試す)
Raspberry Pi上でも性能面で問題がないか確認
scikit-learnにも外れ値検出の実装があるようなので調査 2.7. Novelty and Outlier Detection

現在検証中、。

課題

  • 部屋が暗い
    外出時は電気を消すしカーテンも閉めていることが多いので光量が足りない。露光時間を長く取ると被写体ブレしてしまうし、ISO感度を上げすぎると画質が劣化する。赤外線フィルターを使えば光量問題は解決するけど色が消えてしまう。
  • カメラを外部から電源On/Offしたい
    α7IIの電源On/Offだけは外部からリモートで操作できない。物理レバーを動かす必要があるので何かギミックを考えたい。
  • シャッター音でねこがビビる
    電子先幕シャッターでも音は大きい
  • LEDを簡易ストロボ代わりに使ってみたらチカチカ光ってねこがいちいち反応する