VDA 5050を使った倉庫ロボットのcommissioningでは、JSON Schemaへの適合だけでは判断できない障害が発生します。個々のorderやstateが構文上正しくても、複数topicをまたぐ時系列、接続の切り替わり、送信主体、観測漏れを考慮しなければ原因を特定できません。
この課題に対して、MQTT traceから観測事実・仕様上の期待・不足証拠・次に確認すべき対象を分けて出力するOSS vda5050-lab を実装しました。
課題
対象とする課題は次の4点です。
- 単一メッセージの検証では状態遷移を評価できません。 reconnect、order update、cancel、action lifecycleは複数メッセージの前後関係で決まります。
- 受動ログでメッセージの不在を常に証明できるとは限りません。 capture開始前の状態、QoS 0の欠落、観測地点の違いを区別する必要があります。
- 多台数ではidentityの誤結合が診断結果を変えます。 topic、serialNumber、MQTT ClientId、participant、connection epochをrobotごとに分離しなければなりません。
- 再現用デモの表示と診断対象のtraceが一致している必要があります。 画面上の動きだけを別に作ると、診断器の実動作を検証できません。
v0.2.0では、このうち再接続診断を対象に、1・2・5・10・50・100台を同じ契約で接続・記録・診断する検証基盤を実装しました。

解決方針と実装
デモは、隔離したDockerネットワーク内で次を一気通貫に実行します。
- 使い捨てのEclipse Mosquittoを起動します
- N台の仮想Mobile Robot、Fleet Control、Recorderを同時接続します
- 全ロボットへ固有のVDA
serialNumber、topic、MQTT ClientId、order ID、経路を割り当てます demo-001だけを異常切断し、brokerのLWTでCONNECTION_BROKENを発行します- 同じparticipantを新しいconnection epochで再接続しますが、意図的にretained
ONLINEを送りません - broker-egressの観測をcanonical traceへ保存します
- ネットワークを持たないDoctorコンテナがtraceをオフライン診断します
- manifest、trace、evidence、reportの整合性を検証して全コンテナを破棄します
Scenario runner
└─ Docker internal network
├─ Mosquitto
├─ Virtual robots × N
├─ Fleet actor
└─ Recorder
│
├─ run-manifest.json
├─ trace.canonical.jsonl
└─ synthetic-evidence.json
│
▼ network disabled
vda5050-doctor
│
└─ doctor-report.jsonbrokerのportはホストへ公開しません。Doctorにはnetwork_mode: noneを指定しています。retained messageを使うため、規模ごと・fault/controlごとに新しいbrokerとnetworkを作り、前回の状態が次の判定へ混入しないようにしました。
多台数でのidentity分離検証
このデモではN台すべてが同じbrokerへ同時接続し、それぞれ別のidentityと経路を持ちます。renderer上のrobot数だけでなく、run manifestとcanonical traceに含まれる固有identity数も検証します。
| ロボット数 | 同時MQTT接続上限 | 記録されたbroker-egressメッセージ |
|---|---|---|
| 1 | 3 | 10 |
| 2 | 4 | 18 |
| 5 | 7 | 42 |
| 10 | 12 | 82 |
| 50 | 52 | 402 |
| 100 | 102 | 802 |
接続数は「N台 + Fleet Control + Recorder」です。メッセージ上限、60秒の実行時間、actor数、MQTT接続数をrun manifestに先に固定し、超過したrunは不完全な成功ではなく失敗にします。

x/y座標の定義
各ロボットの位置は、VDA messageに入るプロジェクト固有のmap座標として生成します。mapIdはwarehouse-demo、単位はメートル、左下原点はこの合成デモだけの表示規約です。
0始まりのrobot indexをiとすると、座標は次のとおりです。
column = floor(i / 10)
row = i mod 10
start = (6 × column, 2 × row)
released_end = (6 × column + 4, 2 × row)
horizon_end = (6 × column + 4, 2 × row + 1)したがって、demo-001は(0, 0) → (4, 0) → (4, 1)、demo-100は(54, 18) → (58, 18) → (58, 19)を通ります。rendererはmanifest上の予定座標ではなく、実際にtraceへ記録されたorder/state/visualization payloadのx/yを読んで描画します。
この間隔は経路を見分けるためのものです。robot footprint、制動距離、allowed deviation、衝突回避、実倉庫の安全余裕を証明する値ではありません。
証拠の強さに応じた判定
今回の診断ルールLAB-D4-RECONNECT-STATEは、VDA 5050 3.0.0のconnection lifecycleを対象にしています。異常切断ではbrokerがLWTのCONNECTION_BROKENを送り、再接続したrobotはretained ONLINEを送る、という関係を確認します。
同じ異常シナリオでも、Doctorへ渡す証拠によって結論を変えます。
| 入力 | 結果 | 意味 |
|---|---|---|
| subscriberが観測したtraceだけ | INCONCLUSIVE / UNRESOLVED | publisherの役割、connection epoch、capture完全性を証明できません |
| trace + 同一runに束縛されたbroker/session evidence | FAIL / MOBILE_ROBOT | 新epochのstateはありますが、期待するONLINEを観測できませんでした |
| reconnect後にONLINEを送るcontrol trace | D4 findingなし | この合成controlでは期待メッセージを観測しました |
「見えなかった」を即座に「送られなかった」へ変換すると、相手ベンダーを誤って責める可能性があります。VDA 5050の主要topicにはQoS 0もあるため、受動subscriberだけで不在を証明できない場面があります。そこで、raw observationと外部assertionを分離し、evidence manifestをtrace digestへ束縛しました。
なお「findingなし」は一般的なPASSや適合認証ではありません。単にこのルールの異常条件が、この合成controlでは成立しなかったという意味に限定しています。
利用方法
必要なのはDocker Compose、jq、repository checkoutです。最初は10台だけを実行できます。
git clone https://github.com/ekusiadadus/vda5050-lab.git
cd vda5050-lab
make demo-fleet DEMO_ROBOTS=10公開している6規模をすべて実行する場合は、次のコマンドを使用します。
make demo-fleet-e2e成功すると、各規模にrun-manifest.json、trace.canonical.jsonl、synthetic-evidence.json、doctor-report.jsonが残ります。READMEのGIF/MP4は、この成果物とreportのdigestを検証したrendererから生成します。表示に使う座標とidentityもtrace payloadから取得します。
バイナリ、全6規模のMP4/GIF、overview、SHA256SUMSはv0.2.0 Releaseから取得できます。
何を証明していないか
100台デモは、隔離環境で100台の仮想MQTT actorを扱い、identityを混同せず、同じ診断契約を維持できたという機能結果です。次の主張には使用できません。
- 100台の実機を接続したわけではありません
- 100社・100vendor間の相互運用性を確認したわけではありません
- production fleetでのcapacityやreal-time性能を保証するものではありません
- 衝突回避、機能安全、現場ネットワークの安全性を検証したわけではありません
- VDA 5050全体への適合認証を行ったわけではありません
デモとベンチマークと適合試験を混ぜないことは、このプロジェクトの設計原則でもあります。
残る課題と今後の範囲
v0.2.0は、合成traceで証拠モデルを端から端まで検証した段階です。次は、匿名化できる実障害traceを集め、order update、base/horizon stitching、newBaseRequest、cancel、action lifecycle、2.1から3.0への移行診断を追加します。
対象範囲は一般的なviewerではなく、VDA 5050通信と状態遷移の診断に限定します。commissioning中のengineerがログを入力したとき、次の情報を返すことを目標とします。
- 何が起きたか
- どのmessageが証拠か
- どの仕様節が関係するか
- 何が未観測で断定できないか
- Robot、Fleet Control、通信のどこを次に調べるか
実際のinteroperability障害traceを共有できる場合は、GitHub Issuesからご連絡ください。公開できないtraceについては、未知fieldやparse不能payloadを安全側で拒否するpseudonymization要件を、データ提供者と確認しながら設計します。