This repository is a ROS2 ament_python package that demonstrates a VDA
5050-style fleet control architecture for mixed-brand AGVs.
The repo's current role in the EdgeEmbed stack is a lightweight demo/reference fleet manager. It is the fleet-side target that EdgeEmbed Runtime can drive during demos and integration tests.
The demo has three goals:
- show how multiple AGV brands can communicate with one fleet controller through VDA 5050-style MQTT topics and messages
- provide a simple 2D dashboard for sales demos, operations reviews, and architecture discussions
- provide a concrete fleet-manager target for the Runtime -> scenario -> fleet manager integration story
This is a demo integration, not a certified full VDA 5050 implementation.
This repo is not the EdgeEmbed Runtime itself. It is the open fleet demo that the Runtime can govern.
Recommended full story:
VQA / sensors / MES / scenario input
|
v
EdgeEmbed Runtime
|
| approved business task
v
fleet/task
|
v
this demo fleet manager
|
v
VDA 5050-style order over MQTT
|
v
vehicle_adapter / robot backend / simulator
|
v
state, connection, factsheet feedback
|
v
dashboard, scenario assertions, future journal/replay
In this model:
- VQA and other upstream systems produce structured operational events.
- EdgeEmbed Runtime applies policy, confidence thresholds, interlocks, and trace logging before any task reaches the fleet manager.
- This repo accepts approved task requests on
fleet/task, performs the demo fleet workflow, and publishes VDA 5050-style robot messages. - The E2E scenario runner injects demo events and asserts behavior by observing Runtime decisions, fleet-manager output, adapter state, and dashboard telemetry.
The current repository now implements the fleet-manager target, a minimal Runtime gateway, and an E2E scenario runner for the first end-to-end proof. Structured journal, replay, and real EdgeEmbed-HIL hardware validation remain future integration points.
Current standalone flow:
custom fleet/task request
|
v
master_control
|
v
VDA 5050-style order over MQTT
|
v
vehicle_adapter per AGV
|
v
Nav2 or simulated adapter motion
|
v
VDA 5050-style state, connection, factsheet over MQTT
|
v
dashboard WebSocket and 2D canvas
The VDA 5050-style MQTT topic shape is:
uagv/v2/{manufacturer}/{serial}/{message_type}
Examples:
uagv/v2/EdgeEmbed/AGV001/order
uagv/v2/BrandB/AGV002/state
uagv/v2/BrandC/FL-77/factsheet
The custom fleet/task topic is not VDA 5050. It is the high-level business
task API for the demo. master_control converts those task requests into
VDA 5050-style order messages.
When connected to EdgeEmbed Runtime, fleet/task should be treated as a
governed downstream interface. VQA, MES, sensor, or scenario events should
enter the Runtime first. Only approved Runtime outputs should be published to
fleet/task.
Planned upstream topics:
edgeembed/inference VQA or AI inference events into Runtime
edgeembed/scenario optional scenario control
edgeembed/runtime/event Runtime decision and trace telemetry
fleet/task approved task request into this fleet demo
This folder is one ROS2 package, not one node. It exposes these executable nodes:
master_control
runtime_gateway
scenario_runner
vehicle_adapter
dashboard
They are registered in setup.py as console scripts.
master_control
- subscribes to all AGV
state,connection, andfactsheetmessages - accepts approved
fleet/taskrequests - selects vehicles by online status, idle state, battery, payload, and distance
- locks exclusive traffic zones such as
corridor_1 - publishes VDA 5050-style
ordermessages - publishes dashboard KPI and zone state
runtime_gateway
- subscribes to
edgeembed/inference - calls
/home/edgeembed/edgeembed-sdk/runtime/libedgeembed.so - loads the demo Runtime bundle from
runtime_bundles/fleet_demo - publishes Runtime verdicts to
edgeembed/runtime/event - publishes approved transport tasks to
fleet/task - blocks events when the Runtime dispatches the reject action
scenario_runner
- can run a self-contained Runtime -> fleet manager scenario path
- publishes configured scenarios from
config/e2e_scenarios.yaml - observes Runtime verdicts,
fleet/task, and VDA 5050-style orders - fails if blocked inputs produce downstream tasks or orders
vehicle_adapter
- represents one AGV
- subscribes to VDA 5050-style
orderandinstantActions - publishes
state,connection, andfactsheet - translates order nodes into Nav2 goals when Nav2 is available
- can run in simulated adapter mode for dynamically added demo vehicles
dashboard
- serves
web/index.htmlover HTTP - opens a WebSocket feed on port
8765 - listens to MQTT vehicle and master-control telemetry
- pushes site, vehicle, zone, and KPI snapshots to the browser
edgeembed_vda5050/
├── edgeembed_vda5050/ Python node source code
├── config/ Fleet and site topology configuration
├── launch/ ROS2 launch files
├── maps/ Nav2 map server files
├── runtime_bundles/ EdgeEmbed Runtime demo bundle JSON
├── web/ Browser dashboard
├── tools/ One-key demo scripts
├── resource/ ROS2 ament package marker
├── package.xml ROS2 package metadata and dependencies
├── setup.py Python package install and entry points
├── setup.cfg ROS2 script install paths
└── DEMO_INSTRUCTIONS.md Detailed sales-demo runbook
Generated by colcon build:
build/ temporary build output
install/ installed runnable package
log/ build logs
Do not edit generated folders directly.
Tested with ROS2 Humble.
Required local components:
- ROS2 Humble
- Nav2 packages, including
nav2_bringupandnav2_simple_commander - MQTT broker such as Mosquitto
- Python packages installed by ROS or apt:
paho-mqttwebsocketsyaml
Install common dependencies on Ubuntu with ROS Humble:
sudo apt install \
ros-humble-navigation2 \
ros-humble-nav2-bringup \
mosquitto \
mosquitto-clients \
python3-paho-mqtt \
python3-websockets \
python3-yamlFrom this package root:
source /opt/ros/humble/setup.bash
colcon build --packages-select edgeembed_vda5050
source install/setup.bashThe Runtime E2E path expects the EdgeEmbed SDK checkout at
/home/edgeembed/edgeembed-sdk. The SDK evaluation runtime in this
environment requires ThreadSanitizer to be preloaded before Python starts:
export LD_PRELOAD=/lib/x86_64-linux-gnu/libtsan.so.0Check ROS can see the package:
ros2 pkg executables edgeembed_vda5050Expected:
edgeembed_vda5050 dashboard
edgeembed_vda5050 master_control
edgeembed_vda5050 runtime_gateway
edgeembed_vda5050 scenario_runner
edgeembed_vda5050 vehicle_adapter
After sourcing the absolute install path, you can run ROS commands from any directory:
source /home/edgeembed/edgeembed_vda5050/install/setup.bash
ros2 run edgeembed_vda5050 dashboardTerminal 1, start MQTT:
mosquitto -vTerminal 2, launch the demo:
source /opt/ros/humble/setup.bash
source /home/edgeembed/edgeembed_vda5050/install/setup.bash
ROS_LOG_DIR=/tmp/ros_log ros2 launch edgeembed_vda5050 demo.launch.pyOpen the dashboard:
http://localhost:8080
The WebSocket endpoint is:
ws://localhost:8765
If you edit Python, launch, config, map, or web files, rebuild:
colcon build --packages-select edgeembed_vda5050
source install/setup.bashThen restart the relevant node or launch file.
On launch, you should see:
master_controlstartdashboardstart- one Nav2 bringup group per configured vehicle
- one
vehicle_adapterper configured vehicle
Configured vehicles are in config/fleet.yaml:
AGV001 EdgeEmbed
AGV002 BrandB
FL-77 BrandC
The ROS namespace for FL-77 is sanitized to FL_77, but the MQTT and VDA
serial remains FL-77.
If Nav2 logs this:
Timed out waiting for transform from base_link to odom
it means no simulator or robot driver is publishing TF and odometry yet. The
dashboard demos still work because the tools/demo_*.sh scripts publish
deterministic MQTT telemetry.
Start MQTT, launch the system, and open http://localhost:8080 first.
tools/demo_1_corridor.shWhat it demonstrates:
- two tasks route through
corridor_1 - one AGV enters first
- the corridor turns red
- the other AGV waits near
N01 - after release, the waiting AGV proceeds
The business task trigger is:
mosquitto_pub -h localhost -t "fleet/task" -m '{"from":"L11","to":"S01","weight":50}'
mosquitto_pub -h localhost -t "fleet/task" -m '{"from":"L31","to":"S02","weight":50}'tools/demo_2_vehicle_lost.shWhat it demonstrates:
AGV002is marked disconnected- the task and traffic lock are recovered
- the task label moves to another vehicle
- the dashboard keeps running
The script attempts:
pkill -f "vehicle_adapter.*AGV002"and publishes a CONNECTIONBROKEN message so the visual is deterministic.
tools/demo_3_new_brand.shEquivalent command:
ros2 run edgeembed_vda5050 vehicle_adapter \
--ros-args -p serial:=NEW-99 -p manufacturer:=BrandD -p type:=carrierWhat it demonstrates:
- a new brand appears without restarting master or dashboard
- the vehicle publishes a VDA 5050-style factsheet
- the dashboard shows a new vehicle color
- the system remains brand independent
tools/demo_4_capacity_kpi.shWhat it demonstrates:
- first state: 6 vehicles, 142 boxes/hour, 71 percent utilization
- second state: 8 vehicles, 159 boxes/hour
- throughput rises only about 12 percent because
corridor_1is the bottleneck
This shifts the sales discussion from number of robots to correct system capacity.
See DEMO_INSTRUCTIONS.md for talk tracks and reset commands.
The integration is additive. The current fleet demo still works by publishing
directly to fleet/task, and the Runtime front door now produces the same task
message only after policy approval.
This is the preferred sales-demo path. Start MQTT, launch the web stack, then run onboarding and Runtime scenarios from the browser.
Terminal 1:
mosquitto -vTerminal 2:
source /opt/ros/humble/setup.bash
source install/setup.bash
ros2 launch edgeembed_vda5050 web_demo.launch.pyOpen:
http://localhost:8080
If you run the dashboard on a non-default WebSocket port, pass it in the URL:
http://localhost:8080?wsPort=18765
From the right panel:
- The web demo auto-onboards two simulated robots,
SIM-01andSIM-02. - The top evidence chain shows
Input,Runtime,Fleet, andVDA 5050cards for the active trace. AGV Onboardinglets you add one extra AGV robot at the selected map node. The AGV emits VDA 5050-styleconnection,factsheet, andstateevents.AGV VDA Eventsshows the exact VDA-style event sequence emitted by the onboarded AGV, including MQTT topic names.- MES is not onboarded as equipment. It remains a software source event through
the
MES: Production Completescenario button. VQA Pallet Readypublishes a VQA-style inference event into Runtime. If the Runtime admits it,master_controldispatches a VDA order and the simulated robot moves on the map.MES: Production Completepublishes a deterministic manufacturing software event into the same Runtime-governed fleet path.Block Low ConfidenceandBlock Overweightpublish rejected Runtime cases. They appear in the evidence timeline without producing a fleet task or VDA order.Mark Offlinepublishes a broken connection state for the selected robot.Reset SIM-01 and SIM-02moves the two default simulated robots back toP01andP02and clears their active order state.
Start MQTT:
mosquitto -vBuild and run the self-contained scenario demo:
source /opt/ros/humble/setup.bash
colcon build --packages-select edgeembed_vda5050
source install/setup.bash
export LD_PRELOAD=/lib/x86_64-linux-gnu/libtsan.so.0
ros2 run edgeembed_vda5050 scenario_runnerExpected result:
[scenario] PASS allow_transport
[scenario] PASS block_low_confidence
[scenario] PASS block_out_of_bounds
The Runtime-facing gateway subscribes to upstream events such as
edgeembed/inference:
{
"trace_id": "demo-001",
"class": "pallet_ready",
"confidence": 0.93,
"command": {
"action": "transport",
"from": "L11",
"to": "S01",
"weight": 50
}
}The gateway submits fleet_demo::pallet_ready to the real EdgeEmbed runtime
library with confidence and weight in the runtime payload. The bundle policy
admits the event when confidence is at least 0.85 and weight is at most
1500. It rejects the same event through a fallback reject action otherwise.
If the runtime emits the admit action, the gateway publishes:
{
"trace_id": "demo-001",
"from": "L11",
"to": "S01",
"weight": 50
}to:
fleet/task
If the Runtime blocks the action, it should publish a Runtime event and no
fleet/task message.
The E2E scenario runner can:
- inject upstream inference events into Runtime
- inject fleet faults such as
CONNECTIONBROKEN - inject malformed or stale VDA 5050-style state messages
- observe MQTT output from Runtime,
master_control, andvehicle_adapter - assert that expected side effects did or did not happen
Minimum first scenarios:
- low VQA confidence: Runtime blocks, no
fleet/task, no VDA order - out-of-bounds command: Runtime blocks, no downstream device action
- vehicle lost: fleet manager releases the zone and recovers or requeues the task
- corridor contention: only one vehicle is granted
corridor_1
The first useful contract field is trace_id. It flows through:
edgeembed/inference -> Runtime verdict -> fleet/task -> VDA order -> state/dashboard -> scenario result
The web dashboard shows this as a live evidence chain:
VQA result -> Runtime decision -> fleet task -> VDA order -> robot state -> scenario assertion
This repo's scenario_runner is not EdgeEmbed-HIL. It is an MQTT-level E2E
scenario harness for the fleet demo. Real EdgeEmbed-HIL integration should call
the separate EdgeEmbed-HIL daemon/tools to validate target-side evidence such
as deployed adapter code, camera frames, logs, process health, GPIO, or other
hardware capabilities.
If you only want to verify the browser view:
mosquitto_pub -t uagv/v2/EdgeEmbed/AGV001/connection -m '{"connectionState":"ONLINE"}'
mosquitto_pub -t uagv/v2/EdgeEmbed/AGV001/factsheet -m '{"typeSpecification":{"seriesName":"tugger","maxLoadMass":1000}}'
mosquitto_pub -t uagv/v2/EdgeEmbed/AGV001/state -m '{"manufacturer":"EdgeEmbed","serialNumber":"AGV001","agvPosition":{"x":44,"y":20,"theta":0},"driving":true,"batteryState":{"batteryCharge":86},"orderId":"ORD-demo","errors":[],"nodeStates":[{"nodeId":"N02"}]}'Highlight the narrow corridor and set KPI:
mosquitto_pub -t uagv/v2/EdgeEmbed/master_control/dashboard -m '{"zones":{"corridor_1":{"occupied":true,"holder":"AGV001","edges":["E_N01_N02"]}},"kpi":{"completed":6,"throughput_per_hour":142,"utilization_percent":71,"avg_wait_seconds":18}}'ament_flake8 edgeembed_vda5050 setup.py launch
ament_pep257 edgeembed_vda5050 setup.py launch
/usr/bin/python3 -m compileall edgeembed_vda5050
source /opt/ros/humble/setup.bash
colcon build --packages-select edgeembed_vda5050Launch parse check:
ROS_LOG_DIR=/tmp/ros_log ros2 launch edgeembed_vda5050 demo.launch.py --show-argsImplemented VDA 5050-style concepts:
- topic namespace with interface, major version, manufacturer, serial, message
- common message header fields
orderstateconnectionfactsheetinstantActionsnodes,edges,sequenceId, andreleasedagvPositionbatteryStateerrorssafetyState
Custom demo concepts:
fleet/task- dashboard KPI messages
- site topology YAML
- browser WebSocket payload
Planned EdgeEmbed integration concepts:
edgeembed/inference- Runtime policy verdicts
- E2E scenario inputs and assertions
trace_idacross Runtime, fleet manager, adapter, dashboard, and replay
- This is a demo, not a complete certified VDA 5050 stack.
- This is not the full EdgeEmbed Runtime implementation. The included
runtime_gatewayis the minimal demo gateway. - E2E scenario assertions are implemented for the first MQTT path, but journal, replay, and real EdgeEmbed-HIL hardware validation are still future work.
- Real motion needs robot drivers or a simulator publishing TF and odometry.
- The included Nav2 map is a simple placeholder.
pick,drop, andliftToare simulated actions.- Traffic control is intentionally focused on the
corridor_1exclusive-zone demo.