New ROS2 package saltybot_surround:
surround_costmap_node
- Subscribes to /camera/{front,left,rear,right}/image_raw
- Detects obstacles via Canny edge detection + ground projection
- Pinhole back-projection: pixel row → forward distance (d = h*fy/(v-cy))
- Rotates per-camera points to base_link frame using known camera yaws
- Publishes /surround_vision/obstacles (PointCloud2, 5 Hz)
- Catches chairs, glass walls, people that RPLIDAR misses
- Placeholder IMX219 fisheye calibration (hook for real cal via cv2.fisheye)
surround_vision_node
- IPM homography computed from camera height + pinhole model
- 4× bird's-eye patches composited into 240×240px 360° overhead view
- Publishes /surround_vision/birdseye (Image, 10 Hz)
- Robot footprint + compass overlay
surround_vision.launch.py
- Launches both nodes with surround_vision_params.yaml
- start_cameras arg: set false when csi-cameras container runs separately
Updated:
- jetson/config/nav2_params.yaml add surround_cameras PointCloud2 source
to local + global costmap obstacle_layer
- jetson/docker-compose.yml add saltybot-surround service
(depends_on: csi-cameras, start_cameras:=false)
- projects/saltybot/SLAM-SETUP-PLAN.md Phase 2c ✅ Done
Calibration TODO (run after hardware assembly):
ros2 run camera_calibration cameracalibrator --size 8x6 --square 0.025 \
image:=/camera/front/image_raw camera:=/camera/front
Replace placeholder K/D in surround_costmap_node._undistort()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
225 lines
7.1 KiB
YAML
225 lines
7.1 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 # JetPack NVIDIA runtime
|
||
privileged: false # use device passthrough instead
|
||
network_mode: host # ROS2 DDS multicast needs host networking
|
||
|
||
environment:
|
||
- NVIDIA_VISIBLE_DEVICES=all
|
||
- NVIDIA_DRIVER_CAPABILITIES=all
|
||
- RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
|
||
- ROS_DOMAIN_ID=42
|
||
- DISPLAY=${DISPLAY:-:0} # for RViz2 on host X11
|
||
|
||
volumes:
|
||
# X11 socket for RViz2
|
||
- /tmp/.X11-unix:/tmp/.X11-unix:rw
|
||
# ROS2 workspace (host-mounted for live dev)
|
||
- ./ros2_ws/src:/ros2_ws/src:rw
|
||
# Persistent SLAM maps on NVMe
|
||
- /mnt/nvme/saltybot/maps:/maps
|
||
# NVMe data volume
|
||
- /mnt/nvme/saltybot:/data:rw
|
||
# Config files
|
||
- ./config:/config:ro
|
||
|
||
devices:
|
||
# RPLIDAR A1M8 — stable symlink via udev
|
||
- /dev/rplidar:/dev/rplidar
|
||
# STM32 USB CDC bridge — stable symlink via udev
|
||
- /dev/stm32-bridge:/dev/stm32-bridge
|
||
# RealSense D435i — USB3 device, needs udev rules
|
||
- /dev/bus/usb:/dev/bus/usb
|
||
# I2C bus (Orin Nano i2c-7 = 40-pin header pins 3/5)
|
||
- /dev/i2c-7:/dev/i2c-7
|
||
# CSI cameras via V4L2
|
||
- /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
|
||
"
|
||
|
||
# ── 4× 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 # CSI camera access requires elevated perms
|
||
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 # IMX219 /camera/*/image_raw must be publishing
|
||
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 &&
|
||
ros2 launch saltybot_surround surround_vision.launch.py
|
||
start_cameras:=false
|
||
camera_height:=0.30
|
||
publish_rate:=5.0
|
||
"
|
||
|
||
# ── 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 # RTAB-Map + sensors must be running first
|
||
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 &&
|
||
ros2 launch saltybot_bringup nav2.launch.py
|
||
"
|
||
|
||
volumes:
|
||
saltybot-maps:
|
||
driver: local
|