基于OpenCV三角测量:寻找离相机最近的物体
嘿,这个需求其实挺好实现的,结合你已经完成的相机标定、ArUco板姿态检测这些基础,咱们一步步拆解就能搞定:
核心原理铺垫
首先得明确:你通过ArUco获取的rotation vector(rv)和translation vector(tv),确实是ArUco板相对于相机坐标系的姿态——相机坐标系的原点在相机光心,Z轴指向相机前方,X轴向右,Y轴向下(符合OpenCV的坐标系定义)。
你的黄色圆是基于板的相对坐标定义的,意味着这些圆的3D坐标是在ArUco板的局部坐标系里的,所以咱们要做的就是把这些局部坐标转换到相机坐标系,然后计算每个点到相机光心的距离,找出最小的那个就行。
具体步骤
1. 将物体的局部3D坐标转换到相机坐标系
首先需要把ArUco的旋转向量转换成旋转矩阵(因为向量没法直接做坐标变换),用OpenCV的cv::Rodrigues()函数就能完成:
cv::Mat rotationMatrix; cv::Rodrigues(rv, rotationMatrix); // rv是ArUco返回的旋转向量,输出3x3的旋转矩阵
然后,对于每个黄色圆的局部坐标点P_local(比如板中心为原点的(0,0,10),单位和板的尺寸一致),转换到相机坐标系的公式是:P_cam = rotationMatrix * P_local + tv
这里要注意数据格式:P_local需要转成3x1的列向量矩阵,tv本身就是OpenCV返回的3x1平移向量,直接参与运算即可。
2. 计算每个物体到相机的距离
在相机坐标系下,点到相机光心(原点)的距离就是这个点的欧几里得范数,用OpenCV的cv::norm()函数可以直接计算,省得自己写平方根:
double distance = cv::norm(P_cam);
注意:如果所有物体都在同一条直线上,用Z坐标的大小也能判断远近(Z值越小离相机越近),但如果物体分布在不同位置,必须计算欧几里得距离才准确。
3. 筛选出离相机最近的物体
遍历所有黄色圆的坐标,记录最小距离对应的点即可。这里给你一个完整的代码片段参考:
// 假设你已经有: // - 相机内参cameraMatrix、畸变系数distCoeffs // - ArUco板的旋转向量rv、平移向量tv // - 黄色圆在板局部坐标系的点集合vector<cv::Point3f> yellowMarkerLocalPoints cv::Mat rotationMatrix; cv::Rodrigues(rv, rotationMatrix); double minDistance = INFINITY; cv::Point3f closestPointCam; int closestMarkerIndex = -1; for (int i = 0; i < yellowMarkerLocalPoints.size(); ++i) { // 把局部坐标转成3x1矩阵 cv::Mat localPointMat = (cv::Mat_<double>(3, 1) << yellowMarkerLocalPoints[i].x, yellowMarkerLocalPoints[i].y, yellowMarkerLocalPoints[i].z); // 转换到相机坐标系 cv::Mat camPointMat = rotationMatrix * localPointMat + tv; // 计算距离 double currentDistance = cv::norm(camPointMat); // 更新最近点 if (currentDistance < minDistance) { minDistance = currentDistance; closestPointCam = cv::Point3f( camPointMat.at<double>(0), camPointMat.at<double>(1), camPointMat.at<double>(2) ); closestMarkerIndex = i; } } // 可视化最近点(可选) if (closestMarkerIndex != -1) { cv::Point2f closestPointImg; // 把相机坐标系的点投影到图像上 cv::projectPoints(closestPointCam, cv::Mat::zeros(3,1,CV_64F), // 旋转向量为0(相机自身) cv::Mat::zeros(3,1,CV_64F), // 平移向量为0 cameraMatrix, distCoeffs, closestPointImg); // 用红圈标记最近点 cv::circle(frame, closestPointImg, 8, cv::Scalar(0, 0, 255), -1); std::cout << "最近的黄色标记是第" << closestMarkerIndex << "个,距离:" << minDistance << "单位" << std::endl; }
注意事项
- 单位一致性:确保ArUco板的尺寸单位(比如厘米、米)和你定义黄色圆局部坐标的单位完全一致,这样计算出的距离单位才是准确的。
- 姿态准确性:如果ArUco板的姿态检测有误差,会直接影响坐标转换的结果,所以尽量保证板在相机视野内的清晰度和完整度。
- 坐标系验证:可以先投影几个已知距离的点,验证转换后的距离是否符合预期,确保整个流程没有问题。
内容的提问来源于stack exchange,提问作者Fedir Tsapana

