2. 坐标系基础:世界坐标系、相机坐标系、图像坐标系、像素坐标系的概念与转换关系
做手眼标定,说白了就是搞清楚「机器人的手」和「相机的眼」之间到底怎么配合。但在这之前,有个更基础的东西必须捋清楚——坐标系。
我刚开始接触视觉引导时,就被这四个坐标系绕晕过。明明都是坐标,怎么一会儿毫米一会儿像素?后来踩了几次坑才明白,这四个坐标系就是视觉测量的「语言体系」。你想想看,机器人要抓一个零件,它只认毫米;相机拍到的图像,只认像素。中间怎么换算?全靠坐标系转换。
2.1 四个坐标系,一个都不能少
先给个全景图。视觉测量里,我们通常要打交道的就是这四个坐标系:
| 坐标系 | 单位 | 描述 | 我常说的比喻 |
|---|---|---|---|
| 世界坐标系 | mm | 描述物体在真实世界中的位置 | 「地图上的经纬度」 |
| 相机坐标系 | mm | 以相机光心为原点,描述物体相对相机的位置 | 「从相机视角看世界」 |
| 图像坐标系 | mm | 在成像平面上,以物理尺寸描述像点位置 | 「底片上的实际位置」 |
| 像素坐标系 | pixel | 在图像上,以行列数描述像素位置 | 「屏幕上的格子坐标」 |
嗯,这里要注意:世界坐标系和相机坐标系是三维的,图像坐标系和像素坐标系是二维的。从三维到二维,就是「投影」的过程。
核心记忆点:世界坐标系 → 相机坐标系(刚体变换)→ 图像坐标系(透视投影)→ 像素坐标系(离散化)。这条链路就是视觉测量的「翻译管道」。
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 完整转换链路:从世界坐标到像素坐标
把上面三步串起来,就是完整的「世界坐标 → 像素坐标」转换:
- 世界坐标 → 相机坐标:乘以相机外参矩阵(R, t)
- 相机坐标 → 归一化平面:除以 Zc,得到归一化坐标 (x', y')
- 归一化平面 → 像素坐标:乘以相机内参矩阵(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 怎么来的?就是手眼标定要解决的问题。
我的习惯:每次做项目,我都会先画一张坐标系关系图,把世界坐标系、机器人基坐标系、工具坐标系、相机坐标系全部标出来。画清楚了再写代码,能省掉一半的调试时间。
好了,坐标系的基础就讲到这里。记住那条转换链路,后面讲手眼标定原理时,你会反复用到它。