相机标定实战:内参、畸变矫正一步步做
目录
本文从相机标定的目的与原理出发,用棋盘格完成 内参矩阵 K 与 畸变系数 D 的求解,分别演示 MATLAB 工具箱 与 ROS2 camera_calibration 两条路径,并说明如何在 ROS2 中加载 camera_info、做畸变矫正。
适用说明与实测环境
| 项 | 说明 |
|---|---|
| 文档类型 | 感知-视觉 · 进阶教程 |
| 适用产品 | USB 相机、深度相机 RGB 流;ROSLander / MentorPi 等带视觉的 ROS2 平台 |
| 实测软件 | Ubuntu 22.04 · ROS 2 Humble · OpenCV · MATLAB R2019b+(可选) |
| 标定板 | 8×6 内角点棋盘格,单格边长实测(如 25 mm) |
1. 为什么需要相机标定
不标定会怎样
- 直线在图像里变弯(桶形 / 枕形畸变)
- 像素距离 ≠ 真实距离,测距、抓取、视觉伺服误差大
- 多传感器融合(激光 + 相机)对不齐
标定解决什么
| 目标 | 得到什么 | 用途 |
|---|---|---|
| 内参标定 | 焦距 fx,fy、主点 cx,cy、畸变 k1,k2,p1,p2... |
畸变矫正、像素→射线 |
| 外参标定(本文略述) | 旋转 R、平移 t | 手眼、多相机、相机与雷达对齐 |
可以记成一句话:
标定 = 用已知尺寸的棋盘格,反推“镜头如何把三维点投到像素上”。
2. 原理速览:坐标系、内参、畸变
2.1 四个坐标系(单目)
| 坐标系 | 含义 |
|---|---|
| 世界坐标系 | 标定板平面上的点,单位米或毫米 |
| 相机坐标系 | 光心为原点,Z 轴沿光轴 |
| 图像物理坐标系 | 像平面,单位 mm |
| 像素坐标系 | u,v,整像素索引 |
简化链路:
2.2 内参矩阵 K(3×3)
| fx 0 cx |
K = | 0 fy cy |
| 0 0 1 |
fx, fy:焦距(像素单位)cx, cy:主点,通常接近图像中心
2.3 畸变模型(OpenCV plumb_bob 常用 5 参)
| 类型 | 参数 | 现象 |
|---|---|---|
| 径向畸变 | k1, k2, k3 |
离中心越远弯曲越明显 |
| 切向畸变 | p1, p2 |
镜头与传感器不完全平行 |
标定后 OpenCV 用 cv2.undistort() 做矫正。
2.4 张正友标定法(实践怎么理解)
用多张不同姿态的棋盘格图像,建立 3D 角点 ↔ 2D 像素 对应,最小化重投影误差,同时解出 K 与 D。MATLAB / ROS2 工具箱底层都是这类思路。
3. 准备工作:棋盘格与拍摄
3.1 打印标定板
- 推荐 棋盘格(chessboard),内角点数记为
NxM(如 8×6 表示 8 列 6 行 内角点) - 打印后用 尺子实测单格边长(如 0.025 m),后面
--square参数必须填真实值 - 贴在 硬纸板 上,保证平整、不弯曲
注意:
--size 8x6填的是 内角点 数量,不是黑白格子总数。
3.2 拍摄要点(决定标定成败)
| 要点 | 说明 |
|---|---|
| 数量 | 至少 10 张,建议 15~20 张 |
| 姿态 | 远近、左右、上下、倾斜都要覆盖 |
| 清晰 | 对焦准确,避免运动模糊 |
| 完整 | 整张棋盘格在画面内,角点可被检测 |
| 光照 | 避免过曝、强反光、阴影遮挡格子 |
3.3 ROS2 依赖
sudo apt install ros-$ROS_DISTRO-camera-calibration
sudo apt install ros-$ROS_DISTRO-cv-bridge ros-$ROS_DISTRO-image-transport
4. MATLAB 标定实战
适合先在 PC 上验证流程,或相机只能导出图片、暂不接 ROS 的场景。
4.1 启动标定工具
cameraCalibrator
% 双目标定:stereoCameraCalibrator
4.2 添加图像
- 点击 Add Images,选择棋盘格图片文件夹
- 设置 Square size(格子边长,与实测一致,如
25mm) - 确认工具箱识别到的 可用张数
4.3 执行标定
- 点击 Calibrate,等待优化完成
- 查看 Reprojection Error(平均重投影误差,一般 < 0.5 pixel 较理想)
- 点击 Export Camera Parameters → 导出
cameraParams
4.4 矫正图像验证
img = imread('test.jpg');
undistorted = undistortImage(img, cameraParams);
imshowpair(img, undistorted, 'montage');
4.5 导出给 OpenCV / ROS2
- Export 菜单可导出 MAT / YAML
- 给 ROS2 用:将 yaml 放到
~/.ros/camera_info/<camera_name>.yaml IntrinsicMatrix与 OpenCV 的 K 可能需要转置,导出后务必undistort目视验证
5. ROS2 标定实战
以单目 RGB 话题为例(深度相机彩色流、USB 相机均可)。
5.1 启动相机
ros2 launch usb_cam camera.launch.py
# 或:ros2 launch orbbec_camera gemini.launch.xml
ros2 topic list | grep image
ros2 topic hz /camera/color/image_raw
记下 图像话题 与 camera 命名空间(如 /camera/color/image_raw 与 /camera/color)。
5.2 启动标定节点
ROS 2 中话题重映射写在 --ros-args -r 之后:
ros2 run camera_calibration cameracalibrator \
--size 8x6 \
--square 0.025 \
--no-service-check \
--ros-args \
-r image:=/camera/color/image_raw \
-r camera:=/camera/color
| 参数 | 含义 |
|---|---|
--size 8x6 |
内角点列×行 |
--square 0.025 |
格子边长(米) |
--no-service-check |
不强制等待 set_camera_info 服务(仿真 / 无该服务时常用) |
-r image:= |
图像话题重映射 |
-r camera:= |
相机命名空间(对应 …/set_camera_info) |
5.3 GUI 操作与进度条
手持标定板在相机前缓慢移动,覆盖多种姿态:
| 进度条 | 含义 |
|---|---|
| X | 标定板在画面左右位置覆盖 |
| Y | 上下位置覆盖 |
| Size | 远近 / 尺度覆盖(通常最难满) |
| Skew | 倾斜角度覆盖 |
四条尽量变绿 → CALIBRATE → SAVE → COMMIT。
mkdir -p ~/calibration_result
tar -xzf /tmp/calibrationdata.tar.gz -C ~/calibration_result
# 内含 ost.yaml、标定用图片等
ost.yaml 中需确认含有 camera_name、camera_matrix、distortion_coefficients 等字段,后续与 camera_info_url 配套使用。
5.4 查看标定结果
若已 COMMIT 且相机节点会发布 camera_info:
ros2 topic echo /camera/color/camera_info --once
关注:
distortion_model: plumb_bob
d: [k1, k2, p1, p2, k3]
k: [fx, 0, cx, 0, fy, cy, 0, 0, 1]
也可直接查看导出的 yaml:
cat ~/calibration_result/ost.yaml
6. ROS2 中如何应用标定结果
6.1 方式一:camera_info_url(推荐)
mkdir -p ~/.ros/camera_info
cp ~/calibration_result/ost.yaml ~/.ros/camera_info/my_camera.yaml
编辑 yaml,保证 camera_name: my_camera(与下面参数一致)。file:// 必须使用绝对路径。
launch 示例(usb_cam):
camera_node = Node(
package='usb_cam',
executable='usb_cam_node_exe',
name='camera',
parameters=[{
'video_device': '/dev/video0',
'image_width': 640,
'image_height': 480,
'camera_name': 'my_camera',
'camera_info_url': 'file:///home/ubuntu/.ros/camera_info/my_camera.yaml',
}],
)
camera_name 与 yaml 文件名、文件内 camera_name 字段一致,节点自动发布带 K、D 的 camera_info。
6.2 方式二:OpenCV 节点内矫正
将下列代码保存为 undistort_demo.py 后运行:
#!/usr/bin/env python3
import yaml
import cv2
import numpy as np
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSPresetProfiles
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
class UndistortNode(Node):
def __init__(self):
super().__init__('undistort_demo')
self.bridge = CvBridge()
with open('/home/ubuntu/.ros/camera_info/my_camera.yaml') as f:
calib = yaml.safe_load(f)
self.K = np.array(
calib['camera_matrix']['data'], dtype=np.float64).reshape(3, 3)
self.D = np.array(
calib['distortion_coefficients']['data'], dtype=np.float64).ravel()
qos = QoSPresetProfiles.SENSOR_DATA.value
self.sub = self.create_subscription(
Image, '/camera/image_raw', self.cb, qos)
self.pub = self.create_publisher(Image, '/camera/image_undistorted', 10)
def cb(self, msg):
img = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
undist = cv2.undistort(img, self.K, self.D)
out = self.bridge.cv2_to_imgmsg(undist, 'bgr8')
out.header = msg.header
self.pub.publish(out)
def main():
rclpy.init()
node = UndistortNode()
try:
rclpy.spin(node)
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
python3 undistort_demo.py
# 图像话题不一致时,可先改代码中的订阅名,或:
# ros2 run 方式下用 -r /camera/image_raw:=/usb_cam/image_raw
6.3 方式三:下游视觉 / 导航
| 场景 | 用法 |
|---|---|
| 像素→3D | K 反投影 + 深度或地面平面 |
| 手眼标定 | 内参已知后再标相机与 base_link 外参 |
| ArUco / Tag | 先 undistort 提高角点精度 |
6.4 验证清单
- 含直线的场景(门框、瓷砖缝)矫正后应更直
fx ≈ fy,主点(cx,cy)接近图像中心ros2 topic echo ... camera_info中 K、D 非零且合理
7. 常见踩坑
| 问题 | 原因 | 处理 |
|---|---|---|
| 角点检测不到 | 棋盘格不全、模糊、反光 | 换姿态、柔光、换背景 |
--size 填错 |
格子数 ≠ 内角点数 | 与打印图内角点一致 |
--square 不准 |
未实测格距 | 用尺子量,单位米 |
| Size 进度条难满 | 距离太远 | 靠近一些 |
| 重投影误差大 | 姿态覆盖不足、板子弯 | 重拍,换硬板 |
| MATLAB 与 OpenCV K 不一致 | 矩阵转置习惯不同 | 导出后 undistort 验证 |
| ROS2 不读 yaml | 路径、camera_name 或 file:// 不对 |
用绝对路径,三者同名 |
| 镜像画面标定偏 | 驱动镜像未关 | set_*_mirror false |
| 标定窗口一直黑 | 图像话题未重映射成功 | 检查 --ros-args -r image:=… |
| 订阅不到图 | QoS 不兼容 | 图像订阅用 SENSOR_DATA |
8. 总结与检查清单
记住三句话
- 格距必须实测——
square错 1 mm,尺度全错 - 姿态覆盖比张数更重要——X/Y/Size/Skew 四维都要绿
- 标定结果要用起来验证——直线变直、
camera_info能发布
选型口诀
- 离线验证、双目标定 → MATLAB
- 机器人联调、在线标定 → ROS2 cameracalibrator
- 下游使用 → yaml + camera_info_url 或 cv2.undistort





