feat: Dockerfile BlueOS, deploy_pi.sh rsync, camera_mount.scad baseline 110mm
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
82
Dockerfile
Normal file
82
Dockerfile
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# SLAM Stéréo BlueOS — Extension Docker
|
||||||
|
# Base: ROS2 Humble (Ubuntu 22.04)
|
||||||
|
# ORB-SLAM3 + Flask interface
|
||||||
|
|
||||||
|
FROM ros:humble-ros-base AS base
|
||||||
|
|
||||||
|
LABEL version="0.1.0"
|
||||||
|
LABEL description="SLAM Stéréo visuel pour AUV sous-marin — BlueOS Extension"
|
||||||
|
LABEL authors='[{"name": "Baptiste Moulin", "email": "claude@nowyouknow.fr"}]'
|
||||||
|
LABEL company='{"about": "AUV SLAM project", "name": "Baptiste Moulin", "email": "claude@nowyouknow.fr"}'
|
||||||
|
LABEL type="device-integration"
|
||||||
|
LABEL tags='["slam", "stereo", "vision", "auv", "underwater"]'
|
||||||
|
LABEL permissions='{"NetworkMode": "host", "Devices": ["/dev/video0", "/dev/video1"], "HostConfig": {"Privileged": false}}'
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV ROS_DISTRO=humble
|
||||||
|
|
||||||
|
# --- Dépendances système ---
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
python3-pip \
|
||||||
|
python3-opencv \
|
||||||
|
libopencv-dev \
|
||||||
|
cmake \
|
||||||
|
build-essential \
|
||||||
|
git \
|
||||||
|
wget \
|
||||||
|
libssl-dev \
|
||||||
|
libusb-1.0-0-dev \
|
||||||
|
libeigen3-dev \
|
||||||
|
libglew-dev \
|
||||||
|
libpython3-dev \
|
||||||
|
python3-numpy \
|
||||||
|
python3-yaml \
|
||||||
|
# Pangolin deps
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libglu1-mesa-dev \
|
||||||
|
freeglut3-dev \
|
||||||
|
libwayland-dev \
|
||||||
|
libxkbcommon-dev \
|
||||||
|
wayland-protocols \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# --- Python dependencies ---
|
||||||
|
RUN pip3 install --no-cache-dir \
|
||||||
|
flask>=3.0 \
|
||||||
|
numpy>=1.24 \
|
||||||
|
pyyaml>=6.0 \
|
||||||
|
opencv-python-headless>=4.8
|
||||||
|
|
||||||
|
# --- Pangolin (viewer 3D ORB-SLAM3) ---
|
||||||
|
RUN git clone --depth 1 https://github.com/stevenlovegrove/Pangolin /opt/Pangolin && \
|
||||||
|
cmake -S /opt/Pangolin -B /opt/Pangolin/build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_TOOLS=OFF \
|
||||||
|
-DBUILD_EXAMPLES=OFF && \
|
||||||
|
cmake --build /opt/Pangolin/build --parallel $(nproc) && \
|
||||||
|
cmake --install /opt/Pangolin/build && \
|
||||||
|
rm -rf /opt/Pangolin
|
||||||
|
|
||||||
|
# --- ORB-SLAM3 dependencies (g2o, DBoW2 inclus dans ORB-SLAM3) ---
|
||||||
|
# Note: compilation complète ~15 min. Décommenter pour build final.
|
||||||
|
# RUN git clone --depth 1 https://github.com/UZ-SLAMLab/ORB_SLAM3 /opt/ORB_SLAM3 && \
|
||||||
|
# cd /opt/ORB_SLAM3 && chmod +x build.sh && bash build.sh
|
||||||
|
|
||||||
|
# --- Application SLAM ---
|
||||||
|
WORKDIR /app
|
||||||
|
COPY src/ ./src/
|
||||||
|
COPY config/ ./config/
|
||||||
|
|
||||||
|
# Créer config/ si vide
|
||||||
|
RUN mkdir -p /app/config
|
||||||
|
|
||||||
|
# --- Port Flask ---
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# --- Healthcheck ---
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:5000/ || exit 1
|
||||||
|
|
||||||
|
# --- Entrypoint ---
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
CMD ["python3", "src/interface/app.py"]
|
||||||
0
config/.gitkeep
Normal file
0
config/.gitkeep
Normal file
74
hardware/camera_mount.scad
Normal file
74
hardware/camera_mount.scad
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// camera_mount.scad — Support stéréo pour 2x Microsoft LifeCam HD-3000
|
||||||
|
// Baseline: 110 mm (médiane 10-12 cm optimal)
|
||||||
|
// Fixation: 4x M3 sur châssis AUV
|
||||||
|
// Matériau recommandé: PETG (résistance eau, impact)
|
||||||
|
// Auteur: Baptiste Moulin 2026
|
||||||
|
|
||||||
|
$fn = 60;
|
||||||
|
|
||||||
|
// --- Paramètres principaux ---
|
||||||
|
BASELINE = 110; // mm entre axes optiques
|
||||||
|
CAM_W = 34; // largeur boîtier LifeCam (section base)
|
||||||
|
CAM_H = 34; // hauteur boîtier LifeCam
|
||||||
|
CAM_DEPTH = 10; // profondeur empreinte caméra dans le support
|
||||||
|
MOUNT_THICK = 6; // épaisseur plaque support
|
||||||
|
MOUNT_H = 50; // hauteur totale support
|
||||||
|
BOLT_D = 3.4; // diamètre trou M3 (passage libre)
|
||||||
|
BOLT_PITCH_X = 96; // entraxe vis de fixation AUV (X)
|
||||||
|
BOLT_PITCH_Y = 30; // entraxe vis de fixation AUV (Y)
|
||||||
|
|
||||||
|
// --- Longueur totale de la plaque ---
|
||||||
|
PLATE_L = BASELINE + CAM_W + 10;
|
||||||
|
|
||||||
|
module cam_cutout() {
|
||||||
|
// Empreinte caméra: rectangle + passage câble USB
|
||||||
|
cube([CAM_W, CAM_DEPTH, CAM_H], center=true);
|
||||||
|
// Slot câble USB (6x10 mm) en bas de l'empreinte
|
||||||
|
translate([0, 0, -CAM_H/2 + 5])
|
||||||
|
cube([12, CAM_DEPTH + 2, 10], center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module bolt_holes_mount() {
|
||||||
|
// 4 trous M3 pour fixation sur châssis AUV
|
||||||
|
for (x = [-BOLT_PITCH_X/2, BOLT_PITCH_X/2])
|
||||||
|
for (y = [-BOLT_PITCH_Y/2, BOLT_PITCH_Y/2])
|
||||||
|
translate([x, y, 0])
|
||||||
|
cylinder(d=BOLT_D, h=MOUNT_THICK + 2, center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module support() {
|
||||||
|
difference() {
|
||||||
|
// Corps principal
|
||||||
|
union() {
|
||||||
|
// Plaque horizontale
|
||||||
|
cube([PLATE_L, MOUNT_THICK, MOUNT_H], center=true);
|
||||||
|
// Renforts latéraux sur chaque caméra
|
||||||
|
for (side = [-1, 1])
|
||||||
|
translate([side * BASELINE/2, MOUNT_THICK/2, 0])
|
||||||
|
cube([CAM_W + 4, MOUNT_THICK * 2, MOUNT_H], center=true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empreinte caméra gauche
|
||||||
|
translate([-BASELINE/2, 0, 0])
|
||||||
|
cam_cutout();
|
||||||
|
|
||||||
|
// Empreinte caméra droite
|
||||||
|
translate([BASELINE/2, 0, 0])
|
||||||
|
cam_cutout();
|
||||||
|
|
||||||
|
// Trous de fixation AUV
|
||||||
|
bolt_holes_mount();
|
||||||
|
|
||||||
|
// Allègement central (triangle)
|
||||||
|
translate([0, 0, 0])
|
||||||
|
cube([BASELINE - CAM_W - 8, MOUNT_THICK + 2, MOUNT_H - 16], center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Rendu principal ---
|
||||||
|
support();
|
||||||
|
|
||||||
|
// --- Annotations (commentaires) ---
|
||||||
|
// Axes optiques à Z=0, Y=MOUNT_THICK+5
|
||||||
|
// Baseline mesurée = BASELINE mm entre les 2 marques rouges
|
||||||
|
// Imprimer à 0.2 mm layer height, 40% infill gyroid, 3 périmètres
|
||||||
73
scripts/deploy_pi.sh
Normal file
73
scripts/deploy_pi.sh
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Deploy SLAM stéréo sur Raspberry Pi via SSH/rsync
|
||||||
|
# Usage: bash deploy_pi.sh <IP_PI>
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# --- Variables ---
|
||||||
|
PI_USER="pi"
|
||||||
|
PI_PASS="raspberry"
|
||||||
|
PI_DEST="/home/pi/slam_stereo"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# --- Argument ---
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "[ERREUR] Usage: $0 <IP_PI>"
|
||||||
|
echo "Exemple: $0 192.168.2.2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PI_HOST="$1"
|
||||||
|
|
||||||
|
echo "[INFO] Déploiement vers $PI_USER@$PI_HOST:$PI_DEST"
|
||||||
|
|
||||||
|
# --- Test connectivité ---
|
||||||
|
echo "[INFO] Test SSH..."
|
||||||
|
if ! sshpass -p "$PI_PASS" ssh -o StrictHostKeyChecking=no \
|
||||||
|
-o ConnectTimeout=5 "$PI_USER@$PI_HOST" "echo 'SSH OK'" 2>/dev/null; then
|
||||||
|
echo "[ERREUR] Impossible de se connecter à $PI_HOST"
|
||||||
|
echo "[INFO] Vérifier: IP correcte, Pi allumé, sshpass installé"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Créer dossiers sur Pi ---
|
||||||
|
echo "[INFO] Création des dossiers..."
|
||||||
|
sshpass -p "$PI_PASS" ssh -o StrictHostKeyChecking=no "$PI_USER@$PI_HOST" \
|
||||||
|
"mkdir -p $PI_DEST/{src/calibration,src/slam,src/interface,config}"
|
||||||
|
|
||||||
|
# --- Rsync src/ ---
|
||||||
|
echo "[INFO] Transfert src/..."
|
||||||
|
sshpass -p "$PI_PASS" rsync -avz --progress \
|
||||||
|
-e "ssh -o StrictHostKeyChecking=no" \
|
||||||
|
"$PROJECT_DIR/src/" \
|
||||||
|
"$PI_USER@$PI_HOST:$PI_DEST/src/"
|
||||||
|
|
||||||
|
# --- Rsync config/ ---
|
||||||
|
echo "[INFO] Transfert config/..."
|
||||||
|
sshpass -p "$PI_PASS" rsync -avz --progress \
|
||||||
|
-e "ssh -o StrictHostKeyChecking=no" \
|
||||||
|
"$PROJECT_DIR/config/" \
|
||||||
|
"$PI_USER@$PI_HOST:$PI_DEST/config/" 2>/dev/null || echo "[WARN] config/ vide, skip"
|
||||||
|
|
||||||
|
# --- Install dépendances Python ---
|
||||||
|
echo "[INFO] Installation dépendances Python..."
|
||||||
|
sshpass -p "$PI_PASS" ssh -o StrictHostKeyChecking=no "$PI_USER@$PI_HOST" bash << 'REMOTE'
|
||||||
|
set -e
|
||||||
|
pip3 install --upgrade pip --quiet
|
||||||
|
pip3 install opencv-python flask numpy pyyaml --quiet
|
||||||
|
echo "[OK] Dépendances installées"
|
||||||
|
REMOTE
|
||||||
|
|
||||||
|
# --- Test Flask ---
|
||||||
|
echo "[INFO] Test Flask..."
|
||||||
|
sshpass -p "$PI_PASS" ssh -o StrictHostKeyChecking=no "$PI_USER@$PI_HOST" \
|
||||||
|
"cd $PI_DEST && python3 -c 'import flask, cv2, numpy, yaml; print(\"[OK] Imports OK\")'"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== DEPLOY TERMINÉ ==="
|
||||||
|
echo "Interface web: http://$PI_HOST:5000"
|
||||||
|
echo "Démarrage manuel:"
|
||||||
|
echo " ssh $PI_USER@$PI_HOST"
|
||||||
|
echo " cd $PI_DEST && python3 src/interface/app.py"
|
||||||
|
echo ""
|
||||||
|
echo "Note BlueOS: utiliser aussi le Dockerfile pour extension native"
|
||||||
Reference in New Issue
Block a user