GCS Docker Configuration¶
The Ground Control Station is containerized using Docker Compose, following the same patterns as the robot and simulation components.
File Structure¶
gcs/docker/
├── gcs-base-docker-compose.yaml # Shared base service: command, env, GPU, mounts
├── docker-compose.yaml # gcs (sim/dev) + gcs-real (field) services
├── Dockerfile.gcs # Image: ROS 2 Jazzy desktop + Foxglove Studio
│ # + foxglove_bridge + eProsima DDS Router
├── .bashrc # Bash config mounted into the container
└── Foxglove/ # Foxglove Studio app state (mounted to
# /root/.config/Foxglove, gitignored)
The Foxglove extensions and layout template live one level up in gcs/foxglove_extensions/ (mounted into the container) — not under docker/.
Services and profiles¶
Both services extend the same gcs-base definition:
| Service | Profiles | Networking | Use case |
|---|---|---|---|
gcs |
desktop, desktop_split |
airstack_network bridge; publishes 2222:22 (ssh) and 8766:8765 (Foxglove bridge) |
Sim/dev alongside robot-desktop containers |
gcs-real |
hitl, deploy, offboard |
network_mode: host (DDS directly on the LAN) |
Field laptop / ground station next to real or HITL robots |
gcs-real additionally mounts $HOME/bags at /bags for mission recordings on the host.
Container startup¶
The gcs-base command: runs, in order:
command: >
bash -c "
service ssh restart;
python3 /root/AirStack/gcs/foxglove_extensions/install.py;
python3 /root/AirStack/gcs/foxglove_extensions/render_layout.py;
tmux new -d -s bringup;
if [ $$AUTOLAUNCH = 'true' ]; then
tmux send-keys -t bringup:0.0 'bws && sws; ros2 launch desktop_bringup gcs.launch.xml' ENTER;
fi;
sleep infinity"
install.pywalksgcs/foxglove_extensions/and installs every extension directory (apackage.json+dist/extension.js) into Foxglove's user-extensions dir (/root/.foxglove-studio/extensions) — that's how the Robot Tasks / Waypoint Editor / Polygon Editor panels appear.render_layout.pyexpands the single-robot templateairstack_default.jsoninto/root/airstack_layout_num_robots_<N>.jsonfor the currentNUM_ROBOTS(see GCS Foxglove Visualization for the import flow).- A tmux session named
bringupis created; withAUTOLAUNCH=trueit builds the workspace and launchesdesktop_bringup gcs.launch.xml(Foxglove Studio GUI,foxglove_bridgeon port 8765, gossip bridge,gcs_visualizer,action_relay).
Attach to the session with airstack connect gcs (or tmux attach -t bringup inside the container).
Environment Variables¶
| Variable | Description | Default |
|---|---|---|
AUTOLAUNCH |
Auto-start the GCS bringup in tmux | false in the compose file (the airstack CLI sets it true for full-stack launches) |
NUM_ROBOTS |
Robot count when no fleet file is set: sizes the rendered layout and the per-robot action relays | 1 |
FLEET_CONFIG_FILE |
Fleet roster: when set, action_relay derives robot names/domains from the fleet file instead of NUM_ROBOTS |
unset |
ROBOT_RELAY_MAP |
name:domain,... override for custom robot→domain mappings (wins over both of the above) |
unset |
RECORD_BAGS |
Enable bag recording | unset |
DISPLAY |
Host X11 display for the Foxglove Studio window | inherited |
Example overrides:
# Don't auto-launch (for development)
airstack up gcs --no-autolaunch
# Three robots: three relays + a 3-tab layout
airstack up --sim isaac --robots 3
Networking¶
In the sim/dev profiles the GCS joins the airstack_network bridge (172.31.0.0/24) and talks ROS 2 DDS to the robot containers and the sim. The GCS itself runs on ROS_DOMAIN_ID=0; action_relay and the gossip bridge are the components that cross into the per-robot domains (robot N = domain N) and the gossip domain (99).
Accessing the GCS¶
Foxglove (primary)¶
- The Foxglove Studio desktop app opens on the host's X display when the bringup launches.
- From the host, open your own Foxglove and connect to
ws://localhost:8766(the published bridge port). - Import the rendered layout
/root/airstack_layout_num_robots_<N>.json— full walkthrough in GCS Foxglove Visualization.
tmux / shell¶
SSH¶
Development Workflow¶
Interactive Development¶
# Start GCS without auto-launch
airstack up gcs --no-autolaunch
# Inside the container
airstack connect gcs
bws --packages-select gcs_visualizer # build one package
sws
ros2 launch desktop_bringup gcs.launch.xml
Building GCS Packages¶
# Build from host
docker exec airstack-gcs-1 bash -c "bws --packages-select action_relay"
# Build with debug symbols
docker exec airstack-gcs-1 bash -c "bws --packages-select action_relay --cmake-args '-DCMAKE_BUILD_TYPE=Debug'"
Viewing Logs¶
Troubleshooting¶
Foxglove window won't display:
- Check
DISPLAYon the host:echo $DISPLAY - Allow X11 connections:
xhost +local:docker - Verify the X11 socket mount in
gcs-base-docker-compose.yaml
Host Foxglove won't connect:
- Use port 8766 from the host (8765 is the in-container port)
- Verify nothing else holds the port:
sudo lsof -i :8766 - Check the bridge node is up:
airstack logs gcs
Custom panels show "Unknown panel type":
install.pyruns on container start — restart the container after editing an extension, or re-runpython3 /root/AirStack/gcs/foxglove_extensions/install.pyand restart Foxglove
ROS 2 communication issues:
- The GCS lives on domain 0; robot topics reach it via the DDS router / relays, not raw discovery
- Check all containers are on
airstack_network docker exec airstack-gcs-1 bash -c "ros2 topic list"
SSH connection refused:
- Wait a few seconds after container launch for
sshd - Check the port mapping:
docker port airstack-gcs-1
See Also¶
- GCS Overview - Main GCS documentation
- GCS Foxglove Visualization - Layouts, visualizer, extending markers
- Docker Workflow - General Docker operations
- Robot Docker Configuration - Robot container setup