actfw の使い方

このチュートリアルは ActcastOS 2 以前のバージョンを対象としています。

ActcastOS 1 及び ActcastOS 2 は 2024 年末でサポートが終了します。ActcastOS 3 に対応させるためには ActcastOS 3 Migration ガイド を参照してください。

actfw の使い方 #

ここでは actfw の使い方について説明します。

ライブラリ構成 #

actfw として次の 2 つが提供されています。

actfwのインストール #

actfw-*は pip によりインストールすることが出来ます。 actdk initにより生成されたdependencies.jsonではactfw-raspberrypiがデフォルトでインストールされるようになっています。

actfwの計算モデル #

actfwは非同期計算を記述するための Idein 提供ライブラリです。 このライブラリ上のタスクは「このタスクが行われたとき実行する次のタスク」を登録するモデルになっており、データの流れをconnectメソッドで定義します。

actfwのモデル

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-raspberrypiDisplayオブジェクトを用いると 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')

actfw に戻る