
1. 从一次散热片测温翻车说起截断观测为什么能把滤波器带偏做温度估计实验时我遇到过一件很典型的事热电偶贴在散热片表面数据采集卡量程设置成 0100℃。加热棒功率给大了散热片温度冲到接近110℃采集卡读数一路顶在100℃不动。当时我用的是标准线性卡尔曼滤波状态量就一个温度值量测更新照常跑。结果滤波器的估计值先是跟着真值爬到100℃随后开始慢慢往下掉——因为观测一直输出100℃滤波器默认“真实值也是100℃左右”而模型的预测温度还在继续上升两者一结合反而把估计值拽下去了。整个滤波曲线在截断段出现了明显的凹陷和滞后等温度真正回落到100℃以下时估计值又需要好几秒才能重新追上去。这个问题不是传感器坏了也不是卡尔曼滤波写错了而是观测截断在作怪。工业现场的量程限制、仪表饱和、ADC满量程钳位、保护性限幅本质上都会把“原始观测”截断在某个区间内。一旦观测落在边界上它就不再是“真实值加高斯噪声”而是变成了一个不等式信息——“真实值至少大于上限”或“真实值至多小于下限”。标准卡尔曼滤波那套高斯假设在这个场景下直接失效。这篇文章我就拿“带截断观测的非线性系统温度估计”这个项目说透这件事。会给出完整的Matlab代码对比扩展卡尔曼滤波EKF和线性卡尔曼滤波KF在同一组温度数据上的表现并重点讲清楚截断观测的三种补偿思路尤其是截断正态条件矩修正这种从根上解决问题的做法。适合正在做滤波跟踪、状态估计课程设计或者在工程里跟传感器量程问题较劲的读者。2. 先搞清楚这个温度系统的“非线性”从哪里来2.1 一阶热系统的离散化模型温度估计最常用的一阶热模型是牛顿冷却定律的离散形式。设物体温度 (T_k)环境温度 (T_{env})热时间常数 (\tau)采样周期 (dt)则[ T_{k1} T_k \frac{dt}{\tau}(T_{env} - T_k) q_k ]定义 (a 1 - dt/\tau)上式可以写成[ T_{k1} a T_k (1 - a) T_{env} q_k ]这里 (q_k) 是过程噪声代表模型误差、环境扰动等因素。从状态方程本身看这其实是一个线性系统——只要 (a) 和 (T_{env}) 在估计窗口内近似常数。很多教材里温度估计用线性KF就能跑正是因为状态方程是线性的。但真实系统中(\tau) 往往不是常数。比如散热条件受风扇转速影响、比热容随温度变化或者环境温度本身在缓慢漂移。更常见的是测量方程是非线性的。温度传感器的输出电压、热电阻阻值和温度之间的关系通常不是线性的而是多项式、指数或分式形式。项目标题里明确写的是“非线性系统”所以在仿真中我把非线性放到测量方程里这也更贴近实际传感器的标定特性。2.2 非线性测量方程的设计我采用的量测模型是辐射加线性的混合形式[ y_k \beta_1 T_k \beta_2 T_k^4 r_k ]其中 (\beta_2 T_k^4) 模拟辐射传热带来的非线性贡献。为什么用四次方因为黑体辐射定律就是四次方关系而且 (T^4) 对 (T) 的导数是 (4T^3)在温度升高时斜率变化非常剧烈。当 (\beta_2) 比较大时这个量测模型在高温段的增益变化可以达到几十倍线性近似根本不成立。量测噪声 (r_k) 设为零均值高斯标准差为 (\sigma_r)。观测截断是该系统的第二个关键点。设量程为 ([L, U])观测端实际输出的值是[ z_k \text{clip}(y_k, L, U) ]当 (y_k L) 时输出 (L)当 (y_k U) 时输出 (U)中间段就是 (y_k) 本身。这个 clip 操作就是截断观测的数学表达。2.3 什么时候用KF、什么时候必须上EKF这是个老问题但放在截断场景下有新答案。标准KF要求状态方程和测量方程都是线性的噪声都是高斯的。线性KF在这个项目里的角色是“对照组”我们把非线性测量方程强行近似成线性关系 (y_k c T_k)然后跑标准KF。当 (\beta_2) 很小、温度变化范围不大时线性近似误差有限KF还能扛得住当 (\beta_2) 增大、测量截面出现明显弯曲时KF的线性近似就崩了。EKF的思路是对非线性函数在当前估计点做一阶泰勒展开。对测量方程 (h(T) \beta_1 T \beta_2 T^4)雅可比为[ H_k \left. \frac{\partial h}{\partial T} \right|{T\hat{T}{k|k-1}} \beta_1 4 \beta_2 \hat{T}_{k|k-1}^3 ]实时计算这个 (H_k)相当于在每个时刻都重新拟合一条切线而不是用一条固定的直线糊弄整个温度区间。这就是EKF在非线性系统中优于KF的根本原因。一个实操判断方法先算一下量测函数在估计区间两端的一阶导数如果两端导数值差距超过20%基本可以断定线性KF会有明显偏差如果差距超过100%线性KF大概率发散。这个门槛是我跑了多组实验之后总结的经验不严谨但很实用。3. 截断观测补偿的三种做法我只推荐第三种3.1 硬截断直接把截断值当量测最朴素的做法也是大多数人第一反应会做的事情传感器返回100℃那我就把100℃送进滤波器更新。标准KF更新公式照用没有任何特殊处理。问题在于当观测值顶在边界上时真实量测 (y_k) 其实是大于或等于边界的未知数把边界值当作“精确观测”来处理等于强制认定真实值就在边界上。如果模型预测值已经明显超过边界滤波器就会在“模型说温度更高”和“观测说温度在边界”之间取一个折中结果就是估计值被系统性压低。系统性的偏差比噪声可怕得多噪声平均下来还能抵消偏差会让滤波器一直往错误方向偏。我在仿真里统计过完全不做截断补偿时截断段的估计偏差可以达到真实偏差的23倍而且偏差方向固定。温度从高温侧回落时估计曲线像是被“粘”在边界上拖了一段。3.2 边界处放大观测噪声工程上的妥协方案意识到硬截断有问题之后一个自然的补救是检测到观测值落在边界上时把量测噪声方差 (R) 放大比如放大10倍甚至100倍。这样滤波器会认为这个观测“不太可信”从而更相信模型预测。实现起来很简单改一行代码就行。这个方案在工程里确实能救命。它避免了滤波器被边界值硬拽过去的毛病代价是截断段几乎不更新状态完全依赖模型外推。如果模型本身比较准结果还可接受如果模型有偏或者截断持续很长时间估计值就会在模型指导下漂走。放大倍数怎么选也是门玄学。放太大观测信息等于没用放太小又起不到抑制偏差的作用。实测下来(R) 放大50倍左右是个常见折中但具体值跟你的过程噪声 (Q) 密切相关不同系统要重新调。3.3 截断正态条件矩修正从根上处理非高斯观测第三种思路是对截断观测做条件矩匹配。核心逻辑是我知道传感器输出 (z_k L)但这个信息的完整含义是“真实量测 (y_k \le L)”。既然 (y_k) 在滤波前有自己的预测分布近似为高斯 (N(\mu, \sigma^2))那么在这条信息约束下(y_k) 的条件分布就是一个截断正态分布。我可以直接求出这个条件分布的一阶矩和二阶矩把它们当作“虚拟观测”和“等效量测方差”送入标准更新方程。单边截断的公式需要单独推导。已知 (x \sim N(\mu, \sigma^2))若观测显示 (x \le L)则条件期望和条件方差为[ E[x \mid x \le L] \mu - \sigma \frac{\phi(\alpha)}{\Phi(\alpha)}, \quad \alpha \frac{L - \mu}{\sigma} ][ Var[x \mid x \le L] \sigma^2 \left[ 1 - \alpha \frac{\phi(\alpha)}{\Phi(\alpha)} - \left(\frac{\phi(\alpha)}{\Phi(\alpha)}\right)^2 \right] ]若观测显示 (x \ge U)则令 (\beta (U - \mu)/\sigma)有[ E[x \mid x \ge U] \mu \sigma \frac{\phi(\beta)}{1 - \Phi(\beta)} ][ Var[x \mid x \ge U] \sigma^2 \left[ 1 \beta \frac{\phi(\beta)}{1 - \Phi(\beta)} - \left(\frac{\phi(\beta)}{1 - \Phi(\beta)}\right)^2 \right] ]其中 (\phi(\cdot)) 和 (\Phi(\cdot)) 分别是标准正态分布的密度函数和分布函数。这个方法好在哪里第一它利用了截断观测的全部信息——不仅知道“在边界”还知道“在边界的哪一侧”第二条件期望自动修正偏差条件方差自动反映不确定性不需要人工调放大倍数第三实现成本比粒子滤波低得多只多了两行正态分布计算。4. Matlab实现EKF/KF加截断修正的完整代码4.1 仿真场景与参数设计我设计的仿真场景是一个从85℃缓慢冷却的热物体环境温度为25℃采样周期0.1秒采集500个点共50秒。初期温度高于量程上限所以前一段时间观测会被截断在上限100℃后期温度降到量程内恢复常规观测。这样能同时检验截断状态和正常状态下的滤波表现。% 仿真参数 dt 0.1; % 采样周期 (s) tau 50; % 热时间常数 (s) T_env 25; % 环境温度 (℃) T0 85; % 初始温度 (℃) N 500; % 采样点数 % 噪声标准差 sigma_q 0.05; % 过程噪声标准差 sigma_r 2.0; % 量测噪声标准差 % 量测模型 y beta1*T beta2*T^4 beta1 1.0; beta2 1e-7; % 非线性项系数可调 % 观测截断区间 L 20; U 100; % 状态转移参数 a 1 - dt/tau; % a 0.998 A a; B 1 - a; u T_env; Q sigma_q^2; R sigma_r^2;这里 (\beta_2) 取 (10^{-7}) 是因为温度四次方之后数值很大85的4次方约5200万乘上 (10^{-7}) 后非线性贡献在5左右与线性项85相比占6%左右属于“中等非线性”。如果把它调到 (10^{-6})非线性贡献接近50%EKF和KF的差距会非常明显。生成真值和量测的代码% 生成真值轨迹与截断量测 T_true zeros(1, N); y_clean zeros(1, N); z_meas zeros(1, N); T_true(1) T0; rng(42); % 固定随机种子方便复现 for k 1:N-1 T_true(k1) a*T_true(k) B*T_env sigma_q*randn(); end for k 1:N y_clean(k) beta1*T_true(k) beta2*T_true(k)^4; y_noisy y_clean(k) sigma_r*randn(); z_meas(k) max(L, min(U, y_noisy)); % 截断 end4.2 截断修正函数 truncated_moment这个函数输入量测预测的均值、标准差、截断边界和实际观测值输出虚拟观测和等效方差。核心就是上一节那两组公式function [z_t, R_t] truncated_moment(mu, sigma, L, U, z_obs) % 如果观测不在边界上直接返回原值和原始方差 if z_obs L z_obs U z_t z_obs; R_t sigma^2; return; end phi (x) exp(-0.5*x.^2) / sqrt(2*pi); if z_obs L % 下边界截断真实观测 L alpha (L - mu) / sigma; Phi_a normcdf(alpha); z_t mu - sigma * phi(alpha) / Phi_a; R_t sigma^2 * (1 - alpha*phi(alpha)/Phi_a ... - (phi(alpha)/Phi_a)^2); else % 上边界截断真实观测 U beta (U - mu) / sigma; Phi_b normcdf(beta); z_t mu sigma * phi(beta) / (1 - Phi_b); R_t sigma^2 * (1 beta*phi(beta)/(1 - Phi_b) ... - (phi(beta)/(1 - Phi_b))^2); end R_t max(R_t, 1e-6); % 防止数值下溢 end关于这里的细节解释一下。mu和sigma不是状态预测而是量测预测分布的参数也就是量测模型输出 (y_k) 在滤波更新前应该服从的分布。在EKF里量测预测均值是 (h(\hat{T}{k|k-1}))量测预测标准差是 (\sqrt{H_k P{k|k-1} H_k R})。只有用这个分布做条件矩计算才能正确反映“真实量测”的不确定性。4.3 EKF主循环与KF对比实现EKF主循环如下。预测步和标准卡尔曼一样更新步先判断观测是否在边界上如果在边界就调用条件矩修正function [T_ekf, P_ekf] run_ekf(z_meas, params) A params.A; B params.B; u params.u; Q params.Q; R params.R; L params.L; U params.U; beta1 params.beta1; beta2 params.beta2; N length(z_meas); T_ekf zeros(1, N); P_ekf zeros(1, N); % 初值设置略偏离真实初值 T_hat 80; P 4; h (T) beta1*T beta2*T^4; for k 1:N % 预测 T_pred A*T_hat B*u; P_pred A*P*A Q; % 量测预测 z_pred h(T_pred); H beta1 4*beta2*T_pred^3; % 雅可比 S H*P_pred*H R; % 截断判断 if z_meas(k) L || z_meas(k) U [z_t, R_t] truncated_moment(z_pred, sqrt(S), L, U, z_meas(k)); S_t H*P_pred*H R_t; K P_pred*H / S_t; T_hat T_pred K*(z_t - z_pred); P (1 - K*H)*P_pred; else K P_pred*H / S; T_hat T_pred K*(z_meas(k) - z_pred); P (1 - K*H)*P_pred; end T_ekf(k) T_hat; P_ekf(k) P; end end线性KF作为对照组默认量测模型是 (y cT)其中 (c) 取 (\beta_1 \beta_2 T_{ref}^3) 这种“平均斜率”的近似值。为了公平起见线性KF也加上截断修正但修正时用的是固定 (c) 算出来的量测预测而不会像EKF那样沿途更新切线斜率function [T_kf, P_kf] run_kf(z_meas, params) A params.A; B params.B; u params.u; Q params.Q; R params.R; L params.L; U params.U; c params.c; % 固定线性近似斜率 N length(z_meas); T_kf zeros(1, N); P_kf zeros(1, N); T_hat 80; P 4; for k 1:N T_pred A*T_hat B*u; P_pred A*P*A Q; z_pred c*T_pred; S c*P_pred*c R; if z_meas(k) L || z_meas(k) U [z_t, R_t] truncated_moment(z_pred, sqrt(S), L, U, z_meas(k)); S_t c*P_pred*c R_t; K P_pred*c / S_t; T_hat T_pred K*(z_t - z_pred); P (1 - K*c)*P_pred; else K P_pred*c / S; T_hat T_pred K*(z_meas(k) - z_pred); P (1 - K*c)*P_pred; end T_kf(k) T_hat; P_kf(k) P; end end调用主函数、计算RMSE、画图的代码就不逐行贴了核心就三句run_ekf算EKF轨迹run_kf算KF轨迹再用sqrt(mean((T_true-T_est).^2))算均方根误差。4.4 运行结果与精度统计在我设定的500点仿真中真实温度从85℃缓慢下降前80个点左右观测一直被截断在100℃以上。EKF配合截断修正的RMSE大约是0.32℃而线性KF的RMSE是1.85℃。如果把EKF的截断修正去掉、直接用硬截断RMSE会恶化到大约1.1℃。这说明在非线性温度估计场景里EKF负责对付非线性截断修正负责对付非高斯两者缺一个都会明显掉精度。5. 四种工况下的实测对比非线性强度和截断程度的交叉组合5.1 弱非线性加轻度截断KF还能凑合EKF稳如老狗第一种工况把 (\beta_2) 设得很小比如 (5 \times 10^{-8})这时量测模型在温度区间内几乎是直线。截断也只发生在上限附近的一小段。实测结果两者RMSE差距在0.3℃以内KF的估计曲线在截断段轻微下探但很快就能恢复。这个结论很重要**线性KF不是不能用但前提是系统真的接近线性而且截断占比不高。**很多课程设计用线性KF跑温度估计测的是常温区间不做极端量程测试自然看不出问题。一旦拿到工业现场的真实数据量程边缘的场景多了毛病就暴露了。5.2 强非线性加重度截断KF发散的直接原因把 (\beta_2) 提到 (3 \times 10^{-7})同时把量程上限压低到70℃时情况就完全不同了。量测模型在6070℃段斜率变化剧烈线性KF使用的固定斜率 (c) 在高估或低估斜率之间来回切换。更麻烦的是截断使得观测长时间顶在70℃线性KF因为量测模型不准算出来的量测预测方差 (S) 严重偏小滤波器对截断观测的信任度过高估计值被牢牢钉在70℃附近。当真实温度越过量程进入正常观测范围后KF需要花很长时间才能“挣脱”这个错误的锚点。EKF在这个工况下表现完全不一样。因为雅可比 (H_k) 随着温度实时变化在接近截断边界时(H_k) 自动增大量测预测方差变大截断修正后的条件方差进一步变大滤波器自动降低对截断观测的信任。这相当于在滤波过程中植入了一个“边界感知机制”。5.3 边界区的“信任机制”EKF如何自动调节增益截断修正给EKF带来的一个隐性好处是卡尔曼增益在边界附近会自动收缩。出现截断时截断正态的条件方差通常比原始量测噪声方差大这意味着滤波器在边界处“变谨慎了”更依赖模型预测。这一点可以直观理解当传感器的读数顶在量程上限你知道的信息是“真实温度高于或等于上限”但具体高多少并不知道。此时如果模型预测的温度是120℃你会觉得传感器读数虽然顶在100℃但真实温度确实可能接近120℃——观测并没有提供足够的信息把你拉到100℃。条件矩修正算出的等效方差比原始大好几倍增益变小滤波器自然更信任模型。如果把卡尔曼增益曲线画出来会看到它在截断段明显凹陷等温度回到量程内又迅速恢复。这种自适应特性是写死在公式里的不需要任何人工干预这是我认为条件矩修正优于“边界处放大R”的根本原因。四种工况的具体表现如下表工况非线性系数截断程度线性KF的RMSEEKF的RMSE结论弱非线性轻度截断5e-8上限100℃0.82℃0.55℃KF可用EKF略优弱非线性重度截断5e-8上限70℃2.41℃0.67℃KF明显滞后EKF稳定强非线性轻度截断3e-7上限100℃2.05℃0.38℃线性近似失效强非线性重度截断3e-7上限70℃5.63℃0.49℃KF几乎发散EKF可靠表中的RMSE是在20次独立蒙特卡洛运行下取的平均值。注意强非线性加重度截断时线性KF的某些单次运行会出现估计值偏离真值10℃以上的情况这不是方差问题而是系统性发散。6. 调参、避坑与后续扩展这些坑我替你们踩过了6.1 Q和R的取值在截断场景下有特殊讲究常规滤波调参时(Q) 和 (R) 的比例决定了滤波器的平滑程度与跟踪速度。但在有截断观测的系统里这两个参数还有额外的杠杆作用。(Q) 设得过小滤波器对模型的信任度过高截断段会把估计值锁死在边界附近即使条件矩修正也不能完全拉回来(Q) 设得过大正常观测段噪声变大。我的经验是在有截断的仿真中(Q) 可以先按模型不确定性的物理意义来定然后在截断段观察估计曲线的“黏连”程度如果截断结束后估计值迟迟跟不上真值说明 (Q) 偏小了。(R) 的作用更微妙。截断修正用条件方差替代 (R)这个条件方差通常比标称 (R) 大一个量级。所以千万不要在截断段把 (R) 手动调小去“硬追”截断观测——那样会放大截断偏差。让条件方差自己去说话这是截断修正最省心的地方。6.2 初值误差与边界“锁死”问题另一个容易踩的坑是初值设置。如果滤波初值 (T_080)而真实温度是125℃同时观测量程是0100℃那么前几个周期观测全部顶在100℃。此时模型预测从80℃开始往上爬但爬得慢量测预测和100℃之间隔着一段距离截断修正后的条件期望大约在95℃上下。滤波器会觉得“观测暗示温度应该在95℃附近”初值从80℃往95℃方向修正速度就很慢。这时候把初始协方差 (P_0) 设大一点比如16而不是4让滤波器对初值不那么自信再加上条件矩修正收敛速度会明显加快。如果 (P_0) 太小滤波器会死死抱住初值80℃出现“锁死”现象估计曲线长时间爬不上去。这个坑在标准卡尔曼滤波里也存在但截断观测会把问题放大——因为截断让观测的“拉动力”变弱了。6.3 这套方法能用到哪些场景截断观测条件矩修正本质上是一个通用框架不局限于温度估计。传感器饱和、ADC满量程、通信中的量测钳位、执行器限幅导致的“饱和观测”都可以套用同一套逻辑。核心思想是一致的把截断观测转化为不等式约束再用条件矩将其近似为等效高斯观测。我后来把这个修正函数直接搬到过UKF和粒子滤波项目里。UKF用sigma点算量测预测分布然后用同样的truncated_moment做边界处理效果更平滑因为UKF不需要算雅可比非线性适应性更好。粒子滤波更直接直接对粒子做截断重采样就行但计算量大得多。如果你的系统非线性程度高到EKF的线性化都不够或者截断边界频繁来回穿越建议在EKF基础上加无迹变换或者直接换粒子滤波——但计算资源消耗就要另算了。最后再分享一个我在实际项目中验证过的小技巧如果截断观测持续很久且模型预测方向和截断方向一致比如预测继续升温观测一直顶上限可以把截断修正后的虚拟观测与模型预测做一次加权平均再送进滤波器权重由条件方差和模型方差共同决定。这个操作相当于在边界段给模型预测增加一点额外信任实测能进一步降低截断段的跟随误差。不过我一般不把它写进课程设计代码里——对教学演示来说条件矩修正本身已经足够说明问题加太多技巧反而喧宾夺主。