从棋盘格到精准感知:ROS camera_calibration实战单目与双目相机标定
1. 为什么相机标定是机器人视觉的"体检报告"?
想象一下你新配了一副眼镜,但镜片度数不准——看东西要么变形要么模糊。相机标定就是给机器人的"眼睛"做验光,确保它看到的图像能真实反映物理世界。我在做视觉SLAM项目时,曾因跳过标定步骤导致建图出现10cm的累积误差,不得不返工重来。
相机镜头天生存在两种误差:径向畸变(图像边缘弯曲)和切向畸变(镜头安装倾斜)。就像人眼的散光,不矫正会导致:
- 测量距离时出现系统性偏差
- 三维重建的点云扭曲
- 视觉定位的漂移累积
ROS的camera_calibration功能包就像一套自动化验光设备,通过分析棋盘格图案,计算出相机的:
- 内参矩阵(焦距、光心位置)
- 畸变系数(k1,k2,p1,p2,k3)
- 外参矩阵(双目相机间的相对位置)
实测表明,标定后的Realsense D435i相机,在2米距离上的测距误差从3.2%降至0.8%。下面我会手把手带你完成这个"机器人验光"流程。
2. 标定前的准备工作
2.1 制作高精度棋盘格
标定板相当于验光时的视力表,建议使用:
- 哑光材质(避免反光干扰)
- 边长≥0.1m的大棋盘(我常用8x6格)
- 打印时用游标卡尺验证实际尺寸
生成棋盘格的Python代码:
import cv2 pattern_size = (8, 6) # 角点数量 square_size = 0.1 # 单位:米 pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32) pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2) pattern_points *= square_size img = cv2.drawChessboardCorners(np.ones((800,600)), pattern_size, pattern_points[:,:2], True) cv2.imwrite("chessboard.png", img*255)2.2 搭建标定环境
踩坑经验:
- 光照要均匀(避免强侧光)
- 相机固定在三脚架上
- 关闭自动对焦和白平衡
- 标定前先做10分钟预热
检查相机话题是否正常:
rostopic list | grep image_raw3. 单目相机标定实战
3.1 启动标定节点
运行命令(以Realsense为例):
rosrun camera_calibration cameracalibrator.py \ --size 8x6 \ --square 0.1 \ image:=/camera/color/image_raw \ camera:=/camera \ --no-service-check关键参数说明:
| 参数 | 作用 | 典型值 |
|---|---|---|
| --size | 棋盘格角点数 | 8x6 |
| --square | 单个格子物理尺寸(m) | 0.1 |
| image | 图像话题名 | /camera/color/image_raw |
3.2 数据采集技巧
标定界面会显示四个进度条:
- X/Y移动:让棋盘格覆盖视野四角
- Size变化:从最近到最远距离(建议0.3m~2m)
- Skew旋转:倾斜±60°以内
我的采集秘诀:
- 先做"米字形"移动覆盖视野
- 然后像画螺旋线一样逐渐远离
- 最后添加各种倾斜角度
- 采集100+张有效样本(进度条全绿)
3.3 保存与验证结果
点击CALIBRATE后:
- 终端会输出重投影误差(建议<0.2像素)
- 保存的yaml文件包含:
image_width: 640 image_height: 480 camera_matrix: !!opencv-matrix rows: 3 cols: 3 data: [615.5, 0, 327.2, 0, 615.1, 241.3, 0, 0, 1] distortion_coefficients: !!opencv-matrix rows: 1 cols: 5 data: [0.12, -0.25, 0.001, -0.002, 0.3]验证标定效果:
import cv2 undistorted = cv2.undistort(raw_image, camera_matrix, dist_coeffs)4. 双目相机标定进阶
4.1 同步采集设置
双目标定需要额外获取:
- 左右相机间的旋转矩阵R
- 平移向量T
- 立体矫正参数
启动命令示例:
rosrun camera_calibration cameracalibrator.py \ --approximate 0.1 \ --size 8x6 \ --square 0.1 \ left:=/camera/infra1/image_rect_raw \ right:=/camera/infra2/image_rect_raw \ right_camera:=/camera/right \ left_camera:=/camera/left \ --no-service-check4.2 关键注意事项
- 必须同步触发左右相机(硬件同步最佳)
- 标定板要同时出现在双视野中
- 检查视差图是否连续:
stereo = cv2.StereoBM_create() disparity = stereo.compute(left_img, right_img)4.3 参数解析
典型输出包含:
rotation: [0.999, 0.005, -0.012, -0.005, 0.999, -0.001, 0.012, 0.001, 0.999] translation: [-0.1203, 0.0005, 0.0011] essential: !!opencv-matrix fundamental: !!opencv-matrix5. 标定结果的实际应用
5.1 提升视觉SLAM精度
以RTAB-MAP为例,修改配置:
<Camera type="0"> <Matrix type="opencv-matrix"> <rows>3</rows> <cols>3</cols> <data>615.5 0 327.2 0 615.1 241.3 0 0 1</data> </Matrix> <Distortion type="opencv-matrix"> <rows>1</rows> <cols>5</cols> <data>0.12 -0.25 0.001 -0.002 0.3</data> </Distortion> </Camera>5.2 优化三维测量
标定后测距代码示例:
def calculate_depth(disparity, Q): points = cv2.reprojectImageTo3D(disparity, Q) return np.linalg.norm(points[y,x])实测对比:
| 距离(m) | 标定前误差(cm) | 标定后误差(cm) |
|---|---|---|
| 1.0 | 3.2 | 0.8 |
| 2.0 | 6.7 | 1.5 |
记得每3个月或剧烈震动后重新标定——就像眼镜度数会变化,相机的"视力"也需要定期检查。当看到机器人稳定输出厘米级精度的定位数据时,你会觉得这些准备工作绝对值得。
