Artefacts ツールキットの Rerun ヘルパー

Artefacts ツールキットの Rerun ヘルパーは、シミュレーション中に Rerun.rrd ファイルを記録し、シミュレータ自体ではなくその記録に対してアサーションを行うテストのための便利な関数を提供します。

これによりテストは独立した 2 つの半分に分かれます。シミュレータ(またはその隣のロガーノード)が記録を行い、テスト自体はその記録に対してアサーションを行うため、アサーションを書くのに ROS やシミュレータの知識は必要ありません。

インポート方法:

from artefacts_toolkit.rerun import recorder, reader, video

関数

関数リファレンス

recorder.get_output_dir

記録やその他のテスト出力を書き込むべきディレクトリを返します(存在しなければ作成します)。

recorder.get_output_dir(
    directory=None
)

パラメータ

パラメータ 説明 デフォルト
directory str または Path デフォルトの代わりに使用するディレクトリを明示的に指定 None

戻り値

Path: 出力ディレクトリの絶対パス。

シミュレータ側とテスト側で同じディレクトリが解決されるため、記録の場所について両者が一致します:

from artefacts_toolkit.rerun import recorder

OUTPUT_FOLDER = recorder.get_output_dir()
rrd_path = OUTPUT_FOLDER / "my-rerun-recording.rrd"

recorder.start_recording

directory/filename に保存される Rerun の記録を開始し、記録ストリームとファイルパスを返します。

recorder.start_recording(
    application_id,
    filename="recording.rrd",
    directory=None,
    handle_sigterm=True
)

パラメータ

パラメータ 説明 デフォルト
application_id str Rerun ビューアに表示されるアプリケーション名 必須
filename str または Path .rrd ファイルの名前。絶対パスも指定可能で、その場合 directory は無視されます "recording.rrd"
directory str または Path 保存先ディレクトリ。get_output_dir と同様に解決されます None
handle_sigterm bool プロセスが SIGTERM を受け取ったときに記録を閉じ、クリーンに終了する True

戻り値

tuple: 以下を含むタプルを返します:

  • recording (rerun.RecordingStream): 開始された記録。グローバルな記録としても設定されるため、通常の rr.log(...)rr.set_time(...) の呼び出しはこの記録に送られます。
  • path (Path): 書き込まれる .rrd ファイルへのパス。

以下の例では、シミュレータはベースの姿勢とジョイスティックのコマンドを sim_time タイムライン上に記録し、ミッション終了後にテストハーネスによって停止されます。rr.set_time で独自のタイムラインを設定しておくことで、テストは後で実時間ではなくシミュレーション時間でデータを揃えられます。

import rerun as rr
import simulator as sim
from artefacts_toolkit.rerun import recorder


class RerunSimulator(sim.SimulatorMujoco):
    def _set_sim_time(self):
        rr.set_time("sim_time", duration=float(self.mujoco_data.time))

    def _log_joy(self, joy):
        self._set_sim_time()
        rr.log("cmd/fwd", rr.Scalars(joy.axes[1]))
        rr.log("cmd/yaw", rr.Scalars(joy.axes[2]))

    def _read_state(self):
        super()._read_state()
        q = self.mujoco_data.qpos  # [0:3] base xyz, [3:7] base quat wxyz
        w, x, y, z = q[3:7]
        self._set_sim_time()
        rr.log("base", rr.Transform3D(translation=q[0:3],
                                      quaternion=rr.Quaternion(xyzw=[x, y, z, w])))
        rr.log("base/yaw", rr.Scalars(yaw_from_quat(w, x, y, z)))


def main():
    robot = sim.Robot(sim.RobotType.Tron2, True)
    robot.init("127.0.0.1")

    # robot.init() の後に呼び出す:SDK がシグナルハンドラをリセットするため
    recorder.start_recording("tron2_loop", "recording-loop.rrd")

    RerunSimulator(...).run()

reader.load

記録を一度メモリに読み込み、ファイルを再読み込みすることなく複数のアサーションで共有できるようにします。

reader.load(
    rrd
)

パラメータ

パラメータ 説明 デフォルト
rrd str または Path .rrd ファイルへのパス 必須

戻り値

ChunkStore: 読み込まれた記録。他のすべての reader および video 関数は、パスの代わりにこれを受け付けます。

from artefacts_toolkit.rerun import reader


@pytest.fixture(scope="module")
def recording(recording_path):
    assert recording_path.exists(), f"Recording not found at {recording_path}"
    return reader.load(recording_path)

reader.get_entity_paths

記録に記録されたすべてのエンティティパスを返します。

reader.get_entity_paths(
    rrd
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須

戻り値

list[str]: エンティティパス。例:["/base", "/base/yaw", "/cmd/fwd"]

assert "/bodies/uwb_tag" in reader.get_entity_paths(recording), "target was never logged"

reader.get_timelines

記録内のタイムライン名を返します。

reader.get_timelines(
    rrd
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須

戻り値

list[str]: タイムライン名。例:["log_time", "sim_time"]


reader.get_columns

記録のデータ列名を返します。オプションで 1 つのエンティティの列のみに絞り込めます。

reader.get_columns(
    rrd,
    entity=None
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
entity str このエンティティの列のみを一覧する None

戻り値

list[str]: "/entity:Archetype:field" 形式の列名。

>>> reader.get_columns(recording, "/base")
['/base:Transform3D:quaternion', '/base:Transform3D:translation']

reader.get_column

1 つの列の時刻と値を、タイムラインで並べ替えて返します。

reader.get_column(
    rrd,
    column,
    timeline=None
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
column str 列名。例:"/base/yaw:Scalars:scalars" 必須
timeline str 並べ替えに使うタイムライン。デフォルトは記録固有のタイムライン(log_time 以外の最初のもの)、なければ log_time None

戻り値

tuple: 以下を含むタプルを返します:

  • times (numpy.ndarray): duration タイムラインでは秒(float)、timestamp タイムラインでは datetime64、sequence タイムラインでは整数。
  • values (numpy.ndarray): スカラーでは形状 (n,)、3 次元ベクトルでは (n, 3)。行ごとにインスタンス数が異なる場合(点群など)は、行ごとの配列を格納したオブジェクト配列。

times, yaw = reader.get_column(recording, "/base/yaw:Scalars:scalars")
assert times[-1] > 60.0, "mission ended early"
assert np.abs(yaw).max() <= np.pi

reader.get_final_message

列に最後に記録された値、または静的な列の値を取得します。

reader.get_final_message(
    rrd,
    column
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
column str 列名。例:"/base:Transform3D:translation" 必須

戻り値

Any: アンラップされた値。Scalars では float、平行移動では長さ 3 の配列、複数点の Points3D では (n, 3) の配列。

ウェイポイントは Points3D として一度だけ(静的に)記録され、テストはそれらが描く軌道からロボットがどれだけ逸れたかを測定します:

wp_col = "/network/mission_waypoints:Points3D:positions"
assert wp_col in reader.get_columns(recording), f"Could not find {wp_col} in recording"

track = np.vstack(reader.get_final_message(recording, wp_col))[:, :2]  # N x 2

reader.get_message_count

エンティティに対して行われたログ呼び出しの回数を返します。

reader.get_message_count(
    rrd,
    entity
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
entity str エンティティパス。例:"/cam" 必須

戻り値

int: エンティティに記録された(静的でない)行数。存在しない場合は 0

assert reader.get_message_count(recording, "/cam") >= 100, "camera stopped publishing"

reader.to_dataframe

記録を pandas の DataFrame として返します。選択したタイムライン上の時刻ごとに 1 行です。

異なるエンティティは通常、異なるレートで記録されます。姿勢はシミュレーションステップごと、ジョイスティックのコマンドは変化したときだけ、といった具合です。step を指定すると、選択したすべてのエンティティが 1 つの均一な時間グリッドにリサンプリングされ、fill_latest_at により欠損はそれ以前の最新の値で埋められます。そのため疎なコマンドトピックが密な姿勢ストリームと行ごとに揃い、両者を直接比較できます。

reader.to_dataframe(
    rrd,
    contents=None,
    index=None,
    step=None,
    fill_latest_at=True
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
contents str または list[str] 含めるエンティティ:1 つのエンティティなら "/base"、サブツリーなら "/base/**"、またはそれらのリスト。None の場合はすべてのエンティティ None
index str 行インデックスとして使うタイムライン。デフォルトは get_column と同様 None
step float 最初から最後の時刻まで、この秒数間隔の均一なグリッドにリサンプリングする None
fill_latest_at bool 各列の欠損を、それ以前の最新の値で埋める True

戻り値

pandas.DataFrame: タイムライン列が最初に来て(duration タイムラインでは秒の float)、その後に "/entity:Archetype:field" ごとの列が続きます。静的データはすべての行に結合されます。Scalars 列は通常の float64(何も記録されていない箇所は NaN)、ベクトルは配列です。

@pytest.fixture(scope="module")
def df(recording_path):
    """Whole recording resampled onto a uniform 20 ms sim-time grid."""
    frame = reader.to_dataframe(recording_path, ["/base/**", "/cmd/**"], index="sim_time", step=0.02)

    out = frame.dropna(subset=["/base:Transform3D:translation"]).reset_index(drop=True)
    out["x"] = [t[0] for t in out["/base:Transform3D:translation"]]
    out["y"] = [t[1] for t in out["/base:Transform3D:translation"]]
    out["yaw"] = out["/base/yaw:Scalars:scalars"]
    out["cmd_fwd"] = out["/cmd/fwd:Scalars:scalars"].fillna(0.0)
    return out


def test_straights_cover_side_length(df, straights):
    for i, (a, b) in enumerate(straights):
        dist = math.hypot(df["x"][b - 1] - df["x"][a], df["y"][b - 1] - df["y"][a])
        assert abs(dist - SIDE_LENGTH) < DIST_TOLERANCE

video.extract_video

記録内のカメラエンティティから MP4 動画を作成します。

video.extract_video(
    rrd,
    entity,
    output_path,
    timeline=None,
    frame_rate=20
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
entity str カメラのエンティティパス。例:"/cam" 必須
output_path str または Path 動画の保存先パス(.mp4) 必須
timeline str フレームの並べ替えに使うタイムライン。デフォルトは get_column と同様 None
frame_rate int rr.Image フレームをエンコードする際のフレームレート 20

戻り値

Path: 保存された動画のパス。

from artefacts_toolkit.rerun import recorder, video

video.extract_video(recording, "/cam", recorder.get_output_dir() / "head_camera.mp4")

video.extract_camera_image

カメラエンティティに最後に記録された画像を PNG として保存します。

video.extract_camera_image(
    rrd,
    entity,
    output_dir="output"
)

パラメータ

パラメータ 説明 デフォルト
rrd strPath または ChunkStore 記録へのパス、または読み込み済みの記録 必須
entity str カメラのエンティティパス。例:"/cam" 必須
output_dir str または Path 抽出した画像の保存先ディレクトリ "output"

戻り値

Path: 保存された画像のパス。output_dir/<entity>.last.png で、/_ に置き換えられます(例:output/_cam.last.png)。

video.extract_camera_image(recording, "/cam", recorder.get_output_dir())
最終更新 18.09.2026: Update cli reference (#153) (c83f1b9)