相机标定实战:内参、畸变矫正一步步做

相机标定实战:内参、畸变矫正一步步做


目录

  1. 为什么需要相机标定
  2. 原理速览:坐标系、内参、畸变
  3. 准备工作:棋盘格与拍摄
  4. MATLAB 标定实战
  5. ROS2 标定实战
  6. ROS2 中如何应用标定结果
  7. 常见踩坑
  8. 总结与检查清单

本文从相机标定的目的与原理出发,用棋盘格完成 内参矩阵 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 添加图像

  1. 点击 Add Images,选择棋盘格图片文件夹
  2. 设置 Square size(格子边长,与实测一致,如 25 mm)
  3. 确认工具箱识别到的 可用张数

4.3 执行标定

  1. 点击 Calibrate,等待优化完成
  2. 查看 Reprojection Error(平均重投影误差,一般 < 0.5 pixel 较理想)
  3. 点击 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 验证清单

  1. 含直线的场景(门框、瓷砖缝)矫正后应更直
  2. fx ≈ fy,主点 (cx,cy) 接近图像中心
  3. 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. 总结与检查清单

记住三句话

  1. 格距必须实测——square 错 1 mm,尺度全错
  2. 姿态覆盖比张数更重要——X/Y/Size/Skew 四维都要绿
  3. 标定结果要用起来验证——直线变直、camera_info 能发布

选型口诀

  • 离线验证、双目标定 → MATLAB
  • 机器人联调、在线标定 → ROS2 cameracalibrator
  • 下游使用 → yaml + camera_info_url 或 cv2.undistort