ArUco markers for robot positioning: detection and pose

Detect ArUco markers in OpenCV and turn them into a metric robot pose: dictionaries, solvePnP, pose ambiguity, accuracy limits and docking tips.

Reference
RE-1269
Published
Read
7 min

About the author · Sources, testing and AI policy

ArUco marker printed on a sheet being detected by a camera mounted on a small robot

A printed square with four black blocks can tell a robot exactly where it is. ArUco markers are cheap, fast to detect and precise enough for docking, picking and calibration.

This guide explains how ArUco markers work, how to detect them in OpenCV, how to turn a detection into a metric pose, and the practical limits that decide whether a marker will work in your robot cell.

What an ArUco marker is

An ArUco marker is a black square border containing a grid of black and white cells that encodes an identifier. A dictionary defines how many markers exist and how many bits each one carries.

DictionaryMarker gridCommon use
DICT_4X4_504 by 4 bits, 50 IDsSmall markers, short range
DICT_5X5_1005 by 5 bits, 100 IDsGeneral robotics
DICT_6X6_2506 by 6 bits, 250 IDsMore IDs, more distance
DICT_ARUCO_ORIGINALLegacy dictionaryCompatibility with older systems

Fewer bits means a smaller marker at the same distance, but more chance of confusion with other patterns. For a robot that sees only its own markers, a 4 by 4 dictionary is usually enough. For a warehouse floor with dozens of stations, use 5 by 5 or 6 by 6 and keep a register of IDs.

Detection in OpenCV

Newer OpenCV versions use the ArucoDetector class. Older code uses detectMarkers with a dictionary object. Both return four corners per marker in a fixed order, starting from the top-left as seen in the marker’s own frame.

import cv2

dictionary = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_5X5_100)
params = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(dictionary, params)

corners, ids, rejected = detector.detectMarkers(gray)
if ids is not None:
    for i, marker_id in enumerate(ids.flatten()):
        print(marker_id, corners[i].reshape(4, 2))

Draw the detections during development. A marker that is detected with the wrong corner order, or detected intermittently, is worse than one that is not detected at all because it produces a plausible but wrong pose.

From corners to a metric pose

The marker’s four corners form a square of known side length. Define those four points in marker coordinates, match them to the detected corners, and solve the perspective-n-point problem with the calibrated camera matrix.

marker_mm = 80.0
half = marker_mm / 2.0

object_points = [
    [-half,  half, 0],
    [ half,  half, 0],
    [ half, -half, 0],
    [-half, -half, 0],
]

ok, rvec, tvec = cv2.solvePnP(
    np.array(object_points, dtype=np.float32),
    corners[i].reshape(4, 2).astype(np.float32),
    K, dist, flags=cv2.SOLVEPNP_IPPE_SQUARE)

R, _ = cv2.Rodrigues(rvec)

The result is the marker pose in the camera frame. tvec is the position of the marker centre in millimetres if the object points are in millimetres, and the rotation matrix describes the marker’s orientation, with z pointing out of the printed face.

To use that pose in the robot, multiply by the camera-to-robot transform. That transform is a hand-eye calibration result, and it is the step most home-built systems get wrong.

Pose ambiguity, and why small markers flip

A planar target seen from a distance has two mathematically valid poses that are nearly mirror images. When the marker is small in the image, noise decides which one wins, and the estimate flips between frames.

  • Use a bigger marker if the working distance is fixed.
  • Aim for at least 40 pixels per marker side, and preferably 80 or more.
  • Reject poses with high reprojection error.
  • Filter the pose over time; a jumping estimate is often a solved ambiguity, not noise.
  • Use a board of several markers to constrain the pose.

A marker board, sometimes called a ChArUco board, combines ArUco IDs with chessboard corners. It gives many more correspondences and a much more stable pose, at the cost of a larger printed target.

Accuracy you can expect

ConditionTypical position error
Large marker, close range, calibrated cameraA few millimetres
Small marker at long rangeTens of millimetres, unstable
Uncalibrated cameraNot metric; do not use for control
Marker at a steep angleDegrades quickly beyond about 60 degrees
Motion blur during detectionCorner error, then pose error

Treat these as orders of magnitude, not specifications. The only number that matters is the error measured on your own cell, at your own distance, with your own lighting.

Docking with markers

A common use is docking: the robot drives until the marker pose reaches a target position and angle, then stops. The controller works in the camera frame, so no global map is needed.

  • Mount the marker at the robot’s working height so the camera sees it square on.
  • Approach slowly; marker pose noise at speed causes overshoot.
  • Stop when the pose is within tolerance and stable for several frames, not on a single detection.
  • Keep a physical stop or bumper as a backstop.
  • Log the final pose error to tune the tolerance over time.

Printing markers that survive

  • Print on matte paper or vinyl; glossy prints create reflections that break detection.
  • Use a black that is truly black under your lighting, and check with the camera.
  • Leave a quiet white border of at least one cell width around the marker.
  • Laminate or use vinyl if the marker will be handled or cleaned.
  • Keep markers flat; a wrinkled marker distorts the corner positions.
  • Print at a known size and measure the result before entering the size in code.

Combining markers with wheel odometry

A marker detection is an absolute position, but it is intermittent. Wheel odometry is continuous, but it drifts. Fuse them: use odometry between detections and correct towards the marker pose when it appears.

The simplest version is a complementary filter. Integrate odometry for the prediction, then blend a weighted correction each time a marker is seen, with a weight based on the measured distance to the marker. Far markers are noisy, so weight them down.

# predicted pose from odometry, measured pose from marker
alpha = 0.9 if distance_mm < 600 else 0.6
x = alpha * x_odom + (1 - alpha) * x_marker
y = alpha * y_odom + (1 - alpha) * y_marker

Do the same for heading, taking care with angle wrap-around. This is not a full Kalman filter, but on a slow indoor robot it removes most of the drift and is easy to debug.

One practical detail: timestamp the marker pose at the moment of capture, not at the moment processing finishes. Vision costs tens of milliseconds, and at 0.5 m/s that is a centimetre of motion you would otherwise ignore.

When not to use ArUco

Markers are not a general localisation system. They need line of sight, they degrade in dust and glare, and they must be installed. For a mobile robot in a changing warehouse, lidar or visual SLAM is a better fit. For a fixed cell where a target can carry a sticker, markers are hard to beat on cost and simplicity.

They are also a poor choice for safety functions. A marker can be covered, misprinted or confused with another ID, and none of those failures are detectable by the marker itself. Use them for positioning, not for guarding.

Related: OpenCV camera calibration, robot vision camera guide, OpenCV on Raspberry Pi, mobile robot docking, machine vision lighting.

Do I need a calibrated camera to use ArUco markers?

For detection and ID reading, no. For a metric pose in millimetres, yes. Without calibration the pose is scaled incorrectly and the distortion error grows towards the image edges.

How far away can an ArUco marker be detected?

It depends on marker size, camera resolution and lighting. A practical rule is to keep the marker at least 40 pixels wide in the image, and preferably 80 or more for a stable pose.

Why does my marker pose jump between two values?

That is planar pose ambiguity. The marker is small enough that two poses fit almost equally well. Use a larger marker, reject high-error solutions, filter over time, or use a multi-marker board.

Can I use ArUco for robot safety?

No. Markers can be covered, misprinted or confused, and the detection gives no guarantee of integrity. Use certified safety devices for guarding and treat markers as positioning aids.

What is the difference between ArUco and ChArUco?

ArUco markers encode an ID and give four corners. A ChArUco board combines markers with chessboard corners, providing many more correspondences and a more stable pose.

Markers answer where the target is in the image, while a depth camera answers how far away the surface sits.

Marker poses correct the drift that wheel odometry accumulates over a run.

Sources and further reading

Pass it onWhatsAppLinkedIn

Keep reading

Continue learning

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.