卡尔曼滤波与粒子滤波
定位仪每秒都给出一个位置,但读数有噪声;运动模型也只能近似预测下一秒。状态估计把这两种不完整的信息按明确的误差模型结合起来。卡尔曼滤波在离散时间线性、高斯假设下,用均值和协方差递推完整后验;粒子滤波用带权样本近似后验,能容纳更复杂的非线性或非高斯分布,但需要更多计算。
一次测量的加权
先不考虑运动,只估计一维位置。观测前的预测是 米,误差方差 平方米;新传感器报 米,测量噪声方差 平方米。若两个误差独立、均值零且高斯,测量残差为 米。卡尔曼增益 使更新后的估计成为 它比十更靠近新读数,却没有直接跳到十三,因为这次测量比原预测更不精确。更新方差为 。图上后验位置落在预测与观测之间;标出的三点是各自的中心值,图上的间距并不代替方差计算。
公式也可从最小方差得到。设更新形式 ,预测误差与测量误差独立,则新误差方差是 对 求导并令零:,所以 。这个推导说明增益并非随意调出的“平滑系数”;独立性或误差方差一旦不对,权重也会不对。
随时间预测和校正
一般线性状态模型写成 、观测模型写成 。过程噪声 、测量噪声 的协方差分别是 ;标准递推还假设它们零均值、彼此独立,并与起始估计误差独立。每一步先用动态方程预测 再用新观测修正 数值实现可用 (Joseph 形式)保存对称性与非负性,避免浮点误差让协方差变成负值。若没有当步观测,只做预测,不应伪造一个测量。
粒子在同一问题中承担什么
若状态运动模型非线性或可能有两个相距很远的候选位置,单个均值加协方差难以表达整个位置信念。粒子滤波保留 个状态样本 及归一化权重 ,权重和为一。每步先从状态转移模型抽取新粒子,再依据观测似然 更新权重: 若大量权重几乎为零,有效粒子数 会远小于 ;这时可按权重重采样。重采样后恢复相等权重,但不能从它凭空创造新的位置假设。似然过窄、粒子过少或模型根本漏掉一条可能轨迹,滤波仍会失败。
下面是与上面标量例子一致、可独立运行的 Python 3 代码。卡尔曼函数返回更新均值与方差;粒子函数以给定随机种子,从先验 抽样、按观测 和方差 加权,并返回加权均值、有效粒子数与按权重系统重采样后的粒子。不改写输入。粒子输出因抽样而近似,并不会恰好等于 。
import math
import random
def kalman_scalar(prior_mean, prior_var, reading, measurement_var):
if prior_var <= 0 or measurement_var <= 0:
raise ValueError("variances must be positive")
gain = prior_var / (prior_var + measurement_var)
mean = prior_mean + gain * (reading - prior_mean)
variance = (1 - gain) * prior_var
return mean, variance
def particle_update(prior_mean, prior_var, reading, measurement_var,
count=10000, seed=7):
if min(prior_var, measurement_var) <= 0 or count < 2:
raise ValueError("positive variances and at least two particles required")
rng = random.Random(seed)
particles = [rng.gauss(prior_mean, math.sqrt(prior_var))
for _ in range(count)]
log_w = [-0.5 * (reading - x) ** 2 / measurement_var
for x in particles]
top = max(log_w)
raw = [math.exp(value - top) for value in log_w]
total = sum(raw)
weights = [value / total for value in raw]
estimate = sum(x * w for x, w in zip(particles, weights))
effective = 1 / sum(w * w for w in weights)
start = rng.random() / count
positions = (start + j / count for j in range(count))
resampled = []
index, cumulative = 0, weights[0]
for target in positions:
while target > cumulative and index < count - 1:
index += 1
cumulative += weights[index]
resampled.append(particles[index])
return estimate, effective, resampled
exact_mean, exact_var = kalman_scalar(10, 4, 13, 9)
approx_mean, neff, samples = particle_update(10, 4, 13, 9)
print(round(exact_mean, 3), round(exact_var, 3))
print(round(approx_mean, 1), round(neff), len(samples))
此实现只演示一次观测更新,没有替连续时间跟踪写完整预测循环。标量卡尔曼更新每步耗时与存储为 ;粒子更新和系统重采样都是 时间、 空间。实际应用要用留出的轨迹检查位置误差与不确定区间覆盖率,并分别识别过程噪声 与传感器噪声 ;仅让轨迹看起来平滑不等于估计准确。