Engineering blog
Notes on multi-agent coordination with ROS 2
These are working notes from building and testing multi-agent robotic systems on ROS 2 — the communication patterns, the pain points, and the practices that survived contact with real distributed agents.
What ROS 2 gives you for free
ROS 2's node graph over DDS is a genuinely good starting point for multi-agent work. Three patterns carry most of the load:
- Publisher/subscriber for state broadcast: each agent publishes pose, status, and intent on namespaced topics; peers subscribe to what they need. Decoupled, asynchronous, and tolerant of agents joining and leaving.
- Services for explicit requests: "take this task," "yield this airspace." Synchronous, acknowledged, and easy to reason about — at the cost of blocking semantics you must handle carefully.
- Namespacing to keep a fleet from becoming soup. Every agent runs the same node set under its own namespace; coordination nodes bridge across them.
The subtle early decision is QoS profiles. Reliability, durability, and history settings that work on one machine behave very differently over a real lossy link. Deciding which topics are best-effort (high-rate state) and which must be reliable (task assignment) is a design act, not a config detail.
Where coordination actually gets hard
The middleware moves messages; it does not decide anything. The hard problems live one layer up:
Task allocation. Given n agents and m tasks, who does what? Centralised allocation is simple and a single point of failure. Fully decentralised schemes (market/auction-based approaches, where agents bid on tasks against a shared cost function) degrade more gracefully — an agent that drops out simply stops bidding — but debugging emergent misallocation is its own discipline.
Shared state without shared truth. Each agent has a slightly different, slightly stale picture of the fleet. Designing behaviours that stay safe under disagreement — rather than assuming a consistent global state — is the core mindset shift from single-robot work.
Emergence cuts both ways. Simple local rules produce useful collective behaviour, and also produce failure modes nobody wrote. You do not eliminate emergence; you bound it with invariants (geofences, minimum separation, task timeouts) that hold regardless of what the collective decides.
Containerise everything, immediately
The single most valuable engineering habit in this work: Docker from day one. Multi-agent development multiplies environment problems by n — dependency drift between agents is a bug class you can simply delete by containerising the development and simulation environment.
Reproducible containers mean a coordination bug found in a five-agent simulation can be reproduced exactly, bisected, and turned into a regression test. Without that, distributed debugging is archaeology.
What we are taking into Brobot
For our own coordination stack, these notes reduce to commitments:
- Simulation-first, containerised, always reproducible.
- Decentralised allocation as the default; central coordination only as an optimisation, never a dependency.
- Safety invariants enforced below the coordination layer, so no emergent behaviour can violate them.
- QoS treated as system design, reviewed like an API contract.
Fleets are the point of Brobot. This layer is where that either becomes real or does not.