旋转矩阵 (Rotation Matrix) 详解与 ROS 实践
1. 基本定义与几何意义
在三维刚体运动中,旋转矩阵用于描述一个坐标系相对于另一个坐标系的姿态(Orientation)。
假设存在参考坐标系 $\{s\}$(Space/Fixed)和目标坐标系 $\{b\}$(Body)。旋转矩阵 $R_{sb} \in \mathbb{R}^{3 \times 3}$ 的每一列,恰好是 $\{b\}$ 坐标系的三个单位基向量在 $\{s\}$ 系下的坐标表示:
$$ R_{sb} = \begin{bmatrix} \hat{x}_b^s & \hat{y}_b^s & \hat{z}_b^s \end{bmatrix} = \begin{bmatrix} r_{11} & r_{12} & r_{13} \\ r_{21} & r_{22} & r_{23} \\ r_{31} & r_{32} & r_{33} \end{bmatrix} $$其中 $\hat{x}_b^s, \hat{y}_b^s, \hat{z}_b^s$ 分别表示 $\{b\}$ 系的 $x, y, z$ 轴单位向量在 $\{s\}$ 系中的投影坐标。
2. 核心数学约束与特殊正交群
旋转矩阵并非任意 $3 \times 3$ 矩阵,它必须满足严格的几何约束,数学上称为 特殊正交群 (Special Orthogonal Group),记作 $SO(3)$。
2.1 约束条件
- 单位向量约束(每列模长为 1): $$ \|\hat{x}_b^s\|^2 = \|\hat{y}_b^s\|^2 = \|\hat{z}_b^s\|^2 = 1 $$
- 正交性约束(两两内积为 0): $$ \hat{x}_b^s \cdot \hat{y}_b^s = \hat{x}_b^s \cdot \hat{z}_b^s = \hat{y}_b^s \cdot \hat{z}_b^s = 0 $$
- 右手定则约束(行列式为 +1): $$ \hat{x}_b^s \times \hat{y}_b^s = \hat{z}_b^s \quad \Rightarrow \quad \det(R) = 1 $$
上述 6 个标量约束可统一写为矩阵正交条件:
$$ R^T R = I $$结合行列式约束,完整定义为:
$$ SO(3) = \{ R \in \mathbb{R}^{3 \times 3} \mid R^T R = I, \ \det(R) = 1 \} $$2.2 群公理验证
$SO(3)$ 满足数学群的四大公理:
| 公理 | 数学表达 | 说明 |
|---|---|---|
| 封闭性 | $R_1, R_2 \in SO(3) \Rightarrow R_1 R_2 \in SO(3)$ | 连续旋转仍是纯旋转 |
| 结合律 | $(R_1 R_2) R_3 = R_1 (R_2 R_3)$ | 矩阵乘法天然满足 |
| 单位元 | $I \in SO(3)$,满足 $RI = IR = R$ | 零旋转状态 |
| 可逆性 | $R^{-1} = R^T \in SO(3)$ | 反向旋转只需转置 |
3. 核心性质
| 性质 | 数学表达 | 物理意义 |
|---|---|---|
| 逆等于转置 | $R^{-1} = R^T$ | 求反向姿态无需复杂求逆,计算高效 |
| 非交换性 | $R_1 R_2 \neq R_2 R_1$(一般情况) | 旋转顺序直接影响最终姿态(三维特有) |
| 保范数性 | $\|R \mathbf{v}\| = \|\mathbf{v}\|$ | 纯旋转不改变向量长度 |
| 保内积性 | $(R\mathbf{u})^T (R\mathbf{v}) = \mathbf{u}^T \mathbf{v}$ | 旋转不改变向量夹角 |
4. 常用旋转矩阵表达式
4.1 二维旋转 $SO(2)$
二维仅含 1 个自由度 $\theta \in [0, 2\pi)$:
$$ R(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} $$4.2 绕固定坐标轴旋转(基本旋转矩阵)
$$ R_x(\theta) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta \\ 0 & \sin\theta & \cos\theta \end{bmatrix}, \quad R_y(\theta) = \begin{bmatrix} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \end{bmatrix}, \quad R_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} $$4.3 绕任意单位轴 $\hat{\omega} = (\omega_1, \omega_2, \omega_3)^T$ 旋转 $\theta$
由 罗德里格斯公式 (Rodrigues’ Formula) 给出:
$$ R(\hat{\omega}, \theta) = \begin{bmatrix} c_\theta + \omega_1^2(1-c_\theta) & \omega_1\omega_2(1-c_\theta) - \omega_3 s_\theta & \omega_1\omega_3(1-c_\theta) + \omega_2 s_\theta \\ \omega_1\omega_2(1-c_\theta) + \omega_3 s_\theta & c_\theta + \omega_2^2(1-c_\theta) & \omega_2\omega_3(1-c_\theta) - \omega_1 s_\theta \\ \omega_1\omega_3(1-c_\theta) - \omega_2 s_\theta & \omega_2\omega_3(1-c_\theta) + \omega_1 s_\theta & c_\theta + \omega_3^2(1-c_\theta) \end{bmatrix} $$其中 $s_\theta = \sin\theta,\ c_\theta = \cos\theta$。具有对称性:$R(\hat{\omega}, \theta) = R(-\hat{\omega}, -\theta)$。
5. 两大应用场景:表示 vs 操作
旋转矩阵在工程中有两种截然不同的用法,关键看矩阵是否带下标:
| 用法 | 特征 | 物理含义 | 示例 |
|---|---|---|---|
| 1. 表示 (Representation) | 带下标,如 $R_{sb}$ | 描述坐标系 $\{b\}$ 相对于 $\{s\}$ 的静态姿态 | 相机相对于机器人的朝向 |
| 2. 操作 (Operation) | 无下标,如 $R$ | 在同一坐标系内执行旋转动作 | 机械臂末端绕自身轴转动 |
6. 坐标变换规则与下标消去法
6.1 参考系传递(坐标系连乘)
已知 $\{b\}$ 相对 $\{s\}$ 的 $R_{sb}$,以及 $\{c\}$ 相对 $\{b\}$ 的 $R_{bc}$,求 $\{c\}$ 相对 $\{s\}$ 的 $R_{sc}$:
$$ R_{sc} = R_{sb} R_{bc} $$
下标消去技巧:相邻相同下标自动抵消,$R_{ab} R_{bc} R_{cd} = R_{ad}$。
利用正交性可推导:$R_{sc} = R_{sb} R_{cb}^{-1} = R_{sb} R_{cb}^T$。
6.2 向量坐标变换(点/向量换系)
设点 $P$ 在 $\{b\}$ 系中的坐标为 $\mathbf{p}_b$,求其在 $\{s\}$ 系中的坐标 $\mathbf{p}_s$:
$$ \mathbf{p}_s = R_{sb} \mathbf{p}_b $$向量变换口诀:$R_{ab} \mathbf{p}_b = \mathbf{p}_a$(矩阵下标左侧对应目标系,右侧对应原系)。
7. 左乘与右乘的物理意义(重点)
当旋转矩阵不带下标(如 $R = \text{Rot}(\hat{z}, 90^\circ)$)时,表示在同一坐标系内进行旋转操作。此时左右顺序决定旋转轴参考系:
| 操作 | 公式 | 物理含义 |
|---|---|---|
| 左乘 | $R'_{sb} = R \cdot R_{sb}$ | 绕 固定参考系 $\{s\}$ 的坐标轴旋转 |
| 右乘 | $R'_{sb} = R_{sb} \cdot R$ | 绕 运动载体系 $\{b\}$ 的坐标轴旋转 |
记忆技巧:看 $R_{sb}$ 的字母位置。s 在左侧,左乘 $R$ 表示绕 $\{s\}$ 系转;b 在右侧,右乘 $R$ 表示绕 $\{b\}$ 系转。
无论左乘还是右乘,结果矩阵 $R'_{sb}$ 仍然是描述 $\{b\}$ 相对于 $\{s\}$ 的新姿态。
8. ROS 工程实践 (tf2 库)
在 ROS 中,坐标系变换通过 tf2 管理。每个 TransformStamped 本质是一个齐次变换矩阵(含旋转与平移)。ROS 底层统一使用四元数存储旋转。
8.1 发布者 (Publisher):广播静态坐标系
#include <ros/ros.h>
#include <tf2_ros/static_transform_broadcaster.h>
#include <geometry_msgs/TransformStamped.h>
#include <tf2/LinearMath/Quaternion.h>
int main(int argc, char** argv) {
ros::init(argc, argv, "rotation_publisher");
ros::NodeHandle nh;
tf2_ros::StaticTransformBroadcaster static_broadcaster;
geometry_msgs::TransformStamped static_transformStamped;
// 1. 设置父子关系:s -> b
static_transformStamped.header.stamp = ros::Time::now();
static_transformStamped.header.frame_id = "s";
static_transformStamped.child_frame_id = "b";
// 2. 平移(纯旋转设为 0,此处设 x=-2 仅用于 RViz 视觉区分)
static_transformStamped.transform.translation.x = -2.0;
static_transformStamped.transform.translation.y = 0.0;
static_transformStamped.transform.translation.z = 0.0;
// 3. 旋转:{b} 由 {s} 绕 z_s 轴旋转 90° 得到
tf2::Vector3 z_axis(0, 0, 1);
double theta = M_PI / 2.0;
tf2::Quaternion quat;
quat.setRotation(z_axis, theta);
static_transformStamped.transform.rotation.x = quat.x();
static_transformStamped.transform.rotation.y = quat.y();
static_transformStamped.transform.rotation.z = quat.z();
static_transformStamped.transform.rotation.w = quat.w();
static_broadcaster.sendTransform(static_transformStamped);
ROS_INFO("Published transform: s -> b (Rot z by 90 deg)");
ros::spin();
return 0;
}
8.2 监听者 (Listener):使用 TF 变换向量
#include <ros/ros.h>
#include <tf2_ros/transform_listener.h>
#include <geometry_msgs/PointStamped.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <boost/bind.hpp>
void transformPoint(const tf2_ros::Buffer& buffer) {
// 1. 定义在 {b} 系中的点
geometry_msgs::PointStamped p_b;
p_b.header.frame_id = "b";
p_b.header.stamp = ros::Time(0);
p_b.point.x = -1.0;
p_b.point.y = 0.0;
p_b.point.z = 0.0;
try {
// 2. 获取从 {b} 到 {s} 的变换 (target_frame, source_frame)
geometry_msgs::TransformStamped R_sb = buffer.lookupTransform("s", "b", ros::Time(0));
// 3. 执行坐标变换: p_s = R_sb * p_b
geometry_msgs::PointStamped p_s;
tf2::doTransform(p_b, p_s, R_sb);
ROS_INFO("{b}:(%.2f, %.2f, %.2f) ---> {s}:(%.2f, %.2f, %.2f)",
p_b.point.x, p_b.point.y, p_b.point.z,
p_s.point.x, p_s.point.y, p_s.point.z);
} catch (tf2::TransformException &ex) {
ROS_ERROR("TF Lookup failed: %s", ex.what());
}
}
int main(int argc, char** argv) {
ros::init(argc, argv, "rotation_listener");
ros::NodeHandle nh;
tf2_ros::Buffer tfBuffer;
tf2_ros::TransformListener tfListener(tfBuffer);
// 定时执行 (1Hz)
ros::Timer timer = nh.createTimer(ros::Duration(1.0),
boost::bind(&transformPoint, boost::ref(tfBuffer)));
ros::spin();
return 0;
}
ROS TF 补充说明:
lookupTransform("target", "source", time)返回的是从source到target的变换。- TF 树方向永远是 Parent → Child,但 RViz 中坐标轴箭头显示的是 Child 相对于 Parent 的朝向。二者描述同一数学关系,仅视角不同。
tf2::doTransform内部已处理四元数与向量的乘法,无需手动展开矩阵。
9.要点总结
| 场景 | 规则/口诀 | 示例 |
|---|---|---|
| 连乘消下标 | 中间字母相消,首尾保留 | $R_{ab} R_{bc} R_{cd} = R_{ad}$ |
| 向量换系 | 矩阵下标右侧匹配向量原系 | $R_{sb} \mathbf{p}_b = \mathbf{p}_s$ |
| 求逆转置 | 交换下标顺序等价于求转置 | $R_{bs} = R_{sb}^{-1} = R_{sb}^T$ |
| 左乘/右乘 | 左定右动:左乘绕定系,右乘绕动系 | $R_{new} = R_{\text{fixed}} R_{old}$ (左) $R_{new} = R_{old} R_{\text{body}}$ (右) |
| 带下标 vs 不带下标 | 带下标:改变参考系/表示方向 不带下标:同系内独立旋转操作 |
$R_{sb}$ 表示姿态 $\text{Rot}(\hat{z},\theta)$ 表示操作 |
备注:在实际机器人开发中,当需要频繁进行姿态插值、滤波或优化时,建议结合四元数 (Quaternion) 或李代数 $\mathfrak{so}(3)/\mathfrak{se}(3)$ 使用,可避免万向节死锁并提升数值稳定性。旋转矩阵仍是理解几何关系与坐标系映射最直观的基石。