mavp2p. It also enumerates the MAVROS topics and services the Gateway drives over rosbridge. Everything here is load-bearing — a port number or channel prefix that drifts silently severs two components with no error.
On the vehicle there are two local fabrics (MAVLink and ROS2 DDS), plus a shared Redis bus the drone dials out to and one WebSocket uplink. Redis and MAVLink are documented on this page; the other two are covered in Redis Message Bus & WebSocket Interfaces and MAVLink Routing (mavp2p).
- MAVLink / mavp2p — UDP mesh between the flight controller and every consumer (this page).
- ROS2 DDS — MAVROS publishes
/mavros/*topics on a sharedROS_DOMAIN_ID(default1);isaac-slamreads/mavros/statehere. - Redis pub/sub — JSON control/status messages, IP-namespaced; a shared bus the drone connects out to (
REDIS_HOST, cloud-hosted in prod; drone-local only indocker-compose.local.yml/SITL) (this page). - rosbridge :9090 — the ROS2↔WebSocket uplink the Gateway attaches to (see Rosbridge Connection & Reconnect).
Redis pub/sub channel catalog
Redis is the drone’s control/status bus. It carries manual gamepad input, ArUco tracking overlays, flight-mode/armed announcements, docking status, TTS audio, and video-stream state between the on-drone services (core, gamepad, isaac-slam) and the cloud (WS Proxy → Dashboard). It is separate from the Gateway’s rosbridge path — manual control and these status streams never traverse the Gateway.
Namespacing rule
Almost every channel is prefixed with the drone’s WireGuardwg0 IP, resolved at service startup:
gamepadresolves it viaget_wg0_ip()(or theIP_OVERRIDEenv, legacy typoIP_OVRIDEstill honored) —skyhub_core/docker/gamepad/main.py:86,1145andskyhub_core/docker/gamepad/src/shared/config.py:34. The IP-namespaced channel names are then built inskyhub_core/docker/gamepad/src/modules/redis/handler.py:44-54.isaac-slamreads it from theMY_IPenv (falls back tolocalhost) —skyhub_core/docker/issac-slam/.../services/navigation/mavros_state_monitor.py:335.coremodules format{self._ip_address}:...—skyhub_core/docker/core/src/modules/aruco_landing/module.py:248-249.
IP-namespaced channels
All of these are prefixed{wg0_ip}:. “Backend/UI” reaches them through the WS Proxy ({ip}:gamepad_input inbound, {ip}:output/{ip}:aruco_tracking outbound).
Channel ({ip}: prefix) | Publisher(s) | Subscriber(s) | Purpose |
|---|---|---|---|
gamepad_input | Backend/UI (via WS Proxy), core aruco_landing, isaac-slam docking | gamepad (RedisHandler) | Inbound control bus — axis/button frames, camera/gimbal/mode commands, auto-dock movement |
output | gamepad | WS Proxy → UI | Gamepad telemetry/status output stream |
aruco_tracking | core aruco_landing | gamepad → UI | e-ArUco marker pose for the frontend canvas overlay (not burned into video) |
mode_change | isaac-slam MavrosStateMonitor | Backend/UI | Flight-mode transitions (drives audio + UI) |
armed_state | isaac-slam MavrosStateMonitor | Backend/UI | Armed/disarmed transitions |
docking_status | isaac-slam DockingService | Backend/UI | Vision-docking progress/result |
slam_control | gamepad (publish_slam_command) | isaac-slam | SLAM start/stop/mode commands |
charging_status | gamepad (publish_charging_status) | Backend/UI | Battery-swap / charging pad status |
camera_status | gamepad (publish_camera_status) | Backend/UI | Recording / camera state |
chat | gamepad (publish_chat) | isaac-slam AudioListener | System chat → TTS announcement |
audio | gamepad (publish_audio_announcement) | isaac-slam AudioListener | Explicit TTS audio triggers |
restart_control | gamepad (publish_system_command) | System | Service restart / system commands |
gamepad’s RedisHandler actively subscribes to only three channels at startup — {ip}:gamepad_input, {ip}:aruco_tracking, and the global video_stream_state (handler.py:120-137). The other IP-namespaced channels above are ones it (or isaac-slam) publishes; their consumers are the cloud (via WS Proxy) or the isaac-slam audio/docking services.Global (non-namespaced) channels
Two channels are not IP-prefixed becausecore’s video_stream module is IP-agnostic (skyhub_core/docker/core/src/modules/video_stream/module.py:34-35):
| Channel | Publisher | Subscriber | Purpose |
|---|---|---|---|
video_stream_state | core video_stream | gamepad → UI | Stream START/STOP sync to the UI |
video_stream_status_request | gamepad MessageRouter | core video_stream | Ask core for the current stream status |
Publisher/subscriber matrix
MAVLink port map (mavp2p)
mavp2p (the mavproxy compose service) is the single MAVLink router on the drone. It bridges the ArduPilot flight controller’s serial link to every UDP consumer bidirectionally — each endpoint sees FC traffic and can talk to the FCU. It has no env config; the endpoint list is pure CLI args in skyhub_core/docker-compose.yml:139:
skyhub_core/docker-compose.yml
udps = mavp2p is the UDP server (the consumer dials in with udpout:127.0.0.1:<port>); udpc = mavp2p connects out to a listener (MAVROS binds 14550).
| Endpoint | Role | Consumer | Consumer env |
|---|---|---|---|
serial:/dev/ttyACM0:115200 | Flight controller | ArduPilot FCU (Cube/Pixhawk) | — |
udpc 127.0.0.1:14550 | FC ↔ MAVROS | core MAVROS | FCUURL=udp://127.0.0.1:14550@ |
udps 0.0.0.0:14777 | Manual control in | gamepad (MAV_TYPE_GCS) | MAVLINK=udpout:127.0.0.1:14777 |
udps 0.0.0.0:14560 | RTCM / GGA | rtk-ntrip | RTK_MAVLINK_CONNECTION=udpout:127.0.0.1:14560 |
udps 0.0.0.0:14561 | Direct velocity | core aruco_landing | udpout:127.0.0.1:14561 |
udps 0.0.0.0:14900 | External GCS | e.g. QGroundControl over VPN | — |
14777 path, RTK NTRIP GPS Corrections for 14560, and YOLO Detection, ArUco Landing & Docking for 14561.
MAVROS topic & service inventory
FC telemetry flowsFCU → mavp2p → MAVROS → /mavros/* ROS2 topics → rosbridge :9090 → Gateway. The Gateway addresses these by the (topic_name, ros_type) tuple constants in src/utils/mavros_topics.py. Subscriptions are throttled to TELEMETRY_THROTTLE_RATE (default 200 ms, src/application/settings.py:145) except /diagnostics, which is subscribed with throttle_rate=0.
set_stream_rates.sh on the drone must raise the FC MAVLink stream rate to 10 Hz first, or the sensor /mavros/* topics never populate. The rosbridge subscribe/emit path and the dashboard yaw computation live in Socket.IO Telemetry Streaming; this table is just the topic contract.Telemetry topics (Gateway subscribes)
| Constant | Topic | ROS type |
|---|---|---|
GPS | /mavros/global_position/global | sensor_msgs/msg/NavSatFix |
GPS_RAW | /mavros/gpsstatus/gps1/raw | mavros_msgs/msg/GPSRAW |
RELATIVE_ALT | /mavros/global_position/rel_alt | std_msgs/msg/Float64 |
COMPASS_HEADING | /mavros/global_position/compass_hdg | std_msgs/msg/Float64 |
VFR_HUD | /mavros/vfr_hud | mavros_msgs/msg/VFR_HUD |
IMU_ORIENTATION | /mavros/imu/data | sensor_msgs/msg/Imu |
SYSTEM_STAT | /mavros/battery | sensor_msgs/msg/BatteryState |
HOME_POSITION | /mavros/home_position/home | mavros_msgs/msg/HomePosition |
DIAGNOSTICS | /diagnostics | diagnostic_msgs/msg/DiagnosticArray |
LOGS | /rosout | rcl_interfaces/msg/Log |
Command & mission services (Gateway calls)
| Constant | Service / topic | ROS type |
|---|---|---|
SET_MODE | /mavros/set_mode | mavros_msgs/srv/SetMode |
ARM_THROTTLE | /mavros/cmd/arming | mavros_msgs/srv/CommandBool |
TAKEOFF | /mavros/cmd/takeoff | mavros_msgs/srv/CommandTOL |
COMMAND_LONG | /mavros/cmd/command | mavros_msgs/srv/CommandLong |
PUSH_MISSION | /mavros/mission/push | mavros_msgs/srv/WaypointPush |
SET_CURRENT_MISSION | /mavros/mission/set_current | mavros_msgs/srv/WaypointSetCurrent |
PUSH_FENCE | /mavros/geofence/push | mavros_msgs/srv/WaypointPush |
GLOBAL_POSITION_PUBLISHER | /mavros/setpoint_position/global | mavros_msgs/msg/GlobalPositionTarget |
GLOBAL_POSITION_RAW | /mavros/setpoint_raw/global | mavros_msgs/msg/GlobalPositionTarget |
Parameter services (ArduPilot params)
ROS2 MAVROS usesParamSetV2 (not ROS1’s ParamSet) and has no param/get service, so reads go through rosapi or the batch get_parameters service.
| Constant | Service | ROS type |
|---|---|---|
PARAM_SET | /mavros/param/set | mavros_msgs/srv/ParamSetV2 |
PARAM_GET | /mavros/param/get_parameters | rcl_interfaces/srv/GetParameters |
PARAM_LIST | /mavros/param/list_parameters | rcl_interfaces/srv/ListParameters |
PARAM_PULL | /mavros/param/pull | mavros_msgs/srv/ParamPull |
PARAM_EVENT | /mavros/param/event | mavros_msgs/msg/ParamEvent |
ROSAPI_GET_PARAM | /rosapi/get_param | rosapi_msgs/srv/GetParam |
COMMON_ARDUPILOT_PARAMS in the same file lists the fence / RTL / arming / battery / failsafe / flight / GPS / EKF / serial / logging parameter names the Gateway batch-reads. See DroneControlService & Rosbridge Dispatch for how these are invoked and Mission & Geofence MAVLink Format for the waypoint frame/command encoding.
Video-room control topics (Gateway publishes)
The Gateway pushes Janus room details to the drone over rosbridge topics (std_msgs/msg/String JSON) — consumed by core’s video_stream module:
| Constant | Topic | ROS type |
|---|---|---|
VIDEO_ROOM_DETAILS | /video_room_details | std_msgs/msg/String |
VIDEO_ROOM_STATE | /video_room_state | std_msgs/msg/String |
VIDEO_ROOM_SOURCE | /video_room_source | std_msgs/msg/String |
Related references
Redis Message Bus
Drone-side Redis handlers, WebSocket interfaces, and message routing internals.
MAVLink Routing (mavp2p)
Why the port map is what it is and how to add a MAVLink consumer.
Rosbridge Connection
How the Gateway pools and drives these topics over WebSocket :9090.
Environment Variables
Platform-wide config including IP_OVERRIDE, REDIS_HOST, FCUURL, throttle rates.
Full API Reference
HTTP + Socket.IO surface that fronts these channels.
Database Schema
The drone/user tables the WS Proxy uses to resolve IP prefixes.

