# Docker Configuration for TidyBot++ Platform
This directory contains Docker configurations for running the TidyBot++ ROS 2 platform and NVIDIA Isaac Sim simulator.
## 📁 Directory Structure
```
docker/
├── tidybot/ # TidyBot platform container
│ ├── Dockerfile # Ubuntu 24.04 + ROS 2 Jazzy + Miniforge + workspace
│ ├── build.sh # Build the TidyBot image
│ ├── run.sh # Run the TidyBot container
│ └── entrypoint.sh # Container entrypoint script
│
├── isaac-sim-ros2/ # Isaac Sim + ROS 2 container
│ ├── Dockerfile # Isaac Sim 5.1.0 + ROS 2 Jazzy
│ ├── build.sh # Build the Isaac Sim image
│ ├── run.sh # Run the Isaac Sim container
│ └── entrypoint.sh # Container entrypoint script
│
├── fastdds.xml # DDS config for local cross-container ROS 2
├── cyclonedds.xml # CycloneDDS config for cross-machine ROS 2
├── diagnose_dds.sh # DDS connectivity diagnostic script
└── README.md # This file
```
---
## 🚀 Quick Start
### TidyBot Platform
```bash
# Build the image (first time only)
./docker/tidybot/build.sh
# Run the container
./docker/tidybot/run.sh restart
```
### Isaac Sim with ROS 2
```bash
# Pull the base image (first time only)
docker pull nvcr.io/nvidia/isaac-sim:5.1.0
# Build and run (image is built automatically if not present)
./docker/isaac-sim-ros2/run.sh restart
# Or build manually first
./docker/isaac-sim-ros2/build.sh
./docker/isaac-sim-ros2/run.sh restart
```
---
## 📜 Script Reference
### TidyBot Container (`docker/tidybot/`)
| Script | Description |
|--------|-------------|
| `build.sh` | Build the TidyBot platform Docker image with ROS 2 Jazzy |
| `run.sh restart` | Stop existing container and start fresh (FastDDS) |
| `run.sh restart dds=cyclone` | Start fresh with CycloneDDS (for cross-machine VLA) |
| `run.sh` | Start or attach to existing container |
### Isaac Sim Container (`docker/isaac-sim-ros2/`)
| Script | Description |
|--------|-------------|
| `build.sh` | Build the Isaac Sim + ROS 2 Jazzy image |
| `run.sh restart` | Start the container with Isaac Sim GUI |
| `run.sh restart shell` | Start container with bash shell only |
| `run.sh build` | Force rebuild the Docker image |
---
## 🎮 Isaac Sim Integration
### Prerequisites
1. **NVIDIA GPU** with driver version ≥ 535.x
2. **NVIDIA Container Toolkit** installed:
```bash
sudo apt-get install nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
```
3. **Base Docker image** pulled:
```bash
docker pull nvcr.io/nvidia/isaac-sim:5.1.0
```
### Container Mounts
| Host Path | Container Path | Description |
|-----------|----------------|-------------|
| `src/tidybot_description/` | `/tidybot_description` | Robot URDF and meshes |
| `isaac_sim_workspace/` | `/isaac_workspace` | USD output directory |
| `~/docker/isaac-sim/` | Various cache paths | Persistent cache |
---
## 🔗 ROS 2 Communication Between Containers
Both containers are configured to communicate via ROS 2 DDS using the shared `fastdds.xml` configuration.
| Setting | Value |
|---------|-------|
| Network | `--network=host` |
| ROS_DOMAIN_ID | `0` (configurable via environment) |
| RMW_IMPLEMENTATION | `rmw_fastrtps_cpp` |
| DDS Config | `docker/fastdds.xml` |
## 🌐 Cross-Machine Communication (CycloneDDS)
For communication between the robot and a remote GPU server (e.g., for VLA inference), use **CycloneDDS** instead of FastDDS. FastDDS fragments published images during cross-machine transport, leading to corrupted data.
### Configuration
Edit `docker/cyclonedds.xml` on each machine:
**Client side (robot / laptop):**
```xml
```
**Server side (GPU server):**
```xml
```
Both sides **must** use the same `Domain Id` and `MaxMessageSize`.
### Usage
Pass `dds=cyclone` to `run.sh`:
```bash
# Client side (robot / laptop)
./docker/tidybot/run.sh restart dds=cyclone
# Server side (GPU server) — outside Docker
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=$(realpath docker/cyclonedds.xml)
export ROS_DOMAIN_ID=0
```
If `CYCLONEDDS_XML` is not set, `run.sh` defaults to `docker/cyclonedds.xml`.
For full VLA deployment instructions, see [`src/external/openvla/README.md`](../src/external/openvla/README.md).
## 🛠️ Troubleshooting
### Isaac Sim Won't Start
1. **Check GPU access:**
```bash
docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi
```
2. **Check X11 permissions:**
```bash
xhost +local:
```
3. **On Wayland:** Switch to X11 session or set:
```bash
export DISPLAY=:0
```
### Container Can't Write to Mounted Directories
```bash
# Fix permissions for Isaac Sim workspace
chmod -R 777 ~/Documents/tidybot_platform/isaac_sim_workspace/
chmod -R 777 ~/docker/isaac-sim/
```
### ROS 2 Topics Not Visible Between Containers
1. Ensure both containers use the same `ROS_DOMAIN_ID`:
```bash
export ROS_DOMAIN_ID=0
```
2. Check that `--network=host` is set for both containers
3. Verify FastDDS configuration is mounted correctly:
```bash
# Inside container
cat /fastdds.xml
echo $FASTRTPS_DEFAULT_PROFILES_FILE
```
4. Restart the ROS 2 daemon in both containers:
```bash
ros2 daemon stop && ros2 daemon start
```
### Isaac Sim ROS 2 Bridge Not Publishing
1. Make sure the simulation is **playing** (not paused)
2. Check that `isaacsim.ros2.bridge` extension is enabled (Window > Extensions)
3. Verify the ActionGraph is properly configured
4. Check the Isaac Sim console for errors
### CycloneDDS Cross-Machine Connection Not Working
Use the diagnostic script to check both machines:
```bash
bash docker/diagnose_dds.sh
```
#### 1. Verify UDP connectivity in both directions
DDS requires **bidirectional UDP**. Test with:
```bash
# On machine A (listen):
python3 -c "
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('0.0.0.0', 7400))
s.settimeout(10)
print('Listening on UDP 7400...')
try:
data, addr = s.recvfrom(1024)
print(f'SUCCESS: Received from {addr}')
except socket.timeout:
print('FAIL: No packet received')
"
# On machine B (send):
python3 -c "
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b'hello', ('', 7400))
print('Sent')
"
```
Test **both directions** (swap A and B). If one direction fails, the firewall on the receiving machine is blocking inbound UDP.
#### 2. Open the local firewall
If `ufw` is active, it will block inbound UDP by default:
```bash
# Check firewall status
sudo ufw status
# Allow UDP from the peer
sudo ufw allow proto udp from
```
#### 3. Common configuration mistakes
| Issue | Symptom | Fix |
|-------|---------|-----|
| Domain ID mismatch | No topics discovered | Ensure `Domain Id` in XML matches `ROS_DOMAIN_ID` env var on both sides |
| Wrong NetworkInterface | CycloneDDS binds to wrong interface | Set `address=` to an IP that exists on this machine (`ip a` to check) |
| Missing peer | No discovery | Each side's `` must point to the **other** machine's IP |
| MaxMessageSize mismatch | Image corruption | Use `65500B` on both sides |
| `RMW_IMPLEMENTATION` not set | Uses default FastDDS | Must be `rmw_cyclonedds_cpp` on both sides |
#### 4. VPN-specific issues
When connecting through a VPN (e.g., Cisco AnyConnect):
- Use the **VPN tunnel IP** (from `cscotun0`) as the NetworkInterface address, not the WiFi IP
- The VPN IP is **dynamic** — update `cyclonedds.xml` on both sides after each VPN reconnect
#### 5. Quick end-to-end ROS 2 test
```bash
# Server:
ros2 run demo_nodes_cpp talker
# Client:
ros2 run demo_nodes_cpp listener
# Should print: "I heard: [Hello World: X]"
```
---
## 📚 Additional Resources
- [Isaac Sim Documentation](https://docs.isaacsim.omniverse.nvidia.com/)
- [Isaac Sim ROS 2 Installation](https://docs.isaacsim.omniverse.nvidia.com/latest/installation/install_ros.html)
- [Isaac Sim ROS 2 Tutorials](https://docs.isaacsim.omniverse.nvidia.com/latest/ros2_tutorials/index.html)
- [TidyBot++ Project](https://tidybot2.github.io/)