docs: Sphinx — 9 pages rst, conf.py rtd-theme, sources.bib, navigator_imu
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = source
|
||||||
|
BUILDDIR = build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=source
|
||||||
|
set BUILDDIR=build
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.https://www.sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
||||||
106
docs/source/blueos.rst
Normal file
106
docs/source/blueos.rst
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
Intégration BlueOS
|
||||||
|
==================
|
||||||
|
|
||||||
|
BlueOS Overview
|
||||||
|
---------------
|
||||||
|
|
||||||
|
BlueOS est le système d'exploitation embarqué de Blue Robotics
|
||||||
|
pour le BlueROV2. Il tourne sous Raspberry Pi OS + Docker.
|
||||||
|
Les extensions BlueOS sont des conteneurs Docker exposés via
|
||||||
|
une API REST et une UI web intégrée.
|
||||||
|
|
||||||
|
Extension SLAM
|
||||||
|
--------------
|
||||||
|
|
||||||
|
L'extension SLAM est définie dans ``/Dockerfile`` à la racine du projet.
|
||||||
|
|
||||||
|
Format manifest BlueOS
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Chaque extension doit inclure des labels Docker spécifiques :
|
||||||
|
|
||||||
|
.. code-block:: dockerfile
|
||||||
|
|
||||||
|
LABEL version="0.1.0"
|
||||||
|
LABEL permissions='{"NetworkMode": "host", "Devices": ["/dev/video0", "/dev/video1"]}'
|
||||||
|
LABEL authors='[{"name": "Baptiste Moulin", "email": "baptiste@example.com"}]'
|
||||||
|
LABEL company='{"about": "", "name": "Baptiste Moulin", "email": ""}'
|
||||||
|
LABEL type="device-integration"
|
||||||
|
LABEL tags='["slam", "stereo", "vision"]'
|
||||||
|
LABEL readme='https://raw.githubusercontent.com/.../README.md'
|
||||||
|
LABEL links='{"website": "", "support": ""}'
|
||||||
|
|
||||||
|
Accès caméras USB
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
BlueOS utilise ``udev`` pour les caméras. S'assurer que :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Sur le Pi, vérifier les devices
|
||||||
|
ls /dev/video*
|
||||||
|
# → /dev/video0 /dev/video1
|
||||||
|
|
||||||
|
# Dans BlueOS Docker, les devices sont passés via permissions JSON
|
||||||
|
# "Devices": ["/dev/video0", "/dev/video1"]
|
||||||
|
|
||||||
|
Port réseau
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
L'interface Flask tourne sur le port **5000**.
|
||||||
|
BlueOS expose les extensions via son proxy Nginx.
|
||||||
|
Accès depuis l'interface BlueOS : ``http://<IP_PI>/extensions/slam-stereo``.
|
||||||
|
|
||||||
|
Installation via BlueOS UI
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
1. Ouvrir BlueOS : ``http://<IP_PI>``
|
||||||
|
2. Menu → Extensions → Installer
|
||||||
|
3. Entrer l'image Docker : ``ghcr.io/baptiste-moulin/slam-stereo-blueos:latest``
|
||||||
|
4. Valider les permissions
|
||||||
|
5. Lancer l'extension
|
||||||
|
|
||||||
|
Installation manuelle (SSH)
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ssh pi@<IP_PI>
|
||||||
|
|
||||||
|
# Build l'image localement
|
||||||
|
cd /home/pi/slam_stereo
|
||||||
|
docker build -t slam-stereo-blueos .
|
||||||
|
|
||||||
|
# Lancer le conteneur
|
||||||
|
docker run -d \
|
||||||
|
--network host \
|
||||||
|
--device /dev/video0 \
|
||||||
|
--device /dev/video1 \
|
||||||
|
-p 5000:5000 \
|
||||||
|
--name slam-stereo \
|
||||||
|
slam-stereo-blueos
|
||||||
|
|
||||||
|
Logs
|
||||||
|
----
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
docker logs -f slam-stereo
|
||||||
|
|
||||||
|
MAVLink Integration (optionnel)
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Pour envoyer la pose estimée au pilote automatique (ArduSub/ArduPilot) :
|
||||||
|
|
||||||
|
- Protocole MAVLink ``VISION_POSITION_ESTIMATE`` (ID 102)
|
||||||
|
- Bibliothèque Python : ``pymavlink``
|
||||||
|
- Connexion via ``udpin:0.0.0.0:14550`` (BlueOS MAVLink router)
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Exemple envoi pose (à implémenter dans src/slam/mavlink_bridge.py)
|
||||||
|
from pymavlink import mavutil
|
||||||
|
mav = mavutil.mavlink_connection('udpout:127.0.0.1:14550')
|
||||||
|
mav.mav.vision_position_estimate_send(
|
||||||
|
usec, x, y, z, roll, pitch, yaw
|
||||||
|
)
|
||||||
101
docs/source/calibration.rst
Normal file
101
docs/source/calibration.rst
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
Calibration Stéréo
|
||||||
|
==================
|
||||||
|
|
||||||
|
Matériel nécessaire
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Damier imprimé **9×6 cases** (intérieur), case **25 mm**
|
||||||
|
- Support rigide (plastifié recommandé)
|
||||||
|
- Éclairage uniforme sans reflets
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Imprimer sur fond blanc mat. Plastifier sans brillance pour éviter
|
||||||
|
les reflets. Ne pas utiliser d'écran LCD (déformation optique).
|
||||||
|
|
||||||
|
Procédure
|
||||||
|
---------
|
||||||
|
|
||||||
|
Étape 1 — Capture des paires
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
python src/calibration/stereo_capture.py
|
||||||
|
|
||||||
|
- Lance les 2 webcams simultanément (USB 0 et 1)
|
||||||
|
- Appuyer sur ``ESPACE`` pour capturer une paire
|
||||||
|
- Objectif : **30 paires valides** minimum
|
||||||
|
- Varier : distance (0.3–1.5 m), inclinaison (±30°), position dans frame
|
||||||
|
|
||||||
|
.. tip::
|
||||||
|
Couvrir les 4 coins du champ + centre. Inclure des poses inclinées
|
||||||
|
pour mieux contraindre les coefficients de distorsion tangentielle.
|
||||||
|
|
||||||
|
Étape 2 — Calibration
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
python src/calibration/stereo_calibrate.py
|
||||||
|
|
||||||
|
Résultat attendu :
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
Chargement 30 paires de damier...
|
||||||
|
Calibration stéréo...
|
||||||
|
RMS reprojection error: 0.42 px ← bon si < 0.8 px
|
||||||
|
Sauvegarde: config/stereo_calib.yaml
|
||||||
|
|
||||||
|
Fichier YAML généré
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
``config/stereo_calib.yaml`` contient :
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
K1: [[fx, 0, cx], [0, fy, cy], [0, 0, 1]] # caméra gauche
|
||||||
|
D1: [k1, k2, p1, p2, k3] # distorsion gauche
|
||||||
|
K2: [[fx, 0, cx], [0, fy, cy], [0, 0, 1]] # caméra droite
|
||||||
|
D2: [k1, k2, p1, p2, k3] # distorsion droite
|
||||||
|
R: [[...], [...], [...]] # rotation C2→C1
|
||||||
|
T: [tx, ty, tz] # translation (baseline)
|
||||||
|
E: [[...]] # matrice essentielle
|
||||||
|
F: [[...]] # matrice fondamentale
|
||||||
|
R1, P1, R2, P2, Q # rectification
|
||||||
|
|
||||||
|
Paramètres OpenCV
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
``stereoCalibrate`` avec flags :
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
flags = (
|
||||||
|
cv2.CALIB_FIX_INTRINSIC # si calibration individuelle faite avant
|
||||||
|
# ou cv2.CALIB_RATIONAL_MODEL # pour modèle 8 coefs
|
||||||
|
)
|
||||||
|
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)
|
||||||
|
|
||||||
|
``stereoRectify`` avec ``alpha=0`` (recadrage maximal, pas de bandes noires).
|
||||||
|
|
||||||
|
Validation
|
||||||
|
----------
|
||||||
|
|
||||||
|
Après calibration, vérifier :
|
||||||
|
|
||||||
|
1. Erreur RMS < 0.8 px (idéalement < 0.5 px)
|
||||||
|
2. Lignes épipolaires horizontales sur image rectifiée
|
||||||
|
3. Translation T ≈ [−0.11, 0, 0] m (baseline sur axe X)
|
||||||
|
4. Épipole à l'infini (caméras parallèles après rectif)
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Visualisation rectification
|
||||||
|
python src/calibration/stereo_calibrate.py --show-rectified
|
||||||
|
|
||||||
|
Références
|
||||||
|
----------
|
||||||
|
|
||||||
|
:cite:`bradski2008learning` — OpenCV stereoCalibrate, stereoRectify.
|
||||||
|
:cite:`ferrera2019underwater` — Prise en compte réfraction eau/verre.
|
||||||
38
docs/source/conf.py
Normal file
38
docs/source/conf.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# For the full list of built-in configuration values, see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
|
project = 'SLAM_Stereo_BlueOS'
|
||||||
|
copyright = '2026, Baptiste Moulin'
|
||||||
|
author = 'Baptiste Moulin'
|
||||||
|
|
||||||
|
version = '0.1'
|
||||||
|
release = '0.1'
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.viewcode',
|
||||||
|
'sphinx.ext.mathjax',
|
||||||
|
'myst_parser',
|
||||||
|
'sphinxcontrib.bibtex',
|
||||||
|
]
|
||||||
|
|
||||||
|
templates_path = ['_templates']
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
# BibTeX
|
||||||
|
bibtex_bibfiles = ['sources.bib']
|
||||||
|
|
||||||
|
# MyST
|
||||||
|
myst_enable_extensions = ['dollarmath', 'amsmath']
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
html_static_path = ['_static']
|
||||||
98
docs/source/deployment.rst
Normal file
98
docs/source/deployment.rst
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
Déploiement — Raspberry Pi
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Prérequis
|
||||||
|
---------
|
||||||
|
|
||||||
|
- ``sshpass`` installé (``sudo apt install sshpass`` ou ``brew install sshpass``)
|
||||||
|
- ``rsync`` disponible
|
||||||
|
- Pi accessible via réseau (BlueOS Ethernet ou WiFi)
|
||||||
|
- Credentials par défaut : ``pi`` / ``raspberry``
|
||||||
|
|
||||||
|
Script deploy
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
bash scripts/deploy_pi.sh <IP_DU_PI>
|
||||||
|
# Exemple :
|
||||||
|
bash scripts/deploy_pi.sh 192.168.2.2
|
||||||
|
|
||||||
|
Le script effectue :
|
||||||
|
|
||||||
|
1. Test de connectivité SSH
|
||||||
|
2. ``rsync`` de ``/src/`` vers ``/home/pi/slam_stereo/src/``
|
||||||
|
3. ``rsync`` de ``/config/`` vers ``/home/pi/slam_stereo/config/``
|
||||||
|
4. Installation pip des dépendances Python
|
||||||
|
5. Redémarrage du service Flask (si systemd configuré)
|
||||||
|
|
||||||
|
Installation manuelle
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ssh pi@<IP_PI>
|
||||||
|
|
||||||
|
# Installer dépendances système
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y python3-pip python3-opencv libopencv-dev
|
||||||
|
|
||||||
|
# Installer dépendances Python
|
||||||
|
pip3 install flask numpy pyyaml opencv-python
|
||||||
|
|
||||||
|
# Copier les fichiers (depuis PC)
|
||||||
|
rsync -av src/ pi@<IP_PI>:/home/pi/slam_stereo/src/
|
||||||
|
rsync -av config/ pi@<IP_PI>:/home/pi/slam_stereo/config/
|
||||||
|
|
||||||
|
Service systemd (optionnel)
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Créer ``/etc/systemd/system/slam-stereo.service`` sur le Pi :
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=SLAM Stereo Flask Interface
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=pi
|
||||||
|
WorkingDirectory=/home/pi/slam_stereo
|
||||||
|
ExecStart=/usr/bin/python3 src/interface/app.py
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sudo systemctl enable slam-stereo
|
||||||
|
sudo systemctl start slam-stereo
|
||||||
|
|
||||||
|
Vérification
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Depuis le Pi
|
||||||
|
curl http://localhost:5000/
|
||||||
|
|
||||||
|
# Depuis le réseau
|
||||||
|
curl http://<IP_PI>:5000/
|
||||||
|
|
||||||
|
# Logs Flask
|
||||||
|
journalctl -u slam-stereo -f
|
||||||
|
|
||||||
|
Notes BlueOS
|
||||||
|
------------
|
||||||
|
|
||||||
|
BlueOS expose le Pi sur ``192.168.2.2`` par défaut (connexion USB-Ethernet).
|
||||||
|
En WiFi, l'IP est assignée par DHCP.
|
||||||
|
|
||||||
|
Pour trouver l'IP :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Depuis BlueOS web UI : Settings → Network
|
||||||
|
# Ou scan réseau :
|
||||||
|
nmap -sn 192.168.2.0/24
|
||||||
74
docs/source/hardware.rst
Normal file
74
docs/source/hardware.rst
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
Hardware
|
||||||
|
========
|
||||||
|
|
||||||
|
Composants
|
||||||
|
----------
|
||||||
|
|
||||||
|
Raspberry Pi 4B
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- **CPU** : Broadcom BCM2711, Cortex-A72 64-bit @ 1.8 GHz
|
||||||
|
- **RAM** : 4 GB LPDDR4
|
||||||
|
- **OS** : BlueOS (Raspbian Bullseye + Docker)
|
||||||
|
- **Connexion** : Ethernet ou WiFi 5 GHz
|
||||||
|
- **Alimentation** : 5V 3A via câble BlueROV2 ou USB-C
|
||||||
|
|
||||||
|
Caméras stéréo — Microsoft LifeCam HD-3000
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- **Résolution** : 1280×720 @ 30 fps
|
||||||
|
- **Interface** : USB 2.0
|
||||||
|
- **Focale estimée** : ~525 px (à calibrer)
|
||||||
|
- **Champ** : ~68° horizontal
|
||||||
|
- **Boîtier** : étanche DIY ou logement BlueROV2
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Les caméras ne sont pas étanches nativement. Un boîtier optique
|
||||||
|
plat (port verre optique sans distorsion) est nécessaire.
|
||||||
|
Utiliser du verre borosilicate 5 mm, joint EPDM, serrage 6 vis M3.
|
||||||
|
|
||||||
|
Carte Navigator (Blue Robotics)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
La carte Navigator est directement connectée au GPIO 40-pins du Pi4B.
|
||||||
|
|
||||||
|
- **IMU** : Bosch BMI088 (accéléromètre + gyroscope 6-axes séparés)
|
||||||
|
- **Baromètre/profondimètre** : TE MS5837-30BA (0–300 bar)
|
||||||
|
- **PWM thrusters** : NXP PCA9685 (16 canaux I2C)
|
||||||
|
- **Interface** : SPI (BMI088) + I2C (MS5837, PCA9685)
|
||||||
|
- **Fréquence IMU** : jusqu'à 400 Hz configurable via ArduSub
|
||||||
|
- Voir ``docs/source/navigator_imu.rst`` pour détail fusion VIO
|
||||||
|
|
||||||
|
Support caméras (OpenSCAD)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Voir ``/hardware/camera_mount.scad``.
|
||||||
|
|
||||||
|
- Espacement fixe **11 cm** (médiane de la plage optimale 10–12 cm)
|
||||||
|
- Trous M3 × 4 pour fixation sur châssis AUV
|
||||||
|
- Slot câbles USB intégré
|
||||||
|
- Matériau recommandé : PETG (résistance eau, déformation thermique)
|
||||||
|
|
||||||
|
Montage
|
||||||
|
-------
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Vue de dessus :
|
||||||
|
|
||||||
|
USB-A Pi4 USB-A Pi4
|
||||||
|
| |
|
||||||
|
________↓_________ _________↓________
|
||||||
|
| LifeCam gauche |←11cm→| LifeCam droite |
|
||||||
|
|__________________| |__________________|
|
||||||
|
↓ ↓
|
||||||
|
Cam 0 Cam 1
|
||||||
|
(cv2.VideoCapture(0)) (cv2.VideoCapture(1))
|
||||||
|
|
||||||
|
|
||||||
|
Connectique BlueROV2
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Le Raspberry Pi 4B se loge dans le tube électronique principal.
|
||||||
|
Les câbles USB traversent le presse-étoupe 4 trous M10.
|
||||||
|
Prévoir longueur câble USB ≥ 30 cm (interne) + 15 cm (externe vers support).
|
||||||
24
docs/source/index.rst
Normal file
24
docs/source/index.rst
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
SLAM Stéréo BlueOS — Documentation
|
||||||
|
====================================
|
||||||
|
|
||||||
|
SLAM visuel stéréo pour AUV sous-marin autonome.
|
||||||
|
Intégration BlueOS (Raspberry Pi 4B), ORB-SLAM3, Flask.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contenu
|
||||||
|
|
||||||
|
hardware
|
||||||
|
theory_baseline
|
||||||
|
calibration
|
||||||
|
navigator_imu
|
||||||
|
slam_stack
|
||||||
|
blueos
|
||||||
|
deployment
|
||||||
|
sources
|
||||||
|
|
||||||
|
Indices et tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`search`
|
||||||
68
docs/source/navigator_imu.rst
Normal file
68
docs/source/navigator_imu.rst
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
Carte Navigator — BMI088 IMU
|
||||||
|
=============================
|
||||||
|
|
||||||
|
La carte Navigator de Blue Robotics embarque :
|
||||||
|
|
||||||
|
- **IMU** : Bosch BMI088 (accéléromètre 6-axes + gyroscope 6-axes, séparés physiquement)
|
||||||
|
- **Baromètre** : TE MS5837-30BA (profondimètre 300 bar)
|
||||||
|
- **PWM** : NXP PCA9685 (16 canaux, contrôle thrusters via ArduSub)
|
||||||
|
|
||||||
|
Spécifications BMI088
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Paramètre
|
||||||
|
- Valeur
|
||||||
|
* - Plage accéléromètre
|
||||||
|
- ±3g / ±6g / ±12g / ±24g
|
||||||
|
* - Bruit accéléromètre
|
||||||
|
- 175 μg/√Hz
|
||||||
|
* - Plage gyroscope
|
||||||
|
- ±125 à ±2000 °/s
|
||||||
|
* - Bruit gyroscope
|
||||||
|
- 0.014 °/s/√Hz
|
||||||
|
* - Interface
|
||||||
|
- SPI / I2C
|
||||||
|
* - Fréquence sortie
|
||||||
|
- 100–400 Hz (configurable)
|
||||||
|
|
||||||
|
Accès via BlueOS / MAVLink
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
ArduSub lit le BMI088 via l'interface SPI interne de la Navigator.
|
||||||
|
Les données IMU sont accessibles en MAVLink (message ``SCALED_IMU``).
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pymavlink import mavutil
|
||||||
|
|
||||||
|
mav = mavutil.mavlink_connection('udpin:192.168.0.174:14550')
|
||||||
|
mav.wait_heartbeat()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
msg = mav.recv_match(type='SCALED_IMU', blocking=True)
|
||||||
|
if msg:
|
||||||
|
ax = msg.xacc / 1000.0 # mg -> g
|
||||||
|
ay = msg.yacc / 1000.0
|
||||||
|
az = msg.zacc / 1000.0
|
||||||
|
gx = msg.xgyro / 1000.0 # mrad/s -> rad/s
|
||||||
|
print(f"Acc: {ax:.3f} {ay:.3f} {az:.3f} g")
|
||||||
|
|
||||||
|
Fusion visuo-inertielle (VIO)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
ORB-SLAM3 mode **Stereo-Inertial** requiert :
|
||||||
|
|
||||||
|
1. Extrinsèques IMU-caméra (matrice ``T_cam_imu``) — à calibrer via Kalibr
|
||||||
|
2. Biais accéléromètre et gyroscope (InitialAccBias, InitialGyroBias)
|
||||||
|
3. Fréquence IMU ≥ 200 Hz (configurer ArduSub ``IMU_FAST_RATE=1``)
|
||||||
|
|
||||||
|
Fichier config ORB-SLAM3 : ``config/navigator_imu.yaml`` (à générer après calibration Kalibr).
|
||||||
|
|
||||||
|
Références
|
||||||
|
----------
|
||||||
|
|
||||||
|
- `Blue Robotics Navigator Documentation <https://bluerobotics.com/store/electronics/navigator/>`_
|
||||||
|
- BMI088 Datasheet — Bosch Sensortec BST-BMI088-DS001
|
||||||
134
docs/source/slam_stack.rst
Normal file
134
docs/source/slam_stack.rst
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
Stack SLAM
|
||||||
|
==========
|
||||||
|
|
||||||
|
Choix : ORB-SLAM3
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
ORB-SLAM3 :cite:`campos2021orbslam3` est retenu comme backend SLAM.
|
||||||
|
|
||||||
|
Justification :
|
||||||
|
|
||||||
|
- **Modes** : Monoculaire, Stéréo, RGB-D, IMU (Stéréo-Inertiel)
|
||||||
|
- **Précision** : RMSE trajectoire < 2 cm en stéréo indoor (EuRoC dataset)
|
||||||
|
- **Open-source** : GPLv3, actif sur GitHub
|
||||||
|
- **ROS2** : wrappers disponibles (ros2-orbslam3)
|
||||||
|
- **Robustesse** : réinitialisation automatique, loop closure, multi-map
|
||||||
|
|
||||||
|
Comparatif
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 20 20 20 20 20
|
||||||
|
|
||||||
|
* - Stack
|
||||||
|
- Mode stéréo
|
||||||
|
- IMU
|
||||||
|
- ROS2
|
||||||
|
- Licence
|
||||||
|
* - **ORB-SLAM3**
|
||||||
|
- Oui
|
||||||
|
- Oui
|
||||||
|
- Via wrapper
|
||||||
|
- GPLv3
|
||||||
|
* - VINS-Fusion
|
||||||
|
- Oui
|
||||||
|
- Oui
|
||||||
|
- ROS1 natif
|
||||||
|
- GPLv3
|
||||||
|
* - OpenVSLAM
|
||||||
|
- Oui
|
||||||
|
- Non
|
||||||
|
- Oui
|
||||||
|
- BSD
|
||||||
|
* - Kimera
|
||||||
|
- Oui
|
||||||
|
- Oui
|
||||||
|
- ROS1
|
||||||
|
- BSD
|
||||||
|
* - ElasticFusion
|
||||||
|
- RGB-D only
|
||||||
|
- Non
|
||||||
|
- Non
|
||||||
|
- Non-commercial
|
||||||
|
|
||||||
|
VINS-Fusion :cite:`qin2019fusion` — candidat secondaire.
|
||||||
|
Avantage : robustesse outdoor. Inconvénient : ROS1, moins maintenu.
|
||||||
|
|
||||||
|
Architecture ORB-SLAM3
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ ORB-SLAM3 │
|
||||||
|
│ │
|
||||||
|
│ Tracking Thread ──► Local Mapping Thread │
|
||||||
|
│ │ │ │
|
||||||
|
│ Feature ORB Map Points │
|
||||||
|
│ Matching KeyFrames │
|
||||||
|
│ │ │ │
|
||||||
|
│ Pose Estimation Loop Closing Thread │
|
||||||
|
│ (PnP + IMU) (DBoW2 + g2o) │
|
||||||
|
│ │ │
|
||||||
|
│ Atlas (Multi-Map) │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Dépendances compilation
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Eigen3
|
||||||
|
sudo apt install libeigen3-dev
|
||||||
|
|
||||||
|
# OpenCV 4.x
|
||||||
|
sudo apt install libopencv-dev
|
||||||
|
|
||||||
|
# Pangolin (viewer 3D)
|
||||||
|
git clone https://github.com/stevenlovegrove/Pangolin
|
||||||
|
cd Pangolin && cmake -B build && cmake --build build
|
||||||
|
|
||||||
|
# g2o (inclus dans ORB-SLAM3)
|
||||||
|
# DBoW2 (inclus dans ORB-SLAM3)
|
||||||
|
|
||||||
|
# ORB-SLAM3
|
||||||
|
git clone https://github.com/UZ-SLAMLab/ORB_SLAM3
|
||||||
|
cd ORB_SLAM3 && chmod +x build.sh && ./build.sh
|
||||||
|
|
||||||
|
Paramètres SLAM — fichier YAML
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Voir ``config/orbslam3_stereo.yaml`` (à créer après calibration) :
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
Camera.type: "KannalaBrandt8" # ou "Pinhole"
|
||||||
|
Camera.fx: 525.0
|
||||||
|
Camera.fy: 525.0
|
||||||
|
Camera.cx: 640.0
|
||||||
|
Camera.cy: 360.0
|
||||||
|
|
||||||
|
Stereo.b: 0.11 # baseline en mètres
|
||||||
|
Stereo.ThDepth: 35.0 # profondeur max (b * ThDepth pixels)
|
||||||
|
|
||||||
|
ORBextractor.nFeatures: 1000
|
||||||
|
ORBextractor.scaleFactor: 1.2
|
||||||
|
ORBextractor.nLevels: 8
|
||||||
|
|
||||||
|
Contraintes sous-marines
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- **Texture** : eau lisse = peu de features. Pointer vers fond ou structure.
|
||||||
|
- **Absorption** : rouge absorbé > 3 m. Filtre passe-haut sur illumination.
|
||||||
|
- **Turbidité** : features instables. Réduire ``ThDepth``, augmenter ``nFeatures``.
|
||||||
|
- **Réfraction** : calibration en air ≠ en eau. Recalibrer si possible en eau.
|
||||||
|
|
||||||
|
Références
|
||||||
|
----------
|
||||||
|
|
||||||
|
:cite:`campos2021orbslam3`
|
||||||
|
|
||||||
|
:cite:`qin2019fusion`
|
||||||
|
|
||||||
|
:cite:`joshi2019survey`
|
||||||
59
docs/source/sources.bib
Normal file
59
docs/source/sources.bib
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
% ORB-SLAM3 — Campos et al. 2021, IEEE Transactions on Robotics
|
||||||
|
@article{campos2021orbslam3,
|
||||||
|
author = {Campos, Carlos and Elvira, Richard and Gómez Rodríguez, Juan J.
|
||||||
|
and Montiel, José M. M. and Tardós, Juan D.},
|
||||||
|
title = {{ORB-SLAM3}: An Accurate Open-Source Library for Visual,
|
||||||
|
Visual-Inertial, and Multimap {SLAM}},
|
||||||
|
journal = {IEEE Transactions on Robotics},
|
||||||
|
volume = {37},
|
||||||
|
number = {6},
|
||||||
|
pages = {1874--1890},
|
||||||
|
year = {2021},
|
||||||
|
doi = {10.1109/TRO.2021.3075644},
|
||||||
|
}
|
||||||
|
|
||||||
|
% VINS-Fusion — Qin & Shen 2019
|
||||||
|
@article{qin2019fusion,
|
||||||
|
author = {Qin, Tong and Pan, Jie and Cao, Shaozu and Shen, Shaojie},
|
||||||
|
title = {A General Optimization-based Framework for Local Odometry
|
||||||
|
Estimation with Multiple Sensors},
|
||||||
|
journal = {arXiv preprint arXiv:1901.03638},
|
||||||
|
year = {2019},
|
||||||
|
url = {https://arxiv.org/abs/1901.03638},
|
||||||
|
}
|
||||||
|
|
||||||
|
% Calibration sous-marine — Ferrera et al. 2019
|
||||||
|
@inproceedings{ferrera2019underwater,
|
||||||
|
author = {Ferrera, Maxime and Moras, Julien and Trouvé-Peloux, Pauline},
|
||||||
|
title = {Real-Time Monocular Visual Odometry for Turbid and Dynamic
|
||||||
|
Underwater Environments},
|
||||||
|
booktitle = {Proceedings of the IEEE International Conference on Robotics
|
||||||
|
and Automation (ICRA)},
|
||||||
|
year = {2019},
|
||||||
|
pages = {9720--9726},
|
||||||
|
doi = {10.1109/ICRA.2019.8793818},
|
||||||
|
}
|
||||||
|
|
||||||
|
% Survey SLAM AUV — Joshi et al. 2019
|
||||||
|
@article{joshi2019survey,
|
||||||
|
author = {Joshi, Bharat and Rahman, Sharmin and Kalaitzakis, Michail
|
||||||
|
and Cain, Brennan and Johnson, James and Xanthidis, Marios
|
||||||
|
and Karapetyan, Nare and Hernandez, Aven and Henderson,
|
||||||
|
Thomas C. and Rekleitis, Ioannis},
|
||||||
|
title = {Experimental Comparison of Open Source Visual-Inertial-Based
|
||||||
|
State Estimation Algorithms in the Underwater Domain},
|
||||||
|
booktitle = {2019 IEEE/RSJ International Conference on Intelligent Robots
|
||||||
|
and Systems (IROS)},
|
||||||
|
year = {2019},
|
||||||
|
pages = {7227--7234},
|
||||||
|
doi = {10.1109/IROS40897.2019.8968049},
|
||||||
|
}
|
||||||
|
|
||||||
|
% OpenCV Learning — Bradski & Kaehler 2008
|
||||||
|
@book{bradski2008learning,
|
||||||
|
author = {Bradski, Gary and Kaehler, Adrian},
|
||||||
|
title = {Learning {OpenCV}: Computer Vision with the {OpenCV} Library},
|
||||||
|
publisher = {O'Reilly Media},
|
||||||
|
year = {2008},
|
||||||
|
isbn = {978-0596516130},
|
||||||
|
}
|
||||||
6
docs/source/sources.rst
Normal file
6
docs/source/sources.rst
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Bibliographie
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. bibliography::
|
||||||
|
:all:
|
||||||
|
:style: unsrt
|
||||||
98
docs/source/theory_baseline.rst
Normal file
98
docs/source/theory_baseline.rst
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
Théorie — Baseline Stéréo
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Modèle géométrique
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Le système stéréo calibré suit le modèle trou d'aiguille rectifié.
|
||||||
|
Après rectification, les lignes épipolaires sont horizontales et
|
||||||
|
la disparité est uniquement sur l'axe X.
|
||||||
|
|
||||||
|
Relation profondeur–disparité
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
Z = \frac{f \cdot B}{d}
|
||||||
|
|
||||||
|
Avec :
|
||||||
|
|
||||||
|
- :math:`Z` — profondeur en mètres
|
||||||
|
- :math:`f` — focale en **pixels** (après calibration)
|
||||||
|
- :math:`B` — baseline en **mètres**
|
||||||
|
- :math:`d` — disparité en **pixels**
|
||||||
|
|
||||||
|
Paramètres du système
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Paramètre
|
||||||
|
- Valeur
|
||||||
|
- Source
|
||||||
|
* - Focale estimée :math:`f`
|
||||||
|
- 525 px
|
||||||
|
- LifeCam HD-3000, résolution 1280×720
|
||||||
|
* - Baseline :math:`B`
|
||||||
|
- 0.11 m
|
||||||
|
- Support OpenSCAD, médiane 10–12 cm
|
||||||
|
* - Disparité min :math:`d_{min}`
|
||||||
|
- 1 px
|
||||||
|
- Limite théorique
|
||||||
|
* - Disparité max :math:`d_{max}`
|
||||||
|
- 64 px
|
||||||
|
- StereoBM / SGBM config
|
||||||
|
|
||||||
|
Portée théorique
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
Z_{max} = \frac{f \cdot B}{d_{min}} = \frac{525 \times 0.11}{1} = 57.75 \text{ m}
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
Z_{min} = \frac{f \cdot B}{d_{max}} = \frac{525 \times 0.11}{64} \approx 0.9 \text{ m}
|
||||||
|
|
||||||
|
**Portée pratique sous-marin** : 0.5 m – 5 m
|
||||||
|
|
||||||
|
La limite pratique est imposée par la **visibilité de l'eau** (turbidité,
|
||||||
|
diffusion), pas par la géométrie. En eau claire méditerranéenne,
|
||||||
|
portée max mesurée ≈ 8 m. En eau verte côtière : ≈ 2–3 m.
|
||||||
|
|
||||||
|
Pourquoi 10–12 cm ?
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
**Baseline trop courte** (< 5 cm) :
|
||||||
|
|
||||||
|
- Disparité trop faible → bruit dominant
|
||||||
|
- :math:`Z_{max}` réduit, peu de points 3D à grande distance
|
||||||
|
|
||||||
|
**Baseline trop longue** (> 20 cm) :
|
||||||
|
|
||||||
|
- Zone aveugle centrale grande (:math:`Z_{min}` augmente)
|
||||||
|
- Occultations importantes (objet vu par une seule caméra)
|
||||||
|
- Synchronisation temporelle plus critique
|
||||||
|
|
||||||
|
**Plage 10–12 cm** :
|
||||||
|
|
||||||
|
- Compromise optimal pour portée 0.5–5 m
|
||||||
|
- Compatible avec gabarit BlueROV2 (largeur tube ~10 cm)
|
||||||
|
- Résolution en profondeur : :math:`\delta Z = Z^2 / (f \cdot B)` ≈ 4 cm à 2 m
|
||||||
|
|
||||||
|
Sensibilité à la calibration
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Une erreur de 1 mm sur la baseline :math:`B` induit une erreur
|
||||||
|
:math:`\delta Z / Z = \delta B / B \approx 0.9\%` sur toutes les profondeurs.
|
||||||
|
|
||||||
|
Conserver le support imprimé rigide et éviter le choc thermique
|
||||||
|
(dilatation différentielle PETG/aluminium).
|
||||||
|
|
||||||
|
Références
|
||||||
|
----------
|
||||||
|
|
||||||
|
:cite:`campos2021orbslam3` — ORB-SLAM3, précision stéréo-inertiel.
|
||||||
|
|
||||||
|
:cite:`ferrera2019underwater` — Calibration sous-marine, modèle réfraction.
|
||||||
Reference in New Issue
Block a user