向量与矩阵基础:点积、叉积、矩阵乘法与逆矩阵
好,咱们接着聊。上一章我们把坐标变换的「世界观」搭好了,这一章得把「工具箱」备齐。
向量和矩阵,说白了就是机器人控制里的加减乘除。你想想看,机械臂要抓一个杯子,它得知道杯子在哪儿(向量),还得知道怎么转过去(矩阵)。这些运算搞不明白,后面的变换公式就是空中楼阁。
我个人习惯,先讲向量,再讲矩阵。因为矩阵本质上就是一堆向量排排坐。
2.1 向量的点积:投影的度量
点积,也叫内积。公式很简单:a · b = |a| |b| cosθ。
但别被公式吓到。我换个说法:点积就是向量 a 在向量 b 方向上的投影长度,再乘以 b 的长度。
在机器人里,点积最常见的用处是判断两个方向是否「对齐」。
- 如果点积 > 0,夹角小于 90°,方向大致相同。
- 如果点积 = 0,垂直,正交。
- 如果点积 < 0,夹角大于 90°,方向相反。
实际案例:我在做移动机器人导航时,经常用点积判断机器人当前朝向和目标方向是否一致。如果点积接近 1,说明方向对了,可以直走;如果接近 -1,说明方向反了,得掉头。
代码实现也很简单:
// 二维向量点积
float dotProduct(float ax, float ay, float bx, float by) {
return ax * bx + ay * by;
}
// 三维向量点积
float dotProduct3D(float a[3], float b[3]) {
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
}
小技巧:点积的结果是一个标量,不是向量。很多新手会搞混,记住:点积「点」出一个数。
2.2 向量的叉积:法向量的生成器
叉积就更有意思了。它只存在于三维空间(严格来说二维也有,但结果是个标量)。
公式:a × b = |a| |b| sinθ · n,其中 n 是垂直于 a 和 b 的单位向量。
说白了,叉积生成一个垂直于两个输入向量的新向量。方向由右手定则决定。
我在项目中遇到过什么场景呢?机械臂的逆运动学求解。你要计算关节轴的旋转方向,就得用叉积。比如已知两个不共线的向量,想找到垂直于它们的方向,叉积就是最直接的工具。
// 三维向量叉积
void crossProduct(float a[3], float b[3], float result[3]) {
result[0] = a[1]*b[2] - a[2]*b[1];
result[1] = a[2]*b[0] - a[0]*b[2];
result[2] = a[0]*b[1] - a[1]*b[0];
}
注意:叉积不满足交换律!a × b = - (b × a)。方向搞反了,机械臂可能就往反方向转了。我曾经因为这个 bug 调试了一整天……
2.3 矩阵乘法:变换的串联
矩阵乘法,是坐标变换的核心操作。为什么?因为一个旋转矩阵乘以一个向量,就等于把这个向量旋转了。
多个变换叠加呢?比如先旋转再平移,那就是多个矩阵相乘。
矩阵乘法的规则:C = A × B,其中 C 的第 i 行第 j 列等于 A 的第 i 行与 B 的第 j 列的点积。
嗯,这里要注意:矩阵乘法不满足交换律。A × B 一般不等于 B × A。这在实际中意味着什么?先旋转再平移,和先平移再旋转,结果完全不同。
| 操作顺序 | 矩阵表达式 | 结果 |
|---|---|---|
| 先旋转后平移 | T × R × v | 物体绕原点旋转后再移动 |
| 先平移后旋转 | R × T × v | 物体移动后再绕原点旋转 |
代码示例:
// 3x3 矩阵乘法
void matrixMultiply(float A[3][3], float B[3][3], float C[3][3]) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
C[i][j] = 0;
for (int k = 0; k < 3; k++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
}
2.4 逆矩阵:回到原点
逆矩阵,说白了就是「撤销」操作。你做了一个变换,想回到原来的状态,就用逆矩阵。
定义:如果 A × A⁻¹ = I(单位矩阵),那么 A⁻¹ 就是 A 的逆矩阵。
在机器人控制里,逆矩阵太常用了。比如你知道了末端执行器的位姿,想反推各个关节的角度,这就是逆运动学问题,里面大量用到逆矩阵。
计算逆矩阵的方法有很多:
- 2x2 矩阵:直接用公式,简单快速。
- 3x3 矩阵:用伴随矩阵法,或者高斯消元。
- 更大矩阵:一般用数值方法,比如 LU 分解。
// 2x2 矩阵求逆
void inverse2x2(float A[2][2], float invA[2][2]) {
float det = A[0][0]*A[1][1] - A[0][1]*A[1][0];
if (fabs(det) < 1e-6) {
printf("矩阵不可逆!\n");
return;
}
float invDet = 1.0 / det;
invA[0][0] = A[1][1] * invDet;
invA[0][1] = -A[0][1] * invDet;
invA[1][0] = -A[1][0] * invDet;
invA[1][1] = A[0][0] * invDet;
}
避坑指南:我曾经在写运动学解算时,忘记检查矩阵的行列式是否为零。结果程序跑着跑着就崩了。后来我养成了习惯:每次求逆之前,先算行列式,如果接近零就做特殊处理。
2.5 知识体系总览
这一章的内容比较多,我画了张图帮你理清关系:
你看,向量和矩阵不是孤立的。点积和叉积是向量的基本操作,矩阵乘法是这些操作的推广和组合,逆矩阵则让我们有了「后悔药」。把这些基础打牢,后面的变换公式就是水到渠成的事。
我的建议:别光看,动手算一算。拿个 2x2 矩阵手动求个逆,或者算两个三维向量的叉积。手算一遍,比看十遍都管用。
好了,这一章就到这里。记住:向量是点,矩阵是变换。点积看方向,叉积找法线,乘法做串联,逆矩阵能还原。这些工具备齐了,下一章我们就可以开始搭建真正的坐标变换体系了。