1. 视觉引导基础:机器视觉系统组成、坐标系定义与坐标变换
大家好,我是你们的老朋友。今天咱们正式开讲视觉引导定位的第一章。说实话,我入行那会儿,最头疼的就是坐标系转换。明明相机拍到了,机器人就是抓不准。后来才明白,根子就在坐标系没搞透。
这一章,咱们把地基打牢。我会从系统组成讲起,然后一步步拆解四个坐标系,最后聊聊三种坐标变换。嗯,都是干货,咱们慢慢来。
1.1 机器视觉系统组成
一个典型的视觉引导系统,说白了就是「眼睛 + 大脑 + 手脚」的配合。我习惯把它拆成五个部分:
- 光源与镜头:给眼睛打光、调焦距。我见过太多项目因为光源角度不对,导致定位精度直接崩掉。
- 工业相机:核心传感器。CMOS还是CCD?看场景。高速产线我一般选全局快门的CMOS。
- 图像采集卡/接口:把光信号转成数字信号。GigE、USB3.0、Camera Link,各有各的脾气。
- 视觉处理单元:工控机或智能相机。跑算法的地方。
- 执行机构:机器人、运动平台。最终靠它干活。
你想想看,如果光源没选对,再好的算法也白搭。我曾经在一个项目里,因为用了环形光,导致反光件边缘提取全乱套。后来换成背光源,问题秒解。
1.2 四大坐标系定义
这是视觉引导的核心中的核心。我每次培训新人,都会让他们把这四个坐标系刻在脑子里。
1.2.1 世界坐标系 (World Coordinate System)
它是绝对参考系。你可以把它想象成车间里的一个固定点,比如机器人底座中心。所有物体的位置,最终都要换算到这个坐标系下。单位通常是毫米或米。
1.2.2 相机坐标系 (Camera Coordinate System)
以相机光心为原点,Z轴沿光轴方向。这是一个三维坐标系。说白了,相机看到的世界,就是在这个坐标系下的三维点云。
1.2.3 图像坐标系 (Image Coordinate System)
在相机内部,成像平面上的二维坐标系。原点一般在图像中心,单位是毫米。它把三维空间投影到了二维平面上。
1.2.4 像素坐标系 (Pixel Coordinate System)
我们最终看到的图像,就是像素坐标系。原点在左上角,单位是像素(pixel)。注意,它和图像坐标系之间有个缩放和平移关系。
核心关系链:
世界坐标 → 相机坐标 → 图像坐标 → 像素坐标
每一步都对应一个变换矩阵。搞懂了这条链,手眼标定就通了一半。
1.3 坐标变换的数学基础
数学这东西,你把它当成工具就好。我当年学的时候,也是从死记硬背到后来融会贯通。咱们重点讲三种变换。
1.3.1 刚体变换 (Rigid Transformation)
只包含旋转和平移,不改变物体形状。说白了,就是把一个刚体从A位置挪到B位置,它还是它自己。
数学表达:P' = R * P + T
其中R是旋转矩阵,T是平移向量。我建议你用齐次坐标来写,这样旋转和平移可以合并成一个4x4矩阵,计算起来特别方便。
我的小技巧: 实际项目中,刚体变换常用于机器人手眼标定。我曾经用四元数代替欧拉角,避免了万向锁问题,标定一次通过。
1.3.2 仿射变换 (Affine Transformation)
比刚体变换多了一个缩放和剪切。平行线依然保持平行,但角度和长度会变。
数学表达:P' = A * P + T
其中A是2x2矩阵,包含缩放、旋转、剪切。在图像处理中,仿射变换常用于图像校正、模板匹配。
1.3.3 透视变换 (Perspective Transformation)
这是最接近人眼成像的变换。它模拟了「近大远小」的效果。平行线在透视变换下可能相交于一点(灭点)。
数学表达:齐次坐标下,用3x3单应性矩阵H表示。
注意: 透视变换是非线性的。在做视觉引导时,如果相机有倾斜,必须用透视变换来校正。我曾经在一个项目中忽略了这一点,结果定位误差达到了5mm,后来加上透视校正,误差降到了0.2mm。
1.4 知识体系总览
下面这张图,是我自己画的。它把这一章的核心逻辑串起来了。你多看几遍,心里就有谱了。
1.5 实战中的坐标系转换流程
光讲理论不行,咱们来点实际的。下面是一个典型的视觉引导定位流程:
- 相机采集图像:得到像素坐标下的特征点位置 (u, v)。
- 像素转图像坐标:利用相机内参,把 (u, v) 转成 (x, y),单位毫米。
- 图像转相机坐标:加上深度信息(如果有),得到相机坐标系下的三维点 (Xc, Yc, Zc)。
- 相机转世界坐标:利用手眼标定得到的变换矩阵,把点转换到世界坐标系下。
- 发送给机器人:机器人根据世界坐标去抓取。
关键点: 每一步的精度都会累积。我建议你在每个转换环节都做一次验证,不要等到最后才发现误差。
1.6 一个简单的代码示例
下面是用Python + OpenCV实现像素坐标转世界坐标的示例。代码不长,但很实用。
import cv2
import numpy as np
# 假设我们已经标定好了相机内参和手眼矩阵
camera_matrix = np.array([[fx, 0, cx],
[0, fy, cy],
[0, 0, 1]], dtype=np.float32)
dist_coeffs = np.zeros((4,1)) # 假设无畸变
# 手眼标定得到的变换矩阵 (相机到世界)
R_cam2world = np.eye(3)
T_cam2world = np.array([[0.1], [0.2], [0.5]])
def pixel_to_world(u, v, Zc):
# 1. 像素转归一化坐标
x_norm = (u - cx) / fx
y_norm = (v - cy) / fy
# 2. 归一化坐标转相机坐标
Xc = x_norm * Zc
Yc = y_norm * Zc
Zc = Zc
# 3. 相机坐标转世界坐标
P_cam = np.array([[Xc], [Yc], [Zc]])
P_world = R_cam2world @ P_cam + T_cam2world
return P_world.flatten()
# 示例:像素点 (320, 240),深度 Zc = 500mm
world_point = pixel_to_world(320, 240, 500)
print(f"世界坐标: {world_point}")
注意: 实际项目中,Zc通常由深度相机提供,或者通过标定板上的已知尺寸反算。我习惯用棋盘格标定板,既简单又可靠。
1.7 避坑指南
最后,分享几个我踩过的坑:
- 坐标系方向搞反:我曾经把相机坐标系的Z轴方向搞反了,结果机器人往地下抓。后来我养成了一个习惯:每次标定完,先用手动模式验证一下方向。
- 忽略镜头畸变:很多新手直接用原始图像做定位,结果边缘误差很大。记住,一定要先做畸变校正。
- 单位不统一:有人用毫米,有人用米,还有人用像素。我建议统一用毫米,省得换算出错。
好了,这一章就到这里。内容不少,但都是基础中的基础。你把这些搞懂了,后面学手眼标定就会轻松很多。