普通摄像头如何测出二维码的三维坐标?OpenCV 相机标定与 solvePnP 实战路线
摘要
普通摄像头拍到的只是一张二维图像,但在工业视觉、机器人定位、AGV 导航、机械臂抓取等场景中,我们真正关心的往往不是二维码在图像中的位置,而是它在真实空间中的位置:距离相机多远?相对于相机偏左还是偏右?二维码所在平面有没有倾斜?本文从工程实战角度出发,梳理普通摄像头通过二维码计算三维空间坐标的完整技术路线,重点讲清楚相机标定、二维码角点、真实坐标系、solvePnP 位姿估计之间的关系,为后续 OpenCV 相机标定与二维码空间定位专题打下基础。
关键词
OpenCV、相机标定、二维码识别、solvePnP、三维坐标、位姿估计、工业视觉、机器人视觉
1. 为什么要做这个专题?
在很多工业视觉项目中,摄像头的作用并不只是"拍一张图片"或者"识别一个二维码"。
更常见的需求其实是:
- 识别二维码或 DataMatrix 码;
- 判断二维码在图像中的位置;
- 计算二维码距离相机有多远;
- 判断二维码相对于相机偏左还是偏右;
- 判断二维码所在平面有没有发生倾斜;
- 把二维码作为定位基准,辅助 AGV、机械臂或产线设备完成定位。
也就是说,二维码识别只是第一步,真正有工程价值的是:
从图像中的二维码,计算它在真实空间中的位置和姿态。
这就引出了一个很关键的问题:
普通摄像头明明只能拍到二维图像,为什么还能计算出二维码的三维坐标?
这就是本专题要解决的核心问题。
2. 摄像头看到的不是空间,而是投影
摄像头拍到的图像,本质上是三维世界投影到二维图像平面的结果。
真实世界中,一个二维码有实际尺寸,也有具体空间位置;但是摄像头拍到它之后,只能得到一张二维图像。
整个过程可以简单理解为:
text
真实世界中的二维码
↓
相机成像
↓
图像中的二维码区域
↓
二维码四个角点的像素坐标
比如在图像中,一个二维码的四个角点可能是:
text
左上角:u = 320, v = 180
右上角:u = 450, v = 185
右下角:u = 448, v = 315
左下角:u = 318, v = 310
这里的 u 和 v 只是像素坐标。
它只能说明这个角点在图片上的位置,并不能直接说明这个点在真实世界中距离相机多少毫米。
所以,仅仅有图像像素坐标是不够的。
3. 计算二维码空间坐标需要哪些信息?
二维码空间定位并不是"识别出二维码内容就能算坐标"。
真正有用的是二维码的四个角点。
要想从图像中的二维码计算出空间坐标,至少需要三类信息:
- 相机自身参数;
- 二维码真实尺寸;
- 二维码在图像中的四个角点坐标。
下面分别来看。
4. 第一类信息:相机自身参数
不同摄像头的成像方式并不一样。
即使拍摄同一个二维码,换一个摄像头,图像中的大小和位置也可能发生变化。
因此,在计算三维坐标之前,需要先知道相机本身的参数,例如:
text
fx、fy:焦距在像素坐标下的表示
cx、cy:图像主点坐标
dist:镜头畸变参数
这些参数通常通过相机标定得到。
相机标定的目的,不是为了得到一堆看不懂的数字,而是为了建立一个可靠的成像模型。
简单来说,就是回答一个问题:
真实世界中的三维点,是怎样投影到图像二维像素坐标上的?
如果没有相机内参和畸变参数,后面的空间坐标计算就没有可靠基础。
5. 第二类信息:二维码真实尺寸
如果二维码的实际边长是 50 mm,那么我们就可以人为建立一个二维码坐标系。
常见做法是:
- 将二维码中心作为坐标原点;
- 将二维码所在平面设置为
Z = 0; - 二维码四个角点按照实际尺寸定义三维坐标。
假设二维码边长为 L,那么四个角点的真实三维坐标可以定义为:
python
object_points = np.array([
[-L / 2, L / 2, 0],
[ L / 2, L / 2, 0],
[ L / 2, -L / 2, 0],
[-L / 2, -L / 2, 0]
], dtype=np.float32)
如果二维码实际边长是 50 mm,那么:
python
L = 50.0
这时计算出来的平移向量单位也是毫米。
这里有一个很容易被忽略的细节:
代码中的二维码尺寸必须和真实打印出来的二维码尺寸一致。
如果实际二维码边长是 48 mm,但代码中写成了 50 mm,那么最终计算出来的距离也会出现系统性偏差。
6. 第三类信息:二维码在图像中的四个角点
OpenCV 可以检测二维码,并返回二维码四个角点在图像中的像素坐标。
常用代码如下:
python
detector = cv2.QRCodeDetector()
data, points, _ = detector.detectAndDecode(image)
其中:
python
data
表示二维码识别出来的内容。
python
points
表示二维码四个角点在图像中的坐标。
对于空间定位来说,二维码内容本身并不是最关键的,真正关键的是四个角点。
因为 solvePnP 需要的就是:
text
真实世界中的三维点
+
图像中的二维点
也就是说,我们需要建立这样一组对应关系:
text
二维码左上角三维坐标 → 图像左上角像素坐标
二维码右上角三维坐标 → 图像右上角像素坐标
二维码右下角三维坐标 → 图像右下角像素坐标
二维码左下角三维坐标 → 图像左下角像素坐标
角点顺序一旦对应错误,求出来的位姿就可能完全不对。
7. 空间定位的核心:二维点和三维点建立对应关系
到这里,我们手里已经有两组点。
第一组是二维码真实世界中的三维点:
text
左上角:(-L/2, L/2, 0)
右上角:( L/2, L/2, 0)
右下角:( L/2, -L/2, 0)
左下角:(-L/2, -L/2, 0)
第二组是二维码在图像中的二维像素点:
text
左上角:(u1, v1)
右上角:(u2, v2)
右下角:(u3, v3)
左下角:(u4, v4)
这时候问题就变成了:
已知二维码四个角点在真实空间中的三维坐标,也知道它们投影到图像中的二维像素坐标,能不能反推出二维码相对于相机的位置和姿态?
答案是可以。
这就是 PnP 问题。
在 OpenCV 中,对应的核心函数就是:
python
cv2.solvePnP()
8. solvePnP 到底求的是什么?
solvePnP() 的作用可以简单理解为:
根据一组 3D 点和它们对应的 2D 图像点,估计物体坐标系相对于相机坐标系的旋转和平移关系。
核心代码如下:
python
success, rvec, tvec = cv2.solvePnP(
object_points,
image_points,
camera_matrix,
dist_coeffs
)
其中:
text
object_points:二维码四个角点在真实世界中的三维坐标
image_points:二维码四个角点在图像中的二维像素坐标
camera_matrix:相机内参矩阵
dist_coeffs:相机畸变参数
rvec:旋转向量
tvec:平移向量
这里最容易被关注的是 tvec。
如果我们把二维码中心设置为二维码坐标系原点,那么 tvec 就可以理解为:
二维码中心点在相机坐标系下的位置。
例如输出结果为:
text
X = 12.5 mm
Y = -8.2 mm
Z = 460.3 mm
可以大致理解为:
text
二维码中心相对于相机向右偏移 12.5 mm;
向下偏移 8.2 mm;
距离相机约 460.3 mm。
不过这里要注意一点:
tvec的具体物理含义,取决于你如何建立二维码的真实坐标系。
如果二维码坐标系原点放在中心,那么 tvec 表示二维码中心的位置。
如果二维码坐标系原点放在左上角,那么 tvec 表示左上角这个点的位置。
所以,二维码坐标系怎么建立,是后续必须单独讲清楚的内容。
9. 完整项目流程是什么?
本专题最终要实现的是一个完整的 OpenCV 小项目。
整体流程如下:
text
准备棋盘格标定板
↓
采集多张棋盘格图片
↓
OpenCV 相机标定
↓
得到相机内参和畸变参数
↓
打开摄像头实时采集图像
↓
检测二维码
↓
提取二维码四个角点
↓
建立二维码真实三维坐标
↓
调用 solvePnP 求解位姿
↓
输出二维码相对于相机的 X、Y、Z 坐标
↓
绘制坐标轴并显示实时结果
最终效果类似:
text
QRCode: target_001
X = 15.2 mm
Y = -6.8 mm
Z = 482.6 mm
Yaw = 2.4°
Pitch = -1.7°
Roll = 0.9°
这时候,二维码就不只是一个"被识别出来的码",而是一个可以提供空间位置参考的视觉定位标志。
10. 为什么这个方向在工业视觉中很常见?
二维码或 DataMatrix 码在工业现场中非常常见。
它们不仅可以存储产品信息,还可以作为视觉定位目标。
例如:
text
AGV 到达工位后,通过二维码判断自身位置;
机械臂抓取前,通过二维码估计工件姿态;
工业相机检测产品时,通过码面位置辅助定位;
产线设备通过 DataMatrix 码确认工件身份和空间偏移;
多个二维码可以作为视觉标定或空间校准的参考点。
相比复杂的三维传感器,普通相机加二维码的方案成本较低,部署方便,实现难度适中,因此在很多工程场景中都有实际价值。
但是它也有明显问题:
text
相机标定不准,坐标会偏;
二维码角点检测不准,Z 值会跳;
二维码尺寸写错,距离会整体偏差;
二维码太小,位姿估计会不稳定;
镜头畸变严重,图像边缘误差会变大。
所以这个专题后面不仅会讲"怎么实现",还会讲"为什么不准"和"怎么优化"。
11. 本专题后续内容安排
后面会按照工程项目的方式,一步一步拆开讲。
专题路线如下:
text
第 1 篇:普通摄像头如何测出二维码三维坐标?OpenCV 相机标定与 solvePnP 实战路线
第 2 篇:为什么相机需要标定?从像素坐标到真实空间坐标的关键一步
第 3 篇:OpenCV 棋盘格相机标定实战:获取内参、畸变参数和重投影误差
第 4 篇:相机标定结果怎么看?fx、fy、cx、cy 和畸变系数到底代表什么
第 5 篇:图像去畸变实战:为什么二维码在图像边缘时测距更容易出错
第 6 篇:OpenCV 二维码检测:识别内容不是重点,四个角点才是空间定位的关键
第 7 篇:二维码真实坐标系怎么建?角点顺序、边长单位和坐标原点一次讲清楚
第 8 篇:OpenCV solvePnP 实战:用二维码四个角点求相机和二维码的相对位姿
第 9 篇:tvec 到底是什么?二维码中心在相机坐标系下的 X、Y、Z 坐标如何理解
第 10 篇:二维码姿态角怎么计算?旋转向量、旋转矩阵和欧拉角转换
第 11 篇:实时项目实战:摄像头检测二维码并动态显示三维坐标和姿态角
第 12 篇:为什么二维码测距不准?相机标定、角点误差、尺寸误差和畸变误差分析
第 13 篇:从二维码到 DataMatrix:工业视觉中码面定位的扩展思路
第 14 篇:工业落地总结:如何把 OpenCV 二维码空间定位用于 AGV、机械臂和产线检测
这个专题不会单纯写成 OpenCV API 说明,而是围绕一个完整目标展开:
用普通摄像头实现二维码空间定位。
12. 初学者先记住这一句话
如果你刚开始接触相机标定和空间定位,不需要一上来就记住所有公式。
先记住下面这句话:
二维码空间定位的本质,是用二维码四个角点的真实三维坐标和图像二维坐标,结合相机标定参数,求解二维码坐标系相对于相机坐标系的位置和姿态。
后面的相机标定、畸变校正、角点检测、solvePnP,其实都是围绕这句话展开的。
13. 小结
本文作为专题开篇,主要讲清楚了一个问题:
普通摄像头为什么可以通过二维码计算三维坐标?
核心思路可以概括为:
text
已知二维码真实尺寸
↓
建立二维码四个角点的三维坐标
↓
检测图像中二维码四个角点的二维坐标
↓
结合相机标定参数
↓
使用 solvePnP 求解二维码相对于相机的位姿
↓
得到二维码在相机坐标系下的 X、Y、Z 坐标
下一篇将正式进入相机标定部分,重点讲清楚:
为什么相机需要标定?相机内参到底在空间定位中起什么作用?
只有理解了相机标定,后面的二维码三维定位才不会只是"复制代码能跑",而是真正知道每一个坐标是怎么来的。
text
普通摄像头
↓
拍摄二维码
↓
提取四个角点
↓
相机内参 + 畸变参数
↓
solvePnP
↓
X / Y / Z 坐标 + 姿态角