cv2.solvePnP转相机位姿:四元数与Unity不匹配问题咨询
OpenCV solvePnP四元数与Unity相机四元数差异原因分析
问题背景
通过OpenCV的solvePnP计算得到旋转向量rvec和平移向量tvec,重投影损失为0,说明姿态计算完全正确,但转换得到的四元数与Unity相机预期的四元数存在y、w分量的数值/符号差异,平移量tvec则完全匹配。
核心实现代码如下:
import cv2 import numpy as np from scipy.spatial.transform import Rotation # 2D图像点 image_points_2D = [[1331.284,610],[1473.253,468.6115], [1191.823,748.8911],[1473.253,751.3885],[1191.823,471.1088],[1245.413,347.5563],[1501.027,477.5027],[1487.591,610]] # 3D世界点 figure_points_3D =[[ 0. ,0., -0.5 ], [ 0.5, 0.5, -0.5 ],[-0.5,-0.5,-0.5 ],[ 0.5 , -0.5, -0.5 ],[-0.5 , 0.5, -0.5 ],[-0.45, 1. , 0.4 ],[ 0.5 , 0.5, 0.5 ],[ 0.5 , 0. , 0. ]] # 相机参数 distortion_coeffs = np.zeros((4,1)) matrix_camera = np.array([[flx, 0, center_x], [0, fly, center_y], [0, 0, 1 ]], dtype = "double") # PnP求解姿态 success, vector_rotation, vector_translation = cv2.solvePnP(figure_points_3D, image_points_2D, matrix_camera, distortion_coeffs, flags=cv2.SOLVEPNP_ITERATIVE) # 计算重投影损失 reprojected_points, _ = cv2.projectPoints(figure_points_3D, vector_rotation, vector_translation, matrix_camera, distortion_coeffs) loss = np.mean(np.abs(image_points_2D - reprojected_points.reshape(np.array(image_points_2D).shape))) print("loss", loss) # 转换为四元数 rotation = Rotation.from_rotvec(vector_rotation.flatten()) print("quat (x,y,z,w): ", rotation.as_quat()) print("translation", vector_translation.flatten())
差异核心原因
1. 坐标系约定冲突
- OpenCV采用右手相机坐标系:X轴向右,Y轴向下,Z轴向前(指向场景物体)。
- Unity采用左手相机坐标系:X轴向右,Y轴向上,Z轴向后(背离场景物体)。
Y轴方向的直接翻转,会导致旋转分量的符号产生变化。
2. 姿态变换的定义完全相反
- OpenCV的
solvePnP计算的是世界坐标系到相机坐标系的变换:描述的是物体相对于相机的姿态,即相机“观测”物体的视角变换。 - Unity中相机的四元数表示的是相机在世界坐标系中的姿态:描述的是相机自身相对于世界的旋转,是上述变换的逆变换。
3. 四元数的互补旋转特性
四元数存在一个特性:绕同一轴旋转θ和旋转-θ(或θ+360°)可以通过分量的数值/符号互补来表示。你得到的两个四元数(0, -0.991, 0, 0.131)和(0, -0.131, 0, 0.991),本质是绕Y轴旋转角度互补的结果,正好对应OpenCV与Unity姿态变换的逆关系。
修正方法
要将OpenCV得到的四元数转换为Unity兼容格式,可按以下步骤操作:
- 将OpenCV的旋转向量转换为旋转矩阵,再求其转置(正交矩阵的逆等于转置),得到相机在世界坐标系中的旋转矩阵。
- 将旋转矩阵转换为四元数后,翻转Y轴分量的符号,适配Unity的坐标系方向。
示例代码片段:
# 将OpenCV的rvec转为旋转矩阵 rot_mat, _ = cv2.Rodrigues(vector_rotation) # 求逆变换(相机到世界的旋转) rot_mat_unity = rot_mat.T # 转换为四元数并调整Y轴符号 rot_unity = Rotation.from_matrix(rot_mat_unity) quat_unity = rot_unity.as_quat() quat_unity[1] = -quat_unity[1] # 适配Unity Y轴向上的坐标系 print("Unity兼容四元数 (x,y,z,w):", quat_unity)
内容的提问来源于stack exchange,提问作者harmegiddo
相关产品推荐
相关产品推荐

