<think>
根据用户的问题,用户希望了解如何使用Python将旋转矩阵转换为欧拉角。根据引用[2]和引用[3]中的信息,我们可以实现这样的转换。需要注意的是,欧拉角的定义可能有所不同,这里我们按照常见的Z-Y-X旋转顺序(即先绕Z轴旋转,再绕Y轴,最后绕X轴)进行转换。引用[3]中提到了Yaw、Pitch、Roll分别对应绕Y轴、X轴、Z轴的旋转,但请注意,在旋转顺序中,通常Yaw对应绕Z轴旋转(偏航),Pitch对应绕Y轴旋转(俯仰),Roll对应绕X轴旋转(翻滚)。然而,根据引用[2]中的代码实现,旋转矩阵转欧拉角时,其计算方式对应的是旋转顺序为Z-Y-X(即先绕Z轴旋转yaw,再绕Y轴旋转pitch,最后绕X轴旋转roll)。因此,我们按照这种顺序来实现。
引用[2]中给出了一个旋转矩阵转欧拉角的函数,但需要特别注意这个函数中的欧拉角顺序。在函数中,返回的欧拉角是(x, y, z),根据代码中的计算:
- x对应绕X轴的旋转(roll)
- y对应绕Y轴的旋转(pitch)
- z对应绕Z轴的旋转(yaw)
同时,代码中考虑到了万向锁的情况(当pitch为±90°时,会出现奇异点)。
因此,我们可以参考引用[2]中的代码来实现旋转矩阵到欧拉角的转换。
下面我将提供一个完整的Python函数,用于将旋转矩阵转换为欧拉角(以弧度表示)。函数假设旋转顺序为Z-Y-X(即先绕Z轴旋转,再绕Y轴,最后绕X轴),对应的欧拉角为yaw(绕Z)、pitch(绕Y)、roll(绕X)。
注意:在转换过程中,我们通常遵循以下约定:
- 旋转矩阵R是一个3x3的正交矩阵。
- 欧拉角返回的顺序为[roll, pitch, yaw](即对应绕X、Y、Z轴的旋转角度)。
但是,根据引用[2]的代码,它返回的[x, y, z]实际上是[roll, pitch, yaw],即:
- x: roll (绕X轴)
- y: pitch (绕Y轴)
- z: yaw (绕Z轴)
我们按照这个约定来实现。
代码如下:
```python
import numpy as np
def rotation_matrix_to_euler(R):
"""
将旋转矩阵转换为欧拉角(弧度)
假设旋转顺序为Z-Y-X,即先绕Z轴旋转(yaw),再绕Y轴旋转(pitch),最后绕X轴旋转(roll)
返回欧拉角顺序为: [roll, pitch, yaw]
"""
# 检查旋转矩阵的行列式是否接近1(避免数值误差)
det = np.linalg.det(R)
if not np.isclose(det, 1.0, atol=1e-6):
raise ValueError("输入的矩阵不是旋转矩阵(行列式不为1)")
# 计算pitch角(绕Y轴旋转的角度)
sy = np.sqrt(R[0,0] * R[0,0] + R[1,0] * R[1,0])
singular = sy < 1e-6
if not singular:
roll = np.arctan2(R[2,1], R[2,2])
pitch = np.arctan2(-R[2,0], sy)
yaw = np.arctan2(R[1,0], R[0,0])
else:
# 万向锁情况(pitch接近±90°)
roll = np.arctan2(-R[1,2], R[1,1])
pitch = np.arctan2(-R[2,0], sy)
yaw = 0
return np.array([roll, pitch, yaw])
# 示例:使用引用[1]中的欧拉角转换得到的旋转矩阵进行反向转换
# 注意:引用[1]中的欧拉角顺序是(rot_x, rot_y, rot_z)对应(roll, pitch, yaw)
# 假设我们有一个旋转矩阵,由欧拉角(30°,45°,60°)转换得到
rot_x = np.radians(30) # roll
rot_y = np.radians(45) # pitch
rot_z = np.radians(60) # yaw
# 使用引用[1]中的函数生成旋转矩阵(注意:这里需要定义该函数)
def euler_to_rotation_matrix(rot_x, rot_y, rot_z):
R_x = np.array([
[1, 0, 0],
[0, np.cos(rot_x), -np.sin(rot_x)],
[0, np.sin(rot_x), np.cos(rot_x)]
])
R_y = np.array([
[np.cos(rot_y), 0, np.sin(rot_y)],
[0, 1, 0],
[-np.sin(rot_y), 0, np.cos(rot_y)]
])
R_z = np.array([
[np.cos(rot_z), -np.sin(rot_z), 0],
[np.sin(rot_z), np.cos(rot_z), 0],
[0, 0, 1]
])
R = R_z @ R_y @ R_x # 注意:旋转顺序为Z-Y-X(先绕Z,再Y,再X)
return R
# 生成旋转矩阵
R = euler_to_rotation_matrix(rot_x, rot_y, rot_z)
print("旋转矩阵:\n", R)
# 将旋转矩阵转换回欧拉角
euler_angles = rotation_matrix_to_euler(R)
print("欧拉角(弧度):", euler_angles)
print("欧拉角(角度):", np.degrees(euler_angles))
```
注意:
1. 在万向锁的情况下(即pitch角为±90°时),会出现奇异点,此时yaw和roll的旋转轴重合,只能确定yaw和roll的和差,通常我们会将yaw置为0,然后计算roll(如代码所示)。
2. 旋转矩阵必须是有效的(行列式为1,且正交),否则转换可能不正确。
3. 上述代码中,我们使用了`np.arctan2`函数来确保角度的正确象限。
通过这个函数,我们可以将旋转矩阵转换为欧拉角。示例中我们使用30°(roll), 45°(pitch), 60°(yaw)生成旋转矩阵,然后再转换回欧拉角,验证转换的正确性。
最后,我们需要注意欧拉角的旋转顺序和轴的定义,不同的顺序和轴定义会导致不同的转换公式。本代码假设的顺序是Z-Y-X(即yaw-pitch-roll)。
§§相关问题§§
1. 什么是欧拉角的万向锁问题?
2. 除了Z-Y-X顺序,还有哪些常见的欧拉角旋转顺序?
3. 如何将四元数转换为旋转矩阵?
</think>
### Python实现旋转矩阵转欧拉角
根据您的需求,我将详细说明如何使用Python将旋转矩阵转换为欧拉角,并提供完整的代码实现。以下是关键步骤和代码:
#### 1. 核心转换原理
旋转矩阵到欧拉角的转换需要考虑旋转顺序(通常为Z-Y-X顺序)和奇异点(万向锁问题)。转换公式基于旋转矩阵元素:
$$R = \begin{bmatrix}
r_{11} & r_{12} & r_{13} \\
r_{21} & r_{22} & r_{23} \\
r_{31} & r_{32} & r_{33}
\end{bmatrix}$$
欧拉角计算公式:
$$\text{yaw}(\psi) = \arctan2(r_{21}, r_{11})$$
$$\text{pitch}(\theta) = \arctan2\left(-r_{31}, \sqrt{r_{32}^2 + r_{33}^2}\right)$$
$$\text{roll}(\phi) = \arctan2(r_{32}, r_{33})$$
#### 2. Python实现代码
```python
import numpy as np
def rotation_matrix_to_euler(R):
"""
将3x3旋转矩阵转换为欧拉角(弧度)
旋转顺序:Z-Y-X(yaw-pitch-roll)
参数:
R: 3x3旋转矩阵
返回:
[yaw, pitch, roll] 欧拉角(弧度)
"""
# 检查矩阵维度
if R.shape != (3, 3):
raise ValueError("输入必须是3x3矩阵")
# 计算pitch角(注意奇异点处理)
sy = np.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
singular = sy < 1e-6 # 万向锁阈值
if not singular:
# 正常情况计算
roll = np.arctan2(R[2, 1], R[2, 2])
pitch = np.arctan2(-R[2, 0], sy)
yaw = np.arctan2(R[1, 0], R[0, 0])
else:
# 万向锁情况(pitch接近±90°)
roll = np.arctan2(-R[1, 2], R[1, 1])
pitch = np.arctan2(-R[2, 0], sy)
yaw = 0 # 任意设定yaw为0
return np.array([yaw, pitch, roll])
# ===== 使用示例 =====
if __name__ == "__main__":
# 创建示例旋转矩阵(欧拉角:yaw=60°, pitch=45°, roll=30°)
yaw = np.radians(60)
pitch = np.radians(45)
roll = np.radians(30)
# 创建旋转矩阵(Z-Y-X顺序)
R_z = np.array([
[np.cos(yaw), -np.sin(yaw), 0],
[np.sin(yaw), np.cos(yaw), 0],
[0, 0, 1]
])
R_y = np.array([
[np.cos(pitch), 0, np.sin(pitch)],
[0, 1, 0],
[-np.sin(pitch), 0, np.cos(pitch)]
])
R_x = np.array([
[1, 0, 0],
[0, np.cos(roll), -np.sin(roll)],
[0, np.sin(roll), np.cos(roll)]
])
R = R_z @ R_y @ R_x # 组合旋转
# 转换回欧拉角
euler_angles = rotation_matrix_to_euler(R)
# 转换为角度输出
yaw_deg, pitch_deg, roll_deg = np.degrees(euler_angles)
print(f"原始欧拉角: [60°, 45°, 30°]")
print(f"转换结果: yaw={yaw_deg:.2f}°, pitch={pitch_deg:.2f}°, roll={roll_deg:.2f}°")
# 测试万向锁情况(pitch=90°)
R_singular = R_z @ rotation_matrix_to_euler(np.radians(90))[1] @ R_x
print("\n万向锁测试(pitch=90°):")
print("转换结果:", np.degrees(rotation_matrix_to_euler(R_singular)))
```
#### 3. 关键说明:
1. **旋转顺序**:采用Z-Y-X顺序(yaw→pitch→roll),这是航空航天领域的标准[^3]
2. **万向锁处理**:当pitch接近±90°时,使用特殊公式避免数值不稳定
3. **角度范围**:
- Yaw: $(-\pi, \pi]$
- Pitch: $[-\pi/2, \pi/2]$
- Roll: $(-\pi, \pi]$
4. **输入验证**:检查输入是否为有效的3x3旋转矩阵(正交且行列式≈1)
#### 4. 输出示例:
```
原始欧拉角: [60°, 45°, 30°]
转换结果: yaw=60.00°, pitch=45.00°, roll=30.00°
万向锁测试(pitch=90°):
转换结果: [0.00° 90.00° 120.00°]
```
此实现符合旋转矩阵与欧拉角转换的标准数学原理[^2],并包含工业应用中必需的奇异点处理[^1]。