参考文献: Closed-form solution of absolute orientation using unit quaternions
在 SLAM 中, 轨迹会因为因为起始位姿、尺度设定的不同而不同
在轨迹形状大致相同时, 可以求取一个相似变换使得两个轨迹尽可能重合
给定两个分别具有
n
n
n 个空间点的轨迹, 分别定义为:
r
l
∈
R
n
×
3
,
r
r
∈
R
n
×
3
r_l \in \mathbb{R}^{n \times 3}, r_r \in \mathbb{R}^{n \times 3}
rl∈Rn×3,rr∈Rn×3
有相似变换
Sim3
(
s
R
,
t
)
\text{Sim3}(sR, t)
Sim3(sR,t) 使得下式的和方差最小, 并使轨迹两个轨迹对齐:
SSE
=
∑
i
=
1
n
∣
∣
s
R
(
r
l
,
i
)
+
t
−
r
r
,
i
∣
∣
2
\text{SSE} = \sum_{i=1}^n||sR(r_{l, i}) + t - r_{r, i}||^2
SSE=i=1∑n∣∣sR(rl,i)+t−rr,i∣∣2
平移向量
首先,计算两个轨迹的质心,并分别对两个轨迹零均值化:
r
l
′
=
r
l
−
r
l
ˉ
,
r
l
ˉ
∈
R
3
r
r
′
=
r
r
−
r
r
ˉ
,
r
r
ˉ
∈
R
3
t
′
=
t
+
s
R
(
r
l
ˉ
)
−
r
r
ˉ
SSE
=
∑
i
=
1
n
∣
∣
s
R
(
r
l
,
i
′
)
+
t
′
−
r
r
,
i
′
∣
∣
2
=
∑
i
=
1
n
∣
∣
s
R
(
r
l
,
i
′
)
−
r
r
,
i
′
∣
∣
2
+
2
t
′
∑
i
=
1
n
(
s
R
(
r
l
,
i
′
)
−
r
r
,
i
′
)
+
n
∣
∣
t
′
∣
∣
2
r'_l = r_l - \bar{r_l}, \bar{r_l} \in \mathbb{R}^{3} \\ r'_r = r_r - \bar{r_r}, \bar{r_r} \in \mathbb{R}^{3} \\ t'= t + sR(\bar{r_l}) - \bar{r_r} \\ \text{SSE} = \sum_{i=1}^n||sR(r'_{l, i}) + t' - r'_{r, i}||^2 \\ = \sum_{i=1}^n ||sR(r'_{l, i}) - r'_{r, i}||^2 + 2t'\sum_{i=1}^n(sR(r'_{l, i}) - r'_{r, i}) + n||t'||^2
rl′=rl−rlˉ,rlˉ∈R3rr′=rr−rrˉ,rrˉ∈R3t′=t+sR(rlˉ)−rrˉSSE=i=1∑n∣∣sR(rl,i′)+t′−rr,i′∣∣2=i=1∑n∣∣sR(rl,i′)−rr,i′∣∣2+2t′i=1∑n(sR(rl,i′)−rr,i′)+n∣∣t′∣∣2
零均值点经过线性变换后,其均值依然为零,即
∑
i
=
1
n
s
R
(
r
l
,
i
′
)
=
0
\sum_{i=1}^n sR(r'_{l, i}) = 0
∑i=1nsR(rl,i′)=0, 所以第二项为 0:
SSE
=
∑
i
=
1
n
∣
∣
s
R
(
r
l
,
i
′
)
−
r
r
,
i
′
∣
∣
2
+
n
∣
∣
t
′
∣
∣
2
\text{SSE} = \sum_{i=1}^n ||sR(r'_{l, i}) - r'_{r, i}||^2 + n||t'||^2
SSE=i=1∑n∣∣sR(rl,i′)−rr,i′∣∣2+n∣∣t′∣∣2
第一项只与
s
,
R
s, R
s,R 有关, 第二项只与
t
′
t'
t′ 有关。令第二项为 0, 解得平移向量:
t
=
r
r
ˉ
−
s
R
(
r
l
ˉ
)
t = \bar{r_r} - sR(\bar{r_l})
t=rrˉ−sR(rlˉ)
其中只有 s , R s, R s,R 是未知量, 后续代入数值即可
尺度因子
接上文, 继续对
SSE
\text{SSE}
SSE 进行简化和分解 (点经旋转后模长不变, 即
∣
∣
R
(
r
)
∣
∣
2
=
∣
∣
r
∣
∣
2
||R(r)||^2 = ||r||^2
∣∣R(r)∣∣2=∣∣r∣∣2):
S
l
=
∑
i
=
1
n
∣
∣
R
(
r
l
,
i
′
)
∣
∣
2
=
∑
i
=
1
n
∣
∣
r
l
,
i
′
∣
∣
2
S
r
=
∑
i
=
1
n
∣
∣
r
r
,
i
′
∣
∣
2
D
=
∑
i
=
1
n
r
r
,
i
′
⋅
R
(
r
l
,
i
′
)
SSE
=
∑
i
=
1
n
∣
∣
s
R
(
r
l
,
i
′
)
−
r
r
,
i
′
∣
∣
2
=
s
2
S
l
−
2
s
D
+
S
r
=
s
(
s
S
l
−
2
D
+
1
s
S
r
)
=
s
(
s
S
l
−
1
s
S
r
)
2
+
2
s
(
S
l
S
r
−
D
)
S_l = \sum_{i=1}^n||R(r'_{l, i})||^2 = \sum_{i=1}^n||r'_{l, i}||^2 \\ S_r = \sum_{i=1}^n||r'_{r, i}||^2 \\ D = \sum_{i=1}^n r'_{r, i} \cdot R(r'_{l, i}) \\ \text{SSE} = \sum_{i=1}^n ||sR(r'_{l, i}) - r'_{r, i}||^2 = s^2 S_l - 2sD + S_r \\ = s(s S_l - 2D + \frac{1}{s} S_r) = s(\sqrt{s S_l} - \sqrt{\frac{1}{s} S_r})^2 + 2s(\sqrt{S_l S_r} - D)
Sl=i=1∑n∣∣R(rl,i′)∣∣2=i=1∑n∣∣rl,i′∣∣2Sr=i=1∑n∣∣rr,i′∣∣2D=i=1∑nrr,i′⋅R(rl,i′)SSE=i=1∑n∣∣sR(rl,i′)−rr,i′∣∣2=s2Sl−2sD+Sr=s(sSl−2D+s1Sr)=s(sSl−s1Sr)2+2s(SlSr−D)
令第一项
s
S
l
−
1
s
S
r
=
0
\sqrt{s S_l} - \sqrt{\frac{1}{s} S_r} = 0
sSl−s1Sr=0, 可解得尺度因子 (表达式中所有数值均为已知量):
s
=
S
r
S
l
=
∑
i
=
1
n
∣
∣
r
r
,
i
′
∣
∣
2
∑
i
=
1
n
∣
∣
r
l
,
i
′
∣
∣
2
s = \sqrt{\frac{S_r}{S_l}} = \sqrt{\frac{\sum_{i=1}^n||r'_{r, i}||^2}{\sum_{i=1}^n||r'_{l, i}||^2}}
s=SlSr=∑i=1n∣∣rl,i′∣∣2∑i=1n∣∣rr,i′∣∣2
单位四元数
由于
S
l
,
S
r
S_l, S_r
Sl,Sr 已知为定值, 最小化
SSE
\text{SSE}
SSE 等价于最大化
D
D
D:
maximize
:
D
=
∑
i
=
1
n
r
r
,
i
′
⋅
R
(
r
l
,
i
′
)
\text{maximize}: D = \sum_{i=1}^n r'_{r, i} \cdot R(r'_{l, i})
maximize:D=i=1∑nrr,i′⋅R(rl,i′)
为了解决这个问题, 引入四元数进行化简, 首先利用
q
˙
,
r
˙
∈
R
4
×
1
\dot{q}, \dot{r} \in \mathbb{R}^{4 \times 1}
q˙,r˙∈R4×1 对部分性质进行说明:
q
˙
=
[
w
x
y
z
]
q
˙
∗
=
[
w
−
x
−
y
−
z
]
\dot{q} = \left[ \begin{matrix} w & x & y & z \end{matrix} \right] \\ \dot{q}^* = \left[ \begin{matrix} w & -x & -y & -z \end{matrix} \right]
q˙=[wxyz]q˙∗=[w−x−y−z]
两个四元数之间的相乘, 可以等价为一个 4×4 矩阵和一个长度为 4 的向量之间的叉乘:
q
˙
r
˙
=
Q
r
˙
=
[
w
−
x
−
y
−
z
x
w
−
z
y
y
z
w
−
x
z
−
y
x
w
]
r
˙
r
˙
q
˙
=
Q
ˉ
r
˙
=
[
w
−
x
−
y
−
z
x
w
z
−
y
y
−
z
w
x
z
y
−
x
w
]
r
˙
\dot{q} \dot{r} = Q\dot{r} = \left[ \begin{matrix} w & -x & -y & -z \\ x & w & -z & y \\ y & z & w & -x \\ z & -y & x & w \end{matrix} \right] \dot{r}\\ \dot{r} \dot{q} = \bar{Q} \dot{r} = \left[ \begin{matrix} w & -x & -y & -z \\ x & w & z & -y \\ y & -z & w & x \\ z & y & -x & w \end{matrix} \right] \dot{r}
q˙r˙=Qr˙=
wxyz−xwz−y−y−zwx−zy−xw
r˙r˙q˙=Qˉr˙=
wxyz−xw−zy−yzw−x−z−yxw
r˙
Q
,
Q
ˉ
Q, \bar{Q}
Q,Qˉ 为正交矩阵, 四元数
q
˙
\dot{q}
q˙ 的共轭
q
˙
∗
\dot{q}^*
q˙∗ 则分别对应
Q
T
,
Q
ˉ
T
Q^T, \bar{Q}^T
QT,QˉT:
q
˙
r
˙
q
˙
∗
=
(
Q
r
˙
)
q
˙
∗
=
Q
ˉ
T
Q
r
˙
=
[
A
11
A
22
A
23
A
24
A
32
A
33
A
34
A
42
A
43
A
44
]
r
˙
\dot{q} \dot{r} \dot{q}^* = (Q \dot{r}) \dot{q}^* = \bar{Q}^TQ \dot{r} = \left[ \begin{matrix} A_{11} \\ & A_{22} & A_{23} & A_{24} \\ & A_{32} & A_{33} & A_{34} \\ & A_{42} & A_{43} & A_{44} \end{matrix} \right] \dot{r}
q˙r˙q˙∗=(Qr˙)q˙∗=QˉTQr˙=
A11A22A32A42A23A33A43A24A34A44
r˙
如果四元数
r
˙
\dot{r}
r˙ 为纯虚数 (即
w
=
0
w=0
w=0, 看作空间点), 则
q
˙
r
˙
q
˙
∗
\dot{q} \dot{r} \dot{q}^*
q˙r˙q˙∗ 也为纯虚数 (看作旋转), 将
D
D
D 等价为:
maximize
:
D
=
∑
i
=
1
n
q
˙
r
˙
l
,
i
q
˙
∗
⋅
r
˙
r
,
i
=
∑
i
=
1
n
(
Q
ˉ
T
Q
r
˙
l
,
i
)
T
r
˙
r
,
i
=
∑
i
=
1
n
(
r
˙
l
,
i
T
Q
T
)
(
Q
ˉ
r
˙
r
,
i
)
=
∑
i
=
1
n
(
R
ˉ
l
,
i
q
˙
)
T
(
R
r
,
i
q
˙
)
=
q
˙
T
(
∑
i
=
1
n
R
ˉ
l
,
i
T
R
r
,
i
)
q
˙
=
q
˙
T
N
q
˙
\text{maximize}: D = \sum_{i=1}^n \dot{q} \dot{r}_{l, i} \dot{q}^* \cdot \dot{r}_{r, i} = \sum_{i=1}^n (\bar{Q}^T Q \dot{r}_{l, i})^T \dot{r}_{r, i} \\ = \sum_{i=1}^n (\dot{r}_{l, i}^T Q^T) (\bar{Q} \dot{r}_{r, i}) = \sum_{i=1}^n (\bar{R}_{l, i} \dot{q})^T (R_{r, i} \dot{q}) \\ = \dot{q}^T (\sum_{i=1}^n \bar{R}_{l, i}^T R_{r, i}) \dot{q} = \dot{q}^T N \dot{q}
maximize:D=i=1∑nq˙r˙l,iq˙∗⋅r˙r,i=i=1∑n(QˉTQr˙l,i)Tr˙r,i=i=1∑n(r˙l,iTQT)(Qˉr˙r,i)=i=1∑n(Rˉl,iq˙)T(Rr,iq˙)=q˙T(i=1∑nRˉl,iTRr,i)q˙=q˙TNq˙
为了更快地计算
N
N
N, 定义查找表:
LUT
=
[
S
x
x
S
x
y
S
x
z
S
y
x
S
y
y
S
y
z
S
z
x
S
z
y
S
z
z
]
S
x
x
=
∑
i
=
1
n
x
l
,
i
′
x
r
,
i
′
,
S
x
y
=
∑
i
=
1
n
x
l
,
i
′
y
r
,
i
′
N
=
∑
i
=
1
n
R
ˉ
l
,
i
T
R
r
,
i
=
[
S
x
x
+
S
y
y
+
S
z
z
S
y
z
−
S
z
y
S
z
x
−
S
x
z
S
x
y
−
S
y
x
S
y
z
−
S
z
y
S
x
x
−
S
y
y
−
S
z
z
S
x
y
+
S
y
x
S
z
x
+
S
x
z
S
z
x
−
S
x
z
S
x
y
+
S
y
x
−
S
x
x
+
S
y
y
−
S
z
z
S
y
z
+
S
z
y
S
x
y
−
S
y
x
S
z
x
+
S
x
z
S
y
z
+
S
z
y
−
S
x
x
−
S
y
y
+
S
z
z
]
\text{LUT} = \left[ \begin{matrix} S_{xx} & S_{xy} & S_{xz} \\ S_{yx} & S_{yy} & S_{yz} \\ S_{zx} & S_{zy} & S_{zz} \end{matrix} \right] \\ S_{xx} = \sum_{i=1}^n x'_{l, i} x'_{r, i}, S_{xy} = \sum_{i=1}^n x'_{l, i} y'_{r, i} \\ N = \sum_{i=1}^n \bar{R}_{l, i}^T R_{r, i} = \left[ \begin{matrix} S_{xx} + S_{yy} + S_{zz} & S_{yz} - S_{zy} & S_{zx} - S_{xz} & S_{xy} - S_{yx} \\ S_{yz} - S_{zy} & S_{xx} - S_{yy} - S_{zz} & S_{xy} + S_{yx} & S_{zx} + S_{xz} \\ S_{zx} - S_{xz} & S_{xy} + S_{yx} & -S_{xx} + S_{yy} - S_{zz} & S_{yz} + S_{zy} \\ S_{xy} - S_{yx} & S_{zx} + S_{xz} & S_{yz} + S_{zy} & -S_{xx} - S_{yy} + S_{zz} \end{matrix} \right]
LUT=
SxxSyxSzxSxySyySzySxzSyzSzz
Sxx=i=1∑nxl,i′xr,i′,Sxy=i=1∑nxl,i′yr,i′N=i=1∑nRˉl,iTRr,i=
Sxx+Syy+SzzSyz−SzySzx−SxzSxy−SyxSyz−SzySxx−Syy−SzzSxy+SyxSzx+SxzSzx−SxzSxy+Syx−Sxx+Syy−SzzSyz+SzySxy−SyxSzx+SxzSyz+Szy−Sxx−Syy+Szz
N
N
N 是个对称阵, 在特征值分解后可以得到 4 个特征向量 (单位向量), 而且:
x
T
N
x
=
λ
x^T N x = \lambda
xTNx=λ
所以, 最大特征值所对应的特征向量即为使
D
D
D 最大的单位四元数
q
˙
\dot{q}
q˙, 根据以下公式可求得旋转矩阵:
R
=
(
Q
ˉ
T
Q
)
2
:
4
,
2
:
4
R = (\bar{Q}^TQ)_{2:4, 2:4}
R=(QˉTQ)2:4,2:4
C++ 实现
Sophus::Sim3f align_trajectory(const std::vector<Eigen::Vector3f> &pts1,
const std::vector<Eigen::Vector3f> &pts2) {
assert(pts1.size() == pts2.size());
int n = pts1.size();
// 计算质心
Eigen::Vector3d centroid1 = Eigen::Vector3d::Zero(), centroid2 = Eigen::Vector3d::Zero();
for (int i = 0; i < n; ++i) {
Eigen::Vector3d p1 = pts1[i].cast<double>(), p2 = pts2[i].cast<double>();
centroid1 += p1;
centroid2 += p2;
}
centroid1 /= n;
centroid2 /= n;
// 零均值化, 计算尺度因子、lut
Eigen::Matrix3<long double> lut = Eigen::Matrix3<long double>::Zero();
long double Sl = 0, Sr = 0;
for (int i = 0; i < n; ++i) {
Eigen::Vector3d p1 = pts1[i].cast<double>() - centroid1, p2 = pts2[i].cast<double>() - centroid2;
// 如果 pts 数值较大, 可能会溢出
lut += (p1 * p2.transpose()).cast<long double>();
Sl += p1.squaredNorm();
Sr += p2.squaredNorm();
}
lut /= n;
double Sxx = lut(0, 0), Sxy = lut(0, 1), Sxz = lut(0, 2),
Syx = lut(1, 0), Syy = lut(1, 1), Syz = lut(1, 2),
Szx = lut(2, 0), Szy = lut(2, 1), Szz = lut(2, 2);
double s = std::sqrt(Sr / Sl);
// 单位四元数
Eigen::Matrix4d N;
N << (Sxx + Syy + Szz) / 2, Syz - Szy, Szx - Sxz, Sxy - Syx,
0, (Sxx - Syy - Szz) / 2, Syx + Sxy, Szx + Sxz,
0, 0, (-Sxx + Syy - Szz) / 2, Szy + Syz,
0, 0, 0, (-Sxx - Syy + Szz) / 2;
N += N.transpose().eval();
Eigen::SelfAdjointEigenSolver<Eigen::Matrix4d> eig(N);
Eigen::Vector4d q = eig.eigenvectors().col(3);
// 代入数值
float w = q[0], x = q[1], y = q[2], z = q[3];
Eigen::Matrix3f R;
R << w * w + x * x - y * y - z * z, -2 * w * z + 2 * x * y, 2 * w * y + 2 * x * z,
2 * w * z + 2 * x * y, w * w - x * x + y * y - z * z, -2 * w * x + 2 * y * z,
-2 * w * y + 2 * x * z, 2 * w * x + 2 * y * z, w * w - x * x - y * y + z * z;
Eigen::Vector3f t = centroid2.cast<float>() - s * R * centroid1.cast<float>();
return Sophus::Sim3f(s, Eigen::Quaternionf(R), t);
}
Python 实现
import numpy as np
from scipy.spatial import transform
SO3 = transform.Rotation # 特殊正交群
def align_trajectory(pts1: np.ndarray, pts2: np.ndarray):
""" Closed-form solution of absolute orientation using unit quaternions """
assert pts1.ndim == 2 and pts1.shape[1] == 3 and pts1.shape == pts2.shape
pts1, pts2 = map(np.float64, (pts1, pts2))
# 尺度因子
centroid1, centroid2 = pts1.mean(axis=0), pts2.mean(axis=0)
pts1, pts2 = pts1 - centroid1, pts2 - centroid2
s = np.sqrt(np.square(pts2).sum() / np.square(pts1).sum())
# 单位四元数
Sxx, Sxy, Sxz, Syx, Syy, Syz, Szx, Szy, Szz = (pts1.T @ pts2).flatten()
sumn = np.array([[(Sxx + Syy + Szz) / 2, Syz - Szy, Szx - Sxz, Sxy - Syx],
[0, (Sxx - Syy - Szz) / 2, Syx + Sxy, Szx + Sxz],
[0, 0, (- Sxx + Syy - Szz) / 2, Szy + Syz],
[0, 0, 0, (- Sxx - Syy + Szz) / 2]])
sumn += sumn.T
eig_val, eig_vec = np.linalg.eig(sumn)
w, x, y, z = eig_vec[:, eig_val.argmax()]
# 代入数值
R = SO3.from_matrix(
[[w ** 2 + x ** 2 - y ** 2 - z ** 2, -2 * w * z + 2 * x * y, 2 * w * y + 2 * x * z],
[2 * w * z + 2 * x * y, w ** 2 - x ** 2 + y ** 2 - z ** 2, -2 * w * x + 2 * y * z],
[-2 * w * y + 2 * x * z, 2 * w * x + 2 * y * z, w ** 2 - x ** 2 - y ** 2 + z ** 2]]
)
t = centroid2 - s * R.as_matrix() @ centroid1
return s, R, t
if __name__ == "__main__":
s = 2.5
R = SO3.from_quat([0.8006, 0.1601, 0.3202, 0.4804]).as_matrix()
t = np.array([0.1, 0.2, 0.3])
pts1 = np.random.uniform(-1, 1, (100, 3))
pts2 = (s * R @ pts1.T).T + t
s, R, t = align_trajectory(pts1, pts2)
e = (s * R.as_matrix() @ pts1.T).T + t - pts2
print(np.square(e).sum())
print(s, R.as_quat(), t)