news 2026/7/27 7:40:54

嵌入式常用滤波算法与控制算法(5)卡尔曼滤波(下)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式常用滤波算法与控制算法(5)卡尔曼滤波(下)

第 5 篇:卡尔曼滤波(下)——50 行 C 代码 + MPU6050 实战

上篇把原理讲透了。这篇直接上代码——一维卡尔曼、二维卡尔曼、MPU6050 角度估计,全都有。


1. 一维卡尔曼(不到 50 行)

// kalman1d.htypedefstruct{floatx;// 状态估计值floatp;// 估计误差协方差floatq;// 过程噪声协方差floatr;// 测量噪声协方差}kalman1d_t;voidkalman1d_init(kalman1d_t*kf,floatq,floatr,floatinit_x);floatkalman1d_update(kalman1d_t*kf,floatmeasurement);
// kalman1d.c#include"kalman1d.h"voidkalman1d_init(kalman1d_t*kf,floatq,floatr,floatinit_x){kf->x=init_x;kf->p=1.0f;// 初始不确定性,设为 1(会快速收敛)kf->q=q;// 过程噪声kf->r=r;// 测量噪声}floatkalman1d_update(kalman1d_t*kf,floatz){// === 预测 ===// x = x(一维无控制输入,状态不变)kf->p=kf->p+kf->q;// P⁻ = P + Q// === 更新 ===floatk=kf->p/(kf->p+kf->r);// K = P⁻/(P⁻+R)kf->x=kf->x+k*(z-kf->x);// x̂ = x̂⁻ + K(z - x̂⁻)kf->p=(1.0f-k)*kf->p;// P = (1-K)P⁻returnkf->x;}

使用示例——超声波测距:

kalman1d_tdist_kf;voidsetup(void){// R = 测距噪声方差。超声波测距精度约 ±2cm,方差 ≈ 4// Q = 目标不会瞬间移动,设小一点kalman1d_init(&dist_kf,0.01f,4.0f,100.0f);}voidloop(void){floatraw_dist=ultrasonic_read_cm();// 超声波原始距离floatopt_dist=kalman1d_update(&dist_kf,raw_dist);printf("raw=%.1fcm, kalman=%.1fcm\r\n",raw_dist,opt_dist);delay_ms(50);}

2. 二维卡尔曼——同时估计位置和速度

很多场景下,我们不仅想知道"现在在哪",还想知道"现在多快"。

状态向量 x = [位置, 速度]ᵀ

// kalman2d.htypedefstruct{floatx[2];// [位置, 速度]floatp[2][2];// 2×2 协方差矩阵floatq[2][2];// 过程噪声floatr;// 测量噪声(只测位置)floatdt;// 采样间隔}kalman2d_t;voidkalman2d_init(kalman2d_t*kf,floatdt,floatq_pos,floatq_vel,floatr);floatkalman2d_update(kalman2d_t*kf,floatmeasurement);voidkalman2d_get_state(kalman2d_t*kf,float*pos,float*vel);
// kalman2d.c#include"kalman2d.h"#include<string.h>voidkalman2d_init(kalman2d_t*kf,floatdt,floatq_pos,floatq_vel,floatr){kf->dt=dt;kf->r=r;memset(kf->x,0,sizeof(kf->x));memset(kf->p,0,sizeof(kf->p));kf->p[0][0]=1.0f;kf->p[1][1]=1.0f;// 初始协方差kf->q[0][0]=q_pos;kf->q[1][1]=q_vel;// 对角噪声阵}floatkalman2d_update(kalman2d_t*kf,floatz){floatdt=kf->dt;// === 预测 ===// x⁻[0] = x[0] + x[1]*dt → 位置 = 位置 + 速度×时间// x⁻[1] = x[1] → 速度不变(匀速模型)floatx_pred[2];x_pred[0]=kf->x[0]+kf->x[1]*dt;x_pred[1]=kf->x[1];// P⁻ = A·P·Aᵀ + Q// A = [[1, dt], [0, 1]]floatp_pred[2][2];p_pred[0][0]=kf->p[0][0]+2*dt*kf->p[0][1]+dt*dt*kf->p[1][1]+kf->q[0][0];p_pred[0][1]=kf->p[0][1]+dt*kf->p[1][1];p_pred[1][0]=p_pred[0][1];p_pred[1][1]=kf->p[1][1]+kf->q[1][1];// === 更新(只测量位置 H=[1,0])===floats=p_pred[0][0]+kf->r;// 新息协方差floatk0=p_pred[0][0]/s;// 卡尔曼增益 K[0]floatk1=p_pred[1][0]/s;// 卡尔曼增益 K[1]floaty=z-x_pred[0];// 新息 = 测量值 - 预测值kf->x[0]=x_pred[0]+k0*y;// 更新位置kf->x[1]=x_pred[1]+k1*y;// 更新速度kf->p[0][0]=(1-k0)*p_pred[0][0];kf->p[0][1]=(1-k0)*p_pred[0][1];kf->p[1][0]=p_pred[1][0]-k1*p_pred[0][0];kf->p[1][1]=p_pred[1][1]-k1*p_pred[0][1];returnkf->x[0];}voidkalman2d_get_state(kalman2d_t*kf,float*pos,float*vel){*pos=kf->x[0];*vel=kf->x[1];}

3. 实战:MPU6050 角度卡尔曼滤波

用二维卡尔曼估计俯仰角——同时得到角度和角速度。

kalman2d_tangle_kf;voidmpu6050_angle_init(void){// dt = 5ms (200Hz 采样)// q_pos = 0.001 (角度过程噪声小)// q_vel = 0.003 (角速度噪声稍大)// r = 0.03 (加速度计推算角度的噪声,实际测出的方差)kalman2d_init(&angle_kf,0.005f,0.001f,0.003f,0.03f);}floatmpu6050_get_angle(void){floatax,ay,az,gx,gy,gz;mpu6050_read_all(&ax,&ay,&az,&gx,&gy,&gz);// 加速度计推算的角度作为"测量值"floataccel_angle=atan2f(ay,az)*180.0f/3.14159f;// 卡尔曼融合floatangle=kalman2d_update(&angle_kf,accel_angle);// 注意:这里把角速度信息用在了预测模型里(匀速模型)// 更精确的做法是把 gyro 作为控制输入 u[k] 放进预测方程returnangle;}

效果对比:

纯加速度计: ±3° 晃动(高频振动) 纯陀螺仪: 持续漂移(1分钟漂5°) 互补滤波: ±1° 卡尔曼: ±0.5°

4. 卡尔曼滤波的坑

坑 1:Q 和 R 的初始值选错导致发散
→ 解决办法:先用传感器数据手册算 R,Q 从 0.001 开始调

坑 2:协方差矩阵不对称导致数值不稳定
→ 解决办法:强制对称P[0][1] = P[1][0] = (P[0][1]+P[1][0])/2

坑 3:非线性系统用线性卡尔曼
→ 线性卡尔曼假设 A·x+B·u,如果你的系统不是线性的(比如四旋翼姿态),需要用扩展卡尔曼(EKF)


下一篇:互补滤波——陀螺仪+加速度计的最佳拍档,比卡尔曼更简单实用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/27 7:40:50

C 语言循环与自增运算符组合对比分析

一、实验说明初始变量统一&#xff1a;int i 5; int n 0; i&#xff1a;循环条件变量 n&#xff1a;统计循环体执行次数 核心规则&#xff1a; 后置自增 i&#xff1a;先取值判断&#xff0c;后变量自增 前置自增 i&#xff1a;先变量自增&#xff0c;后取值判断 for / while…

作者头像 李华
网站建设 2026/7/27 7:40:07

Linux进程控制:从基础概念到实战技巧

1. 进程控制基础概念在Linux系统中&#xff0c;进程是程序执行的基本单位。理解进程控制是系统管理和程序开发的核心技能之一。每次我们在终端输入命令时&#xff0c;系统都会创建一个新的进程来执行这个命令。比如运行ls -l查看目录内容时&#xff0c;系统就会启动一个专门处理…

作者头像 李华
网站建设 2026/7/27 7:39:50

7.20-7.26学习笔记

目录 一、软件包管理 1.在 Rocky Linux 上使用 dnf 安装 nginx 2.在 Rocky Linux 上使用 rpm 查询 nginx 安装的文件列表 3.在 Ubuntu 上使用 apt 安装 nginx 4.配置 Rocky使用阿里源 Rocky方法一 Rocky方法二 Rocky替换阿里EPEL源 5.配置Ubuntu使用清华源 6.dnf ins…

作者头像 李华
网站建设 2026/7/27 7:39:42

LLM结构化输出对回答多样性的影响与平衡策略

在实际 AI 应用开发中&#xff0c;我们越来越依赖大语言模型&#xff08;LLM&#xff09;生成结构化的数据输出&#xff0c;比如 JSON 或 XML 格式。这种需求在构建 API 接口、数据提取工具或自动化工作流时尤为常见。开发者通常认为&#xff0c;强制模型输出固定格式&#xff…

作者头像 李华
网站建设 2026/7/27 7:39:34

TMS320DM6467T中断控制器与EMIF实战:从原理到调试避坑指南

1. 项目概述与核心价值在嵌入式系统&#xff0c;尤其是像TMS320DM6467T这类面向音视频处理、通信网关的高性能数字信号处理器&#xff08;DSP&#xff09;设计中&#xff0c;中断控制器和外部存储器接口&#xff08;EMIF&#xff09;是决定系统实时性、稳定性和整体性能的两大基…

作者头像 李华
网站建设 2026/7/27 7:38:46

Netty从入门到精通3

第3章 Reactor 线程模型:从单线程到主从多线程的演进 学习目标 能手绘三种 Reactor 模型(单线程、多线程、主从多线程)的架构图,并标注各组件职责 说清 EventLoop 与 Channel 的绑定机制:next() 选择策略、注册流程、线程安全保证 完成 Netty 4.2 MultiThreadIoEventLoop…

作者头像 李华