cev-sim does not talk to ROS directly. It talks to a separate orchestrator process in orchestrator.
flowchart LR
ros2Nodes[ROS 2 Nodes] --> externalOrchestrator[Orchestrator]
externalOrchestrator --> wsTopics["WebSocket ws://localhost:8080"]
externalOrchestrator --> typesApi["Types API http://localhost:8090"]
wsTopics --> sensorFusion[Sensor Fusion Browser]
typesApi --> sensorFusion
Standalone mode, without ROS 2:
cd /your/path/to/orchestrator
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.pyROS 2 bridge mode:
cd /your/path/to/orchestrator
source .venv/bin/activate
ROS_ENABLED=true python main.pyThe orchestrator defaults are:
WS_HOST=localhostWS_PORT=8080API_HOST=localhostAPI_PORT=8090CUSTOM_TYPES_DIR=custom_typesROS_ENABLED=falseROS_NODE_NAME=orchestrator_bridgeROS_DISCOVERY_PERIOD_SEC=1.0
app/3d/managers/ClientManager.js does three things during setup:
- Tries to sync dynamic message definitions from
http://localhost:8090/api/types. - Registers local fallback
.msgfiles frompublic/messages/. - Creates a
Clientforws://localhost:8080.
app/client/Client.js implements the orchestrator protocol, including standard type encoders, dynamic .msg schemas, syncTypesFromServer, syncTypesToServer, subscribe, publish, echo, and request_all.
The canonical custom message definitions live in the orchestrator repo under its custom_types/ directory. The files in public/messages/ are browser fallback copies and should stay synchronized with orchestrator definitions.
Current fallback definitions include:
geometry_msgs/Point32sensor_msgs/PointCloudsensor_fusion_msgs/AckermannDrivesensor_fusion_msgs/CarPositionsensor_fusion_msgs/LaneBoundssensor_fusion_msgs/Lanessensor_fusion_msgs/StopSignssensor_fusion_msgs/YieldBoundarysensor_fusion_msgs/YieldBoundariessensor_fusion_msgs/Boxsensor_fusion_msgs/Boxessensor_fusion_msgs/imusensor_fusion_msgs/CarSize
Browser and orchestrator topic names use ROS-style strings such as /ackdrive. Message type names use package/Message, for example sensor_fusion_msgs/AckermannDrive.
The orchestrator converts ROS 2 names like package/msg/Message as needed on its side.
The main 3D scene currently consumes /ackdrive. When a message arrives, app/3d/Scene.js reads:
speedin mph, converted to meters per second.steering_anglein degrees, converted to radians and applied to the car steering angle.
The scripting ROS units in app/scripting/units/ROSUnit.js are placeholders. They do not yet publish or subscribe to live orchestrator topics.
- If type sync fails, cev-sim logs a warning and attempts to load fallback
.msgfiles frompublic/messages/. - If
ws://localhost:8080is unavailable, the 3D scene should still load but live topic updates will not arrive. - Run the orchestrator in the same ROS 2 environment as the nodes it needs to discover.
- Dockerized ROS 2 discovery on macOS can be unreliable because ROS 2 uses DDS discovery rather than a single fixed TCP port.