Based on contributions by sarahstam.
The fastest way to get a fixed, top-down camera talking to a robot in the same units: print one ArUco marker, use its known physical size as a ruler, and record a single offset between the camera’s and the robot’s coordinate systems.
This targets OpenCV ≥ 4.7:
getPredefinedDictionary(),generateImageMarker(),DetectorParameters()and theArucoDetectorclass are all part of the API introduced in that version. On OpenCV 4.6 or older the equivalents areDictionary_get(),drawMarker(),DetectorParameters_create()and the free functioncv2.aruco.detectMarkers(image, dictionary, parameters).
What you need
- OpenCV ≥ 4.7 (
opencv-python) andmatplotlib. - A printer, and calipers or a ruler to measure the printed marker.
- A camera mounted directly above the workspace, looking straight down, with its X/Y axes parallel (or exactly mirrored) to the robot’s — this method does not correct for rotation.
- A robot you can jog and read coordinates from.
Check: because this method has no rotation correction, it only works well if the camera is genuinely aligned with the robot base axes. If you’re not sure, use the least-squares fit (How to map camera coordinates to robot coordinates with a least-squares fit) instead — it fits rotation automatically from the same kind of data.
Steps
1. Generate and print an ArUco marker
import cv2
import numpy as np
import matplotlib.pyplot as plt
aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
marker_id = 1 # id of the marker inside the dictionary (0-249 for DICT_6X6_250)
img_size = 250 # size of the generated image, in pixels
marker_img = cv2.aruco.generateImageMarker(aruco_dict, marker_id, img_size)
plt.imshow(marker_img, cmap='gray', interpolation='nearest')
plt.show()
cv2.imwrite(f'aruco{marker_id}.png', marker_img)
Print the marker and measure its actual printed side length in mm — you’ll need this exact value later.
2. Record a reference point
Place the marker in the middle of the camera’s view. Move the robot so its reference point (e.g. TCP) is exactly over the middle of the marker, and write down the robot’s X and Y there. Move the robot out of view and take a photo of the marker in the exact same position (call it e.g. img.png).
3. Detect the marker and compute the pixel-to-mm ratio
import cv2
import numpy as np
import matplotlib.pyplot as plt
marker_actual_size_mm = 66 # measured side length of your PRINTED marker
img = cv2.imread('img.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
parameters = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(aruco_dict, parameters)
corners, ids, rejected = detector.detectMarkers(gray)
if ids is None:
raise RuntimeError("No ArUco marker detected - check the dictionary, lighting and focus.")
cv2.aruco.drawDetectedMarkers(img, corners, ids)
marker_corners = corners[0][0]
top_left, top_right, bottom_right, bottom_left = marker_corners
pixel_width = np.linalg.norm(top_right - top_left)
pixel_to_mm_ratio = marker_actual_size_mm / pixel_width
print(f"Pixel-to-mm conversion factor: {pixel_to_mm_ratio} mm per pixel")
center_x = int(np.mean(marker_corners[:, 0]))
center_y = int(np.mean(marker_corners[:, 1]))
cv2.circle(img, (center_x, center_y), 10, (0, 255, 0), -1)
print(f"Marker center in pixels: X={center_x}, Y={center_y}")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title("Detected ArUco marker")
plt.show()
If this worked, the plot shows a green outline around the marker and a green dot at its center.
4. Check the axis relationship
Look at the plotted image and compare it to the robot’s own axis directions. For example, if the camera’s +X matches the robot’s +X, but the camera’s +Y is opposite the robot’s +Y:
x_cam = x_robot + offset_x
y_cam = -y_robot + offset_y
If your axes are swapped instead, rewrite accordingly, e.g. x_cam = y_robot + offset_x, y_cam = x_robot + offset_y.
5. Compute the offset
You now have the marker center in camera coordinates (from step 3, converted to mm with pixel_to_mm_ratio) and the matching robot coordinates (from step 2). Rearranging the equation from step 4:
x_cam_mm = center_x * pixel_to_mm_ratio
y_cam_mm = center_y * pixel_to_mm_ratio
offset_x = x_cam_mm - x_robot
offset_y = y_cam_mm + y_robot
6. Apply the calibration to new detections
def pixel_to_robot(pixel_x, pixel_y, pixel_to_mm_ratio, offset_x, offset_y):
"""Convert a detected pixel to robot coordinates, using the calibration from steps 3-5."""
x_cam_mm = pixel_x * pixel_to_mm_ratio
y_cam_mm = pixel_y * pixel_to_mm_ratio
x_robot = x_cam_mm - offset_x
y_robot = offset_y - y_cam_mm
return x_robot, y_robot
Check: the exact sign of each term in
pixel_to_robotdepends on the axis relationship you determined in step 4 — the example above matches thex_cam = x_robot + offset_x,y_cam = -y_robot + offset_ycase. Adjust it to match your own axes.
Common mistakes
- No marker detected. Confirm the dictionary used to detect (
DICT_6X6_250) matches the one used to generate the marker, check lighting and focus, and make sure the whole marker (including its white border) is in frame. - Wrong
marker_actual_size_mm. Any measurement error here scales every downstream coordinate by the same percentage — measure the printed marker carefully with calipers, not by eye. - Camera not perfectly aligned with the robot base. This method has no rotation correction. If pick accuracy gets worse the further the object is from the reference point, your camera is rotated relative to the robot and you need a method that fits rotation, like How to map camera coordinates to robot coordinates with a least-squares fit or How to calibrate a camera and map pixels to world coordinates with a ChArUco board.
Related
- Choosing a method to convert camera pixels to robot coordinates — choosing a calibration method
- How to map camera coordinates to robot coordinates with a least-squares fit — least-squares affine fit (also handles rotation)
- How to calibrate a camera and map pixels to world coordinates with a ChArUco board — ChArUco board + homography (multi-point, corrects lens distortion)
- How to do high-precision robot-camera calibration with ArUco markers — high-precision two-stage calibration with multiple ArUco markers
Rewritten and consolidated (Sept 2026) from the original student how-to’s: How to calibrate your camera and robot using ArUco markers in python.