version: "3.8" # Jetson Orin Nano Super — ROS2 Humble SLAM stack # Run with: docker compose up -d # Requires: NVIDIA Container Toolkit (JetPack 6) on host services: # ── Core ROS2 + SLAM node ───────────────────────────────────────────────── saltybot-ros2: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-ros2 restart: unless-stopped runtime: nvidia privileged: false network_mode: host environment: - NVIDIA_VISIBLE_DEVICES=all - NVIDIA_DRIVER_CAPABILITIES=all - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - ROS_DOMAIN_ID=42 - DISPLAY=${DISPLAY:-:0} volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw - ./ros2_ws/src:/ros2_ws/src:rw - /mnt/nvme/saltybot/maps:/maps - /mnt/nvme/saltybot:/data:rw - ./config:/config:ro devices: - /dev/rplidar:/dev/rplidar - /dev/stm32-bridge:/dev/stm32-bridge - /dev/bus/usb:/dev/bus/usb - /dev/i2c-7:/dev/i2c-7 - /dev/video0:/dev/video0 - /dev/video2:/dev/video2 - /dev/video4:/dev/video4 - /dev/video6:/dev/video6 command: > bash -c " source /opt/ros/humble/setup.bash && ros2 launch saltybot_bringup slam_rtabmap.launch.py " # ── RPLIDAR driver node ──────────────────────────────────────────────────── rplidar: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-rplidar restart: unless-stopped runtime: nvidia network_mode: host environment: - ROS_DOMAIN_ID=42 - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp devices: - /dev/rplidar:/dev/rplidar command: > bash -c " source /opt/ros/humble/setup.bash && ros2 launch rplidar_ros rplidar_a1_launch.py serial_port:=/dev/rplidar frame_id:=laser " # ── RealSense D435i driver node ──────────────────────────────────────────── realsense: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-realsense restart: unless-stopped runtime: nvidia network_mode: host environment: - ROS_DOMAIN_ID=42 - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp devices: - /dev/bus/usb:/dev/bus/usb volumes: - /dev:/dev command: > bash -c " source /opt/ros/humble/setup.bash && ros2 launch realsense2_camera rs_launch.py enable_color:=true enable_depth:=true enable_gyro:=true enable_accel:=true unite_imu_method:=linear_interpolation depth_module.profile:=640x480x30 rgb_camera.profile:=640x480x30 " # ── STM32 bridge node (bidirectional serial<->ROS2) ──────────────────────── stm32-bridge: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-stm32-bridge restart: unless-stopped runtime: nvidia network_mode: host environment: - ROS_DOMAIN_ID=42 - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp devices: - /dev/stm32-bridge:/dev/stm32-bridge command: > bash -c " source /opt/ros/humble/setup.bash && ros2 launch saltybot_bridge bridge.launch.py mode:=bidirectional serial_port:=/dev/stm32-bridge " # ── 4x IMX219 CSI cameras ────────────────────────────────────────────────── csi-cameras: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-csi-cameras restart: unless-stopped runtime: nvidia network_mode: host privileged: true environment: - ROS_DOMAIN_ID=42 - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - NVIDIA_VISIBLE_DEVICES=all - NVIDIA_DRIVER_CAPABILITIES=all devices: - /dev/video0:/dev/video0 - /dev/video2:/dev/video2 - /dev/video4:/dev/video4 - /dev/video6:/dev/video6 command: > bash -c " source /opt/ros/humble/setup.bash && ros2 launch saltybot_cameras csi_cameras.launch.py width:=640 height:=480 fps:=30 " # ── Surround vision — 360 bird's-eye view + Nav2 camera obstacle layer ───── saltybot-surround: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-surround restart: unless-stopped runtime: nvidia network_mode: host depends_on: - csi-cameras environment: - NVIDIA_VISIBLE_DEVICES=all - NVIDIA_DRIVER_CAPABILITIES=all - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - ROS_DOMAIN_ID=42 volumes: - ./ros2_ws/src:/ros2_ws/src:rw - ./config:/config:ro command: > bash -c " source /opt/ros/humble/setup.bash && source /ros2_ws/install/local_setup.bash 2>/dev/null || true && ros2 launch saltybot_surround surround_vision.launch.py start_cameras:=false camera_height:=0.30 publish_rate:=5.0 " # ── rosbridge WebSocket server — ws://jetson:9090 ────────────────────────── rosbridge: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-rosbridge restart: unless-stopped runtime: nvidia network_mode: host depends_on: - saltybot-ros2 - stm32-bridge - csi-cameras environment: - ROS_DOMAIN_ID=42 - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp volumes: - ./ros2_ws/src:/ros2_ws/src:rw - ./config:/config:ro command: > bash -c " source /opt/ros/humble/setup.bash && source /ros2_ws/install/local_setup.bash 2>/dev/null || true && ros2 launch saltybot_bringup rosbridge.launch.py " # -- Remote e-stop bridge (MQTT over 4G -> STM32 CDC) ---------------------- # Subscribes to saltybot/estop MQTT topic. {"kill":true} -> 'E\r\n' to STM32. # Cellular watchdog: 5s MQTT drop in AUTO mode -> 'F\r\n' (ESTOP_CELLULAR_TIMEOUT). remote-estop: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-remote-estop restart: unless-stopped runtime: nvidia network_mode: host depends_on: - stm32-bridge environment: - ROS_DOMAIN_ID=42 - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp devices: - /dev/stm32-bridge:/dev/stm32-bridge volumes: - ./ros2_ws/src:/ros2_ws/src:rw - ./config:/config:ro command: > bash -c " source /opt/ros/humble/setup.bash && source /ros2_ws/install/local_setup.bash 2>/dev/null || true && pip install paho-mqtt --quiet 2>/dev/null || true && ros2 launch saltybot_bridge remote_estop.launch.py " # ── Nav2 autonomous navigation stack ────────────────────────────────────── saltybot-nav2: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-nav2 restart: unless-stopped runtime: nvidia network_mode: host depends_on: - saltybot-ros2 environment: - NVIDIA_VISIBLE_DEVICES=all - NVIDIA_DRIVER_CAPABILITIES=all - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - ROS_DOMAIN_ID=42 volumes: - ./ros2_ws/src:/ros2_ws/src:rw - ./config:/config:ro command: > bash -c " source /opt/ros/humble/setup.bash && source /ros2_ws/install/local_setup.bash 2>/dev/null || true && ros2 launch saltybot_bringup nav2.launch.py " # ── Outdoor navigation — OSM routing + GPS waypoints + geofence ─────────── # Start only for outdoor missions. Usage: # ros2 param set /osm_router goal_lat 37.7749 # ros2 param set /osm_router goal_lon -122.4194 # ros2 service call /outdoor/plan_route std_srvs/srv/Trigger '{}' # ros2 service call /outdoor/start_navigation std_srvs/srv/Trigger '{}' saltybot-outdoor: image: saltybot/ros2-humble:jetson-orin build: context: . dockerfile: Dockerfile container_name: saltybot-outdoor restart: unless-stopped runtime: nvidia network_mode: host depends_on: - saltybot-nav2 environment: - NVIDIA_VISIBLE_DEVICES=all - NVIDIA_DRIVER_CAPABILITIES=all - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - ROS_DOMAIN_ID=42 volumes: - ./ros2_ws/src:/ros2_ws/src:rw - ./config:/config:ro - /mnt/nvme/saltybot/osm_cache:/data/osm_cache:rw devices: - /dev/sim7600:/dev/sim7600 command: > bash -c " source /opt/ros/humble/setup.bash && source /ros2_ws/install/local_setup.bash 2>/dev/null || true && ros2 launch saltybot_outdoor outdoor_nav.launch.py use_rtk:=false fence_active:=true " volumes: saltybot-maps: driver: local