このチュートリアルは ActcastOS 2 以前のバージョンを対象としています。
ActcastOS 1 及び ActcastOS 2 は 2024 年末でサポートが終了します。ActcastOS 3 に対応させるためには ActcastOS 3 Migration ガイド を参照してください。
actfw の使い方 #
ここでは actfw
の使い方について説明します。
ライブラリ構成 #
actfw
として次の 2 つが提供されています。
actfw-core
: Actcast アプリに共通する中心機能actfw-raspberrypi
: Raspberry Pi における開発に有用な機能
actfw
のインストール
#
actfw-*
は pip によりインストールすることが出来ます。
actdk init
により生成されたdependencies.json
ではactfw-raspberrypi
がデフォルトでインストールされるようになっています。
actfw
の計算モデル
#
actfw
は非同期計算を記述するための Idein 提供ライブラリです。
このライブラリ上のタスクは「このタスクが行われたとき実行する次のタスク」を登録するモデルになっており、データの流れをconnect
メソッドで定義します。
Application
クラスが全体をコントロールします。
app = actfw_core.Application()
app.run()
非同期に実行するタスクは以下の 3 つに分けられます。
Producer
: リソースを生成し続ける処理Pipe
: リソースを変形して次のタスクに渡す処理Consumer
: リソースを最終的に消費し続ける処理
例えば、次に示す 3 つのタスクがあるとします。
- カメラから画像を取得(
Producer
)- RGB 画像を撮影し続ける処理
- 取得画像を白黒にコンバート(
Pipe
)- RGB 画像をグレイスケール画像に変換する処理
- 画面に描画(
Consumer
)
これらは以下のように記述できます。
import actfw_core
from actfw_core.task import Pipe, Consumer
from actfw_core.capture import V4LCameraCapture
import actfw_raspberrypi
from actfw_raspberrypi.vc4 import Display
class Converter(Pipe):
...
class Presenter(Consumer):
...
app = actfw_core.Application()
# Capture task
cap = V4LCameraCapture('/dev/video0', (CAPTURE_WIDTH, CAPTURE_HEIGHT), CAPTURE_FRAMERATE, format_selector=V4LCameraCapture.FormatSelector.PROPER)
app.register_task(cap)
# Converter task
conv = Converter()
app.register_task(conv)
# Presentation task
pres = Presenter(settings, camera, cmd)
app.register_task(pres)
# Make task connection
cap.connect(conv) # from `cap` to `conv`
conv.connect(pres) # from `conv` to `pres`
app.run()
この例はGetting Started の grayscale exampleを簡略化したものです。
カメラの設定 #
Raspberry Pi で利用可能なカメラは 2 種類に分けられます。
- Official Raspberry Pi Camera Module
- 一般的な USB カメラ
両者でカメラを取り扱うためインターフェイスが異なることに注意ください。
actfw-raspberrypi.capture.PiCameraCapture
は前者のみを、
actfw-core.capture.V4LCameraCapture
は両者をサポートします。
本チュートリアルでは後者を用います。
actfw
では以下のようにV4LCameraCapture
を作ることで、カメラ画像を取得する Producer タスクを生成することが出来ます。
app = actfw_core.Application()
# Capture task
cap = V4LCameraCapture('/dev/video0', (CAPTURE_WIDTH, CAPTURE_HEIGHT), CAPTURE_FRAMERATE, format_selector=V4LCameraCapture.FormatSelector.PROPER)
app.register_task(cap)
Actcast デバイスエージェントとの通信 #
Actcast デバイスエージェントがアプリケーションに対して要求を行います。
この要求は UNIX Domain Socket を介して行われます。
Actcast デバイスエージェントは環境変数ACTCAST_COMMAND_SOCK
を経由してアプリケーションにパスを渡します。
アプリケーションは環境変数ACTCAST_COMMAND_SOCK
が表すパスに UNIX Domain Socket を作成、待ち受けを行う必要があります。
Actcast デバイスエージョントはこの UNIX Domain Socket に接続してアプリケーションに要求を行うので、アプリケーション側はこの要求を処理して応答する必要があります。
main
にはそのアプリケーションが行う任意の処理を記述することが出来ますが、以下の項目に対応することを強く推奨しています。
- 外部コマンド
- 現在は TakePhoto コマンドのみ
カメラを用いるアプリケーションは設置確認のため、TakePhoto コマンド要求に対応することを推奨します。
actfw では以下のようにCommandServer
クラスを用いることで簡単に実装することが出来ます。
CommandServer
は内部的に画像を保持し、画像送信要求を受け取ると内部的に保持していた画像を返します。
そのためupdate_image
メソッドで要求を受け取ったときに返す画像をアップデートする必要があります。
class Presenter(Consumer):
def __init__(self, ..., cmd):
self.cmd = cmd
def proc(self, ...):
...
self.cmd.update_image(rgb_image) # update `Take Photo` image
...
# Actcast application
app = actfw_core.Application()
# CommandServer (for `Take Photo` command)
cmd = actfw_core.CommandServer()
app.register_task(cmd)
# Presentation task
pres = Presenter(..., cmd)
app.register_task(cmd)
アプリケーションの死活監視 #
アプリケーションが連続的に画像を処理できていないなど何らかの不健全な状態にあると判断されたとき、Actcast デバイスエージェントはアプリケーションを再起動します。
アプリケーションの健全性の判定にはhealthchecker
が利用されます。
以下は、healthchecker
の実装例です。
/root/heartbeat
ファイルの更新が 1 分以内に起きていることを確認するスクリプトとなっています。
#!/bin/bash
HEARTBEAT_FILE='/root/heartbeat'
[ "$(find "${HEARTBEAT_FILE}" -mmin -1)" == "${HEARTBEAT_FILE}" ]
healthchecker
に対応するように、main
側ではactfw_core.heartbeat()
によって/root/heartbeat
ファイルを一定間隔で更新します。
class Presenter(Consumer):
def __init__(self):
...
def proc(self, image):
# update image here
actfw_core.heartbeat()
もし、以前のタスクが止まったりすると、この箇所で短い間隔で行われている筈の更新が行われなくなり、healthchecker
に検知されることになります。
画面の描画 #
一般的に Actcast アプリケーションは高度なセンシングのためのアプリケーションであることがほとんどであるため、画面の描画も必要無いことが多いです。
しかし、デモや動作確認のために画面に計算結果を描画できると便利な場合があります。
actfw-raspberrypi
のDisplay
オブジェクトを用いると HDMI で接続されたディスプレイに RGB 画像を表示することが出来ます。
以下の例は画面に RGB 画像を表示するだけのPresenter
タスクの例です。
画面描画を行うだけのPresenter
タスクの例です。Presenter
は、データを消費だけして次のタスクに渡さないConsumer
として作成しているので,proc
メソッドに返り値は不要です.
この例では、HDMI ディスプレイを640x480
の領域として扱い、
320x240
の RGB 画像を(0, 0, 640, 480)
の矩形領域に拡大描画しています。
class Presenter(Consumer):
def __init__(self, camera):
self.display = Display(camera, (640, 480))
def proc(self, image):
self.display.update((0, 0, 640, 480), image.tobytes(), (320, 240), 'rgb')