卡尔曼滤波器的工作原理

原文: https://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/

1 概述

你可以对某个动态系统有不确定信息的任何地方使用卡尔曼滤波器,并且对系统下一步的状态做出有根据的猜测。即使出现混乱的现实状态,卡尔曼滤波器都会给出一个合理的结果。

卡尔曼滤波器非常适合连续变化的系统。它具有占用内存少的优点,而且速度非常快,这使得它非常适合处理实时问题和应用在嵌入式系统中。

卡尔曼滤波器实际上非常简单,如果你以正确的方式看待它,非常容易理解。我将尝试用大量图片来阐明它。先决条件很简单:你只需要对概率和矩阵有基本的了解。

我将以卡尔曼滤波器可以解决的问题的简单示例开始。

2 我们可以用卡尔曼滤波器做什么?

让我们举一个玩具示例:你建造了一个可以在树林中漫步的小型机器人,并且机器人需要准确地知道它在哪里,以便可以导航。

robot_forest.png
我们会说机器人有一个状态 x k ⃗ \vec{x_k} xk ,状态包含了位移 (position) 和速度 (velocity):

x k ⃗ = ( p ⃗ , v ⃗ ) \vec{x_k} = (\vec{p}, \vec{v}) xk =(p ,v )

需要注意,状态只是关于系统底层配置的数字列表,它可以是任何变量。在我们的示例中,他是位移和速度,但它可以是油箱中的液体量、汽车发动机的温度、用户手指在触摸板上的位置数据,或者是你需要跟踪的任何数量的事物。

我们的机器人配备了 GPS 传感器,其精度约为 10 米,这很不错,但它需要比 10 米更精确地知道自己的位置。在树林里有很多沟壑和悬崖,如果机器人的误差超过几英尺它就可能会掉下悬崖,因此仅靠 GPS 是不够的。

robot_ohnoes.png
我们可能还知道一些有关机器人如何移动的信息:它知道发送给车轮马达的命令,并且知道如果它朝一个方向前进并且没有任何东西干扰,那么在下一刻它很可能会沿着同一个方向走得更远。但是它当然并不了解其运动得全部:它可能会受到风得冲击,车轮可能会稍微打滑,或者在颠簸得地形上滚动,因此车轮转动的量可能并不准确代表机器人实际行驶了多远,并且预测不会是完美的。

GPS 传感器会告诉我们一些状态信息,但这些信息只是间接的,而且带有一些不确定性和不准确性。我们的预测会告诉我们一些机器人如何移动的信息,但这些信息只是间接的,而且带有一些不确定性和不准确性。

但是,如果我们利用所有可用的信息,我们能否得到比单独估计更好的答案呢?答案当然是肯定的,这就是卡尔曼滤波器的用途。

3 卡尔曼滤波器分析问题的视角

让我们看看我们试图描述的场景。我们继续使用只有位置和速度的简单状态。

x ⃗ = [ p v ] \vec{x} = \begin{bmatrix} p \\ v \end{bmatrix} x =[pv]

我们不知道实际的位移和速度是多少;位移和速度的可能组合范围可能很广,但其中一些组合比其他组合更有可能:

gauss_0.png

卡尔曼滤波器假设两个变量(在我们的例子中是位移和速度)都是随机的,并且服从高斯分布 (Gaussian distributed)。每个变量都有一个平均值 μ \mu μ,即随机分布的中心(也是最可能的状态),以及方差 σ 2 \sigma^2 σ2,即不确定性:

gauss_1.png

在上图中,位移和速度是不相关的 (uncorrelated),这意味这一个变量的状态并不能告诉你另一个变量的状态。

下面的例子显示了更有趣的东西:位移和速度是相关的。观察到特定位移的可能性取决于你的速度:

gauss_3.png

例如,如果我们根据旧位置估计新位置,则可能会出现这种情况。如果我们的速度很高,我们可能会移动得更远,因此位移会更远;如果我们移动得很慢,就不会走那么远。

跟踪这种关系确实很重要,因为它能给我们提供更多信息:一次测量可以告诉我们其他测量可能是什么。这就是卡尔曼滤波器得目标,我们希望从不确定得测量中尽可能多地获得信息。

这种相关性可以用协方差矩阵 (covariance matrix) 来表示。简而言之,矩阵的每个元素 Σ i j \Sigma_{ij} Σij 是第 i 个变量和第 j 个变量之间的相关程度。可以猜到,协方差矩阵是对称的,这意味着交换 i 和 j 无关紧要。协方差矩阵通常标记为 “ Σ \Sigma Σ”,所以我们称他们的元素为 “ Σ i j \Sigma_{ij} Σij”。

gauss_2.png

4 用矩阵描述问题

我们将关于状态的知识建模为高斯斑点 (Gaussian blob),因此我们同时需要两条信息,我们将给出最佳估计值 x ^ k \mathbf{\hat{x}}_k x^k,(在其他地方成为 μ \mu μ ) 及其协方差矩阵 P k \mathbf{P}_k Pk

x ^ k = [ position velocity ] P k = [ Σ p p Σ p v Σ v p Σ v v ] \begin{equation} \begin{aligned} \mathbf{\hat{x}}_k = \begin{bmatrix} \text{position} \\ \text{velocity} \end{bmatrix} \\ \mathbf{P}_k = \begin{bmatrix} \Sigma_{pp}\ \Sigma_{pv} \\ \Sigma_{vp}\ \Sigma_{vv} \end{bmatrix} \end{aligned} \end{equation} x^k=[positionvelocity]Pk=[Σpp ΣpvΣvp Σvv]

当然,我们这里只使用位移和速度,但状态可以包含任意数量的变量并表示你想要的任何内容。

接下来,我们需要某种方法来查看当前状态(时间 k-1),并预测在时间 k 的下一个状态。其实我们不知道哪个状态是真实的状态,我们的预测函数也并不关心,它只对所有的状态起作用,并为我们提供一个新的分布:

gauss_7.jpg

我们可以用矩阵 F k \mathbf{F}_k Fk 来表示这个预测步骤:

gauss_8.jpg

它会将我们原始估计中的每个点映射到新的预测位置,如果原始估计是正确的,系统会移动到该位置。

让我们应用一下,如何使用矩阵来预测未来一刻的位移和速度,我们将使用一个非常基本的运动学公式:

p k = p k − 1 + Δ t v k − 1 v k = v k − 1 \begin{align} &{p_k} = {p_{k-1}} \ + &{\Delta} t {v_{k-1}} \notag \\ &{v_k} = &{v_{k-1}} \notag \end{align} pk=pk1 +vk=Δtvk1vk1

或者使用另外一种说法:

x ^ k = [ 1 Δ t 0 1 ] x ^ k − 1 = F k x ^ k − 1 \begin{align} \mathbf{\hat{x}}_k & = \begin{bmatrix} 1 \ \ \Delta t \\ 0 \ \ \ \ 1 \end{bmatrix} {\mathbf{\hat{x}}_{k-1}} \\ \\ \notag & = \mathbf{F}_k {\mathbf{\hat{x}}_{k-1}} \end{align} x^k=[1  Δt0    1]x^k1=Fkx^k1

我们现在有一个预测矩阵,它将当前状态映射到下一个状态,但我们仍然不知道如何更新协方差矩阵。

这时我们需要另一个公式。如果我们将分布中的每个点乘以一个矩阵,那么协方差矩阵会发生什么?

这个很简单,我们直接写公式:

C o v ( x ) = Σ C o v ( A x ) = A Σ A T \begin{align} Cov(x) & = \Sigma \notag \\ Cov({\mathbf{A}}x) & = {\mathbf{A}} \Sigma {\mathbf{A}}^T \end{align} Cov(x)Cov(Ax)=Σ=AΣAT

将方程 (4) 与 方程 (3) 结合:

x ^ k = F k x ^ k − 1 P k = F k P k − 1 F k T \begin{equation} \begin{split} {\mathbf{\hat{x}}_k} = \mathbf{F}_k {\mathbf{\hat{x}}_{k-1}} \\ {\mathbf{P}_k} = \mathbf{F_k} {\mathbf{P}_{k-1}} \mathbf{F}_k^T \end{split} \end{equation} x^k=Fkx^k1Pk=FkPk1FkT

4.1 外部影响

不过,我们还没有捕捉到任何东西。可能有一些与状态本身无关的变化,外部世界可能正在影响系统。

例如,如果用状态模拟火车的运动,火车操作员可能会按下油门导致火车加速,同样,在我们的机器人示例中,导航软件可能会发出车轮转动或者停止的命令。如果我们知道关于外界正在发生事情的额外信息,我们可以在向量 u k ⃗ \vec{u_k} uk 里加入一个成员,用它做点什么,并把它添加到我们的预测中作为更正。

比方说,我们知道预期的加速度 a a a 是由油门或者控制指令控制。从基本的运动学中,我们得到:

p k = p k − 1 + Δ t v k − 1 + 1 2 a Δ t 2 v k = v k − 1 + a Δ t \begin{align} {p_k} & = {p_{k-1}} & + {\Delta t}{v_{k-1}} + \frac{1}{2} {a}{\Delta t}^2 \notag \\ {v_k} & = & {v_{k-1}} + {a} {\Delta t} \notag \end{align} pkvk=pk1=+Δtvk1+21aΔt2vk1+aΔt

以矩阵的形式表达:

x ^ k = F k x ^ k − 1 + [ Δ t 2 2 Δ t ] a = F k x ^ k − 1 + B k u k ⃗ \begin{align} {\mathbf{\hat{x}}_k} & = \mathbf{F}_k {\mathbf{\hat{x}}_{k-1}} + \begin{bmatrix} \frac{\Delta t^2}{2} \\ \Delta t \end{bmatrix} {a} \notag \\ & = \mathbf{F}_k {\mathbf{\hat{x}}_{k-1}} + \mathbf{B}_k {\vec{\mathbf{u}_k}} \end{align} x^k=Fkx^k1+[2Δt2Δt]a=Fkx^k1+Bkuk

B k \mathbf{B}_k Bk 被称为控制矩阵, u k ⃗ \vec{u_k} uk 称为控制向量。(对于没有外部影响的非常简单的系统,你可以省略这些)。

让我们再添加一个细节。如果我们的预测不是 100% 准确的模型会发生什么。

4.2 外部不确定性

如果状态根据自身属性演变一切都还好。如果状态是根据外力演变的,只要我们知道这些外力是什么,一切也都会还好。

但是我们不知道的力量呢?例如,如果我们在追踪一架四轴飞行器,它可能会被风吹来吹去。如果我们跟踪一个轮式机器人,轮子可能会打滑,或者地面上的颠簸可能会让它变慢。我们无法跟踪这些事情,如果发生任何这种情况,我们的预测可能会出错,因为我们没有考虑到这些额外的力量。

我们可以通过在每个预测步骤后添加一些新的不确定性来模拟与“世界”(即我们没有跟踪的事情)相关的不确定性:
gauss_9.jpg

在我们最初的估计中,每个状态都可能转移到一系列状态。因为我们非常喜欢高斯斑点,所以我们会说每个点 x ^ K − 1 \hat{x}_{K-1} x^K1被移动到具有协方差 Q k \mathbf{Q}_k Qk 的高斯斑点中的某个点。另一种说法是,我们将未跟踪的影响视为具有协方差的噪声 Q k \mathbf{Q}_k Qk.

gauss_10a.jpg

这产生了一个新的高斯斑点,具有不同的协方差(但平均值相同):

gauss_10b.jpg

我们通过简单地添加来获得扩展的协方差 Q k \mathbf{Q}_k Qk,给出我们对预测步骤的完整表达:

x ^ k = F k x ^ k − 1 + B k u k ⃗ P k = F k P k − 1 F k T + Q k \begin{align} {\mathbf{\hat{x}}_k} & = \mathbf{F}_k {\mathbf{\hat{x}}_{k-1}} + \mathbf{B}_k {\vec{\mathbf{u}_k}} \notag \\ {\mathbf{P}_k} & = \mathbf{F_k} {\mathbf{P}_{k-1}} \mathbf{F}_k^T + {\mathbf{Q}_k} \end{align} x^kPk=Fkx^k1+Bkuk =FkPk1FkT+Qk

换句话说,新的最佳估计是从以前的最佳估计中得出的预测,并加上对已知外部影响的修正。

新的不确定性是从旧的不确定性中预测的,还有一些来自环境的额外不确定性。

好吧,那就够了。我们对这个系统可能在哪里有一个模糊的估计,给出了 x ^ k \mathbf{\hat{x}}_k x^k P k \mathbf{P}_k Pk 。那么当我们从传感器获取一些数据时又会发生什么?

5 通过测量完善估算

我们可能有几个传感器,可以给我们提供有关我们系统状态的信息。就目前而言,他们测量什么并不重要;也许一个读取位移,另一个读取速度。每个传感器都间接地告诉我们一些关于状态的信息 —— 换句话说,传感器在一个状态上运行并产生一组读数。

gauss_12.jpg
请注意,读数的单位和尺度可能与我们正在跟踪的状态的单位和尺度不同。你也许能猜到接下来要做什么:我们将用矩阵 H k \mathbf{H}_k Hk 对传感器进行建模。

gauss_13.jpg
我们可以找出我们期望以通常的方式看到的传感器读数的分布:

μ ⃗ expected = H k x ^ k Σ expected = H k P k H k T \begin{align} \vec{\mu}_{\text{expected}} & = \mathbf{H}_k {\mathbf{\hat{x}}_k} \notag \\ \mathbf{\Sigma}_{\text{expected}} & = \mathbf{H}_k {\mathbf{P}_k} \mathbf{H}_k^T \end{align} μ expectedΣexpected=Hkx^k=HkPkHkT

卡尔曼滤波器有一个很大的优点是处理传感器噪音。换句话说,我们的传感器至少有些不可靠,我们原始估计中的每个状态都可能导致一系列传感器读数。

gauss_14.jpg

从我们观察到的每一次读数中,我们可能会猜测我们的系统处于特定状态。但由于存在不确定性,一些状态比其它状态更有可能产生我们看到的读数:

gauss_11.jpg

我们将把这种不确定性的协方差(即传感器噪声)称为 P k \mathbf{P}_k Pk 。分布的平均值等于我们观察到的读数,我们将称之为 z k ⃗ \vec{\mathbf{z}_k} zk

所以现在我们有两个高斯点:一个围绕我们变换预测的平均值,一个围绕我们得到的实际传感器读数。

gauss_4.jpg
我们必须尝试将我们对基于预测状态(粉红色)的读数的猜测与我们实际观察到的传感器读数(绿色)的不同猜测相调和 (reconcile)。

那么,我们最有可能的新状态是什么?对于任何可能的阅读 ( z 1 , z 2 ) (z_1,z_2) (z1,z2),我们有两个相关的概率:

  • (1) 我们的传感器读取的概率 z k ⃗ \vec{\mathbf{z}_k} zk 是一个(错误的)测量 ( z 1 , z 2 ) (z_1,z_2) (z1,z2)
  • (2) 我们之前的估计认为的概率 ( z 1 , z 2 ) (z_1,z_2) (z1,z2) 是我们应该看到的读数。

如果我们有两个概率,并且我们想知道两个概率都是真的,我们只需将它们相乘即可。因此,我们取两个高斯斑点并乘以它们:

gauss_6a.png
剩下的是重叠,即两个斑点都明亮(可能)的区域。它比我们之前的任何一个估计都要精确得多。这个分布的平均值是两种估计最有可能的配置,因此,鉴于我们掌握的所有信息,是对真实配置的最佳猜测。

嗯。这看起来像另一个高斯斑点。

gauss_6.png

事实证明,当你用单独的均值和协方差矩阵乘以两个高斯点时,你会得到一个新的高斯斑点,它有自己的平均值和协方差矩阵。也许你可以看到这会去哪里:必须有一个公式来从旧参数中获取那些新参数。

6 组合高斯

让我们找到那个公式。首先从一个维度看这个是最简单的。一个协方差为 σ 2 \sigma^2 σ2,均值为 μ \mu μ 的高斯曲线定义 为:

N ( x , μ , σ ) = 1 σ 2 π e − ( x − μ ) 2 2 σ 2 \begin{equation} \mathcal{N}(x, \mu,\sigma) = \frac{1}{ \sigma \sqrt{ 2\pi } } e^{ -\frac{ (x - \mu)^2 }{ 2\sigma^2 } } \end{equation} N(x,μ,σ)=σ2π 1e2σ2(xμ)2

我们想知道当你把两条高斯曲线相乘时会发生什么。下面的蓝色曲线代表了两个高斯曲线(未归一化)的交集:
gauss_joint.png

N ( x , μ 0 , σ 0 ) ⋅ N ( x , μ 1 , σ 1 ) = ? N ( x , μ ′ , σ ′ ) \begin{equation} \mathcal{N}(x, {\mu_0}, {\sigma_0}) \cdot \mathcal{N}(x, {\mu_1}, {\sigma_1}) \stackrel{?}{=} \mathcal{N}(x, {\mu'}, {\sigma'}) \end{equation} N(x,μ0,σ0)N(x,μ1,σ1)=?N(x,μ,σ)

你可以将方程 (9) 代入方程 (10) 并做一些代数变换(小心重新归一化,使总概率为1)以获得:

μ ′ = μ 0 + σ 0 2 ( μ 1 − μ 0 ) σ 0 2 + σ 1 2 σ ′ 2 = σ 0 2 − σ 0 4 σ 0 2 + σ 1 2 \begin{align} {\mu'} & = \mu_0 + \frac{\sigma_0^2 (\mu_1 - \mu_0)} {\sigma_0^2 + \sigma_1^2} \notag \\ {\sigma'}^2 & = \sigma_0^2 - \frac{\sigma_0^4} {\sigma_0^2 + \sigma_1^2} \end{align} μσ2=μ0+σ02+σ12σ02(μ1μ0)=σ02σ02+σ12σ04

我们可以提取出系数 k \mathbf{k} k

k = σ 0 2 σ 0 2 + σ 1 2 \begin{equation} {\mathbf{k}} = \frac{\sigma_0^2}{\sigma_0^2 + \sigma_1^2} \end{equation} k=σ02+σ12σ02

μ ′ = μ 0 + k ( μ 1 − μ 0 ) σ ′ 2 = σ 0 2 − k σ 0 2 \begin{align} {\mu'} & = \mu_0 + {\mathbf{k}} (\mu_1 - \mu_0) \notag \\ {\sigma'}^2 & = \sigma_0^2 - {\mathbf{k}} \sigma_0^2 \end{align} μσ2=μ0+k(μ1μ0)=σ02kσ02

记下如何进行之前的估算,并添加一些东西来进行新的估算。看看那个公式有多简单!

但是矩阵版本呢?好吧,我们把 (12) 和 (13) 写成矩阵形式。如果 Σ \Sigma Σ 是高斯斑点的协方差矩阵, μ ⃗ \vec{\mu} μ 是它沿着每个轴线的平均数,然后:

K = Σ 0 ( Σ 0 + Σ 1 ) − 1 \begin{equation} {\mathbf{K}} = \Sigma_0 (\Sigma_0 + \Sigma_1)^{-1} \end{equation} K=Σ0(Σ0+Σ1)1

μ ′ ⃗ = μ 0 ⃗ + K ( μ 1 ⃗ − μ 0 ⃗ ) Σ ′ = Σ 0 − K Σ 0 \begin{align} {\vec{\mu'}} & = \vec{\mu_0} + {\mathbf{K}} (\vec{\mu_1} - \vec{\mu_0}) \notag \\ {\Sigma'} & = \Sigma_0 - {\mathbf{K}} \Sigma_0 \end{align} μ Σ=μ0 +K(μ1 μ0 )=Σ0KΣ0

K \mathbf{K} K 是一个叫做卡尔曼增益的矩阵,我们一会儿就会使用它。

放松,我们快完成了!

7 综合

我们有两种分布:

  • The predicted measurement: ( μ 0 , Σ 0 ) = ( H k x ^ k , H k P k H k T ) ({\mu_0}, {\Sigma_0}) = ({\mathbf{H}_k \mathbf{\hat{x}}_k}, {\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T}) (μ0,Σ0)=(Hkx^k,HkPkHkT) ;
  • The observed measurement: ( μ 1 , Σ 1 ) = ( z k ⃗ , R k ) ({\mu_1}, {\Sigma_1}) = ({\vec{\mathbf{z}_k}}, {\mathbf{R}_k}) (μ1,Σ1)=(zk ,Rk) ;

我们可以把这些插入到方程中(15) 找到他们的重叠:

H k x ^ k ′ = H k x ^ k + K ( z k ⃗ + H k x ^ k ) H k P k ′ H k T = H k P k H k T − K H k P k H k T \begin{align} \mathbf{H}_k {\mathbf{\hat{x}}_k'} & = {\mathbf{H}_k \mathbf{\hat{x}}_k} & + {\mathbf{K}} ( {\vec{\mathbf{z}_k}} + {\mathbf{H}_k \mathbf{\hat{x}}_k} ) \notag \\ \mathbf{H}_k {\mathbf{P}_k'} \mathbf{H}_k^T & = {\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} & - {\mathbf{K}} {\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} \end{align} Hkx^kHkPkHkT=Hkx^k=HkPkHkT+K(zk +Hkx^k)KHkPkHkT

并且从 (14) 得到,卡尔曼增益是:

K = H k P k H k T ( H k P k H k T + R k ) − 1 \begin{equation} {\mathbf{K}} = {\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} ( {\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} + {\mathbf{R}_k})^{-1} \end{equation} K=HkPkHkT(HkPkHkT+Rk)1

我们可以把 (16) 和 (17) 中的 H k \mathbf{H}_k Hk 替换掉(注意夹在中间的 K \mathbf{K} K),以及 P k \mathbf{P}_k Pk 后面的 H k T \mathbf{H}_k^T HkT .

x ^ k ′ = x ^ k + K ( z k ⃗ − H k x ^ k ) P k ′ = P k − K H k P k \begin{align} {\mathbf{\hat{x}}_k'} & = {\mathbf{\hat{x}}_k} + {\mathbf{K}} ( {\vec{\mathbf{z}_k}} - {\mathbf{H}_k \mathbf{\hat{x}}_k} ) \notag \\ {\mathbf{P}_k'} & = {\mathbf{P}_k} - {\mathbf{K}} {\mathbf{H}_k \mathbf{P}_k} \end{align} x^kPk=x^k+K(zk Hkx^k)=PkKHkPk

K ′ = P k H k T ( H k P k H k T + R k ) − 1 \begin{equation} {\mathbf{K}'} = {\mathbf{P}_k \mathbf{H}_k^T} ( {\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} + {\mathbf{R}_k})^{-1} \end{equation} K=PkHkT(HkPkHkT+Rk)1

上面给出了完整方程。

就是这样! x ^ k \mathbf{\hat{x}}_k x^k 是我们新的最佳估计,我们可以通过 P k ′ \mathbf{P}_k' Pk 进行反馈,来到另一轮预测或更新,想多少次就多少次。
kalflow.png

8 总结

在上述所有数学推导中,你只需要实现方程 (7),(18) 和 (19).(或者如果你忘记了那些,你可以从方程中重新推导一切 (4) 和 (15) )

这将允许您准确地对任何线性系统进行建模。对于非线性系统,我们使用扩展卡尔曼滤波器,它通过简单地将有关其平均值的预测和测量线性化来工作。(我将来可能会做第二份关于 EKF 的文章)。

如果我的工作做得很好,希望其他人会意识到这些事情有多酷,并想出一个意想不到的新地方把它们付诸行动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/76229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PDFtk

如果下载的pdf文件有秘钥的话,使用下面linux命令去掉秘钥: pdftk 纳税记录.pdf input_pw 261021 output 纳税记录_output.pdf将多个单页pdf合并为一个pdf的linux命令: pdftk 自然人电子税务局1.pdf 自然人电子税务局2.pdf 自然人电子税务局3.pdf 自然人…

Openlayers:海量图形渲染之WebGL渲染

最近由于在工作中涉及到了海量图形渲染的问题,因此我开始研究相关的解决方案。我在网络上寻找相关的解决方案时发现许多的文章都提到利用Openlayers中的WebGLPointsLayer类,可以实现渲染海量的点,之后我又了解到利用WebGLVectorLayer类可以渲…

替换jeecg图标

替换jeecg图标 ant-design-vue-jeecg/src/components/tools/Logo.vue <!-- <img v-else src"~/assets/logo.svg" alt"logo">-->

Codeforces Round 970 (Div. 3)题解

题目地址 https://codeforces.com/contest/2008 锐评 本次D3的前四题还是比较简单的&#xff0c;没啥难度区分&#xff0c;基本上差不多&#xff0c;属于手速题。E的码量比F大一些&#xff0c;实现略显复杂一些。G的数学思维较明显&#xff0c;如果很久没有训练这个知识点&a…

操作系统:线程间同步之事件集

事件集是线程间同步的机制之一&#xff0c;一个事件集可以包含多个事件&#xff0c;利用事件集可以完成一对多、多对多的线程间同步。 目录 一、事件集举例说明 二、事件集工作机制 三、RT-Thread为实例说明 四、事件集的应用场合 一、事件集举例说明 以坐公交车为例&…

基于springboot钻孔数据管理系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!

摘要 本钻孔数据管理系统采用B/S架构&#xff0c;数据库是MySQL&#xff0c;网站的搭建与开发采用了先进的Java语言、Hadoop、数据可视化技术进行编写&#xff0c;使用了Spring Boot框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。用户主要功能包括&…

全双工分轨语音数据集:让AI实现无缝对话

清晨&#xff0c;智能音箱根据指令-播放音乐&#xff1b;驾驶途中&#xff0c;车载助手同步处理导航与来电&#xff1b;智能会议工具无缝切换多语种对话……语音交互技术正快速融入生活。然而&#xff0c;用户对于对话体验追求更自然、更流畅&#xff0c;实时理解&#xff0c;动…

Python 网络请求利器:requests 包详解与实战

诸神缄默不语-个人技术博文与视频目录 文章目录 一、前言二、安装方式三、基本使用1. 发起 GET 请求2. 发起 POST 请求 四、requests请求调用常用参数1. URL2. 数据data3. 请求头 headers4. 参数 params5. 超时时间 timeout6. 文件上传 file&#xff1a;上传纯文本文件流7. jso…

linux入门四:Linux 编译器

一、C 语言编译器 GCC&#xff1a;开启编程之旅 1.1 GCC 安装&#xff1a;一站式工具链 GCC&#xff08;GNU Compiler Collection&#xff09;是 Linux 下最常用的 C/C 编译器&#xff0c;支持多种编程语言。安装命令&#xff08;适用于 Debian/Ubuntu 系统&#xff09;&…

建筑兔零基础自学记录69|爬虫Requests-2

Requests库初步尝试 #导入requests库 import requests #requests.get读取百度网页 rrequests.get(http://www.baidu.com) #输出读取网页状态 print(r.status_code) #输出网页源代码 print(r.text) HTTP 状态码是三位数字&#xff0c;用于表示 HTTP 请求的结果。常见的状态码有…

Web测试流程及注意点

在Web工程过程中&#xff0c;基于Web系统的测试、确认和验收是一项重要而富有挑战性的工作。基于Web的系统测试与传统的软件测试不同&#xff0c;它不但需要检查和验证是否按照设计的要求运行&#xff0c;而且还要测试系统在不同用户的浏览器端的显示是否合适。 重要的是&…

基于MATLAB/simulink的信号调制仿真--AM调制

实验内容&#xff1a; 假设y(t)(20.5*2cos&#xff08;2*pi*1000*t&#xff09;)*5cos&#xff08;2*pi*2*1e4*t&#xff09;调幅系统&#xff0c;请将一个频率为1000HZ的余弦波信号&#xff0c;通过进行AM调制&#xff0c;载波信号频率为20kHZ的余弦波&#xff0c;调制度ma0.…

通信协议详解(十):PSI5 —— 汽车安全传感器的“抗干扰狙击手”

一、PSI5是什么&#xff1f; 一句话秒懂 PSI5就像传感器界的“防弹信使”&#xff1a;在汽车安全系统&#xff08;如气囊&#xff09;中&#xff0c;用两根线同时完成供电数据传输&#xff0c;即便车祸时线路受损&#xff0c;仍能确保关键信号准确送达&#xff01; 基础概念…

数据结构与算法-图论-复习1(单源最短路,全源最短路,最小生成树)

1. 单源最短路 单一边权 BFS 原理&#xff1a;由于边权为单一值&#xff0c;可使用广度优先搜索&#xff08;BFS&#xff09;来求解最短路。BFS 会逐层扩展节点&#xff0c;由于边权相同&#xff0c;第一次到达某个节点时的路径长度就是最短路径长度。 用法&#xff1a;适用…

【WRF理论第十七期】单向/双向嵌套机制(含namelist.input详细介绍)

WRF运行的单向/双向嵌套机制 准备工作&#xff1a;WRF运行的基本流程namelist.input的详细设置&time_control 设置&domain 嵌套结构&bdy_control 配置部分 namelist 其他注意事项Registry.EM 运行 ARW 嵌套双向嵌套&#xff08;two-way nesting&#xff09;单向嵌套…

怎么查看苹果手机和ipad的设备信息和ios udid

你知道吗&#xff1f;我们每天使用的iPhone和iPad&#xff0c;其实隐藏着大量详细的硬件与系统信息。除了常见的系统版本和序列号外&#xff0c;甚至连电池序列号、摄像头序列号、销售地区、芯片型号等信息&#xff0c;也都可以轻松查到&#xff01; 如果你是开发者、维修工程…

matlab内置的git软件版本管理功能

1、matlab多人协作开发比普通的嵌入式软件开发困难很多 用过matlab的人都知道&#xff0c;版本管理对于matlab来说真的很费劲&#xff0c;今天介绍的这个工具也不是说它就解决了这个痛点&#xff0c;只是让它变得简单一点。版本管理肯定是不可或缺的&#xff0c;干就完了 2、…

vscode集成deepseek实现辅助编程(银河麒麟系统)【详细自用版】

针对开发者用户&#xff0c;可在Visual Studio Code中接入DeepSeek&#xff0c;实现辅助编程。 可参考我往期文章在银河麒麟系统环境下部署DeepSeek&#xff1a;基于银河麒麟桌面&&服务器操作系统的 DeepSeek本地化部署方法【详细自用版】 一、前期准备 &#xff08…

Java 大厂面试题 -- JVM 深度剖析:解锁大厂 Offe 的核心密钥

最近佳作推荐&#xff1a; Java大厂面试高频考点&#xff5c;分布式系统JVM优化实战全解析&#xff08;附真题&#xff09;&#xff08;New&#xff09; Java大厂面试题 – JVM 优化进阶之路&#xff1a;从原理到实战的深度剖析&#xff08;2&#xff09;&#xff08;New&#…

数据库实践题目:在线书店管理系统

完整的数据库实践题目&#xff1a;在线书店管理系统 数据库表结构及示例数据 书籍表(books) CREATE TABLE books ( book_id INT PRIMARY KEY, title VARCHAR(100) NOT NULL, author VARCHAR(50) NOT NULL, publisher VARCHAR(50), publish_year INT, category VARCHAR(30), …