あっきぃ日誌

鉄道ブログのような技術系ブログのようななにか

Picamera2ライブラリのMJPEGストリーミングサーバーをためす

11日目です。

adventar.org

Picamera2ライブラリのMJPEGストリーミングサーバーをためす

昨日のメダカメラを触っていて思い出しましたが、メダカメラで使っているPicameraライブラリのMJPEGストリーミングサーバーをPicamera2に移行できるかの調査をしたいのでした。

おさらいをすると、Raspberry Pi OS Bullseyeではカメラ関係の技術スタックがlibcameraに移行した影響で、Picameraライブラリはlibcameraとは組み合わせ不可になりました(技術スタックをLegacyに戻すことは可能)。

akkiesoft.hatenablog.jp

libcameraに対応したPicamera2がRaspberry Pi公式で開発されることになり、2月にはプレビューリリースが、直近では0.3.7のベータリリース6まで開発が進んでいます。

akkiesoft.hatenablog.jp

Picamera2は大量のサンプルが用意されており、Picameraからの移行を想定してか、同じ用途のサンプルも整備されています。

github.com

サンプルを見比べる

Picamera2のMJPEGストリーミングサーバーのスクリプトはこちら。

github.com

Picameraのスクリプトはこちら。

github.com

Picamera2のスクリプトはPicameraのものをベースに開発されているらしく、パット見の差分は結構少ないです。実際のdiffも量は多いものの、いくつかの処理がなくなったりインデントが上がったりしている程度に見えます。

--- /Users/akkie/Desktop/web_streaming.py	2022-12-11 12:54:14
+++ /Users/akkie/Desktop/mjpeg_server.py	2022-12-11 12:54:12
@@ -1,39 +1,43 @@
+#!/usr/bin/python3
+
+# Mostly copied from https://picamera.readthedocs.io/en/release-1.13/recipes2.html
+# Run this script, then point a web browser at http:<this-ip-address>:8000
+# Note: needs simplejpeg to be installed (pip3 install simplejpeg).
+
 import io
-import picamera
 import logging
 import socketserver
-from threading import Condition
 from http import server
+from threading import Condition, Thread
 
-PAGE="""\
+from picamera2 import Picamera2
+from picamera2.encoders import JpegEncoder
+from picamera2.outputs import FileOutput
+
+PAGE = """\
 <html>
 <head>
-<title>picamera MJPEG streaming demo</title>
+<title>picamera2 MJPEG streaming demo</title>
 </head>
 <body>
-<h1>PiCamera MJPEG Streaming Demo</h1>
+<h1>Picamera2 MJPEG Streaming Demo</h1>
 <img src="stream.mjpg" width="640" height="480" />
 </body>
 </html>
 """
 
# ベースになるクラスが変わった模様
-class StreamingOutput(object):
+
+class StreamingOutput(io.BufferedIOBase):
     def __init__(self):
         self.frame = None
-        self.buffer = io.BytesIO()
         self.condition = Condition()
 
     def write(self, buf):
# 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)
+        with self.condition:
+            self.frame = buf
+            self.condition.notify_all()
 
+
 class StreamingHandler(server.BaseHTTPRequestHandler):
     def do_GET(self):
         if self.path == '/':
@@ -73,16 +77,20 @@
             self.send_error(404)
             self.end_headers()
 
+
 class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
     allow_reuse_address = True
     daemon_threads = True
 
# withぶんを使わないでインデントを上げたみたいなノリ
-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()
+
+picam2 = Picamera2()
+picam2.configure(picam2.create_video_configuration(main={"size": (640, 480)}))
+output = StreamingOutput()
+picam2.start_recording(JpegEncoder(), FileOutput(output))
+
+try:
+    address = ('', 8000)
+    server = StreamingServer(address, StreamingHandler)
+    server.serve_forever()
+finally:
+    picam2.stop_recording()

picamera2のフレームレートのデフォルト値は見てませんが、picameraでframerate=24を指定していたところをpicamera2で実装する場合は次のようになります。

picam2.configure(picam2.create_video_configuration(main={"size": (640, 480)},controll={"FrameRate": 24.0}))

以上から、スクリプトの移行自体はわりと苦労せずにサクっとできそうです。

動かしてみると……

メダカメラのスクリプトをPicamera2用に変更したバージョンを作って、Bullseye環境で実際に動かしてみました。現在のメダカメラはPi3Bで動かしているので、Bullseye環境も同様にPi3Bを使いました。動作自体はごく普通に、カメラの映像がMJPEGで配信されていて問題はなさそうでしたが、ふと気になってtopコマンドで負荷を覗くと、CPU使用率に差があることに気が付きました。

左のmedaka2.localが本番用、右のmedaka.localがテスト環境です。以下はまだだれもストリーミング画像を開いていない状態。左のPicamera環境が控えめなのに対して、右のPicamera2環境の負荷が高めなのがわかります。おそらく左環境ではGPU側で処理できているのに対して、右側はCPUで処理しているからではないかと推測します。

次にストリーミング画像を開いて少し置いた状態。左も右も少し負荷が上がりましたが、差はとくに変わらない気がしました。また、負荷に応じてCPUの温度が上昇しているのもわかります。なお、左の環境はヒートシンクなし、右の環境はヒートシンクありで、環境差がある点に注意してください。


うーん、まだ待ったほうが良い?

Pi3BやPi4Bで動かすぶんにはCPUで処理しても問題ないと判断されているのか、どこかしらの処理でlibcameraからGPUを使うための実装がまだなのか。いまいち想像がつきませんが、どちらにせよせっかく今までGPU側で処理できていたっぽい部分がCPU側で処理されているのは微妙な印象です。

じつは昨日までPiZero Wでメダカメラを動かしていて、若干カクつくので3Bに入れ替えたのですが、その浮いたPiZero WでPicamera2環境を動かしたら、だいぶ厳しい感じでした。映像も2秒遅れとかガックガクとかで、若干カクつくどころではありませんでした。ぐぬぬ

今後の開発でGPU処理に変われば良いでしょうが、そうでなければちょっと厳しいかなあという印象です。ディスプレイ出力周りの変更もそうですが、プロプライエタリな実装をオープンな実装に移行することが必ずしも便利になるばかりではないみたいなものを感じますね……。

まとめ

MJPEGストリーミングサーバーのスクリプトの移行自体は簡単でしたが、動かしてみると負荷の面でちょっと不安がありそうでした。今は無理をせずLegacyに切り替えてPicameraの利用を継続しつつ、動向をウォッチしておこうかなあと思います。Issueで質問とかしたほうが良い気もしつつ、それは面倒なのでやめておきたい(へたれ)

メダカメラ with イルミネーション

10日目です。

adventar.org

最近ちょくちょくラズパイ入荷してる

本当に欲しくてウォッチしている方はご存知かもしれませんが、ここ数日rpilocatorのinstockに入荷がちょくちょく流れてきています。Pimoroniでは立て続けに3A+が入荷したり、3B+も久々に入荷していました。また、Seeed StudioではCM4が、AdafruitでもPi4 2GB RAMが入荷しているようでした。いずれも日本への発送に一応対応しているため、海外から買ってでもという方はrpilocatorをTwitterもしくはMastodonでフォローしておくと良いでしょう。

何らかの理由で日本からじゃないと買えない方も、先週はKSYさんでPi4 8GBが入荷して半日くらいは在庫がもっていたようです。日本のリセラーはrpilocatorに入ってないので難儀ですね。スイッチサイエンスは一時期入ってましたが秋頃に消えてしまいました。Twitterフォローとか入荷通知とかでウォッチしていくとようでしょう。

なぜか頑なにAmazonで買えるチャンスを狙っている方や、ラズパイがほしいけど買えない(なお回転寿司のレーンでたくさん並んではじめて手に取るかどうか迷う程度のモチベーション)勢のことは知りません……。

Amazonガチ勢は不思議なことに結構います。一生転売ヤー価格を見てため息をついているつもりなんでしょうか。

あと、入荷してから脳内ガジェット予算会議とか家庭内稟議を通し始めるとかいうのも非常にまずいです。決裁が通った頃には手遅れになっているみたいな、日本企業のとろくささをご家庭で再現する必要はありますか。「Pi4用」と書いた封筒に1〜1.5万円(希望のRAMサイズによります)を入れ、入荷通知を受け取ってすぐに迷いなく買える体勢を整えましょう。

メダカメラ with イルミネーション

あまりAdvent Calendarのことを考えてやったネタではなかったんですが、メダカメラにイルミネーション制御機能を実装しました。16時〜24時まで、好き勝手にオンオフできます。

shrimp.marokun.net


なにこれ

ダイソーで見つけたイルミネーションをなんとなく買ってみました。片方は前からあったような乾電池を2本入れて動くタイプでカラー&点滅バージョン、もう片方はUSB電源で動く白&常時点灯バージョンを選んでみました。世の中なんでもUSB電源やん……。

で、人力で点灯させるの面倒だなあと思ってしばらく机の片隅に放置していたのを、昨日になってようやく遊ぶ気になったので、Advent Calendarにはならないけども、こないだ玄関に実装したリモート操作可能なSeeed XIAOあたりで制御しようかと適当に準備をしました。

USB電源タイプの方は5Vといいつつ適当な3Vとか3V3を食わせても普通に点灯したので、もうこれでいいや(雑)という感じにしつつ、USBコネクターは切り落とさずに、余り物の部材にあったUSBのメスコネクターをピンヘッダーケーブルに変換してGPIOにさせるようにしてしまいました。

乾電池タイプの方はもう少しまともに、フォトカプラーを使って制御します。そもそもこの話は、フォトカプラーなケーブルを1本作って、なんとなく構想してる別のクソ工作ですぐ使えるように準備したところから脱線しています。今日のネタは本当になにもプランがない。たぶんUSBの方もフォトカプラー使ったほうが良い気がするけど、動いてるからもう良いです。


リモートイルミネーション v1

で、出来上がったそれぞれをPicoの3VでそれぞれLチカして動くのを確認したあと、どこに設置するか迷って、なんとなく壁がけラズピッピに実装することにしました。配線がきたない。

あとはリモート制御できるようにAPIを整備します。7色電子ペーパーの書き換え機能がすでにAPIとしてインターネットに晒してあるので、ここにイルミネーションオンオフAPIを追加実装しました。5分くらいで書き終えたらSNSに晒します。するとどうでしょう、イルミネーションが勝手についたり消えたりしています!やったぜ。

social.mikutter.hachune.net

当たり前なんだよな

激しく点灯・消灯してもらうためにTwitterにもAPIを晒していたところ、YouTube配信はありませんか?という生ぬるい質問が飛んできました。こちとらイギリスでEjectZeroを起動して、実際の動作のようすは一切見せぬまま現地では夜の日本の人らに(☝ ՞ਊ ՞)☝ウイーンボタンを連打させまくったマンだぞ!イルミネーションの実際の様子も当然非公開よ。

……という気持ちでいましたが、よく考えたら自宅にはメダカの配信環境がありましたわ。メダカメラにイルミネーションを付けたら良いんでしたわ。

リモートイルミネーション v2

イルミネーションをメダカメラ用ラズピッピに差し替えて、APIも実装し直し。ついでにメダカメラのWebページに冒頭のようなボタンと、制御可能な時間の制限をつけて完成。これでメダカとイルミネーション両方が楽しめるクソ工作ができあがり、ついでに今日のカレンダーネタになりました。

ストリーミング映像でもキラキラきれいに光ってるのが確認できますね。よかった。


まとめ

ラズピッピ入荷近況しか有益な話してないわこの記事

Raspberry Pi 4のEEPROMを更新していますか2022

Raspberry Pi Advent Calendar 2022の9日目です。たのむ俺にはもうネタがない。誰か参加して助けてほしい(◞‸◟)

adventar.org

Raspberry Pi 4のEEPROMとは

去年のを参照。つまり同じことをこれからやります。

akkiesoft.hatenablog.jp

2022年のEEPROM更新

今年も諸々リリースされましたが皆さんは気づいたでしょうか。Raspberry Pi OSを普通に使っているぶんには全く気づかないと思いますが、それでも今年は大型の新機能である、ネットワークインストーラーの登場によって、LANさえあればいちおう母艦PCが無くてもOSのインストールが可能になりました。春頃のLTの資料を再掲。

speakerdeck.com

インストーラーの機能も4月末頃にはDEFAULTリリースとなり、最近やっとPi4が買えた!という方なら最初からこれが使えるバージョンになっていることでしょう。

他にも診断画面の機能拡充、NETCONSOLEの出力周りの修正、NVMeの停止処理の最適化、など、たぶんあんまお世話にならないけどこまごました修正がStableリリースされ、それらが最近になって2022-12-01版としてDEFAULTリリースされています。

が、その後、Pi 4 Rev1.1で発生する、SDの電圧が再起動時にPMICによってリセットされない不具合が修正されて、2022-12-07版としてBeta/Stable/DEFAULTぜんぶでリリースされています。ドタバタじゃん。なお、Pi 4 Rev1.1は日本で発売される前のリビジョンなので、国内で該当する人はおそらくほとんどいないんじゃないかと思いますね。しらんけど。

アップデートするには

Raspberry Pi OS Bullseyeを使用している人であれば、OSを最新にして2回再起動すれば適用されるはずです。rpi-eeprom-updateコマンドを実行して、CURRENTが2022年12月7日になっていれば完了です。Busterの場合はパッケージ更新が3月くらいで途絶えてしまっているようなので、Raspberry Pi ImagerでEEPROM更新用のSDカードを作って適用するか、最新のBullseye環境を一時的に動かせば適用されるでしょう。

まとめ

最近の修正はおもにCM4とかSecureBootをつかいたい人というかどっちかと言うと産業を向いたような更新が多いですが、ネットワークインストーラやバグ修正など、不通のユーザーにも影響する更新もあるので、年に一回くらいは更新されているか見てみると良いかもしれませんね。

ところで、なんかわたしネットワークインストーラほぼ全く使ってないな……PCが目の前にあるからネットワークからインストーラーを起動するのが面倒みたいなところがあり……(汗)

Pimoroni Pico Breakout Garden Packのスタンド新バージョン

Raspberry Pi Advent Calendar 2022の8日目ですが、ネタがしょぼくなっていく…!!

adventar.org

Pimoroni Pico Breakout Garden Packのスタンド新バージョン

去年作ったスタンドのやつの続きです。

akkiesoft.hatenablog.jp

ちょっと前にPico Breakout Garden Packの2つ目をポチったので、それ用のケースを作ったのですが、前回よりもフィラメントとプリント時間を減らすために、高さを下げて、側面の壁も削減してみました。が、思いつきで目分量的な値変更をして会社でぶっつけプリントをして自宅に持ち帰ったら、USBポートが干渉したりなんだりで失敗気味でした。。あと、USBの口のところが微妙に右側にずれてて、それもなかなかのアレ。

結局底面をホットナイフで切り落として事なきを得まして、これで先週末のOMMF2022に持ち込みました。

元のやつとの比較。USBケーブルの位置がだいぶ際どく、コネクターがでかいやつを使ったらたぶんダメそうな感じですね。

諸々の失敗を踏まえて、今日の記事ネタ用に修正。USB口の左右ズレ修正、高さを少し増やしてUSBの床面への干渉を防止、ホットナイフで切り落とした部分の図面への反映をしました。いい感じ。

昨日ワクチンを打って出社できないので、このデータもまたぶっつけですが、たぶん大丈夫なのでThingiverseにアップロードしました。

www.thingiverse.com

元のバージョンも、USBの差込口のズレを修正したものをThingiverseに上げました。

www.thingiverse.com

Pimoroni Pico Breakout Garden PackはPicoHと組み合わせれば完全にはんだ付け無しで遊べて、見た目もおしゃれにまとまるので、もっと広まって欲しいなーとおもう一品です。

いまさらだけど壁スイッチをIoT(笑)したい(Seeed XIAO ESP32C3編)

昨日の続き。ただし、Seeed XIAO ESP32C3を使うためRaspberry Pi Advent Calendarではないです。

Seeed XIAOは、Seeed Studioが展開する親指サイズ系開発ボードです。今回触れるESP32C3のほかにBLE版とか、RP2040搭載版とか、いろいろなモデルが展開されています。価格も1,000円前後で、けっこう手頃です。

ESP32C3はスイッチサイエンスだと1,034円ですね。

Seeed Studio XIAO ESP32C3www.switch-science.com

昨日書いたPico Wireless環境は、アレでもまあ良いんですが、やはりかさばったり消費電力が気になったりするので、どうせならESP32単体に置き換えたいところです。で、手持ちに多少ESP32があるのでほじくり返してみますが、あ、う〜ん……。

USBシリアル変換のまともなやつがなくて、どっちもまともに動かせないので、こないだアキバでなんとなく買ってみたSeeed XIAO ESP32C3を使うことにします。これならUSBシリアルなしでも使えるので手軽。

ESP32として使えつつ、小さくて、アンテナが外付けタイプで、無線とBLE両方に対応みたいです。ARMではなくRISC-Vベースなのもポイントでしょうか。そんな感じのやつです。Micro/CircuitどっちのPythonも動いたり、Arduinoで動かしてもヨシみたいです。ただ、CircuitPythonはUSBストレージ的な見え方はしないので、その点は注意がいりそうですが、Thonnyとかを使っていれば問題はないでしょう。

WebサーバーをやりつつAPI的に受けてサーボを動かすやつが作れれば何でも良いですが、いい感じのサンプルが揃っていたArduinoで今回はやっていくことにしました。サーボ部分を足して、なんかついでにチップ温度が取れたのでそれもAPIにしてこうじゃ。

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESP32Servo.h>

const char* ssid = "";
const char* password = "";

WebServer server(80);
Servo servo1;

void handleRoot() {
  server.send(200, "text/plain", "entrance room light\nusage: GET /off\nGET /temp");
}
void handleOff() {
  servo1.write(50);
  delay(500);
  servo1.write(90);
  server.send(200, "text/plain", "ok");
}
void handleTemp() {
  float temp = temperatureRead();
  char msg[10];
  sprintf(msg, "%f", temp);
  server.send(200, "text/plain", msg);
}

void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/off", handleOff);
  server.on("/temp", handleTemp);
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");

  servo1.setPeriodHertz(50);
  servo1.attach(D0);
  servo1.write(90);
}

void loop(void) {
  server.handleClient();
  delay(2);
}

あとはピンヘッダーをはんだ付けして、諸々配線とか貼り付けとかをして、完成(雑)。ピンヘッダーの向きが逆なんですけど、基板が片面実装でつるつるのメンからピンが生えたらなんかもったいないなあと思いまして。この向きでもちょっとピンがでるけど、それでも壁とかに貼りやすいですし。

動作はPico Wirelessのときと同様なので動画は略。消費電力は基本的に0.02Aくらい、Wi-Fiアクセスがあると0.08Aくらいに跳ねる瞬間があるようです。PicoWとほぼ同じっぽくて良さそうですね。

www.raspi.jp

あとは呼び出ししやすいようにMac/iOSのショートカットでGETするだけのショートカットを作成して終わりです。1つ作ればデバイス間で共有されるので、MacからでもApple WatchからでもiPhoneからでも手軽に電気が切れますね。


まとめ

PicoWが待てないのでSeeed XIAO ESP32C3で実装する話でした。小さくて色々遊べそうなので、PicoWはPicoWとして、XIAO ESP32C3ももう一個くらい買ってみても良いかも。

小さいボードと言えば、我らがPimoroniのTiny2040がありますが、XIAO RP2040のほうが片面実装でスッキリしていて国内でも若干XIAOのほうが安いので、覇権の香りがしてます。ぐぬぬ。。