You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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兼容格式,可按以下步骤操作:

  1. 将OpenCV的旋转向量转换为旋转矩阵,再求其转置(正交矩阵的逆等于转置),得到相机在世界坐标系中的旋转矩阵。
  2. 将旋转矩阵转换为四元数后,翻转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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 18:33:15