刚接触机器人视觉定位时,我也纠结过该用哪种方案。试过激光SLAM、视觉SLAM,最后发现对于室内固定场景,ArUco二维码才是真香选择。这就像在商场找洗手间,直接看指示牌比记路线图快多了。
ArUco是专门为机器人定位设计的二维码,相比普通QR码有三个碾压性优势:
去年给仓库AGV做导航时就深有体会。激光方案遇到玻璃货架会丢失定位,而在地面贴了20个ArUco标签后,机器人再没迷路过。下面这张对比表能直观看出差异:
| 定位方案 | 精度 | 速度 | 环境要求 | 硬件成本 |
|---|---|---|---|---|
| 激光SLAM | ±3cm | 10Hz | 无玻璃 | ¥8k+ |
| 视觉SLAM | ±5cm | 15Hz | 光线稳定 | ¥3k+ |
| ArUco二维码 | ±1cm | 30Hz | 无要求 | ¥500 |
不过要注意,ArUco最适合结构化环境。如果是户外或者频繁变动的场景,还是SLAM更合适。接下来我会手把手带你从零搭建整套系统,用的都是经过实战检验的方案。
千万别随便网上下载二维码就用,尺寸不对会直接导致定位漂移。推荐用官方工具生成,我常用的是36h11字典(平衡了识别率和抗噪能力):
python复制import cv2
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_6X6_250)
marker_img = cv2.aruco.drawMarker(aruco_dict, id=42, sidePixels=600)
cv2.imwrite("marker42.png", marker_img)
这里有几个关键参数要注意:
贴标签时踩过的坑:
USB相机和树莓派相机我都实测过,推荐Logitech C920,30FPS下延迟只有50ms。关键是要正确配置分辨率:
bash复制# usb_cam的launch文件要这么改
<param name="video_device" value="/dev/video0" />
<param name="image_width" value="1280" />
<param name="image_height" value="720" />
<param name="framerate" value="30" />
常见的坑是没启用硬件加速,CPU占用直接飙到90%。加上这个参数立竿见影:
bash复制<param name="v4l2_palette" value="3" /> # 使用MJPEG格式
没标定的相机就像没调教过的指南针,定位误差能差出10cm。准备一个棋盘格(建议用A4纸打印8x6格),然后运行:
bash复制rosrun camera_calibration cameracalibrator.py \
--size 8x6 \
--square 0.024 \
image:=/usb_cam/image_raw \
camera:=/usb_cam
移动标定板时要像写"米"字一样覆盖整个视野,直到所有进度条变绿。标定后会在/tmp生成calibrationdata.tar.gz,把里面的ost.yaml复制到~/.ros/camera_info/。
重要细节:
官方源码有些小坑,建议用我这个改好的版本:
bash复制cd ~/catkin_ws/src
git clone https://gitee.com/ros-mirrors/aruco_ros.git -b kinetic-devel
# 关键修复:替换所有CV_FILLED为-1
sed -i 's/CV_FILLED/-1/g' aruco_ros/src/*.cpp
catkin_make -DCATKIN_WHITELIST_PACKAGES="aruco;aruco_ros"
编译时报错fatal error: aruco/marker.h: No such file的话,需要先装核心库:
bash复制sudo apt-get install ros-$ROS_DISTRO-aruco
先启动相机:
bash复制roslaunch usb_cam usb_cam.launch
然后修改single.launch关键参数:
xml复制<arg name="markerId" default="42"/> <!-- 必须和打印的ID一致 -->
<arg name="markerSize" default="0.05"/> <!-- 实际边长单位是米 -->
<arg name="camera_frame" default="usb_cam"/>
<arg name="marker_frame" default="aruco_marker"/>
启动识别节点:
bash复制roslaunch aruco_ros single.launch
用rqt_image_view查看效果,正常应该能看到标记框和坐标系:

通过rostopic可以看到6DOF位姿信息:
bash复制rostopic echo /aruco_single/pose
输出示例:
code复制position:
x: 0.32
y: -0.15
z: 1.08
orientation:
x: -0.01
y: 0.707
z: 0.707
w: 0.01
这里有个易错点:坐标系遵循ROS标准,z轴向前,x轴向右,y轴向下。我在集成导航栈时就在这里栽过跟头,机器人总是往反方向跑。
精度优化技巧:
detector_params.yaml中调高cornerRefinementMaxIterations到50image_proc提升图像对比度python复制import numpy as np
from collections import deque
class PoseFilter:
def __init__(self, window_size=5):
self.buffer = deque(maxlen=window_size)
def update(self, pose):
self.buffer.append(pose.position.z)
return np.mean(self.buffer)
要让定位数据真正有用,必须正确配置TF树。在static_transform_publisher中设置相机到机器人基座的变换:
bash复制rosrun tf static_transform_publisher \
0.1 0 0.15 \
0 0 0 1 \
base_link usb_cam 100
这里参数含义:
验证TF树是否正确:
bash复制rosrun tf view_frames
evince frames.pdf
单纯用ArUco定位会有抖动,配合IMU做融合更可靠:
xml复制<node pkg="robot_pose_ekf" type="robot_pose_ekf" name="robot_pose_ekf">
<param name="output_frame" value="odom"/>
<param name="freq" value="30.0"/>
<remap from="imu_data" to="/imu/data"/>
<remap from="vo" to="/aruco_single/pose"/>
</node>
参数调优经验:
process_noise_covariance对角线元素调到0.01freq到10Hzodom_used和imu_used修改costmap_common_params.yaml:
yaml复制observation_sources: aruco_observation
aruco_observation: {sensor_frame: usb_cam, data_type: PoseWithCovarianceStamped, topic: /aruco_single/pose, marking: True}
实测效果:在20㎡的实验室环境中,定位误差能稳定在±2cm内,完全满足仓储搬运需求。不过要注意定期检查标签是否破损,我有次就因为标签被叉车刮花导致机器人撞货架。