Documentation Index
Fetch the complete documentation index at: https://docs.cyberwave.com/llms.txt
Use this file to discover all available pages before exploring further.
Docker Images
The full robot stack is published as four images on Docker Hub, each available in three tags (jazzy, humble, jetson-humble):
| Image | Description |
|---|
cyberwaveos/go2-ros2-driver | Go2 driver, streaming bridges, edge bridge |
cyberwaveos/ros2-nav2 | Nav2 navigation stack (robot-agnostic) |
cyberwaveos/ros2-slam | SLAM — RTAB-Map, slam_toolbox, Cartographer (robot-agnostic) |
cyberwaveos/ros2-elevation-mapping | GPU-accelerated elevation mapping (robot-agnostic) |
The jetson-humble driver image ships with optimized defaults for constrained hardware.
docker pull cyberwaveos/go2-ros2-driver:jazzy
docker pull cyberwaveos/ros2-nav2:jazzy
docker pull cyberwaveos/ros2-slam:jazzy
docker pull cyberwaveos/ros2-elevation-mapping:jazzy
Quick Start
When connected to the Cyberwave platform, the edge-core service automatically pulls and starts all required containers based on the asset metadata configured for your twin.
For local development with Docker Compose, see the driver README.
ROS 2 Topics
The driver publishes standard ROS 2 topics that any ROS 2 node can consume independently:
| Topic | Type | Description |
|---|
/odom | nav_msgs/Odometry | LiDAR-based odometry |
/point_cloud2 | sensor_msgs/PointCloud2 | 3D point cloud from LiDAR |
/scan | sensor_msgs/LaserScan | 2D laser scan (from point cloud) |
/camera/image_raw | sensor_msgs/Image | Front camera frames |
/joint_states | sensor_msgs/JointState | Joint positions and velocities |
/imu/data | sensor_msgs/Imu | IMU measurements |
/map | nav_msgs/OccupancyGrid | Occupancy map (from SLAM) |
All robot-specific topics are namespaced as twin_<your-twin-uuid> by default.
Key Environment Variables
| Variable | Default | Description |
|---|
CYBERWAVE_API_KEY | — | Cyberwave API token |
CYBERWAVE_TWIN_UUID | — | Digital twin UUID |
CYBERWAVE_MQTT_HOST | mqtt.cyberwave.com | MQTT broker |
ROBOT_IP | (auto-discovered) | Go2 IP — validated on startup; see below |
ROBOT_IP_FALLBACKS | 192.168.12.1,192.168.123.161 | Comma-separated IPs to probe when ROBOT_IP is unset or unreachable |
CONFIG_PROFILE | (auto) | Config profile: jazzy, humble, or jetson |
CYBERWAVE_PERIODIC_MAP_UPLOAD_INTERVAL_SEC | 120 | Periodic map cloud sync interval (0 to disable) |
GO2_USE_CPP_VOXEL_DECODER | true | Use C++ LiDAR decoder (faster) |
GO2_OBSTACLE_AVOIDANCE | true | Enable onboard obstacle avoidance (see below) |
CYBERWAVE_ENABLE_RECORDING | (see below) | Global recording toggle for video streams |
CYBERWAVE_ENABLE_RECORDING_PREVIEW | false | Record preview streams (occupancy, costmaps) |
Obstacle Avoidance
When GO2_OBSTACLE_AVOIDANCE=true (the default), velocity commands are routed
through the Go2’s firmware obstacle avoidance pipeline instead of the standard
Sport API. The firmware fuses ultrasonic and LiDAR sensors to detect obstacles
and will override velocity commands to prevent collisions.
On startup the driver sends SwitchSet and UseRemoteCommandFromApi to the
robot so the firmware accepts velocity commands from the ROS 2 stack rather than
only the physical remote controller.
Onboard obstacle avoidance operates independently from Nav2’s path planner.
The firmware may intervene — slowing down or stopping the robot — even when
Nav2 considers the path clear. This can cause Nav2 goals to take longer or be
aborted if the firmware repeatedly blocks planned motion. If you need full
control over obstacle handling through Nav2’s costmaps and local planner, set
GO2_OBSTACLE_AVOIDANCE=false.
To disable:
GO2_OBSTACLE_AVOIDANCE=false
Robot IP discovery
The driver validates that the Go2 is reachable before starting any ROS 2
node, so you get a fast failure instead of a silent WebRTC hang.
Resolution order:
ROBOT_IP (from env or Edge Core metadata) — probed via TCP on port 9991.
- If unreachable (or unset), each address in
ROBOT_IP_FALLBACKS is tried.
- First address that responds is used for the entire stack.
- If nothing responds, the container exits with an error and pushes a
robot_unreachable alert to Cyberwave (if SDK credentials are available).
The default fallback list covers the two most common Go2 network configurations:
| Address | Scenario |
|---|
192.168.12.1 | Wi-Fi AP / USB-Ethernet |
192.168.123.161 | Ethernet direct-connect |
Custom networks: If your Go2 is on a different subnet (e.g. behind a
router), override the fallback list:
# .env file or Edge Core shared_env
ROBOT_IP_FALLBACKS=10.0.0.50,10.0.0.51
You can set both ROBOT_IP (preferred address) and ROBOT_IP_FALLBACKS
(safety net). If the preferred IP is unreachable, the driver automatically
falls through to the fallback list.
The probe uses a TCP socket connect on port 9991 (Go2 WebRTC signaling) with a
2-second timeout per address. No WebRTC handshake is attempted. Simulation
launches skip the check entirely.
Config Profiles
| Profile | When to use |
|---|
jazzy | Default for ROS 2 Jazzy on desktop/server hardware |
humble | ROS 2 Humble ecosystem compatibility |
jetson | NVIDIA Jetson: reduced costmap resolution, increased transform tolerances, lower rates |
Set CONFIG_PROFILE=jetson or use the jetson-humble Docker image which sets this automatically.
When using Cyberwave Edge Core for managed deployment, the driver stack is
configured through twin/asset metadata. Edge Core reads this metadata, pulls
images, and injects environment variables automatically.
Multi-container configuration
The Go2 ROS 2 stack runs as multiple cooperating containers. Define the stack in
metadata.drivers using the services array:
{
"drivers": {
"linux-aarch64-jetson": {
"services": [
{
"image": "cyberwaveos/go2-ros2-driver:jetson-humble",
"name": "driver",
"command": [
"ros2",
"launch",
"cyberwave_go2_driver",
"robot_driver.launch.py"
]
},
{
"image": "cyberwaveos/go2-ros2-driver:jetson-humble",
"name": "bridges",
"command": [
"ros2",
"launch",
"cyberwave_go2_driver",
"robot_bridges.launch.py"
]
},
{ "image": "cyberwaveos/ros2-nav2:jetson-humble", "name": "nav2" },
{ "image": "cyberwaveos/ros2-slam:jetson-humble", "name": "slam" }
],
"shared_env": {
"CONFIG_PROFILE": "jetson",
"ROS_DOMAIN_ID": "0"
},
"shared_params": ["--network", "host", "-v", "/data:/data"]
},
"default": {
"docker_image": "cyberwaveos/go2-ros2-driver:jazzy",
"prefer_gpu": true
}
}
}
To add a service (e.g. elevation mapping), append to the services array. To
set the robot IP or fallback list, add them to shared_env:
"shared_env": {
"ROBOT_IP_FALLBACKS": "10.0.0.50,10.0.0.51",
"CONFIG_PROFILE": "jetson"
}
See Drivers overview for the full metadata schema and
Writing compatible drivers for the
edge_configs per-device pattern.
Stream Recording
The Go2 driver streams video to the Cyberwave media service via WebRTC. Each
stream can request server-side recording (producing MP4 artifacts for later
replay).
| Variable | Default | Applies to |
|---|
CYBERWAVE_ENABLE_RECORDING | — | All streams (global override) |
CYBERWAVE_ENABLE_RECORDING_PREVIEW | false | Preview streams only (occupancy grid, local/global costmaps) |
Default behavior (no env vars set):
camera_bridge — records by default (true).
occupancy_grid_bridge, local_costmap_bridge, global_costmap_bridge — do not record by default.
Resolution order per bridge:
CYBERWAVE_ENABLE_RECORDING — if set, overrides everything.
- Per-bridge env var (e.g.
CYBERWAVE_ENABLE_RECORDING_PREVIEW) — overrides the built-in default.
- Built-in default.
# Record everything including previews
CYBERWAVE_ENABLE_RECORDING_PREVIEW=true
# Disable all recording (e.g. for development)
CYBERWAVE_ENABLE_RECORDING=false
Recording is handled by the media service SFU, not on the edge device.
Enabling recording adds negligible load to the robot’s hardware.
Running the full Go2 stack on a Jetson Orin Nano (or similar constrained hardware) requires tuning runtime rates and
encoder settings. Add these to your .env file or Edge Core shared_env:
# ── Runtime rates (overrides vs. defaults) ───────────────────────────
GO2_STATE_PUBLISH_HZ=15 # default 10
GO2_POINTCLOUD_MAX_POINTS=4000 # default 25000
GO2_POINTCLOUD_STREAM_FPS=1 # default 5
GO2_SCAN_ANGLE_INCREMENT=0.01745 # default 0.00872
GO2_TRAV_COSTMAP_HZ=5 # default 10
GO2_TRAV_COSTMAP_GLOBAL_HZ=0.5 # default 1
GO2_LOCAL_COSTMAP_FPS=10 # default 5
GO2_GLOBAL_COSTMAP_FPS=10 # default 2
# ── Obstacle avoidance ──────────────────────────────────────────────
# Disable if Nav2 handles obstacle avoidance to save CPU.
GO2_OBSTACLE_AVOIDANCE=false # default true
# ── H.264 encoding ──────────────────────────────────────────────────
# Jetson Orin Nano has NO hardware H.264 encoder (no NVENC).
# Skip codec probing and constrain libx264 to a single thread so it
# doesn't starve SLAM/Nav2. At 424x240@1fps ultrafast this uses <5%
# of one core.
CYBERWAVE_H264_ENCODER=libx264 # default auto
CYBERWAVE_H264_PRESET=ultrafast # default veryfast
CYBERWAVE_H264_BITRATE_KBPS=800 # default 2500
CYBERWAVE_H264_THREADS=1 # default 0 (unlimited)
These values are tuned for a Jetson Orin Nano running the full stack (driver +
Nav2 + SLAM). If your board has more headroom (e.g. Orin NX) or less (e.g.
other services running), you can adjust camera and pointcloud rates
accordingly.
GPU-accelerated containers (simulation, video decode, elevation mapping) require the NVIDIA Container Toolkit. This is especially important on Jetson (L4T / JetPack) where the toolkit must be installed and the runtime registered with Docker.
# Install
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
# Register with Docker
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
On Jetson, set the NVIDIA runtime as the default so all containers get GPU access automatically:
sudo tee /etc/docker/daemon.json <<'EOF'
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"args": [],
"path": "nvidia-container-runtime"
}
}
}
EOF
sudo systemctl restart docker
If nvidia-container-toolkit is already installed but containers cannot
access the GPU, re-run the nvidia-ctk runtime configure step, ensure
daemon.json has the nvidia runtime, and restart Docker.