Tutoriel · Intermédiaire · 22 min de lecture

Étalonnage de caméra et pose ArUco avec OpenCV

Étalonnez une caméra de robot avec OpenCV, mesurez l'erreur de reprojection, détectez des marqueurs ArUco, et estimez leur pose avec les bonnes unités.

Une image de caméra rend des pixels, mais un robot a besoin de rayons et de coordonnées. L’étalonnage de caméra estime les paramètres intrinsèques qui projettent les points 3D du repère caméra vers les pixels, ainsi que la distorsion introduite par l’objectif. Une fois ces valeurs connues, un marqueur de géométrie connue peut fournir sa position et son orientation par rapport à la caméra.

Les marqueurs ArUco sont des repères binaires carrés avec un identifiant. Détecter les quatre coins identifie le marqueur dans l’image ; l’estimation de pose combine ces coins en pixels, la taille réelle du marqueur et les paramètres étalonnés de la caméra.

Comprendre les sorties de l’étalonnage

La matrice de la caméra contient les focales et le centre optique :

[ fx   0  cx ]
[  0  fy  cy ]
[  0   0   1 ]

fx et fy sont exprimés en pixels. cx et cy situent le point principal. Les coefficients de distorsion modélisent des effets comme la distorsion radiale en barillet ou en coussinet et le décentrage tangentiel.

Ces valeurs appartiennent à une caméra, un objectif, un réglage de mise au point et une géométrie d’image. Changer de résolution par recadrage ou redimensionnement peut exiger d’ajuster les paramètres intrinsèques ; changer la mise au point ou remplacer l’objectif impose un réétalonnage.

Imprimez et mesurez une mire d’étalonnage

Un damier est une première mire pratique car les coins intérieurs sont faciles à définir. Mesurez précisément la taille des cases. L’échelle de la pose hérite de cette unité : des points objet en mètres donnent des translations en mètres ; des points objet en millimètres donnent des millimètres.

Gardez la mire plane. Une feuille gondolée viole la géométrie plane supposée par les points objet. Montez-la sur un support rigide si la précision compte.

Définissez le nombre de coins intérieurs, pas de cases imprimées. Un plateau de 10 cases sur 7 compte 9 coins intérieurs sur 6. Confondre ces comptes fait échouer la détection alors même que l’image semble nette.

Constituez un jeu d’images utile

Collectez au moins dix vues réussies comme point de départ, puis ajoutez de la diversité plutôt que des doublons. Promenez la mire dans toute l’image, inclinez-la selon les deux axes, variez la distance, et incluez des coins près des bords de l’image où la distorsion est la plus forte. Gardez le motif net et entièrement visible.

Vingt images frontales presque identiques apportent moins d’information que moins de poses bien réparties. Écartez le flou de bougé et les images où la détection des coins est incertaine. Utilisez la résolution et les réglages de caméra prévus pour l’exploitation.

Pour chaque image acceptée, stockez les coins image détectés et les points objet plans correspondants (colonne × tailleCase, ligne × tailleCase, 0).

Étalonnez avec OpenCV

Le flux Python essentiel est :

import cv2
import numpy as np

pattern = (9, 6)          # internal corners: columns, rows
square_size = 0.024       # metres

object_template = np.zeros((pattern[0] * pattern[1], 3), np.float32)
object_template[:, :2] = np.mgrid[0:pattern[0], 0:pattern[1]].T.reshape(-1, 2)
object_template *= square_size

object_points, image_points = [], []

for gray in calibration_images:
    found, corners = cv2.findChessboardCorners(gray, pattern)
    if not found:
        continue
    refined = cv2.cornerSubPix(
        gray, corners, (11, 11), (-1, -1),
        (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001),
    )
    object_points.append(object_template.copy())
    image_points.append(refined)

rms, camera_matrix, distortion, rvecs, tvecs = cv2.calibrateCamera(
    object_points, image_points, gray.shape[::-1], None, None
)

Méfiez-vous d’un nombre trop faible d’images acceptées avant d’appeler l’étalonnage. Enregistrez ensemble la matrice, les coefficients de distorsion, la largeur et hauteur d’image, la définition de la mire et la date d’étalonnage. Un simple fichier de tableau est facile à mal réutiliser plus tard.

Validez plus que le RMS renvoyé

L’erreur de reprojection quadratique moyenne (RMS) résume l’écart entre les points d’étalonnage reprojetés et les points image détectés. Plus bas est généralement mieux, mais un seul nombre peut masquer de mauvaises zones ou un jeu d’images faible.

Un diagramme en barres de l'erreur de reprojection pour dix-huit images d'étalonnage. Dix-sept barres se tiennent près de 0,3 pixel ; l'image douze est seule à 1,95 pixel et est annotée comme floue de bougé avec des coins détectés dans le désordre. Une ligne pleine marque le RMS sur les dix-huit images à 0,54 pixel, et une ligne en pointillés marque 0,29 pixel sans l'image douze.
Dix-huit images, dont une floue. Le RMS global affiche quand même un honorable 0,54 px — c'est exactement pourquoi le nombre unique qu'affiche l'étalonneur n'est pas un verdict. Télécharger le SVG

Calculez l’erreur de reprojection par image et inspectez les pires vues. Dessinez les points reprojetés sur les images. Vérifiez les arêtes architecturales droites après redressement, surtout près des coins. Puis estimez la pose d’une mire à une distance connue et comparez la translation à une mesure physique.

Si une image domine l’erreur, inspectez-la et retirez-la seulement pour une raison défendable comme le flou ou un ordre de coins incorrect. Supprimer à répétition les images à forte erreur jusqu’à ce que le nombre plaise peut masquer un mauvais modèle ou une mauvaise mire.

Détectez un marqueur ArUco

Choisissez un dictionnaire prédéfini et générez les marqueurs depuis ce même dictionnaire. Des grilles de bits plus grandes offrent plus d’identifiants mais chaque bit reçoit moins de pixels à taille d’image fixée. Imprimez le marqueur avec une bordure blanche calme et mesurez la longueur du côté du carré noir utilisée par le modèle de pose.

dictionary = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_50)
parameters = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(dictionary, parameters)

corners, ids, rejected = detector.detectMarkers(frame)

La construction de l’API diffère selon les anciennes versions d’OpenCV, donc adaptez le code à la documentation officielle de la version installée. Les sorties doivent toujours fournir quatre coins image ordonnés par marqueur détecté.

Estimez la pose avec une taille de marqueur connue

Définissez les coins du marqueur dans son propre repère et résolvez le problème perspective-n-point. Pour un côté de marqueur s, le solveur dédié aux carrés d’OpenCV attend un ordre de coins cohérent :

s = 0.080  # metres
object_corners = np.array([
    [-s/2,  s/2, 0],
    [ s/2,  s/2, 0],
    [ s/2, -s/2, 0],
    [-s/2, -s/2, 0],
], dtype=np.float32)

ok, rvec, tvec = cv2.solvePnP(
    object_corners,
    corners[0].reshape(4, 2),
    camera_matrix,
    distortion,
    flags=cv2.SOLVEPNP_IPPE_SQUARE,
)

tvec est l’origine du marqueur exprimée dans le repère caméra et utilise la même unité de longueur que s. rvec est un vecteur de rotation de Rodrigues, pas des angles d’Euler. Utilisez cv2.Rodrigues() pour obtenir une matrice de rotation ou transformez-le via le système de repères du robot.

Dessinez les axes projetés et vérifiez-les physiquement. Une translation positive d’amplitude crédible ne suffit pas si les axes sont en miroir ou si la mauvaise taille de marqueur a été saisie.

Rendez la pose assez stable pour un robot

La pose devient bruitée quand le marqueur occupe peu de pixels, se présente sous un angle rasant, est flou, ou s’approche du bord de l’image. Améliorez l’éclairage et l’exposition, utilisez un marqueur plus grand, rapprochez-vous, ou employez un panneau de plusieurs marqueurs quand l’application le permet.

Ne filtrez que des poses valides et conservez les horodatages. Un filtre passe-bas réduit la gigue mais ajoute du retard de commande. Rejetez les résultats brusques via l’erreur de reprojection, des bornes de distance et des limites de mouvement plutôt que de moyenner aveuglément chaque détection.

La pose du marqueur est relative à la caméra. Pour commander un bras robotique ou une base mobile, transformez-la via la monture étalonnée de la caméra vers base_link ou la base du bras. Une confusion millimètre/mètre crée ici une erreur d’un facteur mille.

Diagnostiquez les pannes courantes

  • Le redressement courbe les lignes droites : les vues d’étalonnage manquent de couverture, la mire n’était pas plane, ou la géométrie d’image a changé.
  • L’échelle de distance est systématiquement fausse : la taille de case ou de marqueur utilise la mauvaise mesure ou la mauvaise unité.
  • Les axes de la pose pointent dans le mauvais sens : l’ordre des coins objet ou la convention de repère est faux.
  • L’identifiant du marqueur papillote : le marqueur est trop petit, flou, mal imprimé, ou issu d’un autre dictionnaire.
  • La pose saute près d’une vue frontale : l’ambiguïté de pose plane et la faible géométrie en pixels exigent de meilleurs points de vue, du suivi, ou des contraintes multi-marqueurs.
  • L’étalonnage ne vaut qu’à une résolution : les paramètres intrinsèques ont été réutilisés après recadrage ou redimensionnement sans ajustement.
  • Le mouvement du robot traîne derrière le marqueur : un lissage excessif ou des horodatages périmés masquent la géométrie courante.

Utilisez le simulateur de capteurs pour explorer bruit, biais, filtrage et latence. Le simulateur de bras robotique montre comment une pose cible peut rester inatteignable alors même que la géométrie de la caméra est correcte.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références