Skip to content

Commit

Permalink
增加扩展卡尔曼滤波模块,并添加相关文档和测试
Browse files Browse the repository at this point in the history
  • Loading branch information
zhaoxi-scut committed Apr 26, 2024
1 parent 46e5b44 commit 50c5cfa
Show file tree
Hide file tree
Showing 25 changed files with 741 additions and 172 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

**机器人控制与视觉库**

| 构建配置 | 编译器/环境 | Github Actions 工作流状态 |
| :------: | :---------: | :----------------------------------------------------------: |
| CMake | GCC 12.3.0 | [![1.x in Linux](https://github.com/cv-rmvl/rmvl/actions/workflows/linux-1.x.yml/badge.svg)](https://github.com/cv-rmvl/rmvl/actions/workflows/linux-1.x.yml) |
| 构建配置 | 编译器/环境 | Github Actions 工作流状态 |
| :------: | :-------------------------------: | :--------------------------------------: |
| CMake | GCC 7.5.0 x86_64-linux-gnu<br />GCC 9.4.0 x86_64-linux-gnu<br />GCC 12.3.0 x86_64-linux-gnu | [![1.x in Linux](https://github.com/cv-rmvl/rmvl/actions/workflows/linux-1.x.yml/badge.svg)](https://github.com/cv-rmvl/rmvl/actions/workflows/linux-1.x.yml) |

RMVL 最初是面向 RoboMaster 赛事的视觉库,因此称为 RoboMaster Vision Library,现计划并逐步完善与机器人相关的视觉、控制、通信的功能,旨在打造适用范围广、使用简洁、架构统一、功能强大的视觉控制一体库。

Expand Down
2 changes: 1 addition & 1 deletion doc/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<!--#include virtual="/google-search.html"-->
<script type="text/javascript" src="/version.js"></script>
<script type="text/javascript" src="/docs/version.js"></script>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
Expand Down
183 changes: 183 additions & 0 deletions doc/tutorials/modules/core/ekf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
扩展卡尔曼滤波 {#tutorial_modules_ekf}
============

@author 赵曦
@date 2024/04/19
@version 1.0
@brief 扩展卡尔曼滤波

@prev_tutorial{tutorial_modules_kalman}

@next_tutorial{tutorial_modules_union_find}

@tableofcontents

------

相关类 rm::ExtendedKalmanFilter

\f[
\def\red#1{\color{red}{#1}}
\def\teal#1{\color{teal}{#1}}
\def\green#1{\color{green}{#1}}
\def\transparent#1{\color{transparent}{#1}}
\def\orange#1{\color{orange}{#1}}
\def\Var{\mathrm{Var}}
\def\Cov{\mathrm{Cov}}
\def\tr{\mathrm{tr}}
\def\fml#1{\text{(#1)}}
\def\ptl#1#2{\frac{\partial#1}{\partial#2}}
\f]

在阅读本教程前,请确保已经熟悉标准的 @ref tutorial_modules_kalman ,因为核心公式不变,只是在原来的基础上增加了非线性函数线性化的部分。

### 1. 非线性函数的线性化

对于一个线性系统,可以用状态空间方程描述其运动过程

\f[\begin{align}\dot{\pmb x}&=A\pmb x+B\pmb u\\\pmb y&=C\pmb x\end{align}\tag{1-1}\f]

离散化,并考虑噪声后可以写为

\f[\begin{align}\dot{\pmb x}_k&=A\pmb x_{k-1}+B\pmb u_{k-1}+\pmb w_{k-1}&&\pmb w_{k-1}\sim N(0,Q)\tag{1-2a}\\
\pmb z_k&=H\pmb x_{k-1}+\pmb v_k&&\pmb v_k\sim N(0,R)\tag{1-2b}\end{align}\f]

但对于一个非线性系统,我们就无法使用矩阵来表示了,我们需要写为

\f[\left\{\begin{align}\dot{\pmb x}_k&=\pmb f_A(\pmb x_{k-1},\pmb u_{k-1},\pmb w_{k-1})\\
\pmb z_k&=\pmb f_H(\pmb x_{k-1},\pmb v_{k-1})\end{align}\right.\tag{1-3}\f]

其中,\f$\pmb f_A\f$ 和 \f$\pmb f_H\f$ 都为非线性函数。我们在非线性函数中同样考虑了噪声,但是对于状态量以及观测量本身的噪声而言,<span style="color: red">正态分布的随机变量通过非线性系统后就不再服从正态分布了</span>。因此我们可以利用 **泰勒展开** ,将非线性系统线性化,即

\f[f(x)\approx f(x_0)+\frac{\mathrm df}{\mathrm dx}(x-x_0)\tag{1-4}\f]

对于多元函数而言,泰勒展开可以写为

\f[f(x,y,z)\approx f(x_0,y_0,z_0)+\begin{bmatrix}f'_x(x_0,y_0,z_0)&f'_y(x_0,y_0,z_0)&f'_z(x_0,y_0,z_0)\end{bmatrix}\begin{bmatrix}x-x_0\\y-y_0\\z-z_0\end{bmatrix}\tag{1-5a}\f]


\f[f(\pmb x)\approx f(\pmb x_0)+\ptl fx(\pmb x-\pmb x_0)=f(\pmb x_0)+\nabla f(\pmb x_0)(\pmb x-\pmb x_0)\tag{1-5b}\f]

#### 1.1 状态方程线性化 {#ekf_state_function_linearization}

对公式 \f$\fml{1-2a}\f$ 在 \f$\hat{\pmb x}_{k-1}\f$ 处进行线性化,即选取 \f$\text{k-1}\f$ 时刻的后验状态估计作为展开点,有

\f[\pmb x_k=\pmb f_A(\hat{\pmb x}_{k-1},\pmb u_{k-1},\pmb w_{k-1})+J_A(\pmb x_{k-1}-\hat x_{k-1})+W\pmb w_{k-1}\tag{1-6}\f]

令 \f$\pmb w_{k-1}=\pmb 0\f$,则 \f$f_A(\hat{\pmb x}_{k-1},\pmb u_{k-1},\pmb w_{k-1})=f_A(\hat{\pmb x}_{k-1},\pmb u_{k-1},\pmb 0)\stackrel{\triangle}=\tilde{\pmb x}_{k-1}\f$,有

\f[\red{\pmb x_k=\tilde{\pmb x}_{k-1}+J_A(\pmb x_{k-1}-\hat x_{k-1})+W\pmb w_{k-1}\qquad W\pmb w_{k-1}\sim N(0,WQW^T)\tag{1-7}}\f]

其中

\f[\begin{align}J_A&=\left.\ptl{f_A}{\pmb x}\right|_{(\hat{\pmb x}_{k-1},\pmb u_{k-1})}=\begin{bmatrix}\ptl{{f_A}_1}{x_1}&\ptl{{f_A}_1}{x_2}&\cdots&\ptl{{f_A}_1}{x_n}\\\ptl{{f_A}_2}{x_1}&\ptl{{f_A}_2}{x_2}&\cdots&\ptl{{f_A}_2}{x_n}\\\vdots&\vdots&\ddots&\vdots\\\ptl{{f_A}_n}{x_1}&\ptl{{f_A}_n}{x_2}&\cdots&\ptl{{f_A}_n}{x_n}\end{bmatrix}\\
W&=\left.\ptl{f_A}{\pmb w}\right|_{(\hat{\pmb w}_{k-1},\pmb u_{k-1})}\end{align}\f]

#### 1.2 观测方程线性化 {#ekf_observation_function_linearization}

对公式 \f$\fml{1-2b}\f$ 在 \f$\hat{\pmb x}_k\f$ 处进行线性化,有

\f[\pmb z_k=\pmb f_H(\tilde{\pmb x}_k,\pmb v_k)+J_H(\pmb x_k-\tilde x_k)+V\pmb v_k\tag{1-8}\f]

令 \f$\pmb v_k=\pmb 0\f$,则 \f$f_H(\tilde{\pmb x}_k,\pmb v_k)=f_H(\tilde{\pmb x}_k,\pmb 0)\stackrel{\triangle}=\tilde{\pmb z}_k\f$,有

\f[\red{\pmb z_k=\tilde{\pmb z}_k+J_H(\pmb x_k-\tilde x_k)+V\pmb v_k\qquad V\pmb v_k\sim N(0,VRV^T)\tag{1-9}}\f]

其中

\f[J_H=\left.\ptl{f_H}{\pmb x}\right|_{\tilde{\pmb x}_k},\qquad V=\left.\ptl{f_H}{\pmb v}\right|_{\tilde{\pmb x}_k}\f]

### 2. 扩展卡尔曼滤波

#### 2.1 公式汇总

根据卡尔曼滤波的 @ref kalman_filter_formulas 可以相应的改写非线性系统下的卡尔曼滤波公式,从而得到如下的扩展卡尔曼滤波公式。

**① 预测**

1. <span style="color: teal">先验状态估计</span>
\f[\hat{\pmb x}_k^-=\pmb f_A(\pmb x_{k-1},\pmb u_{k-1},\pmb 0)\f]

2. <span style="color: teal">计算先验误差协方差</span>
\f[P_k^-=J_AP_{k-1}J_A^T+WQW^T\f]

**② 校正(更新)**

1. <span style="color: teal">计算卡尔曼增益</span>
\f[K_k=P_k^-J_H^T\left(J_HP_k^-J_H^T+VRV^T\right)^{-1}\f]

2. <span style="color: teal">后验状态估计</span>
\f[\hat{\pmb x}_k=\hat{\pmb x}_k^-+K_k\left[\pmb z_k-\pmb f_H(\hat{\pmb x}_k^-,\pmb 0)\right]\f]

3. <span style="color: teal">更新后验误差协方差</span>
\f[P_k=(I-K_kJ_H)P_k^-\f]

#### 2.2 EKF 模块的使用

下面拿扩展卡尔曼模块单元测试的内容举例子

```cpp
#include <cstdio>
#include <random>
#include <rmvl/core/kalman.hpp>

int main()
{
// 状态量 x = [ cx, cy, θ, ω, r ]ᵀ
// ┌ cx ┌ 1 0 0 0 0 ┐
// │ cy │ 0 1 0 0 0 │
// 状态方程 F = │ θ + ωT Ja = │ 0 0 1 T 0 │ = A
// │ ω │ 0 0 0 1 0 │
// └ r └ 0 0 0 0 1 ┘
// 观测量 z = [ px, py, θ ]ᵀ
// ┌ cx + rcosθ ┌ 1 0 -rsinθ 0 cosθ ┐
// 观测方程 H = │ cy + rsinθ Jh = │ 0 1 rcosθ 0 sinθ │
// └ θ └ 0 0 1 0 0 ┘

// 正态分布噪声
std::default_random_engine ng;
std::normal_distribution<double> err{0, 1};

// 创建扩展卡尔曼滤波
rm::EKF53d ekf;
ekf.init({0, 0, 0, 0, 150}, 1e5);
ekf.setQ(1e-1 * cv::Matx<double, 5, 5>::eye());
ekf.setR(cv::Matx33d::diag({1e-3, 1e-3, 1e-3}));
double t{0.01};
// 设置状态方程(这里的例子是线性的,但一般都是非线性的)
ekf.setFa([=](const cv::Matx<double, 5, 1> &x) -> cv::Matx<double, 5, 1> {
return {x(0),
x(1),
x(2) + x(3) * t,
x(3),
x(4)};
});
// 设置观测方程
ekf.setFh([=](const cv::Matx<double, 5, 1> &x) -> cv::Matx<double, 3, 1> {
return {x(0) + x(4) * std::cos(x(2)),
x(1) + x(4) * std::sin(x(2)),
x(2)};
});

while (true)
{
// 预测部分,设置状态方程 Jacobi 矩阵,获取先验状态估计
ekf.setJa({1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, t, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1});
auto x_ = ekf.predict();
// 更新部分,设置观测方程 Jacobi 矩阵,获取后验状态估计
ekf.setJh({1, 0, -x_(4) * std::sin(x_(2)), 0, std::cos(x_(2)),
0, 1, x_(4) * std::cos(x_(2)), 0, std::sin(x_(2)),
0, 0, 1, 0, 0});
// 以 20 为半径,0.02/T 为角速度的圆周运动(图像上是顺时针),并人为加上观测噪声
auto x = ekf.correct({500 + 200 * std::cos(0.02 * i) + err(ng),
500 + 200 * std::sin(0.02 * i) + err(ng),
0.02 * i + 0.01 * err(ng)});
printf("x = [%.3f, %.3f, %.3f, %.3f, %.3f]\n", x(0), x(1), x(2), x(3), x(4));
}
}
```
34 changes: 18 additions & 16 deletions doc/tutorials/modules/core/kalman.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

@prev_tutorial{tutorial_modules_runge_kutta}

@next_tutorial{tutorial_modules_union_find}
@next_tutorial{tutorial_modules_ekf}

@tableofcontents

Expand All @@ -26,7 +26,7 @@
\def\Var{\mathrm{Var}}
\def\Cov{\mathrm{Cov}}
\def\tr{\mathrm{tr}}
\def\formular#1{\text{(#1)}}
\def\fml#1{\text{(#1)}}
\f]

### 1. 卡尔曼滤波
Expand All @@ -47,9 +47,9 @@

\f[\hat x_k=\frac{z_1+z_2+\cdots+z_k}k\tag{1-1}\f]

这是一条非常简单的取算数平均的公式,但是我们为了估计硬币的长度,需要用到所有的观测值,例如我们已经测了 5 次硬币的长度,并且使用公式 \f$\formular{1-1}\f$ 得到了第 5 次的平均值(长度的估计值),在测完第 6 次长度准备计算第 6 次估计值的时候,使用公式 \f$\formular{1-1}\f$ 还需要重新使用前 5 次的观测值。当观测次数非常高的时候,计算的压力就逐渐高起来了。
这是一条非常简单的取算数平均的公式,但是我们为了估计硬币的长度,需要用到所有的观测值,例如我们已经测了 5 次硬币的长度,并且使用公式 \f$\fml{1-1}\f$ 得到了第 5 次的平均值(长度的估计值),在测完第 6 次长度准备计算第 6 次估计值的时候,使用公式 \f$\fml{1-1}\f$ 还需要重新使用前 5 次的观测值。当观测次数非常高的时候,计算的压力就逐渐高起来了。

针对这一问题,我们可以改写公式 \f$\formular{1-1}\f$
针对这一问题,我们可以改写公式 \f$\fml{1-1}\f$

\f[\begin{align}\hat x_k&=\frac1k(z_1+z_2+\cdots+z_k)\\
&=\frac1k(z_1+z_2+\cdots+z_{k-1})+\frac1kz_k\\
Expand All @@ -59,7 +59,7 @@

这样我们就把硬币长度的估计值,改写成由上一次估计值和当前观测值共同作用的形式。并且我们发现,随着测量次数 \f$k\f$ 增大,\f$\frac1k\f$ 趋向于 \f$0\f$,\f$\hat x_k\f$ 趋向于 \f$\hat x_{k-1}\f$,这也就是说,随着 \f$k\f$ 增长,测量结果将不再重要。

为了不失一般性,我们把公式 \f$\formular{1-2a}\f$ 的结果改写成以下形式。
为了不失一般性,我们把公式 \f$\fml{1-2a}\f$ 的结果改写成以下形式。

\f[\boxed{\hat x_k=\hat x_{k-1}+\red{K_k}(z_k-\hat x_{k-1})}\tag{1-2b}\f]

Expand All @@ -74,7 +74,7 @@

#### 1.3 数据融合 {#kalman_data_fusion}

现在我们可以使用公式 \f$\formular{1-2b}\f$ 的思想来研究数据融合。如果有一辆车以恒定速度行驶,现在得到了两个数据:
现在我们可以使用公式 \f$\fml{1-2b}\f$ 的思想来研究数据融合。如果有一辆车以恒定速度行驶,现在得到了两个数据:

- 根据匀速公式计算得到的汽车当前位置(算出来的,记作 \f$\teal{x_1}\f$)
- 汽车的当前距离传感器数据(测出来的,记作 \f$\red{x_2}\f$)
Expand All @@ -83,7 +83,7 @@

由于自身的原因,它的位置并不是由匀速运动公式得到的精确位置,它的距离传感器数据也不完全准确。两者都有一定的误差。那么我们现在如何估计汽车的实际位置呢?

我们使用公式 \f$\formular{1-2b}\f$ 的思想,得到估计值
我们使用公式 \f$\fml{1-2b}\f$ 的思想,得到估计值

\f[\hat x=\teal{x_1}+\green{K_k}(\red{x_2}-\teal{x_1})\tag{1-4}\f]

Expand Down Expand Up @@ -219,7 +219,7 @@ e_ne_1&e_ne_2&\cdots&e_n^2\end{bmatrix}=\green{E\left(\pmb e\pmb e^T\right)}\tag
\pmb z_k&=H\pmb x_k
\end{align}\right.\tag{1-13b}\f]

这其实是个不准确的结果,因为如果我们考虑上噪声,公式 \f$\formular{1-13b}\f$ 应该改写为
这其实是个不准确的结果,因为如果我们考虑上噪声,公式 \f$\fml{1-13b}\f$ 应该改写为

\f[\left\{\begin{align}
\pmb x_k&=A\pmb x_{k-1}+B\pmb u_{k-1}\red{+\pmb w_{k-1}}&p(\pmb w)\sim N(0,Q)\\
Expand All @@ -237,7 +237,7 @@ e_ne_1&e_ne_2&\cdots&e_n^2\end{bmatrix}=\green{E\left(\pmb e\pmb e^T\right)}\tag
\vdots&\vdots&\ddots&\vdots\\\sigma_{v_n}\sigma_{v_1}&\sigma_{v_n}\sigma_{v_2}&\cdots&\sigma_{v_n}^2\end{bmatrix}\f]
称为测量噪声协方差矩阵

但是,这两个误差我们无从得知,我们只能使用公式 \f$\formular{1-13b}\f$ 的形式进行近似估计,通过 \f$\pmb x_k=A\pmb x_{k-1}+B\pmb u_{k-1}\f$ 算出来的 \f$\pmb x_k\f$ 称为<span style="color: red">先验状态估计</span>,一般写为
但是,这两个误差我们无从得知,我们只能使用公式 \f$\fml{1-13b}\f$ 的形式进行近似估计,通过 \f$\pmb x_k=A\pmb x_{k-1}+B\pmb u_{k-1}\f$ 算出来的 \f$\pmb x_k\f$ 称为<span style="color: red">先验状态估计</span>,一般写为

\f[\red{\hat{\pmb x}_k^-=A\pmb x_{k-1}+B\pmb u_{k-1}\tag{1-14}}\f]

Expand All @@ -249,7 +249,7 @@ e_ne_1&e_ne_2&\cdots&e_n^2\end{bmatrix}=\green{E\left(\pmb e\pmb e^T\right)}\tag

@note 对于一个线性方程组 \f[A\pmb x=\pmb b\f]必定存在最小二乘解 \f[\pmb x=(A^TA)^{-1}A^T\pmb b\f]可以令 \f$A^+=(A^TA)^{-1}A^T\f$ 来表示 Moore-Penrose 广义逆,即\f[\pmb x=A^+\pmb b\f]当 \f$A\f$ 可逆时,\f$A^+=A^{-1}\f$.

目前的两个结果 \f$\hat{\pmb x}_k^-\f$ 和 \f$\hat{\pmb x}_{k_{MEA}}\f$ 都不准确,因此可以回顾 @ref kalman_data_fusion 的部分,在公式 \f$\formular{1-4}\f$ 中使用了算出来的 \f$\teal{x_1}\f$ 和测出来的 \f$\red{x_2}\f$ 得到了最优估计值 \f$\hat x\f$,为此我们可以仿照这一步骤来求出离散系统状态的最优估计值 \f$\hat{\pmb x}_k\f$,称为<span style="color: red">后验状态估计</span>。
目前的两个结果 \f$\hat{\pmb x}_k^-\f$ 和 \f$\hat{\pmb x}_{k_{MEA}}\f$ 都不准确,因此可以回顾 @ref kalman_data_fusion 的部分,在公式 \f$\fml{1-4}\f$ 中使用了算出来的 \f$\teal{x_1}\f$ 和测出来的 \f$\red{x_2}\f$ 得到了最优估计值 \f$\hat x\f$,为此我们可以仿照这一步骤来求出离散系统状态的最优估计值 \f$\hat{\pmb x}_k\f$,称为<span style="color: red">后验状态估计</span>。

\f[\begin{align}\hat{\pmb x}_k&=\hat{\pmb x}_k^-+\green{G_k}(\hat{\pmb x}_{k_{MEA}}-\hat{\pmb x}_k^-)\\
&=\hat{\pmb x}_k^-+\green{G_k}(H^+\pmb z_k-\hat{\pmb x}_k^-)\tag{1-16}\end{align}\f]
Expand Down Expand Up @@ -324,7 +324,7 @@ P_k^-对称\quad&=P_k^--K_kHP_k^--\left(K_kHP_k^-\right)^T+K_kHP_k^-H^TK_k^T+K_k
\f[\begin{align}\frac{\mathrm d\tr(ABA^T)}{A}&=AB+AB^T\\
当B对称时\quad&=2AB\end{align}\tag{1-23b}\f]

那么,公式\f$\formular{1-22}\f$可以写为
那么,公式\f$\fml{1-22}\f$可以写为

\f[\begin{align}2\frac{\mathrm d\tr(K_kHP_k^-)}{\mathrm dK_k}
&=\frac{\mathrm d\tr(K_kHP_k^-H^TK_k^T)}{\mathrm dK_k}+\frac{\mathrm d\tr(K_kRK_k^T)}{\mathrm dK_k}\\
Expand Down Expand Up @@ -368,21 +368,21 @@ P_k^-H^T&=K_k\left(HP_k^-H^T+R\right)

在求解 \f$P_k^-\f$ 的时候用到了 \f$P_{k-1}\f$,因此需要进一步求解 \f$P_k\f$,从而为下一次 \f$P_{k+1}^-\f$ 所使用。

@ref kalman_gain_derivate 的公式 \f$\formular{1-20}\f$ 可以得到
@ref kalman_gain_derivate 的公式 \f$\fml{1-20}\f$ 可以得到

\f[\begin{align}P_k
&=\green{P_k^-}-\red{K_kHP_k^-}-\green{P_k^-}\orange{H^TK_k^T}+\red{K_kHP_k^-}\orange{H^TK_k^T}+K_kRK_k^T\\
&=P_k^--K_kHP_k^--P_k^-H^TK_k^T+K_k(HP_k^-H^T+R)K_k^T\\
代入\formular{1-25}\quad&=P_k^--K_kHP_k^--P_k^-H^TK_k^T+P_k^-H^TK_k^T\\
代入\fml{1-25}\quad&=P_k^--K_kHP_k^--P_k^-H^TK_k^T+P_k^-H^TK_k^T\\
&=P_k^--K_kHP_k^-\tag{1-29}\end{align}\f]

即所谓后验误差协方差矩阵 \f$P_k\f$

\f[\red{P_k=(I-K_kH)P_k^-\tag{1-30}}\f]

#### 1.7 汇总 {#kalman_filter_fomulars}
#### 1.7 汇总 {#kalman_filter_formulas}

至此,Kalman Filter 的 5 大公式已经全部求出,分别是公式 \f$\formular{1-14}\f$、公式 \f$\formular{1-17}\f$、公式 \f$\formular{1-25}\f$、公式 \f$\formular{1-28}\f$ 和公式 \f$\formular{1-30}\f$
至此,Kalman Filter 的 5 大公式已经全部求出,分别是公式 \f$\fml{1-14}\f$、公式 \f$\fml{1-17}\f$、公式 \f$\fml{1-25}\f$、公式 \f$\fml{1-28}\f$ 和公式 \f$\fml{1-30}\f$

按照处理顺序,卡尔曼滤波器划分为两个部分

Expand Down Expand Up @@ -411,7 +411,7 @@ P_k^-H^T&=K_k\left(HP_k^-H^T+R\right)

#### 2.1 如何配置

首先必须要寻找 RMVL 包,即 `find_package(RMVL [OPTIONS])`之后可直接在中使用在 CMakeLists.txt 中链接库
首先必须要寻找 RMVL 包,即 `find_package(RMVL REQUIRED)`之后可直接在 CMakeLists.txt 中链接库

```cmake
target_link_libraries(
Expand All @@ -420,6 +420,8 @@ target_link_libraries(
)
```

这里的 `xxx` 为需要链接到 core 模块的目标

#### 2.2 如何使用

##### 2.2.1 包含头文件
Expand Down
6 changes: 3 additions & 3 deletions doc/tutorials/modules/core/least_square.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ A^TA\hat{\pmb x}&=A^T\pmb b

这就是最小二乘解所满足的代数方程。

### 3. 示例
#### 示例

下面给出 3 个示例,不直接使用公式 \f$\text{(2-7)}\f$,通过几何或者其他手段来表示最小二乘解。

Expand Down Expand Up @@ -293,7 +293,7 @@ f(t_0)\\f(t_1)\\\vdots\\f(t_{s-1})\end{bmatrix}\tag{3-11}\f]

后文将给出另外一种描述最小二乘法的做法,这种做法有别于上面构造向量与子空间垂直的方式,通过对误差平方和直接求其最小值来得到最小二乘解(两种方式结果均能推导出公式 \f$\text{(2-7)}\f$,但出发点不同)。此外,要介绍的这个解法也同样适用于整个线性空间(包括欧式空间、多项式空间等线性空间)。

### 4. 法方程求解最小二乘法
### 3. 法方程求解最小二乘法

相关类 rm::CurveFitter

Expand Down Expand Up @@ -351,7 +351,7 @@ f(t_0)\\f(t_1)\\\vdots\\f(t_{s-1})\end{bmatrix}\tag{3-11}\f]
对于 \f$(\phi_p,\phi_q)\f$,依照公式\f$\text{(4-4)}\f$,可以写成矩阵的表示方式,即

\f[\begin{align}(\phi_p,\phi_q)&=\sum_{i=0}^{s-1}\phi_p(t_i)\phi_q(t_i)\\&=[\phi_p(t_0),\phi_p(t_1),\cdots,\phi_p(t_{s-1})]
\begin{bmatrix}\phi_i(t_0)\\\phi_i(t_1)\\\vdots\\\phi_q(t_{s-1})\end{bmatrix}\end{align}\tag{4-7}\f]
\begin{bmatrix}\phi_q(t_0)\\\phi_q(t_1)\\\vdots\\\phi_q(t_{s-1})\end{bmatrix}\end{align}\tag{4-7}\f]

因此对法方程系数矩阵 \f$G\f$ 的第 \f$k\ (k=0,1,\cdots,n-1)\f$ 行,有

Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/modules/tutorial_modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

##### 数据处理

- @subpage tutorial_modules_kalman
- @subpage tutorial_modules_kalman@subpage tutorial_modules_ekf

### 2. 数据结构与算法

Expand Down
2 changes: 2 additions & 0 deletions extra/combo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ rmvl_generate_module_para(combo)

# build the test program
if(BUILD_TESTS)
find_package(GTest REQUIRED)

rmvl_add_test(
combo Unit
DEPENDS armor rune
Expand Down
2 changes: 2 additions & 0 deletions extra/compensator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ rmvl_generate_module_para(compensator)

# Tests
if(BUILD_TESTS)
find_package(GTest REQUIRED)

rmvl_add_test(
compensator Unit
DEPENDS gravity_compensator
Expand Down
Loading

0 comments on commit 50c5cfa

Please sign in to comment.