Hanjie's Blog

一只有理想的羊驼

由于Chrome从v140版本后就不再支持manifest-v2,导致很多插件无法使用。

降低版本到v139

从这里chrome-versions寻找到版本为139.0.7258.155的chrome,下载后安装并且替代掉现有的chrome。

禁止Chrome自动更新1

删除和设置权限

删除更新程序:

1
rm -rf ~/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle

将更新程序目录设置为系统级别只读:

1
sudo chflags schg ~/Library/Google/GoogleSoftwareUpdate

要恢复:

1
sudo chflags noschg ~/Library/Google/GoogleSoftwareUpdate

如果是全新系统或者新建的用户,未运行 Chrome,可以手动创建上述文件夹再设置为只读

1
2
mkdir -p ~/Library/Google/GoogleSoftwareUpdate
sudo chflags schg ~/Library/Google/GoogleSoftwareUpdate

修改hosts

手动编辑,打开终端,执行:

1
sudo vi /etc/hosts

添加:

1
2
127.0.0.1 update.googleapis.com
127.0.0.1 tools.google.com

以上两步任意一个已经可以屏蔽自动更新,同时操作更加保险。

强制打开manifest-v2 2

打开chrome后,输入:

1
chrome://flags/#temporary-unexpire-flags-m137

设置为Enabled,然后重启浏览器。再次把下面几项的按照这样配置:

1
2
3
4
chrome://flags/#extension-manifest-v2-deprecation-warning [Disabled]
chrome://flags/#extension-manifest-v2-deprecation-disabled [Disabled]
chrome://flags/#extension-manifest-v2-deprecation-unsupported [Disabled]
chrome://flags/#allow-legacy-mv2-extensions [Enabled]

最后重启一次浏览器, 就可以强制启用了。


  1. https://sysin.org/blog/disable-chrome-auto-update/↩︎

  2. https://us.v2ex.com/t/1144634↩︎

在三维空间里讨论旋转的时候,有两种常见的视角——坐标轴旋转和坐标变换。虽然它们可能看似相似,但在实际应用上存在显著区别。以下是它们的详细解释:

1. 坐标轴旋转(Active Rotation)

坐标轴旋转有时也被称为 "矢量旋转" 或 "主动旋转" (Active Rotation)。在这种解析下,我们旋转的是对象本身。换句话说,坐标轴保持不变,旋转的是矢量或点。

例子: 如果我们绕 y 轴逆时针旋转一个矢量 v = (1, 0, 0):

旋转矩阵$ R_y(90^)$ 为:

\[ R_y(90^\circ) = \begin{pmatrix} 0 & 0 & 1 \\ 0 & 1 & 0 \\ -1 & 0 & 0 \end{pmatrix} \]

应用旋转矩阵到矢量 v:

\[ v' = R_y(90^\circ) \begin{pmatrix} 1 \\ 0 \\ 0 \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ -1 \end{pmatrix} \]

这里 v' 是旋转后的矢量。这表示在原始坐标系中,方向 (1, 0, 0) 旋转 90 度后变为方向 (0, 0, -1)。

2. 坐标变换(Passive Rotation)

坐标变换有时也被称为 "坐标系旋转" 或 "被动旋转" (Passive Rotation)。在这种解析下,我们其实是在旋转坐标系本身,矢量或点保持不变。

假设我们仍然使用绕 y 轴逆时针旋转 90 度的例子:

新的坐标系中的基向量会变成: - 新的 x 轴方向:之前的 (1, 0, 0) 旋转后现在方向是 (0, 0, -1)。 - 新的 z 轴方向:之前的 (0, 0, 1) 旋转后现在方向是 (1, 0, 0)。

在旋转后的坐标系中,基向量变换矩阵将会是(对比原始坐标系):

\[ M = \begin{pmatrix} 0 & 0 & -1 \\ 0 & 1 & 0 \\ 1 & 0 & 0 \end{pmatrix}\]

在被动旋转的情况下,逆变换矩阵 $R_y{-1}(90) $ 应用于矢量:

\[R_y^{-1}(90^\circ) = R_y(-90^\circ) = \begin{pmatrix} 0 & 0 & -1 \\ 0 & 1 & 0 \\ 1 & 0 & 0 \end{pmatrix} \]

应用变换:

\[ v = R_y^{-1}(90^\circ) v' \]

基于这个理解,可以得到:

\[ \begin{pmatrix} 1 \\ 0 \\ 0 \end{pmatrix} = R_y^{-1}(90^\circ) \begin{pmatrix} 0 \\ 0 \\ -1 \end{pmatrix} \]

总结

  • 坐标轴旋转(Active Rotation):在这种情况下,坐标轴保持不变,旋转的是矢量。因此,对应的旋转矩阵直接应用于矢量或点。
  • 坐标变换(Passive Rotation):在这种情况下,坐标系在旋转,但矢量或点保持不变。因此,我们要应用逆旋转矩阵(反向旋转矩阵)来转换矢量。

这两个方法本质上描述了相同的几何变换,但应用的视角和方式不同。

在EIS(电子图像稳定)系统中,实现Horizon Lock功能的目的是确保相机的水平方向保持稳定,即在世界坐标系中,相机的视图水平线与地平线保持一致。根据题目定义: 世界坐标系:Z轴向上。 IMU坐标系:X轴指向设备左边,Y轴指向内(设备背面),Z轴指向设备上边,视图方向(view direction)为Y轴负方向。 输入数据:有一系列从IMU坐标系到世界坐标系的四元数数组std::vector<Eigen::Quaterniond> quats。Horizon Lock的核心是补偿相机的滚转(roll)角度,使得相机的上方向(IMU坐标系的Z轴)在世界坐标系中尽可能与世界Z轴对齐,同时保持视图方向不变。下面是实现的具体步骤:

实现思路

  1. 理解四元数作用 每个四元数 q表示从IMU坐标系到世界坐标系的旋转。对于IMU坐标系中的向量 \(\mathbf{v}_{\text{imu}}\),可以通过 \(\mathbf{v}_{\text{world}} = q \cdot \mathbf{v}_{\text{imu}}\)转换到世界坐标系(使用Eigen库的四元数运算规则)。
  2. 确定关键方向
    视图方向:在IMU坐标系中,视图方向是Y轴负方向,即 \(\begin{pmatrix} 0 \\ -1 \\ 0 \end{pmatrix}\)。在世界坐标系中,视图方向为 \(\mathbf{d} = q \cdot \begin{pmatrix} 0 \\ -1 \\ 0 \end{pmatrix}\)。 上方向:在IMU坐标系中,上方向是Z轴,即 \(\begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\)。在世界坐标系中,当前上方向为 \(q \cdot \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\)
  3. Horizon Lock目标 保持视图方向 \(\mathbf{d}\)不变,同时调整旋转,使得相机的上方向在世界坐标系中与世界Z轴 \(\begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\)的投影对齐(在与 \(\mathbf{d}\)垂直的平面内)。
  4. 调整方法 通过在IMU坐标系中围绕Y轴(视图方向的轴)施加一个额外的旋转 r,生成新的四元数 \(q_{\text{lock}} = q \cdot r\),使得:视图方向保持为 \(\mathbf{d}\)。上方向尽可能与世界Z轴对齐。

具体实现步骤

对于四元数数组中的每个四元数 q,按以下步骤处理: ### 计算视图方向 在IMU坐标系中,视图方向为 \(\begin{pmatrix} 0 \\ -1 \\ 0 \end{pmatrix}\)。使用四元数q将其转换到世界坐标系:

\[\mathbf{d} = q \cdot \begin{pmatrix} 0 \\ -1 \\ 0 \end{pmatrix}\]

这里 \(\mathbf{d}\)是世界坐标系中的视图方向向量。

计算期望的上方向\(\mathbf{up}_{\text{desired}}\)

在实现Horizon Lock功能(如在电子图像稳定系统中)时,我们需要计算一个期望的上方向\(\mathbf{up}_{\text{desired}}\),以确保相机的水平方向在世界坐标系中与地平线保持一致,同时不改变相机的视图方向\(\mathbf{d}\)

我们希望计算的期望上方向\(\mathbf{up}_{\text{desired}}\)满足以下两个条件:

  • \(\mathbf{up}_{\text{desired}}\)是世界坐标系中,相机的上方向应该指向的方向,尽可能与世界Z轴\(\begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\)对齐。
  • 为了保持视图方向不变,\(\mathbf{up}_{\text{desired}}\)必须与视图方向\(\mathbf{d}\)垂直。

其中:

  • 视图方向\(\mathbf{d}\)是相机在世界坐标系中指向的方向(通常为相机光轴的方向)。
  • 世界Z轴\(\mathbf{w} = \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\)表示世界坐标系的上方向。

为了实现Horizon Lock,我们需要调整相机的滚动(roll)角度,使得相机的上方向与\(\mathbf{up}_{\text{desired}}\)对齐,同时保持\(\mathbf{d}\)不变。

步骤 1:计算世界Z轴在视图方向\(\mathbf{d}\)上的投影 首先,我们需要将世界Z轴\(\mathbf{w}\)分解为两个分量:

  • 一个分量是\(\mathbf{w}\)\(\mathbf{d}\)方向上的投影(平行于\(\mathbf{d}\))。
  • 另一个分量是\(\mathbf{w}\)在与\(\mathbf{d}\)垂直的平面上的分量(垂直于\(\mathbf{d}\))。

\(\mathbf{w}\)\(\mathbf{d}\)上的投影公式为:

\[\text{proj}_{\mathbf{d}} \mathbf{w} = \left( \mathbf{w} \cdot \mathbf{d} \right) \mathbf{d}\]

其中:

  • \(\mathbf{w} \cdot \mathbf{d}\)\(\mathbf{w}\)\(\mathbf{d}\)的点积,计算\(\mathbf{w}\)\(\mathbf{d}\)方向上的分量大小。
  • \(\mathbf{d}\)是单位向量(如果不是,需要先归一化)。

这个投影分量\(\text{proj}_{\mathbf{d}} \mathbf{w}\)表示\(\mathbf{w}\)中与\(\mathbf{d}\)平行的部分。

步骤 2:计算世界Z轴在与\(\mathbf{d}\)垂直的平面上的分量 为了得到与\(\mathbf{d}\)垂直的分量,我们从\(\mathbf{w}\)中减去其在\(\mathbf{d}\)上的投影:

\[\mathbf{up}_{\text{desired}} = \mathbf{w} - \text{proj}_{\mathbf{d}} \mathbf{w}\]

代入投影公式:

\[\mathbf{up}_{\text{desired}} = \mathbf{w} - \left( \mathbf{w} \cdot \mathbf{d} \right) \mathbf{d}\]

这个\(\mathbf{up}_{\text{desired}}\)就是\(\mathbf{w}\)在与\(\mathbf{d}\)垂直的平面上的分量。

步骤 3:归一化\(\mathbf{up}_{\text{desired}}\)

\[\mathbf{up}_{\text{desired}} = \frac{\mathbf{up}_{\text{desired}}}{\|\mathbf{up}_{\text{desired}}\|}\]

转换到IMU坐标系

\(\mathbf{up}_{\text{desired}}\)从世界坐标系转换回IMU坐标系:

\[\mathbf{up}_{\text{desired}}^{\text{imu}} = q^{-1} \cdot \mathbf{up}_{\text{desired}}\]

由于 \(\mathbf{up}_{\text{desired}}\)\(\mathbf{d}\)垂直,且 \(q^{-1} \cdot \mathbf{d} = \begin{pmatrix} 0 \\ -1 \\ 0 \end{pmatrix}\),因此 \(\mathbf{up}_{\text{desired}}^{\text{imu}}\)的Y分量为零,即形如 \(\begin{pmatrix} a \\ 0 \\ b \end{pmatrix}\)

计算旋转角度

现在,我们需要在IMU坐标系中找到一个旋转角度\(\theta\),使得IMU坐标系中的当前上方向 \(\mathbf{up}_{\text{current}}^{\text{imu}} = \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\)(即Z轴)通过围绕Y轴旋转\(\theta\)后,与\(\mathbf{up}_{\text{desired}}^{\text{imu}}\)对齐。

由于旋转是围绕Y轴进行的,我们可以将问题简化到XZ平面上的二维旋转问题。以下是详细的推导过程:

  1. 分析旋转过程:

旋转轴是Y轴(视图方向),因此旋转只影响X和Z分量,Y分量保持不变。

在XZ平面上,当前上方向\(\mathbf{up}_{\text{current}}^{\text{imu}}\)的投影是\(\begin{pmatrix} 0 \\ 1 \end{pmatrix}\)(即Z轴)。

期望上方向\(\mathbf{up}_{\text{desired}}^{\text{imu}}\)的投影是\(\begin{pmatrix} a \\ b \end{pmatrix}\)

我们需要找到一个角度\(\theta\),使得从\(\begin{pmatrix} 0 \\ 1 \end{pmatrix}\)旋转到\(\begin{pmatrix} a \\ b \end{pmatrix}\),方向遵循右手定则。

  1. 使用\(\text{atan2}\)函数计算角度: 在二维平面上(这里是XZ平面),从向量\(\mathbf{v}_1 = \begin{pmatrix} x_1 \\ z_1 \end{pmatrix}\)到向量\(\mathbf{v}_2 = \begin{pmatrix} x_2 \\ z_2 \end{pmatrix}\)的旋转角度\(\theta\)可以通过以下公式计算:

\[\theta = \text{atan2}(x_2, z_2) - \text{atan2}(x_1, z_1)\]

角度 \(\theta_1 = \text{atan2}(y_1, x_1)\)\(\mathbf{v}_1\)相对于X轴正方向的角度。 角度 \(\theta_2 = \text{atan2}(y_2, x_2)\)\(\mathbf{v}_2\)相对于X轴正方向的角度。 两个向量之间的有向角度就是从\(\mathbf{v}_1\)\(\mathbf{v}_2\)的夹角,可以表示为:\(\text{angle} = \theta_2 - \theta_1\)

在 XZ 平面中,我们希望计算从正 Z 轴到向量的逆时针旋转角度。因此我们使用\(\text{atan2}(x, z)\)

在本问题中:

\(\mathbf{v}_1 = \begin{pmatrix} 0 \\ 1 \end{pmatrix}\),因此\(\text{atan2}(x_1, z_1) = \text{atan2}(0, 1) = 0\)\(\mathbf{v}_2 = \begin{pmatrix} a \\ b \end{pmatrix}\),因此\(\text{atan2}(x_2, z_2) = \text{atan2}(a, b)\)

代入公式,旋转角度\(\theta\)为:

\[\theta = \text{atan2}(a, b) - 0 = \text{atan2}(a, b)\]

其中:

\(a = \mathbf{up}_{\text{desired}}^{\text{imu}}.x\)(期望上方向的X分量)。 \(b = \mathbf{up}_{\text{desired}}^{\text{imu}}.z\)(期望上方向的Z分量)。

\(\text{atan2}(a, b)\)返回的角度\(\theta\)是从正Z轴到向量\(\begin{pmatrix} a \\ b \end{pmatrix}\)的旋转角度,范围在\([- \pi, \pi]\)内。

  1. 旋转方向的解释: 如果\(\theta > 0\),表示逆时针旋转(从Z轴向X轴正方向)。 如果\(\theta < 0\),表示顺时针旋转。

构造旋转四元数:

\[r = \text{Eigen::AngleAxisd}(\theta, \text{Eigen::Vector3d}(0, 1, 0))\]

生成新的四元数

将补偿旋转 \(r\)应用于原始四元数 \(q\)

\[q_{\text{lock}} = q \cdot r\]

注意:四元数相乘顺序表示先应用 \(r\)(局部旋转),再应用 \(q\)(到世界坐标系)。

代码实现

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
32
33
34
35
36
37
#include <Eigen/Geometry>
#include <vector>

std::vector<Eigen::Quaterniond> processHorizonLock(const std::vector<Eigen::Quaterniond>& quats) {
std::vector<Eigen::Quaterniond> quats_locked;
quats_locked.reserve(quats.size());

for (const auto& q : quats) {
// 步骤1:计算世界坐标系中的视图方向
Eigen::Vector3d view_dir_imu(0, -1, 0);
Eigen::Vector3d d = q * view_dir_imu;

// 步骤2:计算期望的上方向(世界坐标系)
Eigen::Vector3d world_up(0, 0, 1);
Eigen::Vector3d up_desired = world_up - world_up.dot(d) * d;

// 步骤3:归一化
up_desired.normalize();

// 步骤4:转换到IMU坐标系
Eigen::Vector3d up_desired_imu = q.inverse() * up_desired;

// 步骤5:计算旋转角度θ
double theta = std::atan2(up_desired_imu.x(), up_desired_imu.z());

// 步骤6:构造滚转补偿四元数
Eigen::AngleAxisd r(theta, Eigen::Vector3d(0, 1, 0));
Eigen::Quaterniond r_quat(r);

// 步骤7:生成新的四元数
Eigen::Quaterniond q_lock = q * r_quat;

quats_locked.push_back(q_lock);
}

return quats_locked;
}
0%