Task A — Orin Nano Super platform update: - docker-compose.yml: update header/comments, switch all service image tags to jetson-orin, update devices to udev symlinks (/dev/rplidar, /dev/stm32-bridge, i2c-7), add NVMe volume mounts (/mnt/nvme/saltybot), update stm32-bridge to saltybot_bridge launch, add csi-cameras service - docs/pinout.md: full rewrite for Orin Nano Super — i2c-7, ttyTHS0, CSI-A/B connectors, M.2 NVMe slot, IMX219 15-pin FFC pinout, V4L2 nodes, GStreamer test commands, updated udev rules - docs/power-budget.md: full rewrite — 25W TDP, 8GB LPDDR5, 67 TOPS, 4-camera CSI bandwidth analysis, nvpmodel modes, Nano vs Orin comparison, 5V 6A PSU recommendation, 4S LiPo architecture - scripts/setup-jetson.sh: full rewrite — JetPack 6 / Ubuntu 22.04, nvidia-container-toolkit new keyring method, NVMe partition/format/fstab, CSI driver check (imx219 modprobe), video group, jtop install, 8GB swap Task B — saltybot_cameras ROS2 package: - launch/csi_cameras.launch.py: 4x v4l2_camera nodes, namespace per camera (front/left/rear/right), 640x480x30fps, includes TF launch automatically - launch/camera_tf.launch.py: static TF for 4 cameras at 90deg intervals on sensor_head_link (r=5cm offset), yaw 0/90/180/-90 deg - package.xml, setup.py, setup.cfg, __init__.py, resource marker - config/cameras_params.yaml: per-camera device/frame/offset configuration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
208 lines
8.8 KiB
Bash
208 lines
8.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Jetson Orin Nano Super host setup script
|
|
# Run once on fresh JetPack 6 installation (Ubuntu 22.04)
|
|
# Usage: sudo bash setup-jetson.sh
|
|
|
|
set -euo pipefail
|
|
|
|
echo "=== Jetson Orin Nano Super Host Setup — saltybot ==="
|
|
echo "JetPack 6.x / L4T R36.x / Ubuntu 22.04 expected"
|
|
|
|
# ── Verify we're on Jetson Orin ───────────────────────────────────────────────
|
|
if ! uname -m | grep -q aarch64; then
|
|
echo "ERROR: Must run on Jetson (aarch64). Got: $(uname -m)"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f /etc/nv_tegra_release ]; then
|
|
L4T_VER=$(head -1 /etc/nv_tegra_release | grep -o 'R[0-9]*' | head -1)
|
|
echo "[i] Detected L4T: $L4T_VER"
|
|
if [[ "$L4T_VER" != "R36" ]]; then
|
|
echo "WARNING: Expected L4T R36 (JetPack 6). Got $L4T_VER"
|
|
echo " This script is tuned for Orin Nano Super / JetPack 6."
|
|
read -rp "Continue anyway? [y/N] " ans
|
|
[[ "${ans,,}" == "y" ]] || exit 1
|
|
fi
|
|
fi
|
|
|
|
# ── System update ─────────────────────────────────────────────────────────────
|
|
apt-get update && apt-get upgrade -y
|
|
|
|
# ── Install Docker + NVIDIA Container Toolkit ─────────────────────────────────
|
|
if ! command -v docker &>/dev/null; then
|
|
echo "[+] Installing Docker..."
|
|
curl -fsSL https://get.docker.com | sh
|
|
usermod -aG docker "$SUDO_USER"
|
|
fi
|
|
|
|
# NVIDIA Container Toolkit (JetPack 6 method — replaces legacy nvidia-docker2)
|
|
if ! dpkg -l | grep -q nvidia-container-toolkit; then
|
|
echo "[+] Installing NVIDIA Container Toolkit..."
|
|
distribution=$(. /etc/os-release; echo "${ID}${VERSION_ID}")
|
|
# JetPack 6 / Ubuntu 22.04 uses the new toolkit keyring
|
|
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
|
|
| 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' \
|
|
> /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
|
apt-get update
|
|
apt-get install -y nvidia-container-toolkit
|
|
nvidia-ctk runtime configure --runtime=docker
|
|
fi
|
|
|
|
# Configure Docker daemon for NVIDIA runtime + NVMe data root
|
|
cat > /etc/docker/daemon.json << 'EOF'
|
|
{
|
|
"runtimes": {
|
|
"nvidia": {
|
|
"path": "nvidia-container-runtime",
|
|
"runtimeArgs": []
|
|
}
|
|
},
|
|
"default-runtime": "nvidia",
|
|
"data-root": "/mnt/nvme/docker"
|
|
}
|
|
EOF
|
|
systemctl restart docker
|
|
|
|
# ── Set Jetson power mode to MAXN 25W ─────────────────────────────────────────
|
|
echo "[+] Setting MAXN 25W power mode (Orin Nano Super)..."
|
|
nvpmodel -m 0
|
|
jetson_clocks
|
|
|
|
# ── NVMe SSD setup ────────────────────────────────────────────────────────────
|
|
echo "[+] Setting up NVMe SSD..."
|
|
if lsblk | grep -q nvme; then
|
|
NVME_DEV=$(lsblk -d -n -o NAME | grep nvme | head -1)
|
|
NVME_PATH="/dev/$NVME_DEV"
|
|
|
|
if ! lsblk "${NVME_PATH}" | grep -q "${NVME_DEV}p1"; then
|
|
echo " [+] Partitioning NVMe at ${NVME_PATH}..."
|
|
parted "${NVME_PATH}" --script mklabel gpt
|
|
parted "${NVME_PATH}" --script mkpart primary ext4 0% 100%
|
|
mkfs.ext4 -F "${NVME_PATH}p1"
|
|
fi
|
|
|
|
mkdir -p /mnt/nvme
|
|
if ! grep -q "/mnt/nvme" /etc/fstab; then
|
|
NVME_UUID=$(blkid -s UUID -o value "${NVME_PATH}p1")
|
|
echo "UUID=${NVME_UUID} /mnt/nvme ext4 defaults,noatime 0 2" >> /etc/fstab
|
|
fi
|
|
mount -a
|
|
|
|
# Create saltybot directories on NVMe
|
|
mkdir -p /mnt/nvme/{saltybot,docker,rosbags,slam-maps}
|
|
mkdir -p /mnt/nvme/saltybot/maps
|
|
chown -R "$SUDO_USER":"$SUDO_USER" /mnt/nvme/saltybot /mnt/nvme/rosbags /mnt/nvme/slam-maps
|
|
echo " [+] NVMe mounted at /mnt/nvme"
|
|
else
|
|
echo " [!] No NVMe detected. Skipping NVMe setup."
|
|
echo " Install an M.2 NVMe SSD in the Key M slot for best performance."
|
|
fi
|
|
|
|
# ── Install udev rules ────────────────────────────────────────────────────────
|
|
echo "[+] Installing udev rules..."
|
|
cat > /etc/udev/rules.d/99-saltybot.rules << 'EOF'
|
|
# RPLIDAR A1M8 (SiliconLabs CP2102)
|
|
KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", \
|
|
SYMLINK+="rplidar", MODE="0666"
|
|
|
|
# STM32 USB CDC (STMicroelectronics Virtual COM)
|
|
KERNEL=="ttyACM*", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", \
|
|
SYMLINK+="stm32-bridge", MODE="0666"
|
|
|
|
# Intel RealSense D435i
|
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="8086", ATTRS{idProduct}=="0b3a", \
|
|
MODE="0666"
|
|
|
|
# IMX219 CSI cameras via V4L2
|
|
KERNEL=="video[0246]", SUBSYSTEM=="video4linux", MODE="0666", GROUP="video"
|
|
EOF
|
|
|
|
udevadm control --reload-rules
|
|
udevadm trigger
|
|
|
|
# ── Install RealSense udev rules ──────────────────────────────────────────────
|
|
echo "[+] Installing RealSense udev rules..."
|
|
if [ ! -f /etc/udev/rules.d/99-realsense-libusb.rules ]; then
|
|
wget -q -O /etc/udev/rules.d/99-realsense-libusb.rules \
|
|
https://raw.githubusercontent.com/IntelRealSense/librealsense/master/config/99-realsense-libusb.rules
|
|
udevadm control --reload-rules
|
|
udevadm trigger
|
|
fi
|
|
|
|
# ── Enable I2C + UART ─────────────────────────────────────────────────────────
|
|
echo "[+] Enabling I2C and UART..."
|
|
modprobe i2c-dev
|
|
# Add user to required groups (i2c-7 on Orin Nano)
|
|
usermod -aG i2c,dialout,gpio,video "$SUDO_USER"
|
|
|
|
# ── Configure UART (disable console on ttyTHS0) ───────────────────────────────
|
|
# ttyTHS0 is the 40-pin header UART on Orin — disable serial console to free it
|
|
if grep -q "console=ttyTCU0" /boot/extlinux/extlinux.conf 2>/dev/null; then
|
|
echo "[i] Serial console is on ttyTCU0 (debug UART) — ttyTHS0 is free."
|
|
else
|
|
echo "[!] Check /boot/extlinux/extlinux.conf — ensure ttyTHS0 is not used"
|
|
echo " as a serial console if you need it for STM32 UART fallback."
|
|
fi
|
|
|
|
# ── Check CSI camera drivers ──────────────────────────────────────────────────
|
|
echo "[+] Checking CSI camera drivers..."
|
|
if modprobe imx219 2>/dev/null; then
|
|
echo " [+] IMX219 driver loaded."
|
|
else
|
|
echo " [!] IMX219 driver not available — may need JetPack camera driver package."
|
|
echo " Install: sudo apt-get install nvidia-jetpack"
|
|
fi
|
|
|
|
if command -v v4l2-ctl &>/dev/null; then
|
|
echo " [i] V4L2 devices:"
|
|
v4l2-ctl --list-devices 2>/dev/null || echo " (no cameras detected yet)"
|
|
else
|
|
apt-get install -y v4l-utils
|
|
fi
|
|
|
|
# ── Docker Compose ────────────────────────────────────────────────────────────
|
|
if ! command -v docker-compose &>/dev/null && ! docker compose version &>/dev/null 2>&1; then
|
|
echo "[+] Installing docker-compose plugin..."
|
|
apt-get install -y docker-compose-plugin
|
|
fi
|
|
|
|
# ── Swap (prefer NVMe if available) ──────────────────────────────────────────
|
|
if [ "$(swapon --show | wc -l)" -le 1 ]; then
|
|
if [ -d /mnt/nvme ]; then
|
|
echo "[+] Creating 8GB swap on NVMe..."
|
|
fallocate -l 8G /mnt/nvme/swapfile
|
|
chmod 600 /mnt/nvme/swapfile
|
|
mkswap /mnt/nvme/swapfile
|
|
swapon /mnt/nvme/swapfile
|
|
echo '/mnt/nvme/swapfile none swap sw 0 0' >> /etc/fstab
|
|
else
|
|
echo "[+] Creating 4GB swap file on eMMC..."
|
|
fallocate -l 4G /swapfile
|
|
chmod 600 /swapfile
|
|
mkswap /swapfile
|
|
swapon /swapfile
|
|
echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
|
fi
|
|
fi
|
|
|
|
# ── Install jtop for power monitoring ────────────────────────────────────────
|
|
if ! command -v jtop &>/dev/null; then
|
|
echo "[+] Installing jetson-stats (jtop)..."
|
|
pip3 install jetson-stats
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== Setup complete ==="
|
|
echo "Please log out and back in for group membership to take effect."
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. cd jetson/"
|
|
echo " 2. docker compose build"
|
|
echo " 3. docker compose up -d"
|
|
echo " 4. docker compose logs -f"
|
|
echo ""
|
|
echo "Monitor power: sudo jtop"
|
|
echo "Check cameras: v4l2-ctl --list-devices"
|