Tutorial · Gevorderd · 22 min lezen

Camerakalibratie en ArUco-pose met OpenCV

Kalibreer een robotcamera met OpenCV, meet de herprojectiefout, detecteer ArUco-markers en schat de markerpose met de juiste eenheden en coördinaten.

Een camerabeeld rapporteert pixels, maar een robot heeft stralen en coördinaten nodig. Camerakalibratie schat de intrinsieke parameters die punten in het 3D-camerastelsel naar pixels vertalen, plus de vervorming die de lens introduceert. Zodra die waarden bekend zijn, kan een marker met bekende geometrie zijn positie en oriëntatie ten opzichte van de camera leveren.

ArUco-markers zijn vierkante binaire fiducials met een ID. De vier hoeken detecteren identificeert de marker in het beeld; posebepaling combineert die pixelhoeken, de werkelijke markergrootte en de gekalibreerde cameraparameters.

Begrijp wat de kalibratie oplevert

De cameramatrix bevat de brandpuntsafstanden en het optisch middelpunt:

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

fx en fy zijn uitgedrukt in pixels. cx en cy bepalen het hoofdpunt. Vervormingscoëfficiënten modelleren effecten zoals radiale barrel- en kussenvormige vervorming en tangentiële verschuiving.

Deze waarden horen bij een camera, een lens, een scherpstelling en een beeldgeometrie. De resolutie veranderen door bijsnijden of herschalen kan betekenen dat de intrinsieke parameters moeten worden aangepast; van scherpstelling wisselen of de lens vervangen vraagt om herkalibratie.

Druk en meet een kalibratiedoel

Een schaakbordpatroon is een praktisch eerste doel omdat de binnenhoeken makkelijk te definiëren zijn. Meet de afmeting van de vierkantjes nauwkeurig. De schaal van de pose erft deze eenheid: objectpunten in meters geven translaties in meters; objectpunten in millimeters geven millimeters.

Houd het doel vlak. Een gekruld blad schendt de vlakke geometrie die de objectpunten veronderstellen. Monteer het op stijf materiaal als nauwkeurigheid telt.

Definieer het aantal binnenhoeken, niet de afgedrukte vierkantjes. Een bord van 10 bij 7 vierkantjes heeft 9 bij 6 binnenhoeken. Die aantallen door elkaar halen laat de detectie mislukken, ook als het beeld er helder uitziet.

Verzamel een bruikbare beeldset

Verzamel minstens tien geslaagde opnames als uitgangspunt en voeg daarna diversiteit toe in plaats van duplicaten. Beweeg het bord door het hele beeld, kantel het om beide assen, varieer de afstand en neem hoeken mee nabij de beeldranden waar de vervorming het sterkst is. Houd het patroon scherp en volledig zichtbaar.

Twintig vrijwel identieke beelden recht van voren leveren minder informatie dan minder, maar goed verspreide standpunten. Verwerp bewegingsonscherpte en beelden waarin de hoekdetectie onzeker is. Gebruik dezelfde resolutie en camera-instellingen als je voor gebruik plant.

Sla voor elk geaccepteerd beeld de gedetecteerde beeldhoeken en de bijbehorende vlakke objectpunten (column × squareSize, row × squareSize, 0) op.

Kalibreer met OpenCV

De essentiële Python-flow is:

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
)

Bewaak tegen te weinig geaccepteerde beelden voordat je de kalibratie aanroept. Sla de matrix, de vervormingscoëfficiënten, de beeldbreedte en -hoogte, de doeldefinitie en de kalibratiedatum samen op. Een kaal arraybestand is later makkelijk verkeerd te gebruiken.

Valideer meer dan de teruggegeven RMS-waarde

De wortelgemiddelde-kwadratische (RMS) herprojectiefout vat samen hoe ver geprojecteerde kalibratiepunten af liggen van gedetecteerde beeldpunten. Lager is doorgaans beter, maar één getal kan slechte gebieden of een zwakke beeldset verhullen.

Een staafdiagram van de herprojectiefout voor achttien kalibratiebeelden. Zeventien staven staan rond 0,3 pixels; beeld twaalf springt eruit op 1,95 pixels en is geannoteerd als bewegingsonscherpte met hoeken in verkeerde volgorde gedetecteerd. Een doorgetrokken lijn markeert de RMS over alle achttien beelden op 0,54 pixels, en een stippellijn markeert 0,29 pixels zonder beeld twaalf.
Achttien beelden, één onscherp. De totale RMS leest nog een respectabele 0,54 px — en juist daarom is het ene getal dat de kalibrator afdrukt geen voldoende. SVG downloaden

Bereken de herprojectiefout per beeld en bekijk de slechtste opnames. Teken de geherprojiceerde punten over de beelden heen. Controleer rechte architectonische lijnen na de undistortie, vooral nabij de hoeken. Schat daarna de pose van een doel op bekende afstand en vergelijk de translatie met een fysieke meting.

Als één beeld de fout domineert, inspecteer en verwijder het alleen om een verdedigbare reden zoals onscherpte of verkeerde hoekvolgorde. Door frames met hoge fout steeds verwijderen tot het getal er goed uitziet, kun je een zwak model of een zwak doel verhullen.

Detecteer een ArUco-marker

Kies een vooraf gedefinieerd woordenboek en genereer markers uit datzelfde woordenboek. Grotere bitrasters bieden meer ID’s, maar elke bit krijgt bij een vaste beeldgrootte minder pixels. Druk de marker met een rustige witte rand en meet de zijlengte van het zwarte vierkant die het posemodel gebruikt.

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)

De API-constructie verschilt per oudere OpenCV-versie, dus laat de code aansluiten op de officiële documentatie van de geïnstalleerde versie. De uitvoer moet nog steeds vier geordende beeldhoeken per gedetecteerde marker opleveren.

Pose schatten met een bekende markergrootte

Definieer de markerhoeken in het eigen coördinatenstelsel van de marker en los het perspectief-n-puntprobleem op. Voor een markerzijde s verwacht OpenCVs oplossing voor vierkanten een consistente hoekvolgorde:

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 is de oorsprong van de marker uitgedrukt in het camerastelsel en gebruikt dezelfde lengte-eenheid als s. rvec is een Rodrigues-rotatievector, geen Euler-hoeken. Gebruik cv2.Rodrigues() om een rotatiematrix te krijgen, of transformeer hem via het referentiestelsel van de robot.

Teken de geprojecteerde coördinaatassen en controleer ze fysiek. Een positieve translatie met een geloofwaardige grootte is niet genoeg als de assen gespiegeld zijn of de verkeerde markergrootte is ingevuld.

Maak de pose stabiel genoeg voor een robot

De pose wordt ruisachtig wanneer de marker weinig pixels beslaat, steil staat, onscherp is of de beeldrand nadert. Verbeter de belichting en de instelling, gebruik een grotere marker, ga dichterbij, of gebruik een bord met meerdere markers als de toepassing dat toestaat.

Filter alleen geldige poses en bewaar tijdstempels. Een laagdoorlaatfilter vermindert het rammelen maar voegt regelvertraging toe. Verwerp plotselinge resultaten op basis van herprojectiefout, afstandsgrenzen en bewegingslimieten in plaats van elke detectie blindelings te middelen.

De markerpose is relatief ten opzichte van de camera. Om een robotarm of mobiele basis aan te sturen, transformeer je hem via de gekalibreerde cameramontage naar base_link of de armbasis. Een afwijking tussen millimeters en meters levert hier een factor duizend aan fout op.

Diagnoseer veelvoorkomende fouten

  • Undistortie buigt rechte lijnen: de kalibratiebeelden dekken onvoldoende af, het doel was niet vlak, of de beeldgeometrie is veranderd.
  • De afstandsschaal is consequent verkeerd: voor het vierkantje of de marker is de verkeerde meting of eenheid gebruikt.
  • De poseassen wijzen de verkeerde kant op: de volgorde van de objecthoeken of de stelselconventie klopt niet.
  • De marker-ID flikkert: de marker is te klein, onscherp, slecht gedrukt, of komt uit een ander woordenboek.
  • De pose springt bij een recht-vooraanzicht: de dubbelzinnigheid van de vlakke pose en de zwakke pixelgeometrie vragen om betere standpunten, tracking of meerdere-markerbeperkingen.
  • Kalibratie werkt maar bij één resolutie: de intrinsieke parameters werden na bijsnijden of opschalen ongewijzigd hergebruikt.
  • De robotbeweging loopt achter op de marker: overmatig gladstrijken of verouderde tijdstempels verbergen de huidige geometrie.

Gebruik de Sensor Simulator om ruis, bias, filtering en vertraging te verkennen. De Robot Arm Simulator laat zien hoe een doelpose ook bij correcte camerageometrie nog onbereikbaar kan zijn.

Verken de graaf

Onderdeel van deze bouwprojecten

Projecten en leerroutes waarin deze tutorial voorkomt.

Verder lezen

Bronnen