☰
【DeepSort 02】多轨迹算法DeepSort-卡尔曼滤波轨迹预测过程
2026/10/28 18:56:36 网站建设 项目流程

卡尔曼滤波轨迹预测过程

源码:deep_sort/sort/kalman_filter.py。
本文按代码顺序、用实际数字逐步推算predict(预测)、project(投影)、update(修正)、initiate(建档)四个函数,
最后是 DeepSORT 级联匹配的核心——gating_distance(马氏距离门控)。所有数字均用本工程源码实际运行验证过。

调用链(谁在调这些函数):

DeepSort.update() # deep_sort.py 每帧入口 ├─ self.tracker.predict() # 预测:先猜这一帧目标在哪 │ └─ for track in self.tracks: # Tracker 逐条轨迹循环(没有批量版) │ track.predict(self.kf) │ └─ kf.predict(mean, covariance) # ← 本文一 ├─ self.tracker.update(detections) # 匹配 + 修正 │ ├─ linear_assignment.gate_cost_matrix # ← 本文五(内部调 kf.gating_distance) │ ├─ track.update(kf, detection) │ │ └─ kf.update(mean, cov, detection.to_xyah()) # ← 本文三(经 project) │ └─ self.kf.initiate(detection.to_xyah()) # 未匹配检测建新轨迹 ← 本文四

公式与符号总表

符号代码里的真身是什么从哪来
mean参数mean状态均值(8 维)轨迹上一帧的 mean(initiate 建档时初始化)
cov(P)参数covariance状态协方差(8×8),对角线是各分量的不确定度轨迹上一帧的 covariance
Fself._motion_mat状态转移矩阵(8×8),匀速模型__init__构造:单位阵,右上 4×4 填 dt=1
Q局部变量motion_cov过程噪声协方差(8×8 对角阵),“匀速假设本身的误差预算”predict 内部用两个噪声权重 × mean[3] 现算
hmean[3]这条轨迹的框高(像素),不是固定值,每条轨迹各自取状态向量第 4 个分量
_std_weight_position类属性位置噪声权重 = 1/20__init__写死
_std_weight_velocity类属性速度噪声权重 = 1/160__init__写死
Hself._update_mat观测矩阵(4×8),只取前 4 维__init__构造
Rinnovation_cov(project 内局部变量)观测噪声(4×4 对角阵),检测框自身的不确定性project 内部用噪声权重 × mean[3] 现算
mean_new返回的mean外推后的状态均值(8 维)F · mean 算出
cov_new(P’)返回的covariance外推后的协方差(不确定性变大)F · cov · Fᵀ + Q 算出

本文三个核心公式:

predict: mean_new = F · mean (位置 = 位置 + 速度×1 帧,速度不变) cov_new = F · cov · Fᵀ + Q (不确定性传播:变大) project: z_mean = H · mean (8 维状态 → 4 维观测空间) S = H · cov · Hᵀ + R (观测空间的协方差) update: mean_new = mean + K · (measurement − H · mean) cov_new = cov − K · H · cov (本工程写法,与 cov − K·S·Kᵀ 恒等)

一、predict(单条轨迹)实际推算

方法作用输入输出
predict预测步:按匀速模型把状态外推到下一帧mean: 上一帧状态均值(8 维 ndarray);covariance: 上一帧协方差(8×8 ndarray)(mean, covariance):外推后的均值与协方差(不确定性变大)

输入(举例)

mean = [100, 50, 1.5, 40, 8, 0, 0, 0] ← x=100, y=50, a=1.5, h=40, vx=8, vy=0, va=0, vh=0 cov = [[16, 0, 0, 0, 0, 0, 0, 0], [0, 18, 0, 0, 0, 0, 0, 0], [0, 0, 0.5, 0, 0, 0, 0, 0], [0, 0, 0, 14, 0, 0, 0, 0], [0, 0, 0, 0, 6, 0, 0, 0], [0, 0, 0, 0, 0, 5, 0, 0], [0, 0, 0, 0, 0, 0, 0.3, 0], [0, 0, 0, 0, 0, 0, 0, 7]] ← 8×8 对角阵(演示值),各分量方差不同、交叉项 0

第 1 步:构造 std_pos(位置类 4 个分量的噪声标准差)

std_pos=[self._std_weight_position*mean[3],# x 的噪声 = h/20self._std_weight_position*mean[3],# y 的噪声 = h/201e-2,# a 的噪声 = 固定 0.01(比例无量纲,不随 h 缩放)self._std_weight_position*mean[3]]# h 的噪声 = h/20

代入 h = mean[3] = 40:

std_pos = [40/20, 40/20, 0.01, 40/20] = [2, 2, 0.01, 2]

第 2 步:构造 std_vel(速度类 4 个分量的噪声标准差)

std_vel=[self._std_weight_velocity*mean[3],# vx 的噪声 = h/160self._std_weight_velocity*mean[3],# vy 的噪声 = h/1601e-5,# va 的噪声 = 固定 0.00001self._std_weight_velocity*mean[3]]# vh 的噪声 = h/160

代入 h = 40:

std_vel = [40/160, 40/160, 0.00001, 40/160] = [0.25, 0.25, 0.00001, 0.25]

第 3 步:拼成 Q 矩阵(motion_cov)

motion_cov=np.diag(np.square(np.r_[std_pos,std_vel]))# np.r_ :把两个 4 元素列表拼成 8 个(顺序 = 状态分量顺序 x,y,a,h,vx,vy,va,vh)# np.square :标准差 → 方差(平方)# np.diag :放到 8×8 对角线上
Q = diag(2², 2², 0.01², 2², 0.25², 0.25², 0.00001², 0.25²) = diag(4, 4, 0.0001, 4, 0.0625, 0.0625, 0.0000000001, 0.0625)

第 4 步:外推 mean

mean=np.dot(self._motion_mat,mean)# F · mean(本工程写法)

F(=_motion_mat,__init__构造)右上角的 1 使"位置 + 速度×dt":

x' = 100 + 8 × 1 = 108 (dt = 1,__init__ 里 ndim, dt = 4, 1. 写死) y' = 50 + 0 × 1 = 50 a' = 1.5 + 0 × 1 = 1.5 h' = 40 + 0 × 1 = 40 vx' = 8, vy' = 0, va' = 0, vh' = 0 (速度不变) mean_new = [108, 50, 1.5, 40, 8, 0, 0, 0]

第 5 步:外推 cov

covariance=np.linalg.multi_dot((self._motion_mat,covariance,self._motion_mat.T))+motion_cov# = F · cov · Fᵀ + Q

cov 是对角阵时,结果规律简单:新对角线 = 旧对角线(自己) + 旧对角线(对应速度分量) + Q(自己),位置分量与自己的速度分量之间产生交叉项 = 旧速度方差:

cov_new(x 行): 16 + 6 + 4 = 26, 交叉项 [x, vx] = 6 cov_new(y 行): 18 + 5 + 4 = 27, 交叉项 [y, vy] = 5 cov_new(a 行): 0.5 + 0.3 + 0.0001 ≈ 0.8001,交叉项 [a, va] = 0.3 cov_new(h 行): 14 + 7 + 4 = 25, 交叉项 [h, vh] = 7 cov_new(速度行): vx 6+0.0625=6.0625,vy 5.0625,va 0.3,vh 7.0625

输出(返回)

mean_new = [108, 50, 1.5, 40, 8, 0, 0, 0] cov_new = [[26, 0, 0, 0, 6, 0, 0, 0 ], [0, 27, 0, 0, 0, 5, 0, 0 ], [0, 0, 0.8001,0, 0, 0, 0.3, 0 ], [0, 0, 0, 25, 0, 0, 0, 7 ], [6, 0, 0, 0, 6.0625, 0, 0, 0 ], [0, 5, 0, 0, 0, 5.0625,0, 0 ], [0, 0, 0.3, 0, 0, 0, 0.3, 0 ], [0, 0, 0, 7, 0, 0, 0, 7.0625]]

位置方差变大(16→26、18→27、14→25,外推后更不确定),速度方差基本不变,x-vx、y-vy、a-va、h-vh 之间出现交叉项(位置的不确定性里掺进了速度的不确定性)。

本工程落点:Tracker.predict()用 for 循环对每条轨迹调track.predict(self.kf)(tracker.py:54-60),轨迹内部再调本文的kf.predict,同时age += 1、time_since_update += 1(track.py:116-128)。

二、project(投影到观测空间)

方法作用输入输出
project把 8 维预测状态投影到 4 维观测空间(检测器只能看到 x、y、a、h 这 4 维),并叠加观测噪声mean: 预测均值(8 维 ndarray);covariance: 预测协方差(8×8 ndarray)(new_mean, new_cov):投影后的均值(4 维)与协方差(4×4)

project 是内部辅助函数,不对外直接调用——update和gating_distance都先用它把状态转到观测空间再算。

公式

new_mean = H · mean new_cov(S) = H · cov · Hᵀ + R
字母是什么从哪来
H观测矩阵(4×8),只取前 4 维__init__的_update_mat
mean8 维预测均值predict 的输出
cov8×8 预测协方差predict 的输出
R观测噪声(4×4 对角阵),检测框自身的不确定性函数内现算:diag((h/20)², (h/20)², 0.1², (h/20)²)
h目标框高(像素)mean[3]

计算过程(实际数字)

输入(predict 输出的那条轨迹):

mean = [108, 50, 1.5, 40, 8, 0, 0, 0] cov = [[26, 0, 0, 0, 6, 0, 0, 0 ], [0, 27, 0, 0, 0, 5, 0, 0 ], [0, 0, 0.8001,0, 0, 0, 0.3, 0 ], [0, 0, 0, 25, 0, 0, 0, 7 ], [6, 0, 0, 0, 6.0625, 0, 0, 0 ], [0, 5, 0, 0, 0, 5.0625,0, 0 ], [0, 0, 0.3, 0, 0, 0, 0.3, 0 ], [0, 0, 0, 7, 0, 0, 0, 7.0625]]

算 R(h = 40):

std = [40/20, 40/20, 0.1, 40/20] = [2, 2, 0.1, 2] ← 注意 a 的噪声这里是 1e-1(predict 里是 1e-2) R = diag(2², 2², 0.1², 2²) = diag(4, 4, 0.01, 4)

投影(H 取前 4 维 = 取 cov 左上 4×4):

new_mean = H · mean = [108, 50, 1.5, 40] new_cov = H · cov · Hᵀ + R = [[26+4, 0, 0, 0 ], [0, 27+4, 0, 0 ], [0, 0, 0.8001+0.01, 0], [0, 0, 0, 25+4]] = [[30, 0, 0, 0 ], [0, 31, 0, 0 ], [0, 0, 0.8101,0 ], [0, 0, 0, 29 ]]

输出:

new_mean = [108, 50, 1.5, 40] ← 4 维 new_cov(S) = [[30, 0, 0, 0 ], ← 4×4 [0, 31, 0, 0 ], [0, 0, 0.8101,0 ], [0, 0, 0, 29 ]]

三、update(修正步)

方法作用输入输出
update修正步:用配对成功的检测框修正预测状态——算卡尔曼增益 K,加权融合预测和检测,输出更准的状态mean: 预测均值(8 维 ndarray);covariance: 预测协方差(8×8 ndarray);measurement: 检测框[x, y, a, h](4 维 ndarray)(mean, covariance):修正后的均值与协方差(不确定性变小)

公式

K = cov · Hᵀ · S⁻¹ (卡尔曼增益,8×4,cholesky 求解) innovation = measurement − H · mean (残差:检测框比预测框偏了多少) mean_new = mean + K · innovation cov_new = cov − K · H · cov (本工程代码的写法)

cov − K·H·cov与cov − K·S·Kᵀ两种写法代数恒等(由 K 的定义推出),本工程 kalman_filter.py:186-187 用前者。

字母是什么从哪来
K卡尔曼增益(8×4 矩阵),每个状态分量的修正权重公式算出
S投影后的协方差(4×4)project 的输出
innovation残差(4 维),检测框比预测框偏了多少检测框 − 投影预测
measurement检测框[x, y, a, h]配对成功的检测(Detection.to_xyah() 转换)
mean / cov8 维预测状态predict 的输出

计算过程(实际数字)

输入:

mean = [108, 50, 1.5, 40, 8, 0, 0, 0] ← predict 的输出 cov = [[26, 0, 0, 0, 6, 0, 0, 0 ], [0, 27, 0, 0, 0, 5, 0, 0 ], [0, 0, 0.8001,0, 0, 0, 0.3, 0 ], [0, 0, 0, 25, 0, 0, 0, 7 ], [6, 0, 0, 0, 6.0625, 0, 0, 0 ], [0, 5, 0, 0, 0, 5.0625,0, 0 ], [0, 0, 0.3, 0, 0, 0, 0.3, 0 ], [0, 0, 0, 7, 0, 0, 0, 7.0625]] ← predict 的输出 measurement = [108.5, 50.5, 1.5, 41] ← 检测框(检测器说目标在 108.5)

project 的输出(上一步算好的):

H·mean = [108, 50, 1.5, 40] S = [[30, 0, 0, 0 ], [0, 31, 0, 0 ], [0, 0, 0.8101,0 ], [0, 0, 0, 29 ]]

算 K(公式带实际数据):

K = cov · Hᵀ · S⁻¹ = [[26, 0, 0, 0 ] [1/30, 0, 0, 0 ] [0, 27, 0, 0 ] [0, 1/31, 0, 0 ] [0, 0, 0.8001,0 ] · [0, 0, 1/0.8101, 0 ] [0, 0, 0, 25 ] [0, 0, 0, 1/29] [6, 0, 0, 0 ] ← 左边 8×4 = cov·Hᵀ(cov 的前 4 列),右边 4×4 = S⁻¹ [0, 5, 0, 0 ] [0, 0, 0.3, 0 ] [0, 0, 0, 7 ]] = [[26/30, 0, 0, 0 ] [0, 27/31, 0, 0 ] [0, 0, 0.8001/0.8101,0 ] [0, 0, 0, 25/29 ] [6/30, 0, 0, 0 ] [0, 5/31, 0, 0 ] [0, 0, 0.3/0.8101, 0 ] [0, 0, 0, 7/29 ]] = [[0.8667, 0, 0, 0 ] ← 8 行(8 个状态分量)× 4 列(4 个观测分量) [0, 0.8710, 0, 0 ] ← 每行的非零值 = 该分量的修正权重 [0, 0, 0.9877, 0 ] ← a 的权重最大(观测比预测可信) [0, 0, 0, 0.8621] [0.2, 0, 0, 0 ] ← 速度行的小权重:位置残差通过它传导到速度 [0, 0.1613, 0, 0 ] [0, 0, 0.3704, 0 ] [0, 0, 0, 0.2414]]

算残差:

innovation = measurement − H·mean = [108.5−108, 50.5−50, 1.5−1.5, 41−40] = [0.5, 0.5, 0, 1]

修正 mean(公式带实际数据):

mean_new = mean + K · innovation = [108, 50, 1.5, 40, 8, 0, 0, 0] + [0.8667×0.5, 0.8710×0.5, 0.9877×0, 0.8621×1, 0.2×0.5, 0.1613×0.5, 0.3704×0, 0.2414×1] = [108, 50, 1.5, 40, 8, 0, 0, 0 ] + [0.4333, 0.4355, 0, 0.8621, 0.1, 0.0806, 0, 0.2414] = [108.4333, 50.4355, 1.5, 40.8621, 8.1, 0.0806, 0, 0.2414]

修正 cov(公式带实际数据):

cov_new = cov − K · H · cov K·H(先算,8×4 · 4×8 → 8×8,每行把自己的权重放回对应的观测列): = [[0.8667, 0, 0, 0, 0, 0, 0, 0] [0, 0.8710, 0, 0, 0, 0, 0, 0] [0, 0, 0.9877, 0, 0, 0, 0, 0] [0, 0, 0, 0.8621, 0, 0, 0, 0] [0.2, 0, 0, 0, 0, 0, 0, 0] [0, 0.1613, 0, 0, 0, 0, 0, 0] [0, 0, 0.3704, 0, 0, 0, 0, 0] [0, 0, 0, 0.2414, 0, 0, 0, 0]] K·H·cov(再乘 cov,8×8 · 8×8,每行 = 该行的权重 × cov 对应位置那一行的取值): = [[0.8667×26, 0, 0, 0, 0.8667×6, 0, 0, 0 ] [0, 0.8710×27, 0, 0, 0, 0.8710×5, 0, 0 ] [0, 0, 0.9877×0.8001,0, 0, 0, 0.9877×0.3, 0 ] [0, 0, 0, 0.8621×25, 0, 0, 0, 0.8621×7] [0.2×26, 0, 0, 0, 0.2×6, 0, 0, 0 ] [0, 0.1613×27, 0, 0, 0, 0.1613×5, 0, 0 ] [0, 0, 0.3704×0.8001,0, 0, 0, 0.3704×0.3, 0 ] [0, 0, 0, 0.2414×25, 0, 0, 0, 0.2414×7]] = [[22.5333, 0, 0, 0, 5.2, 0, 0, 0 ] [0, 23.5161,0, 0, 0, 4.3548, 0, 0 ] [0, 0, 0.7902, 0, 0, 0, 0.2963, 0 ] [0, 0, 0, 21.5517,0, 0, 0, 6.0345] [5.2, 0, 0, 0, 1.2, 0, 0, 0 ] [0, 4.3548, 0, 0, 0, 0.8065, 0, 0 ] [0, 0, 0.2963, 0, 0, 0, 0.1111, 0 ] [0, 0, 0, 6.0345, 0, 0, 0, 1.6897]] cov_new = cov − 上面的矩阵 = [[26−22.5333, 0, 0, 0, 6−5.2, 0, 0, 0 ] [0, 27−23.5161, 0, 0, 0, 5−4.3548, 0, 0 ] [0, 0, 0.8001−0.7902, 0, 0, 0, 0.3−0.2963, 0 ] [0, 0, 0, 25−21.5517, 0, 0, 0, 7−6.0345] [6−5.2, 0, 0, 0, 6.0625−1.2, 0, 0, 0 ] [0, 5−4.3548, 0, 0, 0, 5.0625−0.8065,0, 0 ] [0, 0, 0.3−0.2963, 0, 0, 0, 0.3−0.1111, 0 ] [0, 0, 0, 7−6.0345, 0, 0, 0, 7.0625−1.6897]] = [[3.4667, 0, 0, 0, 0.8, 0, 0, 0 ] [0, 3.4839, 0, 0, 0, 0.6452, 0, 0 ] [0, 0, 0.0099, 0, 0, 0, 0.0037, 0 ] [0, 0, 0, 3.4483, 0, 0, 0, 0.9655 ] [0.8, 0, 0, 0, 4.8625, 0, 0, 0 ] [0, 0.6452, 0, 0, 0, 4.2560, 0, 0 ] [0, 0, 0.0037, 0, 0, 0, 0.1889, 0 ] [0, 0, 0, 0.9655, 0, 0, 0, 5.3728 ]]

输出:

mean_new = [108.4333, 50.4355, 1.5, 40.8621, 8.1, 0.0806, 0, 0.2414] cov_new = [[3.4667, 0, 0, 0, 0.8, 0, 0, 0 ], [0, 3.4839, 0, 0, 0, 0.6452, 0, 0 ], [0, 0, 0.0099, 0, 0, 0, 0.0037, 0 ], [0, 0, 0, 3.4483, 0, 0, 0, 0.9655], [0.8, 0, 0, 0, 4.8625, 0, 0, 0 ], [0, 0.6452, 0, 0, 0, 4.2560, 0, 0 ], [0, 0, 0.0037, 0, 0, 0, 0.1889, 0 ], [0, 0, 0, 0.9655, 0, 0, 0, 5.3728]]

修正后的框中心 (108.4333, 50.4355) 落在预测 108 和检测 108.5 之间(按 0.8667 的权重偏向预测);方差全面变小(26→3.47、25→3.45,观测吸收了不确定性)。这两个值存回轨迹,下一帧 predict 从它们出发——predict 变大 / update 变小,循环闭环。

本工程落点:Track.update(kf, detection)内部调kf.update(self.mean, self.covariance, detection.to_xyah())(track.py:130-151),同时追加特征、hits+1、time_since_update 清零、hits≥n_init 时转 Confirmed。

四、initiate(建档初始化)

方法作用输入输出
initiate建档初始化:用建档检测框生成轨迹的初始状态——位置来自检测框,速度置 0(没观测过速度),并给一个初始不确定性(速度方差给得大,位置方差给得小)measurement: 建档检测框[x, y, a, h](4 维 ndarray,Detection.to_xyah() 转换来的中心坐标、宽高比、高)(mean, covariance):初始均值(8 维 ndarray,速度 4 维全 0)与初始协方差(8×8 对角阵)

计算过程(实际数字)

输入(检测框 tlwh[30, 40, 50, 100]→ 转 xyah):

measurement = [55, 90, 0.5, 100] ← x=55, y=90(框中心),a=0.5(宽高比),h=100(框高)

第 1 步:拼初始 mean(位置 + 速度)

mean_pos = [55, 90, 0.5, 100] ← 位置 4 维直接用检测框 mean_vel = [0, 0, 0, 0] ← 速度 4 维:没观测过,全置 0 mean = np.r_[mean_pos, mean_vel] ← 拼成 8 维 = [55, 90, 0.5, 100, 0, 0, 0, 0]

第 2 步:构造初始 std(8 个分量的噪声标准差,h=100)

std = [ 2 × (1/20) × 100, ← x:2 × h/20 = 10 2 × (1/20) × 100, ← y:10 1e-2, ← a:固定 0.01 2 × (1/20) × 100, ← h:10 10 × (1/160) × 100, ← vx:10 × h/160 = 6.25 10 × (1/160) × 100, ← vy:6.25 1e-5, ← va:固定 0.00001 10 × (1/160) × 100] ← vh:6.25 = [10, 10, 0.01, 10, 6.25, 6.25, 0.00001, 6.25]

位置噪声用 2 倍权重、速度噪声用 10 倍权重(比 predict 的 1 倍大)——刚建档时啥都不确定,尤其速度完全没观测过,方差给大,让 update 阶段能快速修正它。

第 3 步:拼初始 covariance(std 平方上对角线)

covariance = diag(10², 10², 0.01², 10², 6.25², 6.25², 0.00001², 6.25²) = diag(100, 100, 0.0001, 100, 39.0625, 39.0625, ~0, 39.0625)

输出:

mean = [55, 90, 0.5, 100, 0, 0, 0, 0] covariance = [[100, 0, 0, 0, 0, 0, 0, 0 ], [0, 100, 0, 0, 0, 0, 0, 0 ], [0, 0, 0.0001, 0, 0, 0, 0, 0 ], [0, 0, 0, 100,0, 0, 0, 0 ], [0, 0, 0, 0, 39.0625,0, 0, 0 ], [0, 0, 0, 0, 0, 39.0625,0, 0 ], [0, 0, 0, 0, 0, 0, ~0, 0 ], [0, 0, 0, 0, 0, 0, 0, 39.0625]]

本工程落点:Tracker._initiate_track对未匹配检测调self.kf.initiate(detection.to_xyah())(tracker.py:139),返回的 mean/covariance 存进新建的 Track 对象,下一帧 predict 从它们出发。

五、gating_distance(马氏距离门控)

方法作用输入输出
gating_distance算一条轨迹的预测分布与N 个检测框各自的平方马氏距离,用于级联匹配的运动门控:距离超过卡方阈值的配对判为不可行mean: 这条轨迹的 8 维状态均值;covariance: 这条轨迹的 8×8 协方差;measurements: Nx4 检测框矩阵(每行 x, y, a, h);only_position: 只按中心位置算(默认 False)长度 N 的数组,第 i 个元素 = 这条轨迹与第 i 个检测框的平方马氏距离

公式

d² = dᵀ · S⁻¹ · d (d = 残差 = 检测框 − 投影后的预测均值;S = H·cov·Hᵀ + R)
字母是什么从哪来
d残差(4 维),检测框比预测框偏了多少measurements − H·mean
S投影后的协方差(4×4)project 的输出(本文二,检测框噪声 R 已叠在里面)
d²平方马氏距离(标量),一个检测框一个公式算出
measurementsNx4 检测框矩阵,每行 x, y, a, h调用方把 Detection 转 to_xyah() 拼成

代码实现(kalman_filter.py:223-230)不直接求 S⁻¹,用 Cholesky 分解代替,推导:

S = L·Lᵀ → S⁻¹ = (L·Lᵀ)⁻¹ = L⁻ᵀ·L⁻¹ d² = dᵀ·S⁻¹·d = dᵀ·L⁻ᵀ·L⁻¹·d = (L⁻¹·d)ᵀ·(L⁻¹·d) = ‖L⁻¹·d‖²
mean,covariance=self.project(mean,covariance)# 第 1 步:投影到 4 维观测空间(本文二)cholesky_factor=np.linalg.cholesky(covariance)# L,使 S = L·Lᵀd=measurements-mean# 第 2 步:残差(Nx4)z=scipy.linalg.solve_triangular(# 第 3 步:解方程 L·z = d,得 z = L⁻¹·dcholesky_factor,d.T,lower=True,check_finite=False,overwrite_b=True)squared_maha=np.sum(z*z,axis=0)# 第 4 步:d² = ‖z‖²,每个检测框一个# (only_position=True 时在 cholesky 前把 mean/cov/measurements 都截成前 2 维)

S 是对角阵时 L = diag(√S),z_i = d_i/√S_ii,于是d² = Σ d_i²/S_ii(残差平方除以方差再求和)——下面的手算就用这个形式。

判定阈值(模块顶部的 chi2inv95 表)

卡方分布 0.95 分位数表(kalman_filter.py:11-20):

自由度123456789
阈值3.84155.99157.81479.487711.07012.59214.06715.50716.919

自由度 = 参与计算的维数:only_position=False(默认)用 4 维(x, y, a, h)→ 阈值9.4877;only_position=True只用 2 维(x, y)→ 阈值 5.9915。d² 小于阈值 ≈ 检测框落在轨迹 95% 的预测范围内。

计算过程(实际数字)

输入(轨迹 = 本文一 predict 之后的那个例子;两个候选检测框):

mean = [108, 50, 1.5, 40, 8, 0, 0, 0] ← predict 后的状态 cov = [[26, 0, 0, 0, 6, 0, 0, 0 ], [0, 27, 0, 0, 0, 5, 0, 0 ], [0, 0, 0.8001,0, 0, 0, 0.3, 0 ], [0, 0, 0, 25, 0, 0, 0, 7 ], [6, 0, 0, 0, 6.0625, 0, 0, 0 ], [0, 5, 0, 0, 0, 5.0625,0, 0 ], [0, 0, 0.3, 0, 0, 0, 0.3, 0 ], [0, 0, 0, 7, 0, 0, 0, 7.0625]] measurements = [[108.5, 50.5, 1.5, 41], ← 检测1:就在预测位置附近 [130, 60, 1.5, 42]] ← 检测2:中心偏了 22 像素

第 1 步:project 投影到观测空间(过程与本文二完全相同,同一组数):

H·mean = [108, 50, 1.5, 40] S = [[30, 0, 0, 0 ], [0, 31, 0, 0 ], [0, 0, 0.8101,0 ], [0, 0, 0, 29 ]]

第 2 步:算两个检测框的残差 d = 检测框 − H·mean:

检测1: d = [108.5−108, 50.5−50, 1.5−1.5, 41−40] = [0.5, 0.5, 0, 1] 检测2: d = [130−108, 60−50, 1.5−1.5, 42−40] = [22, 10, 0, 2]

第 3 步:d² = Σ 残差² / 方差(S 对角线):

检测1: d² = 0.5²/30 + 0.5²/31 + 0²/0.8101 + 1²/29 = 0.0083 + 0.0081 + 0 + 0.0345 = 0.0509 检测2: d² = 22²/30 + 10²/31 + 0²/0.8101 + 2²/29 = 16.1333 + 3.2258 + 0 + 0.1379 = 19.4971

第 4 步:与阈值 9.4877 比较:

d² = [0.0509, 19.4971] [0.0509 < 9.4877, 19.4971 > 9.4877] [进门, 拒绝] ← 检测2 不可能是这条轨迹的

输出:[0.0509, 19.4971](长度 2 的数组,第 i 个元素 = 这条轨迹与第 i 个检测框的平方马氏距离;本例检测1 进门、检测2 被拒)。

描述链接
项目基于 Ultralytics + DeepSORT 多目标跟踪与改进
往期回顾链接
数据集数据集汇总
软件开发深度学习软件开发汇总

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询