Design: Bridge
The bridge (package cpm_lab_bridge, executable bridge_node) connects the lab domain and the planner domain. It
is the only path between them: it forwards the vehicle states, the map, the commands and the triggers, checks that
every component is ready before an experiment can start, and stops the experiment when a vehicle state goes missing.
Understanding the Domain IDs explains the idea; this page describes the implementation.
Source: src/bridge/cpm_lab_bridge/ — component/bridge_component.cpp (ROS interface),
src/bridge.cpp (state machine without ROS, unit-tested in test/).
One Process, Two Domains
The control flow starts the bridge with ROS_DOMAIN_ID of the planner domain. The node /planner/bridge lives in
that domain. For the lab domain, the bridge creates a second rclcpp::Context with lab_domain_id (21) and a
node /lab/bridge in it, spun by its own executor thread. Each message is copied from one node to the other; the
bridge never relays topics generically.
Lab domain ( |
Direction |
Planner domain ( |
|---|---|---|
|
➝ collected, sent every period as |
|
|
➝ |
|
|
⟵ by |
|
|
➝ |
|
|
⟵ |
|
Triggers with another source_id prefix are rejected with a warning, and the bridge ignores its own triggers
(source_id bridge), so nothing loops between the domains. Commands for vehicles that were not deployed are
rejected.
Parameters (config/default.yaml, set by the control flow): vehicle_ids (the deployed vehicles), period_ms
(the planner period), lab_domain_id (21) and vehicle_timeout_seconds (1.0).
State Machine
---
config:
layout: elk
startOnLoad: false
---
stateDiagram-v2
[*] --> STARTED
STARTED --> READY: planner READY for every vehicle and a state of every vehicle
READY --> RUNNING: START from the lab
RUNNING --> STOPPED: STOP from the lab
RUNNING --> EMERGENCY_STOP: a vehicle state missing or older than the timeout
States of the bridge
Every period, a timer on the lab node acts on the state:
STARTED: sendREADY_REQUESTto both domains. A planner answers withREADYandsource_idplanner_<vehicle_id>for each of its vehicles; a vehicle counts once its state arrived.READY: sendREADY(source_idbridge), which the control flow waits for.RUNNING: publish the last state of every deployed vehicle as oneVehicleStateListto the planner. If a vehicle never sent a state, or its last state is older thanvehicle_timeout_seconds(measured from itsvalid_after_stamp), switch toEMERGENCY_STOP.EMERGENCY_STOP: publishSTOP(source_idlab) once to both domains. The vehicles stop, the planner stops, and the control flow tears the experiment down.
The state and the number of received vehicle states are published on /diagnostics.
Timing
The planner period is the timer period of the bridge, so the planner is driven by the vehicle states: it plans once per
VehicleStateList and does not need its own timer. The states are the latest received, not interpolated; their
valid_after_stamp tells the planner how old they are. The lab domain runs at 20 ms, so periods below 20 ms do not
make sense; the default is 400 ms.
Changing the Bridge
Keep the logic in
Bridge(src/bridge.cpp) free of ROS and extend its unit tests.A new topic between the domains needs a subscription on one node and a publisher on the other; document it in Interface Reference and in the table above.
The checks before Start are a safety feature. Relaxing them needs agreement of the team.