Raspberry Pi Zero Wでやりたかったことの一つにIPカメラがあります。Zero Wにカメラをつないで、他の端末からZero WのIPアドレスにアクセスして監視できるようにするものです。主には、レーザー加工時の監視用カメラ(直接目で確認するより安全なので)です。
カメラの種類:
カメラは以前AliExpressから以下のような赤外線カメラを購入しておりました。このタイプはカメラ両脇に赤外線LED照明がついており、それを照射して暗い部屋の中でもその赤外線反射光を頼りに撮影可能になるらしい。ただし、レンズには赤外線フィルターがついていないため、明るい状況下ではやや色味が実際とは異なってしまうと思います(やや赤紫っぽくなる)。
AliExpress.com Product - Raspberry Pi Camera RPI Focal Adjustable Night Version Camera + Acrylic Holder + IR Light + FFC Cable for Raspberry Pi 2 / 31677円(送料込み)、赤外線LED照明のついた暗視タイプ、5メガピクセル。カメラの種類:
カメラは以前AliExpressから以下のような赤外線カメラを購入しておりました。このタイプはカメラ両脇に赤外線LED照明がついており、それを照射して暗い部屋の中でもその赤外線反射光を頼りに撮影可能になるらしい。ただし、レンズには赤外線フィルターがついていないため、明るい状況下ではやや色味が実際とは異なってしまうと思います(やや赤紫っぽくなる)。
また通常のタイプのものであれば以下。こちらは赤外線フィルターがついているため色味は自然なほうかと。
どうやら5メガピクセルのカメラV1がV2へバージョンアップして8メガピクセルになったようで、以下。
なぜかカメラが国内では高いのですが、試す程度なら730円のV1でもよさそうです。
カメラ用プログラム:カメラ撮影を可能にするプログラム/アプリケーションがいくつかあるようで、ざっと調べた限りでは、・Motion・MJPG-streamer・Picamera(python)があるようです。こちらの記事を参考にすると、Motionは遅延があるようでMJPG-streamerのほうがいいらしい。また、公式ドキュメンテーションを見ると、Picameraが標準インストールされているらしいので、すぐにコマンドを打てば使えるらしい。ということから、MJPG-streamerは後回しにして、まずはPicameraで軽く実験してみることに。
Picamera:公式ドキュメンテーションのカメラ使用については、・RASPBERRY PI CAMERA MODULEというページ(英語)があります。さらに、Picameraのドキュメンテーションとしてはこちらのページに詳しく書かれています。
セッティングと動作チェック:Zero Wでカメラ使用を可能にするための設定として、以下の「Raspberry Piの設定」画面で「カメラ」を「有効」にチェックを入れておきます。そして一度再起動が必要らしいです。このあたりの設定については、ドキュメンテーションのGetting Startedに書いてあります。
尚、パソコンからWifiを通してZero Wを遠隔操作しているため「VNC」も「有効」にしてあります。どうやら、その後「シリアル」も「有効」にしておいたほうがよさそうですが、いまのとこ無効のまま。Picameraを起動する前に、sudo apt-get updatesudo apt-get upgradeをしておいたほうがいいでしょう。数ヶ月使っていなかったため、10分以上アップグレードに時間がかかりました。
Zero Wを再起動後、ターミナルを開いて、
raspistill -o test.jpg
を入力すれば、test.jpgという名前のファイルで静止画を出力(保存)するようです。実際にやってみると、
このように、test.jpgが/home/piディレクトリ内に出力保存されています。これをクリックし写っていればカメラは問題なく機能しているということになります。
プレビュー画面:ビデオ映像をプレビューするには、raspivid -t 10000を入力すれば、10秒間プレビュー画面がでてくるようです。raspivid -t 0であれば、ctrl+cを押してプログラムを終了するまでずっとプレピューとなるようです。しかしながら、VNCを通してプレビューしようとするとプレビュー画面が現れません。これはVNCにプレビュー画面を転送表示する機能がないからのようです。その場合はでネットワーク表示させろと書いてあります。
Webストリーミング:次に本題のWebストリーミングの方法に入っていきたいと思います。ちなみに、これらすべてはVNCを通してMacBook ProからWifi経由で操作しています。いろいろ探してみると、PicameraのドキュメンテーションのAdvanced Recipesに、そのまんま「4.10 Web Streaming」という項目があります。冒頭の説明で、Webを通してのビデオストリーミングは驚くほど複雑だと書いてあります。しかしながら、Pythonによる88行からなるサンプルソースが掲載されています。試しにコピペしてZero W上で確かめてみようと思います。
import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server
PAGE="""\
<html>
<head>
<title>picamera MJPEG streaming demo</title>
</head>
<body>
<h1>PiCamera MJPEG Streaming Demo</h1>
<img height="480" src="stream.mjpg" width="640" />
</body>
</html>
"""
class StreamingOutput(object):
def __init__(self):
self.frame = None
self.buffer = io.BytesIO()
self.condition = Condition()
def write(self, buf):
if buf.startswith(b'\xff\xd8'):
# New frame, copy the existing buffer's content and notify all
# clients it's available
self.buffer.truncate()
with self.condition:
self.frame = self.buffer.getvalue()
self.condition.notify_all()
self.buffer.seek(0)
return self.buffer.write(buf)
class StreamingHandler(server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(301)
self.send_header('Location', '/index.html')
self.end_headers()
elif self.path == '/index.html':
content = PAGE.encode('utf-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(content))
self.end_headers()
self.wfile.write(content)
elif self.path == '/stream.mjpg':
self.send_response(200)
self.send_header('Age', 0)
self.send_header('Cache-Control', 'no-cache, private')
self.send_header('Pragma', 'no-cache')
self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
self.end_headers()
try:
while True:
with output.condition:
output.condition.wait()
frame = output.frame
self.wfile.write(b'--FRAME\r\n')
self.send_header('Content-Type', 'image/jpeg')
self.send_header('Content-Length', len(frame))
self.end_headers()
self.wfile.write(frame)
self.wfile.write(b'\r\n')
except Exception as e:
logging.warning(
'Removed streaming client %s: %s',
self.client_address, str(e))
else:
self.send_error(404)
self.end_headers()
class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
allow_reuse_address = True
daemon_threads = True
with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
output = StreamingOutput()
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8000)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()
とりあえず、デスクトップ上にテキストエディタでwebstream.pyなるファイルをつくって、そこにコピペです。どうやらこれは、Python3用のソースです(このままではPython2.7では動きません)。Zero WにPIXELをインストールしていれば、Python2.7も3も両方入っているので特に問題はありません。
保存したあと、ターミナルを開いて、まずはwebstream.pyのあるDesktopディレクトリへ移動するために、
cd Desktop
そして、Python3でこのスクリプトを起動するのですが、
python3 webstream.py
と打ち込みます。そうすると、特にモニタリング用の画面などは現れずに以下のような画面。
これは、Zero W上のターミナル画面です。サンプルが起動したら、Zero WのIPアドレス:8000/index.htmlに他の端末のブラウザでアクセスするとリアルタイムでの映像を見ることができます。
こんな感じで、193.168.3.2:8000/index.htmlにMacBook ProのChromeでアクセスしてみました。手を揺らしてみましたが、特に目立った遅延などなくスムースに動いています。コードを編集する必要もなく、すぐに動作確認することができました。画面上方の文字が邪魔ですが、コード11行目のHTMLを編集し直せばすぐに消しとることができます。
プログラム終了方法:ctrl+cを押すか、ターミナルを閉じてしまうと終了します。
Zero WのIPアドレス:
ちなみに、Zero WのIPアドレスは、画面右上のWifiアンテナマークにカーソルを重ねると出てきます。
またポート:8000に関しては、コード下から5行目に8000を指定してあるので、変更したい場合はその部分を任意のポートにすればいいと思います。
Python IDLEで起動:
ターミナルが苦手というのであれば、すでにPython 3 (IDLE)がインストールされているので、
コピペしたPythonスクリプトをデスクトップ上で右クリックして、
アプリケーションで開くを選択し、
Python 3(IDLE)を選択すれば、
先程のコードがIDLE上にでてきます。あとはRun Moduleすれば、このプログラムが起動します。
そして、別の端末のブラウザでIPアドレスにアクセスすれば撮影内容を見ることができます。
モバイルバッテリーにつなげばスタンドアロンのIPカメラとして使えるので便利そうです。バッテリーとUSBケーブルの間に電圧電流計をはさんでみましたが、カメラ起動中は約0.2〜0.3A程度の電流になっていました(以下)。
カメラ起動中は、赤いLEDがオンになります。
まとめ:
比較的簡単にWebストリーミングできました。このソースにHTMLの表示内容も書き込んであるので、見た目や配置を変更することも簡単にできそうです。
この実験のあと、Python2.7でも動かせるかどうか確かめてみました。Python3のsocketserverならびにserverモジュールが、python2.7ではSocketServer、SimpleHTTPServerに置き換わるのですが、そのために多少書き直す部分がでてくるので面倒。そのうちやろうと思います。
それから、かなりZero Wのチップが熱くなっていたので、ヒートシンクはつけたほうがよさそうです。