1. 图像坐标轴:被大多数人忽略的“第一性原理”
1.1 为什么在具身智能里要单独讲坐标轴
做具身智能项目时间长了,我发现一个很有意思的现象:很多新手拿到OpenCV第一件事就是跑人脸识别、跑YOLO、跑findContours,觉得这些才是“干货”。但真到了把视觉结果交给机械臂去执行抓取、导航、避障的时候,问题就全冒出来了——为什么检测框的坐标画出来是歪的?为什么我明明框住了目标,机械臂却抓到了旁边的位置?为什么深度图对齐之后点云错位?
这些问题的根源,往往都可以追溯到一个最基础的概念:图像坐标轴。
在具身智能的完整链路里,图像坐标轴不只是一个学术概念,它是连接“像素”和“物理世界”的第一座桥。视觉感知系统输出的每一个检测框、每一条轮廓、每一组关键点坐标,本质上都是在图像坐标系下描述的。而机械臂、移动底盘、灵巧手这些执行机构,它们工作在自己的坐标系下。从“看”到“动”中间的每一次坐标变换,起点都是图像坐标系。起点错了,后面全都白搭。
我见过不少从业三五年的工程师,写代码很溜,但问起图像坐标系的原点在哪里、x轴和y轴各自指向什么方向、行列号和Point(x,y)之间到底什么关系,还是会愣一下。这不是基础不基础的问题,这是会不会在真实项目里踩坑的问题。
1.2 OpenCV坐标系的铁律:原点、方向和行列
OpenCV的坐标系约定非常固定,几乎没有什么例外:原点在图像的左上角,x轴水平向右,y轴垂直向下。这个约定从OpenCV 1.0时代一直延续到现在,Python接口和C++接口完全一致。
这里最反直觉的地方在于y轴方向。我们从小在数学课上学的直角坐标系,y轴是向上的;但图像坐标系的y轴是向下的。因为这背后对应的是图像在内存中的存储方式——图像数据本质上是一个二维数组,第一维是行(row),第二维是列(col)。数组的行索引从小到大,对应的就是屏幕上从上到下的方向,所以y轴向下是自然而然的结果。
还有一个容易被忽略的细节:访问像素时的行列顺序和Point结构的参数顺序是相反的。写img.at<uchar>(row, col)的时候,第一个参数是行号,对应y坐标;第二个参数是列号,对应x坐标。而写Point(x, y)的时候,第一个参数是x坐标,对应列号;第二个参数是y坐标,对应行号。
这个反转太容易出错了。我见过有人写Mat src(480, 640, CV_8UC3)创建了一张高480、宽640的图像,然后拿Point(300, 500)去访问,结果越界崩溃。原因就是Point(300, 500)对应的行列号是row=500、col=300,而图像只有480行,row=500早就超出了范围。这种错误在调试的时候特别隐蔽,因为光看代码逻辑完全没毛病,只有运行到边界处才崩。
1.3 像素坐标与行列号的映射陷阱
再展开一点说,图像在OpenCV里的数据结构可以理解为一个矩阵。对于单通道灰度图,Mat的尺寸是(rows, cols),也就是(height, width)。对于多通道彩色图,比如BGR三通道,每个像素位置实际上存储了三个值,但行列的组织方式不变。
很多从别的框架转过来的开发者会在这里犯迷糊。比如在深度学习框架PyTorch里,图像张量的形状通常是(C, H, W),也就是通道在前,高度和宽度在后。但在OpenCV里,是(H, W, C)。当你把OpenCV读出来的图像直接丢给PyTorch模型之前,必须做维度重排。这个过程中如果坐标系意识不清晰,很容易把H和W搞混,最后模型输入全乱套。
更隐蔽的是ROI操作的坑。cv::Rect(x, y, w, h)这个结构里,x是左上角顶点的列坐标,y是左上角顶点的行坐标,w是宽度(列方向),h是高度(行方向)。做图像裁剪的时候,src(rect)得到的结果是一个宽w、高h的子图像。但如果你把Rect的x和y写反了,比如把行号当x、列号当y,裁剪出来的ROI就会跑到完全错误的位置,而且很多时候不会报错,只是效果不对。这种“不报错的错”在项目调试里最消耗时间。
图像放缩、旋转这些几何操作同样依赖坐标系理解。cv::resize的dsize参数是(width, height),而很多人的直觉是先写高度再写宽度。写反了的结果就是图像被非等比缩放,目标变形,检测算法精度断崖式下降。这些看起来都是“小问题”,但在具身智能的复杂系统里,每一个小问题都会累积成大问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 坐标轴在具身智能“看-想-动”链路中的实际位置
2.1 从像素坐标到相机坐标:先搞懂图像坐标才能谈标定
具身智能系统里,视觉模块的任务通常不是“看懂图像”就结束了,最终要输出可供执行机构使用的空间位置信息。这就涉及到图像坐标系向相机坐标系、再向世界坐标系的转换。
图像坐标系描述的是“目标在画面中的哪里”,单位是像素。相机坐标系描述的是“目标相对于相机光心的哪里”,单位是毫米或米。两者之间的桥梁是相机内参矩阵:
code复制| x_cam | | fx 0 cx | | u |
| y_cam | = | 0 fy cy | * | v |
| z_cam | | 0 0 1 | | 1 |
其中(u, v)是像素坐标,(cx, cy)是光心在像素坐标系中的位置,fx和fy是焦距的像素表达。这个公式看起来很简洁,但实际推导过程中处处都是坐标系陷阱。比如u对应的是列方向,v对应的是行方向,它们直接映射到图像坐标系的x和y。如果在这里把u和v搞反,算出来的三维点会直接镜像错位。
在做深度相机配准的时候,这个问题尤其致命。RGB图像和深度图像对齐之后,每个像素位置同时有颜色和深度值。要从深度值恢复三维坐标,用户就得用上面这个公式做反投影。此时如果坐标系理解不到位,点云生成出来就是错乱的,机械臂根本没法用。
2.2 ROI提取与目标定位:坐标系意识的第一次实战
在具身智能的感知流水线里,目标检测过后通常跟着ROI提取。比如识别到桌面上有一个杯子,检测模型输出一个边界框(x, y, w, h),接下来要从原始图像中裁剪出杯子区域做进一步处理,或者把边界框的中心点作为抓取候选点。
这个环节是坐标系意识的第一场实战。边界框的(x, y)是左上角顶点在图像坐标系中的坐标,center_x = x + w / 2,center_y = y + h / 2算出来的是目标中心的像素位置。如果把这个中心位置直接交给机械臂去抓取,那误差会非常大,因为图像坐标系的原点在图像的左上角,而
