(二)solvepnp 单目三维位姿估计--------利用二维码求解相机世界坐标

本文是通过相机拍摄二维码得到世界坐标和图像坐标点对,求解相机的姿态或目标(二维码)的姿态。

文章转自:https://blog.csdn.net/cocoaqin/article/details/77848588

博主代码:https://github.com/xukeqin/PnP

实现平台:

IDE:visual studio 2013 
使用库:Eigen opencv2.4.9 
文档版本:1.1

(一)solvepnp 单目三维位姿估计--------理论

matlab相机标定获取内参

旋转矩阵到旋转角之间的换算

以下为正文内容:


前言
原理简介
输入参数准备
1 objectPoints特征点世界坐标
2 imagePoints特征点在摄像头下的像素点坐标
3cameraMatrixdistCoeffs内参矩阵和畸变矩阵
相机世界坐标的求解
1求世界坐标中的点在相机坐标系下的坐标
2求相机在世界坐标中的坐标
3效果求解相机在世界坐标系下的坐标点
4求解代码


1.前言


  很多时候,当我们利用单目相机获取图像时,往往只能获取图像中特征物体的像素坐标。而在空间三维点计算时,我们也第一时间会想到深度相机(Kinect、Realsense等)、双目相机等。 
  但是在实际中,我们亦可以通过单目相机来求得空间三维点,但这个往往有一个条件,已知特征物体的三个以上特征点参数。

在本章中,我们将使用二维码作为标志物,通过二维码四个角点,通过P4P求解相机的位姿及空间坐标。 
特别说明:在本章以及未来的几篇博文中都将以右手坐标系为基准,Z轴垂直于二维码平面往里。

 这里写图片描述 
   
               二维码参数:长140mm,宽140mm

在求解时不一定要使用二维码,也可以用其它标志物代替,使用二维码的好处是,通过识别二维码内部的信息可以更精准的匹配以及识别,同时也能更容易的确定四个角点的顺序。 
  

2.原理简介

bool solvePnP(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false, int flags=ITERATIVE )

  objectPoints:特征点的世界坐标,坐标值需为float型,不能为double型,可以为mat类型,也可以直接输入vector 
  imagePoints:特征点在图像中的像素坐标,可以输入mat类型,也可以直接输入vector,注意输入点的顺序要与前面的特征点的世界坐标一一对应 
  cameraMatrix:相机内参矩阵 
  distCoeffs:相机的畸变参数【Mat_(5, 1)】 
  rvec:输出的旋转向量 
  tvec:输出的平移矩阵 
  最后的输入参数有三个可选项: 
  CV_ITERATIVE,默认值,它通过迭代求出重投影误差最小的解作为问题的最优解。 
  CV_P3P则是使用非常经典的Gao的P3P问题求解算法。 
  CV_EPNP使用文章《EPnP: Efficient Perspective-n-Point Camera Pose Estimation》中的方法求解。 
  详见 solvepnp三维位姿估算

  因此我们只要获得 特征点的世界坐标(三维坐标)、2D坐标(像素坐标)、相机内参矩阵、相机畸变参数矩阵 以上四个参数即可以解得相机与标志物之间的外参(R、T),并以此求得相机的世界坐标(以标志物为世界坐标平面,且原点为标志物已知某一点)。

3.输入参数准备

       在上一节中我们已知solvePnP需要输入四个参数,在这一节中,将介绍如何获得这四个参数。

3.1 objectPoints特征点世界坐标

       以特征点所在平面为世界坐标XY平面,并在该平面中确定世界坐标的原点,以我设计的二维码为例,我设定二维码的中心为世界坐标的原点,并按照顺时针方向逐个输入四个角点的世界坐标。

注意,输入一定要按照顺序输入

    m_markerCorners3d.push_back(cv::Point3f(-70.0f, -70.0f, 0));
    m_markerCorners3d.push_back(cv::Point3f(+70.0f, -70.0f, 0));    
    m_markerCorners3d.push_back(cv::Point3f(+70.0f, +70.0f, 0));
    m_markerCorners3d.push_back(cv::Point3f(-70.0f, +70.0f, 0));


3.2 imagePoints特征点在摄像头下的像素点坐标

       在这儿将获得四个特征点对应2D的像素点坐标,而这个过程你可以人为的从图像中逐个点获得,也可以通过二维码检测程序检测出,详见 PnP 单目相机位姿估计(三):二维码四个角点的检测

注意,这儿检测到的四个像素点的输入顺序要和输入的世界坐标的顺序相同

3.3cameraMatrix,distCoeffs内参矩阵和畸变矩阵

      我们这儿采用matlab的标定工具标定,(参考文章:matlab相机标定获取内参)。并形成以下矩阵输入solvePnP

camMatrix = (Mat_<double>(3, 3) << 598.29493, 0, 304.76898, 0, 597.56086, 233.34673, 0, 0, 1);
distCoeff = (Mat_<double>(5, 1) << -0.53572,1.35993,-0.00244,0.00620,0.00000);

4.相机世界坐标的求解

       在上一节中,我们准备好了四个参数,把这四个参数输入solvePnP中,即可获得求得的R旋转矩阵、T平移矩阵,拿到这两个矩阵的值,我们就可以获得许多我们想要的空间信息啦~

已知 Pc=R*Po+T 
定义Pc为相机坐标系的点值,Po为世界坐标系的点值,R、T为世界坐标系和相机坐标系的相对外参。

而我们利用solvePnP解得的R和T正是相机坐标系和世界坐标系的相对外参。

注意,solvePnP返回的raux是旋转向量,可通过罗德里格斯变换成旋转矩阵R。

4.1求世界坐标中的点在相机坐标系下的坐标

        当Po=[0;0;0;]时,Pc=T即世界坐标原点(二维码的中心)在相机坐标系下的坐标就为T 
同样的道理,当我们已知一点的世界坐标Po,我们就可以求得Pc

4.2求相机在世界坐标中的坐标

       因为相机在相机坐标中相当于Pc=[0;0;0],故Po=-R’*T 即可解得相机在世界坐标系下的坐标 
其中R’为R的逆或者转置矩阵(R是正交矩阵,R的逆等于R的转置)

4.3效果(求解相机在世界坐标系下的坐标点):

 

这里写图片描述
4.4求解代码

        Marker& m = detectedMarkers[i];
        cv::Mat Rvec;
        cv::Mat_<float> Tvec;
        cv::Mat raux, taux;

        cv::solvePnP(m_markerCorners3d, m.points, camMatrix, distCoeff, raux, taux, false, CV_P3P);

        raux.convertTo(Rvec, CV_32F);    //旋转向量
        taux.convertTo(Tvec, CV_32F);   //平移向量

        cv::Mat_<float> rotMat(3, 3);
        cv::Rodrigues(Rvec, rotMat);  //由于solvePnP返回的是旋转向量,故用罗德里格斯变换变成旋转矩阵

        //格式转换
        Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> R_n;
        Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> T_n;
        cv2eigen(rotMat, R_n);
        cv2eigen(Tvec, T_n);
        Eigen::Vector3f P_oc;

        P_oc = -R_n.inverse()*T_n;

 

### 相机位姿估计的技术与实现 相机位姿估计是指通过已知的三维空间点及其对应的维图像投影点来计算相机相对于世界坐标系的姿态(旋转和平移)。以下是基于 OpenCV 和 Python 的相机位姿估计技术及相关算法原理。 #### 1. 基于棋盘格的相机标定 相机标定是位姿估计的基础,通常使用棋盘格作为校准板。棋盘格具有精确的几何结构,便于提取角点并建立物-像点对应关系。具体过程如下: - **获取角点**:使用 `cv2.findChessboardCorners` 函数检测棋盘格上的角点位置。 - **优化角点精度**:调用 `cv2.cornerSubPix` 提高亚像素级精度。 - **标定相机**:通过 `cv2.calibrateCamera` 计算内参矩阵和畸变系数[^1]。 ```python import cv2 import numpy as np # 棋盘格尺寸 (宽度, 高度) chessboard_size = (7, 6) # 创建对象点数组 objp = np.zeros((chessboard_size[0]*chessboard_size[1], 3), dtype=np.float32) objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2) # 存储物体点和图像点 object_points = [] image_points = [] for img_path in images: img = cv2.imread(img_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None) if ret: object_points.append(objp) refined_corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) image_points.append(refined_corners) ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, gray.shape[::-1], None, None) ``` --- #### 2. 利用 PnP 方法求解位姿 在完成相机标定后,可以使用 PnP(Perspective-n-Point)方法估算相机姿态。该方法的核心是从一组已知的 3D 点与其对应的 2D 投影点中推导出相机的外参(R 和 T),即旋转矩阵和平移向量。 ##### (1)输入数据准备 需提供以下三个要素: - **world_points**:目标物体上若干特征点的世界坐标(单位通常为毫米或米)。 - **image_points**:这些特征点在当前帧中的图像平面坐标(像素单位)。 - **camera_matrix** 和 **dist_coeffs**:由标定获得的相机内参矩阵及畸变参数。 ##### (2)执行 solvePnP 调用 OpenCV 中的 `cv2.solvePnP` 函数即可得到旋转向量 (`rvec`) 及平移向量 (`tvec`)。 ```python success, rvec, tvec = cv2.solvePnP(world_points, image_points, camera_matrix, dist_coeffs) ``` 如果需要将旋转向量转换成标准形式的旋转矩阵,则可借助 Rodrigues 转换函数: ```python rotation_matrix, _ = cv2.Rodrigues(rvec) print("Rotation Matrix:\n", rotation_matrix) print("Translation Vector:\n", tvec) ``` 上述操作实现了从给定点集到相机姿态的具体映射[^2][^3]。 --- #### 3. ArUco 编码标记辅助定位 除了传统的棋盘格外,还可以采用 ArUco 标记简化流程。ArUco 是一种特殊的二维码样式图案集合,能够被快速识别并与之关联唯一 ID。其优势在于无需手动指定哪些区域属于感兴趣部分——系统会自动解析每张图片内的所有可见标签,并返回它们各自的顶点座标列表以及相应的身份编号。 一旦确定了某个特定 ARUCO marker 所处的确切方位之后,就可以直接应用之前提到过的 SolvePNP 方程组来进行进一步处理了: ```python aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_6X6_250) parameters = cv2.aruco.DetectorParameters_create() corners, ids, rejectedImgPoints = cv2.aruco.detectMarkers(image, aruco_dict, parameters=parameters) if ids is not None and len(ids) > 0: for i in range(len(ids)): corner = corners[i].reshape(4, 2).astype(np.int32) # 定义四个角点的真实世界坐标 real_world_coords = np.array([ [-marker_length / 2, marker_length / 2, 0], [ marker_length / 2, marker_length / 2, 0], [ marker_length / 2,-marker_length / 2, 0], [-marker_length / 2,-marker_length / 2, 0]], dtype=np.float32) _, rvec, tvec = cv2.solvePnP(real_world_coords, corner, camera_matrix, dist_coeffs) ``` 此段脚本展示了如何结合 ArUco 库寻找预定义模板实例的位置信息进而完成整个系统的闭环控制逻辑构建工作. --- ### 总结 综上所述,无论是依赖经典模式还是现代手段,在实际项目开发当中都可以灵活选用不同工具链组合起来达成最终目的—准确捕捉实时动态变化下的摄像头指向角度方向等相关属性值.
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值