四元数详解
四元数 (Quaternion) 是一种用于表示三维空间旋转的数学工具,比欧拉角更稳定,没有万向锁问题。
四元数的定义
四元数由 4个分量 组成:
qx, qy, qz: 向量部分 (imaginary parts)
qw: 标量部分 (real part)
⚠️ 重要说明:没有单独的”前两个分量含义”
四元数的4个分量是一个整体,不能单独解释某一个或某两个分量的物理意义。它们共同描述旋转,必须作为整体来理解。
为什么不能单独解释?
四元数表示旋转的公式是:
1
| q = cos(θ/2) + sin(θ/2) * (nx*i + ny*j + nz*k)
|
展开为:
1 2 3 4
| qw = cos(θ/2) qx = sin(θ/2) * nx qy = sin(θ/2) * ny qz = sin(θ/2) * nz
|
其中:
- θ: 旋转角度
- [nx, ny, nz]: 旋转轴的单位向量
可以看到,每个分量都同时包含了角度和轴的信息,无法单独解释。
四元数表示旋转的原理
轴角表示法 → 四元数
假设绕某个轴旋转:
1 2 3 4 5 6 7 8 9 10 11
| θ = π/2 axis = [0, 0, 1]
qw = cos(π/4) = 0.707 qx = sin(π/4) * 0 = 0 qy = sin(π/4) * 0 = 0 qz = sin(π/4) * 1 = 0.707
q = [0, 0, 0.707, 0.707]
|
1 2 3 4 5 6 7 8 9 10 11
| θ = π axis = [1, 0, 0]
qw = cos(π/2) = 0 qx = sin(π/2) * 1 = 1 qy = sin(π/2) * 0 = 0 qz = sin(π/2) * 0 = 0
q = [1, 0, 0, 0]
|
特殊四元数示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| q = [0, 0, 0, 1]
q = [0.707, 0, 0, 0.707]
q = [0, 0.707, 0, 0.707]
q = [0, 0, 0.707, 0.707]
q = [1, 0, 0, 0]
|
四元数的约束
重要: 四元数必须是单位四元数 (unit quaternion),即:
1
| qx² + qy² + qz² + qw² = 1
|
否则不能正确表示旋转。
在机器人仿真中的应用
从代码中提取旋转信息
1 2 3 4 5 6 7 8 9 10
| self.base_quat = self.rigid_body_param[:, self.imu_in_torso_indice, 3:7]
q = self.base_quat[0]
norm = torch.sqrt(q[0]**2 + q[1]**2 + q[2]**2 + q[3]**2) print(f"Norm: {norm}")
|
四元数 → 欧拉角(Roll, Pitch, Yaw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import torch
def quat_to_euler(quat): """ 将四元数转换为欧拉角 (roll, pitch, yaw) quat: [qx, qy, qz, qw] 返回: [roll, pitch, yaw] (弧度) """ qx, qy, qz, qw = quat[..., 0], quat[..., 1], quat[..., 2], quat[..., 3] sinr_cosp = 2 * (qw * qx + qy * qz) cosr_cosp = 1 - 2 * (qx * qx + qy * qy) roll = torch.atan2(sinr_cosp, cosr_cosp) sinp = 2 * (qw * qy - qz * qx) pitch = torch.asin(torch.clamp(sinp, -1, 1)) siny_cosp = 2 * (qw * qz + qx * qy) cosy_cosp = 1 - 2 * (qy * qy + qz * qz) yaw = torch.atan2(siny_cosp, cosy_cosp) return torch.stack([roll, pitch, yaw], dim=-1)
euler = quat_to_euler(self.base_quat) roll = euler[:, 0] pitch = euler[:, 1] yaw = euler[:, 2]
|
提取旋转角度(不考虑轴)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def quat_to_angle(quat): """ 从四元数提取旋转角度 quat: [qx, qy, qz, qw] 返回: 旋转角度(弧度) """ qw = quat[..., 3] angle = 2 * torch.acos(torch.clamp(qw, -1, 1)) return angle
rotation_angles = quat_to_angle(self.base_quat)
large_tilt = rotation_angles > 0.5
|
提取旋转轴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| def quat_to_axis_angle(quat): """ 从四元数提取旋转轴和角度 quat: [qx, qy, qz, qw] 返回: (axis, angle) """ qx, qy, qz, qw = quat[..., 0], quat[..., 1], quat[..., 2], quat[..., 3] angle = 2 * torch.acos(torch.clamp(qw, -1, 1)) sin_half_angle = torch.sin(angle / 2) sin_half_angle = torch.clamp(sin_half_angle, min=1e-8) axis_x = qx / sin_half_angle axis_y = qy / sin_half_angle axis_z = qz / sin_half_angle axis = torch.stack([axis_x, axis_y, axis_z], dim=-1) return axis, angle
axis, angle = quat_to_axis_angle(self.base_quat)
|
在双足机器人中的实际应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
euler = quat_to_euler(self.base_quat) roll = euler[:, 0] pitch = euler[:, 1]
fallen = (torch.abs(roll) > 0.5) | (torch.abs(pitch) > 0.5)
orientation_error = 1 - self.base_quat[:, 3]**2 reward = -orientation_error
yaw = euler[:, 2]
target_yaw = self.commands[:, 2] yaw_error = torch.abs(yaw - target_yaw)
|
四元数的数学性质
四元数乘法(组合旋转)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def quat_multiply(q1, q2): """ 四元数乘法,组合两个旋转 先应用 q1,再应用 q2 """ w1, x1, y1, z1 = q1[..., 3], q1[..., 0], q1[..., 1], q1[..., 2] w2, x2, y2, z2 = q2[..., 3], q2[..., 0], q2[..., 1], q2[..., 2] w = w1*w2 - x1*x2 - y1*y2 - z1*z2 x = w1*x2 + x1*w2 + y1*z2 - z1*y2 y = w1*y2 - x1*z2 + y1*w2 + z1*x2 z = w1*z2 + x1*y2 - y1*x2 + z1*w2 return torch.stack([x, y, z, w], dim=-1)
|
四元数共轭(逆旋转)
1 2 3 4 5
| def quat_conjugate(q): """ 四元数共轭(逆旋转) """ return torch.cat([-q[..., :3], q[..., 3:4]], dim=-1)
|
总结
- 四元数的4个分量必须作为整体理解,不能单独解释前两个
- 每个分量都混合了旋转角度和旋转轴的信息
- 四元数 [qx, qy, qz, qw] 通过公式与旋转轴角对应
- 实际使用中,通常转换为欧拉角或旋转矩阵来理解
- 在机器人仿真中,四元数主要用于表示机器人基座的姿态
如果你想知道机器人的具体姿态(倾斜、俯仰等),建议将四元数转换为欧拉角!