卡尔曼滤波器的定义,实例和代码实现

卡尔曼滤波器(Kalman filter)是一种高效的递归滤波器, 能够从一系列包含噪音的测量值中估计动态系统的状态. 因为不需要存储历史状态, 没有复杂计算, 非常适合在资源有限的嵌入式系统中使用. 常用于飞行器的导引, 导航及控制, 机械和金融中的时间序列分析, 轨迹最佳化等. 卡尔曼滤波不需要假设误差是正态分布, 但如果误差属于正态分布, 卡尔曼滤波的结果会更为准确.

卡尔曼滤波的计算分二个步骤: 预测与更新. 在预测阶段, 滤波器基于上一步的预测结果, 预测当前状态和误差; 在更新阶段, 滤波器利用当前的测量值和预测值, 计算得到新的状态值和误差.

  1. Original Error Estimate, calculate the Kalman Gain using Error in Estimate and Error in Data(Measurement)
    预测阶段, 用预测误差和测量误差计算卡尔曼增益
  2. Original Estimate, calculate Current Estimate using Kalman Gain, Previous Estimate and Measured Value
    更新阶段, 结合测量值, 用卡尔曼增益计算当前的状态
  3. Calculate the new Error in Estimate
    计算新的预测误差

定义

完整的卡尔曼滤波定义是这样的

  • Predict step 预测阶段

    • State prediction 预测系统状态:
      x t ∣ t − 1 ^ = F t x t − 1 ∣ t − 1 ^ + B t u t \hat{x_{t|t-1}} = F_t \hat{x_{t-1|t-1}} + B_t u_t xtt1^=Ftxt1∣t1^+Btut
    • Uncertainty prediction 预测误差:
      P t ∣ t − 1 = F t P t − 1 ∣ t − 1 F t T + Q t P_{t|t-1} = F_t P_{t-1|t-1} F_t^T + Q_t Ptt1=FtPt1∣t1FtT+Qt
  • Update step 更新阶段

    • Kalman gain 更新卡尔曼增益:
      K t = P t ∣ t − 1 H t T H t P t ∣ t − 1 H t T + R t K_t = \frac{P_{t|t-1} H_t^T} {H_t P_{t|t-1} H_t^T + R_t} Kt=HtPtt1HtT+RtPtt1HtT
    • State update 更新状态:
      x t ∣ t ^ = x t ∣ t − 1 ^ + K t ( z t − H t x t ∣ t − 1 ^ ) \hat{x_{t|t}} = \hat{x_{t|t-1}} + K_t (z_t - H_t \hat{x_{t|t-1}}) xtt^=xtt1^+Kt(ztHtxtt1^)
    • Uncertainty update 更新误差:
      P t ∣ t = ( I − K t H t ) P t ∣ t − 1 P_{t|t} = (I - K_t H_t) P_{t|t-1} Ptt=(IKtHt)Ptt1

对以上符号的说明

  • x ^ \hat{x} x^: 预测的系统状态向量
    The state vector, which represents the true state of the system that we want to estimate.
  • t t t: 时间序列
    The time step index, corresponding to different time periods.
  • F t F_t Ft: 状态转移矩阵
    The state transition matrix, which models how the system evolves from time step t − 1 t-1 t1 to t t t without taking into account external factors.
  • B t B_t Bt: 控制输入矩阵
    The control input matrix, used to incorporate the effect of any external factors u t u_t ut (e.g., motors or steer engines inputs).
  • u t u_t ut: 控制输入向量
    The control input vector, containing the external factors impacting the system.
  • P P P: 误差矩阵
    The uncertainty (covariance) matrix, which quantifies our uncertainty about the estimated state.
  • Q t Q_t Qt: 过程噪声协方差矩阵
    The process noise covariance matrix, representing the estimation error caused by our simplified model of the system state dynamics. Q矩阵表示系统模型的过程噪声, 系统模型是一个近似值, 在系统状态的整个生命周期中, 系统模型的准确性会发生波动, Q矩阵用于表示这种不确定性, 并增加了状态上的现有噪声. 例如飞行器电机的震动给加速度的读数带来的误差.
  • H t H_t Ht: 观察值转换矩阵
    The observation matrix, which models how the true system state is transformed into the observed system state.
  • K t K_t Kt: 卡尔曼增益
    The Kalman gain, which determines how much we trust the new observation relative to our prediction. 卡尔曼增益是一个介于0到1之间的数, 用于表示在预测中观察值所占的比重, 卡尔曼增益越大说明噪声越大, 观察值越重要.
  • z t z_t zt: 观察值(测量值)向量
    The observation (measurement) vector, containing the recorded states.
  • R t R_t Rt: 测量噪声协方差矩阵
    测量噪声指的是测量工具(如传感器)测量时的固有噪声, 例如在静止时加速度传感器读数的上下波动, The observation noise covariance matrix, representing the measurement noise in the observed states.
  • I I I: 单位矩阵
    The identity matrix.

简化

对于一个静止(或匀速运动)的物体观测加速度和角速度, 可以忽略控制输入 B t B_t Bt u t u_t ut, 将 F t F_t Ft, H t H_t Ht视为单位矩阵, 卡尔曼计算公式可以简化为

  • Predict step 预测阶段,

    • State prediction 预测状态等于前一步的状态:
      x t ∣ t − 1 ^ = x t − 1 ∣ t − 1 ^ \hat{x_{t|t-1}} = \hat{x_{t-1|t-1}} xtt1^=xt1∣t1^
    • Uncertainty prediction 预测误差等于更新后的误差加上过程噪声:
      P t ∣ t − 1 = P t − 1 ∣ t − 1 + Q t P_{t|t-1} = P_{t-1|t-1} + Q_t Ptt1=Pt1∣t1+Qt
  • Update step 更新阶段,

    • Kalman gain 更新卡尔曼增益:
      K t = P t ∣ t − 1 P t ∣ t − 1 + R t K_t = \frac{P_{t|t-1}} {P_{t|t-1} + R_t} Kt=Ptt1+RtPtt1
    • State update 更新状态:
      x t ∣ t ^ = x t ∣ t − 1 ^ + K t ( z t − x t ∣ t − 1 ^ ) \hat{x_{t|t}} = \hat{x_{t|t-1}} + K_t (z_t - \hat{x_{t|t-1}}) xtt^=xtt1^+Kt(ztxtt1^)
    • Uncertainty update 更新误差:
      P t ∣ t = ( I − K t ) P t ∣ t − 1 P_{t|t} = (I - K_t) P_{t|t-1} Ptt=(IKt)Ptt1

实例

1. 初始化

令预测误差初始值为 P = 10000 P = 10000 P=10000
测量误差 σ = 0.1 σ = 0.1 σ=0.1,方差 σ 2 = 0.01 σ^2 = 0.01 σ2=0.01, 即 R R R 为固定的 0.01
噪声方差为 q = 0.15 q = 0.15 q=0.15
令初始预测值 $ \hat{x} = 10 $
预测误差 $ P = P + q = 10000 + 0.15 = 10000.15 $

2. 观察值 Z = 50.486 Z = 50.486 Z=50.486

卡尔曼增益

K = P P + r = 10000.15 10000.15 + 0.01 = 0.99999 K = \frac{P}{P + r} = \frac{10000.15}{10000.15 + 0.01} = 0.99999 K=P+rP=10000.15+0.0110000.15=0.99999

更新系统状态(等于预测状态)

x ^ = x ^ + K ∗ ( Z − x ^ ) = 10 + 0.99999 ∗ ( 50.486 − 10 ) = 50.486 \hat{x} = \hat{x} + K * (Z - \hat{x}) = 10 + 0.99999 * (50.486 - 10) = 50.486 x^=x^+K(Zx^)=10+0.99999(50.48610)=50.486

更新预测误差

P = ( 1 − K ) ∗ P = ( 1 − 0.99999 ) ∗ 10000.15 = 0.01 P = (1 - K) * P = (1 - 0.99999) * 10000.15 = 0.01 P=(1K)P=(10.99999)10000.15=0.01

P = P + q = 0.01 + 0.15 = 0.16 P = P + q = 0.01 + 0.15 = 0.16 P=P+q=0.01+0.15=0.16

3. 观察值 Z = 50.963 Z = 50.963 Z=50.963

卡尔曼增益

K = P P + r = 0.16 0.16 + 0.01 = 0.9412 K = \frac{P}{P + r} = \frac{0.16}{0.16 + 0.01} = 0.9412 K=P+rP=0.16+0.010.16=0.9412

更新系统状态(等于预测状态)

x ^ = x ^ + K ∗ ( Z − x ^ ) = 50.486 + 0.9412 ∗ ( 50.963 − 50.486 ) = 50.934 \hat{x} = \hat{x} + K * (Z - \hat{x}) = 50.486 + 0.9412 * (50.963 - 50.486) = 50.934 x^=x^+K(Zx^)=50.486+0.9412(50.96350.486)=50.934

更新预测误差

P = ( 1 − K ) ∗ P = ( 1 − 0.9412 ) ∗ 0.16 = 0.0094 P = (1 - K) * P = (1 - 0.9412) * 0.16 = 0.0094 P=(1K)P=(10.9412)0.16=0.0094

P = P + q = 0.0094 + 0.15 = 0.1594 P = P + q = 0.0094 + 0.15 = 0.1594 P=P+q=0.0094+0.15=0.1594

可以看到 P P P K K K 的值迅速收敛


实现

一个简单的C语言演示代码, 会输出每次迭代后产生的K增益, P误差和预测值.

#include <stdio.h>const int measures[] = {-269,   -255,   -130,    228,   -437,  -1234,   1247,    173,   -400,  -1561,  -1038,    207,    958,   -516,   -581,   -716,    -18,  -1193,   -989,   -593,    484,    102,    718,   1362,   1563,   2683,    428,   1616,   2922,   2968,   3046,   3572,   4006,   4821,   3964,   3127,   3086,   3190,   3682,   4015,   4471,   4211,   4523,   5098,   6452,   5947,   6150,   5694,   6498,   7048,   7519,   6820,   5652,   6608,   7409,   8729,  10569,  10760,   9054,   9856,   8656,   7972,   9320,   6958,   6820,   7391,   7702,   8248,   9426,   8812,   8666,   8838,   7943,   6878,   7233,   7536,   8381,   8314,   7267,   6704,   7343,   6321,   6409,   6023,   7334,   7975,   7659,   6159,   5990,   6187,   6645,   6702,   6273,   7196,   7381,   6939,   4201,   4108,   5338,   6469,   4528,   3679,   4113,   4158,   3428,   2966,   3466,   3704,   3220,   2582,   2818,   3039,   2835,   1929,   1362,    890,    396,   -201,   -992,  -1502,  -2009,  -1667,  -1503,  -1881,  -2713,  -3231,  -2856,  -2868,  -2989,  -4140,  -4878,  -4690,  -3838,  -4244,  -5312,  -9966,  -6514,  -5246,  -4559,  -4832,  -6833,  -8869,  -9207,  -8021,  -7959,  -9219, -10911, -12606, -12296, -11710, -10460, -10827, -13095, -12183, -10989,  -9458,  -9520, -10622, -12221, -11792,  -9510,  -7964,  -7935,  -8728,  -9137,  -8076,  -6628,  -6379,  -7132,  -8076,  -7499,  -6536,  -5855,  -6285,  -7310,  -7517,  -7217,  -6997,  -6440,  -5806,  -4647,  -4006,  -4144,  -3800,  -2820,  -1811,    215,    768,    531,    186,    514,   2117,   2618,   2396,   1600,   1477,   1800,   2329,   2015,   1585,   1461
};static float k_gain, r_noise, q_noise, x_est, p_err, z_measure;void Kalman_Init(void)
{p_err = 1.0;r_noise = 10.0;q_noise = 1;x_est = -200.0;p_err = p_err + q_noise;
}float Kalman_Update(float measure)
{k_gain = p_err / (p_err + r_noise);x_est = x_est + k_gain * (measure - x_est);p_err = (1 - k_gain) * p_err;p_err = p_err + q_noise;return x_est;
}int main(int argc, char *const argv[])
{int i;float estimate, new_measure;Kalman_Init();for (i = 0; i < sizeof(measures)/sizeof(int); i++){estimate = Kalman_Update((float)measures[i]);printf("%3d: %6d %10.5f %10.5f %10.5f\r\n", i, measures[i], k_gain, p_err, estimate);}return 0;
}

对参数的说明

  • measures数值来自于手持物体旋转时陀螺仪传感器的真实读数, 本例中陀螺仪的实测噪声 R R R在10至20这个数量级, 运动中的抖动来源于手持产生的抖动
  • p_err = 1.0;x_est = -200.0;, 预测和误差的初始值可以随意取一个接近的值, 如果不知道取什么值, 设为0也问题不大.
  • r_noise = 10.0;q_noise = 1; 这两个值会显著影响结果, 其中r_noise可以使用传感器收集静止状态数据后计算方差得到, q_noise无法明确计算, 初始可以赋0.1至1之间的数.

输出结果格式

  0:   -269    0.04762    0.97619  -12.809521:   -255    0.08894    1.38937  -34.349242:   -130    0.12199    1.71988  -46.017523:    228    0.14675    1.96749   -5.805664:   -437    0.16440    2.14403  -76.695335:  -1234    0.17655    2.26550 -281.017646:   1247    0.18471    2.34705    1.215127:    173    0.19009    2.40090   33.869718:   -400    0.19361    2.43607  -50.130489:  -1561    0.19589    2.45887 -346.0908210:  -1038    0.19736    2.47359 -482.6455111:    207    0.19831    2.48306 -345.8844312:    958    0.19891    2.48915  -86.5228013:   -516    0.19930    2.49305 -172.1196414:   -581    0.19955    2.49555 -253.7136815:   -716    0.19971    2.49715 -346.0391816:    -18    0.19982    2.49818 -280.4912117:  -1193    0.19988    2.49883 -462.88641
...

使用不同的 r_noiseq_noise 得到的变化曲线如图

图中变化最剧烈的蓝色曲线是从传感器得到的原始测量值, 可以看到原始数据的抖动是很明显的, 经过卡尔曼滤波后可以明显消除抖变, 使结果数据更平滑.

通过变换多种噪声组合, 可以观察到的现象有

  1. r_noiseq_noise 不变的情况下, 不管 p_errx_est 设置什么初始值, 都会很快收敛, 最后输出相同的结果序列(这点没有在本例体现, 需要自己验证)
  2. r_noise越大表示测量噪声越大, 测量值的权重越低, r_noise越大, 结果越平滑但是延迟也越大
  3. q_noise是系统的固有误差, q_noise越小, 结果越平滑延迟越大
  4. r_noiseq_noise 等比例变化时, 产生的结果序列不变, 图中 r=10,q=0.5 和 r=20,q=1 这两个曲线是重合的.

总结

从卡尔曼滤波器的定义看

  • 整个过程中, 对状态 x ^ \hat{x} x^ 的预测和更新, 除了自身和观测值 z t z_t zt之外, 关系到这几个参数 F t , B t , u t , K t , H t F_t, B_t, u_t, K_t, H_t Ft,Bt,ut,Kt,Ht, 其中 F t , u t , H t F_t, u_t, H_t Ft,ut,Ht 在系统中都相对固定, 而 B t B_t Bt 是已知输入, 例如电机或舵机的动作, 已知且确定的, 不存在噪声, 真正起作用的是 K t K_t Kt 这个参数.
  • K t K_t Kt 这个参数的计算, 和 x ^ \hat{x} x^ 没关系. 系统中不存在反馈, 观测值 z t z_t zt 和预测值 x ^ \hat{x} x^ 都不会影响 K t K_t Kt, 只要 H t , R t , Q t H_t, R_t, Q_t Ht,Rt,Qt 这三个值固定, 最后 K t K_t Kt 会收敛为一个常量

当符合上面两点条件时, 状态的更新公式就变成下面的式子

x t ∣ t ^ = x t ∣ t − 1 ^ + K t ( z t − x t ∣ t − 1 ^ ) = x t ∣ t − 1 ^ + K t z t − K t x t ∣ t − 1 ^ = ( 1 − K t ) x t ∣ t − 1 ^ + K t z t \hat{x_{t|t}} = \hat{x_{t|t-1}} + K_t (z_t - \hat{x_{t|t-1}}) \\ = \hat{x_{t|t-1}} + K_t z_t - K_t \hat{x_{t|t-1}} \\ = (1 - K_t) \hat{x_{t|t-1}} + K_t z_t xtt^=xtt1^+Kt(ztxtt1^)=xtt1^+KtztKtxtt1^=(1Kt)xtt1^+Ktzt

β = 1 − K t \beta = 1 - K_t β=1Kt, 这就是一个典型的离散序列差分方程(difference equation)构成的低通滤波器

x t ∣ t ^ = β x t ∣ t − 1 ^ + ( 1 − β ) z t \hat{x_{t|t}} = \beta \hat{x_{t|t-1}} + (1 - \beta) z_t xtt^=βxtt1^+(1β)zt

在实际使用中, Q Q Q R R R大概率是常数, 增益 K t K_t Kt会快速收敛, 上面的式子更简单, 更容易理解和实现, 也符合它的典型使用方式, 即手动调整 β \beta β (等价于调整 Q Q Q R R R), 在延迟和平滑之间找到最佳平衡.


参考

  • 卡尔曼滤波 非常好的介绍 https://www.kalmanfilter.net/CN/alphabeta_cn.html
  • 扩展卡尔曼滤波 https://simondlevy.github.io/ekf-tutorial/
  • 概念说明和Python实现 https://forecastegy.com/posts/kalman-filter-for-time-series-forecasting-in-python/
  • 另一篇浅显易懂的卡尔曼滤波器说明 https://thekalmanfilter.com/kalman-filter-explained-simply/

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode59. 螺旋矩阵 II(C++)

LeetCode59. 螺旋矩阵 II 题目链接代码 题目链接 https://leetcode.cn/problems/spiral-matrix-ii/ 代码 class Solution { public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> res(n, vector<int>(n, 0));int startx …

Linux第67步_linux字符设备驱动_注册和注销

1、字符设备注册与注销的函数原型” /*字符设备注册的函数原型*/ static inline int register_chrdev(unsigned int major,\ const char *name, \ const struct file_operations *fops) /* major:主设备号&#xff0c;Limnux下每个设备都有一个设备号&#xff0c;设备号分…

点云检测网络PointPillar

1. 提出PointPillar的目的 在此之前对于不规则的稀疏的点云的做法普遍分为两派: 一是把点云数据量化到一个个Voxel里&#xff0c;常见的有VoxelNet和SECOND , 但是这种做法比较普遍的问题是由于voxel大部分是空集所以会浪费算力(SECOND利用稀疏卷积解决了它) &#xff0c;但是…

MNIST数据集下载(自动下载)

&#x1f4da;**MNIST数据集下载(自动下载)**&#x1f4da; &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得到您的…

本地数据库Room——study_1

目录 1.概念 2.组成 3.导入依赖 4.具体实现 4.1 数据表的设置 4.2 方法接口 4.3 数据库类 -》 基石&#xff0c;使用模板 4.4 实现真正的实例Room库 1.概念 Room 持久性库在 SQLite 上提供了一个抽象层&#xff0c;以便在充分利用 SQLite 的强大功能的同时&#xff0c…

Qt程序设计-钟表自定义控件实例

本文讲解Qt钟表自定义控件实例。 效果如下: 创建钟表类 #ifndef TIMEPIECE_H #define TIMEPIECE_H#include <QWidget> #include <QPropertyAnimation> #include <QDebug> #include <QPainter> #include <QtMath>#include <QTimer>#incl…

基于InternLM和LangChain搭建自己的知识库

背景 LLM存在一定的局限性&#xff0c;如&#xff1a; 知识时效性受限&#xff1a;如何让LLM能够获取最新的知识专业能力有限&#xff1a;如何打造垂直领域的大模型定制化成本高&#xff1a;如何打造个人专属的LLM应用 正文 为了突破LLM的局限性&#xff0c;目前有两种范式…

51单片机(6)-----直流电机的介绍与使用(通过独立按键控制电机的运行)

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 直流电机模块介绍 1.直流电机介绍 2.电机参数 二. 程序设计…

day08_面向对象基础_内存关系

零、今日内容 一、作业 二、面向对象 一、作业 package com.qf.homework;import java.util.Arrays;/*** --- 天道酬勤 ---** author QiuShiju* date 2024/2/28* desc*/ public class Homework {public static void main(String[] args) {test();}//写一个方法 用于合并两个int…

事件驱动的奇迹:深入理解Netty中的EventLoop

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 事件驱动的奇迹&#xff1a;深入理解Netty中的EventLoop 前言基础概念EventLoop的工作原理Channel与EventLoop的关系定时任务与延时任务EventLoop的生命周期EventLoop中的线程模型性能优化与最佳实践 …

minikube windows 下载安装

下载地址 https://multipass.run/ 选择版本 下载结果 安装就是持续的下一步&#xff0c;安装后的效果 打开后的效果

【数据结构】从链表到LinkedList类

&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;个人主页&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; &#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;数据结构专栏&#x1f388;&#x1f388;&#x1f388;&…

5.WEB渗透测试-前置基础知识-常用的dos命令

内容参考于&#xff1a; 易锦网校会员专享课 上一篇内容&#xff1a;4.WEB渗透测试-前置基础知识-快速搭建渗透环境&#xff08;下&#xff09;-CSDN博客 常用的100个CMD指令 1.gpedit.msc—–组策略 2. sndrec32——-录音机 3. Nslookup——-IP地址侦测器 &#xff0c;是一个…

Vue.js实战:构建一个简单的学生管理系统

摘要&#xff1a; 本文将引导你使用Vue.js框架来构建一个完整的学生管理系统。我们将从环境搭建开始&#xff0c;逐步介绍Vue的核心概念、组件创建、数据管理、事件处理、路由配置以及项目构建等关键步骤。通过实际操作&#xff0c;你将能够掌握Vue.js的基础知识&#xff0c;并…

搭建XSS 测试平台

XSS 测试平台是测试XSS漏洞获取cookie并接收Web 页面的平台&#xff0c;XSS 可以做 JS能做的所有事&#xff0c;包括但不限于窃取cookie、后台增删改文章、钓鱼、利用XSS漏洞进 行传播、修改网页代码、网站重定向、获取用户信息(如浏览器信息、IP 地址)等。这 里使用的是基于x…

Linux:Kubernetes(k8s)——基础理论笔记(1)

我笔记来源的图片以及共享至GitHub&#xff0c;本章纯理论。这是k8s中部分的基础理论 &#x1f447; KALItarro/k8spdf: 这个里面只有一个pdf文件 (github.com)https://github.com/KALItarro/k8spdf&#x1f446; 什么是kubernetes kubernetes 是一个开源的&#xff0c;用于管…

Unity中的UI系统之GUI

目录 概述工作原理和主要作用基础控件重要参数及文本和按钮多选框和单选框输入框和拖动条图片绘制和框 复合控件工具栏和选择网络滚动视图和分组窗口 自定义整体样式自定义皮肤样式 概述 什么是UI系统 UI是User Interface&#xff08;用户界面&#xff09;的简称&#xff0c;用…

【基于React实现共享单车管理系统】—项目简介(一)

【基于React实现共享单车管理系统】—项目简介&#xff08;一&#xff09; 一、项目整体架构

探索 Sora 背后的核心技术

2024年2月16日&#xff0c;OpenAI发布Sora文生视频模型&#xff0c;一石激起千层浪&#xff0c;迅速刷屏爆火于整个AI圈。一方面&#xff0c;Sora从文本、图像迈向视频大模型&#xff0c;这可以说是通向通用人工智能的里程碑事件&#xff1b;另一方面&#xff0c;训练和推理需求…

基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统

文章目录 基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统项目概述招聘岗位数据爬虫分析系统展示用户注册登录系统首页IT招聘数据开发岗-javaIT招聘数据开发岗-PythonIT招聘数据开发岗-AndroidIT招聘数据开发岗-其它招聘岗位数据分析算法方面运维方面测试方面招聘岗…