Implements Phase 2d outdoor autonomous navigation for SaltyBot. GPS source: SIM7600X /gps/fix from PR #65 (saltybot_cellular). saltybot_outdoor package: - osm_router_node: Overpass API + A* haversine graph + Douglas-Peucker simplification, /outdoor/route (Path) + /outdoor/waypoints (PoseArray) - gps_waypoint_follower_node: GPS->Nav2 navigate_through_poses bridge, quality-adaptive tolerances (2m cellular / 0.30m RTK) - geofence_node: ray-casting polygon safety, emergency stop on violation - outdoor_nav.launch.py: dual-EKF + navsat_transform + all nodes - outdoor_nav_params.yaml: 1.5m/s, no static_layer, 2m GPS tolerance - ekf_outdoor.yaml: robot_localization dual-EKF + navsat_transform - geofence_vertices.yaml: template with usage instructions docker-compose.yml: fix malformed saltybot-surround block; add saltybot-outdoor service (depends on saltybot-nav2, OSM NVMe cache) SLAM-SETUP-PLAN.md: Phase 2d done RTK upgrade: SIM7600X (+-2.5m) -> ZED-F9P (+-2cm), set use_rtk:=true Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
310 lines
9.5 KiB
YAML
310 lines
9.5 KiB
YAML
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
|
|
' to STM32.
|
|
# Cellular watchdog: 5s MQTT drop in AUTO mode -> 'F
|
|
' (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
|