ArUco Detector Node (aruco_detector)¶
Overview¶
aruco_detector detects OpenCV ArUco markers in a camera stream and estimates each marker’s 6-DoF pose using known camera intrinsics + marker size. It can:
Subscribe to raw or compressed images
Optionally subscribe to CameraInfo for live calibration
Filter weak detections using a minimum bounding-box area threshold
Publish annotated debug images
Publish detections on a topic (
ObjectDetections) (optional)Broadcast TF frames for each detected marker (
aruco_marker_<id>) (optional)Provide detections via a service, with an option to save an annotated image to disk
Node Name¶
aruco_detector
Subscribed Topics¶
Image input (raw)¶
Used when compressed_io = false:
<input_img> (sensor_msgs/Image)
Default
/camera/camera/color/image_raw
Image input (compressed)¶
Used when compressed_io = true:
<input_img>/compressed (sensor_msgs/CompressedImage)
Camera info (optional)¶
Used only when use_camera_info = true and compressed_io = false (as implemented):
<camera_info_topic> (sensor_msgs/CameraInfo)
Default
/camera/camera/color/camera_info
Note: In the current code,
camera_infosubscription is created only in the raw-image branch (not in the compressed branch).
Published Topics¶
Annotated output image (raw)¶
Published when publish_img = true and compressed_io = false:
<output_img> (sensor_msgs/Image)
Default
/detection/aruco/image
Annotated output image (compressed)¶
Published when publish_img = true and compressed_io = true:
<output_img>/compressed (sensor_msgs/CompressedImage)
Detection output topic (optional)¶
Published when publish_output = true:
<output_topic> (perseus_interfaces/msg/ObjectDetections)
Default
/detection/aruco/detections
TF Frames¶
If publish_tf = true, for every detected marker ID N, the node publishes:
aruco_marker_<N>
Parent frame:
tf_output_frame
Default parent frame
odom
Services¶
Detect Objects¶
/detect_objects (perseus_interfaces/srv/DetectObjects)
Returns the latest cached detections, including:
ids[]: marker IDsposes[]: marker poses intf_output_framestamp: timestamp of the processed frameframe_id: set totf_output_frame
Image capture feature (new)¶
The request supports an image capture mode:
request->capture_image(bool)request->img_save_path(string path)
If capture_image = true, the node will:
Create the directory
img_save_path(viamkdir -p)Save an annotated PNG image:
Filename includes detected marker IDs:
aruco_<id1>_<id2>...pngIf no markers:
aruco_no_markers.png
Overlay text on the image:
Human-readable timestamp (system clock)
Marker coordinate summary (
X, Y, Zderived fromtvecconversion)
The saved image uses the node’s cached
latest_frame_which includes drawn markers/axes.
Parameters and Defaults¶
All parameters are under:
aruco_detector:
ros__parameters: ...
Marker detection / pose estimation¶
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
double |
|
Physical marker size (meters). Used for pose estimation scale. |
|
double |
|
Length of drawn axes in the debug image (meters). |
|
int |
|
OpenCV predefined dictionary enum value (must match the printed markers). |
|
double |
|
Filters detections: marker’s 2D bounding box area in pixels must be ≥ this threshold to be accepted. |
Bounding box filtering details
For each detected marker’s 4 corner points, the node computes:
min_x, max_x, min_y, max_yarea =
(max_x - min_x) * (max_y - min_y)
If area <
min_bounding_box_area, the marker is ignored (no TF, no output pose).
This helps reject:
tiny far-away false positives
noisy corner detections
partially detected markers
Frames / transforms¶
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Frame ID assigned to marker poses before TF transform. Should be the camera optical frame. |
|
string |
|
Target frame to transform marker poses into. Also used as |
Image I/O¶
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Raw image topic (base). |
|
string |
|
Output annotated image topic (base). |
|
bool |
|
If true: subscribe/publish to |
|
bool |
|
Publish annotated debug images. |
Outputs¶
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
bool |
|
Broadcast TF transforms |
|
bool |
|
Publish |
|
string |
|
Detection output topic name. |
Camera calibration¶
You support two modes:
1) Parameter-based calibration (always initialized)¶
These parameters are always declared and used as initial calibration.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
double[9] |
|
Row-major intrinsic matrix K. |
|
double[] |
|
Distortion coeffs, typically |
2) CameraInfo override (optional)¶
If enabled, incoming CameraInfo replaces intrinsics.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
bool |
|
If true, subscribe to |
|
string |
|
Topic for |
CameraInfo behavior
camera_matrix_is built frommsg->k[0..8]dist_coeffs_is built frommsg->d[](any length supported)
In
processImage, ifcamera_matrix_is empty, pose estimation is skipped (warn once). In the current code,camera_matrix_is initialized from params, so it will not be empty unless changed elsewhere.
Detection Pipeline (detailed)¶
Receive image (raw or compressed)
Detect markers with:
detector_.detectMarkers(frame, corners, ids);Clone frame for annotation:
annotated_frame = frame.clone();Clear cached detections and update timestamp:
latest_ids_,latest_poses_latest_timestamp_ = header.stamp
If markers exist:
draw marker borders
for each marker:
estimate pose via
cv::solvePnPusing 3D marker corner points and detected 2D image pointscompute bbox area in pixels
apply
min_bounding_box_areafilterdraw axes
convert pose + transform to output frame
cache pose + id
optionally publish TF transform
Cache annotated frame:
latest_frame_ = annotated_frame.clone()latest_marker_coords_stores marker positions (in the camera-converted XYZ convention used for display)
Optionally publish
ObjectDetectionsmessage if enabled.
Example YAML Configuration¶
aruco_detector:
ros__parameters:
marker_length: 0.35
axis_length: 0.03
# 4x4: 50=0, 100=1, 250=2, 1000=3 | 5x5: 50=4, 100=5, 250=6, 1000=7 | 6x6: 50=8, 100=9, 250=10, 1000=11
dictionary_id: 1
min_bounding_box_area: 150.0
camera_frame: camera_link_optical
tf_output_frame: odom
input_img: /camera/camera/color/image_raw
output_img: /detection/aruco/image
compressed_io: false
publish_img: true
publish_tf: true
publish_output: true
output_topic: /detection/aruco/detections
use_camera_info: true
camera_info_topic: /camera/camera/color/camera_info
Usage¶
Run with params file¶
ros2 run perseus_vision aruco_detector_node --ros-args \
--params-file <path_to_yaml>
Service call (detections only)¶
ros2 service call /detect_objects perseus_interfaces/srv/DetectObjects "{}"
Service call (capture image)¶
ros2 service call /detect_objects perseus_interfaces/srv/DetectObjects \
"{capture_image: true, img_save_path: '/tmp/aruco_captures'}"
Notes¶
dictionary_idmust match the marker dictionary used to generate/print the tags.marker_lengthmust match the real marker size in meters.Filtering by
min_bounding_box_areais in pixels², so thresholds depend on:camera resolution
distance to marker
FOV and lens
In current modifications,
camera_infosubscription happens only in the raw-image path.