news 2026/9/7 11:52:20

Matlab中kalman函数用法详解:从教科书公式到LQG状态估计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Matlab中kalman函数用法详解:从教科书公式到LQG状态估计

简介:一份简明实用的MATLAB Kalman滤波器函数说明文档,面向利用控制系统工具箱进行状态估计与噪声抑制的工程师、科研人员和相关专业学生。内容围绕kalman函数展开,从离散系统状态空间模型出发,讲解稳态Kalman滤波器的设计方法、语法格式和参数含义(A、B、C、Q、R、kalmf、L、P、M),并给出完整可运行的MATLAB代码,用于构建带噪系统、并联反馈闭环、生成高斯噪声并对比滤波前后的输出与误差。文档还延伸到误差协方差计算和时变Kalman滤波器的迭代实现,帮助读者掌握实际调试与验证思路。资源包为单个PDF文档,共1个文件,大小约180KB,内容紧凑,便于查阅与打印。目前已有125人浏览/学习,适合正在做控制系统课程设计、仿真实验或需要快速上手Kalman滤波器的MATLAB使用者。 如果你在Matlab里打开help文档搜索kalman,大概率会有一种“这和我看过的卡尔曼滤波教程怎么对不上”的错愕感。我在做LQG控制器设计时也经历过这个阶段:教科书里明明是预测、更新两个公式来回迭代,但Matlab却让我先给一个sys、再给两个协方差矩阵Qn和Rn,最后返回一个叫kalmf的状态空间对象。这篇博文就围绕Matlab里的kalman函数展开,把它和教科书卡尔曼滤波的关系、模型构造方法、参数含义一次讲清楚,最后用直流电机转速估计的实例走完整个流程。无论你是刚开始学状态估计的学生,还是正在写LQG、跟踪、导航融合代码的工程师,应该都能从里面找到直接能用的东西。

1. kalman函数到底在解决什么问题:和教科书递推公式的差异

1.1 先回顾教科书里的经典卡尔曼滤波递推

卡尔曼滤波最常见的教学形式是离散系统的五条公式。假设系统写成

x[k+1] = A x[k] + B u[k] + w[k] y[k] = C x[k] + v[k]

其中w是过程噪声,v是测量噪声,它们的协方差分别为Q和R。从初始估计出发,每个时刻要做两件事:

预测: x_pred = A * x_hat(k-1) + B * u(k-1) P_pred = A * P(k-1) * A' + Q 更新: K = P_pred * C' * inv(C * P_pred * C' + R) x_hat(k) = x_pred + K * (y(k) - C * x_pred) P(k) = (I - K * C) * P_pred

这组公式你可以在任何一本现代控制理论教材里找到。它描述的是一个递推过程:先根据系统模型预测状态和误差协方差,再用最新测量值修正预测。滤波增益K不是固定的,它在每个时刻都会随P的变化而更新。

但不知道你有没有注意到一个细节:如果系统是线性时不变的,噪声统计特性也不随时间变化,那么经过一段足够长的时间后,P_pred和P会逐渐收敛到常量,K也会收敛到一个固定值。这就是所谓的稳态卡尔曼滤波。

1.2 控制工具箱里要的是稳态估计器,不是滤波循环

Matlab的kalman函数来自Control System Toolbox,它的定位就是设计稳态估计器,准确说叫线性二次估计器(LQE),是LQG控制设计的一环。它的设计目标不是帮你在每个采样周期里跑一遍预测-更新循环,而是直接求解代数黎卡提方程,算出稳态误差协方差P和稳态滤波增益L,再把整个估计器动态封装成一个状态空间模型kalmf。

也就是说,你调用

[kalmf, L, P] = kalman(sys, Qn, Rn);

拿到的kalmf不是一个函数句柄,而是一个状态空间模型对象。它内部已经包含了这么一段动态:

dx_hat/dt = A * x_hat + B * u + L * (y - C * x_hat - D * u)

这不是什么“另一种卡尔曼滤波”,它就是卡尔曼滤波的稳态形式。用教科书公式迭代很多步之后,最终会收敛到和这个估计器完全一致的行为。

这就解释了为什么第一次用kalman函数的人总觉得它别扭:你习惯了把卡尔曼滤波当作一段“算法”去执行,但Matlab把它当成一个“对象”去设计和装配。比如在Simulink里做LQG闭环时,kalman函数生成的kalmf可以直接和lqr设计出的控制器K拼成一个完整的LQG控制器,这种“模型对模型”的接口比手写递推循环方便得多。

2. 用kalman函数之前,先把噪声写进状态空间模型

2.1 为什么sys必须包含噪声输入通道

很多人的报错都出在这一步。kalman函数设计估计器时,必须知道过程噪声从哪个通道进入系统、测量噪声又长什么样。你光给它一个ss(A, B, C, D)模型是不行的,因为在这个模型里只有确定性输入u,根本没有w的位置。

正确的做法是把过程噪声w当作系统的一个额外输入通道,和u一起拼进输入矩阵B里。比如一个连续时间系统

dx/dt = A x + B u + Bw * w y = C x + D u + Dw * w + v

对应的状态空间模型应该写成:

sys = ss(A, [B, Bw], C, [D, Dw]);

其中sys的输入向量是[u; w],前几列是确定性控制输入,最后一列或几列是随机噪声输入。kalman函数默认把sys输入中最后面的通道当作过程噪声w,所以构造模型时一定要把噪声通道放在最后。

这里有一个很多人会忽略的小点:Dw矩阵。如果噪声w会直接串到测量输出y上,Dw这列就不能填0。比如过程噪声通过某种物理耦合直接影响了传感器读数,那么D矩阵对应的那一列必须填实际的耦合系数。如果w只影响状态更新、不影响输出,那Dw填0即可。

2.2 连续与离散模型的构造细节

连续时间系统的构造刚才已经说了,关键在于把B矩阵扩展成[B, Bw]。离散时间系统的道理完全一样,只是矩阵名称看起来不同:

sysd = ss(Ad, [Bd, Bwd], Cd, [Dd, Dwd], Ts);

这里Ts是采样周期。如果你要从连续模型得到离散模型,可以用c2d做离散化,它会同步返回离散后的Ad、Bd、Cd、Dd。

有一个工程上的坑我必须提醒你:离散化时,过程噪声协方差Q也会发生变化。连续系统里的Q代表噪声强度,离散化后通常要乘以采样周期或做更精确的矩阵指数积分,不能直接把连续Q原封不动塞给离散kalman函数。很多人在c2d离散化之后发现滤波器行为异常,多半是这个原因。

具体做法上,如果连续系统是sys = ss(A, [B Bw], C, [D Dw]),在Matlab中直接:

sysd = c2d(sys, Ts);

然后调用kalman时,Qn和Rn要换成离散域对应的数值。实际项目中如果噪声统计特性不明确,通常的做法是先做一个粗略估算,然后在仿真里微调,这个调参经验后面专门讲。

2.3 忽略噪声通道时会发生什么

最常见的报错是矩阵维度不匹配,比如:

Error using kalman (line 32) The plant model has no random inputs.

或者类似“Q matrix must have dimension Nw-by-Nw”的提示。前者说明sys输入里没有噪声通道,后者说明Qn的维度和Bw的列数对不上。

遇到这种错误,第一反应不要急着改Qn或Rn的维度,先检查sys是不是把w写进去了。我见过不少朋友把[B, Bw]写错成[B; Bw],导致模型输入数量翻倍而不是增加一列,这种低级错误只要看一眼size就能发现。

3. 语法拆解:kalmf、L、P和离散版本中的M、Z都是什么

3.1 基本调用形式与参数含义

kalman函数最常见的用法是:

[kalmf, L, P] = kalman(sys, Qn, Rn);

四个输入的用途分别是:

  • sys:包含了确定性输入和随机噪声输入的状态空间模型,就是前面构造的带噪声系统。
  • Qn:过程噪声w的协方差矩阵。注意它是w的协方差,维度等于w的数量,不是状态数量。如果只有一个噪声源,Qn就是一个标量。
  • Rn:测量噪声v的协方差矩阵,维度等于测量输出y的数量。单输出系统里Rn也是标量。
  • 可选参数Nn:过程噪声和测量噪声的互协方差矩阵。如果两者不相关,这个参数可以不传,默认是0。

三个输出参数的含义也要说清楚:

  • kalmf:状态空间估计器模型。在Simulink里做LQG闭环时,它就是你在框图中拖出来用的那个滤波器模块,输入端对应u和y,输出端对应状态估计x_hat和输出估计y_hat。
  • L:稳态卡尔曼滤波增益。它的物理意义是估计器动态方程里的矫正增益,也就是估计状态受测量残差影响的强度。你完全可以拿它手写一个连续的估计器方程:dx_hat/dt = A*x_hat + B*u + L*(y - C*x_hat - D*u)
  • P:稳态状态估计误差协方差矩阵,就是黎卡提方程的解。它告诉你估计值可信程度;对角线元素就是各状态估计方差。

这里有一个容易混淆的点:教科书里通常把滤波增益叫K,Matlab里用L,两者是一个东西。P在教科书里是随时间更新的估计误差协方差,Matlab返回的是收敛后的稳态值。

3.2 离散系统里的额外输出M和Z

如果sys是离散时间模型,调用形式变成:

[kalmf, L, P, M, Z] = kalman(sysd, Qn, Rn, Nn);

多出来的两个输出是:

  • M:最优预测器增益,对应状态的一步预测x[k+1|k]场景下的增益。
  • Z:预测器对应的误差协方差矩阵。

换句话说,离散系统里会同时存在两个视角:一个是滤波器(filter),估计的是当前时刻x[k|k];另一个是预测器(predictor),估计的是下一时刻x[k+1|k]。滤波器的增益L和协方差P我们上面已经说了,预测器的增益M和协方差Z就是额外的这两个输出。

实际上你在Matlab里跑连续系统调用,返回的也是L和P;跑离散系统,才能拿到M和Z。这算是kalman函数和教科书离散递推公式衔接得最微妙的地方:教科书里每个采样周期都要算一次K和P,而kalman函数直接帮你解出稳态的对应值,还包括了预测器的版本。

3.3 版本差异和端口顺序问题

不同Matlab版本对kalmf端口顺序的定义有过调整,尤其老的R2007b之前版本和现在的新版本不完全一致。我建议你在自己机器上执行一次help kalman,看一眼当前版本支持哪些语法,再在模型建好之后用size(kalmf)检查一下输入输出个数。这个习惯能帮你省掉很多不必要的排查时间。

4. 一个完整示例:对直流电机转速做卡尔曼估计

4.1 建模:一阶惯性系统加噪声

假设有一个直流电机,转速动态可以简化为一阶惯性模型:

dx/dt = -10 x + 2 V + w y = x + v

x是电机转速,V是控制电压,w是过程噪声,v是测量噪声。取Qn=0.5,Rn=0.2,这个模型虽然简单,但足够看清kalman函数从设计到仿真的完整链路。

建模代码:

a = 10; b = 2; bw = 1; A = -a; B = [b, bw]; % 第1列是控制输入V,第2列是过程噪声w C = 1; D = [0, 0]; % w不直接串到输出 sys_plant = ss(A, B, C, D);

这里sys_plant有两个输入,第一个是控制电压,第二个是噪声w。kalman函数会默认把最后一个输入通道当作随机噪声输入,所以一切都对得上。

4.2 设计估计器并仿真

接下来调用kalman函数,并把真实系统初值设为1,让估计器从0开始去追,这样收敛过程会看得非常清楚:

Qn = 0.5; Rn = 0.2; [kalmf, L, P] = kalman(sys_plant, Qn, Rn); t = 0:0.01:10; u = 2 * ones(size(t)); w = sqrt(Qn) * randn(size(t)); v = sqrt(Rn) * randn(size(t)); [~, ~, x] = lsim(sys_plant, [u; w]', t, 1); y = C * x' + v;

然后拿kalman返回的L手动实现连续估计器。这里明明有kalmf对象可以直接用,但我故意写成显式循环,目的是让你看清L到底用在什么地方:

x_hat = zeros(size(t)); x_hat(1) = 0; for i = 2:numel(t) dt = t(i) - t(i-1); x_hat(i) = x_hat(i-1) + dt * (A*x_hat(i-1) + b*u(i-1) + ... L * (y(i-1) - C*x_hat(i-1) - D(1)*u(i-1))); end plot(t, x, 'LineWidth', 1.5); hold on; plot(t, y, '--'); plot(t, x_hat, 'LineWidth', 1.5); legend('真实状态x', '含噪测量y', '卡尔曼估计x\_hat'); xlabel('时间/s'); ylabel('转速'); grid on;

4.3 运行结果里能看到什么

运行这段代码后,L大约是0.124,P大约是0.025。看曲线图会有几个直观印象:

  • 含噪测量y的曲线上下抖动很剧烈,单看任何一个时刻的测量值都很难判断真实转速。
  • x_hat的曲线明显平滑很多,说明卡尔曼滤波器确实把测量噪声压下来了。
  • 初始阶段x_hat从0往1收敛,这个收敛速度由L决定。L越大,收敛越快,但对噪声越敏感;L越小,输出越平滑,但响应越迟钝。

这个例子里的Qn和Rn选得相对合理,所以估计器看起来很正常。实际工程里Qn和Rn往往不知道准确值,这时就需要按照下一章的经验去调试。

5. 手写递推和kalman函数的选择,以及协方差调试经验

5.1 稳态滤波适合哪些场景,时变滤波又适合哪些场景

kalman函数的本质是求稳态解,这意味着它最适合系统线性时不变、噪声平稳、模型参数固定的场景。尤其是LQG控制设计,kalman函数和lqr、lqgreg配合是最标准的做法,你不需要自己写递推循环,直接生成kalmf对象搭闭环即可。

但如果你处理的是时变系统,比如模型参数在处理过程中会变化,或者你希望滤波增益K每个时刻都随协方差动态更新,那就应该自己写教科书里的递推循环。此外,如果你需要在每个采样周期里读取估计协方差P(k)作为输出,递推形式也更直接。

我个人的经验是:能用kalman函数的就别自己写循环。一方面Matlab内置实现经过优化,数值稳定性有保障;另一方面LQG设计里它和lqr调用天然配套,能少写不少代码。只有当任务明确需要时变增益时,才考虑手写递推。

5.2 协方差Qn和Rn怎么调:最关键的两个经验

调Qn和Rn是卡尔曼滤波实际工程应用中最耗时间的环节。我最深的两个体会:

第一,Rn可以实测。把传感器放在静止场景中,采集一段数据,计算方差就直接得到Rn。这个方法在电机转速测量、GPS定位、温度测量上都非常有效。如果测量噪声相关,还要考虑协方差矩阵,但通常对角线方差够用了。

第二,Qn很难直接测量,但从物理量级入手比盲调快得多。Qn描述的是你对系统模型的信任程度。Qn开很大,意味着你觉得模型不可靠,更信任测量值,滤波曲线会变毛糙、噪声抑制变弱;Qn开很小,意味着你觉得模型很准确,更信任预测值,滤波曲线会变得很平滑,但动态响应会明显滞后。实际调的时候,先把Qn和Rn设成同一数量级,跑一遍仿真,再根据估计曲线是过“毛”还是过“钝”来调整比例。

5.3 滤波发散时怎么排查

如果估计值直接飞掉或者明显不合理,按下面顺序排查:

  1. 检查sys的噪声通道是否构造正确,w是不是放在了输入序列最后。
  2. 检查Qn和Rn维度是否和w、y的维度匹配。
  3. 检查系统是否可观。如果某个状态在输出里完全不可观测,卡尔曼滤波对该状态的估计没有意义,增益和协方差也可能表现异常。可以用rank(obsv(sys_plant))快速检查。
  4. 检查Qn和Rn的数量级是否差异过大,比如一个1e-8、一个1e8,这种情况数值上很容易出问题。

5.4 别把kalman函数和其他工具箱的卡尔曼API混用

Matlab里叫“卡尔曼”的可不止kalman函数一个。vision.KalmanFilter是计算机视觉工具箱里做目标跟踪的;dsp.KalmanFilter是DSP系统工具箱里做信号滤波的;trackingKFtrackingUKFtrackingEKF是传感器融合与跟踪工具箱里的自适应滤波对象,用于多目标跟踪;insfilter系列则是惯性导航和GNSS融合的专用滤波器。它们都基于卡尔曼思想,但接口和使用方式完全不一样。

如果你搜kalman函数是想做图像里的目标框跟踪,或者做惯性导航融合,用kalman(sys, Qn, Rn)大概率不是最顺手的方案。先想清楚自己的应用场景,再选对应工具箱。反过来,如果你就是在做LQG控制或者线性系统状态估计,那么kalman函数是首选。

另外提醒一句:如果你在系统辨识与自适应控制仿真里用卡尔曼滤波做参数在线估计,那通常需要的是recursiveLSrecursiveAR这类递推辨识函数,而不是kalman。这一点容易被忽略,我一开始就走偏过一次。

卡尔曼滤波的用法其实不难,难点在于搞清楚自己手里函数到底是“算法”还是“设计工具”。kalman函数是后者,它替你完成了稳态滤波器设计中最复杂的求解过程。只要建模时把噪声通道写对,再花点心思把Qn、Rn调到合理范围,剩下的工作基本就是仿真验证。看完这篇之后,建议你把自己项目的系统模型代入跑一遍,有报错就先用help kalman对照一下当前版本的语法,很多问题其实一两分钟就能定位。

本文还有配套的精品资源,点击获取

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

GitHub热榜风向:迷你小模型与本地部署实战指南

早上打开 GitHub 热榜,我的第一反应是:风向真的变了。往年这个位置通常留给某个千亿参数大模型发布或训练框架更新,而 2026-09-01 这一天,前排集中出现了一批迷你小模型相关的开源项目——小尺寸模型权重、量化工具、端侧推理框架…

作者头像 李华
网站建设 2026/9/7 11:51:48

YOLOv11智能交通车牌识别与超速抓拍系统设计方案与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 11:51:35

学生管理系统排名模块:从基础排序到生产级解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 11:51:24

MicroPython日志模块uLogLite实战:级别、轮转与过滤

做嵌入式开发这些年,我一直坚持一个观点:凡是打算在设备上跑超过一个月的 MicroPython 项目,日志模块从来就不是“锦上添花”,而是“保命工具”。很多朋友图省事,从开发到量产全程用print打日志,结果现场出…

作者头像 李华
网站建设 2026/9/7 11:49:39

PID控制器原理与工程整定实战:从核心算法到工业应用

1. PID 控制器的核心原理拆解:为什么三位一体能通吃全场1.1 三个字母的通俗解读:比例、积分、微分到底各管什么事聊 PID 控制器之前,先把这三个字母代表的算法逻辑说清楚。P 是比例,I 是积分,D 是微分,这三…

作者头像 李华
网站建设 2026/9/7 11:49:10

AI Agent安全警钟:思维链泄露与多轮上下文边界防御

最近圈子里有个话题很热闹:有人通过多轮对话把 Claude 的思维链“套”出来了,甚至用上了“加密漏洞被爆破”这种说法。乍一看很像一次针对大模型的顶级攻击秀,但如果你正在做 AI Agent 开发,这件事其实是一记警钟,而且…

作者头像 李华