卡爾曼濾波器的工作原理

原文: 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?=pk?1??+vk?=?Δtvk?1?vk?1??

或者使用另外一種說法:

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^k?1?=Fk?x^k?1???

我們現在有一個預測矩陣,它將當前狀態映射到下一個狀態,但我們仍然不知道如何更新協方差矩陣。

這時我們需要另一個公式。如果我們將分布中的每個點乘以一個矩陣,那么協方差矩陣會發生什么?

這個很簡單,我們直接寫公式:

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?=Fk?x^k?1?Pk?=Fk?Pk?1?FkT????

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} pk?vk??=pk?1?=?+Δtvk?1?+21?aΔt2vk?1?+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??=Fk?x^k?1?+[2Δt2?Δt?]a=Fk?x^k?1?+Bk?uk? ???

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^K?1?被移動到具有協方差 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^k?Pk??=Fk?x^k?1?+Bk?uk? ?=Fk?Pk?1?FkT?+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??=Hk?x^k?=Hk?Pk?HkT???

卡爾曼濾波器有一個很大的優點是處理傳感器噪音。換句話說,我們的傳感器至少有些不可靠,我們原始估計中的每個狀態都可能導致一系列傳感器讀數。

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π ?1?e?2σ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?)=σ02??kσ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? ?)=Σ0??KΣ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?)=(Hk?x^k?,Hk?Pk?HkT?) ;
  • 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} Hk?x^k?Hk?Pk?HkT??=Hk?x^k?=Hk?Pk?HkT??+K(zk? ?+Hk?x^k?)?KHk?Pk?HkT???

并且從 (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=Hk?Pk?HkT?(Hk?Pk?HkT?+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^k?Pk??=x^k?+K(zk? ??Hk?x^k?)=Pk??KHk?Pk???

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=Pk?HkT?(Hk?Pk?HkT?+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.pswp.cn/bicheng/76229.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76229.shtml
英文地址,請注明出處:http://en.pswp.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), …