2. 坐标系基础:世界坐标系、相机坐标系、图像坐标系、像素坐标系的概念与转换关系

做手眼标定,说白了就是搞清楚「机器人的手」和「相机的眼」之间到底怎么配合。但在这之前,有个更基础的东西必须捋清楚——坐标系。

我刚开始接触视觉引导时,就被这四个坐标系绕晕过。明明都是坐标,怎么一会儿毫米一会儿像素?后来踩了几次坑才明白,这四个坐标系就是视觉测量的「语言体系」。你想想看,机器人要抓一个零件,它只认毫米;相机拍到的图像,只认像素。中间怎么换算?全靠坐标系转换。

2.1 四个坐标系,一个都不能少

先给个全景图。视觉测量里,我们通常要打交道的就是这四个坐标系:

坐标系 单位 描述 我常说的比喻
世界坐标系 mm 描述物体在真实世界中的位置 「地图上的经纬度」
相机坐标系 mm 以相机光心为原点,描述物体相对相机的位置 「从相机视角看世界」
图像坐标系 mm 在成像平面上,以物理尺寸描述像点位置 「底片上的实际位置」
像素坐标系 pixel 在图像上,以行列数描述像素位置 「屏幕上的格子坐标」

嗯,这里要注意:世界坐标系和相机坐标系是三维的,图像坐标系和像素坐标系是二维的。从三维到二维,就是「投影」的过程。

核心记忆点:世界坐标系 → 相机坐标系(刚体变换)→ 图像坐标系(透视投影)→ 像素坐标系(离散化)。这条链路就是视觉测量的「翻译管道」。

四个坐标系转换链路 世界坐标系 (Xw, Yw, Zw) 相机坐标系 (Xc, Yc, Zc) 图像坐标系 (x, y) 像素坐标系 (u, v) 刚体变换 R, t 透视投影 f 离散化 dx, dy 关键理解 • 世界→相机:外参矩阵(旋转R + 平移t) • 相机→图像:内参矩阵(焦距f) • 图像→像素:内参矩阵(像元尺寸dx, dy + 主点偏移u₀, v₀)

2.2 世界坐标系与相机坐标系:刚体变换

世界坐标系是我们自己定义的。你可以把原点放在机器人底座,也可以放在工作台角落。相机坐标系呢?原点在相机光心,Z轴沿着光轴指向物体。

从世界坐标到相机坐标,说白了就是一次「旋转+平移」。数学上用一个4×4的齐次矩阵表示:

| Xc |   | R11 R12 R13 tx | | Xw |
| Yc | = | R21 R22 R23 ty | | Yw |
| Zc |   | R31 R32 R33 tz | | Zw |
| 1  |   |  0   0   0   1 | | 1  |

这个矩阵,就是相机的外参矩阵。我习惯把它理解为「相机在世界中的位姿」。你换个相机位置,外参就变了。

我的经验:做手眼标定时,外参矩阵就是我们要标定的核心之一。我曾经在一个项目里,因为机器人末端和相机之间的外参算错了3度,结果抓取精度差了5毫米。后来花了整整两天排查,才发现是旋转矩阵的符号搞反了。

2.3 相机坐标系到图像坐标系:透视投影

这一步是「从三维到二维」的关键。相机坐标系里的一个点 (Xc, Yc, Zc),通过小孔成像模型,投影到成像平面上。

公式很简单:

x = f * Xc / Zc
y = f * Yc / Zc

其中 f 是焦距。注意,这里的 x, y 单位是毫米,不是像素。

你想想看,Zc 在分母上——这就是「近大远小」的数学本质。同一个物体,离相机越近,成像越大。

避坑指南:我曾经在标定一个广角镜头时,发现图像边缘畸变严重,直接用这个公式算出来的坐标偏差很大。后来才意识到,真实镜头不是理想小孔模型,必须先做畸变校正。HALCON里用 change_radial_distortion_cam_par 可以搞定,但前提是你得先标定出畸变参数。

2.4 图像坐标系到像素坐标系:离散化

图像坐标系是连续的物理坐标(毫米),像素坐标系是离散的整数坐标(像素)。转换关系:

u = x / dx + u0
v = y / dy + v0

其中:

  • dx, dy:每个像素在x和y方向的物理尺寸(mm/pixel)
  • u0, v0:主点(光轴与成像平面的交点)在像素坐标系中的位置

说白了,就是把连续的毫米坐标「塞进」离散的像素格子里。dx, dy 越小,每个像素代表的物理尺寸越小,分辨率越高。

把前面两步合并,就得到了完整的相机内参矩阵:

| u |   | f/dx  0    u0 | | Xc |
| v | = |  0   f/dy  v0 | | Yc |
| 1 |   |  0    0    1  | | Zc |

注意,这里省略了 Zc 的除法,实际使用时需要归一化。

2.5 完整转换链路:从世界坐标到像素坐标

把上面三步串起来,就是完整的「世界坐标 → 像素坐标」转换:

  1. 世界坐标 → 相机坐标:乘以相机外参矩阵(R, t)
  2. 相机坐标 → 归一化平面:除以 Zc,得到归一化坐标 (x', y')
  3. 归一化平面 → 像素坐标:乘以相机内参矩阵(fx, fy, u0, v0)

在 HALCON 里,这个链路被封装得很好。你只需要标定出相机内参和外参,然后调用 image_points_to_world_plane 或 world_plane_to_image_points 就能直接转换。

一句话总结:世界坐标系是「真实位置」,相机坐标系是「相对位置」,图像坐标系是「物理像点」,像素坐标系是「数字格子」。四个坐标系通过外参、内参、畸变参数串联起来。搞懂了这条链路,手眼标定你就已经入门了一半。

2.6 坐标系在 HALCON 中的实际应用

我平时做项目,最常用的场景就是「像素坐标转世界坐标」。比如视觉引导机器人抓取,相机拍到零件在像素 (320, 240),你得告诉机器人这个点在世界的哪个位置。

HALCON 里典型的做法:

* 读取标定好的相机内参
read_cam_par('calib_data/cam_param.dat', CamParam)

* 设置世界坐标系平面(比如 Z=0 的工作台面)
set_origin_pose(Pose, 0, 0, 0, 0, 0, 0, PoseOrigin)

* 像素坐标转世界坐标
image_points_to_world_plane(CamParam, PoseOrigin, 
                            [row], [col], 'mm', 
                            Xw, Yw)

这里的关键是 Pose——它包含了相机相对于世界坐标系的位姿。这个 Pose 怎么来的?就是手眼标定要解决的问题。

我的习惯:每次做项目,我都会先画一张坐标系关系图,把世界坐标系、机器人基坐标系、工具坐标系、相机坐标系全部标出来。画清楚了再写代码,能省掉一半的调试时间。

好了,坐标系的基础就讲到这里。记住那条转换链路,后面讲手眼标定原理时,你会反复用到它。