API仕様¶
📋 概要¶
Crane Visualizerパネルは、ROSメッセージングシステムを通じてSVGベースの可視化データを受信・処理します。この文書では、サポートするメッセージ形式、データ構造、および統合のためのAPI仕様を詳細に説明します。
📡 ROSトピック仕様¶
1. スナップショットトピック¶
トピック情報¶
- デフォルト名:
/aggregated_svgs - メッセージ型:
SvgLayerArray - 推奨頻度: 0.1-0.33 Hz(3-10秒間隔)
- 用途: 完全な状態情報の定期配信
メッセージ構造¶
# SvgLayerArray.msg
svg_primitive_arrays: SvgPrimitiveArray[]
---
# SvgPrimitiveArray.msg
string layer # 階層パス(例: "field/center_circle")
string[] svg_primitives # SVG要素の配列
実装例¶
# Python (rospy)
from your_msgs.msg import SvgLayerArray, SvgPrimitiveArray
def publish_snapshot():
msg = SvgLayerArray()
# フィールド境界線
field_layer = SvgPrimitiveArray()
field_layer.layer = "field/boundary"
field_layer.svg_primitives = [
"<rect x='-4500' y='-3000' width='9000' height='6000' stroke='white' fill='none'/>",
"<circle cx='0' cy='0' r='500' stroke='white' fill='none'/>"
]
# ロボット位置
robots_layer = SvgPrimitiveArray()
robots_layer.layer = "robots/team_blue"
robots_layer.svg_primitives = [
"<circle cx='1000' cy='500' r='90' fill='blue'/>",
"<circle cx='1500' cy='-200' r='90' fill='blue'/>"
]
msg.svg_primitive_arrays = [field_layer, robots_layer]
snapshot_pub.publish(msg)
// C++ (roscpp)
#include "your_msgs/SvgLayerArray.h"
void publishSnapshot() {
your_msgs::SvgLayerArray msg;
// フィールドレイヤー
your_msgs::SvgPrimitiveArray field_layer;
field_layer.layer = "field/boundary";
field_layer.svg_primitives.push_back(
"<rect x='-4500' y='-3000' width='9000' height='6000' stroke='white' fill='none'/>"
);
// ロボットレイヤー
your_msgs::SvgPrimitiveArray robots_layer;
robots_layer.layer = "robots/team_blue";
robots_layer.svg_primitives.push_back(
"<circle cx='1000' cy='500' r='90' fill='blue'/>"
);
msg.svg_primitive_arrays.push_back(field_layer);
msg.svg_primitive_arrays.push_back(robots_layer);
snapshot_pub_.publish(msg);
}
2. 更新トピック¶
トピック情報¶
- デフォルト名:
/visualizer_svgs - メッセージ型:
SvgUpdateArray - 推奨頻度: 10-50 Hz
- 用途: レイヤー単位の増分更新
メッセージ構造¶
# SvgUpdateArray.msg
updates: SvgLayerUpdate[]
---
# SvgLayerUpdate.msg
string layer # 対象レイヤーの階層パス
string operation # "append", "replace", "clear"
string[] svg_primitives # 操作対象のSVG要素
操作種別の詳細¶
append操作¶
既存のSVG要素に新要素を追加
# 軌跡ポイントの追加例
update = SvgLayerUpdate()
update.layer = "trajectories/robot_0"
update.operation = "append"
update.svg_primitives = [
"<circle cx='1200' cy='600' r='3' fill='red'/>"
]
replace操作¶
レイヤー全体を新しい内容で置換
# ロボット位置の更新例
update = SvgLayerUpdate()
update.layer = "robots/team_blue/robot_0"
update.operation = "replace"
update.svg_primitives = [
"<circle cx='1300' cy='700' r='90' fill='blue'/>",
"<line x1='1300' y1='700' x2='1400' y2='700' stroke='darkblue'/>"
]
clear操作¶
レイヤーの全要素を削除
# 古い軌跡のクリア例
update = SvgLayerUpdate()
update.layer = "debug/old_trajectories"
update.operation = "clear"
update.svg_primitives = [] # 空配列
フォールバック互換の指針¶
スナップショット不在時にも更新のみで状態を再構築できるよう、以下を推奨します。
- 動的レイヤーは原則 replace を用いて「レイヤー完全表現」を送る(自己完結)
- 長尺データ(軌跡など)は append を使いつつ、適宜 clear や定期的な replace で区切る
- これにより、過去時刻へのシークでスナップショットが無くても視覚破綻を回避できます
🗂️ データ構造仕様¶
TypeScript型定義¶
// スナップショット用
interface SvgLayerArray {
svg_primitive_arrays: SvgPrimitiveArray[];
}
interface SvgPrimitiveArray {
layer: string;
svg_primitives: string[];
}
// 更新用
interface SvgUpdateArray {
updates: SvgLayerUpdate[];
}
interface SvgLayerUpdate {
layer: string;
operation: "append" | "replace" | "clear";
svg_primitives: string[];
}
// パネル設定
interface PanelConfig {
backgroundColor: string;
message: string;
viewBoxWidth: number;
aggregatedTopic: string;
updateTopic: string;
enableUpdateTopic: boolean;
maxHistoryDuration: number;
maxHistorySize: number;
namespaces: {
[key: string]: {
visible: boolean;
children?: { [key: string]: { visible: boolean; children?: any } };
};
};
}
🎨 SVG要素仕様¶
座標系¶
- 原点: フィールド中央 (0, 0)
- 単位: ミリメートル(mm)
- X軸: 右方向が正
- Y軸: 上方向が正
- 標準フィールドサイズ: 12000mm × 9000mm
サポートするSVG要素¶
基本図形¶
<!-- 円 -->
<circle cx="0" cy="0" r="500" fill="blue" stroke="white" stroke-width="2"/>
<!-- 矩形 -->
<rect x="-1000" y="-500" width="2000" height="1000" fill="red" opacity="0.5"/>
<!-- 線分 -->
<line x1="0" y1="0" x2="1000" y2="1000" stroke="green" stroke-width="3"/>
<!-- 多角形 -->
<polygon points="0,0 500,300 200,800" fill="yellow" stroke="black"/>
<!-- パス -->
<path d="M 100 100 L 300 100 L 200 300 z" fill="purple"/>
テキスト¶
<!-- ラベル表示 -->
<text x="0" y="0" font-family="Arial" font-size="100" fill="white" text-anchor="middle">
Robot 1
</text>
<!-- 多行テキスト -->
<text x="1000" y="500" font-size="80" fill="cyan">
<tspan x="1000" dy="0">Speed: 2.5 m/s</tspan>
<tspan x="1000" dy="90">Angle: 45°</tspan>
</text>
グループ化¶
<!-- 複合要素のグループ化 -->
<g transform="translate(1000,500) rotate(45)">
<circle r="90" fill="blue"/>
<line x1="0" y1="0" x2="100" y2="0" stroke="white" stroke-width="3"/>
<text y="30" font-size="60" fill="white" text-anchor="middle">1</text>
</g>
🏗️ レイヤー命名規則¶
階層構造の推奨パターン¶
field/ # 静的フィールド要素
├─ boundary # フィールド境界線
├─ goals # ゴール
├─ center_circle # センターサークル
├─ penalty_areas # ペナルティエリア
└─ corner_arcs # コーナーアーク
robots/ # ロボット関連
├─ team_blue/ # 青チーム
│ ├─ robot_0 # 個別ロボット
│ ├─ robot_1
│ └─ formations # フォーメーション表示
├─ team_yellow/ # 黄チーム
└─ referee/ # レフェリーロボット
ball/ # ボール関連
├─ current_position # 現在位置
├─ predicted_path # 予測軌道
└─ velocity_vector # 速度ベクトル
game_state/ # 試合状態
├─ score # スコア表示
├─ time # 時間表示
├─ referee_signals # レフェリー信号
└─ game_phase # 試合フェーズ
strategies/ # 戦術情報
├─ offensive_play # 攻撃戦術
├─ defensive_play # 守備戦術
├─ set_plays # セットプレー
└─ role_assignments # 役割分担
debug/ # デバッグ情報
├─ trajectories/ # 軌跡情報
│ ├─ robot_0_path
│ └─ ball_path
├─ ai_decisions/ # AI判断
│ ├─ target_positions
│ └─ decision_tree
├─ sensors/ # センサー情報
│ ├─ vision_confidence
│ └─ communication_status
└─ performance/ # パフォーマンス
├─ frame_rate
└─ processing_time
📤 発行パターンとベストプラクティス¶
スナップショット発行戦略¶
定期発行¶
# 5秒間隔での完全状態発行
rate = rospy.Rate(0.2) # 0.2 Hz = 5秒間隔
while not rospy.is_shutdown():
publish_complete_state()
rate.sleep()
イベント駆動発行¶
# 重要なゲーム状態変化時に発行
def on_game_state_change(new_state):
if new_state in ['STOP', 'BALL_PLACEMENT', 'KICKOFF']:
publish_complete_state()
更新発行戦略¶
高頻度更新(推奨)¶
# 30Hz での動的要素更新
rate = rospy.Rate(30.0)
while not rospy.is_shutdown():
updates = []
# ロボット位置の更新
if robot_moved():
updates.append(create_robot_update())
# ボール位置の更新
if ball_moved():
updates.append(create_ball_update())
# 軌跡の追加
if should_add_trajectory_point():
updates.append(create_trajectory_append())
if updates:
publish_updates(updates)
rate.sleep()
変化検出ベース¶
# 変化があった要素のみ更新
def on_robot_position_change(robot_id, new_pos):
update = SvgLayerUpdate()
update.layer = f"robots/team_blue/robot_{robot_id}"
update.operation = "replace"
update.svg_primitives = [create_robot_svg(new_pos)]
publish_single_update(update)
🔧 統合例¶
ROS launchファイル¶
<launch>
<!-- Crane Visualizer用のトピック発行 -->
<node name="svg_aggregator" pkg="your_package" type="svg_aggregator_node">
<param name="snapshot_topic" value="/aggregated_svgs"/>
<param name="snapshot_rate" value="0.2"/>
<param name="update_topic" value="/visualizer_svgs"/>
<param name="update_rate" value="30.0"/>
</node>
<!-- 既存のシステムとの統合 -->
<node name="ssl_vision_adapter" pkg="your_package" type="vision_adapter">
<remap from="vision_input" to="/ssl_vision/vision"/>
<remap from="svg_output" to="/visualizer_svgs"/>
</node>
</launch>
パッケージ統合¶
# CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS
rospy
roscpp
your_msgs # SvgLayerArray, SvgUpdateArray を含む
)
📊 パフォーマンス考慮事項¶
メッセージサイズ最適化¶
- SVG要素の簡素化: 不要な属性の削除
- 座標の丸め: 適切な精度での数値丸め
- 文字列圧縮: 冗長な表現の排除
発行頻度の調整¶
# 要素別の最適な更新頻度
frequencies = {
'robots': 30, # 30Hz - 高頻度
'ball': 50, # 50Hz - 最高頻度
'referee_signals': 5, # 5Hz - 中頻度
'field_elements': 0.1 # 0.1Hz - 低頻度
}
この包括的なAPI仕様により、Crane Visualizerパネルとの効率的な統合と最適なパフォーマンスを実現できます。